diff --git a/.codex/README.md b/.codex/README.md index 60f6af9..f23eeb8 100644 --- a/.codex/README.md +++ b/.codex/README.md @@ -4,20 +4,22 @@ Запускайте Codex из корня репозитория или любой вложенной папки. Корень определяется через `git rev-parse --show-toplevel`. -На каждом компьютере установите `uv` и добавьте его в PATH процесса Codex. -По умолчанию сервер расположен в `mame/sources/MAME/src/mame_mcp.py`. -Каталог `mame/` не хранится в Git и должен быть установлен отдельно. -Первый запуск uv может потребовать сеть для установки Python 3.12 и mcp<2. +На каждом компьютере установите `uv` и `pyenv`. Используется локальный +pyenv Python 3.12 из `.python-version`; uv ставит только пакет `mcp<2`, +не управляет версией Python. По умолчанию сервер расположен в соседнем +`../MAME/src/mame_mcp.py`. При иной раскладке задайте явный путь. Если расположение отличается, создайте `.codex/mame.local.env` (игнорируется Git): ```sh MAME_UV="/opt/homebrew/bin/uv" -MAME_MCP_SCRIPT="mame/sources/MAME/src/mame_mcp.py" +MAME_MCP_SCRIPT="/путь/к/MAME/src/mame_mcp.py" +MAME_PYTHON="/путь/к/pyenv/versions/3.12/bin/python" ``` Путь к серверу может быть абсолютным или относительно корня проекта. -Путь к uv может быть абсолютным, относительным к корню или именем из PATH. +Пути к uv/Python могут быть абсолютными или именами из PATH; если +`MAME_PYTHON` не задан, скрипт вызывает `pyenv which python`. Переменные можно также передать через окружение Codex; локальный файл имеет приоритет. Файл читается как shell-код запускным скриптом, а не самим Codex. Не храните в нём секреты и не добавляйте его в Git. diff --git a/.gitignore b/.gitignore index af4a29e..ecf26a2 100644 --- a/.gitignore +++ b/.gitignore @@ -10,24 +10,7 @@ build/ .sprinter-cc-*/ .resource-stamps/ -# Per-program final/intermediate outputs landing alongside the source -# (real apps under examples/ and libc feature tests under tests/). -examples/*/*.exe -examples/*/*.asm -examples/*/*.lst -examples/*/*.lk -examples/*/*.ihx -examples/*/*.noi -examples/*/*.sym -examples/*/*.map -examples/*/*.rel -examples/*/*.cdb -examples/*/*.mem -examples/*/*.rst - -# Temporary build directory for floppy disk image preparation -examples/*/.disk_tmp/ - +# Per-program final/intermediate outputs landing alongside SDK tests. tests/*/*.exe tests/*/*.asm tests/*/*.lst @@ -41,30 +24,6 @@ tests/*/*.cdb tests/*/*.mem tests/*/*.rst -# applications/// — реальные приложения (та же схема, что -# examples/ и tests/, но на уровень глубже: applications/PoP/roomtest/...) -applications/*/*/*.exe -applications/*/*/*.asm -applications/*/*/*.lst -applications/*/*/*.lk -applications/*/*/*.ihx -applications/*/*/*.noi -applications/*/*/*.sym -applications/*/*/*.map -applications/*/*/*.rel -applications/*/*/*.cdb -applications/*/*/*.mem -applications/*/*/*.rst - -# PoP-порт: внешние референс-репозитории (собственные git-клоны — НЕ -# часть этого репозитория) + оригинальные game-данные (копирайт, только -# для реверса форматов на этой машине). -applications/PoP/SDLPoP/ -applications/PoP/mininim/ -applications/PoP/PR/ -applications/PoP/Prince-of-Persia-Apple-II/ -applications/PoP/MSDOS/ - # libc + libbgi archives (built by libc/Makefile + libbgi/Makefile) lib/*.lib @@ -93,9 +52,6 @@ third_party/sdcc-*/ third_party/*.tar.bz2 third_party/*.tar.gz -# MAME emulator install — ~1 GB binary + ROMs + CHDs -mame/ - # =========================================================================== # OS / editor / AI assistant # =========================================================================== @@ -126,22 +82,5 @@ mame/ docs/extra/ docs/sources/ -# Записи музыки DOS-версии PoP (43 МБ в четырёх форматах) — ИСТОЧНИК для -# toolchain/pop_pack_music.py, а не ресурс сборки: на диск игры уходят уже -# упакованные poc/res/music/*.bin, и они в репозитории есть. Если понадобится -# перегенерировать музыку — положить сюда PoP1_DOS_music (flac). -applications/PoP/PoP1_DOS_music/ - -# R1 — рабочая копия roomtest для экспериментов пользователя, в репозиторий -# не идёт (сама roomtest и есть версируемая ветка разработки). -applications/PoP/R1/ - -# SprPoP — автономное приложение, и правила игнора у него СВОИ: -# applications/SprPoP/.gitignore. Он написан так, чтобы стать корневым -# .gitignore, когда SprPoP выделят в отдельный репозиторий, — поэтому -# здесь его содержимое НЕ дублируется (иначе разъедется). Коротко: в -# репозиторий не идут assets/orig/ (чужие данные — их выкачивает -# `make fetch`) и assets/packed/LEVELS/ (уровни из оригинала как есть). - # Локальные пути MCP; общая конфигурация .codex остаётся в Git. /.codex/mame.local.env diff --git a/AGENTS.md b/AGENTS.md index a1f1a98..86894dc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,10 +6,10 @@ Target-слой SDCC 4.5 (z80) для компьютера Sprinter Sp2000: crt0 ## Сборка и проверка ``` -make # tools + lib + libbgi + все тесты (45) + examples +make # tools + lib + libbgi + все тесты SDK make -C libc # только libc → lib/sprinter.lib (fast) + sprinter_safe.lib make -C libbgi # только BGI → lib/bgi256.lib (fast) + bgi256_safe.lib -make floppy # упаковать все .exe в mame/v306/IMG/mc.img +make floppy # упаковать тесты в build/media/toolkit-tests.img make size-check # размерный регресс: _CODE vs docs/size_baseline.tsv make size-baseline # принять текущие размеры эталоном ``` @@ -26,8 +26,8 @@ make size-baseline # принять текущие размеры эталон дискету и запускает MAME автоматически через `toolchain/mame_interactive.py`, снимает скриншоты, выводит пути). Для сложных сценариев (диалог, несколько шагов ввода) — прямой вызов: -`python3 toolchain/mame_interactive.py tests/<имя>/<имя>.exe --snap T1,T2 --timeout T`. -Скриншоты лежат в `mame/v306/snap_auto/sprinter/` (читаются инструментом Read). +`pyenv exec python toolchain/mame_interactive.py tests/<имя>/<имя>.exe --snap T1,T2 --timeout T`. +Скриншоты лежат в `build/mame-autotest//sprinter/`. Подробности: `docs/mame-autotest.md`. После правок libc/libbgi: пересборка от чистого листа (`make -C libc clean` @@ -87,7 +87,8 @@ make size-baseline # принять текущие размеры эталон - `libbgi/` — графика BGI (отдельная библиотека): `common/` — mode-agnostic (один исходник, .rel в обеих driver-библиотеках), `bgi256/` + `bgi16/` — mode-specific leaf'ы (реальные реализации, без обёрток); `include/` — graphics.h + gfx.h; `_bgi.h` — внутренний заголовок. Собирает `lib/bgi256.lib` (и `bgi16.lib` в Фазе 2). Выбор режима линковкой: `--gfx 256` / `--gfx 16`. - `runtime/` — crt0-семейство, heap, bank (bank.s собирается per-build) - `bin/sprinter-cc` — обёртка компилятора; `toolchain/mkexe` — упаковщик -- `tests/` — по одному API/фиче; `examples/` — реальные приложения +- `tests/` — по одному API/фиче; примеры находятся в отдельном репозитории + `../Examples`, приложения — в независимых репозиториях `../Applications/*` - `docs/` — дизайн-доки; `docs/TODO.md` — roadmap - `third_party/solid-c/` — нативный Sprinter C (референс, CP866; их ABI несовместим — только как образец) diff --git a/LICENSE b/LICENSE index 217ff75..780e208 100644 --- a/LICENSE +++ b/LICENSE @@ -30,10 +30,10 @@ Vendored third-party components are under their own licenses: for compatibility. Licence not specified upstream; treat as reference material, do not redistribute original binaries. - mame/v306/ — MAME (https://www.mamedev.org), GPL v2+. - mame/v306/IMG/*.img *.chd *.iso — Sprinter ROM / DSS / sample disk images - from Peters Plus. Redistribution policy: see - Peters Plus documentation. + +MAME and its Sprinter ROM/DSS/CHD installation are held in a separate +project and are not distributed with this SDK. Their own licenses and +redistribution terms apply. Documentation in docs/converted/, docs/reference/, docs/samples/, and docs/memory management/ contains material originally published by Peters Plus diff --git a/Makefile b/Makefile index 9b76c42..f276792 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,11 @@ TESTS := hello hello2 simple banked bankedbg banktest strtest cat seek \ # ещё не собирается). Вернуть мигрированными на BGI --gfx 16. FLOPPY_IMG := build/media/toolkit-tests.img MAKE_DISK := toolchain/make_disk.py -MAME_HOME ?= $(if $(wildcard mame/v306/mame.arm),$(CURDIR)/mame/v306,) +MAME_HOME ?= +PYTHON ?= $(shell pyenv which python) +ifeq ($(strip $(PYTHON)),) +$(error pyenv Python 3.12 не найден; задайте PYTHON=/путь/к/python3.12) +endif TEST_EXES := $(foreach t,$(TESTS),tests/$(t)/$(t).exe) ALL_EXES := $(TEST_EXES) @@ -55,17 +59,17 @@ $(TESTS): tools lib # Generate big.txt if missing (gen_bigfile.py creates 100 KB marker file). tests/seek/big.txt: - cd tests/seek && python3 gen_bigfile.py big.txt 102400 + cd tests/seek && $(PYTHON) gen_bigfile.py big.txt 102400 # Собственный SDK-носитель: не изменяет диск MAME или приложения. floppy: tests tests/seek/big.txt - python3 $(MAKE_DISK) $(FLOPPY_IMG) $(ALL_EXES) $(DATA_FILES) + $(PYTHON) $(MAKE_DISK) $(FLOPPY_IMG) $(ALL_EXES) $(DATA_FILES) @echo @echo "Floppy ready: $(FLOPPY_IMG)" @echo "Run: make run MAME_HOME=/путь/к/MAME/runtime" run: floppy - python3 toolchain/run_sprinter_mame.py --mame-home "$(MAME_HOME)" --floppy "$(FLOPPY_IMG)" + $(PYTHON) toolchain/run_sprinter_mame.py --mame-home "$(MAME_HOME)" --floppy "$(FLOPPY_IMG)" # Модульные тесты под ucsim_z80. Обвязка — testkit/, сами наборы лежат # рядом с кодом, который проверяют. MAME не нужна, идут за секунды; @@ -79,15 +83,15 @@ host-tests: # Отладочная карта: реальные SDCC/linker и протокол транспорта без MAME. # Для local pyenv: pyenv exec make sdbg-tests. sdbg-tests: tools lib - python3 -m unittest discover -s tests/sdbg -v + $(PYTHON) -m unittest discover -s tests/sdbg -v # Размерный регресс: сверить _CODE всех программ с docs/size_baseline.tsv. size-check: - python3 toolchain/size_check.py + $(PYTHON) toolchain/size_check.py # Принять текущие размеры как эталон (после осознанных изменений). size-baseline: - python3 toolchain/size_check.py --update + $(PYTHON) toolchain/size_check.py --update clean: $(MAKE) -C toolchain/mkexe clean diff --git a/app.mk b/app.mk index 6cadeae..a9a3cda 100644 --- a/app.mk +++ b/app.mk @@ -40,14 +40,21 @@ ifeq ($(strip $(SPRINTER_ROOT)),) SPRINTER_ROOT := $(PROJ_ROOT) endif -PYTHON ?= python3 +# SPRINTER_PYTHON в sprinter-cc — один исполняемый файл, а не shell-команда. +# pyenv which выбирает local .python-version текущего приложения. +PYTHON ?= $(shell pyenv which python) +ifeq ($(strip $(PYTHON)),) +$(error pyenv Python не найден; задайте PYTHON=/путь/к/python3.12) +endif SPRINTER_CC := $(PROJ_ROOT)/bin/sprinter-cc MKEXE := $(PROJ_ROOT)/toolchain/mkexe/mkexe LIB := $(PROJ_ROOT)/lib/sprinter.lib -# Временный fallback для старого общего дерева. После переноса нет MAME -# внутри SDK, поэтому внешний проект задаёт MAME_HOME или отдельные пути. -MAME_HOME ?= $(if $(wildcard $(PROJ_ROOT)/mame/v306/mame.arm),$(PROJ_ROOT)/mame/v306,) +ifeq ($(wildcard $(SPRINTER_CC)),) +$(error SPRINTER_ROOT=$(SPRINTER_ROOT): bin/sprinter-cc не найден) +endif + +MAME_HOME ?= MAME_BIN ?= $(if $(strip $(MAME_HOME)),$(MAME_HOME)/mame.arm,) MAME_ROMPATH ?= $(if $(strip $(MAME_HOME)),$(MAME_HOME)/roms,) MAME_DSS_IMAGE ?= $(if $(strip $(MAME_HOME)),$(MAME_HOME)/IMG/dss171u.img,) @@ -69,7 +76,7 @@ MAME_PROFILE_ARGS = --mame-home "$(MAME_HOME)" --mame-bin "$(MAME_BIN)" \ MEMORY ?= tiny # SRC_DIR / BUILD_DIR — раскладка приложения, которое НЕ держит исходники и -# выхлоп в одной папке с Makefile (applications/SprPoP: src/ и build/). По +# выхлоп в одной папке с Makefile (например, SprPoP: src/ и build/). По # умолчанию обе пусты, то есть всё как было: ./$(EXAMPLE).c → ./$(EXAMPLE).exe. # Пустое значение обрабатывается отдельной веткой намеренно: "./prog.exe" и # "prog.exe" — разные имена целей, и склеивать префикс безусловно нельзя. diff --git a/applications/PoP/.gitignore b/applications/PoP/.gitignore deleted file mode 100644 index fad014b..0000000 --- a/applications/PoP/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -# Референсные клоны и оригинальные игровые данные — не часть порта. -SDLPoP/ -PR/ -mininim/ -Prince-of-Persia-Apple-II/ -MSDOS/ -PoP1_DOS_music/ -R1/ -local.mk -build/ -.sprinter-cc-*/ -.resource-stamps/ -*.exe -*.asm -*.lst -*.lk -*.ihx -*.noi -*.sym -*.map -*.rel -*.cdb -*.mem -*.rst -__pycache__/ -*.py[cod] -.DS_Store -._* -.vscode/ diff --git a/applications/PoP/.python-version b/applications/PoP/.python-version deleted file mode 100644 index e4fba21..0000000 --- a/applications/PoP/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.12 diff --git a/applications/PoP/CLAUDE.md b/applications/PoP/CLAUDE.md deleted file mode 100644 index d530f00..0000000 --- a/applications/PoP/CLAUDE.md +++ /dev/null @@ -1,91 +0,0 @@ -# Prince of Persia → ZX Sprinter — правила подпроекта - -Порт Prince of Persia (DOS/Apple II) на Sprinter Sp2000 поверх нашего -sprinter-cc / libc / libbgi. Действуют правила корневого -`CLAUDE.md` (сборка, libc, ABI, MAME-автотест); ниже — только специфика PoP. -Общение и комментарии — на русском. - -## Главное правило: SDLPoP — источник истины. Сначала читай, потом кодь - -**`SDLPoP/src/` (github.com/NagyD/SDLPoP, GPLv3) — ЕДИНСТВЕННЫЙ авторитетный -источник того, как оригинальный движок это делает.** Правило без исключений: - -1. **Перед реализацией ЛЮБОЙ функции** (движение, коллизия, окклюзия, - падение, loose-полы, стражники, отрисовка, тайминги, любые числовые - константы) — СНАЧАЛА найди и прочитай соответствующий код в `SDLPoP/src/`, - и портируй по нему. Не пиши по памяти, не выводи логику «из общих - соображений», не угадывай значения — это источник багов, которые потом - ловятся в MAME часами. -2. **По любому вопросу «как в оригинале должно быть»** (что окклюдит что, - в каком порядке слои, когда меняется тайл, какая скорость/задержка, - что делает такой-то кадр анимации) — ответ ищи в `SDLPoP/src/`, а не - строй гипотезу. Если в 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 непонятно/нужен другой ракурс): -- `Prince-of-Persia-Apple-II/` — оригинальный 6502-исходник 1989 (Мехнер). -- `PR/` (github.com/NagyD/PR, GPLv2) — Princed Resources. -- `mininim/` — независимая реализация. - -Все эти папки — **справочник логики/структур/констант и источник ассетов**, -но НЕ код для копирования (лицензии несовместимы, наш ABI другой): читаем -и переписываем под наш движок, а не вставляем куски. - -## Ассеты - -Готовые распакованные VGA-256 ассеты (то, что нужно под 320×256×256) — -`SDLPoP/data/` (`res.png`/`.pal`/`.bin`). Брать оттуда, а НЕ писать свой -декодер DOS `.DAT`. Локальные `.DAT` — в `MSDOS/`. - -**Каноническая спецификация форматов `.DAT` — `docs/POP-DAT-FormatSpecifications.pdf`** -(грепаемая копия — `docs/POP-DAT-FormatSpecifications.txt`): первоисточник -Princed для DAT v1.0 (контейнер/индекс/чек-сумма, кодеки RLE/LZG, палитры, -формат уровней, звук), на нём построены и SDLPoP, и Princed Resources. Наши -разборы (`docs/MSDOS_RESOURCE_FORMAT.md` / `docs/APPLEII_RESOURCE_FORMAT.md` / -`docs/README.md`) — практические заметки/сверки; при расхождении источник -истины — спецификация. Формат уровня почти идентичен в Apple II и DOS. - -Упаковка ассетов под Sprinter (атласы `.atl`, палитра) — python-скрипты в -`toolchain/` (`render_room.py`, `pop_pack_bg.py`, `pop_pack_kid.py`, -`pop_extract_kid_data.py`). Их дёргают Makefile'ы тестов. - -## Структура папки - -- `docs/` — планы и форматы; **индекс с отметками актуальности — - `docs/README.md`**, начинать чтение оттуда. Ключевое: - `levels_plan.md` (следующий этап), `layout_plan_v2.md` (раскладка кода по - окнам/банкам + скорость отрисовки), `PORT_PLAN.md` (карта фаз со - статусами). -- `roomtest/` — **активная разработка**: уровень 1 целиком (Kid, стражи, - ловушки, ворота, loose-полы). Свой `CLAUDE.md`; текущие задачи — - `roomtest/TASKS_OPEN.md` (закрытые с протоколами — - `roomtest/TASKS_CLOSED.md`), открытые баги — `roomtest/BUGS_OPEN.md`, - закрытые с разбором корней — `roomtest/BUGS_CLOSED.md`. -- `poc/` — ранний proof-of-concept (снег/атлас/kbd_raw); ассеты в `poc/res/`. -- `bgtest/`, `coltest/` — отдельные проверки фона/коллизии. -- `toolchain/` — python-упаковщики ассетов + эталонные PNG (`1.1-2.png`). -- `SDLPoP/`, `PR/`, `Prince-of-Persia-Apple-II/`, `mininim/`, `MSDOS/` — - референсы/оригинальные данные (см. выше). - -## Ключевые архитектурные решения (memory/) - -- `pop_port_project` — общий статус порта. -- `pop_banking_architecture` — будущее: big+BANK_W1, графику нельзя в W3, - один файл = один банк = прямые вызовы, main резидентен. -- `pop_background_strategy` — фон = композиция тайлов в рантайме (вариант 3). -- `pop_kid_plan` / `pop_hang_state` / `pop_fore_layer` / - `pop_fall_debug_baseline` — этапы Kid. -- `kbd_raw_fifo_drain` — held-state клавиатуры (единственный принципиальный - пробел движка, закрыт ``): вычерпывать FIFO SIO циклом. -- `png_strip_padding_tradeoff`, `pop_tile_atlas_palette_merge` — квирки - упаковки ассетов. diff --git a/applications/PoP/LICENSE b/applications/PoP/LICENSE deleted file mode 100644 index a738358..0000000 --- a/applications/PoP/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Sprinter C Compiler contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/applications/PoP/README.md b/applications/PoP/README.md deleted file mode 100644 index 07ba5b8..0000000 --- a/applications/PoP/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Prince of Persia на ZX Sprinter - -Порт Prince of Persia на компьютер Sprinter Sp2000 поверх нашего -target-слоя SDCC (sprinter-cc / libc / libbgi). Цель — 320×256×256 (режим -0x81), VGA-256 ассеты оригинала переносятся почти впрямую. - -**Состояние (2026-08-01): играется весь уровень 1** — комнаты и переходы, -Kid со всем набором действий, ловушки, ворота, дверь уровня, меч и бой, -стражи с ИИ, HP и зелья. Нет: перехода на следующий уровень, звука, -таймера/HUD, сохранений. - -- Что в работе прямо сейчас — [`roomtest/TASKS_OPEN.md`](roomtest/TASKS_OPEN.md). -- Следующий этап (уровни 2+) — [`docs/levels_plan.md`](docs/levels_plan.md). -- Общий план и статус фаз — [`docs/PORT_PLAN.md`](docs/PORT_PLAN.md). -- Правила работы для ИИ-сессий — [`CLAUDE.md`](CLAUDE.md). - -## Что где - -| Папка | Назначение | -|-------|-----------| -| `roomtest/` | **Активная разработка.** Уровень 1 целиком: фон композицией тайлов, Kid (seqtbl-анимация, ввод, коллизия, падение, зацеп, окклюзия), ловушки, ворота, стражи, бой. Свой README/CLAUDE/TASKS. | -| `docs/` | Планы и разбор форматов ресурсов Apple II / DOS — см. индекс в [`docs/README.md`](docs/README.md). | -| `toolchain/` | Python-упаковщики ассетов под Sprinter (рендер комнат, атласы тайлов/Kid, извлечение данных анимации) + эталонные скриншоты. | -| `poc/` | Ранний proof-of-concept (снег, атлас, raw-клавиатура). Ассеты в `poc/res/`. | -| `bgtest/`, `coltest/` | Точечные проверки фона и коллизии. | -| `SDLPoP/`, `PR/`, `mininim/` | Референсные реализации движка (GPL) — читаем логику/константы, НЕ копируем код. `SDLPoP/data/` — источник распакованных VGA-ассетов. | -| `Prince-of-Persia-Apple-II/` | Оригинальный 6502-исходник 1989 г. | -| `MSDOS/` | Локальные `.DAT`-ресурсы DOS-версии. | - -## Референсы = только справочник - -`SDLPoP/`, `PR/`, `mininim/`, `Prince-of-Persia-Apple-II/` используются как -справочник структур/логики и как источник готовых ассетов — их код НЕ -копируется в наш порт (лицензии несовместимы, ABI другой). Любая механика -сверяется с `SDLPoP/src/` **до** реализации. - -## Форматы ресурсов - -Формат уровня почти идентичен в Apple II и DOS (2304 / 2305 байт, -`blueprnt`). Графика различается принципиально, но брать распакованные PNG -из `SDLPoP/data/` практичнее, чем декодировать сырой `.DAT`. Подробности — -[`docs/README.md`](docs/README.md). diff --git a/applications/PoP/bgtest/Makefile b/applications/PoP/bgtest/Makefile deleted file mode 100644 index 505393f..0000000 --- a/applications/PoP/bgtest/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# bgtest — мини-тест атласов статического фона PoP (Шаг 2, до порта room.c). -# Проверяет загрузку .atl + палитру + прямую адресацию + прозрачность. -# -# --memory huge (как poc): atlas_load/gfx_blit трогают W3; huge кладёт -# CODE в W1, DATA/BSS в W2 — без банков. --gfx 256 подлинкует bgi256. -# -# Данные фона генерит toolchain/pop_pack_bg.py в ../poc/res/bg/. - -EXAMPLE := bgtest -MEMORY ?= huge -EXTRA_FLAGS ?= --gfx 256 - -BG_DIR := $(CURDIR)/../poc/res/bg -BG_DATA := $(BG_DIR)/pop_env0.atl $(BG_DIR)/pop_env1.atl $(BG_DIR)/pop_env2.atl \ - $(BG_DIR)/pop_env3.atl $(BG_DIR)/pop_env4.atl \ - $(BG_DIR)/pop_wall.atl $(BG_DIR)/pop_fore.atl $(BG_DIR)/pop_bg.pal - -EXTRA_DATA := $(BG_DATA) -include $(CURDIR)/../sdk.mk - -# Ассеты фона: пересобрать пакером, если исходники поменялись. -$(BG_DATA): $(POP_ROOT)/toolchain/pop_pack_bg.py \ - $(POP_ROOT)/toolchain/render_room.py - cd $(POP_ROOT)/toolchain && python3 pop_pack_bg.py - -$(EXAMPLE).exe: $(BG_DATA) diff --git a/applications/PoP/bgtest/bgtest.c b/applications/PoP/bgtest/bgtest.c deleted file mode 100644 index 6d76751..0000000 --- a/applications/PoP/bgtest/bgtest.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * bgtest.c — мини-тест атласов статического фона PoP (Шаг 2 порта, ДО - * порта room.c). Проверяет на MAME: загрузку 7 .atl, палитру pop_bg.pal, - * ПРЯМУЮ адресацию (id -> страница/idx без remap-таблиц) и прозрачность - * (индекс 0xFF). Рисует сетку репрезентативных спрайтов на цветной - * заливке — прозрачные области должны показать фон. - * - * Раскладка из toolchain/pop_pack_bg.py (см. pop_bg_atlas.h): - * ENV фон id N -> env[N>>5], idx N&31 ; WALL/FORE idx = id. - */ -#include -#include -#include -#include -#include - -/* Имена файлов — плоская ФС диска (make_disk кладёт по basename). */ -static const char *const ENV_ATL[5] = { - "pop_env0.atl", "pop_env1.atl", "pop_env2.atl", - "pop_env3.atl", "pop_env4.atl" -}; - -static atlas_t env[5]; -static atlas_t wall_a; -static atlas_t fore_a; - -/* Блит одного спрайта ленты idx атласа a в (x,y): страница атласа в W0, - * gfx_blit читает w/h из getimage-заголовка ленты (в W0). */ -static void put(atlas_t *a, unsigned char idx, int x, int y) -{ - const void *img = atlas_image(a, idx); - gfx_w0_map(a->page); - gfx_blit(x, y, img); - gfx_w0_unmap(); -} - -static void put_env(unsigned char id, int x, int y) -{ - put(&env[id >> 5], (unsigned char)(id & 31), x, y); -} - -int main(void) -{ - int i; - - /* Загрузка всех 7 атласов (env0..4 + wall + fore). */ - for (i = 0; i < 5; i++) { - if (atlas_load(&env[i], ENV_ATL[i]) != 0) { - printf("atlas_load %s failed\n", ENV_ATL[i]); - return 1; - } - } - if (atlas_load(&wall_a, "pop_wall.atl") != 0) { puts("wall atl fail"); return 1; } - if (atlas_load(&fore_a, "pop_fore.atl") != 0) { puts("fore atl fail"); return 1; } - - initgraph(); - gfx_set_draw_page(0); - gfx_set_visible_page(0); - if (gfx_pal_fload(0, "pop_bg.pal") < 0) - gfx_pal_fload(0, "a:\\pop_bg.pal"); - /* Свой яркий фон-индекс (вне 0x50..0x6F, занятых графикой) — чтобы - * прозрачные (0xFF) области спрайтов были ЯВНО видны. */ -#define BG_IDX 0x20 - gfx_pal_set(0, BG_IDX, 120, 0, 90); /* r,g,b — тёмно-пурпурный */ - gfx_pal_sync(); - - setfillstyle(SOLID_FILL, BG_IDX); - bar(0, 0, 319, 255); - setcolor(WHITE); - outtextxy(2, 2, "PoP bg atlas test"); - - /* Прозрачный блит: банк не пишет 0xFF (фон проступает). */ - gfx_set_bank(GFX_BANK_TRANSPARENT); - - /* --- Стены (chtab_7): нижние грани 7/9/5/3, основные 8/10/6/4 --- */ - put(&wall_a, 3, 4, 20); put(&wall_a, 5, 40, 20); - put(&wall_a, 7, 76, 20); put(&wall_a, 9, 112, 20); - put(&wall_a, 4, 4, 90); put(&wall_a, 6, 40, 90); - put(&wall_a, 8, 76, 90); put(&wall_a, 10, 112, 90); - /* декали-марки стен 14..17 */ - put(&wall_a, 14, 150, 20); put(&wall_a, 15, 168, 20); - put(&wall_a, 16, 186, 20); put(&wall_a, 17, 204, 20); - - /* --- Столб: база 92, боковая грань 93, фронт 95 (fore) --- */ - put_env(92, 4, 160); - put_env(93, 40, 160); - put(&fore_a, 95, 76, 160); - - /* --- Пол: база 41, правый треуг. 42, низ 43; силуэт 44/45 --- */ - put_env(41, 150, 120); - put_env(42, 190, 120); - put_env(44, 230, 120); - put_env(45, 260, 120); - - /* --- Решётка-окно 126, дебрис 97/98, слайсы ворот 52/53 --- */ - put_env(126, 150, 160); - put_env(97, 190, 160); - put_env(52, 230, 160); - put_env(53, 250, 160); - - gfx_set_bank(GFX_BANK_NORMAL); - /* Держим картинку на экране до нажатия (не полагаемся на блокирующий - * getch — в автотесте клавиш нет, крутимся до таймаута MAME). */ - while (!kbhit()) - gfx_wait_vsync(); - - closegraph(); - for (i = 0; i < 5; i++) atlas_free(&env[i]); - atlas_free(&wall_a); - atlas_free(&fore_a); - return 0; -} diff --git a/applications/PoP/coltest/Makefile b/applications/PoP/coltest/Makefile deleted file mode 100644 index eb55155..0000000 --- a/applications/PoP/coltest/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# coltest — прототип вертикального accel-copy (gfx_blit_cols) + флип. -EXAMPLE := coltest -MEMORY ?= huge -EXTRA_FLAGS ?= --gfx 256 -include $(CURDIR)/../sdk.mk diff --git a/applications/PoP/coltest/coltest.c b/applications/PoP/coltest/coltest.c deleted file mode 100644 index 87a6ce6..0000000 --- a/applications/PoP/coltest/coltest.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * coltest.c — прототип K2a: проверка вертикального accel-COPY - * (gfx_blit_cols) + прозрачности 0xFF + горизонтального флипа. - * - * Спрайт 16x24 column-major, АСИММЕТРИЧНЫЙ: - * левые 8 колонок: верх (row<12) = ПРОЗРАЧНО (0xFF), низ = БЕЛЫЙ (1) - * правые 8 колонок: КРАСНЫЙ (2) - * На синем фоне (3). Ожидаем на MAME: - * normal @ (50,100): слева бело-снизу+прозрачно-сверху, справа красный; - * flip @(120,100): ЗЕРКАЛО — слева красный, справа бело+прозрачно-сверху; - * в прозрачных местах виден СИНИЙ фон. - */ -#include -#include -#include - -#define W 16 -#define H 24 -static unsigned char spr[4 + W * H]; - -int main(void) -{ - int col, row; - - spr[0] = W; spr[1] = 0; - spr[2] = H; spr[3] = 0; - for (col = 0; col < W; col++) - for (row = 0; row < H; row++) { - /* ФИНАЛ: АСИММЕТРИЧНЫЙ — лево верх прозрачно / низ белый, - * право красное. normal: лево бело+прозрач-верх, право красн; - * flip: ЗЕРКАЛО (лево красн, право бело+прозрач-верх). */ - unsigned char v; - if (col < 8) - v = (row < 12) ? 0xFF : 1; - else - v = 2; - spr[4 + col * H + row] = v; - } - - initgraph(); - gfx_set_draw_page(0); - gfx_set_visible_page(0); - gfx_pal_set(0, 1, 255, 255, 255); /* белый */ - gfx_pal_set(0, 2, 224, 32, 32); /* красный */ - gfx_pal_set(0, 3, 32, 48, 200); /* синий фон */ - gfx_pal_set(0, 255, 0, 224, 0); /* ДИАГ: 0xFF как ЗЕЛЁНЫЙ цвет (bank NORMAL) */ - gfx_pal_sync(); - - setfillstyle(SOLID_FILL, 3); - bar(0, 0, 319, 255); - setcolor(1); - outtextxy(40, 80, "normal"); - outtextxy(110, 80, "flip"); - - gfx_set_bank(GFX_BANK_TRANSPARENT); /* ДИАГ: 0x58 подавление 0xFF (с тенью) */ - gfx_blit_cols(50, 100, spr, 0); /* обычный */ - gfx_blit_cols(120, 100, spr, 1); /* зеркало */ - gfx_set_bank(GFX_BANK_NORMAL); - - while (!kbhit()) - gfx_wait_vsync(); - closegraph(); - return 0; -} diff --git a/applications/PoP/docs/APPLEII_RESOURCE_FORMAT.md b/applications/PoP/docs/APPLEII_RESOURCE_FORMAT.md deleted file mode 100644 index a08fed1..0000000 --- a/applications/PoP/docs/APPLEII_RESOURCE_FORMAT.md +++ /dev/null @@ -1,283 +0,0 @@ -# Формат ресурсов 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/PoP/docs/KID_PLAN.md b/applications/PoP/docs/KID_PLAN.md deleted file mode 100644 index 42771ac..0000000 --- a/applications/PoP/docs/KID_PLAN.md +++ /dev/null @@ -1,214 +0,0 @@ -# Prince of Persia — Kid (персонаж): анализ и план - -> **Статус: РЕАЛИЗОВАНО (2026-08-01).** Kid играется целиком: интерпретатор -> `seqtbl` + `frame_table` (`roomtest/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`, объём спрайтов) — он нужен -> при портировании остальных акторов (скелет, тень, визирь). Текущие -> задачи — `../roomtest/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/PoP/roomtest`, см. -`memory/pop_background_strategy`) — Kid развиваем в том же `roomtest` как -новый 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) в roomtest поверх фона на верном - тайле — проверить палитру/позицию/прозрачность на 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/roomtest). diff --git a/applications/PoP/docs/MSDOS_RESOURCE_FORMAT.md b/applications/PoP/docs/MSDOS_RESOURCE_FORMAT.md deleted file mode 100644 index 580e92b..0000000 --- a/applications/PoP/docs/MSDOS_RESOURCE_FORMAT.md +++ /dev/null @@ -1,301 +0,0 @@ -# Формат ресурсов 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/PoP/docs/POP-DAT-FormatSpecifications.pdf b/applications/PoP/docs/POP-DAT-FormatSpecifications.pdf deleted file mode 100644 index 0d5c219..0000000 Binary files a/applications/PoP/docs/POP-DAT-FormatSpecifications.pdf and /dev/null differ diff --git a/applications/PoP/docs/POP-DAT-FormatSpecifications.txt b/applications/PoP/docs/POP-DAT-FormatSpecifications.txt deleted file mode 100644 index c2aa09a..0000000 --- a/applications/PoP/docs/POP-DAT-FormatSpecifications.txt +++ /dev/null @@ -1,2345 +0,0 @@ -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/PoP/docs/PORT_PLAN.md b/applications/PoP/docs/PORT_PLAN.md deleted file mode 100644 index 525f333..0000000 --- a/applications/PoP/docs/PORT_PLAN.md +++ /dev/null @@ -1,555 +0,0 @@ -# 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+стрелка; диагноз и план в `../roomtest/TASKS_CLOSED.md` (KBD-1) | -| §3 форматы данных | актуально; уровень читается живьём (`roomtest/pop_level.c`) | -| §4 стратегия фона | **сделано** — тайловый рендерер в рантайме (`roomtest/pop_bg.c`) | -| §5 PoC | **закрыт и превзойдён.** `poc/` (плейсхолдер-персонаж) — история; активная разработка ушла в `roomtest/` с настоящей графикой | -| §6 модель движения | **сделано**: `play_seq` + `frame_table` оригинала, не физика с нуля | -| §7 фазы | см. отметки статуса прямо в разделе | -| §8 риски | п.1 закрыт, п.3 закрыт (28 страниц-атласов Кида), п.2/п.4 — см. отметки в разделе | -| §10 режим памяти | **сделано и переросло план**: `huge` + четыре банка кода; актуальная раскладка — `layout_plan_v2.md` | - -**Где смотреть текущее состояние, а не план:** `../roomtest/README.md` -(что играется), `../roomtest/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 работает. -> Всё, что ниже про плейсхолдер-персонажа и приблизительную дугу прыжка, -> — уже неправда для активной ветки: в `roomtest/` стоит настоящая графика -> Кида и авторские таблицы кадров (§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` - кладётся на образ как есть и читается по офсетам в рантайме - (`roomtest/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 в -`../roomtest/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+стрелка: `../roomtest/TASKS_CLOSED.md`, KBD-1. -2. **Бюджет кадра** — риск подтвердился, но не в том виде, в каком ожидался: - спрайтовый движок для персонажей не используется, поэтому лимит - «~21 спрайт/кадр» неприменим. Реальный бюджет упирается в heal+блиты и - перерисовку тайлов; замер 2026-07-30 — типичный кадр ~371 К тактов - (~86 % периода). Инструмент замера уже в коде: полосы бордюра `PROF()` - в `roomtest.c`. План выжимания — `../roomtest/TASKS_CLOSED.md` (CLIP-1) и - `../roomtest/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/с)**. У нас (`roomtest.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/PoP/docs/README.md b/applications/PoP/docs/README.md deleted file mode 100644 index 2d9d46d..0000000 --- a/applications/PoP/docs/README.md +++ /dev/null @@ -1,143 +0,0 @@ -# `applications/PoP/docs` — индекс + сводка по форматам ресурсов - -## Индекс документов (актуальность на 2026-08-22) - -**Живые планы — читать перед работой:** - -| Документ | О чём | -|----------|-------| -| [`../roomtest/TASKS_OPEN.md`](../roomtest/TASKS_OPEN.md) | **Что берётся в работу сейчас** (не в этой папке, но входная точка) | -| [`../roomtest/BUGS_OPEN.md`](../roomtest/BUGS_OPEN.md) | Открытые баги roomtest (закрытые — в `BUGS_CLOSED.md` рядом) | -| [`impl_diff.md`](impl_diff.md) | **Осознанные расхождения с SDLPoP**: где мы сделали не дословно и почему | -| [`perf_l13_room23.md`](perf_l13_room23.md) | **Сцена и метод замера кадра** (каскад плит, ур.13 к.23): как воспроизвести, зонды, канал `clog`, сводка по кадрам, габариты спрайтов и ответ про `uint8_t`. 2026-08-17 | -| [`perf_green_phase.md`](perf_green_phase.md) | **ЗЕЛЁНАЯ фаза (слой фона)**: раскладка тактов, способы ускорения (G1..G6), журнал правок — рабочий документ между сессиями. 2026-08-17 | -| [`perf_cyan_phase.md`](perf_cyan_phase.md) | **ЦИАН фаза (персонажи + передний слой)**: раскладка тактов, способы ускорения (C1..C7), журнал правок — рабочий документ между сессиями. 2026-08-17 | -| [`perf_backlog.md`](perf_backlog.md) | Отложенная оптимизация отрисовки с замерами 2026-08-10 + **как мерить** (wait-state'ы, границы кадра). Позиции 1–7 переехали в фазовые документы выше | -| [`quicksave_plan.md`](quicksave_plan.md) | **QuickSave/QuickLoad** (✅ реализовано, F6/F9, POP.SAV+BAK): разбор (это enhancement SDLPoP, в оригинале 1989 его НЕТ), инвентаризация нашего состояния, формат снимка 'POPQ' v3, шаги QS1..QS6. Справочник. 2026-08-22 | -| [`full_game_plan.md`](full_game_plan.md) | **Полноценная игра**: app state machine, title/intro, demo level 0, таймер, cutscenes, уровни 1..14, ending и Hall of Fame. Уровень 15 исключён. 2026-08-21 | -| [`menu_settings_plan.md`](menu_settings_plan.md) | **Pause menu и Settings**: QuickSave/QuickLoad в основном menu, `POP.CFG`, один `POP.SAV` + `POP.BAK`, текущий VANILLA и задел под ENHANCED; §10 — выбор UI-рендера (текстовые строки + свой растровый рендерер, референс SDLPoP: два шрифта), restart без подтверждения. 2026-08-22 | -| [`palette_plan.md`](palette_plan.md) | **Палитры и fade**: карта всех 256 слотов (kid.pal/title/story, тайлсеты dungeon/palace, стражи), механика fade (4 ступени vs ~64 у SDLPoP), план модуля `pop_pal.c` (API load/apply/black) + переход уровня через fade. 2026-08-23 | -| [`status_line_text.md`](status_line_text.md) | **Строка HP как статус-строка**: полная инвентаризация ВСЕХ текстов SDLPoP в `rect_bottom_text` (геометрия, семантика `text_time_total`, мигание, рестарт по истечении) + что из этого уже есть у нас. 2026-08-25 | -| [`levels_plan.md`](levels_plan.md) | Следующий этап: уровни 2+, второй тайлсет, читы SDLPoP | -| [`levels_12_15_plan.md`](levels_12_15_plan.md) | **Уровни 12/13** (тень, Джафар, падающие плиты) + что такое 14/15 и 0. 2026-08-13 | -| [`midtable_analysis.md`](midtable_analysis.md) | **Слои отрисовки**: как устроены back/mid/fore и objtable в оригинале, чего стоит порт, развилки. 2026-08-13 | -| [`roomnav_skip.md`](roomnav_skip.md) | **Комнаты для отладочного телепорта**: какие пропускать и почему (посчитано по данным уровней). 2026-08-13 | -| [`layout_plan_v2.md`](layout_plan_v2.md) | Раскладка кода по окнам/банкам/страницам + замеры скорости отрисовки | -| [`room_model_plan.md`](room_model_plan.md) | `kid_room ≠ drawn_room` (straddle): сделан S1, остальное впереди | -| [`host_tests_plan.md`](host_tests_plan.md) | Модульные тесты движка под ucsim_z80: два шва, регрессии из `BUGS_CLOSED.md`, дифф против SDLPoP | -| [`shadow_render.md`](shadow_render.md) | **Вид Тени (OR+XOR)** — отложено: почему XOR несовместим с прозрачностью `#FF`, замер подготовки источника, четыре варианта | -| [`ideas_backlog.md`](ideas_backlog.md) | Осознанно отложенные гипотезы (мышь, PRNG) | -| [`prng_alternatives.md`](prng_alternatives.md) | Запасные генераторы, если упрёмся в бюджет кадра | - -**Исполненные планы, оставленные как справочники:** - -| Документ | Чем ещё полезен | -|----------|-----------------| -| [`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) | Раскладка объектов уровня 1 по комнатам, декод `LINKLOC`/`LINKMAP`, точные ссылки на seg-код | - -**Форматы ресурсов** (ниже по этому файлу): `POP-DAT-FormatSpecifications.pdf` -/ `.txt` (первоисточник), `APPLEII_RESOURCE_FORMAT.md`, -`MSDOS_RESOURCE_FORMAT.md`. - -Удалены 2026-08-01 как полностью исполненные и перекрытые кодом: -`clip_char_plan.md`, `double_buffer_plan.md`, `loose_floors_plan.md`, -`size_optimization_plan.md` (его §8 про скорость отрисовки перенесён в -`layout_plan_v2.md` §9). Ищутся в истории git, если понадобятся. - ---- - -## Форматы ресурсов — сводка - -**Каноническая спецификация форматов** — `POP-DAT-FormatSpecifications.pdf` -(+ текстовая конверсия `POP-DAT-FormatSpecifications.txt` для grep/цитирования): -*«Prince of Persia — Specifications of File Formats»*, Princed Development Team, -2008. Это первоисточник формата `DAT v1.0` (контейнер, индекс, чек-сумма, -кодеки RLE/LZG, палитры, уровни, звук), на котором построены и SDLPoP, и -Princed Resources. Документы ниже — наши практические заметки/сверки; при -расхождении источником истины считать спецификацию. - -Цель этих документов — подготовить почву для будущего порта Prince of Persia -на ZX Sprinter, разобрав, как устроены ресурсы игры в двух доступных нам -версиях: - -- [`APPLEII_RESOURCE_FORMAT.md`](./APPLEII_RESOURCE_FORMAT.md) — формат - уровней и графики по официально опубликованным исходникам 1989 года - (6502-ассемблер). Уверенность высокая везде — восстановлено прямым чтением - кода движка, а не догадками. -- [`MSDOS_RESOURCE_FORMAT.md`](./MSDOS_RESOURCE_FORMAT.md) — формат `.DAT` - ресурсов DOS-версии (исходников нет). Восстановлено эмпирически (разбор - байтов + перепроверка скриптами) и сверено с документацией открытых - сторонних инструментов (SDLPoP, Princed Resources). - -## Главный вывод - -**Формат уровня практически идентичен в обеих версиях**: Apple II `LEVELn` -занимает ровно 2304 байта (структура `blueprnt` — тайлы, связи -плит/дверей, граф экранов, метаданные старта Кида/стражников), а запись -уровня в DOS `levels.dat` занимает 2305 байт с байтовыми значениями тайлов -того же диапазона. То есть Джордан Мехнер перенёс формат карты уровня в -DOS-порт практически без изменений (+1 байт, вероятно контрольная сумма от -DOS-упаковщика). Это значит: раскладку `BLUETYPE`/`BLUESPEC`/`LINKLOC`/ -`LINKMAP`/`MAP`/`INFO`, задокументированную по Apple II исходникам, можно -применять напрямую и к DOS `levels.dat`. - -Формат же **графики отличается принципиально**: на Apple II это простой -несжатый rowbyte-формат hi-res экрана с плоской таблицей указателей; в DOS — -контейнер с оглавлением ресурсов (id/size/offset), с отдельными вариантами -под CGA/EGA/VGA — точный кодек пикселей внутри сырого `.DAT`-чанка не -восстановлен ни для той, ни для другой версии до конца. **Но для DOS-графики -это не блокирует работу**: в репозитории github.com/NagyD/SDLPoP (папка -`data/`) уже лежат готовые распакованные PNG для каждого спрайта/фона -(включая VGA-256-цветный вариант `VPALACE`/`VDUNGEON` — то, что нужно под -320×256×256 Sprinter), см. §5 `MSDOS_RESOURCE_FORMAT.md`. Это другой -релиз/сборка данных, чем наш локальный `MSDOS/` (некоторые звуковые `.dat` -отличаются по размеру), но нумерация ресурсов и формат контейнера — те же, -что подтверждено побайтовой сверкой уровня `res2001.bin`. - -## Общий контейнерный формат DOS `.DAT` (кратко) - -``` -[0x00] u32 LE tableOffset — смещение начала таблицы оглавления -[0x04] u16 LE tableSize — размер таблицы оглавления -[0x06..tableOffset) — данные ресурсов (конкатенация чанков) -[tableOffset..tableOffset+tableSize) - — массив записей по 8 байт: - u16 size, u16 id, u16 offset(абсолютный), u16 reserved(=0) -``` - -Инвариант `tableOffset + tableSize == размер файла` подтверждён на всех 28 -`.dat`-файлах в `MSDOS/`, и независимо — именованием файлов `res.*` в -`data/` репозитория SDLPoP. - -## Готовые ассеты для порта (важно для практической работы) - -`github.com/NagyD/SDLPoP/tree/master/data` содержит не только код движка, но -и сами ресурсы игры — как сырые `.DAT`, так и распакованные поштучно файлы -(`res.png` для спрайтов/фонов, `res.pal` для палитр, `res.bin` -для уровней). Для арт-ассетов (в т.ч. нужного полноцветного VGA-варианта -дворца/подземелий) практичнее взять их оттуда напрямую, чем писать свой -декодер сжатия пикселей DOS `.DAT`. - -## Что дальше по форматам (не сделано и пока не нужно) - -Порт читает уровень напрямую из `res200N.bin` (`roomtest/pop_level.c`), а -графику берёт из распакованных PNG `SDLPoP/data/` — поэтому ни один пункт -ниже сейчас не блокирует работу. - -1. Точный кодек сжатия пикселей спрайтов в сыром DOS `.DAT` (нужен только - если понадобится читать именно нашу локальную копию `MSDOS/*.dat` - "как есть", а не ассеты из SDLPoP `data/`) — сверка с исходником SDLPoP, - `src/seg009.c`. -2. Семантика служебных полей `digisnd*.dat`/`ibm_snd*.dat` перед сырыми - сэмплами/нотами (частично прояснено документацией Princed Resources — - PC speaker: 1 байт заголовка + повторяющиеся тройки байт "2 байта частоты - + 1 байт длительности"; WAV: 8 бит, моно, unsigned, 11025 Гц). -3. Назначение бит `secmask` в `BLUETYPE` (Apple II) и служебного блока - `id=2000` в начале DOS `levels.dat` (16 байт в нашей копии, но 2305 байт - в версии SDLPoP — расхождение между релизами, не разобрано). -4. Оценка, какие видеорежимы/цветовые палитры ZX Sprinter реалистично - покрывают исходную графику (CGA/EGA/VGA варианты в DOS-ресурсах против - hi-res Apple II) — отдельная архитектурная задача порта, не формат - ресурсов как таковой. diff --git a/applications/PoP/docs/frame_pacing_plan.md b/applications/PoP/docs/frame_pacing_plan.md deleted file mode 100644 index 38a1669..0000000 --- a/applications/PoP/docs/frame_pacing_plan.md +++ /dev/null @@ -1,603 +0,0 @@ -# Фиксированный логический кадр — разбор перед реализацией - -Дата разбора: 2026-08-19. Отправная точка — тег `0.0.1-prealpha`. -Статус: **АНАЛИЗ, кода не трогали.** - -Задача пользователя: перевести логический кадр на фиксированный размер, -не зависящий от длительности синей/зелёной/циан фаз. Инструмент — -`gfx_set_fps_div`, режимы FASTEST / FAST / NORMAL. - - -## 1. Что на самом деле меняется - -Сейчас главный цикл (`roomtest.c`) после отрисовки ждёт **три** -`gfx_wait_vsync()` подряд. Каждый ждёт ближайший фронт луча, поэтому -период логического кадра равен - - период = ceil(W) + 2 растровых кадра, W = работа в растрах - -Первое ожидание доедает хвост текущего растра, два следующих — целые -растры. Отсюда наблюдаемое: `W <= 1` → период 3; `W = 1.1` → период -уже 4. То есть **реальный бюджет логического кадра сегодня — один -растр (430 080 тактов)**, всё сверх него стоит целого лишнего растра. - -Нужное поведение: - - период = max(n, ceil(W)) - -При `n = 3` бюджет становится **1 290 240 тактов** — втрое больше. -Замеренный максимум работы на 13/23 (911 862) укладывается туда с -запасом, а 11/15 (437 484 лёгкая / ~603 000 тяжёлая позиция) — вдвойне. - -Это главный выигрыш, и он не про скорость игры, а про **исчезновение -скачков**: сегодня превышение растра на один такт стоит +33 % к периоду. - - -## 2. Три режима и их привязка к оригиналу - -Растровый кадр Sprinter: 320 строк × 896 пикселей при 14 МГц = -**20,48 мс** (48,83 Гц). В тактах CPU (21 МГц) — **430 080**; -замерено на холостом DSS: 430 131 на прерывание. - -Оригинал (`SDLPoP/src/seg003.c:363`): `BASE_FPS = 60`, -`base_speed = 5` тика = **83,3 мс**, `fight_speed = 6` = **100 мс**. - -| режим | обычно | в бою | мс обычно | мс в бою | к оригиналу | -|---|---:|---:|---:|---:|---| -| FASTEST | 3 | 3 | 61,4 | 61,4 | +36 % скорости | -| FAST | 3 | 4 | 61,4 | 81,9 | +36 % / точно | -| NORMAL | 4 | 5 | 81,9 | 102,4 | точно (83,3 / 100) | - -NORMAL воспроизводит оригинал с точностью 1,7 % и 2,4 % — расхождение -только из-за 48,83 Гц против 60 Гц, целыми делителями точнее не выйдет. - -**Условие «бой» берём у оригинала буквально** — оно проще, чем кажется: - -```c -if (Kid.sword == sword_2_drawn) set_timer_length(timer_1, fight_speed); -else set_timer_length(timer_1, base_speed); -``` - -Это не «идёт бой» и не «есть страж рядом», а **только «у Кида вынут -меч»**, и проверяется в самом верху главного цикла, до `play_frame()`. -У нас поле есть (`Kid.sword`, `SWORD_2_DRAWN` — `guards.c`), так что -переключение — одна строка в том же месте цикла. - -Это закрывает и давнюю задачу **L1-SPEED** (`TASKS_OPEN.md`): сейчас мы -идём на 60 мс вместо 83,3 — примерно на 39 % быстрее эталона. - - -## 3. Как устроен темп сейчас и что мешает - -`gfx_wait_vsync()` имеет две ветки (`libbgi/common/gfx_wait_vsync.c`): - -- **`_gfx_fps_div <= 1`** — лучевой поллинг бита 5 порта `0xFE` в тесном - цикле, и в этом цикле зовётся **idle-хук** (`gfx_set_idle_hook`). - roomtest вешает туда `kbd_raw_poll` — это единственное, что делает - клавиатуру работоспособной (задача KBD-1: приёмный FIFO SIO 3 байта, - импульс запроса прерывания живёт 32 такта и теряется в DI-окнах - акселератора; лечится только плотным опросом раз в ~0,5 мс). -- **`_gfx_fps_div >= 2`** — счётчиковый путь: ждёт, пока фоновый ISR - (`_gfx_frame_isr`, слот кадровой цепочки) насчитает `n` фронтов, а - ожидание реализовано через **`ei; halt`**. - -**И вот здесь блокер.** На счётчиковом пути idle-хук не зовётся вообще. -Включив `gfx_set_fps_div(3)` как есть, мы немедленно возвращаем KBD-1: -теряются нажатия при зажатом Shift, залипают клавиши. Это не мелочь и -не «потом поправим» — это единственная причина, по которой клавиатура -сейчас вообще работает. - - -## 4. Что измерено (MAME, 2026-08-19) - -### Метод и его границы - -Прерывания считались брейкпоинтами на резидентных адресах трамплина -(`_irq_tramp` = 0x88B4, ветка кадрового пути `tr_frame` = 0x8995). - -**Счёт попаданий брейкпоинтом на этом драйвере недостоверен**: sprinter -дёргает `Z80_INPUT_LINE_WAIT` (`do_mem_wait`), инструкция пересчитывается, -и один и тот же PC срабатывает по нескольку раз. Наблюдалось -«попаданий больше, чем растровых кадров» и «попаданий в `tr_frame` -больше, чем входов в трамплин» — логически невозможные результаты. - -Достоверен только **детектор разрыва**: брейкпоинт на следующей -инструкции пишет `temp3 = totalcycles`, брейкпоинт с условием -`(totalcycles - temp3) > 0x9D800` (1,5 растра) останавливает машину. -Дубли попаданий его не портят. Все числа ниже — этим методом. - -Отдельная грабля: **литералы в отладчике MAME шестнадцатеричные**. -Первый прогон с порогом «700000» на деле проверял 0x700000 = 17 растров -и не срабатывал никогда. - -### Факты - -1. **Холостой DSS** — 799 прерываний на 343 674 880 тактов = 430 131 на - прерывание. Подтверждает константу растра и что потерь нет, когда - нечего рисовать. - -2. **11/15, покой (чомпер + два факела + страж)** — потери ЕСТЬ: - разрыв ровно 860 129 тактов = два растра = одно потерянное - прерывание. Частота в «плохой» фазе: 12 потерь на 427 растровых - кадров = **2,8 %**; повтор — 12 на 495 (2,4 %) при бегущем Киде. - -3. **Та же сцена после сдвига Кида** (`]`/`[`, попиксельно) — - **0 потерь на 3919 кадров**, и после возврата обратно **0 на 4010**. - -4. **Полная перерисовка комнаты** (переход/`+`) — разрыв 1 720 289 = - ровно четыре растра = **три потерянных прерывания подряд**. - -5. **13/23** — ни в покое, ни при беге потерь не поймано; на смене - комнаты — поймано. - -6. **Клавиатура ни при чём**: с удержанной клавишей 3,1 %, без неё - 2,8 % — в пределах разброса. - -### Как это читать - -Пункты 2 и 3 вместе — самое важное. Одна и та же сцена даёт то 2,8 %, -то ноль. Значит потеря определяется **не нагрузкой, а фазой**: попадает -ли момент кадрового прерывания внутрь DI-окна блита. - -Механизм подтверждён исходником MAME (`sprinter.cpp`): - -- `irq_on` поднимает линию и заводит `irq_off_timer` на **32 такта - неразогнанного клока (3,5 МГц) = 9,14 мкс**; `irq_off` гасит. Ядро - z80 в MAME уровневое (`m_irq_state` без защёлки) — импульс, пришедший - под `di`, теряется НАСОВСЕМ. Это же поведение у настоящего Spectrum - (INT 32 такта), так что это не эмуляторный артефакт. -- DI-окно у нас — один вызов `_bgi_blit_rows_raw`, а он по контракту - режется вызывающим на **чанки ≤16 строк** (≈6 200 тактов ≈ 0,29 мс). - То есть DI-окна короткие и с промежутками, отсюда и «то теряем, то - нет»: всё решает, куда попал 9-микросекундный импульс. -- `irqack_cb` гасит **все три** входа мержера (экран/клавиатура/CBL) - одним подтверждением. Плюс трамплин обслуживает за вход ровно один - источник и делает приватный RETI. Значит кадровое прерывание может - быть съедено клавиатурной веткой или (в будущем) CBL-веткой. - -**Вывод по фазе.** Наш период сейчас 3 растра, но иногда 4 — и каждый -такой случай сдвигает фазу рендера относительно луча. Отсюда «полосы»: -десятки секунд без потерь, потом полоса с потерями. При жёстком -пейсинге период станет РОВНО n, фаза перестанет плавать — и сцена может -**залипнуть в плохой фазе надолго**. Это хуже случайных 3 %: систематическая -потеря по прерыванию на кадр превратит логический кадр из 3 растров в 4, -то есть даст ровные −25 % скорости, которые никак не проявятся в -профиле тактов. - - -## 5. Проблемы по убыванию риска - -| # | проблема | риск | -|---|---|---| -| P1 | счётчиковый путь ждёт через `halt` → idle-хук не зовётся → возврат KBD-1 (потеря нажатий, залипание клавиш) | блокер | -| P2 | счётчик кадров теряет тики (фазозависимо, 0…3 %), при жёстком пейсинге может залипнуть в плохой фазе → ровный минус скорости | высокий | -| P3 | полноэкранная перерисовка (вход в комнату, старт уровня) теряет 3+ тика подряд → счётчик недосчитает, ожидание растянется сильнее самой работы | средний | -| P4 | звук через CBL (`_irq_cbl_hook`, реальный ISR в трамплине): (а) ещё один источник, крадущий кадровые тики приватным RETI; (б) обратно — любое подтверждение гасит ожидающий запрос CBL → underrun (счётчик `cbl_underruns()` уже есть); (в) ISR длинный (полный сейв обоих наборов + вызов в приложение) | средний, растёт | -| P5 | второй call-site `gfx_wait_vsync()` — ветка `frozen` (`roomtest.c:320`) ждёт один фронт; под делителем её смысл меняется | низкий | -| P6 | `IRQ_CHAIN_MAX = 4`; делитель занимает слот, звук — свой; запас есть, но конечный | низкий | -| P7 | бит 5 порта `0xFE` доступен только при включённом `cbl_mode`; сейчас его лениво занимает сам `gfx_wait_vsync` через `_cbl_port_ref`, при открытии реального звука владение переходит к CBL — переход уже спроектирован, но его надо проверить в связке | низкий | - -Отдельно, не проблема а рычаг: по сообщению разработчиков -(IvanMak.txt:846–848) **новая прошивка позволяет акселератору работать с -EI** — по приходу прерывания он отключается, по `RETI` включается. -Если это подтвердится на железе и моделируется в MAME, P2 исчезает -полностью. Проверять отдельно; строить на этом нельзя (неизвестно, какая -прошивка у пользователя). - - -## 6. Варианты реализации - -### A. Включить `gfx_set_fps_div(3)` как есть -Отвергается: P1 (убивает клавиатуру) — сразу, без вариантов. - -### B. Свой `wait` в приложении: счётчик только на рендер, ожидание — лучом -Счётчик кадровых прерываний отвечает на один вопрос — «сколько фронтов -съел рендер», а само ожидание идёт **существующим лучевым поллингом с -idle-хуком**, то есть клавиатура работает ровно как сегодня. - -``` -k = tick - tick_at_frame_start; /* фронтов съел рендер */ -if (k >= n) k = n - 1; /* опоздали — ждём хотя бы один */ -повторить (n - k) раз: ждать фронт луча (поллинг + idle-хук) -tick_at_frame_start = tick; /* якорь на фактическом фронте */ -``` - -Плюсы: минимальная правка, клавиатура нетронута, фаза переякоривается -каждый кадр (ошибка не копится). Минус: остаётся P2 — при залипании в -плохой фазе `k` систематически занижен на 1, и кадр ровно на растр -длиннее. Профилем это не видно. - -### C. Программный счётчик кадров по лучу (без прерываний вообще) -Считать фронты **выборкой бита 5 в точках, которые мы и так проходим**. -Окно бланка (строки 272…319) длится **3,07 мс**, максимальное DI-окно — -0,29 мс, значит достаточно опрашивать чаще, чем раз в 3 мс. - -Точки выборки: `pop_blit_b` (наша обёртка, зовётся на каждый блит — -в фазах рисования это плотнее 0,3 мс) плюс несколько точек в синей фазе -(она 149 106 тактов ≈ 6,9 мс без единого блита, нужно 3-4 точки). - -Цена: `in a,(0xFE)` + проверка бита + дедуп фронта ≈ 40-50 тактов; при -~50 выборках это 2 500 тактов = 0,6 % растра. - -Плюсы: **точно, и точность не зависит ни от DI, ни от звука, ни от -клавиатуры** — снимает P2, P3, P4(а) разом. Минус: заводит инвариант -«между выборками не больше 3 мс», который легко нарушить будущей правкой. -Инвариант проверяем в MAME тем же детектором разрыва. - -### D. CTC как источник кадра -`irq_ctc_install` уже есть (каналы 2+3, вектор 0x06, отдельный от 0xFF). -Запрос CTC **защёлкивается** (daisy chain — в `_irq.h` прямо записано, -что без `RETI` следующего прерывания не будет), поэтому под `di` он не -теряется, а откладывается. Пресет 112 × 160 даёт ровно период кадра -без дрейфа (обе частоты — от одного X_SP). - -Блокер: `irq_ctc_install` вектрится напрямую и требует кода в W2 → -сейчас только tiny/big, а roomtest — **huge**. Нужна W2-копия -CTC-трамплина (в дизайн-доке помечена как follow-up). Плюс защёлка -хранит только ОДИН отложенный запрос — на полноэкранной перерисовке -(P3) всё равно недосчитает. - -### Рекомендация - -**B как первый шаг, C — как способ закрыть P2**, и оба под одним -интерфейсом: приложение зовёт свой `pop_wait_logical_frame(n)`, а чем -внутри считаются кадры — деталь реализации. Тогда B→C не трогает ни -главный цикл, ни режимы. - -D не нужен, пока C справляется, и требует работы в libc (W2-копия -CTC-трамплина) ради того же результата. - - -## 7. Порядок работ с критериями приёмки - -**Ш0. Инструмент.** Скрипт замера потерь кадровых прерываний (детектор -разрыва) — зафиксировать как повторяемую процедуру, он понадобится на -каждом шаге. Критерий: воспроизводит числа §4 на 11/15 и на смене комнаты. - -**Ш1. Свой `wait` (вариант B), делитель ещё не включён.** Вынести -хвост главного цикла в `pop_wait_logical_frame(n)`, поведение при n=3 -должно быть **бит-в-бит прежним** (три фронта после работы). Критерий: -такты по фазам и распределение периода не изменились, клавиатура -работает. - -**Ш2. Счётчик кадров.** Слот кадровой цепочки + `k = tick - anchor`. -Критерий: в 11/15 период стал ровно 3 растра ВСЕГДА (сейчас 3/4/5), а -на 13/23 — 3 вместо нынешних 3/4/5; клавиатура не деградировала -(проверка Shift+стрелки по методике KBD-1, не «на глаз»). - -**Ш3. Режимы.** `POP_SPEED_FASTEST/FAST/NORMAL` + условие боя -`Kid.sword == SWORD_2_DRAWN` в верху цикла, как у оригинала. Критерий: -NORMAL секундомером совпадает с живым SDLPoP на одинаковом отрезке -(методика из L1-SPEED — секундомер, не глазомер). - -**Ш4. Закрыть P2 (вариант C).** Выборка луча в `pop_blit_b` и в синей -фазе. Критерий: детектор разрыва не ловит ни одного расхождения между -программным счётчиком и лучом за 10 000 кадров, включая смену комнаты. - -**Ш5. Звук.** Только после Ш4: открыть CBL и перемерить P4 — -`cbl_underruns()` и потери кадровых тиков. - -## 8. Что проверить артефактом до начала - -1. Сколько именно фронтов съедает вход в комнату — от этого зависит, - нужен ли отдельный «resync» на тяжёлых переходах или хватит того, - что якорь переставляется каждый кадр. -2. Ветка `frozen` (`roomtest.c:320`) — какой темп ей нужен под делителем. -3. Проверить, что при n=4/5 бит 5 всё ещё единственный источник фронта - (то есть `_cbl_port_ref` держится всё это время). - - ---- - -# 9. Предлагаемый вариант подробно: программный счётчик кадров по лучу - -Дополнение от 2026-08-19 по запросу: как именно получается **точное** -число пройденных кадровых интервалов. Акселератор рассматриваем только -в нынешнем виде — с DI/EI (режим «акселератор с EI» из новой прошивки -из рассмотрения снят). - -## 9.1. Сигнал и его геометрия - -Единственный источник — **бит 5 порта `0xFE`**. Точная семантика по -исходнику MAME (`sprinter.cpp`, `kbd_fe_r`): - -```c -data |= 0xe0; -data ^= 0x40; -if (cbl_mode()) { - data &= ~0xa0; /* гасит биты 5 и 7 */ - data |= (vpos >= BORDER_TOP + SCREEN_YSIZE) << 5; - data |= ... & 0x80; /* бит 7 — CBL */ -} -``` - -То есть **бит 5 = 1 ровно тогда, когда луч ниже картинки** -(`vpos >= 16 + 256 = 272`), и это ЧТЕНИЕ ПОЛОЖЕНИЯ ЛУЧА, а не событие: -ни прерывания, ни защёлки, ни очереди — его невозможно «потерять», -можно только не посмотреть. - -Важное следствие из той же строки: вне `cbl_mode` бит читается как 1 -всегда (его выставляет `data |= 0xe0` и уже ничто не гасит). Поэтому -счётчик обязан работать только при взведённом `_cbl_port_ref()` — том -самом, который сейчас лениво взводит `gfx_wait_vsync`. - -Геометрия кадра (320 строк × 896 пикселей при 14 МГц): - -| | строк | мс | тактов CPU (21 МГц) | -|---|---:|---:|---:| -| бит 5 = 1 (нижний бланк) | 48 | 3,07 | **64 512** | -| бит 5 = 0 (картинка + верхний бордер) | 272 | 17,41 | 365 568 | -| кадр целиком | 320 | 20,48 | 430 080 | - -Границей кадра берём **фронт 1→0** — это `vpos = 0`, ровно то же -событие, которого ждёт сегодняшний `gfx_wait_vsync`. Значит момент -свопа страниц не меняется: до начала картинки остаётся верхний бордер, -16 строк ≈ 1 мс запаса, как и сейчас. - -## 9.2. Счётчик - -```c -static uint8_t beam_prev; /* бит 5 на прошлой выборке */ -static uint8_t frame_tick; /* счётчик кадров, разностная арифметика */ - -/* ~20 T-состояний тела + вызов; в тактах MAME ≈ 120 на выборку */ -void pop_beam_sample(void) -{ - uint8_t b = in_fe() & 0x20; - if (beam_prev && !b) frame_tick++; /* фронт 1→0 = начало кадра */ - beam_prev = b; -} -``` - -Вся арифметика ожидания — разностная по модулю 256, wrap безопасен -(тот же приём, что в существующем `_gfx_fps_state`). - -## 9.3. Почему счёт ТОЧНЫЙ (условие и запас) - -Утверждение: **если между соседними выборками проходит меньше 64 512 -тактов, то каждый фронт 1→0 будет засчитан ровно один раз.** - -Доказательство прямое. Пусть максимальный зазор между выборками -Δ < 64 512. Окно «бит 5 = 1» длится 64 512 тактов, то есть длиннее Δ, -значит в него попадает хотя бы одна выборка → `beam_prev` обязательно -станет 1 внутри каждого бланка. Окно «бит 5 = 0» длится 365 568 — тем -более содержит выборку → сразу после бланка `beam_prev` перейдёт в 0 и -даст ровно один инкремент. Двойной счёт невозможен: инкремент -происходит только на переходе 1→0, а `beam_prev` тут же обновляется. - -Условие ОДНО и оно про зазор, а не про нагрузку, не про DI, не про -прерывания. Отсюда все свойства варианта. - -**Какой запас по факту.** Самый длинный неделимый кусок кода без -возможности выборки — одно DI-окно акселератора, то есть один вызов -`_bgi_blit_rows_raw`. Он по контракту режется вызывающим на чанки -**≤16 строк**; при ширине 32 это ≈ 6 200 тактов, при полной высоте -спрайта 63 строки самый дорогой замеренный блит целиком — 32 073. -Даже если мерить самым грубым образом (одна выборка на целый блит, -а не на чанк), зазор вдвое меньше окна бланка. - -## 9.4. Где ставить выборки - -Правило простое: **выборка обязана стоять так, чтобы ни один путь -исполнения не давал зазора длиннее 64 512 тактов.** По фазам: - -- **Зелёная и циан** (436 494 и 382 770 тактов на 13/23) состоят из - блитов и хилов. Достаточно одной выборки на вызов наших обёрток - `pop_blit_b`, `pop_heal_off`, `pop_heal_fast` — но НЕ только их: - прямые вызовы `gfx_blit_*` / `gfx_heal*` разбросаны по шести файлам - (`pop_cdraw.c`, `pop_draw.c`, `pop_kdraw.c`, `pop_room.c`, - `pop_state.c`, `pop_tile.c`). Точный набор точек определяем НЕ - рассуждением, а замером (см. 9.7): ставим в обёртки, меряем худший - зазор, добавляем точки только там, где замер их требует. -- **Синяя** (149 106 тактов) — блитов нет вообще, это 2,3 окна бланка. - Нужны явные точки: после ввода, после физики, после `pop_process_trobs`, - после mob-тика. Ставятся на границах, которые и так размечены - зондами `pop_dbg_m*`. - -Чего заведомо НЕ хватит: выборок только в главном цикле. Один -`pop_floor_bake` — 179 914 тактов, почти три окна бланка. - -## 9.5. Ожидание — тот же примитив - -Ожидание фронта и есть плотная выборка, поэтому оно сливается со -счётчиком, а опрос клавиатуры остаётся ровно таким же плотным, как -сегодня: - -```c -static void wait_edge(void) -{ - uint8_t t = frame_tick; - do { - pop_beam_sample(); - kbd_raw_poll(); /* то, что сейчас висит idle-хуком */ - } while (frame_tick == t); -} -``` - -`gfx_set_idle_hook` при этом больше не нужен — опрос зовётся прямо. -Обязателен аварийный выход по счётчику попыток (как в нынешнем -`gfx_wait_vsync`): на железе, где бит ведёт себя иначе, цикл не должен -виснуть насмерть. - -## 9.6. Пейсинг целиком - -```c -uint8_t k = (uint8_t)(frame_tick - anchor); /* фронтов съел рендер */ -if (k >= n) { - wait_edge(); /* опоздали — выравниваемся на ближайший */ -} else { - do { wait_edge(); } while ((uint8_t)(frame_tick - anchor) < n); -} -anchor = frame_tick; /* якорь по ФАКТУ, фаза не копится */ -flip_page(); -``` - -`anchor = frame_tick`, а не `anchor += n` — сознательно: догонять -пропущенное время нельзя, иначе после тяжёлого кадра игра рванёт -вперёд. Это же правило заложено в исходном дизайне делителя -(«выравнивание на ближайший фронт, без накопления фазовой ошибки»). - -Поведение по случаям: - -| работа W (растров) | период | комментарий | -|---|---|---| -| W ≤ n | ровно n | цель задачи | -| n < W ≤ n+1 | ceil(W) | подтормаживает ровно настолько, насколько не успели | -| вход в комнату, W ≫ n | ceil(W) + 1 | ветка «опоздали»: один фронт, без растягивания | -| загрузка уровня, файловые операции | ceil(W) + 1…n | выборок нет вовсе → k занижен; худшее — n лишних растров ОДИН раз | - -Последняя строка — единственный случай, где счёт неточен, и он -безобиден: во время `ESTEX`-вызова выбирать нечего, а ошибка живёт один -кадр, потому что якорь переставляется по факту. - -## 9.7. Как это доказывается, а не декларируется - -Инструмент уже построен и проверен на нынешнем коде (§4): брейкпоинт на -следующей инструкции пишет `temp3 = totalcycles`, второй с условием -`(totalcycles - temp3) > 0x9D800` останавливает машину. - -Для приёмки он ставится **на инструкцию инкремента `frame_tick`**. -Если хоть один фронт пропущен, зазор между инкрементами станет два -растра и детектор остановит машину. Критерий: **10 000 кадров без -единого срабатывания**, включая смену комнаты и смерть Кида. - -Дополнительно, в отладочной сборке — перекрёстная проверка со счётчиком -кадровых прерываний (слот цепочки, один INC): прерывания теряются, луч -не должен, значит `frame_tick` обязан идти НЕ МЕДЛЕННЕЕ `irq_tick`. -Расхождение в другую сторону = пропущенная выборка. - -Напоминание о методике: **счёт попаданий брейкпоинтом на этом драйвере -недостоверен** (WAIT-линия, инструкция пересчитывается) — только -детектор разрыва. И литералы в отладчике MAME шестнадцатеричные. - -## 9.8. Цена - -| статья | тактов | -|---|---:| -| одна выборка (с вызовом) | ≈ 120 | -| ~60 выборок за логический кадр | ≈ 7 200 | -| доля от бюджета при n=3 (1 290 240) | **0,6 %** | - -В горячих местах (`pop_blit_b`) выборку можно заинлайнить и снять цену -вызова. - -## 9.9. Чем это лучше счётчика прерываний - -| | счётчик кадровых IRQ | счётчик по лучу | -|---|---|---| -| теряет тик под `di` акселератора | да, фазозависимо 0…3 % | нет — читается положение луча | -| теряет тик, если IRQ съела клавиатурная/CBL-ветка трамплина | да (приватный RETI) | нет | -| ломается от добавления звука | да (ещё один источник, `irqack` гасит все входы мержера) | нет | -| поведение на полной перерисовке | 3 тика подряд мимо | считает все | -| условие корректности | никакого — не в нашей власти | зазор выборок < 64 512 тактов, проверяется артефактом | -| риск залипнуть в плохой фазе и ровно потерять 25 % скорости | есть | нет | - -## 9.10. Что осталось проверить зондами до кодирования - -1. **Худший зазор между выборками** при размещении «только в обёртках» - — сколько точек реально нужно добавить. Это же число решает, нужна - ли выборка в синей фазе в четырёх местах или в двух. -2. **Владение `cbl_mode`**: сейчас бит 5 доступен потому, что - `gfx_wait_vsync` взвёл `_cbl_port_ref()` при первом вызове. Свой - ожидатель обязан взвести его сам — значит примитив логичнее держать - в libbgi (там доступен `_cbl_port_ref`), а не в приложении. -3. **Ветка `frozen`** (`roomtest.c:320`) — какой темп ей нужен. -4. Совпадает ли момент возврата `wait_edge()` с нынешним возвратом - `gfx_wait_vsync()` с точностью до микросекунд (иначе поедет момент - свопа и появятся разрывы картинки). - - ---- - -# 10. РЕЗУЛЬТАТ (2026-08-19, реализовано и проверено в MAME) - -Реализовано в приложении (`roomtest/pop_pace.c/.h`), в libbgi пока НИЧЕГО не -переносили — по решению пользователя: сначала обкатать у себя. - -## 10.1. Что сделано - -- `pop_beam_sample()` — выборка бита 5 порта `0xFE`, 10 инструкций, - быстрый путь 46 T + вызов. Модуль НЕ банковый, поэтому из банков - зовётся прямым `call` (проверено: банки так зовут `_pop_cd_hit_slot`). -- `pop_wait_edge()` — ожидание одного фронта; внутри тот же - `kbd_raw_poll()`, что раньше висел idle-хуком. -- `pop_pace_end(n)` — добрать до n фронтов от якоря; якорь ставится ПО - ФАКТУ. Главный цикл: `pop_wait_edge()` → строб вспышки → - `pop_pace_end(n)` → своп страниц. -- `pop_pace_arm()` — взводит `cbl_mode` через `gfx_wait_vsync()` и - ПРОВЕРЯЕТ, что фронты идут; если нет — `pace_ok = 0` и всё молча - откатывается на прежние `gfx_wait_vsync`. -- Режимы FASTEST/FAST/NORMAL, клавиша **P** по кругу, дефолт FASTEST. - Условие боя — `Kid.sword == SWORD_2_DRAWN`, буквально как у оригинала. - -## 10.2. Где стоят выборки и как они выбраны - -Точки ставились **не на глаз, а по замеру**: детектор зазора между -выборками (порог 64 512) останавливает машину, адрес возврата со стека -называет виновника. Пять итераций «замерил → закрыл дыру → перемерил»: - -| итерация | найденная дыра | тактов | -|---|---|---:| -| 1 | `kid_tick` + `pop_phys_tick` без выборок | 68 340 | -| 1 | весь циан, когда оба персонажа «тихие» | 100 044 | -| 2 | отрисовка персонажей идёт мимо `pop_blit_b` | 82 242 | -| 2 | полоса HP: `pop_kid_img_blit` в цикле | 86 586 | -| 3 | между двумя `pop_blit_b` — работа `pop_bg` | 75 000 | -| 4 | сам блит и сам heal (выборка была только НА ВХОДЕ) | 71 900 | -| 5 | `pop_loose_tick` | 73 990 | - -Итог: выборки в `pop_blit_b` (вход и перед каждым `gfx_w0_unmap`), -`pop_heal_fast` (вход и выход), после каждого `gfx_set_bank(SPRITE)` в -`pop_cdraw/pop_kdraw/pop_room`, в 16 потайловых функциях `pop_bg`, после -зондов `pop_dbg_p1..p8` в физике и в 21 точке главного цикла. - -## 10.3. Замеры - -Метод точности счётчика — **атомарный снимок одной командой отладчика**: -`printf "%d %d", totalcycles, b@<адрес pop_frame_tick>`. Раздельные -`lmem` и `print totalcycles` НЕ ГОДЯТСЯ: между двумя обращениями к мосту -проходят десятки кадров, и «недосчёт» получается на ровном месте (на этом -я сначала и обжёгся). - -| проверка | результат | -|---|---| -| счётчик, 11/15 покой, 5 окон | недосчёт **0** (270 растровых кадров) | -| счётчик, 11/15 тяжёлая позиция Кида | недосчёт **0** | -| счётчик, 13/23 | недосчёт **0** | -| период кадра, 11/15 | **ровно 3 растра**: ни длиннее 3,1, ни короче 2,9 на 302 логических кадрах | -| период кадра, 13/23 | **ровно 3 растра** на 308 логических (эталон был 3/4/5) | -| режим NORMAL | ровно 4 растра | -| NORMAL + `Kid.sword = 2` | ровно 5 растров | -| клавиша P | 0 → 1 → 2 → 0 | -| клавиатура | Кид отвечает на удержание и отпускание | - -**Цена выборок** — A/B прямо в памяти (заглушить `pop_beam_sample` -байтом `C9` и снять `pace_ok`, чтобы игра не зависла в ожидании фронта): -работа за кадр 543 860 с выборками против 539 832 без — **≈4 000 тактов, -0,9 %**. На фоне бюджета, который вырос втрое, это ничто. - -## 10.4. Грабли, стоившие времени - -1. **Литералы в отладчике MAME шестнадцатеричные.** Порог «700000» на - деле проверял 0x700000 = 17 растров и не срабатывал никогда. -2. **Счёт попаданий брейкпоинтом на этом драйвере недостоверен** (WAIT- - линия, инструкция пересчитывается): наблюдались «попаданий больше, чем - растровых кадров». Достоверны только сравнения ВРЕМЁН. -3. **Раздельные чтения через мост не атомарны** (см. 10.3). -4. **Мёртвый Кид перезапускает уровень** раз в `RESPAWN_DELAY` тиков, а - рестарт уровня — это 3-5 растров без единой выборки. Полдня я гонялся - за «дырой в статике», которой не было: Кид успел убежать в соседнюю - комнату и погибнуть, пока я мерил. **Проверяй, что на экране, прежде - чем объяснять числа.** -5. **`make LEVEL=13` без `make clean` не пересобирает** — флаги в - зависимостях не участвуют, на диск уезжает старый уровень. - -## 10.5. Что осталось - -- Перенос примитива в libbgi — по решению пользователя ПОСЛЕ обкатки. - Там же уместнее взводить `_cbl_port_ref` напрямую, без обходного - `gfx_wait_vsync()` в `pop_pace_arm`. -- Загрузка уровня/комнаты остаётся без выборок (ESTEX-вызовы) — счётчик - там недосчитывает. Это безобидно: якорь переставляется по факту, и - ошибка живёт один кадр. Отдельного «resync» не потребовалось. -- Проверить на реальном железе, что `pace_ok` взводится (в MAME — да). diff --git a/applications/PoP/docs/full_game_plan.md b/applications/PoP/docs/full_game_plan.md deleted file mode 100644 index 6c0a643..0000000 --- a/applications/PoP/docs/full_game_plan.md +++ /dev/null @@ -1,367 +0,0 @@ -# От roomtest к полноценной игре — сценарий и оболочка - -Статус: **частично реализовано; аудит обновлён 2026-08-24**. Ранее пометка -«завершены FG0–FG12» была неверной: для многих этапов уже есть код и -host-тесты, но их критерии приёмки на Sprinter ещё не выполнены. Фактический -статус каждого FG приведён в [§14](#14-этапы-реализации). - -Этот документ описывает превращение текущего игрового цикла -`roomtest` в законченную игру: заставка, интро, демонстрационный уровень, -сцены между уровнями, таймер, финал и 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`. Сейчас это - означает **существующую реализацию roomtest**, включая уже встроенные - исправления. Аудит и разведение `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. Текущее состояние roomtest - -Уже реализованы игровой кадр, комнаты, уровни, тайлсеты, 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 живёт одним модулем `roomtest/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/PoP/docs/gates_spikes_plan.md b/applications/PoP/docs/gates_spikes_plan.md deleted file mode 100644 index 2a2abd1..0000000 --- a/applications/PoP/docs/gates_spikes_plan.md +++ /dev/null @@ -1,279 +0,0 @@ -# Интерактивные объекты (кнопки/гейты/пики) + HP/смерть — ПОДРОБНЫЙ план - -> **Статус: РЕАЛИЗОВАНО (2026-08-01).** Все фазы плана (P0 персистентный -> per-room `room_modif`, S пики, B кнопки+ворота) сделаны и играются: -> `roomtest/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) продолжают экономить время при отладке. -> Текущие задачи — `../roomtest/TASKS_OPEN.md`. - -Составлен 2026-07-20. Документ самодостаточный: рассчитан на старт -«с чистого листа» (пустой контекст). Всё сверено с -`applications/PoP/SDLPoP/src/` и данными `res2001.bin`. - -Правило проекта (см. `applications/PoP/CLAUDE.md`): **SDLPoP — источник истины**, -перед кодингом читать соответствующий код seg*.c, не гадать. - ---- - -## 0. КОНТЕКСТ: текущее состояние `applications/PoP/roomtest` (что уже готово) - -roomtest — живой прототип порта PoP: комната 1 уровня 1 живой композицией -тайлов + Kid (анимация/управление/коллизия/падение/зацеп/переходы). Собрать: -`cd applications/PoP/roomtest && 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}roomtest.exe{ENTER}")`). -Отладка: клавиши `1`=freeze / `2`=resume в roomtest; MCP-мост `mame-z80` -(read_logical_memory, set_breakpoint, disassemble); адреса символов — -`.sprinter-cc-roomtest/roomtest.map` (сдвигаются при пересборке!). - -### Модули (все в `applications/PoP/roomtest/`) -- `roomtest.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/PoP/docs/host_tests_plan.md b/applications/PoP/docs/host_tests_plan.md deleted file mode 100644 index f356e6c..0000000 --- a/applications/PoP/docs/host_tests_plan.md +++ /dev/null @@ -1,142 +0,0 @@ -# План: модульные тесты движка roomtest под ucsim_z80 - -Обвязка общая — `testkit/` в корне репозитория (там же объяснение, почему -прогон именно под z80, а не хостовым gcc). Наборы лежат в -`../roomtest/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()` в `roomtest.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/PoP/docs/ideas_backlog.md b/applications/PoP/docs/ideas_backlog.md deleted file mode 100644 index 87a0878..0000000 --- a/applications/PoP/docs/ideas_backlog.md +++ /dev/null @@ -1,91 +0,0 @@ -# Идеи и вопросы «на подумать» (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/PoP/docs/impl_diff.md b/applications/PoP/docs/impl_diff.md deleted file mode 100644 index 09099c6..0000000 --- a/applications/PoP/docs/impl_diff.md +++ /dev/null @@ -1,649 +0,0 @@ -# Осознанные расхождения с SDLPoP - -Правило подпроекта (`../CLAUDE.md`): расхождение нашей реализации с -`SDLPoP/src/` — по умолчанию **баг у нас**. Этот файл — список исключений: -мест, где мы сознательно сделали иначе, потому что платформа/ABI/бюджет -кадра требуют другого, а НАБЛЮДАЕМОЕ поведение обязано совпадать. - -Формат записи: что делает оригинал → что делаем мы → почему → чем платим и -что проверять при регрессе. Если запись перестала быть верной (портировали -дословно, отказались от обхода) — удалять, а не оставлять «для истории»: -история в git. - ---- - -## D-1. История флагов перекрытия у бокового шва: сдвиг вместо тега комнаты - -**Файлы:** `roomtest/pop_map.c` (`pop_coll_shift`, `pop_coll_invalidate`, -`check_collisions`), `roomtest/roomtest.c` (`enter_room_side`). -**Связанный баг:** BUG-GATE-PASS-1 (`roomtest/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 roomtest/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` (roomtest.c) подмешивает в сигнатуру -ряда бит «кнопка нажата» (`pop_doorlink2(mod) & 0x1F > 1`) для тайлов -`0x0F`/`0x06`, и change-driven редрой `pop_room_redraw_seam_left` срабатывает -на нём так же, как на openness ворот. - -### Почему - -Голый порт давал видимый залип (SEAM-BUTTON-STALE, roomtest/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-силуэтом - -**Файлы:** `roomtest/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: тень стоит слева -через провал, поза совпадает с позой Кида-в-стойке. Родственная запись — -«Слияние с тенью» ниже. - ---- - -## Слияние с тенью: мигания Кида спрайтами тени нет - -**Файлы:** `roomtest/guards.c` (`autocontrol_shadow_level12`), -`roomtest/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 у нас «не анимируется» - -**Файлы:** `roomtest/pop_map.c` (`pop_check_fall_flo`, `pop_loose_tick`), -`roomtest/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` и парный контроль на другом -уровне). - ---- - -## Чит навигации по комнатам не запускает бесшовный переход уровня - -**Файлы:** `roomtest/roomtest_cold.c` (`pop_dbg_roomnav`), `roomtest/roomtest.c`, -`roomtest/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) идёт через штатный путь смерти - -**Файлы:** `roomtest/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/PoP/docs/keys.txt b/applications/PoP/docs/keys.txt deleted file mode 100644 index 5c4966b..0000000 --- a/applications/PoP/docs/keys.txt +++ /dev/null @@ -1,58 +0,0 @@ -* 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/PoP/docs/l9_invert_plan.md b/applications/PoP/docs/l9_invert_plan.md deleted file mode 100644 index 82e7be0..0000000 --- a/applications/PoP/docs/l9_invert_plan.md +++ /dev/null @@ -1,428 +0,0 @@ -# L9-INVERT — план реализации зелья переворота (уровень 9) - -Рабочий план, по которому задача делается с чистого контекста. Доска — -[`../roomtest/TASKS_OPEN.md#l9-invert`](../roomtest/TASKS_OPEN.md); правила -подпроекта — `../CLAUDE.md` (SDLPoP = источник истины, диагноз железа — -только артефактом). - -## 0. Что и зачем - -Зелья типа 4 на уровне 9 (комната 7 тайл `(1,7)`, комната 10 тайл `(0,4)`) -переворачивают картинку вверх ногами. В оригинале это `toggle_upside()` -(seg000:15E9): `upside_down = ~upside_down`, `need_redraw_because_flipped = 1`. -Больше зелье не делает НИЧЕГО — ни вспышки, ни урона (seg006:1885, ветка -`case 4`). Снимается: смертью Кида (seg000:1224, при `alive >= 0`) и стартом -уровня (seg003:38/188). Управление НЕ инвертируется. Второе зелье -переворачивает обратно. - -Оригинал переворачивает готовый offscreen на выводе (`flip_screen` перед -копированием прямоугольников на экран, seg000:939/946). Нам этот путь -закрыт: страниц ровно две (`gfx_set_visible_page` → ESTEX $54 SELPAGE, бит 0), -рабочей третьей нет. Поэтому: - -- **уже нарисованное** переворачиваем ОДИН РАЗ построчной копией - акселератора (решение пользователя 2026-08-12); -- **всё, что рисуется дальше**, рисуем зеркально: фон (row-major) — новыми - vflip-блитами, персонажи (column-major) — заранее подготовленными - зеркальными кадрами. - -Полоса HP и лейбл комнаты живут в борту (`y = 194 + POP_YOFF`, поле — -`POP_PLAYFIELD_H = 192`) и не переворачиваются, как и в оригинале. - ---- - -# Часть I — libbgi - -## I.0 РАЗВЕДКА ЖЕЛЕЗА — ЗАКРЫТА 2026-08-12 - -**Оба факта подтверждены, ядро и обёртка написаны и проверены в MAME** -(`tests/pageflip`, 5/5 PASS — прямая копия, vflip, целость рамки, широкая -копия 320 = два прохода, неприкосновенность источника). - -- **Р1 подтверждён**: Port_Y можно менять между read- и write-триггером, - если ПЕРЕД вторым OUT стоит STOP (`LD B,B`) — он разоружает accel, и fetch - immediate-операнда OUT уже безопасен. Приём не новый: ровно так работает - `_bgi_scroll_cols_raw` (ядро `gfx_scroll_v`), то есть он был в проде - задолго до этой задачи — указал пользователь. Значит и обходной путь - через ОЗУ-буфер не нужен; -- **Р2 подтверждён**: обе страницы адресуемы одновременно, копия между ними - идёт по разнице баз (`_gfx_addr_shadow_base` / `_gfx_addr_base`), строку - выбирает Port_Y. - -Сделано: `libbgi/bgi256/_bgi_flip_rows_raw.c` (ядро) + -`libbgi/common/gfx_copy_page.c` (обёртка, режимы `GFX_COPY_DIRECT` / -`GFX_COPY_VFLIP`) + `tests/pageflip`. Пункты I.1 и I.3 ниже — ЗАКРЫТЫ этим -же коммитом; описание оставлено как контракт. - -### Исходный текст разведки (для истории) - -Два факта, на которых стоит вся схема, сейчас НЕ подтверждены артефактом. -Пока их нет, остальные пункты не начинать (memory `defer_unexplained_quirks`). - -**Р1. Смена Port_Y между burst-чтением и burst-записью.** Схема требует: -армировать копию, `LD A,(HL)` (burst-чтение строки src), сменить Y, -`LD (DE),A` (burst-запись в другую строку). Между триггерами НЕЛЬЗЯ -выполнять инструкции, читающие ОПЕРАНД из памяти — fetch операнда -перезабивает буфер акселератора кодом (memory `accel_operand_fetch_retrigger`). -Значит: - -- смена порта — только `out (c),a` (ED 79, регистровая форма); `out (#89),a` - (D3 89) читает immediate байт и УБЬЁТ буфер; -- новое значение Y — только из регистра (`ld a,d`), не `ld a,#n` и не из - памяти; -- `C` = 0x89 и оба значения Y должны лежать в регистрах ДО триггера чтения. - -Проверка: `tests/accflip` по образцу `tests/accop` — заполнить строку -источника известным паттерном, скопировать в другую строку со сменой Y, -прочитать VRAM побайтно и сравнить. Отрицательный результат — стоп-сигнал: -переворот придётся делать через промежуточный ОЗУ-буфер (строка 320 Б), и -бюджет вырастет примерно вдвое. - -**Р2. Адресация двух страниц в одном окне W3.** `gfx.h` утверждает, что -страница 1 «начинается на 320 байт дальше (0xC140)», но это комментарий, а не -замер. Нужно снять дампом: как адрес строки складывается из Port_Y и -смещения в окне, и видны ли обе страницы одновременно. От ответа зависит, -делается ли копия A→B одним проходом или через смену видеобанка. - -Итог разведки записать в `libbgi/docs` (или `docs/new/06-accel.md` -дополнением) и в memory — это знание переиспользуется во всех будущих -экранных эффектах. - -## I.1 Ядро копии с реверсом Y - -`libbgi/bgi256/_bgi_copy_rows_raw.c` умеет только ИНКРЕМЕНТ Port_Y на строку -(`y0` + шаг вперёд; страйды патчатся SMC при входе). Нужен вариант, где -одна сторона идёт вниз, другая вверх. - -- предпочтительно: новый leaf `_bgi_flip_rows_raw.c` (правило «1 функция = - 1 модуль»), а не флаг в существующем — горячий путь блиттера трогать не - надо, и DCE не потащит лишнего в программы без переворота; -- вход тот же (`__sdcccall(1)`: src→HL, dst→DE, дальше стек), плюс - `y0_src` / `y0_dst` и признак направления; -- DI-окно — одна строка, как в остальных ядрах (арминг в каждой скобке: - в окне EI между чанками CBL-ISR армирует акселератор своим размером, - см. `docs/accel-fill-budget.md`). - -## I.2 Публичные vflip-блиты для row-major — СДЕЛАНО 2026-08-12 - -Ассемблера не потребовалось вовсе: у row-major картинки вертикальное -зеркало — это порядок строк, а он задаётся ЗНАКОМ src-страйда, который -`_bgi_blit_rows_raw` и так принимает знаковым (`int sstride`) и патчит SMC -в adc-цепочку. Обёртки просто дают адрес ПОСЛЕДНЕЙ строки и `-stride`, -цена кадра та же, что у обычного блита. - -Написаны `gfx_blit_noclip_vflip`, `gfx_blit_part_noclip_vflip`, -`gfx_blit_part_vflip`; `tests/pageflip` расширен до 8 проверок, все PASS. - -Грабли, которые поймал тест: у клипающего варианта первая строка источника -обязана считаться от высоты ДО клипа (`h0`), иначе обрезка сверху экрана -сдвигает картинку — сначала формула брала уже укороченную `h`. - -### Контракт (исходное описание) - -Все row-major блиты приложения идут ровно через три функции (`pop_tile.c:281`, -`:283`, `:286`, `:325`, `:327`): - -| есть | нужен vflip-двойник | -|---|---| -| `gfx_blit_noclip` | `gfx_blit_noclip_vflip` | -| `gfx_blit_part_noclip` | `gfx_blit_part_noclip_vflip` | -| `gfx_blit_part` (с клипом) | `gfx_blit_part_vflip` | - -Семантика: `(x,y)` — левый ВЕРХНИЙ угол результата на экране (как у обычных -блитов), строки источника выводятся снизу вверх. Это позволяет вызывающему -не менять арифметику позиции, а только пересчитать `y` под поле. - -`gfx_blit_cols*` (column-major) vflip-двойников НЕ получают: реверс идёт -внутри колонки, а accel копирует блок только вперёд. Для персонажей — -часть II.4. - -`gfx_heal*` не трогаем: heal симметричен, он восстанавливает прямоугольник из -ОЗУ-копии по экранным координатам, а координаты вызывающий уже даёт -перевёрнутые. - -Тест: `tests/blitvflip` — эталонный спрайт, побайтная сверка VRAM. - -## I.3 Копия прямоугольника экран→экран - -```c -#define GFX_COPY_DIRECT 0 -#define GFX_COPY_VFLIP 1 -void gfx_copy_rect(uint8_t src_page, int sx, int sy, - uint8_t dst_page, int dx, int dy, - uint16_t w, uint16_t h, uint8_t mode); -``` - -- `w > 256` режется на burst'ы вызывающим ядром (320 = 160+160 либо 256+64 — - выбрать по замеру, разницы в тактах на строку почти нет); -- **горизонтального флипа не будет**: accel копирует блок только вперёд, - побайтовый реверс дал бы 320 burst'ов на строку вместо двух. Если он - когда-нибудь понадобится — только через ОЗУ-буфер с CPU-реверсом; -- клип не нужен (вызывающий гарантирует границы), но проверка «прямоугольник - в пределах экрана» в safe-варианте библиотеки — обязательна. - -Тест: `tests/pagecopy` — прямой режим и VFLIP, побайтная сверка. - -## I.4 Чего в списке пользователя не хватало - -1. **Размерный регресс**: после каждого шага `make size-check`; новые модули - не должны утянуть за собой рост в программах, которые их не зовут (проверка - на `examples/` и `tests/`). -2. **Обе сборки библиотеки** — fast и safe (`sprinter-cc --safe`): гарды - параметров в safe, критичные — в обеих. -3. **Документация**: `docs/sprite-api-design.md` (раздел про блиттер), - `docs/TODO.md`, справочник API; факты разведки I.0 — в `docs/new/06-accel.md`. -4. **IM2/CBL**: длинные серии коротких DI-окон не должны ломать кадровые - прерывания и клавиатуру — проверить `tests/kbdpoll`-сценарием после - внедрения (клавиатура вычерпывается idle-хуком, а переворот идёт вне - ожидания кадра). -5. **Порядок «страница ↔ ОЗУ-копия»**: копия должна идти банком - `GFX_BANK_NORMAL`, иначе перевёрнутый фон не попадёт в ОЗУ-копию и heal - начнёт восстанавливать старую картинку. Это ключевой инвариант всей - схемы — вынести отдельным утверждением в тест. - ---- - -# Часть II — приложение (roomtest) - -## II.1 Состояние и момент переключения — СДЕЛАНО 2026-08-12 - -Проверено в MAME (уровень 9, комната 7): нажатие чита переворачивает всю -комнату целиком за один кадр, повторное — возвращает. Персонажи и -анимируемые тайлы (факелы) пока рисуются НЕ зеркально — это шаги II.3/II.4. - -Что появилось: - -- `pop_upside` + `pop_upside_dirty` (порт `upside_down` и - `need_redraw_because_flipped`) в `pop_map.c`; -- ветка `case 4` в `pop_proc_get_object`: только тоггл, без вспышки и урона - (как seg006:1885); -- сброс в `pop_start_level` (seg003:38/188) и по смерти Кида (seg000:1224); -- `pop_flip_screen()` в банке 8: `gfx_copy_page(VFLIP)` в скрытую страницу → - показать её → `gfx_copy_page(DIRECT)` во вторую → инвалидация слотов - персонажей, метки «фон трогали» и полосы HP; -- **чит-клавиша U** — это ПОРТ, а не костыль: в оригинале переворот тоже - висит на чит-клавише (seg000:0793). Без неё эффект проверяется только - честным проходом уровня 9 до комнаты 7 — чит-телепорт до зелья не - дотягивается (оно на `(1,7)`, а навигация ставит Кида на нижний ряд). - -Цена: резидент +602 Б (новые функции libbgi линкуются в него), куча -1707 → 1050 Б. Это аргумент за [MEM-COLD2](../roomtest/TASKS_OPEN.md#mem-cold2) -до начала II.4. - -### Контракт (исходное описание шага) - -- `pop_upside` (uint8_t) — рядом с `pop_feather` в `pop_map.c`, экспорт в - `pop_map.h`; -- тоггл — в `pop_proc_get_object`, ветка `potion_type == 4` (сейчас там TODO, - как было у пера). Ничего кроме тоггла: ни вспышки, ни звука — так в - оригинале; -- сброс: `pop_start_level` (уже переехал в `roomtest_cold.c`) и смерть Кида — - по образцу seg000:1224 (в `kid_phys`/там, где ловится `alive >= 0`); -- переключение (функция `pop_flip_screen()`, банк 8 — холодный код): - 1. `gfx_copy_rect(front, …, back, …, GFX_COPY_VFLIP)` — скрытая страница - получает перевёрнутый чистый фон (accel читает ОЗУ-копию, персонажей в - ней нет); - 2. флип страниц, чтобы игрок сразу увидел результат; - 3. `gfx_copy_rect(new_front, …, new_back, …, GFX_COPY_DIRECT)` — вторая - страница дабл-буфера получает то же; - 4. инвалидация: `pop_cd[].valid = 0` для обоих слотов, `pop_mirror_heal` - -состояние, `pop_hp_invalidate()`, метки «фон трогали» (`pop_cd_touch`) - на обе страницы, `seam_sig` — чтобы шов перерисовался. - -## II.2 Геометрия — единая точка пересчёта - -```c -/* pop_bg.h рядом с POP_YOFF/POP_PLAYFIELD_H */ -#define POP_FLIP_Y(y, h) (2 * POP_YOFF + POP_PLAYFIELD_H - (y) - (h)) -``` - -Применяется ТОЛЬКО к тому, что внутри поля. Правило: любая функция, -получающая экранный `y`, либо сама зеркалит его по флагу, либо принимает уже -зеркальный — смешивать нельзя, иначе получим двойной переворот (это самый -вероятный класс багов здесь). Решение: зеркалит ВЫЗЫВАЮЩИЙ, у примитивов -семантика не меняется. - -## II.3 Фон — СДЕЛАНО 2026-08-12 - -Флаг проведён в ЕДИНСТВЕННУЮ точку — `pop_blit_b`/`blit_b_clip` -(`pop_tile.c`): позиция пересчитывается макросом `FLIP_TOP` (он один на весь -слой — дублировать нельзя, иначе двойной переворот), а блит выбирает -vflip-двойник. Тем самым зеркалятся и полная отрисовка комнаты, и точечные -редрои, и анимации trob, и fore-слой, и кладка — все они ходят через эту -функцию. - -Тонкость клипованного пути: при зеркале обрезка СВЕРХУ экрана съедает -НИЖНИЕ строки источника, поэтому кусок пересчитывается как -`sy' = h - sy - dh`. - -Проверено в MAME (уровень 9, чит U): комната перевёрнута целиком — пол -вверху, дверь уровня и кладка вверх ногами. Персонажи пока обычные (II.4). - -Цена: три функции libbgi ушли в резидент, куча 1574 → 450 Б, поэтому тем же -заходом сделан **MEM-COLD2 п.1** — `enter_room_side`/`enter_room` уехали в -банк 8 (куча вернулась к **1231 Б**, банк занят на 14.6%). Рабочие массивы -комнаты остались в `_DATA` и видны обеим половинам; `enter_room` обязана быть -`__banked` — её зовёт главный цикл. - -### Контракт (исходное описание) - -Одна точка: `pop_tile.c` (блит куска атласа) выбирает vflip-двойник по -`pop_upside` и пересчитывает `y`. Тогда автоматически попадают: - -- полная отрисовка комнаты (`pop_room.c`); -- точечные редрои (`pop_redraw.c`: ворота, пики, кнопки, дверь уровня, - дрожащие плиты); -- анимации `pop_trob` (факелы, пузырьки зелий); -- fore-слой и оверлеи кромки, кладка стены (`pop_bg.c`); -- падающие куски loose (`pop_room.c`, `pop_loose_mob_*`). - -Проверить отдельно: `wall_pattern` (кладка рисуется своей геометрией) и -оверлеи кромки — у них позиция считается от ряда, а не от `y` спрайта. - -## II.4 Персонажи (column-major) - -Нужны зеркальные кадры: 34 атласа, 228 563 Б (kid0..27 + sword, guard g0..g4). - -- реверс колонки — через стек (`pop`/`push`), ~10 тактов номинала на байт; -- источник маппится в W0, приёмник — новая EMM-страница; на спрайт: прочитать - колонки в буфер W2 (максимальный кадр — 40×16 ≈ 640 Б), развернуть, писать - в приёмник (перемаппинг W0 на спрайт — один OUT); -- `pop_cdraw.c` выбирает набор атласов (оригинал/зеркальный) по `pop_upside`; -- **кэш ключуется по СТРАНИЦЕ атласа**, а не по кадру: страница = 8 кадров, - 3-10 КБ, ~0.2-0.4 растрового кадра на переворот. - -**Стратегия наполнения — ЛЕНИВЫЙ ПОСТРАНИЧНЫЙ** (решение пользователя -2026-08-12): страница переворачивается при первом обращении к ней в -перевёрнутом режиме. Реально в ходу 5-10 страниц из 34, и если игрок зелье не -трогал, не тратится ни такта, ни страницы. - -**Время жизни кэша — ДО КОНЦА УРОВНЯ**: освобождаем страницы при смене -уровня, а не при снятии эффекта. На уровне 9 переворот случается минимум -дважды (второе зелье возвращает всё назад), плюс его снимает смерть Кида — -готовить пачку заново каждый раз было бы обидно. - -После [UI-SPRITES](../roomtest/TASKS_OPEN.md#ui-sprites) страницы `kid27` и -`g0` станут чисто «полевыми» (деления HP уедут в константы резидента), и -исключений в кэше не останется — эту задачу логично сделать ДО II.4. - -## II.5 Клип — ЧАСТЬ СДЕЛАНА 2026-08-12 (поле), остаётся clip_char - -**Клип ПОЛЯ сделан.** Симптом (нашёл пользователь): после телепорта в -перевёрнутом виде ниже поля — мусор. Причина: спрайты, которые в обычном -виде торчат ВЫШЕ поля (полоса кладки у потолка, её режет `pop_t_clip_top`), -после отражения торчат НИЖЕ и лезут в борт, где живёт полоса HP. - -Фикс в `blit_b_clip`: при перевороте режем по ОБЕИМ границам поля всегда, а -не по `pop_t_clip_top` — зеркальный спрайт может вылезти и там, где в -обычном виде клип не ставили. Плюс в `pop_blit_b` быстрый путь (без клипа) -теперь пропускает в клипующий всё, что выходит за поле. - -Проверено в MAME: телепорт по комнатам в перевёрнутом виде — борта чистые, -комнаты рисуются зеркально (факелы, чомперы, пол). То есть **переходы -между комнатами в перевёрнутом виде работают**. - -Осталось из этого пункта: `clip_char` (верх поля ↔ низ) и клип падающей -плиты — они про ПЕРСОНАЖЕЙ и падающие объекты, то есть идут вместе с II.4. - -### Контракт (исходное описание) - -- `clip_char` (`pop_map.c:2287`, таблица `y_clip[5] = {-60,3,66,129,192}`) — - режет верх спрайта по линии ряда; при перевороте линия становится нижней. - Симметрия сама не сойдётся: нужен зеркальный расчёт `y_clip` и обмен - «сверху/снизу» местами; -- то же для зеркала уровня 4 (`pop_map.c:2732`) — на уровне 9 зеркал нет, но - код общий, поэтому ветку надо хотя бы не сломать; -- `pop_clip_sprite` (`pop_room.c:1069`) — клип падающей плиты; -- `pop_room_clip_borders` — борта симметричны (28 сверху и снизу), но - проверить, что чистится именно поле. - -## II.6 Проверка - -- **хост-тест** (`tests-host`): арифметика `POP_FLIP_Y` и зеркальный `y_clip` - — сцена «Кид у верхней кромки» в обычном и перевёрнутом режиме даёт - симметричные значения клипа. Логику отрисовки хост-тесты не видят, поэтому - здесь проверяем только счёт; -- **MAME**: уровень 9, комната 7 — выпить зелье `(1,7)`, проверить: картинка - перевернулась целиком, Кид ходит и цепляется корректно, факелы анимируются - перевёрнуто, ворота/пики перерисовываются перевёрнуто, полоса HP осталась - внизу и не зеркальная; второе зелье (комната 10) возвращает как было; - смерть Кида снимает эффект; -- **регресс**: обычные уровни (1-8) не должны измениться ни на пиксель — - прогнать пару комнат и сверить скриншоты с прежними. - ---- - -# Часть III — ENTER-ROOM-FAST — СДЕЛАНО 2026-08-12 - -Комната теперь рисуется ОДИН раз — в скрытую страницу, — тут же -показывается флипом, а вторая страница получает её `gfx_copy_page(DIRECT)`. -Процесс рисования тайлов больше не виден: игрок получает готовый кадр. -Проверено в MAME: старт уровня и чит-переход между комнатами, два -последовательных снимка идентичны (обе страницы синхронны, мерцания нет). - -Побочно понадобилось: видимую страницу главный цикл больше не ведёт сам, а -перечитывает у железа в начале кадра (`front = gfx_get_visible_page()`) — -её меняют и вход в комнату, и переворот, причём оба из банка, где локальные -переменные `main` недоступны. - -# Часть III (исходное описание) - -Идея пользователя, самостоятельная ценность (сейчас при входе в комнату видно, -как рисуются тайлы): рисовать комнату ОДИН раз в скрытую страницу, показать её -флипом, а во вторую страницу залить `gfx_copy_rect(…, GFX_COPY_DIRECT)`. - -- сейчас `enter_room_side` рисует комнату дважды (цикл `for (pg = 0; pg < 2)`) - — вторая отрисовка (~полмиллиона тактов) меняется на копию (~115-200 К); -- процесс рисования перестаёт быть виден: игрок видит только готовый кадр; -- делается тем же `gfx_copy_rect`, что и I.3, то есть ничего дополнительно - писать не надо. - -Проверять отдельно: состояние ОЗУ-копии второй страницы после копии -(инвариант из I.4 п.5), метки «фон трогали», и что heal на второй странице -работает. - ---- - -# Порядок работ - -1. I.0 разведка (`tests/accflip`) → факты в docs + memory. -2. I.1 ядро + I.3 `gfx_copy_rect` (+ `tests/pagecopy`). -3. II.1 состояние и `pop_flip_screen` — уже можно посмотреть в MAME: - картинка обязана перевернуться целиком, дальше она «поедет» по мере - перерисовок (это ожидаемо на этом шаге). -4. Часть III (ENTER-ROOM-FAST) — дешёвый выигрыш на том же кирпиче, - заодно обкатывает копию в бою. -5. I.2 vflip-блиты (+ `tests/blitvflip`) → II.3 фон. После этого сцена - должна выглядеть правильно во всём, кроме персонажей. -6. UI-SPRITES (деления HP в резидент) → II.4 зеркальные кадры персонажей. -7. II.5 клип → II.6 проверка и регресс. - -Критерий готовности каждого шага — артефакт (побайтный тест в MAME либо -скриншот сцены), а не «код написан». - -# Риски - -- **Р1 не подтвердится** (нельзя менять Y между триггерами) — переворот - пойдёт через ОЗУ-буфер строки, бюджет вырастет вдвое (всё ещё ≈0.5 - логического кадра), схема в целом выживает; -- **двойной переворот координат** — самый вероятный баг: часть кода зеркалит - `y` сама, часть получает уже зеркальный. Лечится правилом из II.2 и тем, - что зеркалит только вызывающий; -- **кэш зеркальных кадров и память**: 34 страницы EMM в худшем случае; по - `sprinter_emm_budget` на старте свободно 215 — влезает, но проверить - фактический остаток на уровне 9 (там уже загружены атласы фона, Кида, - стража и уровень); -- **fore-слой поверх персонажа**: он рисуется по футпринту спрайта, а - футпринт после переворота другой — проверить именно на сцене с колоннами. - -# Решения пользователя (2026-08-12) - -1. **Кэш зеркальных кадров — ленивый постраничный**, наполняется по первому - обращению (см. II.4). -2. **Живёт до конца уровня**, освобождается при смене уровня. -3. **ENTER-ROOM-FAST делается сразу**, шагом 4 — на готовом `gfx_copy_rect`, - до того как на копию завяжется переворот. - -Открытых вопросов не осталось: план исполняется как есть, новые развилки — -только если разведка I.0 даст отрицательный результат по Р1. diff --git a/applications/PoP/docs/layout_plan_v2.md b/applications/PoP/docs/layout_plan_v2.md deleted file mode 100644 index 4c5f91d..0000000 --- a/applications/PoP/docs/layout_plan_v2.md +++ /dev/null @@ -1,532 +0,0 @@ -# roomtest — план 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` -> в `roomtest.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 - roomtest 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 КБ -roomtest : 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, roomtest 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-roomtest/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, roomtest, - 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-часть roomtest? -pop_ctrl (ввод/диспетчер) -roomtest (главный цикл) -``` - -Почему так: - -- **`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 (+ проверка кодов в roomtest) | 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-roomtest/*.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/PoP/roomtest/TASKS_OPEN.md` — что из этого берётся в работу сейчас. - ---- - -## 9. Скорость отрисовки: замеры и запас - -Перенесено из удалённого `size_optimization_plan.md` §8 (замер 2026-07-27) — -единственная его часть, которая не была перекрыта этим документом. -Профилирование в MAME: маркеры в порт 0xFE + `wpiset … totalcycles` (приём из -memory `mame_mcp_bridge`); в самом `roomtest.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 в `../roomtest/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 - (`../roomtest/TASKS_CLOSED.md`) длинные DI-окна и есть причина потери байт - клавиатуры. Батчинг эту проблему УХУДШИТ, если делать его вслепую. -2. **Решётка ворот одним спрайтом** — `draw_gate_back` рисует бары по одному - (до 7 блитов). Сгенерировать в атласе «столб решётки» и выводить одним - `gfx_blit_part` с обрезкой по фазе `gate_bot_y & 7`: 7 блитов → 1. -3. **Не перерисовывать статичные части шва** — грань ворот, пол и кромка при - анимации решётки не меняются (см. OPT-1 в `../roomtest/BUGS_CLOSED.md` — - решено не делать, стоимость транзиентная). -4. **T-1 / T-2** (`../roomtest/BUGS_OPEN.md`) — перерисовка пик по причине и - idle-skip Кида: самый большой оставшийся резерв, потому что убирает работу - целиком, а не удешевляет её. diff --git a/applications/PoP/docs/levels_12_15_plan.md b/applications/PoP/docs/levels_12_15_plan.md deleted file mode 100644 index 533555b..0000000 --- a/applications/PoP/docs/levels_12_15_plan.md +++ /dev/null @@ -1,462 +0,0 @@ -# Что осталось на уровнях 12, 13, 14, 15 и 0 - -Статус: разбор по `../SDLPoP/src/`, 2026-08-13. Продолжает -[`levels_plan.md`](levels_plan.md) (машинерия уровней и тайлсеты — уже -сделаны). Здесь только СПЕЦСОБЫТИЯ, которых у нас ещё нет. - -Правило проекта: источник истины — SDLPoP; все ссылки ниже даны на функцию и -строку, чтобы порт начинался с чтения, а не с гипотезы. - ---- - -## 0. Что из этой области УЖЕ есть - -Проверено грепом по `roomtest/`: - -| механика | где у нас | статус | -|---|---|---| -| слот соперника, общий на всех 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`, `roomtest.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`](../roomtest/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, хост-набор - `roomtest/tests-host/t_shadow.c` (45 проверок); живой проверки в MAME - ещё не было — доска [`L12-SHADOW`](../roomtest/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, хост-набор - `roomtest/tests-host/t_jaffar.c` (44 проверки); живой проверки в MAME ещё - не было — доска [`L13-JAFFAR`](../roomtest/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/PoP/docs/levels_plan.md b/applications/PoP/docs/levels_plan.md deleted file mode 100644 index 5872360..0000000 --- a/applications/PoP/docs/levels_plan.md +++ /dev/null @@ -1,209 +0,0 @@ -# План: от одного уровня к нескольким (загрузка, переходы, тайлсеты) - -Статус: план, 2026-08-01. Продолжает `PORT_PLAN.md` §7 (Фаза 1: «переходы -между экранами» → теперь между УРОВНЯМИ). Текущая точка: `roomtest` играет -уровень 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()` — реализованы и пока - НЕ вызываются (см. `../roomtest/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](../roomtest/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 осталось — `../roomtest/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` (`../roomtest/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_*` в `roomtest.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` в -`roomtest.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 (`roomtest.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/PoP/docs/menu_settings_plan.md b/applications/PoP/docs/menu_settings_plan.md deleted file mode 100644 index bb1a52b..0000000 --- a/applications/PoP/docs/menu_settings_plan.md +++ /dev/null @@ -1,529 +0,0 @@ -# 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`. Под этим именем пока понимается - **текущее поведение roomtest**, включая уже встроенные исправления. -- Дизайн файла и 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`; -- сбрасывает состояние, которое сбрасывает текущая реализация roomtest. - -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 забиты игрой -(тот же вывод зафиксирован комментарием в `roomtest_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, вспышки урона в roomtest.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, перезапустить `roomtest`, увидеть - 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/PoP/docs/midtable_analysis.md b/applications/PoP/docs/midtable_analysis.md deleted file mode 100644 index 635e98d..0000000 --- a/applications/PoP/docs/midtable_analysis.md +++ /dev/null @@ -1,275 +0,0 @@ -# Слои отрисовки: как устроен оригинал и чего стоит порт - -Разбор 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`) | -| `roomtest.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/PoP/docs/palette_plan.md b/applications/PoP/docs/palette_plan.md deleted file mode 100644 index 8aeed34..0000000 --- a/applications/PoP/docs/palette_plan.md +++ /dev/null @@ -1,297 +0,0 @@ -# План: консолидация работы с палитрами + переход уровня через fade - -Статус: **этапы A и B реализованы; визуальная приёмка полного маршрута ещё -идёт** (2026-08-24). Палитры выделены в bank 10, а renderer cutscene/intro — -в bank 11, чтобы не переполнять bank 9 оболочки. -Обсуждение велось вокруг `roomtest/` (банк 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` в roomtest.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 раза (roomtest_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. Создан `roomtest/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: - - `roomtest_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 — низкоуровневый экспорт); - - `roomtest.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. - -Реализация находится в `roomtest.c` / `roomtest_cold.c`: последний кадр -уровня N темнеет, `pop_level_switch()` подготавливает первый кадр N+1 и -обновляет логический источник через `pop_pal_level_load(1)`, затем главный -цикл показывает кадр только через fade-in. Восемь ступеней и отдельная -анимация смерти не входят в этот этап. - -**Этап B закрывает два открытых бага** (разборы — `roomtest/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`, roomtest.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/PoP/docs/perf_backlog.md b/applications/PoP/docs/perf_backlog.md deleted file mode 100644 index 0789b7a..0000000 --- a/applications/PoP/docs/perf_backlog.md +++ /dev/null @@ -1,335 +0,0 @@ -# Оптимизация отрисовки — что НЕ сделано (замеры на 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. -- **Адреса символов меняются после КАЖДОЙ пересборки** (`roomtest.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()` из `roomtest.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/PoP/docs/perf_cyan_phase.md b/applications/PoP/docs/perf_cyan_phase.md deleted file mode 100644 index c99f125..0000000 --- a/applications/PoP/docs/perf_cyan_phase.md +++ /dev/null @@ -1,172 +0,0 @@ -# ЦИАН фаза (персонажи + передний слой) — анализ и оптимизация - -Рабочий документ: живёт между сессиями. Внизу **журнал правок** — каждая -запись с замером до/после. Сцена, рецепт воспроизведения и зонды — -[`perf_l13_room23.md`](perf_l13_room23.md); там же ответ про 8-битные -габариты. Парная фаза — [`perf_green_phase.md`](perf_green_phase.md). - -Границы фазы в `roomtest.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/PoP/docs/perf_green_phase.md b/applications/PoP/docs/perf_green_phase.md deleted file mode 100644 index ff58d1f..0000000 --- a/applications/PoP/docs/perf_green_phase.md +++ /dev/null @@ -1,327 +0,0 @@ -# ЗЕЛЁНАЯ фаза (слой фона) — анализ и оптимизация - -Рабочий документ: живёт между сессиями. Внизу **журнал правок** — каждая -запись с замером до/после. Сцена, рецепт воспроизведения и зонды — -[`perf_l13_room23.md`](perf_l13_room23.md); там же ответ про 8-битные -габариты. Парная фаза — [`perf_cyan_phase.md`](perf_cyan_phase.md) (её цель -достигнута). - -Границы фазы в `roomtest.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, не перемерена. Подробности — - `roomtest/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/PoP/docs/perf_l11_room15.md b/applications/PoP/docs/perf_l11_room15.md deleted file mode 100644 index 062a3db..0000000 --- a/applications/PoP/docs/perf_l11_room15.md +++ /dev/null @@ -1,238 +0,0 @@ -# Сцена и замер: факел + чомпер + страж, уровень 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`, база модуля `roomtest.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/PoP/docs/perf_l13_room23.md b/applications/PoP/docs/perf_l13_room23.md deleted file mode 100644 index 6c1042e..0000000 --- a/applications/PoP/docs/perf_l13_room23.md +++ /dev/null @@ -1,271 +0,0 @@ -# Сцена и метод замера: каскад плит, уровень 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`, база модуля -`roomtest.c` = **0x42AD**. **Адреса зондов меняются после КАЖДОЙ -пересборки** — брать заново из `.sprinter-cc-roomtest/roomtest.map` и -`roomtest.lst`. - ---- - -## 1. Как воспроизвести сцену - -**Только перезапуском программы.** Проверено и отвергнуто: - -- **выход из комнаты и возврат** (чит `+`/`-`) — не работает: провалившаяся - плита-потолок уходит в страницу уровня насовсем (`pop_level_set_tile` в - `roomtest.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` → зонды → -`roomtest`**. `ESC` выходит в DSS, запуск заново стартует уровень 13 с нуля, -Кид сразу в комнате 23, каскад начинается через ~5 логических кадров после -отрисовки комнаты. Зонды обязаны стоять **ДО** набора `roomtest` — за время -набора (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` → зонды → `roomtest` → `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` (полосы бордюра) из `roomtest.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` | - -Полезные адреса состояния (из `roomtest.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/PoP/docs/perf_registry.md b/applications/PoP/docs/perf_registry.md deleted file mode 100644 index e9116bb..0000000 --- a/applications/PoP/docs/perf_registry.md +++ /dev/null @@ -1,871 +0,0 @@ -# Реестр оптимизаций: всё отложенное, в одном списке - -Собрано 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), -[`../roomtest/TASKS_OPEN.md`](../roomtest/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:` и `roomtest`. Кид встаёт в -(0,2) лицом к чомперу, справа факел и страж — та самая сцена замеров. -Штатный старт уровня возвращается через `make ROOM=`. - -Проверка, что программа ЖИВА, обязательна перед любым чтением памяти: -`cur_room` (0x97AA) должен лежать в 1..24 — на этом уже был сорван один -замер (прочитаны два случайных байта остановленной машины). - -### Метод замера - -Зонды — `out (_io_border)` в `roomtest.c` (база модуля 0x42AD) плюс -резидентные пустышки `pop_dbg_m*` из `pop_state.c`. Адреса брать ЗАНОВО из -`.sprinter-cc-roomtest/roomtest.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))` -(`roomtest/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/PoP/docs/prng_alternatives.md b/applications/PoP/docs/prng_alternatives.md deleted file mode 100644 index b5ed432..0000000 --- a/applications/PoP/docs/prng_alternatives.md +++ /dev/null @@ -1,148 +0,0 @@ -# Генераторы псевдослучайных чисел: запасные варианты - -Что сейчас стоит в порте, какие есть альтернативы и сколько на них реально -можно выиграть. Заготовка на случай, если упрёмся в бюджет кадра — -**сейчас менять ничего не нужно**. - -## Что стоит сейчас - -`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/PoP/docs/quicksave_plan.md b/applications/PoP/docs/quicksave_plan.md deleted file mode 100644 index fa73d4c..0000000 --- a/applications/PoP/docs/quicksave_plan.md +++ /dev/null @@ -1,328 +0,0 @@ -# QuickSave / QuickLoad — разбор оригинала и план реализации - -Статус: **РЕАЛИЗОВАНО и проверено в MAME** (2026-08-22; F6/F9, POP.SAV + -POP.BAK — см. коммит `v0.6-pop-quicksave`). Документ оставлен как -справочник по формату снимка и разбору. Задача на доске — -[`../roomtest/TASKS_OPEN.md#qsave`](../roomtest/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-roomtest/roomtest.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:` из -roomtest. - ---- - -## 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 (пробный файл из roomtest) | файл создался и читается обратно после рестарта программы | -| **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`, запустить roomtest заново, загрузить — и оказаться там же. Именно -этого сценария сейчас нет ничем, и ради него задача и делается. - ---- - -## 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](../roomtest/BUGS_CLOSED.md#bug-cheat-imm-1). -5. **Дабл-буфер.** Самый вероятный источник «почти работает»: забыть вторую - страницу. Симптом — мерцание через кадр - (см. `roomtest/CLAUDE.md`, раздел про дабл-буфер). -6. **Транзакция SAV/BAK.** До кодинга проверить на DSS семантику - rename/replace. Если атомарная замена не гарантирована, писать через - `POP.NEW`, проверять его после close и не удалять единственную валидную копию - до завершения новой. diff --git a/applications/PoP/docs/resident_budget.md b/applications/PoP/docs/resident_budget.md deleted file mode 100644 index ccc43e3..0000000 --- a/applications/PoP/docs/resident_budget.md +++ /dev/null @@ -1,111 +0,0 @@ -# Бюджет резидента 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). - -**Перенос КОДА в банк — штатный путь, на нём стоят все наши банки. Ломался -именно перенос ДАННЫХ.** - -## Что осталось - -- `roomtest.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/PoP/docs/room_model_plan.md b/applications/PoP/docs/room_model_plan.md deleted file mode 100644 index 2b685e2..0000000 --- a/applications/PoP/docs/room_model_plan.md +++ /dev/null @@ -1,86 +0,0 @@ -# PoP roomtest — модель `kid_room ≠ drawn_room` (баг #4) - -> **Статус: ЖИВОЙ ПЛАН, сделан частично (сверено 2026-08-01).** -> - **S1 — сделан:** `kid_room` заведён отдельно от `cur_room`, -> `update_kid_render_dx()` (`roomtest.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` (разбор корня — -> `../roomtest/BUGS_CLOSED.md`, BUG-SEAM-PINGPONG). -> -> **Зачем документ остаётся.** Полная straddle-модель понадобится для: -> (а) читов осмотра соседних комнат `H/J/U/N` (`levels_plan.md` §4), -> (б) сцен, где Кид и страж в разных комнатах кадра, (в) остатков окклюзии у -> шва (S4). Брать из `../roomtest/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 (глубина), правый край. - -## Связанные баги — все ЗАКРЫТЫ (`../roomtest/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/PoP/docs/roomnav_skip.md b/applications/PoP/docs/roomnav_skip.md deleted file mode 100644 index 1278924..0000000 --- a/applications/PoP/docs/roomnav_skip.md +++ /dev/null @@ -1,65 +0,0 @@ -# Комнаты для отладочного телепорта (`+` / `-`) - -Считано скриптом прямо по `../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`, `roomtest_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/PoP/docs/shadow_atlas_plan.md b/applications/PoP/docs/shadow_atlas_plan.md deleted file mode 100644 index 3c838c6..0000000 --- a/applications/PoP/docs/shadow_atlas_plan.md +++ /dev/null @@ -1,266 +0,0 @@ -# Атлас Тени — разбор и план - -Дата: 2026-08-20. Статус: **ЗАКРЫТО.** Ш0-Ш4 сделаны, прогон -пользователем на уровнях **4, 5, 6 и 12** — всё корректно. - -Задача: сейчас Тень рисуется спрайтами Кида (вне боя) и спрайтами стража -(в бою) как есть, поэтому от Кида она не отличается. В оригинале её вид -даёт наложение спрайта на себя со сдвигом; наш пакетный блит такого не -умеет, поэтому запекаем результат в отдельный атлас заранее. - -## 1. Что делает оригинал (проверено по SDLPoP, не по памяти) - -`draw_objtable_item`, `seg008.c:1600`: - -```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); -``` - -Тот же самый спрайт кладётся ДВАЖДЫ: первый проход в x, второй в x+1. -Два уточнения, которые меняют алгоритм запекания: - -1. **`blitters_2_or` — это НЕ побитовое ИЛИ.** В SDLPoP он реализован - обычным блитом с colour key = индекс 0 (`method_6_blit_img_to_scr`, - `seg009.c:3306`: `SDL_SetColorKey(image, SDL_TRUE, 0)`). То есть - первый проход — наш обычный прозрачный блит, один в один. -2. **`blitters_3_xor` работает по 24-битному RGB, а не по индексам - палитры** (`blit_xor`, `seg009.c:3190`: конвертация в 24 бита, затем - `*p_dest ^= *p_src` побайтно). Прозрачности у него нет вообще — - XOR'ится весь прямоугольник, но прозрачные пиксели спрайта это - чёрный 0x000000, а XOR с нулём ничего не меняет. - -Отсюда и берётся необходимость СВОЕЙ палитры: XOR двух цветов игровой -палитры даёт цвет, которого в ней нет. - -### Каким набором спрайтов рисуется Тень (проверено 2026-08-20) - -Сначала я решил, что в боевых кадрах Тень рисуется спрайтами СТРАЖА, и -записал это в план. **Это было неверно, поправка ниже.** - -Набор выбирает НЕ charid. `load_frame_to_obj` (`seg008.c:1752`): - -```c -word chtab_base = id_chtab_2_kid; // жёстко Кид -obj_chtab = chtab_base + (cur_frame.sword >> 6); // старшие 2 бита кадра -``` - -то есть набор берётся из САМИХ ДАННЫХ КАДРА, поле `sword`: младшие 6 бит — -картинка меча, старшие два — смещение chtab относительно Кида -(`types.h:361`). Проверил обе таблицы: - -- `frame_table_kid` — **все 241 кадра** имеют `sword & 0xC0 == 0` → chtab_2. - Значит **Кид всегда рисуется своими спрайтами**, боевые кадры 150..189 не - исключение; -- `frame_tbl_guard` — все кадры имеют `0xC0` → chtab_5. - -Тень берёт `frame_tbl_guard` для кадров 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 -там серая рампа под перекраску), а спрайты — Кид в боевых позах, не страж. -Отрендерил тройками «Кид / SHADOW.DAT / GUARD.DAT» для кадров -151/153/158/161/167: первые две колонки — один и тот же персонаж, третья — -серый страж в тюрбане. - -**Вывод: Тень ВСЕГДА выглядит Кидом, и ощущение пользователя верно.** -Спрайты при этом лежат в двух файлах: не-боевые кадры в chtab_2 (KID), -боевые — в chtab_5, заполненном SHADOW.DAT. - -### Можно ли взять для боя собственные кадры Кида - -Механически да: `frame_table_kid` покрывает 150..189 со своей геометрией, -и хватило бы снять спецветку для `charid_1_shadow` в `load_frame`. Но -кадры НЕ совпадают: из 34 боевых у 31 отличается габарит (на 1-6 px), у 3 -отличаются пиксели. Это разная графика, а не одна и та же в двух файлах. -Поэтому берём SHADOW.DAT — он и есть «кадры Кида для Тени», подготовленные -авторами. - -## 2. Единственное место, где запечка отличается от оригинала - -XOR идёт по тому, что УЖЕ на экране, то есть результат зависит от фона. -Разбор по пикселям показывает, что зависимость узкая: - -| пиксель | первый проход | второй проход | зависит от фона? | -|---|---|---|---| -| спрайт непрозрачен в x | закрашен цветом спрайта | XOR с цветом из x−1 | **нет** | -| прозрачен в x, непрозрачен в x−1 | фон | фон XOR цвет | **да** | -| прозрачен в обоих | фон | фон | нет (не рисуем) | - -То есть от фона зависит только **кайма в один пиксель по левым кромкам -силуэта**. На чёрном фоне (а Тень почти всегда на нём — уровень 4 у -зеркала, уровень 6, бой на 12-м) `фон XOR цвет == цвет`, и запечка точна. -На светлом фоне оригинал подкрасит эту кайму, мы — нет. - -**Это осознанное расхождение, в `impl_diff.md` при реализации.** - -## 3. Замеры на реальных ассетах - -Прогон алгоритма по правильным наборам (`SDLPoP/data/KID` + -`SDLPoP/data/SHADOW`): - -| набор | кадров | макс. габарит | разных цветов | -|---|---:|---|---:| -| Кид (chtab_2) | 219 | 56×57 | 44 | -| SHADOW.DAT (chtab_5 на ур. 12) | 32 | 49×38 | 22 | -| **вместе** | **251** | 57×57 (с учётом +1 px сдвига) | **45** | - -Цветов вместе почти столько же, сколько у одного Кида: SHADOW.DAT сидит на -той же палитре, новых сочетаний XOR почти не даёт. Всего пикселей во всех -кадрах Тени — 91 045. - -### Насколько заметна подмена - -«Затронуто» само по себе ничего не говорит — важно, НА СКОЛЬКО сместился -цвет. Порог различимости на плоской заливке ~30-40 единиц евклида в RGB -(максимум возможного — 441). Пробовал три стратегии подбора: - -- **A** — оставить N самых частых цветов ТОЧНО, остальные в ближайший; -- **B** — взвешенный k-means по всем цветам (двигает вообще все); -- **C** — гибрид: часть слотов под точные частые, остаток — кластеры хвоста. - -| палитра | стратегия | изменено px | заметно (40..90) | сильно (>90) | худшая | -|---|---|---:|---:|---:|---:| -| 16 | A самые частые | 959 (1,05 %) | 569 | 381 | 128 | -| **16** | **C гибрид 5+11** | 6 984 (7,67 %) | 1 078 | **76** | 114 | -| 32 | A самые частые | 50 (0,05 %) | 40 | 4 | 113 | -| 32 | C гибрид 28+4 | 73 (0,08 %) | 58 | **0** | 90 | - -Читается так. При 32 цветах всё практически идеально: 73 пикселя на 251 -кадр, грубых промахов нет вовсе. При 16 цветах выбор стратегии виден: -«самые частые» трогает меньше пикселей (959), но 381 из них уезжает СИЛЬНО; -гибрид размазывает ошибку — грубых остаётся 76, то есть примерно **0,3 -пикселя на кадр**. - -### Решение: **16 цветов, стратегия C (гибрид 5 + 11)** - -Числа выше — про пиксели, а решает глаз. Отрендерил одни и те же кадры в -трёх видах (точный цвет / 32 / 16) и сравнил в увеличении ×3: **отличий -не видно**. Объяснение в самих числах: перцептивно значимых пикселей при -16 цветах гибридом — 1 154 на 251 кадр, это ~4,6 пикселя на кадр при -~1 000 видимых, и они РАССЫПАНЫ по контуру, а не собраны в пятно. - -Поэтому берём 16, а не 32: экономим блок палитры (пригодится под будущие -наборы — принцесса, визирь, мышь), а разница неразличима. - -**Но стратегия обязана быть гибридной.** При 16 цветах «взять самые -частые» впятеро хуже по грубым промахам (381 пиксель против 76), и это -единственное место, где выбор стратегии виден. Гибрид: 5 самых частых -берём ТОЧНО, оставшиеся 11 слотов отдаём под взвешенные кластеры хвоста. - -Если в реальной игре кайма всё же будет резать глаз — переход на 32 цвета -это одна константа в упаковщике и один блок палитры, данные не меняются. - -## 4. Палитра: что занято и куда класть - -| блок | кто | примечание | -|---|---|---| -| 0x30..0x3F | VGA-16 | общая; из неё цвет вспышки, пузырьки зелий, отладочная метка | -| 0x40..0x4F | chtab_1 | зелья, пламя | -| 0x50..0x5F | env тайлсета | **меняется** подземелье/дворец | -| 0x60..0x6F | wall тайлсета | **меняется** подземелье/дворец | -| 0x70..0x7F | chtab_2 | Кид | -| 0x80..0x8F | chtab_0 | меч в руке | -| 0x90..0x9F | chtab_5 | страж, перезаливается цветом стража | - -Занято 112 слотов из 256, **свободно 144** — девять выровненных блоков по -16. Оговорки: 0xFF в наших атласах это маркер прозрачности, а запись 0 -правит `flash_bg`, так что блоки 0x00 и 0xF0 лучше не трогать. - -**Берём 0xA0..0xAF** (16 слотов) — сразу за стражем, персонажи остаются -сгруппированы, а блок 0xB0 остаётся свободным (под 32 цвета Тени, если -понадобится, или под будущие наборы). - -Важно: в наших атласах прозрачность кодируется байтом 0xFF, а исходный -индекс 0 в них означает «прозрачно». У Тени **чёрный — настоящий цвет** -(это XOR-погашенная середина силуэта, 51 % всех её пикселей), поэтому у -неё маппинг свой: «нет пикселя» → 0xFF, цвет k → 0xA0 + k, и слот 0xA0 = -чёрный НЕПРОЗРАЧНЫЙ. - -## 5. Объём - -251 кадр против 219 у Кида — по страницам EMM примерно как нынешний -набор Кида (28 страниц), плюс пара на кадры из SHADOW.DAT. При 215 свободных -страницах на старте (memory `sprinter_emm_budget`) это не проблема. - -Кадры смерти («убитый Кид») пока НЕ вырезаем — экономия несколько -страниц, а риск промахнуться мимо нужного кадра реальный: Тень на 12-м -уровне умирает. - -## 6. План работ - -**Ш0. Довезти SHADOW.DAT.** Его у нас нет вовсе (см. BUG-SHADOW-SET) — -добавить каталог ассетов и правило в Makefile рядом с GUARD/SKEL/VIZIER. - -**Ш1. Упаковщик** `toolchain/pop_pack_shadow.py`: прогнать оба набора -через алгоритм §1, собрать 16-цветную палитру гибридом (10 точных + 6 -кластеров хвоста), выдать -`poc/res/shadow/shadow0..N.atl` + `shadow.pal` + `pop_shadow_atlas.h`. -Критерий: предпросмотр PNG совпадает с видом Тени в SDLPoP. - -**Ш2. Загрузка**: `pop_shadow_load()` рядом с `pop_kid_load`, палитра в -0xA0..0xAF, и обе страницы дабл-буфера (как `bg_load_tile_pal`). -Грузить ЛЕНИВО — только когда на уровне есть Тень (4, 5, 6, 12), иначе -28 страниц EMM висят зря. - -**Ш3. Отрисовка**: одно место — `pop_cdraw.c:551..568`, где выбирается -`pages`. Сейчас там для соперника берётся `gp`, а для не-боевых кадров -Тени подменяется на `kidp`; станет «charid == CHARID_1_SHADOW → shadowp» -БЕЗ подмены: обе половины (кадры Кида и кадры SHADOW.DAT) лежат в ОДНОМ -атласе Тени, так что ветка становится проще нынешней. - -**Ш4. Проверка в MAME**: уровень 4 (Тень у зеркала), уровень 6 (Тень -крадёт зелье), уровень 12 (бой с Тенью — там она в боевых кадрах, то -есть проверяется вторая половина набора). - -## 7. Что проверить артефактом до Ш3 - -1. Индекс кадра для Тени в боевых кадрах: `frame_tbl_guard` адресуется - как `frame + add_frame - 149` (`seg006.c:535`), то есть у нашего - атласа Тени нумерация двух половин должна совпадать с тем, что уже - делает `pop_frame_tbl_is_guard`. -2. Перекраска Тени НЕ нужна: SHADOW.DAT приходит уже в палитре Кида, а - `curr_guard_color` у не-стражей равен 0 (`seg002:183`). Ветку - `pop_guard_set_palette` для типа 4 звать нельзя — она затрёт палитру - Тени палитрой стража. - ---- - -# 8. Как сделали (2026-08-20) - -- `toolchain/pop_pack_shadow.py` — запекает обе половины, собирает - 16-цветную палитру гибридом (получилось 5 частых точно + 11 кластеров - хвоста), пишет `poc/res/shadow/sk0..27.atl`, `sf0..3.atl` и - `roomtest/pop_shadow_atlas.h`. Итог: **251 спрайт, 32 EMM-страницы, - 225 178 Б**. -- `roomtest/pop_shadow.c/.h` (банк 8) — загрузка. **Грузим один раз при - старте**, а не по уровням: страниц EMM с запасом, а забыть перезагрузку - на границе легко — ровно так и появился BUG-SHADOW-SET. -- `pop_cdraw.c` — одна ветка выбора атласа; заодно брызги урона теперь - берут атлас отдельным указателем (`spl`), потому что у оригинала они - всегда из «своего» chtab, а у Тени кадр может прийти из другой - половины набора. - -## Грабля, стоившая одного прогона - -Набор загрузился, силуэт нарисовался правильной формы — и **целиком -чёрный**. Причина: `gfx_pal_fload("KID\\kid.pal")` заливает ВСЕ 256 -записей палитры и затирает любые слоты, выставленные до него. Ровно та -же беда уже была с тайлсетом — сразу за этим вызовом стоит -`pop_bg_pal_apply`. Поэтому палитра Тени вынесена в отдельный -`pop_shadow_pal_apply()` и зовётся там же, а не внутри загрузки атласов. - -**Правило на будущее: любой новый набор палитровых слотов красится ПОСЛЕ -kid.pal, рядом с pop_bg_pal_apply.** - -## Проверено - -Уровни 4 (рождение из зеркала), 5 (кража зелья), 6 (прыжок через -пропасть) и 12 (бой — там работает вторая половина набора, `sf*`) — -прогон пользователя 2026-08-20, расхождений не найдено. - -Расхождение из §2 (кайма в один пиксель по левым кромкам на НЕчёрном -фоне) записано в `impl_diff.md`. diff --git a/applications/PoP/docs/shadow_render.md b/applications/PoP/docs/shadow_render.md deleted file mode 100644 index af0a39b..0000000 --- a/applications/PoP/docs/shadow_render.md +++ /dev/null @@ -1,160 +0,0 @@ -# Отрисовка Тени (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/PoP/docs/sound_plan.md b/applications/PoP/docs/sound_plan.md deleted file mode 100644 index f7c5234..0000000 --- a/applications/PoP/docs/sound_plan.md +++ /dev/null @@ -1,963 +0,0 @@ -# Звук в порте 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), -а roomtest — 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` из roomtest. -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 | клинок о клинок | `roomtest.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/PoP/docs/status_line_text.md b/applications/PoP/docs/status_line_text.md deleted file mode 100644 index 75cd199..0000000 --- a/applications/PoP/docs/status_line_text.md +++ /dev/null @@ -1,218 +0,0 @@ -# Текст в нижней статус-строке (строке 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/PoP/poc/Makefile b/applications/PoP/poc/Makefile deleted file mode 100644 index 5a3a809..0000000 --- a/applications/PoP/poc/Makefile +++ /dev/null @@ -1,52 +0,0 @@ -# PoC порта Prince of Persia (applications/PoP/docs/PORT_PLAN.md §5). -# --memory huge, БЕЗ --bank. -# -# ПОЧЕМУ huge, а НЕ small (исправлено 2026-07-16): poc использует -# raw-клавиатуру (kbd_raw_open → IM2-таблица). Буферы IM2 (_irq_vec_buf, -# BSS) ОБЯЗАНЫ жить в W2 (0x8000-0xBFFF) — во время прерывания W1/W3 -# могут быть перемаплены DSS (см. libc/irq/_irq_table.c, memory/ -# fps_divider: «verified tiny/big/huge, small=EINVAL»). --memory small -# пулит W1+W2 в плоские ~32КБ и чейнит DATA за CODE — при небольшом CODE -# BSS уезжает в W1 (<0x8000), и _irq_table_ref отдаёт EINVAL → -# kbd_raw_open молча возвращал -1, poc печатал ошибку УЖЕ в графическом -# режиме (невидимо) и выходил в prompt. huge кладёт CODE в W1, а -# DATA/BSS/STACK/HEAP жёстко в W2 → IM2 работает. Цена: раздел 16КБ -# CODE / 16КБ DATA вместо общего 32КБ-пула small — сейчас влезает с -# запасом; при росте настоящего PoP CODE>16КБ понадобится банк под код. -# -# --bank НЕ нужен: gfx_blit_part()/atlas_load() сами временно трогают W3 -# (видеобанк / чтение атласа) — банк room.c в W3 давал вероятностный -# «снег»; банк в W1 несовместим с CODE=W1 (трамплин переключения W1 сам -# бы уехал). sprintf() заменён на ручное hex-форматирование пути в -# tile_atlas_load() (единственный потребитель printf, ~2.9КБ). - -EXAMPLE := poc -MEMORY ?= huge -EXTRA_FLAGS ?= --gfx 256 -EXTRA_SRCS := room.c -TILE_ATLASES := res/tiles/tile01.atl res/tiles/tile14.atl res/tiles/tile03.atl \ - res/tiles/tile13.atl res/tiles/tile0e.atl res/tiles/tile0b.atl -EXTRA_DATA := tools/kid.atl tools/room.pal res/room1.dat $(TILE_ATLASES) -include $(CURDIR)/../sdk.mk - -LEVEL1_BIN := $(POP_ROOT)/SDLPoP/data/LEVELS/res2001.bin - -tools/kid.raw tools/kid.pal: tools/gen_kid_placeholder.py - cd tools && python3 gen_kid_placeholder.py - -tools/kid.atl: tools/kid.raw - python3 $(PROJ_ROOT)/toolchain/mkatlas.py $@ tools/kid.raw:16x16:1x12 - -res/room1.dat: tools/extract_room.py $(LEVEL1_BIN) - python3 tools/extract_room.py $(LEVEL1_BIN) 1 res/room1.dat - -res/tiles/1F-0.png: tools/gen_tile_placeholders.py - cd tools && python3 gen_tile_placeholders.py - -tools/room.pal $(TILE_ATLASES): tools/kid.pal res/tiles/1F-0.png tools/build_room_palette.py - cd tools && python3 build_room_palette.py - -# make_disk.py упаковывает EXTRA_DATA на диск ПОД БАЗОВЫМ ИМЕНЕМ -# (плоская ФС) — tileNN.atl/room1.dat оказываются в корне рядом с -# kid.atl/room.pal, room.c/poc.c грузят их без пути. -$(EXAMPLE).exe: room.c tools/kid.atl tools/room.pal res/room1.dat $(TILE_ATLASES) diff --git a/applications/PoP/poc/level.h b/applications/PoP/poc/level.h deleted file mode 100644 index fe119df..0000000 --- a/applications/PoP/poc/level.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * level.h — структуры уровня PoP под наш движок. - * - * Формат — POP-DAT-FormatSpecifications.pdf §3.4 (DAT 1.0): комната = - * 30 тайлов (10 колонок x 3 ряда), foretable даёт тип тайла, - * backtable — модификатор/состояние (семантика зависит от типа). - * Адресация тайла: tile = (room-1)*30 + tileOffset, tileOffset 0-9 = - * верхний ряд, 10-19 = средний, 20-29 = нижний, слева направо. - * - * Фаза 1 (applications/PoP/docs/PORT_PLAN.md §5, сузили объём): - * только геометрия (пол/стены/провалы) для коллизий — двери, факелы, - * ловушки, гарды НЕ используются (структуры под них здесь тоже - * упрощены/оставлены как заготовка на потом, см. §7 плана Фаза 2). - */ -#ifndef LEVEL_H -#define LEVEL_H - -#include - -/* 1 (не 24) — PoC грузит и рисует только комнату 1; расширить, когда - * появится настоящий level_load() на несколько комнат. */ -#define LEVEL_ROOMS 1 /* комнаты нумеруются 1..24 в файле, - * здесь индекс 0..23 = комната N+1 */ -#define ROOM_COLS 10 -#define ROOM_ROWS 3 -#define ROOM_TILES (ROOM_COLS * ROOM_ROWS) /* 30 */ - -/* --- Типы тайлов (Table 7 спецификации) --- */ -#define TILE_TYPE(byte) ((uint8_t)((byte) & 0x1F)) -#define TILE_MODIFIER(byte) ((uint8_t)(((byte) >> 5) & 1)) - -enum { - TILE_EMPTY = 0x00, - TILE_FLOOR = 0x01, - TILE_SPIKES = 0x02, - TILE_PILLAR = 0x03, - TILE_GATE = 0x04, - TILE_STUCK_BUTTON = 0x05, - TILE_DROP_BUTTON = 0x06, - TILE_TAPESTRY = 0x07, - TILE_PILLAR_BOTTOM = 0x08, - TILE_PILLAR_TOP = 0x09, - TILE_POTION = 0x0A, - TILE_LOOSE = 0x0B, - TILE_TAPESTRY_TOP = 0x0C, - TILE_MIRROR = 0x0D, - TILE_DEBRIS = 0x0E, - TILE_RAISE_BUTTON = 0x0F, - TILE_EXIT_LEFT = 0x10, - TILE_EXIT_RIGHT = 0x11, - TILE_CHOPPER = 0x12, - TILE_TORCH = 0x13, - TILE_WALL = 0x14, - TILE_SKELETON = 0x15, - TILE_SWORD = 0x16, - TILE_BALCONY_LEFT = 0x17, - TILE_BALCONY_RIGHT = 0x18, - TILE_LATTICE_PILLAR = 0x19, - TILE_LATTICE_SUPPORT= 0x1A, - TILE_LATTICE_SMALL = 0x1B, - TILE_LATTICE_LEFT = 0x1C, - TILE_LATTICE_RIGHT = 0x1D, - TILE_TORCH_DEBRIS = 0x1E, - TILE_NULL = 0x1F -}; - -/* Твёрдые тайлы — Фаза 1 (только геометрия); классификация наша, для - * коллизий движка, не часть исходного формата. Двери/шипы/дробилки и - * т.п. сознательно исключены из объёма Фазы 1 (см. §5 плана) — при - * встрече в реальных данных трактовать как проходимые до Фазы 2. - * - * tile_is_solid() — "есть опора сверху" (вертикальный смысл: можно - * стоять НА этом тайле) — Floor ТОЖЕ solid в этом смысле! Для - * горизонтальной коллизии (можно ли ВОЙТИ в эту клетку сбоку) нужен - * ОТДЕЛЬНЫЙ предикат — см. tile_blocks_side ниже. Баг 2026-07-16: - * col_blocked() в poc.c ошибочно звал tile_is_solid() для бокового - * упора — Floor блокировал сам себя, Кид не мог сдвинуться с места - * стоя на полу. */ -static inline uint8_t tile_is_solid(uint8_t byte) -{ - switch (TILE_TYPE(byte)) { - case TILE_FLOOR: - case TILE_PILLAR: - case TILE_PILLAR_BOTTOM: - case TILE_PILLAR_TOP: - case TILE_WALL: - case TILE_BALCONY_LEFT: - case TILE_BALCONY_RIGHT: - return 1; - default: - return 0; - } -} - -/* tile_blocks_side() — настоящая преграда СБОКУ (нельзя войти в - * клетку по горизонтали): Wall/Pillar-семейство. Floor/Balcony НЕ - * блокируют — по ним идёшь (тайл под ногами, не впереди). Lattice- - * колонны (0x19-0x1D) — узкие, Кид физически проходит мимо (см. - * gen_tile_placeholders.py draw_lattice_like) — тоже НЕ блокируют. */ -static inline uint8_t tile_blocks_side(uint8_t byte) -{ - switch (TILE_TYPE(byte)) { - case TILE_PILLAR: - case TILE_PILLAR_BOTTOM: - case TILE_PILLAR_TOP: - case TILE_WALL: - return 1; - default: - return 0; - } -} - -/* --- Тайл и комната --- */ -typedef struct { - uint8_t type; /* foretable byte (rrmccccc — см. TILE_TYPE/MODIFIER) */ - uint8_t state; /* backtable byte — модификатор/состояние */ -} tile_t; - -typedef struct { - tile_t tiles[ROOM_TILES]; /* индекс = tileOffset 0..29 */ - uint8_t link_left, link_right; /* links-блок: 0 = нет соседа */ - uint8_t link_up, link_down; - uint8_t guard_location; /* 0..29; 30 (и выше) = нет гарда */ - int8_t guard_direction; /* 0 = вправо, -1 = влево */ - uint8_t guard_skill; /* 0..9 */ - uint8_t guard_colour; /* индекс палитры, Table 11 */ - /* door I/II (событийные цепочки) — Фаза 2, не здесь */ -} room_t; - -typedef struct { - room_t rooms[LEVEL_ROOMS]; /* индекс 0 = комната 1 (файл 1-based) */ - uint8_t start_room; /* 1..24 */ - uint8_t start_location; /* 0..29 */ - int8_t start_direction; /* 0 = вправо, -1 = влево */ -} level_t; - -/* Тайл по (room 1-based, col 0-9, row 0-2). */ -static inline tile_t *level_tile(level_t *lv, uint8_t room, uint8_t col, uint8_t row) -{ - return &lv->rooms[room - 1].tiles[row * ROOM_COLS + col]; -} - -/* Загружает ОДНУ комнату + стартовую позицию уровня из файла в формате - * tools/extract_room.py (63 Б: foretable[30]+backtable[30] той комнаты - * + start_room+start_pos+start_dir, вырезанные из res20NN.bin — layout - * подтверждён декодом байт 2026-07-15/16: файл начинается СРАЗУ с - * foretable[720], потом backtable[720], без заголовка; start_position - * — смещение 2112, сверено со структурой level_type в SDLPoP/src/ - * types.h). Заполняет lv->start_room/start_location/start_direction. - * 0 — OK, -1 — файл не найден/короче 63 Б. */ -int level_load_room(level_t *lv, uint8_t room, const char *path); - -#endif diff --git a/applications/PoP/poc/poc.c b/applications/PoP/poc/poc.c deleted file mode 100644 index 5c03ad5..0000000 --- a/applications/PoP/poc/poc.c +++ /dev/null @@ -1,264 +0,0 @@ -/* - * poc.c — PoC порта Prince of Persia (applications/PoP/docs/PORT_PLAN.md - * §5): проверяем управление (raw-клавиатура, held-state) + коллизию - * по краям экрана + анимацию ходьбы + прыжок/присед поверх готового - * спрайтового движка (sprite.h). - * - * ВАЖНО: персонаж — ВРЕМЕННАЯ ЗАГЛУШКА (лицензированный спрайт-пак - * third_party/16x16-RPG-characters через tools/gen_kid_placeholder.py, - * тот же источник, что уже использует examples/rpgwalk), НЕ графика - * оригинальной Prince of Persia — см. §5 и §8.5 плана. У заглушки нет - * отдельных поз прыжка/приседа — механика (тайминг дуги, состояние, - * коллизия с полом) проверяется на том же спрайте без смены позы; - * визуально это упрощение, не финальный вид. - * - * Дуга прыжка (jump_height[]) — СВОЯ, приблизительная (не таблица - * смещений оригинала — см. §6 плана: авторские таблицы кадров решено - * не переносить, только код/структуры). - * - * Нет ещё (следующие итерации): реальный уровень/фон по BLUETYPE, - * рывок вбок при прыжке с разбега, зацепление за уступ. - */ -#include -#include -#include -#include -#include -#include -#include "level.h" -#include "room.h" - -/* Комната 1 уровня 1 — РЕАЛЬНАЯ геометрия (foretable/backtable), - * вырезана tools/extract_room.py из applications/PoP/SDLPoP/data/ - * LEVELS/res2001.bin (level_load_room(), см. level.h/room.c) — не - * плейсхолдер. Верхний ряд (row0) — floor-уступ на cols 3-7 (там - * реально стоит персонаж в оригинале), стены по cols 8-9; ряды 1-2 — - * ниже уступа (торч/колонны/пол — Фаза 1 просто их отрисовывает по - * тем же типам, без многоуровневой физики падения). */ -static level_t test_level; - -#define CHAR_ROW 0 /* ряд, где стоит персонаж (floor-уступ room1) */ -#define ROW_Y(r) ((r) * 64) /* room_row_h все по 64 */ -#define GROUND_Y ROW_Y(CHAR_ROW + 1) /* низ ряда CHAR_ROW = верх пола */ -#define KIDY (GROUND_Y - 16) /* y спрайта (16 px высотой) */ -#define SPEED 2 /* px/кадр — заглушка, не авторский темп */ - -/* Настоящая преграда (Wall/Pillar) слева/справа от кандидата x в - * CHAR_ROW — блокирует движение (грубая проверка по краям хитбокса - * 16px, без под-тайловой подгонки — для PoC достаточно, см. - * PORT_PLAN.md §6). tile_blocks_side(), НЕ tile_is_solid(): Floor — - * тайл, на котором Кид СТОИТ (тот же CHAR_ROW), tile_is_solid() его - * тоже считает "твёрдым" (можно стоять сверху) — если проверять им же - * боковую преграду, Кид не мог сдвинуться с собственного пола (баг, - * найден 2026-07-16). */ -static uint8_t col_blocked(int x) -{ - uint8_t c0, c1; - - if (x < 0 || x + 15 >= ROOM_COLS * ROOM_TILE_W) - return 1; - c0 = (uint8_t)(x / ROOM_TILE_W); - c1 = (uint8_t)((x + 15) / ROOM_TILE_W); - if (tile_blocks_side(level_tile(&test_level, 1, c0, CHAR_ROW)->type)) - return 1; - if (tile_blocks_side(level_tile(&test_level, 1, c1, CHAR_ROW)->type)) - return 1; - return 0; -} - -/* Ленты атласа: dir*3+frame, dir 0=вниз/1=влево/2=вправо/3=вверх, - * 3 кадра маятника на направление (см. tools/gen_kid_placeholder.py). */ -#define DIR_DOWN 0 -#define DIR_LEFT 1 -#define DIR_RIGHT 2 - -/* Дуга прыжка: своя, приблизительная (не авторская таблица, см. шапку - * файла) — высота над полом (px) по кадрам 0..19, УЖЕ ПОЛНЫЙ горб - * (подъём 2→22 к элементу 10, спуск обратно к 2 к элементу 19) — БЕЗ - * зеркалирования в коде, массив читается один раз целиком. БАГ, - * найденный пользователем 2026-07-15: раньше код ЕЩЁ РАЗ зеркалил - * этот уже полный горб на 40 кадров — получалось два полных прыжка - * подряд от одного триггера (не проблема клавиатуры/декодера, чистая - * рассинхронизация данных и комментария). 20 кадров @ 50 Гц ~= 0.4 с. */ -static const uint8_t jump_height[20] = { - 2, 4, 7, 10, 13, 16, 18, 20, 21, 22, - 22, 21, 20, 18, 16, 13, 10, 7, 4, 2 -}; -#define JUMP_FRAMES 20 - -static atlas_t at; -static sprite_t kid; -static uint8_t facing = DIR_DOWN; /* текущее направление анимации */ -static uint8_t jumping = 0; /* 0 = на земле */ -static uint8_t jump_t = 0; /* кадр дуги, 0..JUMP_FRAMES-1 */ -static uint8_t crouching = 0; -/* Прыжок — level-triggered НАМЕРЕННО (не edge-detect): если UP всё ещё - * зажат к моменту приземления — следующий прыжок стартует СРАЗУ (цепочка - * прыжков, пока держишь); отпустил раньше — второй прыжок не начнётся - * сам, только по следующему нажатию. Раньше здесь были up_prev/ - * jump_cooldown — попытка "починить" ровно ЭТО поведение, приняв его - * за баг; убрано 2026-07-15 после уточнения желаемого поведения. */ - -static void draw_room(void) -{ - setfillstyle(SOLID_FILL, BLACK); - bar(0, 0, 319, 255); - room_draw(&test_level, 1); - setcolor(LIGHTGRAY); - outtextxy(60, 4, "PoP PoC: hold LEFT/RIGHT to walk, ESC to quit"); - outtextxy(4, 14, "(placeholder tiles -- not original PoP art)"); -} - -/* HUD-плашка состояния (нет отдельной позы прыжка/приседа — статус - * текстом, рисуется банком 0x50, heal спрайтового движка её не - * трогает — как fps-плашка в examples/rpgwalk). */ -static void show_state(uint8_t jump, uint8_t crouch) -{ - setfillstyle(SOLID_FILL, BLACK); - bar(0, 24, 60, 32); - setcolor(YELLOW); - if (jump) - outtextxy(0, 24, "JUMP"); - else if (crouch) - outtextxy(0, 24, "CROUCH"); -} - -static void set_facing(uint8_t dir) -{ - if (facing == dir) - return; - facing = dir; - sprite_anim(&kid, (uint8_t)(dir * 3), (uint8_t)(dir * 3 + 2), - 6, ANIM_PINGPONG); -} - -int main(void) -{ - uint8_t page, hidden; - int x; - - if (level_load_room(&test_level, 1, "room1.dat") != 0 && - level_load_room(&test_level, 1, "a:\\room1.dat") != 0) { - puts("room1.dat not found"); - return 1; - } - /* Стартовая позиция. В данных room1 start_location = tileOffset 0 - * (row0/col0) — а там EMPTY (провал, без пола); в Фазе 1 нет физики - * падения, поэтому для PoC ставим Кида на floor-уступ (col 3, где он - * реально стоит в оригинале). Когда появится многоуровневая физика — - * брать col из start_location. start_direction: -1 = влево, 0 = - * вправо (level.h). */ - x = 3 * ROOM_TILE_W; - facing = (test_level.start_direction < 0) ? DIR_LEFT : DIR_RIGHT; - - if (atlas_load(&at, "kid.atl") != 0 && - atlas_load(&at, "a:\\kid.atl") != 0) { - puts("kid.atl not found"); - return 1; - } - if (tile_atlas_load("") == 0 && tile_atlas_load("a:\\") == 0) { - puts("tile atlases not found"); - atlas_free(&at); - return 1; - } - - initgraph(); - /* room.pal = EGA16 + Kid + Floor + Wall — ОДНА палитра на всё, - * собрана tools/build_room_palette.py (см. --seed-pal в - * toolchain/png_strip.py) — Kid и тайлы на экране одновременно, - * их "свои" цвета обязаны жить в одной таблице. */ - if (gfx_pal_fload(0, "room.pal") < 0) - gfx_pal_fload(0, "a:\\room.pal"); - gfx_pal_sync(); - gfx_sprite_clip(0); /* коллизия по краям гарантирует границы */ - - /* kid.atl — ОДНА лента (12 кадров вертикально: dir*3+кадр, см. шапку - * файла и examples/rpgwalk); индекс atlas_sprite_init — это НОМЕР - * ЛЕНТЫ (персонажа), а не кадра. Лента одна → всегда 0. Стартовый - * кадр направления выставляем sprite_frame (вертикальная лента, fh=16: - * кадр N на sy=N*16). Баг Соннета (найден 2026-07-16): здесь стоял - * facing*3 как индекс ЛЕНТЫ — при старте лицом влево (idx 3) читался - * мусор за каталогом атласа → мусорные w/h/src → блит спрайта заливал - * пол-экрана «снегом». */ - atlas_sprite_init(&kid, &at, 0); - sprite_frame(&kid, 0, (int)(facing * 3) * 16); - kid.x = x; - kid.y = KIDY; - sprite_show(&kid); - - for (page = 0; page < 2; page++) { /* фон + спрайт на обе страницы */ - gfx_set_draw_page(page); - draw_room(); - sprite_update(&kid, 1); - } - gfx_set_visible_page(0); - - /* kbd_raw требует BSS в W2 (IM2-таблица) — недоступно в --memory small - * (там BSS может уехать в W1 → EINVAL); poc собирается --memory huge - * (CODE в W1, DATA/BSS в W2). closegraph ДО puts — иначе сообщение - * ушло бы в графический режим (невидимо), а программа молча вышла бы - * в prompt (баг Соннета, найден 2026-07-16). */ - if (kbd_raw_open() != 0) { - closegraph(); - puts("kbd_raw_open failed (need memory mode with BSS in W2)"); - atlas_free(&at); - return 1; - } - - for (;;) { - uint8_t moving = 0; - int y = KIDY; - - if (jumping) { - /* дуга идёт сама; направлением можно скользить вбок, - * поза не меняется (нет отдельного кадра прыжка) */ - if (kbd_raw_down(KBD_LEFT)) { - if (!col_blocked(x - SPEED)) x -= SPEED; - } else if (kbd_raw_down(KBD_RIGHT)) { - if (!col_blocked(x + SPEED)) x += SPEED; - } - y = KIDY - jump_height[jump_t]; - jump_t++; - if (jump_t >= JUMP_FRAMES) { - jumping = 0; - y = KIDY; - } - } else if (crouching) { - if (!kbd_raw_down(KBD_DOWN)) - crouching = 0; - } else if (kbd_raw_down(KBD_UP)) { - jumping = 1; - jump_t = 0; - } else if (kbd_raw_down(KBD_DOWN)) { - crouching = 1; - } else if (kbd_raw_down(KBD_LEFT)) { - if (!col_blocked(x - SPEED)) x -= SPEED; - set_facing(DIR_LEFT); - moving = 1; - } else if (kbd_raw_down(KBD_RIGHT)) { - if (!col_blocked(x + SPEED)) x += SPEED; - set_facing(DIR_RIGHT); - moving = 1; - } - if (!moving && !jumping && (sprite_anim_status(&kid) & SPR_ANIM_ON)) - sprite_anim_stop(&kid, (int8_t)(facing * 3)); - - sprite_move(&kid, x, y); - - if (kbd_raw_down(KBD_ESC)) - break; - - hidden = gfx_get_visible_page() ^ 1; - gfx_set_draw_page(hidden); - show_state(jumping, crouching); - sprite_update(&kid, 1); - gfx_wait_vsync(); - gfx_set_visible_page(hidden); - } - - kbd_raw_close(); - closegraph(); - tile_atlas_free(); - atlas_free(&at); - return 0; -} diff --git a/applications/PoP/poc/res/bg/bg.arc b/applications/PoP/poc/res/bg/bg.arc deleted file mode 100644 index cc36258..0000000 Binary files a/applications/PoP/poc/res/bg/bg.arc and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pal_env0.atl b/applications/PoP/poc/res/bg/pal_env0.atl deleted file mode 100644 index 70c510c..0000000 Binary files a/applications/PoP/poc/res/bg/pal_env0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pal_env1.atl b/applications/PoP/poc/res/bg/pal_env1.atl deleted file mode 100644 index 634d398..0000000 Binary files a/applications/PoP/poc/res/bg/pal_env1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pal_env2.atl b/applications/PoP/poc/res/bg/pal_env2.atl deleted file mode 100644 index ed26d18..0000000 Binary files a/applications/PoP/poc/res/bg/pal_env2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pal_env3.atl b/applications/PoP/poc/res/bg/pal_env3.atl deleted file mode 100644 index 6a86a5a..0000000 Binary files a/applications/PoP/poc/res/bg/pal_env3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pal_env4.atl b/applications/PoP/poc/res/bg/pal_env4.atl deleted file mode 100644 index bb17cdf..0000000 Binary files a/applications/PoP/poc/res/bg/pal_env4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pal_env5.atl b/applications/PoP/poc/res/bg/pal_env5.atl deleted file mode 100644 index cf17816..0000000 Binary files a/applications/PoP/poc/res/bg/pal_env5.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pal_env6.atl b/applications/PoP/poc/res/bg/pal_env6.atl deleted file mode 100644 index 8b415b4..0000000 Binary files a/applications/PoP/poc/res/bg/pal_env6.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pal_env7.atl b/applications/PoP/poc/res/bg/pal_env7.atl deleted file mode 100644 index 260d801..0000000 Binary files a/applications/PoP/poc/res/bg/pal_env7.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pal_env8.atl b/applications/PoP/poc/res/bg/pal_env8.atl deleted file mode 100644 index ebfd0fa..0000000 Binary files a/applications/PoP/poc/res/bg/pal_env8.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pal_env9.atl b/applications/PoP/poc/res/bg/pal_env9.atl deleted file mode 100644 index 671c49f..0000000 Binary files a/applications/PoP/poc/res/bg/pal_env9.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pal_fore.atl b/applications/PoP/poc/res/bg/pal_fore.atl deleted file mode 100644 index 82e017f..0000000 Binary files a/applications/PoP/poc/res/bg/pal_fore.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pal_tile.pal b/applications/PoP/poc/res/bg/pal_tile.pal deleted file mode 100644 index df429b2..0000000 Binary files a/applications/PoP/poc/res/bg/pal_tile.pal and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pal_wall.atl b/applications/PoP/poc/res/bg/pal_wall.atl deleted file mode 100644 index f3a72c2..0000000 Binary files a/applications/PoP/poc/res/bg/pal_wall.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pop_bg.pal b/applications/PoP/poc/res/bg/pop_bg.pal deleted file mode 100644 index b574a91..0000000 Binary files a/applications/PoP/poc/res/bg/pop_bg.pal and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pop_bg_atlas.h b/applications/PoP/poc/res/bg/pop_bg_atlas.h deleted file mode 100644 index 9e861ea..0000000 --- a/applications/PoP/poc/res/bg/pop_bg_atlas.h +++ /dev/null @@ -1,47 +0,0 @@ -/* 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/PoP/poc/res/bg/pop_env0.atl b/applications/PoP/poc/res/bg/pop_env0.atl deleted file mode 100644 index 70c510c..0000000 Binary files a/applications/PoP/poc/res/bg/pop_env0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pop_env1.atl b/applications/PoP/poc/res/bg/pop_env1.atl deleted file mode 100644 index 634d398..0000000 Binary files a/applications/PoP/poc/res/bg/pop_env1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pop_env2.atl b/applications/PoP/poc/res/bg/pop_env2.atl deleted file mode 100644 index 5848405..0000000 Binary files a/applications/PoP/poc/res/bg/pop_env2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pop_env3.atl b/applications/PoP/poc/res/bg/pop_env3.atl deleted file mode 100644 index 9bb9bea..0000000 Binary files a/applications/PoP/poc/res/bg/pop_env3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pop_env4.atl b/applications/PoP/poc/res/bg/pop_env4.atl deleted file mode 100644 index 54e5ab8..0000000 Binary files a/applications/PoP/poc/res/bg/pop_env4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pop_env5.atl b/applications/PoP/poc/res/bg/pop_env5.atl deleted file mode 100644 index c55cb87..0000000 Binary files a/applications/PoP/poc/res/bg/pop_env5.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pop_env6.atl b/applications/PoP/poc/res/bg/pop_env6.atl deleted file mode 100644 index ead77d9..0000000 Binary files a/applications/PoP/poc/res/bg/pop_env6.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pop_env7.atl b/applications/PoP/poc/res/bg/pop_env7.atl deleted file mode 100644 index c8ac665..0000000 Binary files a/applications/PoP/poc/res/bg/pop_env7.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pop_env8.atl b/applications/PoP/poc/res/bg/pop_env8.atl deleted file mode 100644 index 6e34d75..0000000 Binary files a/applications/PoP/poc/res/bg/pop_env8.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pop_env9.atl b/applications/PoP/poc/res/bg/pop_env9.atl deleted file mode 100644 index 3b57c9b..0000000 Binary files a/applications/PoP/poc/res/bg/pop_env9.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pop_fore.atl b/applications/PoP/poc/res/bg/pop_fore.atl deleted file mode 100644 index 7d6fe93..0000000 Binary files a/applications/PoP/poc/res/bg/pop_fore.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pop_pot.atl b/applications/PoP/poc/res/bg/pop_pot.atl deleted file mode 100644 index 09fe972..0000000 Binary files a/applications/PoP/poc/res/bg/pop_pot.atl and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pop_tile.pal b/applications/PoP/poc/res/bg/pop_tile.pal deleted file mode 100644 index e62c573..0000000 Binary files a/applications/PoP/poc/res/bg/pop_tile.pal and /dev/null differ diff --git a/applications/PoP/poc/res/bg/pop_wall.atl b/applications/PoP/poc/res/bg/pop_wall.atl deleted file mode 100644 index 9556592..0000000 Binary files a/applications/PoP/poc/res/bg/pop_wall.atl and /dev/null differ diff --git a/applications/PoP/poc/res/font/font.atl b/applications/PoP/poc/res/font/font.atl deleted file mode 100644 index a8a98e1..0000000 Binary files a/applications/PoP/poc/res/font/font.atl and /dev/null differ diff --git a/applications/PoP/poc/res/guard/g0.atl b/applications/PoP/poc/res/guard/g0.atl deleted file mode 100644 index 4041d2c..0000000 Binary files a/applications/PoP/poc/res/guard/g0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/guard/g0_v.atl b/applications/PoP/poc/res/guard/g0_v.atl deleted file mode 100644 index f2e09a1..0000000 Binary files a/applications/PoP/poc/res/guard/g0_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/guard/g1.atl b/applications/PoP/poc/res/guard/g1.atl deleted file mode 100644 index 04b3da3..0000000 Binary files a/applications/PoP/poc/res/guard/g1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/guard/g1_v.atl b/applications/PoP/poc/res/guard/g1_v.atl deleted file mode 100644 index 3968333..0000000 Binary files a/applications/PoP/poc/res/guard/g1_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/guard/g2.atl b/applications/PoP/poc/res/guard/g2.atl deleted file mode 100644 index 5bf728c..0000000 Binary files a/applications/PoP/poc/res/guard/g2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/guard/g2_v.atl b/applications/PoP/poc/res/guard/g2_v.atl deleted file mode 100644 index 27c83b1..0000000 Binary files a/applications/PoP/poc/res/guard/g2_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/guard/g3.atl b/applications/PoP/poc/res/guard/g3.atl deleted file mode 100644 index 3f8bc15..0000000 Binary files a/applications/PoP/poc/res/guard/g3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/guard/g3_v.atl b/applications/PoP/poc/res/guard/g3_v.atl deleted file mode 100644 index 0cc147a..0000000 Binary files a/applications/PoP/poc/res/guard/g3_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/guard/g4.atl b/applications/PoP/poc/res/guard/g4.atl deleted file mode 100644 index 25e914a..0000000 Binary files a/applications/PoP/poc/res/guard/g4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/guard/g4_v.atl b/applications/PoP/poc/res/guard/g4_v.atl deleted file mode 100644 index a385014..0000000 Binary files a/applications/PoP/poc/res/guard/g4_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/guard/guard.arc b/applications/PoP/poc/res/guard/guard.arc deleted file mode 100644 index ad3aed9..0000000 Binary files a/applications/PoP/poc/res/guard/guard.arc and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid.ani b/applications/PoP/poc/res/kid/kid.ani deleted file mode 100644 index ec368af..0000000 Binary files a/applications/PoP/poc/res/kid/kid.ani and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid.arc b/applications/PoP/poc/res/kid/kid.arc deleted file mode 100644 index 112e465..0000000 Binary files a/applications/PoP/poc/res/kid/kid.arc and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid.pal b/applications/PoP/poc/res/kid/kid.pal deleted file mode 100644 index bfbd2cd..0000000 Binary files a/applications/PoP/poc/res/kid/kid.pal and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid0.atl b/applications/PoP/poc/res/kid/kid0.atl deleted file mode 100644 index 865a068..0000000 Binary files a/applications/PoP/poc/res/kid/kid0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid0_v.atl b/applications/PoP/poc/res/kid/kid0_v.atl deleted file mode 100644 index 65fb32c..0000000 Binary files a/applications/PoP/poc/res/kid/kid0_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid1.atl b/applications/PoP/poc/res/kid/kid1.atl deleted file mode 100644 index 2bb6b45..0000000 Binary files a/applications/PoP/poc/res/kid/kid1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid10.atl b/applications/PoP/poc/res/kid/kid10.atl deleted file mode 100644 index fa95752..0000000 Binary files a/applications/PoP/poc/res/kid/kid10.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid10_v.atl b/applications/PoP/poc/res/kid/kid10_v.atl deleted file mode 100644 index 3656692..0000000 Binary files a/applications/PoP/poc/res/kid/kid10_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid11.atl b/applications/PoP/poc/res/kid/kid11.atl deleted file mode 100644 index f9ad644..0000000 Binary files a/applications/PoP/poc/res/kid/kid11.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid11_v.atl b/applications/PoP/poc/res/kid/kid11_v.atl deleted file mode 100644 index 5622703..0000000 Binary files a/applications/PoP/poc/res/kid/kid11_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid12.atl b/applications/PoP/poc/res/kid/kid12.atl deleted file mode 100644 index 64e27f5..0000000 Binary files a/applications/PoP/poc/res/kid/kid12.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid12_v.atl b/applications/PoP/poc/res/kid/kid12_v.atl deleted file mode 100644 index d199db7..0000000 Binary files a/applications/PoP/poc/res/kid/kid12_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid13.atl b/applications/PoP/poc/res/kid/kid13.atl deleted file mode 100644 index 8d3440d..0000000 Binary files a/applications/PoP/poc/res/kid/kid13.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid13_v.atl b/applications/PoP/poc/res/kid/kid13_v.atl deleted file mode 100644 index 1f9e0a7..0000000 Binary files a/applications/PoP/poc/res/kid/kid13_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid14.atl b/applications/PoP/poc/res/kid/kid14.atl deleted file mode 100644 index a63711b..0000000 Binary files a/applications/PoP/poc/res/kid/kid14.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid14_v.atl b/applications/PoP/poc/res/kid/kid14_v.atl deleted file mode 100644 index e5c7157..0000000 Binary files a/applications/PoP/poc/res/kid/kid14_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid15.atl b/applications/PoP/poc/res/kid/kid15.atl deleted file mode 100644 index 2365673..0000000 Binary files a/applications/PoP/poc/res/kid/kid15.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid15_v.atl b/applications/PoP/poc/res/kid/kid15_v.atl deleted file mode 100644 index fa2529b..0000000 Binary files a/applications/PoP/poc/res/kid/kid15_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid16.atl b/applications/PoP/poc/res/kid/kid16.atl deleted file mode 100644 index d7c351b..0000000 Binary files a/applications/PoP/poc/res/kid/kid16.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid16_v.atl b/applications/PoP/poc/res/kid/kid16_v.atl deleted file mode 100644 index 06cbd36..0000000 Binary files a/applications/PoP/poc/res/kid/kid16_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid17.atl b/applications/PoP/poc/res/kid/kid17.atl deleted file mode 100644 index 940aa54..0000000 Binary files a/applications/PoP/poc/res/kid/kid17.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid17_v.atl b/applications/PoP/poc/res/kid/kid17_v.atl deleted file mode 100644 index 6801a59..0000000 Binary files a/applications/PoP/poc/res/kid/kid17_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid18.atl b/applications/PoP/poc/res/kid/kid18.atl deleted file mode 100644 index d964cdc..0000000 Binary files a/applications/PoP/poc/res/kid/kid18.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid18_v.atl b/applications/PoP/poc/res/kid/kid18_v.atl deleted file mode 100644 index b333607..0000000 Binary files a/applications/PoP/poc/res/kid/kid18_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid19.atl b/applications/PoP/poc/res/kid/kid19.atl deleted file mode 100644 index cd127b9..0000000 Binary files a/applications/PoP/poc/res/kid/kid19.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid19_v.atl b/applications/PoP/poc/res/kid/kid19_v.atl deleted file mode 100644 index 25a2f72..0000000 Binary files a/applications/PoP/poc/res/kid/kid19_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid1_v.atl b/applications/PoP/poc/res/kid/kid1_v.atl deleted file mode 100644 index 1c3d8e0..0000000 Binary files a/applications/PoP/poc/res/kid/kid1_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid2.atl b/applications/PoP/poc/res/kid/kid2.atl deleted file mode 100644 index 8e2f3a1..0000000 Binary files a/applications/PoP/poc/res/kid/kid2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid20.atl b/applications/PoP/poc/res/kid/kid20.atl deleted file mode 100644 index 32fd208..0000000 Binary files a/applications/PoP/poc/res/kid/kid20.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid20_v.atl b/applications/PoP/poc/res/kid/kid20_v.atl deleted file mode 100644 index 5f9c5f2..0000000 Binary files a/applications/PoP/poc/res/kid/kid20_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid21.atl b/applications/PoP/poc/res/kid/kid21.atl deleted file mode 100644 index 22cbda0..0000000 Binary files a/applications/PoP/poc/res/kid/kid21.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid21_v.atl b/applications/PoP/poc/res/kid/kid21_v.atl deleted file mode 100644 index deb1f23..0000000 Binary files a/applications/PoP/poc/res/kid/kid21_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid22.atl b/applications/PoP/poc/res/kid/kid22.atl deleted file mode 100644 index a5fd553..0000000 Binary files a/applications/PoP/poc/res/kid/kid22.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid22_v.atl b/applications/PoP/poc/res/kid/kid22_v.atl deleted file mode 100644 index 786b495..0000000 Binary files a/applications/PoP/poc/res/kid/kid22_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid23.atl b/applications/PoP/poc/res/kid/kid23.atl deleted file mode 100644 index b61dd25..0000000 Binary files a/applications/PoP/poc/res/kid/kid23.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid23_v.atl b/applications/PoP/poc/res/kid/kid23_v.atl deleted file mode 100644 index f9326aa..0000000 Binary files a/applications/PoP/poc/res/kid/kid23_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid24.atl b/applications/PoP/poc/res/kid/kid24.atl deleted file mode 100644 index 57623df..0000000 Binary files a/applications/PoP/poc/res/kid/kid24.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid24_v.atl b/applications/PoP/poc/res/kid/kid24_v.atl deleted file mode 100644 index 19c80a4..0000000 Binary files a/applications/PoP/poc/res/kid/kid24_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid25.atl b/applications/PoP/poc/res/kid/kid25.atl deleted file mode 100644 index e543d74..0000000 Binary files a/applications/PoP/poc/res/kid/kid25.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid25_v.atl b/applications/PoP/poc/res/kid/kid25_v.atl deleted file mode 100644 index caece9e..0000000 Binary files a/applications/PoP/poc/res/kid/kid25_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid26.atl b/applications/PoP/poc/res/kid/kid26.atl deleted file mode 100644 index 39ee166..0000000 Binary files a/applications/PoP/poc/res/kid/kid26.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid26_v.atl b/applications/PoP/poc/res/kid/kid26_v.atl deleted file mode 100644 index 82d69f1..0000000 Binary files a/applications/PoP/poc/res/kid/kid26_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid27.atl b/applications/PoP/poc/res/kid/kid27.atl deleted file mode 100644 index 03c1cff..0000000 Binary files a/applications/PoP/poc/res/kid/kid27.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid27_v.atl b/applications/PoP/poc/res/kid/kid27_v.atl deleted file mode 100644 index 0788362..0000000 Binary files a/applications/PoP/poc/res/kid/kid27_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid2_v.atl b/applications/PoP/poc/res/kid/kid2_v.atl deleted file mode 100644 index 0969b6b..0000000 Binary files a/applications/PoP/poc/res/kid/kid2_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid3.atl b/applications/PoP/poc/res/kid/kid3.atl deleted file mode 100644 index 39a3f9f..0000000 Binary files a/applications/PoP/poc/res/kid/kid3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid3_v.atl b/applications/PoP/poc/res/kid/kid3_v.atl deleted file mode 100644 index 3a8199a..0000000 Binary files a/applications/PoP/poc/res/kid/kid3_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid4.atl b/applications/PoP/poc/res/kid/kid4.atl deleted file mode 100644 index fe74474..0000000 Binary files a/applications/PoP/poc/res/kid/kid4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid4_v.atl b/applications/PoP/poc/res/kid/kid4_v.atl deleted file mode 100644 index 28ddd41..0000000 Binary files a/applications/PoP/poc/res/kid/kid4_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid5.atl b/applications/PoP/poc/res/kid/kid5.atl deleted file mode 100644 index bcc0463..0000000 Binary files a/applications/PoP/poc/res/kid/kid5.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid5_v.atl b/applications/PoP/poc/res/kid/kid5_v.atl deleted file mode 100644 index b15e6f2..0000000 Binary files a/applications/PoP/poc/res/kid/kid5_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid6.atl b/applications/PoP/poc/res/kid/kid6.atl deleted file mode 100644 index 1b2fd42..0000000 Binary files a/applications/PoP/poc/res/kid/kid6.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid6_v.atl b/applications/PoP/poc/res/kid/kid6_v.atl deleted file mode 100644 index 8c3f82f..0000000 Binary files a/applications/PoP/poc/res/kid/kid6_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid7.atl b/applications/PoP/poc/res/kid/kid7.atl deleted file mode 100644 index a95a4f0..0000000 Binary files a/applications/PoP/poc/res/kid/kid7.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid7_v.atl b/applications/PoP/poc/res/kid/kid7_v.atl deleted file mode 100644 index afe4669..0000000 Binary files a/applications/PoP/poc/res/kid/kid7_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid8.atl b/applications/PoP/poc/res/kid/kid8.atl deleted file mode 100644 index 04de057..0000000 Binary files a/applications/PoP/poc/res/kid/kid8.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid8_v.atl b/applications/PoP/poc/res/kid/kid8_v.atl deleted file mode 100644 index 5813b5d..0000000 Binary files a/applications/PoP/poc/res/kid/kid8_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid9.atl b/applications/PoP/poc/res/kid/kid9.atl deleted file mode 100644 index ed13db5..0000000 Binary files a/applications/PoP/poc/res/kid/kid9.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid9_v.atl b/applications/PoP/poc/res/kid/kid9_v.atl deleted file mode 100644 index 606cab4..0000000 Binary files a/applications/PoP/poc/res/kid/kid9_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/kid_atlas.h b/applications/PoP/poc/res/kid/kid_atlas.h deleted file mode 100644 index 709400b..0000000 --- a/applications/PoP/poc/res/kid/kid_atlas.h +++ /dev/null @@ -1,41 +0,0 @@ -/* 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/PoP/poc/res/kid/sword.atl b/applications/PoP/poc/res/kid/sword.atl deleted file mode 100644 index 917470a..0000000 Binary files a/applications/PoP/poc/res/kid/sword.atl and /dev/null differ diff --git a/applications/PoP/poc/res/kid/sword_v.atl b/applications/PoP/poc/res/kid/sword_v.atl deleted file mode 100644 index f453547..0000000 Binary files a/applications/PoP/poc/res/kid/sword_v.atl and /dev/null differ diff --git a/applications/PoP/poc/res/music/m24.bin b/applications/PoP/poc/res/music/m24.bin deleted file mode 100644 index d861766..0000000 --- a/applications/PoP/poc/res/music/m24.bin +++ /dev/null @@ -1 +0,0 @@ -{}|{~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/PoP/poc/res/music/m25.bin b/applications/PoP/poc/res/music/m25.bin deleted file mode 100644 index 6559022..0000000 --- a/applications/PoP/poc/res/music/m25.bin +++ /dev/null @@ -1 +0,0 @@ -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/PoP/poc/res/music/m26.bin b/applications/PoP/poc/res/music/m26.bin deleted file mode 100644 index ac75614..0000000 --- a/applications/PoP/poc/res/music/m26.bin +++ /dev/null @@ -1,3 +0,0 @@ -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/PoP/poc/res/music/m27.bin b/applications/PoP/poc/res/music/m27.bin deleted file mode 100644 index 7472064..0000000 --- a/applications/PoP/poc/res/music/m27.bin +++ /dev/null @@ -1 +0,0 @@ -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/PoP/poc/res/music/m28.bin b/applications/PoP/poc/res/music/m28.bin deleted file mode 100644 index fbdd262..0000000 --- a/applications/PoP/poc/res/music/m28.bin +++ /dev/null @@ -1 +0,0 @@ -{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/PoP/poc/res/music/m30.bin b/applications/PoP/poc/res/music/m30.bin deleted file mode 100644 index 744fa8d..0000000 --- a/applications/PoP/poc/res/music/m30.bin +++ /dev/null @@ -1 +0,0 @@ -~}~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/PoP/poc/res/music/m32.bin b/applications/PoP/poc/res/music/m32.bin deleted file mode 100644 index 44fa7c7..0000000 --- a/applications/PoP/poc/res/music/m32.bin +++ /dev/null @@ -1 +0,0 @@ -û~~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/PoP/poc/res/music/m33.bin b/applications/PoP/poc/res/music/m33.bin deleted file mode 100644 index 5c9c638..0000000 --- a/applications/PoP/poc/res/music/m33.bin +++ /dev/null @@ -1 +0,0 @@ -{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/PoP/poc/res/music/m35.bin b/applications/PoP/poc/res/music/m35.bin deleted file mode 100644 index 40340d0..0000000 --- a/applications/PoP/poc/res/music/m35.bin +++ /dev/null @@ -1 +0,0 @@ -}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/PoP/poc/res/music/m36.bin b/applications/PoP/poc/res/music/m36.bin deleted file mode 100644 index 66f4253..0000000 --- a/applications/PoP/poc/res/music/m36.bin +++ /dev/null @@ -1 +0,0 @@ -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/PoP/poc/res/music/m37.bin b/applications/PoP/poc/res/music/m37.bin deleted file mode 100644 index dcce0f0..0000000 --- a/applications/PoP/poc/res/music/m37.bin +++ /dev/null @@ -1 +0,0 @@ -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/PoP/poc/res/music/m39.bin b/applications/PoP/poc/res/music/m39.bin deleted file mode 100644 index cdd7951..0000000 --- a/applications/PoP/poc/res/music/m39.bin +++ /dev/null @@ -1 +0,0 @@ -~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||}}}}}}}}}}}}}}}}~~~~~~~~~~~~~}}}}}}||||||{{{{{{{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/PoP/poc/res/music/m40.bin b/applications/PoP/poc/res/music/m40.bin deleted file mode 100644 index 018cc3b..0000000 --- a/applications/PoP/poc/res/music/m40.bin +++ /dev/null @@ -1 +0,0 @@ -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/PoP/poc/res/music/m41.bin b/applications/PoP/poc/res/music/m41.bin deleted file mode 100644 index fae6352..0000000 --- a/applications/PoP/poc/res/music/m41.bin +++ /dev/null @@ -1,2 +0,0 @@ -ĺ}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/PoP/poc/res/music/m43.bin b/applications/PoP/poc/res/music/m43.bin deleted file mode 100644 index 0aba339..0000000 --- a/applications/PoP/poc/res/music/m43.bin +++ /dev/null @@ -1 +0,0 @@ -{{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/PoP/poc/res/music/m50.bin b/applications/PoP/poc/res/music/m50.bin deleted file mode 100644 index 00a7ce1..0000000 --- a/applications/PoP/poc/res/music/m50.bin +++ /dev/null @@ -1 +0,0 @@ -}~}}~||||~}~}}|~~}~~~~~}~}~~~~}~}}~~~~~~}~}~~}~~~~}~}~~~}~}~~~~~~}~~|~}~~~}~~|~~}}}|~~~~~}~{~}{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/PoP/poc/res/music/m52.bin b/applications/PoP/poc/res/music/m52.bin deleted file mode 100644 index f9fa0ff..0000000 --- a/applications/PoP/poc/res/music/m52.bin +++ /dev/null @@ -1 +0,0 @@ -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/PoP/poc/res/music/m54.bin b/applications/PoP/poc/res/music/m54.bin deleted file mode 100644 index 5d9b1ae..0000000 --- a/applications/PoP/poc/res/music/m54.bin +++ /dev/null @@ -1 +0,0 @@ -~}||{{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/PoP/poc/res/music/m55.bin b/applications/PoP/poc/res/music/m55.bin deleted file mode 100644 index 8188946..0000000 --- a/applications/PoP/poc/res/music/m55.bin +++ /dev/null @@ -1,3 +0,0 @@ -~~~~~}}}{{|}{|}||~~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/PoP/poc/res/music/m56.bin b/applications/PoP/poc/res/music/m56.bin deleted file mode 100644 index 5268478..0000000 --- a/applications/PoP/poc/res/music/m56.bin +++ /dev/null @@ -1 +0,0 @@ -{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/PoP/poc/res/pv/a0.atl b/applications/PoP/poc/res/pv/a0.atl deleted file mode 100644 index e196cd9..0000000 Binary files a/applications/PoP/poc/res/pv/a0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/a1.atl b/applications/PoP/poc/res/pv/a1.atl deleted file mode 100644 index dc21da7..0000000 Binary files a/applications/PoP/poc/res/pv/a1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/a2.atl b/applications/PoP/poc/res/pv/a2.atl deleted file mode 100644 index bc1cfb0..0000000 Binary files a/applications/PoP/poc/res/pv/a2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/b0_0.atl b/applications/PoP/poc/res/pv/b0_0.atl deleted file mode 100644 index e392090..0000000 Binary files a/applications/PoP/poc/res/pv/b0_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/b0_1.atl b/applications/PoP/poc/res/pv/b0_1.atl deleted file mode 100644 index 935140f..0000000 Binary files a/applications/PoP/poc/res/pv/b0_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/b0_2.atl b/applications/PoP/poc/res/pv/b0_2.atl deleted file mode 100644 index d605002..0000000 Binary files a/applications/PoP/poc/res/pv/b0_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/b0_3.atl b/applications/PoP/poc/res/pv/b0_3.atl deleted file mode 100644 index 985f941..0000000 Binary files a/applications/PoP/poc/res/pv/b0_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/b0_4.atl b/applications/PoP/poc/res/pv/b0_4.atl deleted file mode 100644 index b2cb293..0000000 Binary files a/applications/PoP/poc/res/pv/b0_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/h0.atl b/applications/PoP/poc/res/pv/h0.atl deleted file mode 100644 index 4dae700..0000000 Binary files a/applications/PoP/poc/res/pv/h0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/j0.atl b/applications/PoP/poc/res/pv/j0.atl deleted file mode 100644 index 0eec08a..0000000 Binary files a/applications/PoP/poc/res/pv/j0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/j1.atl b/applications/PoP/poc/res/pv/j1.atl deleted file mode 100644 index 936922b..0000000 Binary files a/applications/PoP/poc/res/pv/j1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/j2.atl b/applications/PoP/poc/res/pv/j2.atl deleted file mode 100644 index 9426213..0000000 Binary files a/applications/PoP/poc/res/pv/j2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p0_0.atl b/applications/PoP/poc/res/pv/p0_0.atl deleted file mode 100644 index e392090..0000000 Binary files a/applications/PoP/poc/res/pv/p0_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p0_1.atl b/applications/PoP/poc/res/pv/p0_1.atl deleted file mode 100644 index 935140f..0000000 Binary files a/applications/PoP/poc/res/pv/p0_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p0_2.atl b/applications/PoP/poc/res/pv/p0_2.atl deleted file mode 100644 index 822e3c3..0000000 Binary files a/applications/PoP/poc/res/pv/p0_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p0_3.atl b/applications/PoP/poc/res/pv/p0_3.atl deleted file mode 100644 index f9b4968..0000000 Binary files a/applications/PoP/poc/res/pv/p0_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p0_4.atl b/applications/PoP/poc/res/pv/p0_4.atl deleted file mode 100644 index b2cb293..0000000 Binary files a/applications/PoP/poc/res/pv/p0_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p1_0.atl b/applications/PoP/poc/res/pv/p1_0.atl deleted file mode 100644 index e392090..0000000 Binary files a/applications/PoP/poc/res/pv/p1_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p1_1.atl b/applications/PoP/poc/res/pv/p1_1.atl deleted file mode 100644 index 935140f..0000000 Binary files a/applications/PoP/poc/res/pv/p1_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p1_2.atl b/applications/PoP/poc/res/pv/p1_2.atl deleted file mode 100644 index 56be514..0000000 Binary files a/applications/PoP/poc/res/pv/p1_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p1_3.atl b/applications/PoP/poc/res/pv/p1_3.atl deleted file mode 100644 index bb8d8a6..0000000 Binary files a/applications/PoP/poc/res/pv/p1_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p1_4.atl b/applications/PoP/poc/res/pv/p1_4.atl deleted file mode 100644 index b2cb293..0000000 Binary files a/applications/PoP/poc/res/pv/p1_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p2_0.atl b/applications/PoP/poc/res/pv/p2_0.atl deleted file mode 100644 index e392090..0000000 Binary files a/applications/PoP/poc/res/pv/p2_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p2_1.atl b/applications/PoP/poc/res/pv/p2_1.atl deleted file mode 100644 index 935140f..0000000 Binary files a/applications/PoP/poc/res/pv/p2_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p2_2.atl b/applications/PoP/poc/res/pv/p2_2.atl deleted file mode 100644 index 897aef5..0000000 Binary files a/applications/PoP/poc/res/pv/p2_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p2_3.atl b/applications/PoP/poc/res/pv/p2_3.atl deleted file mode 100644 index a25a5d8..0000000 Binary files a/applications/PoP/poc/res/pv/p2_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p2_4.atl b/applications/PoP/poc/res/pv/p2_4.atl deleted file mode 100644 index b2cb293..0000000 Binary files a/applications/PoP/poc/res/pv/p2_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p3_0.atl b/applications/PoP/poc/res/pv/p3_0.atl deleted file mode 100644 index e392090..0000000 Binary files a/applications/PoP/poc/res/pv/p3_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p3_1.atl b/applications/PoP/poc/res/pv/p3_1.atl deleted file mode 100644 index 935140f..0000000 Binary files a/applications/PoP/poc/res/pv/p3_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p3_2.atl b/applications/PoP/poc/res/pv/p3_2.atl deleted file mode 100644 index a0ef0f2..0000000 Binary files a/applications/PoP/poc/res/pv/p3_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p3_3.atl b/applications/PoP/poc/res/pv/p3_3.atl deleted file mode 100644 index b50a2d2..0000000 Binary files a/applications/PoP/poc/res/pv/p3_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p3_4.atl b/applications/PoP/poc/res/pv/p3_4.atl deleted file mode 100644 index b2cb293..0000000 Binary files a/applications/PoP/poc/res/pv/p3_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p4_0.atl b/applications/PoP/poc/res/pv/p4_0.atl deleted file mode 100644 index e392090..0000000 Binary files a/applications/PoP/poc/res/pv/p4_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p4_1.atl b/applications/PoP/poc/res/pv/p4_1.atl deleted file mode 100644 index 935140f..0000000 Binary files a/applications/PoP/poc/res/pv/p4_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p4_2.atl b/applications/PoP/poc/res/pv/p4_2.atl deleted file mode 100644 index 0eaee8e..0000000 Binary files a/applications/PoP/poc/res/pv/p4_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p4_3.atl b/applications/PoP/poc/res/pv/p4_3.atl deleted file mode 100644 index 1bf5ff6..0000000 Binary files a/applications/PoP/poc/res/pv/p4_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p4_4.atl b/applications/PoP/poc/res/pv/p4_4.atl deleted file mode 100644 index b2cb293..0000000 Binary files a/applications/PoP/poc/res/pv/p4_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p5_0.atl b/applications/PoP/poc/res/pv/p5_0.atl deleted file mode 100644 index e392090..0000000 Binary files a/applications/PoP/poc/res/pv/p5_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p5_1.atl b/applications/PoP/poc/res/pv/p5_1.atl deleted file mode 100644 index 935140f..0000000 Binary files a/applications/PoP/poc/res/pv/p5_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p5_2.atl b/applications/PoP/poc/res/pv/p5_2.atl deleted file mode 100644 index ed06ed1..0000000 Binary files a/applications/PoP/poc/res/pv/p5_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p5_3.atl b/applications/PoP/poc/res/pv/p5_3.atl deleted file mode 100644 index 510f32a..0000000 Binary files a/applications/PoP/poc/res/pv/p5_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p5_4.atl b/applications/PoP/poc/res/pv/p5_4.atl deleted file mode 100644 index b2cb293..0000000 Binary files a/applications/PoP/poc/res/pv/p5_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p6_0.atl b/applications/PoP/poc/res/pv/p6_0.atl deleted file mode 100644 index e392090..0000000 Binary files a/applications/PoP/poc/res/pv/p6_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p6_1.atl b/applications/PoP/poc/res/pv/p6_1.atl deleted file mode 100644 index 935140f..0000000 Binary files a/applications/PoP/poc/res/pv/p6_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p6_2.atl b/applications/PoP/poc/res/pv/p6_2.atl deleted file mode 100644 index 4b043b6..0000000 Binary files a/applications/PoP/poc/res/pv/p6_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p6_3.atl b/applications/PoP/poc/res/pv/p6_3.atl deleted file mode 100644 index 5500a9d..0000000 Binary files a/applications/PoP/poc/res/pv/p6_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p6_4.atl b/applications/PoP/poc/res/pv/p6_4.atl deleted file mode 100644 index b2cb293..0000000 Binary files a/applications/PoP/poc/res/pv/p6_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p7_0.atl b/applications/PoP/poc/res/pv/p7_0.atl deleted file mode 100644 index e392090..0000000 Binary files a/applications/PoP/poc/res/pv/p7_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p7_1.atl b/applications/PoP/poc/res/pv/p7_1.atl deleted file mode 100644 index 935140f..0000000 Binary files a/applications/PoP/poc/res/pv/p7_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p7_2.atl b/applications/PoP/poc/res/pv/p7_2.atl deleted file mode 100644 index 1b1c361..0000000 Binary files a/applications/PoP/poc/res/pv/p7_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p7_3.atl b/applications/PoP/poc/res/pv/p7_3.atl deleted file mode 100644 index 589cea3..0000000 Binary files a/applications/PoP/poc/res/pv/p7_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/p7_4.atl b/applications/PoP/poc/res/pv/p7_4.atl deleted file mode 100644 index b2cb293..0000000 Binary files a/applications/PoP/poc/res/pv/p7_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/pv.arc b/applications/PoP/poc/res/pv/pv.arc deleted file mode 100644 index a5de9d7..0000000 Binary files a/applications/PoP/poc/res/pv/pv.arc and /dev/null differ diff --git a/applications/PoP/poc/res/pv/pv.pal b/applications/PoP/poc/res/pv/pv.pal deleted file mode 100644 index ec99c2b..0000000 Binary files a/applications/PoP/poc/res/pv/pv.pal and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s0_0.atl b/applications/PoP/poc/res/pv/s0_0.atl deleted file mode 100644 index 66083f2..0000000 Binary files a/applications/PoP/poc/res/pv/s0_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s0_1.atl b/applications/PoP/poc/res/pv/s0_1.atl deleted file mode 100644 index 46fd21b..0000000 Binary files a/applications/PoP/poc/res/pv/s0_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s0_2.atl b/applications/PoP/poc/res/pv/s0_2.atl deleted file mode 100644 index 36bfcbe..0000000 Binary files a/applications/PoP/poc/res/pv/s0_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s0_3.atl b/applications/PoP/poc/res/pv/s0_3.atl deleted file mode 100644 index 076e4da..0000000 Binary files a/applications/PoP/poc/res/pv/s0_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s0_4.atl b/applications/PoP/poc/res/pv/s0_4.atl deleted file mode 100644 index 76b82fe..0000000 Binary files a/applications/PoP/poc/res/pv/s0_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s1_0.atl b/applications/PoP/poc/res/pv/s1_0.atl deleted file mode 100644 index 21a35c8..0000000 Binary files a/applications/PoP/poc/res/pv/s1_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s1_1.atl b/applications/PoP/poc/res/pv/s1_1.atl deleted file mode 100644 index 425b3d0..0000000 Binary files a/applications/PoP/poc/res/pv/s1_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s1_2.atl b/applications/PoP/poc/res/pv/s1_2.atl deleted file mode 100644 index 1a73f95..0000000 Binary files a/applications/PoP/poc/res/pv/s1_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s1_3.atl b/applications/PoP/poc/res/pv/s1_3.atl deleted file mode 100644 index 0d7d6f4..0000000 Binary files a/applications/PoP/poc/res/pv/s1_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s1_4.atl b/applications/PoP/poc/res/pv/s1_4.atl deleted file mode 100644 index 76b82fe..0000000 Binary files a/applications/PoP/poc/res/pv/s1_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s2_0.atl b/applications/PoP/poc/res/pv/s2_0.atl deleted file mode 100644 index 91fb4bf..0000000 Binary files a/applications/PoP/poc/res/pv/s2_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s2_1.atl b/applications/PoP/poc/res/pv/s2_1.atl deleted file mode 100644 index ceb2280..0000000 Binary files a/applications/PoP/poc/res/pv/s2_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s2_2.atl b/applications/PoP/poc/res/pv/s2_2.atl deleted file mode 100644 index 43556e8..0000000 Binary files a/applications/PoP/poc/res/pv/s2_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s2_3.atl b/applications/PoP/poc/res/pv/s2_3.atl deleted file mode 100644 index bb08237..0000000 Binary files a/applications/PoP/poc/res/pv/s2_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s2_4.atl b/applications/PoP/poc/res/pv/s2_4.atl deleted file mode 100644 index 76b82fe..0000000 Binary files a/applications/PoP/poc/res/pv/s2_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s3_0.atl b/applications/PoP/poc/res/pv/s3_0.atl deleted file mode 100644 index 4a016ec..0000000 Binary files a/applications/PoP/poc/res/pv/s3_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s3_1.atl b/applications/PoP/poc/res/pv/s3_1.atl deleted file mode 100644 index 749cf43..0000000 Binary files a/applications/PoP/poc/res/pv/s3_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s3_2.atl b/applications/PoP/poc/res/pv/s3_2.atl deleted file mode 100644 index d39f273..0000000 Binary files a/applications/PoP/poc/res/pv/s3_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s3_3.atl b/applications/PoP/poc/res/pv/s3_3.atl deleted file mode 100644 index 7cc6862..0000000 Binary files a/applications/PoP/poc/res/pv/s3_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s3_4.atl b/applications/PoP/poc/res/pv/s3_4.atl deleted file mode 100644 index 76b82fe..0000000 Binary files a/applications/PoP/poc/res/pv/s3_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s4_0.atl b/applications/PoP/poc/res/pv/s4_0.atl deleted file mode 100644 index 1f80462..0000000 Binary files a/applications/PoP/poc/res/pv/s4_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s4_1.atl b/applications/PoP/poc/res/pv/s4_1.atl deleted file mode 100644 index d76e764..0000000 Binary files a/applications/PoP/poc/res/pv/s4_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s4_2.atl b/applications/PoP/poc/res/pv/s4_2.atl deleted file mode 100644 index 7c687e3..0000000 Binary files a/applications/PoP/poc/res/pv/s4_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s4_3.atl b/applications/PoP/poc/res/pv/s4_3.atl deleted file mode 100644 index 53f2c40..0000000 Binary files a/applications/PoP/poc/res/pv/s4_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s4_4.atl b/applications/PoP/poc/res/pv/s4_4.atl deleted file mode 100644 index 76b82fe..0000000 Binary files a/applications/PoP/poc/res/pv/s4_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s5_0.atl b/applications/PoP/poc/res/pv/s5_0.atl deleted file mode 100644 index fbdbcca..0000000 Binary files a/applications/PoP/poc/res/pv/s5_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s5_1.atl b/applications/PoP/poc/res/pv/s5_1.atl deleted file mode 100644 index 992fc9c..0000000 Binary files a/applications/PoP/poc/res/pv/s5_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s5_2.atl b/applications/PoP/poc/res/pv/s5_2.atl deleted file mode 100644 index 7c687e3..0000000 Binary files a/applications/PoP/poc/res/pv/s5_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s5_3.atl b/applications/PoP/poc/res/pv/s5_3.atl deleted file mode 100644 index 53f2c40..0000000 Binary files a/applications/PoP/poc/res/pv/s5_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/s5_4.atl b/applications/PoP/poc/res/pv/s5_4.atl deleted file mode 100644 index 76b82fe..0000000 Binary files a/applications/PoP/poc/res/pv/s5_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/pv/story.pal b/applications/PoP/poc/res/pv/story.pal deleted file mode 100644 index 1cb8dc0..0000000 Binary files a/applications/PoP/poc/res/pv/story.pal and /dev/null differ diff --git a/applications/PoP/poc/res/pv/t0.atl b/applications/PoP/poc/res/pv/t0.atl deleted file mode 100644 index d541c08..0000000 Binary files a/applications/PoP/poc/res/pv/t0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/room1.dat b/applications/PoP/poc/res/room1.dat deleted file mode 100644 index 71beaa2..0000000 Binary files a/applications/PoP/poc/res/room1.dat and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sf0.atl b/applications/PoP/poc/res/shadow/sf0.atl deleted file mode 100644 index 313de95..0000000 Binary files a/applications/PoP/poc/res/shadow/sf0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sf1.atl b/applications/PoP/poc/res/shadow/sf1.atl deleted file mode 100644 index daccba6..0000000 Binary files a/applications/PoP/poc/res/shadow/sf1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sf2.atl b/applications/PoP/poc/res/shadow/sf2.atl deleted file mode 100644 index 1f0b6d4..0000000 Binary files a/applications/PoP/poc/res/shadow/sf2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sf3.atl b/applications/PoP/poc/res/shadow/sf3.atl deleted file mode 100644 index 5c54e5a..0000000 Binary files a/applications/PoP/poc/res/shadow/sf3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/shadow.arc b/applications/PoP/poc/res/shadow/shadow.arc deleted file mode 100644 index 1c0f9b1..0000000 Binary files a/applications/PoP/poc/res/shadow/shadow.arc and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk0.atl b/applications/PoP/poc/res/shadow/sk0.atl deleted file mode 100644 index 00d2e35..0000000 Binary files a/applications/PoP/poc/res/shadow/sk0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk1.atl b/applications/PoP/poc/res/shadow/sk1.atl deleted file mode 100644 index 4089c79..0000000 Binary files a/applications/PoP/poc/res/shadow/sk1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk10.atl b/applications/PoP/poc/res/shadow/sk10.atl deleted file mode 100644 index 943fcf7..0000000 Binary files a/applications/PoP/poc/res/shadow/sk10.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk11.atl b/applications/PoP/poc/res/shadow/sk11.atl deleted file mode 100644 index c573db6..0000000 Binary files a/applications/PoP/poc/res/shadow/sk11.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk12.atl b/applications/PoP/poc/res/shadow/sk12.atl deleted file mode 100644 index c4be0c0..0000000 Binary files a/applications/PoP/poc/res/shadow/sk12.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk13.atl b/applications/PoP/poc/res/shadow/sk13.atl deleted file mode 100644 index eb433b5..0000000 Binary files a/applications/PoP/poc/res/shadow/sk13.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk14.atl b/applications/PoP/poc/res/shadow/sk14.atl deleted file mode 100644 index f07e418..0000000 Binary files a/applications/PoP/poc/res/shadow/sk14.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk15.atl b/applications/PoP/poc/res/shadow/sk15.atl deleted file mode 100644 index 80217bb..0000000 Binary files a/applications/PoP/poc/res/shadow/sk15.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk16.atl b/applications/PoP/poc/res/shadow/sk16.atl deleted file mode 100644 index ee4bd35..0000000 Binary files a/applications/PoP/poc/res/shadow/sk16.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk17.atl b/applications/PoP/poc/res/shadow/sk17.atl deleted file mode 100644 index 86e63a6..0000000 Binary files a/applications/PoP/poc/res/shadow/sk17.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk18.atl b/applications/PoP/poc/res/shadow/sk18.atl deleted file mode 100644 index 07498ba..0000000 Binary files a/applications/PoP/poc/res/shadow/sk18.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk19.atl b/applications/PoP/poc/res/shadow/sk19.atl deleted file mode 100644 index 24a06d3..0000000 Binary files a/applications/PoP/poc/res/shadow/sk19.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk2.atl b/applications/PoP/poc/res/shadow/sk2.atl deleted file mode 100644 index c4e5e72..0000000 Binary files a/applications/PoP/poc/res/shadow/sk2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk20.atl b/applications/PoP/poc/res/shadow/sk20.atl deleted file mode 100644 index 6b1e0f7..0000000 Binary files a/applications/PoP/poc/res/shadow/sk20.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk21.atl b/applications/PoP/poc/res/shadow/sk21.atl deleted file mode 100644 index d446b37..0000000 Binary files a/applications/PoP/poc/res/shadow/sk21.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk22.atl b/applications/PoP/poc/res/shadow/sk22.atl deleted file mode 100644 index ae853da..0000000 Binary files a/applications/PoP/poc/res/shadow/sk22.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk23.atl b/applications/PoP/poc/res/shadow/sk23.atl deleted file mode 100644 index 450828b..0000000 Binary files a/applications/PoP/poc/res/shadow/sk23.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk24.atl b/applications/PoP/poc/res/shadow/sk24.atl deleted file mode 100644 index 258ba93..0000000 Binary files a/applications/PoP/poc/res/shadow/sk24.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk25.atl b/applications/PoP/poc/res/shadow/sk25.atl deleted file mode 100644 index 8d3c861..0000000 Binary files a/applications/PoP/poc/res/shadow/sk25.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk26.atl b/applications/PoP/poc/res/shadow/sk26.atl deleted file mode 100644 index e24d76a..0000000 Binary files a/applications/PoP/poc/res/shadow/sk26.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk27.atl b/applications/PoP/poc/res/shadow/sk27.atl deleted file mode 100644 index 81600d0..0000000 Binary files a/applications/PoP/poc/res/shadow/sk27.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk3.atl b/applications/PoP/poc/res/shadow/sk3.atl deleted file mode 100644 index e2a92c1..0000000 Binary files a/applications/PoP/poc/res/shadow/sk3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk4.atl b/applications/PoP/poc/res/shadow/sk4.atl deleted file mode 100644 index 358078e..0000000 Binary files a/applications/PoP/poc/res/shadow/sk4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk5.atl b/applications/PoP/poc/res/shadow/sk5.atl deleted file mode 100644 index 8b92d5f..0000000 Binary files a/applications/PoP/poc/res/shadow/sk5.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk6.atl b/applications/PoP/poc/res/shadow/sk6.atl deleted file mode 100644 index 39b4993..0000000 Binary files a/applications/PoP/poc/res/shadow/sk6.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk7.atl b/applications/PoP/poc/res/shadow/sk7.atl deleted file mode 100644 index 65d80bb..0000000 Binary files a/applications/PoP/poc/res/shadow/sk7.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk8.atl b/applications/PoP/poc/res/shadow/sk8.atl deleted file mode 100644 index 75ac4ce..0000000 Binary files a/applications/PoP/poc/res/shadow/sk8.atl and /dev/null differ diff --git a/applications/PoP/poc/res/shadow/sk9.atl b/applications/PoP/poc/res/shadow/sk9.atl deleted file mode 100644 index a69cacf..0000000 Binary files a/applications/PoP/poc/res/shadow/sk9.atl and /dev/null differ diff --git a/applications/PoP/poc/res/skel/g0.atl b/applications/PoP/poc/res/skel/g0.atl deleted file mode 100644 index aef7dfd..0000000 Binary files a/applications/PoP/poc/res/skel/g0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/skel/g1.atl b/applications/PoP/poc/res/skel/g1.atl deleted file mode 100644 index d702e13..0000000 Binary files a/applications/PoP/poc/res/skel/g1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/skel/g2.atl b/applications/PoP/poc/res/skel/g2.atl deleted file mode 100644 index b3e1ddb..0000000 Binary files a/applications/PoP/poc/res/skel/g2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/skel/g3.atl b/applications/PoP/poc/res/skel/g3.atl deleted file mode 100644 index e134587..0000000 Binary files a/applications/PoP/poc/res/skel/g3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/skel/skel.arc b/applications/PoP/poc/res/skel/skel.arc deleted file mode 100644 index be05df5..0000000 Binary files a/applications/PoP/poc/res/skel/skel.arc and /dev/null differ diff --git a/applications/PoP/poc/res/sound/s0.bin b/applications/PoP/poc/res/sound/s0.bin deleted file mode 100644 index 9dc621d..0000000 --- a/applications/PoP/poc/res/sound/s0.bin +++ /dev/null @@ -1 +0,0 @@ -{{yzyz}}iG0!#=]t_PLI_q}}R]g`E*JG^Ƽc9%.GME5( !A[{ǻ|zkdkgnmZ\K:/4<>Snw|}mUVRH8-4NoX[x˹u|o{y`MXvqmQKabl|yȰξbcszif_x}ib_aNK[aoz~i[P7JowfD4OfjZOQS\lx}wpxflearvsq|~|lenkJBVw|wxpprWVg|tfPWtxZc~||^o}}sS\ns`X]nt~jhnmulhXY[`u~pjnqlqzraOKKUWYiosxtgbturk^YR`s~~{up{xtxtpzmhovyw~yvxxivtoy|ypuglwxwnlz{}{}yqvxyyr}rpy}wzxpsu||~|zuqkopopnsuz{|{vnmpu{swstuv{ljsxxuhvw||t{~}xu{}|xwzyrqv||zz~zxuqty|vv|}zz{uswuuwtonsx|~|urtyyux~~}|}{}xvtyyqinnptx}{zz{|zvy}z{|||~~tx|}||||{zxwyxz{tqx~}~|ustx{{yxz}|||}}}z{|~~{z~}}}|~~|zw|~}y{{}}{~~}zxzz|{{z~~}}~}}z~~xuy}~~}||}}{{|yx||z{}yxyyxy{}|~|{{~~}~}||zz{}}~}}}~~|z{{~||}yxxy~~~~||yzz}|}||~~z{~}{|{yy{~~|||}~~}}~~~~~}}~~~}}~~}}}z|}~~}||~yy{}~~}|~~|{}}|z}~}|}~~~~|zyyy{|}}zyzyxy{||zxz|}~}~~{{~yy{|||~~~|}~~}|{|}~~~~~}||{{|}~~}}~~~~~~}~}||~}y{~|{}yxy}wx~vttzyy}|z}{|~wwzztv|zsqyzz}~}}|trxtkjvwty|~|wt{tjnzurvy~vt|xx~wpt|ulnyuopxww{~~tnuxibq{tv||{|y~olt~{srx}vrw{z}{y{|poxunqzxqysu}rr{upt|{x{}||{qs~rlr~|stzxw|voxpks~}zwwy~z}uuroyxv|~~~xv|urwymjswnq|uywnr}ljuphn{zvv{~kjykZi|qsx}{~qozzigpk`cxyjk{zs|vfh{yrswvpuzptvnuxtw~{wxtqx}mjsnah}wgfwvt}rrzscl}tbbtwqm|wysjsmY`z|urnw}tvvjqj[bxxqpoy{svvouoch{wrqv|uz|twtjm{urryxt|tpu~mhn|sf]omgupqlXU}lcqqix{rwi[bzvjdrnizvnvdYe~}tniw|oo~kkxx]Xlzujk{rzslvlW_{yrhotn|mit~`R^|tm^ppi~jh{w_Qjxshd~|jrzloo[]uuphprkxwhnfWa{{spkw~mlrgngYd|{tll{nv}jhzz`Znzqgoum|oit`S_~ri^qidag{kVFpkRLpX^~xw^_zfS`lT_|~uyk^g|xmfu}icxljwjW]u~{qaqpg}sgokXaw|tfrvpkguaR]{um[kof{^kgQIl|ufR}z_dyjtiTYx|xhktgrxlpm]`ruldoohzwhljU]vvk]pfd}pgoeVfyje}{holjuz_[j|rebpai]dqmPPhvh[dgScheumOZzzk\oeVlwhmmVcz{upopbcw{TNljLMlnjhkrYaVPmiLVrhiqzsnwmfro`iz`\p\Wj`Oa[IUymZdtoeezoPZubL[uwciuxnncawsaj~d^l]Re_TcXPbjU^lfW^|TOknO]ukdft|lelgZft]f|pasvjqa\mSKcaQcxjUbwgITygJGmw^Yexzfbwr]f]WijZbxxdcrgUa[Rgkbmztioy\Q\{PF^gP]kd[dzUX{bTb~hV]{p[_ucHY[Kbl`mvrrwa\lkIZxu[YjYThaDPhFGrzcZWfiYmi]o[E\{xgUUvvUIk^RbxgnxxLFwǮ[9LsZlt^ixďSFTH=Xwl\TXxpRU|aQf]F^{oXWk}[QbiLVa\o]RfWKd}_assdkzcHKuY8BvaS^d{iVU{—RNYȵ_=TiyUS[]IM|qG=hӯ]EQul]]v}c[o_G[zEIgkR[upJSo{HDUĀ>5RƨmGHWreLPl„RNe̲kHNfrNA`UCZ[FW{JG^|WN]hZ\zUOdd@OpaKdq^D]r_I6_`D1fĚlGW`hSVxϯfOQˇNIY[GLrnG>b}B>dΦ\9iةK6IqŃMCOxFOgeW[{hY_oND_̽k5CsS7KmĮb3@meB\pyQfr~tdYkhRPexSAHzB8GzvVK[mSV{Ȱ{\JhgUaX?T˾l45`ƜMERq_RZgiOIdʠJ8WcMIrͶhzکX1Wvf\aTcIRڕV':a9&dzLgf``Url?1oN*JȨ{^Ub^ujT^ʟV2F~;6`•iVZgmaZtyE?gܧP1Mĥ~]Xicyi^cԜX3Eڄ>1bȖkXjphm^rI4]_2;vdUebfx^Y|²^;8w8(Y—ujWcPvfOZmJ7fL,PԤkc[o^iy_PƮb@FzӁ:5gəcHNZgjJHhӥW4BӁ::_ȝiGL^{iLS|υA(R^,BvʻSNama\fμyB2iܯa5F|ƳQOhuv][o͟S/9؎9*Qţd?TkqfQZv݂=#GV4&s̈́YG`jtkQ[k;9nܠN3TɣqVVajcRc®j;Bz͎I8Zr[_fpfWfY@Ny5@nĠVG}B?W̤YG?gcJPiϲfI4dͭ^GFqмnOEj¡aM[vƳrPB\ìoJGcſW9IU?QŎT=UƯi@LmÇTAD|K@Ktՠ`F9k̙WGQtԷ}X:Q½WDRPKcҒ\Q(bǮmS9^؞dM=}DGcΦiQIZwQOZĽlPENͬ[DRhΆ_=>~ʟd@@p̱qJDlTMg˦eOMfΨ]@JgāY<@\HQƋV@SȷoADb˛bI5]ūhJ8^_A,q݌G9O۶sT@R}SKW{L;Da4@\\; yX;auL0W̪_DWwxH>Sѿo<@Y֢S>$fбY>9kת\<@ˆGAaΨkJFbëpKLa|Lvӯk?=oUEUu˫aB6]ѫY>@a}]*6ڐY5;{b8>̂BD\ר\E8cӮUzÏ^MOyҠc9:wѝW5=txK3OzDCHV12~L5bɩ^>MlvE9SqC=Y֣d>3lӳoA5^ƇM9TijqDLhǏR?DӐM8CwڽyO'IʈR5GӕS5Iv@C`Ѧ]E0[νeB/NiK%Nk>:Zݞ\>:rΣW?IgӺbD4YұX=F^}T+2ޏO-=ղe>:lƛ\ETrƳqC?_ϴi>FeԞ\?.mбiB7aѸnCDa֮sK0RLJI3EΕF8Kѻb2KpǕOCBs˟YBEg~O6KľQDV͕P9IŁ?;T˱oJ5I]CK~˒U8L~GCdɦiJLcxTQYtK6Jh@EYܟl;*m׵pA*XϔO9FRO]~ҟF<8E4KwӀ[-0٦`92g|A8\ʭb;NmƒMBEϗN>GrϿmH5^˶nF>\×_CJufQWlnJF`ƯkFLg˒Y>C~ϗV=Fqƾ\@Aq¦pMB_˹jFBsכR6Z~վyOEA~ǞbI<_ʽpI5\ͪaBNjM?EϒG9GtĺqHG>]MIs֤Z=EԎP=WʳyPELskKF]¶lDCaȞYEUoQ>OďTQo|vhw]G\D(NZd~sb;lmKK{oCNqqko|e~oqaTbgn|mowk[ateSc|mvʺ|Mfmeg{sloWabtnwo;b}v\oxY{Ȟh[kMRrkc`Pynd_5ifRof^wws}jJMlznZ\oz|}wozwzxu}j<@f{}nWc_T]daYC,8XgF-Odtrp|ýǚjj<=qt}}NWR'Cm|ZN~lXsv{pstRSvs|KLffF8RVM>)M\KIdzkhn~}hsxsxnwhp}xtn{kJYlP@VzeYܧϹeHv`ocoUjgNTYph{}vvovUN:0^p[H9}cTT`acz}tplg۱m}w~y{~QEd|}xdeZwihnObtgsplnogn{|or}wqaJ=X|T(QwjWtaVtkJ–x_fl|w|s{uysvmpƣiGc`qs=~ngU?tbC?BtNq^fSmJr_Ty~|opvjrkafPzzs[r~aWgwkv|qck_}֫tYsiu`UifgHQ~KEe7\\FIzkqrqc@xbV6_}NdqNycOfxvztgTPlo`t|i]=II~BS}julgwq[Lny[i}gyocef[VkŤdU]PAAO|~dxzi_rRi_sewwpUZW}tkeaVK>8y|_̐|ub`gxv~ix}kP\Rgzjs}qs^orpaayzSWxMS}unl_|eVCaj^jsjlzl\RcdB>NNnodo{xe{xxitnLD\tuguxwkеʥ|l]o[aSV^T5[sM[F[Y}czrzQFSx^Swsprò̸wvy\cjJ:CPmxOK^_n_LTKLiv~|fgyam{t0AfS:=EfJ!7OnRB9AzfsuNJu|~o\TFDIOKdj|o{oMnr|qcbwJNo~ia[VNaqiMavdgis{viqyv\Xn]Xk}e\UuaknO_Slq-8_OwtXiZYgH|qIefXvySktpl`egp\kYTxh`\||yoxsaTkryyvriXdlt`bm~iTdleztrypn~bri~tjmp{axzy}|SrbWdut:BA^LTtPWj}hQzfew~}{mpaEUtlmhmjRAepqlitdf|yw~^Wgvk_`ctsvj]umM_qx~hZxwxm~o]|ztxsmOa{rez~Zm}yfe|{|kn~ok{fme_k[p||pjonlz^juawŶsl]UYcg~~ns}uy|wUGa{}bptFCojOYZnzjmurnfsdlcpu~ylhX}fqzc]{1JU`C[~zigqrmbaxha{gdxti{uJJ}sqrzjhrekk[n{jxfhvaklgpr~nVQvzm٦yjVKsgl6XRHkScsR^Oniftkr`̗¤{vpypvvkmp`FQ^qghmnzr[lvn~qq]RiX@JyM9WRxpbdKRVhsci|zt`iG[\ttyyyXw}idt{amot}bXt~jI^v`a{ngyz||wZLaUKValagf|}{|{tcloplxn{fU\p}rX_twit~p{]de_jdcjadyjr}usnahzxyw[E{^kzxWlqv{tgt~Vhl{p~lxtuqwmRGRbldG\jtp]ons}vwyhax[T~_`|yegnjpwqp]elgkv}mmN_oo_hsxqv}f}u|}fvxsuUTmobab}~f[lyWly~on|qswzt{woke{vtyywufj^XY_ibq{{yw}~iQGJQb|xont~|oyes}]NSUfwn{wyobPaIDAVk]9Bdyy_cnYnKTz~cdkQSqsvnjqu{q|zm^hnYkwqn^]qYgslr{wl}~~srlrvX`au|nht~ix~n]byhxkxp}~oa^jzokid[yw|uty}kpu|va_sgPJVeinnvzeZhu~z|r}}|YZjojojltr{~jjzv{r}l||lY}y|~x|Vjmbb[sq{mqrguhMVe\n}|xq{ubyv{s{~sxnrqUQdvu{wohv}uhh{wcsq~g]k}wldewuqpkqoo|w~z{wwjslhnm||oux~xrux}~Nc|{i]OhdVQM\aWY]brv|orwztolz|x{{{irrcrztj]i}wts{|oXm~nit{zm|u}borye[ozs`_jibnr}tXnSIyuy|}nmc\swn}xkr~utxyuvdarymbio}qfcfw{rlj{}tllus~|gkx}|mj~fsrnutysvwt|}wxu|wp]K\nmz[r|vfsx|rox\WI5Nfn`S`ywsuvnkrvysvu}}v|~~|vxyyrzxrqtzz{vxkWiujMGcvo_fxz{wu|tzx_d{sttyju|ojis~rv}y}xc_hrz~|w~ri~okqyvq{{|{z~nhhc^bprimpbdjz{pq}yhbnnseYk{t~~wxbtnqvvfp_fmhmc}anpvq{vzostqmzz~~t|ru}~kjsuririjuqkowtim|xrr~yz}{~y|zts~xlrcheit{z{~vy\j~nvwwsmthgxnwkx}ysm~ztvzzmmwxr{wvz}}~{zoendbv}pbi~uq~|ry{xdl}tvzz||{ux{vspprkmtotw{v}}yxvvtkqgf^cYgkdmonu{{|}qkx}~v{}ztz~}zzswy{}|rpysssz|zx}~xvyjbmkejlqxux}z~~mt~txx}{}}{{rt|zvmhzqp|hstypq{}}z}srp{xovjoegelowmpmirzwuzy~|xfwohpw}em}ertd|vz~~yiWYikpxss{~vlw~|n]lwnnkl}x}qxsv~oz~||t|~|}p}}~~uuwjhppkqant|wo~{wznlmw{i`jpfdd]gmafs|tbmxxv|~{{es|wtsrlyyt||{~yxx|{|sjzsqymw}gmpvthwn|ivoyvyyzwnxuvkwpqrgqq|zyzqxy{|ttvqssjghekyvsvt}u}wuqs~}y|uw{{z{~}}tspgfpnw~uvxz}}}sx|yy{}vyyz{kvylwvlr~zz~|{zw|wvy}yvtxsmrtz|xxtx~ywytr{z{us|{||}zxz|}zpl}}~}y~xzyyzwz}ytw{sn}|x{}}{yt{||y}}}z{y{z~wswxw~{pw{y|yw{}|zz|}{sux{wv|}|{x{zw{}|z{|}~|wu}x~yu{yvwx}yxy}~z~}zy{z}|}z||{||}{{}{}z|}}~|wxzzywwyyyxx{|~}xvy}zy{}ztuxz~}~|{}y|{}}~}~}}}}z|{|yxxvvx|y{|z~}}{|}||||z~~}~~~}~~{{~z}~~}~zy{{|}}~}~{{||{{zyz}{}}y~~|~}~}|~z|~|}||~~~~|~~|{}~}~}}~~}~}|~}}}|{~~~}}~~~}~~|}~smrr`bOGg^Drpqg,)IIJO3>TmjEUbӵb/P~qw›j]}ݸe" "^{XcuƓE *LŖdTmˈxYmqLAHE~Ćfz~eu4JfcQpr_^apmP`mi|jrpxfvTPɍdvcSObğ|zrdto_O=^}{~m|hJQr~jWlƼwmJ>SjwncqvxW7#@yljzn\HKd~u[Yuepmizyqs|vberyfohah^N_x|x`o}qgq{~zig{iUUtarrfdvzx}rzwsvys|qxyzrjcg\[q|vwrrx{jczsxwm^Xs{|~}~zlrnjbli^fu~tpx{n_l}yps|wjm}|pv~zwyy~xnmvyyv|usvusvx{pl}z{{{~~urxtnx|~wtvyvry{wsuz~~upw~{sqw{vtw}pgt~usv}{}|xz{zwux~|{~~|}|y|}z{ywwvz}~}~~yvxyvuzz{|z~~{y{{wx|}wyrpty}~{try{|zuw{}}~}~~{wuu{~~}zz~{zz}}ztv|||}|~xv|}}}{zz|zx|}}wtv{|}{xxy}}~|~|{}z{}~|~|zz~~}|~}}~{~}}~~|{~}|}~~z|}ypaRpfgcNryNOm{kMI^n|mctv|tsQexMNxYFkaAMzhL\ePer}omfZmmsn[\h_djYxFQsq]cY`irze|hXl|WebZh|}lfmjVA8?JVbrxrorsusosobSP[ky̾˺`DDR\[\ev}w|}rb\fnXZf`KFWmvuvxvojmmeepxfiwsc^c`RE=756647BXuÿsfbdhms|wommmoxy|{m^\eomb\cllb^epy}{qf_``\WUX^bel{ú÷xmfb_`flppnrx|~wePA=<703M^grxm\^xzzȻԵмɺs`_soI9IhmPFZsyun`QO]lpk`TR^lka\_``egb[[`eksreZ`p|ysxwtihp|sa^jrrqmaU[ozreZW[ixtbY`efnvsgbjrvz|uot}smw~pfdgnsqhgknorxwojq|yvv}{qr|wonqsohb^[_imh][cillh``dlkhe`]courlmt}y|ywvqlnrollnkhkjqmhijpwxpijpuw{yqou}xokox{riimnnrskfint{xpu}|~z{{stvyywrjimqssqlefnsnihilsyxqooooqxyngmv{|yrmt}{|}~|yrlmpqomligjorpkhiovwpgflrwyxtruz{wz|xvvvy||xx~zux}}zxtpsz{vqqttv{{vtttwz|xrv|}yy|~~|yvvwwursuuuvwutsrqrvxyz}}}}zz}|{}}~~}}{ywy||{wtrtxxsopqrrrqnnqtv{{y{~~||{zxxzzzywuwy{yvuwz|~~{y{~~}{zz|~}|}}||}~~|z|~|}~~~x}~}}vns}}phbyok~cX{giswUXw|yumhqpU`}|hspioqsk^j[JX|[kh,)Wsn_RUwnCV}znzcubtvleiowG[hov`9Hn`O5Qͺw߭G4.KbetqvYVA"']hvɳm?5EgptikY29~ȺdyԻwzfFT޿wFHuylhpF#&fldȷTHUHgY\ybAXsttuam\*-]\`hax:Grv}B@/yOcǒ}ടʹȜUg|voN\",]VHGwFTsd`s|{`B7IozjB9XhjHVXAas^l|LU˽wTPJAXtѫu]Kcƶ|nscFbu> 3fB1Wgal|Y&<~RR[v=30Fhlf9elN!PϓUlmᣆȬ~o}|S)2`ytgfuoM$;Zlr]Vl]3HXd|pfsv`gqzvsy{~siwɹԺſrjgrV@Kh_|vY`yxJ..;Xcwc\KWdsoz]FaL1FW55X_dsr^WbAHx|I[JCě}}~^`؜U)Nm'*hs/?sh`fxc23cΜǯÎ`RgwhgRBRdnsvu`D$(`}wbND?>9QZu`509;AsVeͻˬ~ʹМqx]iy`VicG_qi[M[kpf[Z`hpqfpsWayZ@7>OXNOrzgbpmnxz{w}WKmiNIblsf\PB<.1GMhcKCSjodes{XENYYbif^gr~~nƼyrzxyws{[JOSaxwTE=Gf|uSH[rw^EAJT_pnt~tzwt}xsvsį{jh[Y\ff]]lyw{xiW:Ikwrmztqldi_R[go|tkgkmpvzvyxy~yaU[joquya]hpwstteZ[dhj|prpgmxustYJYojkov{y{xwx}|xe[Xatw{z{uk[ZfnpgikcU[caMHEBT[NJUsyrx}~xyzutuu~zyypt~}|ocejov~urphdqvzwfuxkuwabskbgywityykwoowtnne\UV^hmehr}yfV\nzfbaVC9=Oi}vmdPBN`p{vgaaeuzsmlpuy|{{xi[]ppp}tglzxyplnqmg]Xar}ps~q~~ydVSYYcrxt{u_Z^]nxsh[OKSUWTTguyyy}zxtmifnt|{yz|~yuv}|l_o}zwus}|zv{~xmhp~qbVQW^`XWURTU`lilgr|wcamgc]_ev~sxycXewxwzo\TLDBGIJU`jnpzwh`jyyqikxsqysotqcev{nfoytou~vqk`ixu~|ou{zfmurloff^_kme^QXfqonvwhjidiyqib]]dq{~vg\W]luutmyups}~~zqrx|wjmv{tkiir|}vzttw~vji~~yov|rklks~}}}~zyuy}yrlmoqi^baag^OP[dbK22DSY\]`hnmnojgpyzxukfba`fv~~|nor{yy}}s}wztt}wx||trvz{sllllihhmvvnjegmuyxullw|wvz|zyxztkhf^^eefe`cr~yxzz{xpks}{|{olr~zowsw~xnknvth`dqoc]gonquxodahnlmjllnmjedeny|yuqnkhoxtkjlpw}~yonmmmozzuztlmnwsqt}wxz|~{z{~ws{}wz}~ssv|yyzw}}|{~|v}}z~{w{zqru{{trtyxniov{}yv|{}zw{ytx{zz{xla]gw~|zmotuwxz~xsvmn|{x~njhlpkZ[fkjljfijebglz}xvx|{~|ystw~~yyvtw{wprtzvrv~|{spy||~rhmty~xssvw{wx{yywumpqswz||qmtxwnot~}}yy|x||wodgprry|tpswz}wtz{yz}{sov~zvuxxvuojlloz{}~z}ypls~~unlnpmlorru~wv{~zupmpvy~z{|~}xz}{xxzttx}{vuz{wtw~}|}zxxqtz~}~~z{{|}znljhmpvyxtmjjknrruxz}~yxwx|~zzropx|wzzyxvy|~zz}}wrw|yqnjgjpqsy~~~xy{}zqptwxvy~|xy{tlnuy{|truyts||www{~|{~xtruxxxrkflx}vosy|wrrtz~rmlqw|~ytvwxtzxpjjjlnjlkgebbeknllsyvswz||}}||wtyywywrsv|~{|{y|}}truvxvsstvsqvzzxqlmsx|}{xvy~~}}~{}|z{}{~}zz{}tnntyy|~{y~z{~}}{upu}{yxy}zvzzpjpxyrnopou|~|{}}~yy~}urty}|wuz{|||yspuzzz|unpwzuomrvvsusrx~xw~}~}zy||zxtonpu}~xy~|yusw|~~|yz|~~|}{|}xy|}xy~~~}~|{~}}yyy{~z{|~~x|}}}}~zz}~|}~|}}{y{|{{|}xv{}{zyz{}~~}}zyyyvv}~~~z{{z{|~}xx|{|~{|~{{}}{~~}}}{wtwz|~}~yvtw|zqptutwyzz{||{|~~}|zxxyzyvtv|}zz{{{}}||{ywuuwyyy|{x{~~|~~{z|~~~|zz{{}~}}}|}zyz|}|}~~~~}~{{}}{{|}||}~~~~ \ No newline at end of file diff --git a/applications/PoP/poc/res/sound/s2.bin b/applications/PoP/poc/res/sound/s2.bin deleted file mode 100644 index f7044f3..0000000 Binary files a/applications/PoP/poc/res/sound/s2.bin and /dev/null differ diff --git a/applications/PoP/poc/res/sound/s3.bin b/applications/PoP/poc/res/sound/s3.bin deleted file mode 100644 index 00b0cf0..0000000 Binary files a/applications/PoP/poc/res/sound/s3.bin and /dev/null differ diff --git a/applications/PoP/poc/res/sound/s4.bin b/applications/PoP/poc/res/sound/s4.bin deleted file mode 100644 index 50c3438..0000000 --- a/applications/PoP/poc/res/sound/s4.bin +++ /dev/null @@ -1,5 +0,0 @@ -vwwxxz}{vspmjhfdb`_^]\ZYWUTSRSUWXYYXXUQLHEB@>@DHMQVZ_aabcbba`aabcdfhjiihgeb^[ZYXW[_chmsy~xrlea]XTVWY[^adgfedc`]ZWY[]_djosuvxywusrqponopprtwy|~~~|zwvvwwy|Ŀ|zwtvwy{}}{xvtvxz|~|yvroliklnpsvy|}}~}zwtpmifdfilpv||xusrrqrtvxyzz{{{zz{~}zwtrqonlkjhhhhhhggfffgghjlnoonnkfa\XUQNLKJIJJKLOSWZ]_adfgikmpruuuuusrpnnnnnoqrtx{~xqib]\[ZYZZ[ZWTQNMLKLNPRTUVWXXYYYXWVVVWWZ^bgkoswz}~{xvutsrsstuvxz{}~}{xuqmifdb_]\[ZYXWVUVVWWWXXXWWVVVWWWVUTSPMKGC?;730,++,,/37;>@BDEDDCBBA@@@@@AABCDEFGIKMOQSUXY[\^_`bcegjlptx|~{yxwusqnlhd`\ZXWUVWYZZYYXWUTRRRQQPONMKJHFFEEDDEEEEEEEDDCCCCCCEGIKORVY\_behjlnoqrstuvxz|~~}|{yxwwwwy|~~|ywusqqqppruxz~ytpnkihiklnqsvwwvvuqnkgc`\YXWVVWXYZZYYXVTSRRRRSUWY[]_`a``_^]\ZYYXWXZ[]_aceffeec`^[\\]^`bdfedcb`^\ZZZ[[\^_abcefghjklnopqrstuuvvtsqpnlkijlmoty~~}{zxwvttuvwwwwwvtrpnnmllmmnnnnnmmlkkjjiijjknqtx{~}|{z|}|vqmljhhknpsvx{|}}~}{xvspmiijkloty~}|{z|~}}}}}}}}}{yxvuttstvwxyzz{{|||}}~~~}}}|{{zxvtrpnljjiiijjkjigfedcbbdeffeca_\YVTUVWX[^acdfghijkkllmmmmmmnnoqtx{}~}{{zy{}{xvsqnkfa\WPJC=:742468::::97420//./247;?DIMOQTUTSRQPONORUW[afkortwwvtsqpnmllmmmllkkklllkkjihfedccbccdegjloty~~|ywvtsqoljheb_\ZWUSQOMKJIHJMPTX^cilmnpnligfedcdfhjmoqsuwy{}}~~~}}|{{zzyyzz{yxvtrqomoprtvxz|||}}|zyxvtrpomlklmnoprstsrrqpnmljhfeeeeeedddba`^\ZXWUSQPONMMOQSVZ_cghjkllkklmoqtw{{tnnmmmrw|{yvsrux{~|yvronoqsuz~zupkfb][[\\\]]^]\[ZWUROLIFBAABBDGKNOONNMJHFDCBAAABBCEGIMQVZ^bfjmpsvvvutplgc`]ZWXXYZ\^`bdfhjmqtxz|~{xuplgca_][^acgmsy~~}|{zyyxxxvtsqpnmlkihgfeedfgijkmnonnmmlkkjhfdca`__adfimrvyxwwuqnjhgfedefghhhhgeca_]ZXWWVVWXZ[]_`bccccb`_]]^`aeinrttuutrpnnopqsvz}}{yw{}zwusqoonnnprtvx{~|zwurpmkiihhimptx~}|zyyyyyyxxwtromkihhjlnprtvvtsqolifc_\YWUTRSUVXY[]_``abcefhknrvz~{vusrpu{}{yxxyyyzz{{zzyywvutrqpooppqtvy{||}}|{zz}~{wtrqonopqrrsssqomlkjihgfedba`____`bdfhikmnnnnnmmllnoprtvxyz{|}|{zyxwvvwxyyzz{zxusplheb`^\[\\]]^_`_^][YWTRPONLMNOPQQRSRPOMLJIGILNQUY]accdec`^[[[ZZ\^acddefffeeefffgijklmnoonnnljhfdba__```bcefhiklmmnnmllklmnprtvxyyzzz{{|}|ywutruwz||ywxyy{~~}}~~~|yvsstuwy|~{wsokheaaabbdgillllligda]YUQNKHEC@><841-)&"!#%'+047889851-*(&$#&*-18>DJNSW[]_abdegikmoqtvy{{||{ywtsrrqrtwy{~{xvtrqqppqrstvx{}{wrnkosx}}|}|wrnifedcbccdddeeeeffghjkmnpqpnkid^XSNJFBBDEGKOTY]adhjklmlkihhiijkmnpqqrstuwx{}}zwurqonmnopqqpppmkhfc`][YXVUVVWWYZ\]_`bcegijlmoppoooqrsuwz||z{{|yqmifbgmsy|wurpnoqrssrrrmhc^[XUTY_eku~|y{}wphc`^[\`cgmt{~{wvuuttttttssrrrssuwy{|vrmieddccegikmpruwxz|{{zyyxxwz|~{xurpnljihgfedcba`_^]\[[ZYXXWVUTRPNJFA=83.*)*+,/26:::9851,(&$" "$&(+/36889:853000//37;?BDGJKLMNPRTVWXZ[\^_`cfhklnoppqqqux|~}{z{{||zwusmhc^XRLGDA><==>>>>>><:8789:;=?ACCBBBAA@@BCDEEFFFEEDDDDDDFGHJLORSRQPNKGDBBAACFILNOPQPOMLJIGFFGIJNRVZ]_acb`][YXWVX\_chmsx|~yurnmoqsuwz|~}|{ywusrssttuvwxwvusrqppoonoqrtwz}~~~}{zywwvvuuttsuxz}ysokgcfhjmt{}ytqppoptw{{vspmlnprv{|wsnlkjikoswz}}{yy{|~ztojefhkmrx}ztokgb^\[YXWUTSPMJGC?;77888<;:9741-*('%$&(*,.024578:;<=>>==<<<==>@ACEGIKMPRTX[_begjllkkkklllptx{}yuqnljhgggggghhggfeca_][ZXVUTSROKHD@;612234:AGNT[agggggc`\YWVTSSRRRSTUVY\_adfilnqtwz}{wsnid``__`dhlpuz|yvroljhfdba_^\ZXVTQOMLLKLORUWYZ[[XTQNJFB?><;:987765433333445543210/./02369<@CGKORTVXXWUTRQPNOQSUY]bfkpuz~~{yvuuuuvy|~~~}ztoje`[VSRQOOPPQQPPONMLKJJIHHHIIHGFEC@>;97523467<:752112258<:7543212468;>ADFIKMNOOPONLKJIIHILNPSVX[]_acccccdefhkosw|~{ywxxyy|~|zyxwvvuuutsrqomkheb_\ZWTSSSSSTTUUUUUVWXY[]`bdefgfedccbbacegikmoqqqrrqpnmmmnnqtw{|}~|zxwwvuvxy{~¿}vpid`\XWWWWXYZ\\\[[]_acfjnrux|~|yvsqomkmnpruy||xtpkfb]YVROMJHFCA>;72.*&" "&*.13467889:<>@BEGJMORTW[_cfjmqtvxz|~|xwutsrrqqpoonnooooppprtvxz|~~|zxvspnlkjijklmopqqomkiea][YXWWXZ\]^_`_\ZWURPMLMMNORUXZ[\]\YWTQNJGEFFGJNRVZ^aeeca_[VQMJHFDEGJLPTY]^]]\ZWTRRSTUY]bfkotx}ÿ|xupkgb^ZUQNKHEC@><9753211013579;=?@BCEEFFGHIKLORUY\`cgjmptwz}~yuponmlosvy{~}|ywusqomljihfdb`^\[ZZ\^`begjmnprssttsqomkgda__``bfkptw{~~~|zxvw{~~xrmhb]\\[[]`cfilortvx{~ſ¿yvrolkigec`^ZTNID@=98:ADGKNRUYXWUTNG@840+'+/48?ELSX]afimpsw{~{vsrqpqrsttsrqponlnruy|~|ywtqnlllmnt|}{ywurpmknqsvy{~}zxuqmifdba_]\ZYYXXXYZZ[\\]]_`abbccccccb^[WTPMIGIJKMQVZ^djpw}ypgb`][]dkrx}}ywusqqrsttsrqqppopqrtuvwyxwvupjd]WPJCBBAAFMSY^bfkhda]XQKEDDCBFJNRTUWYVSQNLJIGGGGGKOSW]djprtuwwxxy}ĿĿ|vohb[TNGC?;8889:;=?ABCDEEEDDA>:741-*'%"!)19AFJOSRRQQRRSTY_djnsw{xvtqmiebccdfnv{rkfb]]_behmquz~~|zxvuttsrqqponnmnopqqppolheb`^\ZYWUSPLIFDB?>?@ABEHKOSW\^_`a^YTOID>96778:@CEGKPTX[]`b_\ZWTQNKNPSV[`eilortvxz|~}|yvroljhfdcbaa``_____]ZWTRPOMORTWYYZ[XTQMLKKJNSY^bfjmnnnnnoopqrtuxz}}xtrpmkpv{yxwww~}|ywurolifb_[YWVTTSSRQPONNNMMNOPQPONMHB=71,'" !"$(,15:@EKLMMNKHEBAA@@CGJNSY^djpv{~~}{zz}}yusrpnnnmmmllkhea^XQKEDBA@HPYajs|}}ulfc`]\_bdghijjhgecba``cehjmorsstttssrrsstvz}~xrlf_YSMJHECHMRW^elsvx{~|zywsolhea^[[ZZZ^adhov}~}|{zxvtrojfa\WRNJIHGFFEEEDCBAAAA@?=<:864479;?DINQSUWWVUTUWXZ^chmqsuwwutrqqpooonnprtwxz|~{xurpnlilpsw|{vqrrssx}}wqkihhghjkmnpqstvwyyyyyzz{|~~||{{{zyxwtqnkgc_\XUROLJHFFEEEEFFFDB@?>=<;98766666655532001346:?CFJMQSTUVVSPNJFB=:741////0134679:??@ABBCDDCCBCEFHLRW]cioux{~¿}{ywvusqoljgc_[WRNIFDA?=<;99887788999::::9986420-+(&$" !"$%),047:>AEINRVZ]abccdddddgknry}zwtrpmkjjiiiihhgfedb`][WRMIEB>;9753210..//024689:;<:752/,)&$#! &,26:?CFHKNOPPQSUVX\`dhknqtx{~yupljhgefhikkkjjgc_[UPJE?93-)&# !"#$&'&&%$$$##$&')*+,-+)(&"#(-39>??@@><:9;<>?BDFHIJJKLMNOPPQQPONMNOPRVY]`bdggfdb`^\YXWVUVWXY]bglrw|Ŀ~|{wtronpruy~ytplgghijmptwwwvutrpnnnnnljhfa\VQLHD??>>=AFJOTY^cbba`[WRMKHFDFGIJLOQSUWXZXWUTRPNLMOPQTX[^adgiiiiihgffhjlnsw|}|zxvsqnlifedba`_^]]^^^`bdfghijiihhggffghhiihhgd`\XTPLIGFDCDDEDB?=94/*%#   - !&+06=EMSY_ehjlnpqrsuy}}|zywuspmjgeb_\ZWTPLHC>94/*%   !(/47:>?@ABBCDEGILOSX]afkpty~zvqnlkiilorux|{vrnjfbabcdfimpponmhb[TOLHDEHKMPSVXWURPKE@:51,('(()/7>ELSZadegigeb``__^adhkorvy|~~~~~}{zxvttwz~~ytomjhfghiiklmnpqrsttuuvwxyyzzzyxwxyz{þ~~}}|xndZTNHBFIMQ[epyysonmmmpsvy|~}vojgebcfilorvy|}~|zvqmhc_ZVSQOMMNOPSX]behknnljhffedfiloqqrsoje`\XTPOOOORUX[]_ac`]ZWQKD><:86:50+%  -  #(/7>FLPTWXWUTRPNMNQTX]ciotx|}yuqmkjihkpty}}yuromkikmoqsuxzyxwvspmjigfddeefhjlnprtvvwwwwvvvxy{|~~{xussrrsux{}|{zxtokhfdbacdfimrw| \ No newline at end of file diff --git a/applications/PoP/poc/res/sound/s5.bin b/applications/PoP/poc/res/sound/s5.bin deleted file mode 100644 index ad44575..0000000 --- a/applications/PoP/poc/res/sound/s5.bin +++ /dev/null @@ -1 +0,0 @@ -xqjfb^Z[]_aeimqqqrromjgggffhjlnmllkgd`]ZXUSUWY\dlu}|void_ZZ\_bfkptvwxyxvtssstux|{vrmmmnou{|qg`ZTNOPQRUX[^__``aabbdfgiklnoooookhea\WQMLKKKNQTX\afjmqtvtrpnid_[XVSQQQQRSUWZ_dintz~}|{}|yvspnligfecbbbccfimpswz}{zxwqke`[WSOQRTUY\_bdfhjkklljhfdb`^]_acehkoqonmkfb^ZYWUUXZ]aejnrtwz{||}}|{zyxxwwwwwvutsqpnmkihfeeeegjmprstusqnlifc`__^^__`aaaaaabbcccbbbaa``aabbbaaaa``abdefhikmpruwy|~~~~~}}}xsrtuwy{~~|zwtqnljighhijkmnpppoolhd`\WSNMLJIKNPSVZ]aabbc`\YVTRQOOPPPRTUWZ]`ceghjjjjjklmnqsuwxyyzzzzzz{{{|}~}zxwvuuwz|}yvsqopruwz|~|zyxwvtsrqonljhfdcbaa`aaba_]ZWPJD>83-*+,-048ACDLSY`dijqzzphd]YRND><697=COXajmtrw}zxrrnkknie`\XVRPKGA?BFQUZffkmpszzsmkihgijjhfida]XUNJGHJMUY_ehmpu{xspnlorrsppmkga\WPNLORVY^_ehos|~{yxvvtvvuqnnjfebYUXS\]fimwx{~ywxtslffddcbdfhijloquuz}{vqlmnomilnponnotuz~~xtqrpprpofebdgkmnprw|~|wspmkigcdfjmptw~}yvqlgda]XVWUTQNKKNSY_dhjjlosy~yuqpmnlnonkhhac]_[XUTRNNNNLMNQRTUTSVYZ\cfs{xwuwwrjfddehgjigfeffheaXRLKKLJGHKOTY\`dhmrx~~zvsqrpnhd`]ZXUSRPMHFFHLPRQQTY\cmt|~}zz|{~wzxyusogd`_YWRQMOS\bjoptt}yuttuwvsqmhea^ZURNJHFGILORVX[`elv}}zvqnopsrsqnia_]`eiotx}vqkiopttrlgeabafdeehlt~~zvrnje_ZWWZ[\\]]_acfgfdeghnpsw||zurptronlmghgfgfhe_^WWRTWW_ajllqr{}~|z{{~|{{{{z|{yxvututuvuuyy{~~|wrmhdbciptutrqppolhda__beinsuwz}}|}~}ywtrmjihhlmmkhgghfda`aaaceghklnrttsuux|~}{yxwutusux~}{xvuusttuusstuvx|~~~~{z|~|tpmnopqomlhhhilosvusqopux||{xusttxz}}|~}~||}|yvspmjgda_^adhlosvxxvusrqonoqsvz~~~|zyz{|~}{xvvwvwvvupnllmnprttuutussrppmknqv{}}}~~~~~|{|}||{}|zxvutvvuttsrnjhffgijklmnonnonooppprsuvwvvwvwvvwwxz~~{z{~~|zvsqpqsuvutuxz}}{{z{}}{zyzz|}||{zzzxxx{{{|}~~}}~}|{|}~~~|xttsrqonmnoqqpnllmoponomkllmopopopnpqtx{|~~||{|{{z{zwvututuvz}~~~~~~}||||~~|}~|{zyz{}~~}zxxy|~|yxyz|~~}{yyz||~~~}|zwrnommnpsw{}|{z|~~{xvvwz{{|{|}}~{yurssvx|||{{{|{zyyyyxy{~~~~~}|zyyz}{xxz}~~~}}|}~||}~~}}}~~}~~~~~~|{zxxyz}}}{ywvwxz{{||{xusqrqpruwy{~~|zyy|~~~}|{z{zz|~~}~~~~|~~}}}}~~~~~~~}}|{{zz{}}}}}~||{{{|{}~~|}||{{~~||}~~~}}{yxwwwxy|~|zyzz|~}|zyxx{~~|{{}~{ywtrqqsvxyzyxwwy||{{z{}~~}}yyxx{~~}}|~|yxx{{xwvvvvvwxzz|~~|{}~}{{zzzz{~}{ywwxy{|{{z{{{{{||~~~~~~}}{yxwwusssvy{}}}{zxyy{}}{zwwvvwxy{{~~}yurrrttvy|}zyxy|~~~~}zxyzz{z{}~~}{yzyyyyz|~~}|yyyz|~}{zyxxx{|~}|||{|}}~~~~~~~}|~}}}}zwvvuuvx{}~~|{||}~~|zyxxy{~~||{|~~|z{|~}{{z{||||{|}}{zyxxyz|~~~~~~~{{yxxy|~~~}||}}|||{|}~~~}}~~}||{}~~}||{zyzzz{|~~}{zyyy{}~~~~}~~}e@(")%(/6DcwbN>3..16;@FLRY\V[he[[]`cfjmortwz|~¼~wrsqnrupmquoe]VZ]ZZ]bgeemnnpopsvusuuuuw|{zyz~}|~~|}zvvwxywwxuuuvy}~~|uokkiffjllmomgb_\^daWZffcfhhgbadbcedeecacikkllmmnqsqv{{yywwurtusw{}}|yuronopruxz}|xuqmkklkkloruy||}~|{{|}|zzyxwwxy{}~~}|}~~|zxvtsrpnlkkkjhgffghjlklllmnoqqponnnopomjhfcaaa``a`bdgjlmopqppomjgea^ZWWWWWXYZ]`bflptx||zxvtrpomlkjhijklnqstuxyyxurponpqsuvy{|||}~}{yvutstuwy{}}}}~}{yyyyyyz|}}zxvuvwxyyyz|~zyz|~|yvplhe`[TOMNRV[_dfhhikkkjihhhiknmllmorux{|}~|{zzxtrpnmlkihhhilpsuuuuuuvwxuqnic_ZWVUUVZ^bglqtwyxvsojfb_\YWWWY[_ejosuw}{upmlotwx{|}}|}|zz{|}}}|||~~~}|zxtqmklnqstw|}|||{yxwxyxwwusppqrrruxz|~~|zwspppppprssuz}{zz{}{xuuvwxwutv{}xvuutsqonnpsvyzz{}~~}{yuqmkmoruy|}|||}~{xsqooqvzzvsplhc_^]\]]ZVUVWZ]bdfjlmnprrsssssssttuvx{|~}ysmkllkkmrw|~{wsponosx|{unigda`aadgjmqx{xutux{~{xsnifdba`_^]\^`cfkpuwxz|}}{ywvwxwvwvtqomlkkkjjkmoruwz|}||}|zwutuuvwxyz|}~|zwspljhfefhjmpuzzuonnlkkkkkkllnopoprtutsrrrrqpoliecbdfhhjkkjklnnnnnmmnqtvvwwxz}~|zxurqsuvvvwxwvvvvusrpmkjhfc_[YXXYZ[\]_aabceghijlmnpsuwz|}~~|{yvspppprrsuw{}|{{{z{}~{wsokhhfefiknosvy||}|{yunhd^[XVTTVVX[`eiloruuwxyz{||{zxwwwxwtrponmlllkkkkklmnmmnnnmljihgecba`aabdgjnqtwyzyxvutrpljhffgjlmprtuvwy{||{zyxvuutttuuwxyyz{||~{yyxwwxxwwx{|}~|||}~|ywusrstuvxz}}zwrollllkklmmmnnprrtuvx{|~{vqmjgdbba_^^^^`abaa`^^^_``bcddegjlpuz~|yvtrrrqqqrsuw{}~}}|{zyxtrpnoprsuwz|}|yyyyz|~}|~}|{ywvvw{|{zzz{z{|}~~||}}}~|zxvvxz|||{{}~|{{z{|}{zyy{}~~~}|}||}|{zyyxwvvuuuvvwxz|~~}{{|{{zyxusttvy{~}|||{yvttuvwyzz{{|~~~~{yxwwwwwwwuuuvy{{|zxwvwxxxvtrpppprsttuuvvwy{|||{ywvvvvurqpoprtuuttuvxyz|~~{xvvusrrrrstuwxyyxxxyz{{{zxvustuuuuuvwy{|~}}{zyxxyz{||z{||~~~}|{{}~}zyyyyyyxwxz}~}{yxwwxyz}}{zzz{|}}{z|~{xwwz|~~{ywvtsqonmnnnnnooooppqrtttsrrqqsuuuuvvvwyz|{yz|~~}yyyxwvvuuvw{}|{{zyyyz|~}||}~}|{ywutuutuuvwxyy{|~}zxusrqqrtvxz|~}{ywusqonnoruwxyz|}}zxwwxxyyyyz|~~|zxvtrpnmnnnnpprsuwz|~~}|{zzyyyz{zzyxxxyzzzzzyyyyyzzz{|||}~~~}~~}{yxxwvvuwyz{|}~~}||}}}|||{{||}}}~~~}|||}}}~~}|}|{zzyyyxxxyz{||~~}}}|||{{{{||| \ No newline at end of file diff --git a/applications/PoP/poc/res/sound/s6.bin b/applications/PoP/poc/res/sound/s6.bin deleted file mode 100644 index 20ad5a4..0000000 --- a/applications/PoP/poc/res/sound/s6.bin +++ /dev/null @@ -1 +0,0 @@ -~~}}}|||||||{zzzyyyxxxxxxxyyyyyyyzzz{{|||}}~~~}}|{{zyyxxxwwwwvvvuuuuuuvvwwwwxyyyzz{|}~~~}|{zyxwvutssrqpppooooopqqrstvwyz|}~|zxvtsponmmmlmmmnopqsuvxz|~}{zxwwvvuuuuvvwxyz{}~~}|{zyxwwwwwwxxxyz{}~~}{zzyxxxxxxyyyzz{{|}~~~}}||{{{{{|||}}~~~~~~t}}barkKLfkZWr{xw}vtwwqmmmha^_]YWZ]^_bfijjlnoopruwx{~}||}|||{zywusrqpppqrqqqrsrpppooopqrrtuvwwxxxxyxwyz{{}~~}|||||}}||||{zzyxxxwwwwxxwwwwwwvvvvvvvvwxxxxyyyyyyyyzzzz{{|}}}~}~~~~~~~~~}}}}}}}}|||{zzzzyyyyyxxyyyyyxxxxxxxxxxxyyyyyzzzz{{|||||}}||}}}}}}}~~~~~~~~~~~~~}}}}|||||||{{{{{{{{zzzzzzyzzzzzzzzzzzzzzzzzzzzzzz{{{||}}}}}}~~~~~~~~~~}}|||||{{{{{{{{{{{{{{{{|{|{{{{{{{{zzz{{{|||||||||||||||||||||||||}}~~~~~~~~~}}}}}||{||{{{{zzzzzzzzzzzzzzzzzz{{{{zzz{{{{{{{{{{{||||}}||}}}}~~~~~~}}}}||||||{{{{{{{{{{zzzzzyyyyyyzzzzzz{{{{{{{|||||||||||}}~~~~~~}}}~~~~~~~}}}|||{{{{{{zzzzzzzzzzyyyyyyyyyzzzzzzyzzzzz{{{||||||}}~~~~~~~~~~}}~~~~~~~~~~~~}}}}}}}}||{{{{{zzzyyyyyxxxyyyyyyyyyyyyyzzzzz{{{{{{{|||}}}}~~~~~~~}}||||{{{zzzzyyyyyyxxxxxxxxxxxwwxxxxxxxxxxxxxxxxyyyzzzzz{{z{|||}}~~~}|||{{zyyyxxxxwwwvuuvuvuvvvvvvvvvvvuuuuuuuvvvvwwwxxxxxxyyyz{{||}}~~~~~}||{{zzyxxxwwvvuutsssrrqqqqqppppqqqrrrrssssttttuuvwwxxyz{|}}~~}|{{yxxwvutsrqpponnmmlllkkkkjjjkkkkkkkllmnoppqrstuvwxyz{}~~|zywutsqpomljigedcba`__^]]\\]]]^^^__`abdeghjkmortvxz}|yurolifc`]ZWUROMKIHFEEEEEFGHIKLNPSUX[_bfimquy}{tmga[TNHC=840,)&%$$%&(*-048<@EKPV\cjrzſ}rg[QG>60*%!"&+18?HR\gq|Žwmc[SKE?:620.-.037@CGKOSX]chntzĿyqh_WOG?82-($" !$'*.28>DKRZcluľzqibZTNHC?<:87789:<@CGKPV[bhnu|~xrmhc_[WUSQONNNOPRTWZ^bfjnsw|}yurnlifdba`_____abdfiknqsvy|umu_AHTTFEVconl}Ҫ{~wjmg|jqvj\Z]UVNJLPTPT\bnt|}zyy}{qgfeiu~yi\RLOWbqrdVMLOXfu{naXUYcqsg_]akxpcZY^huyj^WW[ftzla[[`jxzl`[[`lzuf]YZbm{vh_[[bm{rf]X[blzrg^[^eq~xlebdjtuia^`gp|tjc`bgp|xngcejr|zqjggjr|{rkhhkrz{slggkpvvpljlqxzsommrx~zsnlmptz|wqppruz}xp{sm}|zwsrqu||wstootwz{tspmswy{urpopux~}wtrqsvz}ywuuy|}yuttvz~{xvvwz|yvvwy}{yvuvx{}zwuttvx{~}zxvuuvx{}{{{|~~~~~~}|{zz{|}}|{zzz|}~}||||}~~||{{|}}~~||{{{{{|}~~}}}|||}}~~~}}~~}|{{{z{{|}~~}}}||}~~~~~~}||{{|||}~~}~~~{~~~~}~~z~|~~~}~}}~~~}|~}{}{z~~~~||~}~~{~}~~~|{}~}}}x|~x|vx~|||}~}~}{{}}~|~~~}~}~}}{|~}}}{z||~|{||{}}|z{|~|~z~{x~|~{yz~{~w~~|{|z}w}yw}|uwz~z{~~}zx}|}|y~{}~zz|~z~~}}|}}}~}{}||~y|~~|~|z~||}~{|{yz{z{t~~|xxy~xu}|{qvy{r{~~~yyx{|{}s{~x}ox}||y~yw|x~{}~}~||}~}y~~}}}}}}y{}}w}}}z}y~xyw{wr|xtxklkhowvoqt|~xkwourzzrixvxqy~~yt~wxwz|yr}tw~usyv}|xzp~ux}}rz~}|t~xu~tzwv|s{x{{x~{vqnmlnt~{{ny^[sdiqjtovop|euzpwku|r|~qwjzuqyszyj{}x{owy{hx}olglasfy}cultzXqzrxk{ylyv~`avrv_v{xywZfryuyw~~pxeucygyh~xyvgi~o{qywv~{zpyxqwtu{ko}hzf{akfpueqvwlrvyltwvalHzeoy~gprt^d{p~bfuga[va~uab`hp{wynzsr{pzlwfiv~ht|zxu{x{|}~}r|i}sxbyl~t|co|tivor}pskkvq_[u`wuomxvizqsp~wT{mw|fqqe_xgUtx|V{no{sxot~tlou|}mw||sidnzvi}}z~pquohxk}{mrvrimjwpvznxw{r]tsuyzzg{wlz~_{yrwbvquqqirzrayxwzmukvbsirjuvykbletmnx}~yikz~{n}kowrltk_~fyozbw|t]ZrKtoV]`xl_lxTqlrevo{nkmy~dzosnm}_~wI{kxthsqszpo~vx{}h_}t^_slgwzvew|uhjfjhzbw~k}{uomxx|xkpl\vgK~xivpvprqr~y{gzpo|~xrzys{qno~xqhui{}jkq|zw|}{yu~y}mtzsk{uwy{nvlrh~ry|qpxutnstvolavtqhett|jv}qwV~eiisw}wlvivrnxr}rthzxgw||~rv{|}qg|bh}}~znk|e{|xxejxuynutrvq]}ztupyotxvwzhpfqu{y{rzudyrv{ptnxvxyx~n{yy{ozyz{|s|yv}|z~}{{z}v{~|z}~}|~|x{~~}{|w~ys||{x~zx}{|y{x|~zz|{~}~{~q{~}~}w|~|~~{{w~~|{{}|y~yzz{~zy}~~~~}~z{||}~~~}~|}z}||}{~|~}{}~}~|}z~}|~~~x}y~|z~{~~~~~}~~~~}|~~~|{~~~}|~|~}~}~}~}}~~~|~}}}~~|}~~~~y~lp{|tqq]ytaeqkUMYbny\ax|ujivojkuvrzp~zloxlf{xoYenqz}uoqltreT`gbv}er]UbkycOmkctw[v~buIi{LTejiWTp}|v~ym^ltlxzty{wfLivprtrk]Xc|yr~z~sqry{n_`qvjdnwt~vltqelmjxtydqndqrvpW~daurrsyvcn`khXUclfzők~XRle\{xb]p{neZcryy}x~|srwwlwqXd{tpqbtd`8Hmxyn`wǭs[TSeoUb{pql\kogn{`U`jxhknmy|gyvvqoox~vhzsy}q^Rl~zkhhgnlmlgovzwxz|}t^lUL~gpzm[^}wynat|aUf}yrw}tt~tot}{t[tqg~{rclniz{~ysz|{sqypitz}}zw|yxwqy||y|whn|}yy~~xt~x{}rjnv{~{xz{vqrywtw|}zy{xyuvxry~ztx~~y~~}zy{xz{}~||{}~~}~~}}yv{~}}xx}~~{w|~}}|~|{~z}z~y{~}}|}}{|~}~}}zwz}~zz~}~~|{~|~~yy~~zyz~~}}~}{wz}y}}~}{~{z~}}{{~}~|~}}~~~{~~~}}|}~~|~~~~~}~~~uvmgfe~{gcgTUKex}uRIf[dfSOhҊI2KpYW]h͓A'A^=\ďW)&WϹxE6\Z;EpБI8`tGAerINmVW|X:Rznjn[Hg~hfvlroneSY}vbnw}~Y^u^bhftmwy|ff}x{}uyo`rvt}qvy~wkyti|shxzz}u{v|}wrhp{l]gpgrkjxzzuqnrxisxvtihk_swkfskeiu}cfh]cofWnrjfne_swmk~oepqdilhwztzvqw~qp{xx{zz}vuu|xowwu|~xs}{{x~tu~yvwzvw}vu{y}~{z{yr{{{~}{}}~}~~}}xu||xy||}}{~~xw|z{~|y|~{}|{zz|{zx{~{uw{|}~}}xy}|z}zy}~{yz|}{~xx}|~~~}~~}~~{|~~~|~~||~}}~~z|~}{}~{~{{~~}}|}~|||}~}~~z{|{}}}|~}{|~|}~~~{z}~}~{|~}}}~~}|{{~~}}~~~|}~~~~|}~smrr`bOGg^Drpqg,)IIJO3>TmjEUbӵb/P~qw›j]}ݸe" "^{XcuƓE *LŖdTmˈxYmqLAHE~Ćfz~eu4JfcQpr_^apmP`mi|jrpxfvTPɍdvcSObğ|zrdto_O=^}{~m|hJQr~jWlƼwmJ>SjwncqvxW7#@yljzn\HKd~u[Yuepmizyqs|vberyfohah^N_x|x`o}qgq{~zig{iUUtarrfdvzx}rzwsvys|qxyzrjcg\[q|vwrrx{jczsxwm^Xs{|~}~zlrnjbli^fu~tpx{n_l}yps|wjm}|pv~zwyy~xnmvyyv|usvusvx{pl}z{{{~~urxtnx|~wtvyvry{wsuz~~upw~{sqw{vtw}pgt~usv}{}|xz{zwux~|{~~|}|y|}z{ywwvz}~}~~yvxyvuzz{|z~~{y{{wx|}wyrpty}~{try{|zuw{}}~}~~{wuu{~~}zz~{zz}}ztv|||}|~xv|}}}{zz|zx|}}wtv{|}{xxy}}~|~|{}z{}~|~|zz~~}|~}}~{~}}~~|{~}|}~~~}~wx{}}}z{}~~zwqlndmam[jpoo^|kfuobv|`oomklmmllnjj||vtxwx|}~{wx~}zy~~~~|}}{xxy{}}zy{|yy~}{zzy{|}~}~~}~~~}z}~{x{~{~}||~}|}}~xxwwz}z|}~~{wvx~~~{xz}~|}|yy}}|{zz{~{vrqqsy~yvy~|z{~}|{yxy|{yz}}zy{~|}~|z|~{yz|}|}}}}}|{}~~~~~|{z|~~~}~}{z~~~}|}~}}{|~~}|}~zxy|~|{|}}||~}|~~~~~}}|}~~~}yxz|~}|}~~}|}}}~~}}~}}~~~~||}}}}}}~}~}}~~~~~~~}}~~~~~}}~~}}~}~~~~}~~~~~~}}~~}~~~~~}|~~~~ \ No newline at end of file diff --git a/applications/PoP/poc/res/sound/s7.bin b/applications/PoP/poc/res/sound/s7.bin deleted file mode 100644 index f7a69cc..0000000 --- a/applications/PoP/poc/res/sound/s7.bin +++ /dev/null @@ -1 +0,0 @@ -}}}~~~~}}}}|}jeq}|z|yrryyux}{xqjq}|{uf_`^[f|zyv_NXf[LNQKY~htr[K=,(ǁ:g̝aFeC]^%ja;Z½O)ۙKA]Tsp!C}ƭyUUi~fLF`ʨ\=;OalsiWTamqz[1-L{ǬoI2;füX.$:b|zҠ_1%8Z{umq|~o^V`}uYQXemh\SWktXQ`}|w_C5?arfmiVUkw]RZpkQELa{ykhqzƦo?.9CI^uzw`SkhjyykhvzlkxebpzpkdfwstvjRMo~}vjfs}lfeeiqpd^k{yf]mq=*DoɹjUXx¶nadg_TQUc`;;^}o\SfƗo]cry~zWAKow\J=:ETf}rmq|jYZlzaTS^qq`Z]ht{dC5FiaB8Gayza[ltehu\LYu}jaepskdi~mUQ_qyozxh`gqvwzhQO]ktvqmqwxrlmz{oms||wz~znaY[clrvzshehlmpzsel|xtor|wgdm{xoszzrlp~vuxyxuqrwyshag{pho{}vrszyswzrns~lfimnlhgmtvtpoyytx||{|zy~|vtvwtrrtvvvtsv{}wngfn{zuv|aMXp}~wjjtrhimlsoillp|{quzhXlwkn}z~F$Bijkuã~vt[Tpi3;[ceq|ûqr|vS,0LXbq`tӶod{|WHA2:^vx|fn~xpr];1DZl{}{ֵ~lQ6"$C]_`ltx_A@KS\jtztP?EP]kpowhB,&1NgfbjntĬnJ4?bvvzzjfydF8CUg|}|hO?^}yaX_oxmXMSk|igpvo_OGJ\t~{|}sf^^gr{|rhdhs}zvxzz|~|vmfeipx}z~neiw|k`]ezzpr}|eSOZpsdajm`bp|kaeuxe^fzs_V\po``m~j\Zfx}qkm{vfdkxrijt{tsw~xplms{zx{zohhoz}upqz~xy}tpqqwznpmWYtgOaW7GʼnF6b^?`|R8NZJupQIoRS~_D:icbaȃhm^y73Mcf_Ukz}wV0IINoYgq`XXZom||fTi|smt}|obUb~~sdl~}{qxrjqmelupnwvso\PJKZlzzwol|}i]YTUafdkqqu||xbYVR\ox{woljm{~wssuy~|unh\W^fr~yjb\RQYeoyuqmlgbbackw{zzxvvyuqlklnrv}~|~zyxuttw{}{z{~{wy}}xrnov~}zyyxurswzyurnkkklow}{ysnlqtx~|xtstvz}{wtwx|}~~{www{||{|~~~~}|}|{{|{zyzyyyxwvututssrpooonnlkjkjiiijjjkjjjiiihjklklnopqrstuuuuttuvwyyz{|}~|{{~}ytrrruuoloolkhghihfeeedgihfedfhhiijlmjghkntvutsrqquvwxxz|zvu{~{|zwttrpqolpqqphcgknmjffgdbdgomlkkjfbgknnllmnkfgkrvqmmppppuxvsposuvv{~~}zwttw}xtsqonnpmidceimonkgedeknkcZ[djigeglqmjnsz|yuw||{zvvvzzsrrokeceig_^flnd]^didY\chd]]gsvpmouxwy|||wruy|vrvwwtsuyxqkpw}~zqr|~~wurniclqtxsry|{|~|y}}~vonnlc]][[VRX\WNMWeuxruy{vlp{uhilsmaagf]RRY__]`iqvxvxotmcad\YV_daS>AOWbXYlifa]irunlvz}wuynjkrsnigd\TW`kttty|uokryz~{}{jco}}j\]^^VOZdkh^gpme\^k{}qswz|yĺȷy}mTEIVWH>:4. )30!&::7/3FVYOISX^YXr{lyyyprzylmiheetxx}wtoq}~~syshjnwjWH;0)#1:;/ 2DURQ[ihd]y˼Ȼxs~xYXQNLEQbeaQRZUH>DOVWD1W_bPPzwKVamransVbѶzp}|mtrd_z[XdhoinsZL?BQ]lhWGHRJIWmvwwwpT@AFKSTemjuyh`ON^btr`GDLXlgm~henzii\^eYm|ziUSKQccxdo޽ͿŪƯrc\TYZjpX:'5IO_e}zhXceaZ[}^X]fniqweJ69@BLUm~mUVcgqw·›{|{mVH,% "-9FDN\}t}ʹ~oxtvTH9:PRZRFMIQk|}_fhqna\NH>'/OxĺrtyR7CQ]XMOE7$"D]ffr`Zlqalv{ueZftduK?UhvbSD>;2P{mlUC]zǺƸvtrVrnfaiFBcoXT\\YEYxusun^:A^l{wtZ.GlqkqtmZioIG]sumpmdOX_@QpcSU[P@Xrs{s79UhV+'$(Euʠ_\|s`fňe|nF 5byj;Hp|c-/Pso\ryweqĴ˶tbntZ7+Zɲe-0]P331),Uw}snhֳN-@ex`@BNULBLa}bvpqqXK_{aiyqmYmyVNl\qokUB>2IeinnYMAUxorVZVglmydO^o}|xznkloybr~naj^ae^TpeoyuusviZbdRI7.02NMKwsz]KQBDUagOI0Dchzkntp[^~|g[XL]XYx{yssr~wrskecf`eyx|}~yxg]\RRV^pz}o`epsi`r{jhjasrla^USPQopmbl||zx{}~pjbejp|t|{{piclokcu|qtzuyrn|{{}vilpuql~}s|h^ct{w}|wngjlp}|yklrumt~yyskovz|~~yiqpyyqx{}wifi}{}ytpkorz|{skmxzvy|{z{ttsxz~rkrx}{zz}}njlw}~quruuu|}|ynpuwz{yzsuxzz{}y}~iow~|}z{vlpr}~xotxzyx|yztqu}|{}~}~usx|}|~|}tms||~|}qru|}}}otz~~v{~}|y|wrw{~z}|xty}~{z}}}qrz~~}}wtwz~~~xtx}|{{||vx{}~|vx{}|~}vsw~}~{vx{~}}~~uw{|}~~|}zx{~~~~zy{~}~|wx|~~~zz{~~~|z|~~~~}{|~}z|}}~{z|~~~x{}}}|~}{|}{}~|~~ww|{|}{~{|}~~}{z}|}~|ww~z{}}~}xzy|~~{||z{yz~}z|zwz{{~}{}}w{z~~~~{||~}yz|{x|xxywy{}~~}}|~{}}~}}}}}~y~zx|}}}yxy}~||xx~|x{|{}{y}zz~vvxxswx}~|y~{||x~|xy}}}|}}y~z{}z|z~~{}}}{x|t~|}}}}z~||{}~x|~}}||}ztsvzwr~w{}}t{|wx}|wy~|wmsxyzvz~~|~zzx}~{~bbexzhy{iyvwz|x|~sy~|{w}yz~ru|z{t{vn~}wszx}stv{{wxy|~~xrwupx}{}|~u|~|ls}}r~pqs|{yylu|xtwyrvusbywvvup}jqglxysxyzu}r}xx}{||}xuyxyz~|zsmutyytq{wzvz|hxwwvty}|{xtvym|xz}wq~p{~{}|vy|{~||xz{}{{yryvxtn~w}}x|zyn}pryn}xl{rlvxn}rz}|nvwdurn|zspvrtyxpz}~vmr}{{vtjf`{sfwdxzxww}jhup}waXztxl{q]quq}jpxvRp}amqZH?mxoWkh{urzb+1zoO^vVUdwNȎP]Lgqcը*M;buGfƩ?C|RloplҔ4[j%gWRl[i%gn>-]kUYr^e_KX{pUcy]QO.DxdGZikr|پuMKcu{ursw͹{m`^k}}tlgYKMZfg^VMELduvrph`affW>/-8Q\VSX[Zns{x}|}ɿs_[`cfims{~xpgZL=1/7<@DHMXaa`_VOUYVW^gkpw|xwvx{zrmljec_YVUSTURNKHHIMU]doy}vplc]]ZWWVVVVWWTRQNJGECBBCBDGDEMPOSYXYZ\]Z\bcdlw{}}~yttv|~~xsttxyrrx{ww{{~x{ytpmg_[SR]^WVW[VICDED@<9?LQB>QWMPTONOTOG\gWTZ]fqsstvn\]pxsoXRW^fq}}sswy{{xvtty{~{w{~vojhd]\bghjmoruy{ywz|ytvz{yxwy{{yxzyxvrsuspruwxvuxytrsonuz|~xrwxuwyy|~~xsojfhjlnigjmosxywvusqole^YTOMORUY]^_bdfkrxzxwvwwupkkprsw}}}{xvtqnjhhfcdb`dedjrv}yvvvvwwuw{|}~}~~~~|zyxvsokga\WQKFCCDHIGEGJNTY]diihhgilmkjnommoqux{|~|xtpnnoponmptx{~~}||{{}}yvwwvvvuttuvxyyz||zxurpmigfcbbbbb`aba_]]]\\[ZYXWXZ\_a``dfgknljlnprtx~~|zyxspopnmlkmmkhfffec``chha_bcbcgiiihghmrstw{}~{z{|{|~~||{y{||}~}~~~|yzyy{~}~}zxxxxwwxwy{zyxwvxyyxxyxwvwyzz{zxvsqqqpooonmkklkkkklnqsuwyyzzyxwxxvwyxvuw|~|{zyxyzyyzyxywuttrqpqqrsqponmkigedbaabcdegjnoqtvuvxwwxxy{|}~~}}~~}}~~}|zwuttsrrqpqpooqrponnoooopqrrtvyzzyz|}}}}}~~}{zxwvvvwxwuuuuuuvvvwwvwwwxxwxyzzz|~|yxxwvvtsrponmkjijklklnoonpqqrstttsssrsrstuvxz{|{{{{zyyz{{|}~~~~~~}|zxvuusqqpoooopqrssrqpoooponmnnmmmnmnoprqppqppqpppoonnoooopqrssrqrsstuwyyxwxwxyxy}~~}~}zwtqooruwy{zwutstuvwwwutrqppqrtuuutsrqpqsttsssrrqonmmkjjknrtw{~~|zxyz}~}|{|}~~}}|}~}{zz{|||{|}||~~|zxvutsrssrqponmlkjiijiiklllmnopppppoonmkkklmprtvvwyyz|~~~~}|{{{zyyxxwvvvxxwwvvutsrrqqpooppqrtuvvvwxyyyyyyxyzzzzyzzz{{||||}}~~}~}|~~~~~}{z{|}~~}|{{yxxxwyzzyzxwwwvvxyxwxyyyzzyyzzyxxwwxxxxxxyxwvutsrrrrstwyzz{}}}}|{{zzxvuwxwvvvuttuvutstttuuuvvwxxxwwwxxxyyyz{|}}}||}}}~~ \ No newline at end of file diff --git a/applications/PoP/poc/res/sound/s8.bin b/applications/PoP/poc/res/sound/s8.bin deleted file mode 100644 index 4a4da05..0000000 --- a/applications/PoP/poc/res/sound/s8.bin +++ /dev/null @@ -1 +0,0 @@ -~{~{{~{{olftRq``gՓurIkb?=* ,T/ ob;jtG_Ŷkc̰ũP5qPBLw9ZvrP=A8P^boаth8]t`hafd^Y5&^]t}]Hwgr{lHCRB Q{aJju;jvdqw^R#Br'AwCkBsmӺǵynyOPQ)):+bnGRȵw^2[flfejpveW;45=CVAqpRˉjpʦqx7,\ANMY|SybRBjf}wzklJPJW>R(q/mov͠w͖jrgM22;Z0IKPVdYB/g[F@l|szëȬrz|wwvHOhC_|`fks`XZlPf[[qq~y}gZpyimfoxd\T]\fwsyzvnwhTdWLTR[qdowzm}|{mSmw]cJLiwYQhiftdzhhV@^L`mQ^Xy`ph8\_ujx}}zow~zzvaN\`mqdapzvvl[`jfeilwz{grnv|vzyrggW_za^xwjlsygknogkmp~plujRrlHTV1WYUNcWM^_ejvxkiyvyhe~zspOV`e__vvwuohdkVP\i_SWYTXmefzeZ`NJUi_cjdy{r{qblyWqz{v}n`jXcsqrgyuirpfiV>@C5Gd@EfQIeerƽqlkJOf_]krr{v|peuvibYPTTPcnsjYZ[U`qbp|nrx|qidRMPQdld}uur~|w\opYYQSSO\n|{ntx}snaiQMOUZbdq{rle\S?@RUYdlwx_fdYrslu|pqzndS\LJZekzzrdMLBNtkwQByPSttoXRnjhjSiiSmѧz~nlrrj}y\lÜU|]LSqql[bgb|f=xqr=vSAYcktPo.dvm_fc{Ldkbpk`~qA`o7qvfx|ilvOL~eo{]_\WIVB[jZ{eYgSvqIv{{yjr0;uY;$0TVPcy[uTjJMlIvj;^uhuMev|\XРcmdjAdcix]uqTO]^`Y{xv˚r}yyzimVc`r}d[``~RpVrtyvxnw~sG{ʈ>xsVfxUr^IleyNT=R#Yr~{zmRam|nik|ll`|r}tyuyƉ~uyuiayg@[|eKp{uF?zeyq^LtinosldRYkokvn{bbnt|mi{fnqowiWapIcURGUuhtZLb8viOm_|bhPribhm}yjv~Wpog}{ĬÑywnw}byzuxy`xmmw}f}i}}{ywm}xmvz_~mdgw`hZ|WnMSyYdkv^n}U|wz}mvolo^nwqqWkpd{lglgvhgBP|z}d`Zye^q]PyXq~HqriM_j>crL{A{exLdX{PwQh]PzYkuY_qag}Y}ul[UliQoZedY|ot`fu~nrm|kbw|woqa|uvnzlr{pz{su}j}ydym{tpyv{czhwowtw}xfhNrk}oalfLhseJ=FJDMx{A\e^q`ka]dVSUW[fqSOGDz_jmXO3ZuPGUKa7=QO~xt@H,Faz[}}|R5dFTE4pYXmz{ZelfMM[Zmhb[mVoRzTz|}wnm}{~tK{yx|~~ew}jt~Qþh{yrnnw~{rwy}rxzuzzehq~~evrdVqup]baxjdWt`gyykejz|_ofythik~zzvxyzuvvn}uu~ss{zry}|s~~}tcajs|~qw~z}z|pdqzbbvy}|znjvjn|xaQvyx|wtx||yq~t~~tj{j~u~t{}{z}zx~kjqnf[uxp^l}|pfcsvfqzskzop}lTRnto|q|xtwk}tp{ikn~y}mfsut}|wrv{|{sxvnrxoux|nq~wuyw||qx~~t~~}yz||tzs~rrps{}}|vwpr`omiflyz}x}z{no|rs~txuvs|pfks||~y~wu}v}z~|rwuu}y~voj{xz{|{wyyvv|}|~|~zz}zwu|~}|v~~}~~}|w{wx~x~}z~{u{yf|cuza_czv^yxtgerbixw}ok~ytzl`p~hiwrs~z|{w~z{u|mzvw|~xyr|_x{lw|~spzuzxxuxz~xv|w{}|xuvyy|{w|{|{z}}}|xz{{y}~}}{}~|{{}|vyzz}x|{ux}vsxn{zswvx{y~umkfhurlhglvgg}oi^w`o|vyikpzjzxnq}xz|~}}|{z~|x~xz{xv|~|~zzoyz||}}~{}~~{~||x|z~y{xw||}z~~z||}}~|~}~|{}||||~~}||{~}{||}~{||}xy}|xz~}~||{~}~}}{x|~}}}||}~~}~~}}~z~~y~}~~~|~~~~~~~~~}~~~~~}~~~~~~}~~}y{}~~~~~~~~~}~~~~~~}~~}~~~~~zz|}}|~|}}~~~~~~~~}}~~~{|}z|}~~~}|}~~}}~}}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}~}}~~}~}~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~|}}}~~~}~~~~~~~~~~~~|{{{{{|}}|~~||}{|~~|{{}~~}||zvsrsuxz}|xwxz|zyyz|||{zxyzzyxvtssrqpnmlmooosx}yvvtqmd[VW[_dgkljgffeimopsyz{{{}~xuvwvwupnqtrrrpmjjlooqrsstvz~~}}~~zurruy{yursx}~}~~||~|~|z|oux}}}}vtnkghinw}|}~zsvrsy}~|~}}{}|yx|}~yxtsonppssptxz~~}|}{{zuqrwzz|~y|vpptz|}~~}~}yldflpv}zz}~~{snikptxyyz~{xwkig`adl}ywzo`W[hqrty~}zxyn_Zblszvqsy|wssvwcL=>JYqykdfkotzytxsianx}raWY`o|trnv|iZ[pvk\[wi\[cnt{uqoeclkp}wljwq[Waz{ePSgtimx|}ne_]\[j|~toqu|xjlqyvbKFMn|jgr|uwr|~pm}~xtxyurvw{tikgdemmu}|vrmpiehdl~yqot}|tqnpyx{zywtpopxyqnljmrx~xvrjhmmrwtxznkt~wrz}~{{iduyt{rv{upw}saYSJiieqxtxx~pwxhc]]mm_VMOkwT<7Ikzpx~spon{`VVXavzmf_^[[_gw|~~{roowwzrknns~su}|xiekm{wswy}snx~wofaitpqtsv}{nefiwrlf__trhnpd`zlp^N_|y[Sct~z}x`Vi{z^RYjty~uivtsx|b\mwzywv^Ycxm[ey}aTZlg_o|Z=An}nkx|jai|z~wsorvs{wzi_ipttocajkmv~yhZOWeunqyv|rqbZcrhX]kmtkqw}here}rkvstnovuࢢsfbfnqn~GOax~}}tztzuqno}sY]|_TkBJetp{yefYWdcmiebyqz`R^|ycmvXBUsxy|zholyxg{pq~edRtz_J+Npk^_OV|yalqKrv}k{|mj_M5HsqS_acqvrZV_vx}lo{aSZtlein{n_jwhs~~xknag?3<[qO83[eJgrrmbpGhYNw|Htqnۥy~{{sa=44>=Qmv|wU;.<_bnO +Vӭyp{fhywywzri]UYTJSY\|nwgZ}ֶzxi|mj}ovMICUcj[[n{hOtdTko}uww~nkp{{uwlnt|mnw{uviboxydYq|v`ix~{h[]SNE+2=" &1Halnwұ|npmxttjkia_DF7B;38WQc|wYOdixzw}tTUvzuoy{|shcYcry|w{ƽ|uhRB@BHJEK\y}ngjVQ\__mujefloulfe^ZSSfmosqwbgolmjnlnow\Lfuqr~~j\UYfrzq_eiuofopw}}unkg\Z^eq}gabehkjmpy~xs~srvtgljtzx{s~zot~wuyUHOfn|x|momd\^pnsv^qs^LLWeeiou{n~~zhlnkjrz~uj_epqu{wmpxy|||}uxy}sy}rmkcackpicljnokhuytzqqcjjleq~yrxzzzqjldefhkqqx|rlttio{}}ywyrt{zqsskfkpt}zqq{}~wogWOR[ejhosrukpriakpnwx}xtzyonoimukfa`]dirpu|yyyzsy{urysjjifmppqmhdcgnpx|~|~{txxy~~yvtvz~~~z|wu|zwtvzx{p}{}|zyspllmnyxvz~uligeiiimopqv|{vvy}~xnjggjmptyyyyzz{{y|}}}wyy}}~}zwx}x|{xwvtuww|{zwtrpnmpqsusolmkkkiffhjmruwwvvx||xwxwyvsqqnkklosyyvvwy}}|{ww}rjdaainqpsvvuxyxy{~|{yyz~{yzyz~|wtqtz|yzxz~}zvtssqpopokjnrtw|~z{ytxz{zrnklnrux|wstux{|~zvsohdbaeipu}~{z{z|}|yututpmkjkmoopppppqsuxz|zxyy{~~|{yxvxywtqnmoqssuvvvvvvvuvvy|{xvx{~|{{zzxvuuwyxwvvuvx{|{{|}}|}~~~}||zxvtstuvx{~~{xssssuvuuvvvvvuvuuttvw||}}~}}~~{ywvwz|}~|{z{}~zwtrrsux{{z{|}~|{zyyxwwxxy{~}zzz{|~~|yyxy|~}}}}}}||zyyyyxyz|||||{z|||~}|}~}~~~~~}~~}}|{zyyzzyyz{{|||||}~~}}~}}|||||||}~~~~~~~~~ \ No newline at end of file diff --git a/applications/PoP/poc/res/sound/s9.bin b/applications/PoP/poc/res/sound/s9.bin deleted file mode 100644 index 6f7f476..0000000 --- a/applications/PoP/poc/res/sound/s9.bin +++ /dev/null @@ -1 +0,0 @@ -~}|z{}~|yxy{~}zwwy||ywx|}zy{}|yy|}{{}~|zxxz}}|zwutux|~~}}|{z{~|zz|~ywtsrsw{~|||zyz}}{zyyz~~~~yutv||vrqonorx}{spsvwz}zvx|}xsswy|zusv{}yz}{ussstuwxxurnmnmmnqvz}~{yyxvvx|}}xtrty~tlhioy{tomqywnjjp{zqjgktwpknvyqlnu~|smmr|vpnr|~ulgiq|~xod_akxzrnqzyrrxzogekvxogdisxokmu~wpnry~umjls{~ulghox~tmlqz{tqsyvmggmv}{qkjpyvnkoxwmhir}xmfgmxsgcgqwjejtqiiq{xmglwxlb`ix|sg`bn{tjeitxvurqyulow}|nhhhhp||g\cott{~oecixskjihlw~|~{vv~{{ztvztpw~~qfjxyqlgis|pkr~~zspruyyvvyywvvwvw}~}wsv}y}z}{{|ysquvts|zppuwqjlwztvzzwy~{{}yppw~|wsvyvtuyysruzxohly{tsvvpmswsywt{~x{oehsy|{upjipxmo{{|xnnyvf^amyri`Z_q{pkr|||}}ukb^br{necflt||qhejs|zsppryxrkkv{|qlmpwtkeck{uf\atyj`cmws\V`p~jdhkmv|z{seey}{o^Xh|bU_w{pmkhgo{zslkvvrqkb^dqqb_huzppv~uebn{z~ycQZ|qimztebk{wmfgoz}rhfp{~}~{x|zuppy{qfcm}xhVQ^uxoqz~ytlegwrmssh]b~seamxoaVXlo[MPlyrbL@OttVGQmvaTS`tjWTao~|ikuqiq~ttqf`ksmlgXQ`suzq_Wfx\Xi|wnprrzzsj^^t|ri\R]{uhXLTokSQhhT[n~e\fnnnz|f_gpqpxtmuxu~srtq|ystrdTVoyxwjUQgsZQf}fVdlZaywcaozzxsmt{yst}~plqx{}rjoutqwyhciot~|nkllrzne]\m|meXKKe~vaOSpl^guyu}xrtl_`ysYKYyy`PWr}hevw|yu~rw}laj}pqz|m\[m{zrr~men{|us}||qtuuocgqtrum^]flotwqsrnp~||ypio{uy{sik~ytozw|txzxxr}yspvwpkqvlnohagy{vundh{zpc[cyyePIUnz[OYm~ladpx}unhaZ]m|scY`tngnpdeoz~|k]]iuz{w}yps{sy{~{nhpwhgr{xrv~lhs~{lfpzwjdo||k_g{zqw~mouksrkpn]MObuvliu}ta]j|wovsktux}zvovyrxqddpy{z|qtywomwrdcovsournxysz|{nku~wrv~ysy~tpu~{v{~rnt||yy}|rlr|zxyvrpty~}}||zz{}}zz}~}xwy|xpmt}~~ssz|yz~{rjkpttuy~{yvtsw~}~~~yrpv|uqsutpowzuuxyxvx~||}{x|}yqljmqsrpppmga][^dinrw}|zz}~}}xttvunhdee`[VYbjmmmoqnf_^fs~wpot~}yx||xtszviabhnmjehq{yohgknnigiotw|}yssx}~ztmhfghec``chovzxsoosxxtpkknv~~~~|xutx}{{xqpt|zspokc\Y^gqvuqqyzqprtsrrx|phhnv}}sh`bn|ypjhhgdbelsyzl`[^hzpeemwtifhlqsu{|vla_ht~}nd_blw|vwtaSORZesymdadm{wlgimomknwzoiiigddhoxzmeceijhhmuui`[Y[ahnt~vsv||pnrzyrooljhhjq{}|unmortqjedkt{zoffmw{wsu|tnnsyxqhekxzngfgfcabjuyz|nglstqtz|{~|uoiiu|ywuuvzuqrstwz|~zsot}rlosqmjhfglt{~}ymc^ZWZdsrg^WST_qtd]XRKGLYgopsz~wokijknrw|}|}}|zy|{tnlotyzvrswyvsx}}}}yyytleaafr~{pd[X\cijlpu|zqhdcfkpw|yz}|sf``aaeoy}yussx|vuy~}~|unlnu||wsswywros|ywvtsxzssz~wpnpqqrw~}urstsqrtwyzyxz}tt}}}zngfjoruywd_fqvrkghp}vomptx{~{okq{}uopv{~~~~||xvtqlgefjpwyrnlnrtvy||yusv{zussuxy}~{{zsmhddhov|{yyxwy{{xsoou}tqw}|ukegoy~~sos|yzxpifinrsrv}sga`ciry~|xxz{xsposy}wja\]agox}{||xwywsnmr~wneaaeoz~yuw|yrmklptvx|~zvssvz{vsruwwxz|}||}}vnkmrvy}~|zxuqkefqtpoliec`bkw}vstx|}|wsru|xttuvwxyxwxy}{usuy|yrljq|~~}{yy{}~|xuuxzxtppt{|vsuz}}|{|{yxxyywtqquz|}|~~}|~|{{zwspqtxz{}~~~}zxxyzzyy{~~zwuuvxyyyy{}~{yy}{ursvz}~{yy|~}|z}~{yyz{{{{|||{zywvwwvspnosy~}~~~{yxvurpnnooooqsvy{||||}~~|zyywuttvxz|~}|{zyyyzzzz{{{||}~~}}|}~|yxwwxxwvuuvxz{{{yvuvwy}~~~~}||{{{{{||{zywvuuvwxyxwvvvwxyz{|~}{z{{}~~|zwwxz{|}}||zzyyxwvvvvwwxxyyz}~~~~}|zzz{||}}||{zzzyyyz{{{|}~~~~}||||||||| \ No newline at end of file diff --git a/applications/PoP/poc/res/sound/snd.arc b/applications/PoP/poc/res/sound/snd.arc deleted file mode 100644 index b543331..0000000 Binary files a/applications/PoP/poc/res/sound/snd.arc and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/0-0.png b/applications/PoP/poc/res/tiles/0-0.png deleted file mode 100644 index e0ed9f1..0000000 Binary files a/applications/PoP/poc/res/tiles/0-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/1-0.png b/applications/PoP/poc/res/tiles/1-0.png deleted file mode 100644 index 87ac84a..0000000 Binary files a/applications/PoP/poc/res/tiles/1-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/10-0.png b/applications/PoP/poc/res/tiles/10-0.png deleted file mode 100644 index 9592e3d..0000000 Binary files a/applications/PoP/poc/res/tiles/10-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/11-0.png b/applications/PoP/poc/res/tiles/11-0.png deleted file mode 100644 index 4285c0c..0000000 Binary files a/applications/PoP/poc/res/tiles/11-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/12-0.png b/applications/PoP/poc/res/tiles/12-0.png deleted file mode 100644 index 95f277e..0000000 Binary files a/applications/PoP/poc/res/tiles/12-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/13-0.png b/applications/PoP/poc/res/tiles/13-0.png deleted file mode 100644 index 2451799..0000000 Binary files a/applications/PoP/poc/res/tiles/13-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/14-0.png b/applications/PoP/poc/res/tiles/14-0.png deleted file mode 100644 index 0cc421c..0000000 Binary files a/applications/PoP/poc/res/tiles/14-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/15-0.png b/applications/PoP/poc/res/tiles/15-0.png deleted file mode 100644 index 9bf0709..0000000 Binary files a/applications/PoP/poc/res/tiles/15-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/16-0.png b/applications/PoP/poc/res/tiles/16-0.png deleted file mode 100644 index 72eca2b..0000000 Binary files a/applications/PoP/poc/res/tiles/16-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/17-0.png b/applications/PoP/poc/res/tiles/17-0.png deleted file mode 100644 index 0cc421c..0000000 Binary files a/applications/PoP/poc/res/tiles/17-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/18-0.png b/applications/PoP/poc/res/tiles/18-0.png deleted file mode 100644 index 0cc421c..0000000 Binary files a/applications/PoP/poc/res/tiles/18-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/19-0.png b/applications/PoP/poc/res/tiles/19-0.png deleted file mode 100644 index c7dc664..0000000 Binary files a/applications/PoP/poc/res/tiles/19-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/1A-0.png b/applications/PoP/poc/res/tiles/1A-0.png deleted file mode 100644 index 7fdc838..0000000 Binary files a/applications/PoP/poc/res/tiles/1A-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/1B-0.png b/applications/PoP/poc/res/tiles/1B-0.png deleted file mode 100644 index adae927..0000000 Binary files a/applications/PoP/poc/res/tiles/1B-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/1C-0.png b/applications/PoP/poc/res/tiles/1C-0.png deleted file mode 100644 index 4f4b779..0000000 Binary files a/applications/PoP/poc/res/tiles/1C-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/1D-0.png b/applications/PoP/poc/res/tiles/1D-0.png deleted file mode 100644 index a1e1fdf..0000000 Binary files a/applications/PoP/poc/res/tiles/1D-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/1E-0.png b/applications/PoP/poc/res/tiles/1E-0.png deleted file mode 100644 index 1a18c88..0000000 Binary files a/applications/PoP/poc/res/tiles/1E-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/1F-0.png b/applications/PoP/poc/res/tiles/1F-0.png deleted file mode 100644 index 92f75ea..0000000 Binary files a/applications/PoP/poc/res/tiles/1F-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/2-0.png b/applications/PoP/poc/res/tiles/2-0.png deleted file mode 100644 index 4a75f0d..0000000 Binary files a/applications/PoP/poc/res/tiles/2-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/3-0.png b/applications/PoP/poc/res/tiles/3-0.png deleted file mode 100644 index 0cc421c..0000000 Binary files a/applications/PoP/poc/res/tiles/3-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/4-0.png b/applications/PoP/poc/res/tiles/4-0.png deleted file mode 100644 index 49356c3..0000000 Binary files a/applications/PoP/poc/res/tiles/4-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/5-0.png b/applications/PoP/poc/res/tiles/5-0.png deleted file mode 100644 index ce25026..0000000 Binary files a/applications/PoP/poc/res/tiles/5-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/6-0.png b/applications/PoP/poc/res/tiles/6-0.png deleted file mode 100644 index ae5ae7f..0000000 Binary files a/applications/PoP/poc/res/tiles/6-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/7-0.png b/applications/PoP/poc/res/tiles/7-0.png deleted file mode 100644 index e090d76..0000000 Binary files a/applications/PoP/poc/res/tiles/7-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/8-0.png b/applications/PoP/poc/res/tiles/8-0.png deleted file mode 100644 index 0cc421c..0000000 Binary files a/applications/PoP/poc/res/tiles/8-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/9-0.png b/applications/PoP/poc/res/tiles/9-0.png deleted file mode 100644 index 0cc421c..0000000 Binary files a/applications/PoP/poc/res/tiles/9-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/A-0.png b/applications/PoP/poc/res/tiles/A-0.png deleted file mode 100644 index 85e7a55..0000000 Binary files a/applications/PoP/poc/res/tiles/A-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/B-0.png b/applications/PoP/poc/res/tiles/B-0.png deleted file mode 100644 index 1c698b4..0000000 Binary files a/applications/PoP/poc/res/tiles/B-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/C-0.png b/applications/PoP/poc/res/tiles/C-0.png deleted file mode 100644 index 15824da..0000000 Binary files a/applications/PoP/poc/res/tiles/C-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/D-0.png b/applications/PoP/poc/res/tiles/D-0.png deleted file mode 100644 index ae6fc2f..0000000 Binary files a/applications/PoP/poc/res/tiles/D-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/E-0.png b/applications/PoP/poc/res/tiles/E-0.png deleted file mode 100644 index a468fe6..0000000 Binary files a/applications/PoP/poc/res/tiles/E-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/F-0.png b/applications/PoP/poc/res/tiles/F-0.png deleted file mode 100644 index 5251ccb..0000000 Binary files a/applications/PoP/poc/res/tiles/F-0.png and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile00.pal b/applications/PoP/poc/res/tiles/tile00.pal deleted file mode 100644 index 1ca5537..0000000 Binary files a/applications/PoP/poc/res/tiles/tile00.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile01.atl b/applications/PoP/poc/res/tiles/tile01.atl deleted file mode 100644 index 854bd57..0000000 Binary files a/applications/PoP/poc/res/tiles/tile01.atl and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile01.pal b/applications/PoP/poc/res/tiles/tile01.pal deleted file mode 100644 index d15dee2..0000000 Binary files a/applications/PoP/poc/res/tiles/tile01.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile01.raw b/applications/PoP/poc/res/tiles/tile01.raw deleted file mode 100644 index f406d72..0000000 --- a/applications/PoP/poc/res/tiles/tile01.raw +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/applications/PoP/poc/res/tiles/tile01_merged.pal b/applications/PoP/poc/res/tiles/tile01_merged.pal deleted file mode 100644 index 350006d..0000000 Binary files a/applications/PoP/poc/res/tiles/tile01_merged.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile02.pal b/applications/PoP/poc/res/tiles/tile02.pal deleted file mode 100644 index ac7c0f1..0000000 Binary files a/applications/PoP/poc/res/tiles/tile02.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile03.atl b/applications/PoP/poc/res/tiles/tile03.atl deleted file mode 100644 index e91004b..0000000 Binary files a/applications/PoP/poc/res/tiles/tile03.atl and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile03.raw b/applications/PoP/poc/res/tiles/tile03.raw deleted file mode 100644 index 040d79b..0000000 --- a/applications/PoP/poc/res/tiles/tile03.raw +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/applications/PoP/poc/res/tiles/tile03_merged.pal b/applications/PoP/poc/res/tiles/tile03_merged.pal deleted file mode 100644 index e792994..0000000 Binary files a/applications/PoP/poc/res/tiles/tile03_merged.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile04.pal b/applications/PoP/poc/res/tiles/tile04.pal deleted file mode 100644 index c631629..0000000 Binary files a/applications/PoP/poc/res/tiles/tile04.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile05.pal b/applications/PoP/poc/res/tiles/tile05.pal deleted file mode 100644 index 3383048..0000000 Binary files a/applications/PoP/poc/res/tiles/tile05.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile06.pal b/applications/PoP/poc/res/tiles/tile06.pal deleted file mode 100644 index 1dd634a..0000000 Binary files a/applications/PoP/poc/res/tiles/tile06.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile07.pal b/applications/PoP/poc/res/tiles/tile07.pal deleted file mode 100644 index d3ba12e..0000000 Binary files a/applications/PoP/poc/res/tiles/tile07.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile08.pal b/applications/PoP/poc/res/tiles/tile08.pal deleted file mode 100644 index 3358611..0000000 Binary files a/applications/PoP/poc/res/tiles/tile08.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile09.pal b/applications/PoP/poc/res/tiles/tile09.pal deleted file mode 100644 index 3358611..0000000 Binary files a/applications/PoP/poc/res/tiles/tile09.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile0a.pal b/applications/PoP/poc/res/tiles/tile0a.pal deleted file mode 100644 index 1100030..0000000 Binary files a/applications/PoP/poc/res/tiles/tile0a.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile0b.atl b/applications/PoP/poc/res/tiles/tile0b.atl deleted file mode 100644 index cd4f242..0000000 Binary files a/applications/PoP/poc/res/tiles/tile0b.atl and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile0b.raw b/applications/PoP/poc/res/tiles/tile0b.raw deleted file mode 100644 index 251fc9d..0000000 --- a/applications/PoP/poc/res/tiles/tile0b.raw +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/applications/PoP/poc/res/tiles/tile0b_merged.pal b/applications/PoP/poc/res/tiles/tile0b_merged.pal deleted file mode 100644 index b965cf3..0000000 Binary files a/applications/PoP/poc/res/tiles/tile0b_merged.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile0c.pal b/applications/PoP/poc/res/tiles/tile0c.pal deleted file mode 100644 index 4ea9a75..0000000 Binary files a/applications/PoP/poc/res/tiles/tile0c.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile0d.pal b/applications/PoP/poc/res/tiles/tile0d.pal deleted file mode 100644 index a6144a1..0000000 Binary files a/applications/PoP/poc/res/tiles/tile0d.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile0e.atl b/applications/PoP/poc/res/tiles/tile0e.atl deleted file mode 100644 index 114a61f..0000000 Binary files a/applications/PoP/poc/res/tiles/tile0e.atl and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile0e.raw b/applications/PoP/poc/res/tiles/tile0e.raw deleted file mode 100644 index 10116ff..0000000 --- a/applications/PoP/poc/res/tiles/tile0e.raw +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/applications/PoP/poc/res/tiles/tile0e_merged.pal b/applications/PoP/poc/res/tiles/tile0e_merged.pal deleted file mode 100644 index 1dcf99d..0000000 Binary files a/applications/PoP/poc/res/tiles/tile0e_merged.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile0f.pal b/applications/PoP/poc/res/tiles/tile0f.pal deleted file mode 100644 index 32fde77..0000000 Binary files a/applications/PoP/poc/res/tiles/tile0f.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile10.pal b/applications/PoP/poc/res/tiles/tile10.pal deleted file mode 100644 index 36cf0d8..0000000 Binary files a/applications/PoP/poc/res/tiles/tile10.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile11.pal b/applications/PoP/poc/res/tiles/tile11.pal deleted file mode 100644 index 6c1b705..0000000 Binary files a/applications/PoP/poc/res/tiles/tile11.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile12.pal b/applications/PoP/poc/res/tiles/tile12.pal deleted file mode 100644 index 7ed7e8e..0000000 Binary files a/applications/PoP/poc/res/tiles/tile12.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile13.atl b/applications/PoP/poc/res/tiles/tile13.atl deleted file mode 100644 index 0b8d6af..0000000 Binary files a/applications/PoP/poc/res/tiles/tile13.atl and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile13.raw b/applications/PoP/poc/res/tiles/tile13.raw deleted file mode 100644 index 69c1670..0000000 --- a/applications/PoP/poc/res/tiles/tile13.raw +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/applications/PoP/poc/res/tiles/tile13_merged.pal b/applications/PoP/poc/res/tiles/tile13_merged.pal deleted file mode 100644 index 83541b0..0000000 Binary files a/applications/PoP/poc/res/tiles/tile13_merged.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile14.atl b/applications/PoP/poc/res/tiles/tile14.atl deleted file mode 100644 index e91004b..0000000 Binary files a/applications/PoP/poc/res/tiles/tile14.atl and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile14.pal b/applications/PoP/poc/res/tiles/tile14.pal deleted file mode 100644 index 3358611..0000000 Binary files a/applications/PoP/poc/res/tiles/tile14.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile14.raw b/applications/PoP/poc/res/tiles/tile14.raw deleted file mode 100644 index 040d79b..0000000 --- a/applications/PoP/poc/res/tiles/tile14.raw +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/applications/PoP/poc/res/tiles/tile14_merged.pal b/applications/PoP/poc/res/tiles/tile14_merged.pal deleted file mode 100644 index e792994..0000000 Binary files a/applications/PoP/poc/res/tiles/tile14_merged.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile15.pal b/applications/PoP/poc/res/tiles/tile15.pal deleted file mode 100644 index cb4ec0a..0000000 Binary files a/applications/PoP/poc/res/tiles/tile15.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile16.pal b/applications/PoP/poc/res/tiles/tile16.pal deleted file mode 100644 index 4f0b536..0000000 Binary files a/applications/PoP/poc/res/tiles/tile16.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile17.pal b/applications/PoP/poc/res/tiles/tile17.pal deleted file mode 100644 index 3358611..0000000 Binary files a/applications/PoP/poc/res/tiles/tile17.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile18.pal b/applications/PoP/poc/res/tiles/tile18.pal deleted file mode 100644 index 3358611..0000000 Binary files a/applications/PoP/poc/res/tiles/tile18.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile19.pal b/applications/PoP/poc/res/tiles/tile19.pal deleted file mode 100644 index 9f81666..0000000 Binary files a/applications/PoP/poc/res/tiles/tile19.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile1a.pal b/applications/PoP/poc/res/tiles/tile1a.pal deleted file mode 100644 index f7a99d7..0000000 Binary files a/applications/PoP/poc/res/tiles/tile1a.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile1b.pal b/applications/PoP/poc/res/tiles/tile1b.pal deleted file mode 100644 index a2b647d..0000000 Binary files a/applications/PoP/poc/res/tiles/tile1b.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile1c.pal b/applications/PoP/poc/res/tiles/tile1c.pal deleted file mode 100644 index 4823657..0000000 Binary files a/applications/PoP/poc/res/tiles/tile1c.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile1d.pal b/applications/PoP/poc/res/tiles/tile1d.pal deleted file mode 100644 index 1a1f7dc..0000000 Binary files a/applications/PoP/poc/res/tiles/tile1d.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile1e.pal b/applications/PoP/poc/res/tiles/tile1e.pal deleted file mode 100644 index 121acae..0000000 Binary files a/applications/PoP/poc/res/tiles/tile1e.pal and /dev/null differ diff --git a/applications/PoP/poc/res/tiles/tile1f.pal b/applications/PoP/poc/res/tiles/tile1f.pal deleted file mode 100644 index 185a9ed..0000000 Binary files a/applications/PoP/poc/res/tiles/tile1f.pal and /dev/null differ diff --git a/applications/PoP/poc/res/title/t0_0.atl b/applications/PoP/poc/res/title/t0_0.atl deleted file mode 100644 index f65d6e5..0000000 Binary files a/applications/PoP/poc/res/title/t0_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t0_1.atl b/applications/PoP/poc/res/title/t0_1.atl deleted file mode 100644 index 0150781..0000000 Binary files a/applications/PoP/poc/res/title/t0_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t0_2.atl b/applications/PoP/poc/res/title/t0_2.atl deleted file mode 100644 index 506f97f..0000000 Binary files a/applications/PoP/poc/res/title/t0_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t0_3.atl b/applications/PoP/poc/res/title/t0_3.atl deleted file mode 100644 index a4144fa..0000000 Binary files a/applications/PoP/poc/res/title/t0_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t0_4.atl b/applications/PoP/poc/res/title/t0_4.atl deleted file mode 100644 index 76b82fe..0000000 Binary files a/applications/PoP/poc/res/title/t0_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t1_0.atl b/applications/PoP/poc/res/title/t1_0.atl deleted file mode 100644 index f65d6e5..0000000 Binary files a/applications/PoP/poc/res/title/t1_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t1_1.atl b/applications/PoP/poc/res/title/t1_1.atl deleted file mode 100644 index 0150781..0000000 Binary files a/applications/PoP/poc/res/title/t1_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t1_2.atl b/applications/PoP/poc/res/title/t1_2.atl deleted file mode 100644 index f5dbcb6..0000000 Binary files a/applications/PoP/poc/res/title/t1_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t1_3.atl b/applications/PoP/poc/res/title/t1_3.atl deleted file mode 100644 index 721abae..0000000 Binary files a/applications/PoP/poc/res/title/t1_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t1_4.atl b/applications/PoP/poc/res/title/t1_4.atl deleted file mode 100644 index 76b82fe..0000000 Binary files a/applications/PoP/poc/res/title/t1_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t2_0.atl b/applications/PoP/poc/res/title/t2_0.atl deleted file mode 100644 index f65d6e5..0000000 Binary files a/applications/PoP/poc/res/title/t2_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t2_1.atl b/applications/PoP/poc/res/title/t2_1.atl deleted file mode 100644 index 0150781..0000000 Binary files a/applications/PoP/poc/res/title/t2_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t2_2.atl b/applications/PoP/poc/res/title/t2_2.atl deleted file mode 100644 index a7e40b9..0000000 Binary files a/applications/PoP/poc/res/title/t2_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t2_3.atl b/applications/PoP/poc/res/title/t2_3.atl deleted file mode 100644 index 8309a95..0000000 Binary files a/applications/PoP/poc/res/title/t2_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t2_4.atl b/applications/PoP/poc/res/title/t2_4.atl deleted file mode 100644 index 76b82fe..0000000 Binary files a/applications/PoP/poc/res/title/t2_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t3_0.atl b/applications/PoP/poc/res/title/t3_0.atl deleted file mode 100644 index f65d6e5..0000000 Binary files a/applications/PoP/poc/res/title/t3_0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t3_1.atl b/applications/PoP/poc/res/title/t3_1.atl deleted file mode 100644 index 0150781..0000000 Binary files a/applications/PoP/poc/res/title/t3_1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t3_2.atl b/applications/PoP/poc/res/title/t3_2.atl deleted file mode 100644 index b86535a..0000000 Binary files a/applications/PoP/poc/res/title/t3_2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t3_3.atl b/applications/PoP/poc/res/title/t3_3.atl deleted file mode 100644 index f7b5ca0..0000000 Binary files a/applications/PoP/poc/res/title/t3_3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/t3_4.atl b/applications/PoP/poc/res/title/t3_4.atl deleted file mode 100644 index 76b82fe..0000000 Binary files a/applications/PoP/poc/res/title/t3_4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/title/title.arc b/applications/PoP/poc/res/title/title.arc deleted file mode 100644 index 3cf9f33..0000000 Binary files a/applications/PoP/poc/res/title/title.arc and /dev/null differ diff --git a/applications/PoP/poc/res/title/title.pal b/applications/PoP/poc/res/title/title.pal deleted file mode 100644 index 2b69603..0000000 Binary files a/applications/PoP/poc/res/title/title.pal and /dev/null differ diff --git a/applications/PoP/poc/res/vizier/g0.atl b/applications/PoP/poc/res/vizier/g0.atl deleted file mode 100644 index 4a8b0bd..0000000 Binary files a/applications/PoP/poc/res/vizier/g0.atl and /dev/null differ diff --git a/applications/PoP/poc/res/vizier/g1.atl b/applications/PoP/poc/res/vizier/g1.atl deleted file mode 100644 index 39fbb21..0000000 Binary files a/applications/PoP/poc/res/vizier/g1.atl and /dev/null differ diff --git a/applications/PoP/poc/res/vizier/g2.atl b/applications/PoP/poc/res/vizier/g2.atl deleted file mode 100644 index 5c8405c..0000000 Binary files a/applications/PoP/poc/res/vizier/g2.atl and /dev/null differ diff --git a/applications/PoP/poc/res/vizier/g3.atl b/applications/PoP/poc/res/vizier/g3.atl deleted file mode 100644 index 6b6aff3..0000000 Binary files a/applications/PoP/poc/res/vizier/g3.atl and /dev/null differ diff --git a/applications/PoP/poc/res/vizier/g4.atl b/applications/PoP/poc/res/vizier/g4.atl deleted file mode 100644 index 36515a4..0000000 Binary files a/applications/PoP/poc/res/vizier/g4.atl and /dev/null differ diff --git a/applications/PoP/poc/res/vizier/vizier.arc b/applications/PoP/poc/res/vizier/vizier.arc deleted file mode 100644 index ffb7818..0000000 Binary files a/applications/PoP/poc/res/vizier/vizier.arc and /dev/null differ diff --git a/applications/PoP/poc/room.c b/applications/PoP/poc/room.c deleted file mode 100644 index 798728a..0000000 --- a/applications/PoP/poc/room.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * room.c — вся логика комнаты, ОБЫЧНЫЙ HOME-файл (без --bank). - * - * 2026-07-15/16: изначально была банковская (--bank 1), сначала в W3 - * (huge-дефолт), потом пробовали W1 (--memory big) — оба варианта - * упирались в проблемы (W3 конфликтует с gfx_blit_part/atlas_load, - * которые сами трогают W3; W1-банк несовместим с CODE=W1 — трамплин - * сам бы уехал из W1; --memory big тесно — CODE+DATA+BSS ~20КБ, не - * лезет в один 16КБ). После замены sprintf() на ручное hex-форматирование - * пути (экономия ~2.9КБ — sprintf/vsprintf тянули за собой заметный - * кусок форматирующего кода, единственный потребитель в HOME) всё - * снова влезает в W1 обычным HOME-кодом — банк ОКАЗАЛСЯ НЕ НУЖЕН. - * Так проще: gfx_blit_part()/atlas_load() (трогают W3 транзитивно) - * теперь просто обычные HOME-функции, без всяких W1/W3-танцев. - * - * gfx_blit_part пишет ПРЯМО в текущий банк (видео-ОЗУ + теневая копия - * при GFX_BANK_NORMAL) — перманентно, не через sprite_t. Вызывающий - * код обязан позвать room_draw() на КАЖДУЮ из двух страниц дабл-буфера - * при входе в комнату, иначе heal спрайтов увидит фон только на одной - * странице. - */ -#include -#include -#include "room.h" - -/* 32x64 на тайл: 3*64+8=200 — сходится с разрешением DOS 320x200 - * (8px внизу — под полоску жизней/строку текста, не часть игрового - * поля). Уточнить точнее, когда будут готовы настоящие ассеты. */ -uint8_t room_row_h[ROOM_ROWS] = {64, 64, 64}; -tile_atlas_t tile_atlas[32]; - -int tile_atlas_load(const char *dir) -{ - static const char HEXD[] = "0123456789abcdef"; - char path[64]; - int i, count = 0; - - for (i = 0; i < 32; i++) { - /* Вручную, не sprintf("%stile%02x.atl",...) — единственный - * потребитель printf-семейства в этом приложении, тянул за - * собой ~2.9КБ форматирующего кода; формат тут фиксированный - * (2 hex-цифры). */ - char *p = path; - const char *s = dir; - while (*s) *p++ = *s++; - s = "tile"; - while (*s) *p++ = *s++; - *p++ = HEXD[(i >> 4) & 0xF]; - *p++ = HEXD[i & 0xF]; - s = ".atl"; - while (*s) *p++ = *s++; - *p = '\0'; - if (atlas_load(&tile_atlas[i].at, path) == 0) { - tile_atlas[i].loaded = 1; - count++; - } else { - tile_atlas[i].loaded = 0; - } - } - return count; -} - -void tile_atlas_free(void) -{ - int i; - - for (i = 0; i < 32; i++) { - if (tile_atlas[i].loaded) { - atlas_free(&tile_atlas[i].at); - tile_atlas[i].loaded = 0; - } - } -} - -int level_load_room(level_t *lv, uint8_t room, const char *path) -{ - FILE *f = fopen(path, "rb"); - uint8_t buf[ROOM_TILES * 2 + 3]; /* fore[30]+back[30]+start_room/pos/dir */ - int i; - - if (!f) - return -1; - if (fread(buf, 1, sizeof(buf), f) != sizeof(buf)) { - fclose(f); - return -1; - } - fclose(f); - - for (i = 0; i < ROOM_TILES; i++) { - tile_t *t = level_tile(lv, room, (uint8_t)(i % ROOM_COLS), - (uint8_t)(i / ROOM_COLS)); - t->type = buf[i]; - t->state = buf[ROOM_TILES + i]; - } - lv->start_room = buf[ROOM_TILES * 2]; - lv->start_location = buf[ROOM_TILES * 2 + 1]; - lv->start_direction = (int8_t)buf[ROOM_TILES * 2 + 2]; - return 0; -} - -/* _bgi_blit_rows_raw держит ОДИН DI на весь блит (см. её шапку) — - * вызывающий обязан резать блиты выше 16 строк на чанки, иначе окно - * DI слишком длинное (риск сорвать кадровую цепочку IRQ/CBL). Наши - * тайлы 64px высотой — режем на чанки по CHUNK_H строк. */ -#define CHUNK_H 16 - -void room_draw(level_t *lv, uint8_t room) -{ - int row, col, y = 0; - - for (row = 0; row < ROOM_ROWS; row++) { - int x = 0; - uint8_t fh = room_row_h[row]; - - for (col = 0; col < ROOM_COLS; col++) { - tile_t *t = level_tile(lv, room, (uint8_t)col, (uint8_t)row); - uint8_t type = TILE_TYPE(t->type); - - if (tile_atlas[type].loaded) { - uint8_t count = tile_atlas[type].at.count; - uint8_t variant = 0; - uint8_t is_var; - - if (count != 0) { - switch (type) { - case TILE_SPIKES: - case TILE_GATE: - case TILE_TAPESTRY: - case TILE_TAPESTRY_TOP: - case TILE_CHOPPER: - case TILE_POTION: - is_var = 1; - break; - default: - is_var = 0; - } - variant = is_var - ? (uint8_t)(t->state % count) - : (uint8_t)((room * 31u + col * 7u + row * 13u) % count); - } - const void *img = atlas_image(&tile_atlas[type].at, 0); - uint8_t sub_y; - - gfx_w0_map(tile_atlas[type].at.page); - for (sub_y = 0; sub_y < fh; sub_y = (uint8_t)(sub_y + CHUNK_H)) { - uint8_t chunk = (uint8_t)(fh - sub_y); - if (chunk > CHUNK_H) - chunk = CHUNK_H; - gfx_blit_part(x, y + sub_y, img, variant * ROOM_TILE_W, - sub_y, ROOM_TILE_W, chunk); - } - gfx_w0_unmap(); - } - x += ROOM_TILE_W; - } - y += fh; - } -} diff --git a/applications/PoP/poc/room.h b/applications/PoP/poc/room.h deleted file mode 100644 index ee26e91..0000000 --- a/applications/PoP/poc/room.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * room.h — отрисовка комнаты из тайлов В РАНТАЙМЕ, через спрайтовые - * атласы (см. пересмотр PORT_PLAN.md §4, 2026-07-15). - * - * Один атлас на ТИП тайла (0x00..0x1F) — лента из N вариантов - * (кадр fw x fh, fw=ROOM_TILE_W, fh=высота ряда, где тип реально - * стоит). Для типов, где backtable — настоящее состояние - * (gate/tapestry/chomp/spike) — вариант = state; для остальных — - * простой псевдослучайный выбор по позиции (свой приём, не алгоритм - * оригинала — просто чтобы плитка не повторялась один-в-один). - * - * Загрузка "всей лентой — копии одной картинки" на старте, до - * появления настоящих вариантов — нормальная стратегия: движок/API - * не меняются, когда картинки заменятся на настоящие. - * - * Фаза 1 (PORT_PLAN.md §5): анимированные типы (факел/дверь/шипы/ - * дробилка) НЕ рисуются вообще — просто не грузим для них атлас, - * room_draw пропускает клетки без атласа. Анимация через отдельные - * sprite_t поверх фона — Фаза 2, см. §7 плана. - * - * room_draw() зовётся ОДИН РАЗ при входе в комнату, не в игровом - * цикле — бюджет кадра не затронут (gfx_w0_map/unmap на тайл — тоже - * разовая цена, не per-frame). - */ -#ifndef ROOM_H -#define ROOM_H - -#include -#include /* atlas_t, atlas_load/free, atlas_image, gfx_w0_map/unmap */ -#include "level.h" - -#define ROOM_TILE_W 32 /* ширина колонки, px (10 колонок = 320) */ - -/* Высоты рядов, px — измерено по реальным тайлам подземелья - * (PORT_PLAN.md §3: ~20/60/62) — ПОДТВЕРДИТЬ при подготовке настоящих - * ассетов, значения приблизительные. */ -extern uint8_t room_row_h[ROOM_ROWS]; - -typedef struct { - atlas_t at; - uint8_t loaded; /* 0 = атласа нет — тип не рисуется */ -} tile_atlas_t; - -/* Индекс — TILE_TYPE (0..31). */ -extern tile_atlas_t tile_atlas[32]; - -/* Грузит "{dir}tileNN.atl" (hex TILE_TYPE, 2 знака, напр. "tile01.atl" - * — Floor) — какие есть, те и грузятся. dir — префикс ПОДКЛЕИВАЕТСЯ - * как есть (без добавления разделителя) — файлы на диске лежат ПЛОСКО - * (см. соглашение других examples: "name.ext" либо "a:\\name.ext"), так что - * dir обычно "" или "a:\\". Каждый файл — ОДНА лента (idx 0) из N - * кадров fw=ROOM_TILE_W x fh=room_row_h[row] (row, где этот тип - * реально стоит) — готовится toolchain/png_strip.py + - * toolchain/mkatlas.py. Возврат: число загруженных (0..32). - * - * room.c — обычный HOME-файл, БЕЗ --bank (см. шапку room.c за - * историей: банк в W3 конфликтовал с gfx_blit_part/atlas_load, - * банк в W1 невозможен при CODE=W1 — трамплин сам уехал бы из W1; - * после чистки sprintf() всё влезает в HOME без банка вообще). */ -int tile_atlas_load(const char *dir); -void tile_atlas_free(void); - -/* Нарисовать комнату (room 1..24) — только типы с загруженным - * атласом; остальные клетки пропускаются (в т.ч. все анимированные - * типы в Фазе 1 — атлас для них просто не грузится). */ -void room_draw(level_t *lv, uint8_t room); - -#endif diff --git a/applications/PoP/poc/tools/build_room_palette.py b/applications/PoP/poc/tools/build_room_palette.py deleted file mode 100644 index f74a7a8..0000000 --- a/applications/PoP/poc/tools/build_room_palette.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python3 -""" -build_room_palette.py — сводит kid.pal (уже собран -gen_kid_placeholder.py) + Floor + Wall тайлы в ОДНУ merged-палитру -room.pal, грузимую ОДИН раз через gfx_pal_fload (см. poc.c) — экран -показывает Kid и тайлы ОДНОВРЕМЕННО, значит их "свои" цвета обязаны -жить в одной палитре (см. --seed-pal в toolchain/png_strip.py). - -На вход — только типы, которые Фаза 1 реально рисует (room_draw -рисует только загруженные атласы; Floor/Wall — единственные твёрдые -типы с нетривиальной картинкой заглушки, см. gen_tile_placeholders.py -draw_floor_like/draw_wall_like). Остальные 30 типов из Фазы 1 -исключены полностью (не грузятся, не рисуются, PORT_PLAN.md §5) — -добавить сюда, когда понадобятся. - -Перезаписывает res/tiles/tile01.raw/.atl (Floor) и tile14.raw/.atl -(Wall, TILE_WALL=0x14 — room.c ищет "tile%02x.atl" ОБЯЗАТЕЛЬНО в hex, -не decimal!) с индексами палитры, согласованными с kid.pal; пишет -kid+floor+wall.pal рядом с kid.pal (poc.c грузит её вместо отдельной -kid.pal). -""" -import subprocess -import sys -from pathlib import Path -import os - -HERE = Path(__file__).resolve().parent -PROJ_ROOT = Path(os.environ["SPRINTER_ROOT"]).resolve() -TILES_DIR = HERE.parent / "res" / "tiles" -PNG_STRIP = PROJ_ROOT / "toolchain" / "png_strip.py" -MKATLAS = PROJ_ROOT / "toolchain" / "mkatlas.py" - -ROOM_TILE_W = 32 -ROOM_ROW_H = 64 - -KID_PAL = HERE / "kid.pal" -OUT_PAL = HERE / "room.pal" - -TYPES = [0x01, 0x14, 0x03, 0x13, 0x0E, 0x0B] # Floor, Wall, Pillar, Torch, Debris, Loose -# (2026-07-16: временное ограничение до 2 типов снято — корень -# вероятностного "снега" найден: tile_atlas_load()/room_draw() трогали -# W3 транзитивно (через atlas_load/gfx_blit_part) ИЗ БАНКА, который сам -# живёт в W3 — конфликт. Обе функции переехали в room_draw.c (HOME), -# см. её шапку.) - - -def run(cmd): - print("+", " ".join(str(c) for c in cmd)) - subprocess.run([str(c) for c in cmd], check=True) - - -def main(): - if not KID_PAL.exists(): - sys.exit(f"build_room_palette: нет {KID_PAL} — сначала gen_kid_placeholder.py") - - seed = KID_PAL - for t in TYPES: - base = f"tile{t:02x}" # room.c ищет "tile%02x.atl" — hex, не decimal - png = TILES_DIR / f"{t:X}-0.png" - raw = TILES_DIR / f"{base}.raw" - pal = TILES_DIR / f"{base}_merged.pal" - atl = TILES_DIR / f"{base}.atl" - - run([sys.executable, PNG_STRIP, raw, pal, png, - "--canvas", f"{ROOM_TILE_W}x{ROOM_ROW_H}", - "--anchor", "top-left", "--seed-pal", seed]) - run([sys.executable, MKATLAS, atl, f"{raw}:{ROOM_TILE_W}x{ROOM_ROW_H}:1x1"]) - seed = pal - - import shutil - shutil.copyfile(seed, OUT_PAL) - print(f"build_room_palette: итоговая палитра -> {OUT_PAL}") - - -if __name__ == "__main__": - main() diff --git a/applications/PoP/poc/tools/build_tile_atlases.py b/applications/PoP/poc/tools/build_tile_atlases.py deleted file mode 100644 index f085ef8..0000000 --- a/applications/PoP/poc/tools/build_tile_atlases.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python3 -""" -build_tile_atlases.py — собирает tileNN.atl для всех 32 типов тайлов -из applications/PoP/poc/res/tiles/{TYPE:X}-{N}.png (см. -gen_tile_placeholders.py). Для каждого типа находит ВСЕ файлы -{TYPE:X}-*.png (сколько бы вариантов ни было — 1 заглушка или много -настоящих), сортирует по номеру варианта и собирает через -png_strip.py + mkatlas.py. Типы без ни одного файла — пропускаются -(room_draw их и так не рисует). - -Запускать заново каждый раз, когда добавили/заменили картинки в -res/tiles/ — атласы просто пересоберутся. -""" -import re -import subprocess -import sys -from pathlib import Path -import os - -HERE = Path(__file__).resolve().parent -PROJ_ROOT = Path(os.environ["SPRINTER_ROOT"]).resolve() -TILES_DIR = HERE.parent / "res" / "tiles" -OUT_DIR = HERE.parent / "res" / "tiles" -PNG_STRIP = PROJ_ROOT / "toolchain" / "png_strip.py" -MKATLAS = PROJ_ROOT / "toolchain" / "mkatlas.py" - -ROOM_TILE_W = 32 -ROOM_ROW_H = 64 # см. room.c room_row_h — держать в синхроне - -PATTERN = re.compile(r"^([0-9A-Fa-f]+)-(\d+)\.png$") - - -def run(cmd): - print("+", " ".join(str(c) for c in cmd)) - subprocess.run([str(c) for c in cmd], check=True) - - -def main(): - by_type = {} - for f in TILES_DIR.glob("*.png"): - m = PATTERN.match(f.name) - if not m: - continue - t = int(m.group(1), 16) - n = int(m.group(2)) - by_type.setdefault(t, {})[n] = f - - if not by_type: - sys.exit(f"build_tile_atlases: нет файлов TYPE-N.png в {TILES_DIR}") - - built = 0 - for t in sorted(by_type): - variants = by_type[t] - files = [variants[n] for n in sorted(variants)] - strip_raw = OUT_DIR / f"tile{t:02x}.raw" - strip_pal = OUT_DIR / f"tile{t:02x}.pal" - atl = OUT_DIR / f"tile{t:02x}.atl" - - run([sys.executable, PNG_STRIP, strip_raw, strip_pal, - *files, "--canvas", f"{ROOM_TILE_W}x{ROOM_ROW_H}", - "--anchor", "top-left"]) - run([sys.executable, MKATLAS, atl, - f"{strip_raw}:{ROOM_TILE_W}x{ROOM_ROW_H}:{len(files)}x1"]) - built += 1 - - print(f"build_tile_atlases: собрано {built} атласов " - f"(типы: {', '.join('%X' % t for t in sorted(by_type))})") - - -if __name__ == "__main__": - main() diff --git a/applications/PoP/poc/tools/extract_room.py b/applications/PoP/poc/tools/extract_room.py deleted file mode 100644 index 0027782..0000000 --- a/applications/PoP/poc/tools/extract_room.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python3 -""" -extract_room.py — вырезает ОДНУ комнату (foretable+backtable, 30+30 -байт) + стартовую позицию уровня (start_room/start_pos/start_dir, 3 -байта) из level-ресурса формата "res20NN.bin" (SDLPoP data/LEVELS/). - -Layout подтверждён структурным разбором: файл начинается СРАЗУ с -foretable[720] (24 комнаты * 30 тайлов), затем backtable[720] — без -заголовка (applications/PoP/docs/POP-DAT-FormatSpecifications.pdf §3.4 -+ декод байт 2026-07-15). Тайл tileOffset 0-9 = верхний ряд, 10-19 = -средний, 20-29 = нижний (см. level.h level_tile()). - -Смещение start_room/start_pos/start_dir НЕ сразу после links (как -предполагалось изначально) — сверено со структурой level_type в -applications/PoP/SDLPoP/src/types.h (маленький структурный факт, -порядок полей, не объём кода): fg[720]+bg[720]+doorlinks1[256]+ -doorlinks2[256]+roomlinks[96]+used_rooms[1]+roomxs[24]+roomys[24]+ -fill_1[15] = 2112 -> start_room(u8), start_pos(u8), start_dir(s8) -(2026-07-16, подтверждено на res2001.bin: start_room=1, start_pos=0, -start_dir=-1 — согласуется с links[room1] и решулось эмпирически -через таблицу room-links, которая идеально легла на смещение 1952). - -Использование: - extract_room.py LEVEL.bin ROOM OUT.dat - -Выход: OUT.dat — 63 байта (fore[30]+back[30]+start_room+start_pos+ -start_dir), формат level_load_room() (room.c). ROOM — 1-based (как в -файле/level.h); start_* — общеуровневые (не зависят от ROOM). -""" -import sys - -FORETABLE_OFF = 0 -BACKTABLE_OFF = 720 -ROOM_TILES = 30 -START_OFF = 720 + 720 + 256 + 256 + 96 + 1 + 24 + 24 + 15 # 2112 - - -def main(): - if len(sys.argv) != 4: - sys.exit(__doc__.strip()) - src, room_s, out = sys.argv[1], sys.argv[2], sys.argv[3] - room = int(room_s) - - data = open(src, "rb").read() - if len(data) < START_OFF + 3: - sys.exit(f"extract_room: {src}: {len(data)} Б — меньше ожидаемых " - f"{START_OFF + 3} (foretable+backtable+...+start_position)") - - base = (room - 1) * ROOM_TILES - fore = data[FORETABLE_OFF + base:FORETABLE_OFF + base + ROOM_TILES] - back = data[BACKTABLE_OFF + base:BACKTABLE_OFF + base + ROOM_TILES] - start = data[START_OFF:START_OFF + 3] - open(out, "wb").write(fore + back + start) - print(f"extract_room: комната {room} из {src} -> {out} " - f"({len(fore)}+{len(back)}+{len(start)} Б); " - f"start_room={start[0]} start_pos={start[1]} " - f"start_dir={start[2] if start[2] < 128 else start[2] - 256}") - - -if __name__ == "__main__": - main() diff --git a/applications/PoP/poc/tools/gen_kid_placeholder.py b/applications/PoP/poc/tools/gen_kid_placeholder.py deleted file mode 100644 index fb5dca1..0000000 --- a/applications/PoP/poc/tools/gen_kid_placeholder.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python3 -""" -gen_kid_placeholder.py — временная ЗАГЛУШКА персонажа для PoC. - -НЕ используем графику оригинальной Prince of Persia (авторские права -Broderbund/Ubisoft) — PoC проверяет управление/коллизии/движок, не -визуальную точность (applications/PoP/docs/PORT_PLAN.md §5). Вместо -этого берём уже лицензированный спрайт-пак, который в проекте и так -используется (examples/rpgwalk/conv_sprites.py) — -third_party/16x16-RPG-characters, один персонаж (индекс 0) той же -раскладкой: 12 кадров (dir*3+frame, dir 0=вниз/1=влево/2=вправо/3=вверх, -3 кадра маятника на направление). - -Замену на настоящую графику Кида (если/когда появится легально чистый -источник) пользователь делает сам — вне этого скрипта. - -Выход: kid.raw (16×192, 12 кадров) + kid.pal (EGA 16 + цвета спрайта). -""" -from PIL import Image - -SRC = ("../../third_party/16x16-RPG-characters/sprites/" - "old-style/02-bard.png") -CHAR = 0 # какой из 8 персонажей листа берём - -EGA = [ # B, G, R, pad — как в libbgi/bgi256/_bgi_mode_set.c - (0, 0, 0), (168, 0, 0), (0, 168, 0), (168, 168, 0), - (0, 0, 168), (168, 0, 168), (0, 84, 168), (168, 168, 168), - (84, 84, 84), (255, 84, 84), (84, 255, 84), (255, 255, 84), - (84, 84, 255), (255, 84, 255), (84, 255, 255), (255, 255, 255), -] -BASE = 16 - -im = Image.open(SRC).convert("RGBA") -assert im.size == (192, 128), im.size - -colors = {} - - -def px(x, y): - r, g, b, a = im.getpixel((x, y)) - if a < 128: - return 0xFF - key = (r, g, b) - if key not in colors: - assert BASE + len(colors) < 0xFF - colors[key] = BASE + len(colors) - return colors[key] - - -bx, by = (CHAR % 4) * 48, (CHAR // 4) * 64 -data = bytearray() -for d in range(4): - for f in range(3): - for y in range(16): - for x in range(16): - data.append(px(bx + f * 16 + x, by + d * 16 + y)) -open("kid.raw", "wb").write(data) - -pal = bytearray() -for b, g, r in EGA: - pal += bytes((b, g, r, 0)) -for (r, g, b) in colors: - pal += bytes((b, g, r, 0)) -open("kid.pal", "wb").write(pal) - -print(f"gen_kid_placeholder: kid.raw 16x192 (12 кадров), " - f"kid.pal EGA+{len(colors)} цветов ({len(pal)} Б)") diff --git a/applications/PoP/poc/tools/gen_tile_placeholders.py b/applications/PoP/poc/tools/gen_tile_placeholders.py deleted file mode 100644 index 54ac170..0000000 --- a/applications/PoP/poc/tools/gen_tile_placeholders.py +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/env python3 -""" -gen_tile_placeholders.py — ЗАГЛУШКИ для 32 типов тайлов (не графика -оригинала!) — простая процедурная геометрия (кирпичная кладка линиями, -плоская поверхность и т.п.), чтобы визуально читалось "это стена"/ -"это пол", пока не готовы настоящие ассеты (см. room.h/room.c: -tile_atlas[TYPE], вариант 0 — "index"-картинка на тип). - -Выход: applications/PoP/poc/res/tiles/{TYPE:X}-0.png — 32 файла, -32x64 (ROOM_TILE_W x room_row_h), RGBA. - -Для твёрдых типов (Фаза 1: Floor/Wall/Pillar/PillarBot/PillarTop/ -BalconyL/R) — функциональная заглушка (см. draw_wall_like/draw_floor -ниже); Lattice-типы (0x19-0x1D) — узкая проходимая колонна той же -кладки (draw_lattice_like, см. reference-скриншот оригинала); для -остальных (декоративные/интерактивные, вне объёма Фазы 1) — просто -цвет + подпись, этого достаточно для отладки конвейера. Empty (0x00) -— полностью прозрачно (ничего не рисуется). - -По мере готовности настоящих ассетов — рядом кладутся 0-1.png, -0-2.png и т.д. (следующие варианты той же ленты), см. room.h. -""" -import colorsys -from pathlib import Path -from PIL import Image, ImageDraw, ImageFont - -W, H = 32, 64 - -NAMES = [ - "Empty", "Floor", "Spikes", "Pillar", "Gate", "StuckBtn", "DropBtn", - "Tapestry", "PillarBot", "PillarTop", "Potion", "Loose", "TapestryTop", - "Mirror", "Debris", "RaiseBtn", "ExitL", "ExitR", "Chopper", "Torch", - "Wall", "Skeleton", "Sword", "BalconyL", "BalconyR", "LatticePillar", - "LatticeSupport", "LatticeSmall", "LatticeL", "LatticeR", "TorchDebris", - "Null", -] -assert len(NAMES) == 32 - -WALL_LIKE = {0x03, 0x08, 0x09, 0x14, 0x17, 0x18} # Pillar/PillarBot/Top/Wall/BalconyL/R -FLOOR_LIKE = {0x01} # Floor -# Lattice — узкие декоративные каменные колонны (см. reference-скриншот -# оригинала 2026-07-16): Кид физически проходит МИМО них (не полноценная -# стена на всю ширину тайла) — колонна той же STONE/MORTAR кладки, но -# ýже тайла, с прозрачными полями по бокам. (col_w, align): -# align: "center"/"left"/"right". -LATTICE_LIKE = { - 0x19: (14, "center"), # LatticePillar - 0x1A: (18, "center"), # LatticeSupport — потолще - 0x1B: (10, "center"), # LatticeSmall — потоньше - 0x1C: (14, "left"), # LatticeLeft - 0x1D: (14, "right"), # LatticeRight -} -# Плоский однотонный цвет БЕЗ текста (не через антиалиасинг шрифта) — -# держит "своих" цветов в палитре по 1 на тип, иначе объединённая -# палитра (--seed-pal) быстро упирается в лимит 0xFF (см. -# pop_tile_atlas_palette_merge в памяти). -FLAT_LIKE = {0x13: (200, 90, 30, 255), # Torch — тёплый оранжевый - 0x0E: (110, 90, 70, 255), # Debris — серо-бурый - 0x0B: (140, 120, 60, 255)} # Loose — тускло-жёлтый (плита) - -STONE = (150, 150, 158, 255) -MORTAR = (90, 90, 98, 255) -GROUND = (120, 100, 80, 255) -GROUND_TOP = (170, 150, 120, 255) - -out_dir = Path(__file__).resolve().parent.parent / "res" / "tiles" -out_dir.mkdir(parents=True, exist_ok=True) - -try: - font = ImageFont.load_default() -except Exception: - font = None - - -def draw_wall_like(im): - """Кирпичная кладка — просто ряды прямоугольников со смещением - в шахматном порядке и линиями раствора между ними (обобщённый - геометрический приём, не стиль конкретной игры).""" - draw = ImageDraw.Draw(im) - draw.rectangle([0, 0, W - 1, H - 1], fill=STONE) - brick_h = 12 - brick_w = 16 - row = 0 - y = 0 - while y < H: - offset = (brick_w // 2) if (row % 2) else 0 - x = -brick_w + offset - while x < W: - draw.rectangle([x, y, x + brick_w - 2, y + brick_h - 2], - outline=MORTAR, width=1) - x += brick_w - y += brick_h - row += 1 - - -def draw_lattice_like(im, col_w, align): - """Узкая колонна кирпичной кладки шириной col_w {out_dir}") diff --git a/applications/PoP/poc/tools/kid.atl b/applications/PoP/poc/tools/kid.atl deleted file mode 100644 index 6eff625..0000000 Binary files a/applications/PoP/poc/tools/kid.atl and /dev/null differ diff --git a/applications/PoP/poc/tools/kid.pal b/applications/PoP/poc/tools/kid.pal deleted file mode 100644 index 0eeb432..0000000 Binary files a/applications/PoP/poc/tools/kid.pal and /dev/null differ diff --git a/applications/PoP/poc/tools/kid.raw b/applications/PoP/poc/tools/kid.raw deleted file mode 100644 index 7c51c15..0000000 --- a/applications/PoP/poc/tools/kid.raw +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/applications/PoP/poc/tools/room.pal b/applications/PoP/poc/tools/room.pal deleted file mode 100644 index b965cf3..0000000 Binary files a/applications/PoP/poc/tools/room.pal and /dev/null differ diff --git a/applications/PoP/roomtest/BUGS_CLOSED.md b/applications/PoP/roomtest/BUGS_CLOSED.md deleted file mode 100644 index 449c613..0000000 --- a/applications/PoP/roomtest/BUGS_CLOSED.md +++ /dev/null @@ -1,3744 +0,0 @@ -# roomtest — архив закрытых багов - -Сюда переезжает всё, что **закрыто**: подтверждённые фиксы, снятые -диагнозы, осознанные решения «не делать». Открытые баги — в -[`BUGS_OPEN.md`](BUGS_OPEN.md), текущие задачи — в -[`TASKS_OPEN.md`](TASKS_OPEN.md), закрытые задачи с протоколами — в -[`TASKS_CLOSED.md`](TASKS_CLOSED.md). - -Файл существует не ради истории как таковой: половина записей ниже — это -разбор КОРНЯ (odd-pixel арифметика `char_x`, подстановка тайла нажатой -кнопки, баг кодогенератора SDCC), и он экономит часы, когда похожий симптом -всплывёт снова. Прежде чем заводить новый баг — грепни здесь по симптому. - ---- - - -## CUTSCENE-SKIP-HELD-KEY. Катсцены схлопывались в один кадр (и финал — в чёрный экран) — **ЗАКРЫТ 2026-08-25** - -**Наблюдение (пользователь, 2026-08-24/25).** Сначала: после 14-го уровня -вместо финала чёрный экран. Затем, после первого фикса: во ВСЕХ сценах -(8, 9, 12 и финал) показывается один кадр — и сразу следующий уровень. - -**Корень.** Все сцены проверяют «не просят ли пропустить» через -`kbd_raw_any_down()` на КАЖДОЙ итерации, начиная с первой. А клавиша, -которой закончили уровень, в этот момент ещё зажата: в финальную комнату Кид -ВБЕГАЕТ (стрелка удерживается), дверь уровня проходят на ходу, чит Shift+L -тоже удерживается. Удержание засчитывалось как запрос skip. - -Первый фикс (ждать отпускания перед сценой) проблему только сдвинул: ждать -безусловно нельзя — потерянный break-код вешает игру намертво -([KBD-STUCK-WAIT](BUGS_OPEN.md#kbd-stuck-wait)), — а с таймаутом в секунду -сцена всё равно стартовала при зажатой клавише и гасла на первом же кадре. - -**Правка.** Детектор ФРОНТА (`intro_skip_begin`/`intro_skip_requested`): -пока со старта сцены хоть что-то зажато, skip не взводится; как только всё -отпущено — следующее нажатие сцену прерывает. Применён во всех четырёх -местах: `intro_run`, `pre_princess_animated`, `intro_pv_animated`, `cut_run`. -Ожидание отпускания убрано за ненадобностью. Залипший бит в худшем случае -делает сцену непропускаемой — она доиграет сама, игра не встанет. - -**Проверка.** MAME: переход 7 -> 8 читом (клавиша удерживается) — сцена 8 -отыгрывает целиком, видно сидящую принцессу и мышь; финал (уровень 14, -комната 5) — принцесса поворачивается и обнимает Кида, затем мышь, затем -текст HAIL. - ---- - - -## GATE-SAFE-STEP-SHORT. Перед ОТКРЫТОЙ решёткой осторожный шаг вдвое короче — **ЗАКРЫТ 2026-08-24** - -**Наблюдение (пользователь, 2026-08-24).** Идя слева направо к поднятой -решётке, Кид делает лишний мелкий шаг: чтобы подойти к краю, Shift+вперёд -надо нажать дважды. - -**Корень.** Длину осторожного шага выбирает сам `safe_step` (seg005:0604) по -числу из `get_edge_distance`: последовательности 29..42 — это «шаг ровно на -distance пикселей». А `dist_from_wall_forward` у оригинала ПЕРВОЙ строкой -отвечает −1, если тайл — ворота, сквозь которые можно пройти (seg004:0A18: -`if (tiletype == tiles_4_gate && !can_bump_into_gate()) return -1`); минус -единица уводит разбор дальше, где открытые ворота опознаются обычным полом и -дают `distance = 11`. В нашем порту этой строки не было: расстояние честно -считалось до створки (у ворот `wall_type = 1`, «стена справа»), край -классифицировался как `EDGE_WALL`, и шаг выходил коротким. - -**Правка.** Ветка возвращена в `dist_from_wall_forward` (pop_map.c), -проходимость берётся у нашего `gate_passable` (это и есть -`!can_bump_into_gate`). - -**Проверка.** Хост-набор `tests-host/t_gate.c` (новый): на одном и том же X -открытая решётка обязана давать то же, что чистый пол, а закрытая — остаться -`EDGE_WALL`. Со снятым фиксом набор падает ровно на симптоме: `d = 9`, -`EDGE_WALL` вместо `11`, `EDGE_FLOOR`. - ---- - - -## GATE-SUPERPOSITION. Кид остаётся в тайле опустившейся решётки — **ЗАКРЫТ 2026-08-24** - -**Наблюдение (пользователь, 2026-08-24).** Кид встал в тайл решётки, пока та -была поднята; решётка опустилась — Кид остался внутри. Дальше он «в -суперпозиции»: лицом влево рисуется ЗА решёткой, лицом вправо — ПЕРЕД ней, и -уйти может в обе стороны. То же самое — при попытке проползти под -опускающейся решёткой в присяде. - -**Корень.** В оригинале выталкиванием занимается ОТДЕЛЬНАЯ функция кадровой -цепочки — `check_gate_push` (seg004:0833, зовётся из play_kid_frame, -seg000:1241, между `check_bumped` и `check_action`). В нашем `kid_phys` её -не было вовсе: `check_bumped` ловит только движение В препятствие, а -«препятствие появилось вокруг стоящего» — случай именно `check_gate_push`. - -**Правка.** Порт добавлен в pop_map.c и вставлен в `kid_phys` на своё место. -Условия оригинала сохранены: срабатывает только в позах «стоит на месте» — -разворот (action 7), стойка (кадр 15), присед (кадры 108..110); решётка -ищется в своём тайле И в тайле слева (створка нарисована у правой грани); -перекрытие обязано быть обеими гранями и в этом кадре, и в прошлом; толчок — -5 пикселей влево (решётка своя) либо вправо (решётка левого соседа) + -`bumped_sound`. - ---- - - -## GUARD-DROPPEDOUT-CARRY. Страж идёт в челюсти: `droppedout` пережил границу уровня — **ЗАКРЫТ 2026-08-24** - -**Наблюдение (пользователь, 2026-08-24).** Уровень 4, комната 22: страж без -всякого повода идёт навстречу Киду и гибнет в челюстях; если Кид спустился -рядом ниже и челюсти остановились — страж проходит сквозь них и сваливается к -Киду. Воспроизводилось несколько раз, но ТОЛЬКО когда на 4-й уровень -приходили ногами, через дверь 3-го; при отладочном старте прямо на 4-м -(`make LEVEL=4 ROOM=22`) сцена вела себя правильно — страж стоял, боя не было. - -**Корень.** Единственная ветка, которая двигает стража ВПЕРЁД при -`can_guard_see_kid == 0`, — `guard_follows_kid_down()` (seg002:0806), и гейтит -её флаг `droppedout`: «Кида спихнули из боя вниз — идти ли следом». Флаг -взводит `start_fall` (seg006:1123, у нас pop_map.c), когда персонаж срывается -с уступа В БОЕВОЙ ПОЗЕ (кадры 150..179) — то есть обычная концовка боя со -скелетом на 3-м уровне. В оригинале его гасит `set_start_pos()` -(seg003:028A) при старте КАЖДОГО уровня; в нашем порту (pop_start_level, -roomtest_cold.c) этого сброса не было, и флаг переезжал на следующий уровень. - -Дальше всё сходится: `can_guard_see_kid` считался ПРАВИЛЬНО (в живой сцене -прочитано `1` — чомпер в луче даёт «вижу, но не пойду»), но активная ветка -при 0 уходила не в «попятиться» (`move_down_back`), а в «идти за упавшим -Кидом», а она смотрит только на пол впереди и внизу — чомпер ей не помеха. - -**Правка.** В порт `set_start_pos` возвращены сбросы оригинала: -`pop_droppedout = 0`, `offguard = 0`, `knock = 0` (fall_x/fall_y уже обнуляет -`kid_init`). - -**Проверка.** MAME, сборка `make LEVEL=4 ROOM=22 POS=0`: флаг взведён -отладчиком (`pop_droppedout = 1`), затем меню -> RESTART LEVEL -> флаг снова -0, страж стоит. Само старое поведение вживую НЕ переигрывалось: причина -доказана кодом (в оригинале сброс есть, у нас его не было) и тем, что -единственный путь стража вперёд при `can_guard_see_kid == 0` гейтится этим -флагом. - -**Урок.** Меж-уровневые флаги проверять поимённо по `set_start_pos`: -симптом вылезает НЕ там, где флаг взводится, и не воспроизводится отладочным -стартом с нужного уровня — именно потому, что тот стартует с чистым -состоянием. - ---- - - -## PAL-SKEL-GUARD. Скелет (уровень 3) розовый — палитру набора затирал kid.pal — **ЗАКРЫТ 2026-08-24** - -**Наблюдение (пользователь, 2026-08-24).** Уровень 3, комната 1: поднявшийся -скелет нарисован розово-телесным вместо костяного белого. Кид, фон и факелы — -верные. - -**Корень.** Все пиксели спрайтов скелета — ОДИН индекс 3 его набора -(`SKEL/res751..res778.png`, проверено гистограммой), то есть слот `0x93`. -Палитру набора `pop_guard_load()` заливал ОДИН раз, сразу после атласов, а -дальше по маршруту загрузки уровня шёл `pop_pal_level_load(1)` → -`pop_pal_game_load()` → `gfx_pal_fload("KID\\kid.pal")`. `kid.pal` содержит -ВСЕ 256 записей, включая диапазон стража `0x90..0x9F` с запечённым цветом 2 -(`pop_pack_guard.py`, `COLOR = 2`), — и он затирал палитру скелета. Слот -`0x93` цвета 2 = `(FF,79,79)`, ровно тот розовый, что был на экране. Тот же -механизм ждал Джафара на уровне 13 (`tbl_guard_type == 3`, палитра тоже из -файла набора). - -Обычный страж симптома не давал только по случайности: у него цвет -переставляет `pop_guard_set_palette()` при входе в комнату (enter_guard), то -есть ПОСЛЕ file-load. У скелета и визиря `curr_guard_color == 0` (seg002:186) -— пере-установки нет вовсе. - -**Правка.** `0x90..0x9F` — такой же ДИНАМИЧЕСКИЙ диапазон, как тайлсет и Тень, -и обязан доплачиваться после каждого file-load логической палитры. Добавлен -`pop_guard_pal_apply()` (pop_cdraw.c, банк 4) — пара к `pop_bg_pal_apply` / -`pop_shadow_pal_apply`: набор со своим цветом (скелет/Джафар) берёт палитру из -своей таблицы, обычный страж — последний `curr_guard_color` -(`guard_pal_cur`, его пишет `pop_guard_set_palette`). Зовётся из -`pop_pal_game_load()` и `pop_pal_level_load(0)` ДО snapshot, чтобы правильный -цвет попал и в снимок для fade. `pop_guard_load()` больше не грузит палитру -сам, а зовёт ту же функцию. - -**Проверка.** MAME, сборка `make LEVEL=3 ROOM=1 POS=12`, `pop_leveldoor_open` -взведён отладчиком, чтобы скелет поднялся сразу. Скелет белый; цвет пикселей -на скриншоте `(255,255,242)` — ровно запись 3 `pop_skel_pal`. - ---- - - -## GRAB-BELOW-ROOM. Зацеп за кромку НИЖНЕГО ряда при пролёте вниз не работал вовсе - -**Наблюдение (пользователь, 2026-08-12).** Уровень 7, комната 14: по сценарию -уровня Кид спускается с ряда 0 на ряд 2 через зацеп — виснет на кромке кнопки -`(0,2)`, отпускает и в падении цепляется за кромку `(2,2)`. «Пытался много -раз — Кид летит и не цепляется ни за `(2,2)` комнаты 14, ни за `(1,2)` комнаты -15». При этом вис на `(2,2)` с последующим перелётом на `(1,2)` **работает -стабильно** — то есть сам зацеп в падении жив. - -**Корень.** `do_fall` (порт seg005:0030) не давал ряду персонажа выйти за -нижнюю границу комнаты: - -```c -else if (Char.curr_row < 2) - inc_curr_row(); /* «следующий ряд ВНУТРИ комнаты» */ -``` - -В оригинале `inc_curr_row()` безусловный, и `curr_row` доходит до **3** — ряда -«за нижней кромкой»; `get_tile` для него уходит по `links.down` в комнату снизу -(`find_room_of_tile`, seg006:005D). Ряд 3 нужен не сам по себе, а потому, что -`check_grab` целится в тайл **спереди-сверху**, то есть в ряд `curr_row − 1`: -только при `curr_row == 3` целью становится ряд 2 своей комнаты. - -С залипшим рядом 2 происходило вот что: пока Кид летел вдоль нижнего ряда, -`do_fall` каждый кадр уходил в ветку «достиг `y_land`», где `check_grab` не -вызывается вовсе. К моменту, когда переход в комнату снизу открывал окно -зацепа заново (там цель — «ряд −1», уже реализованный `g_above`), скорость -падения успевала дойти до `fall_y = 33`, а `check_grab` требует `< 32`. Окно -закрывалось **по скорости** — отсюда «летит и не цепляется никак». - -Второй дефект, найденный там же: ветка `action == ACT_MIDAIR` в `check_action` -стояла ПУСТОЙ заглушкой с комментарием «frames 102..105: check_grab — K4». В -оригинале (seg006:0619) это первые четыре кадра падения, где `fall_y` ещё не -разгоняется (`fall_accel` работает только в `ACT_FREEFALL`), — самое широкое -окно зацепа. У нас его не было. - -**Фикс** (`pop_map.c`): `inc_curr_row()` теперь безусловный внутри ветки -`curr_row <= 2` (то есть 2 → 3 разрешён), а сама ветка «достиг `y_land`» -гейтится по `curr_row <= 2` — при ряде 3 ни `in_wall`, ни `land` звать нельзя -(тайлов своей комнаты там нет, `get_tile` отдаёт `WALL`-сентинел; до -`y_land[4] = 244` дело не доходит, `check_leave_below` переводит в комнату -снизу на `y >= 211`). Плюс восстановлена ветка `ACT_MIDAIR` с кадрами 102..105. - -**Подтверждение.** -- Хост-тест `tests-host/t_grab.c` → `grab_below_room_edge_window_exists`: - сцена комнаты 14 + переход в 15 по `pop_fell_out` (как в главном цикле). - Карта исходов по фазе X и задержке Shift **до** фикса — сплошные точки (ни - одного зацепа), **после** — окно из 8 фаз X при задержках 0..5 кадров. - Тест проверяет и играбельность: зацеп обязан удаваться при самом - естественном вводе (отпустил вис и сразу зажал Shift). -- Живьём в MAME (мост, 2026-08-12): Кид повис на кромке `(2,2)` — в памяти - `frame=91 y=55 row=0 room=15 act=6`, на экране держится за правый край плиты. - -**Грабли теста, стоившие часа.** Первая версия сцены засчитывала как успех -ЛЮБОЙ вис — а Кид на первых кадрах падения цепляется обратно за ту же верхнюю -кромку, и тест проходил даже на сломанном коде. Признак цели пришлось делать -позиционным (`went_below` + `curr_row == 0` после перехода). Вторая ловушка: -кадры спуска `seq_68` идут с `action == 3`, то есть «дождаться падения» по -одному лишь `action` нельзя — ждать надо ВИСА, и только потом отпускать Shift. - ---- - - -## BUG-CHEAT-FIGHT-1. Чит `+`/`−` в бою: Кид остаётся в режиме боя и теряет управление - -**Наблюдение (пользователь, 2026-08-07).** Если нажать `+`/`−` (ROOMNAV, -переход по комнатам) в момент, когда Кид вытащил меч для битвы, — в новой -комнате Кид не управляется: нажатия стрелок игнорируются. - -**Корень (прочитан по коду, сверен с seg005).** Чит ROOMNAV -(`roomtest.c:576`) телепортирует и сбрасывает позу и HP — -`enter_room()`, `kid_init(SEQ_STAND, …)`, `pop_kid_hp_reset()`, — но **не -трогает состояние боя**: `Kid.sword` остаётся `SWORD_2_DRAWN`. Диспетчер -`pop_control` (`pop_ctrl.c:578`) при вынутом мече уходит в -`control_with_sword`, а там единственный выход из режима боя — - -```c -if (Char.frame == FRAME_171_STAND_WITH_SWORD) { /* seg005:987 */ - Char.sword = SWORD_0_SHEATHED; - seqtbl_offset_char(SEQ_92_PUT_SWORD_AWAY); -} -``` - -Стража в новой комнате нет (`can_guard_see_kid` = 0), кадр после -`kid_init(SEQ_STAND)` — обычная стойка, а не 171, поэтому не срабатывает ни -`swordfight()`, ни ветка «убрать меч»: `control_with_sword` каждый кадр -не делает НИЧЕГО, и ввод не доходит до движения. Заклинивание вечное. - -Это баг **нашего чита**, а не порта: в оригинале телепорта между комнатами -нет, и в режим боя без соперника попасть нечем. - -**Как чинить.** В ветке ROOMNAV телепорт трактовать как выход из боя (то же -самое, что делает `pop_start_level`): `Kid.sword = SWORD_0_SHEATHED`, -`holding_sword = 0`, сбросить `offguard`/`guard_refrac` и состояние стража -(`pop_guard_reset()` вызывается по входу в комнату — проверить, что он -обнуляет `Opp`). Меч в инвентаре (`pop_have_sword`) при этом НЕ терять. -Второй кандидат на ту же болезнь — чит `K` (убить стража) в момент, когда -Кид в стойке с мечом, но не в кадре 171: там оригинал сам доводит до 171 -через `swordfight`, так что проверить сценарием, а не менять вслепую. - -**ЗАКРЫТ 2026-08-10.** Сделано ровно по плану выше: ветка ROOMNAV после -`kid_init`/`pop_kid_hp_reset` гасит состояние схватки — -`Kid.sword = SWORD_0_SHEATHED`, `holding_sword = 0`, `offguard = 0`, -`guard_refrac = 0`. Меч в инвентаре (`pop_have_sword`) не теряется, только -режим боя. - - - -## BUG-LOOSE-BUTTON-1. Упавшая плита не нажимает кнопку — **ЗАКРЫТ 2026-08-10** - -**Симптом (пользователь, уровень 4).** Плита `16(1,1)` падает вниз, в комнате -17 ничего не меняется: кнопка цела, щебня нет, ворота не открылись. - -**Корень — три слоя.** - -1. Порт `loose_land` (seg007:11E8) у нас вообще не звал `trigger_button`: и в - своей комнате (`pop_map.c`), и в комнате снизу (`roomtest.c`) он только - клал щебень. -2. `pop_room_col_landing` (pop_level.c) считала площадкой **только чистый - пол** (`t == 1`). У оригинала площадок семь — `tiles_1_floor`, - `tiles_2_spike`, `tiles_6_closer`, `tiles_10_potion`, `tiles_15_opener`, - `tiles_19_torch`, `tiles_30_torch_with_debris`; на всём прочем кусок просто - исчезает. Кнопка в набор не входила, поэтому плита растворялась. -3. Сигнал `pop_loose_fell` взводится в момент ОТРЫВА плиты, а по нему главный - цикл и щебень клал, и кнопку жал — ворота начинали открываться, пока плита - ещё в воздухе. - -**Механика оригинала (важно, интуиция обманывает).** Плита не «постоянно -давит» на кнопку. Нажатие ОДНО, но с `button_type = tiles_14_debris`, а это у -`trigger_gate` отдельная ветка: возврат 2 → `animate_door` доводит створку и -ставит `curr_modifier = 0xFF` → ворота заморожены открытыми навсегда, обычный -opener их больше не трогает (`if (modifier == 0xFF) return -1`). Сама кнопка -съедается: тайл становится щебнем. На closer — быстрое закрытие и тоже -щебень. На факеле остаётся отдельный тайл `tiles_30_torch_with_debris`. - -**Фикс.** Набор площадок расширен до оригинального; `pop_trigger_button` -зовётся в обоих местах с правильным `button_type`; посадка в комнате снизу -переехала с `pop_loose_fell` на новый сигнал `pop_loose_exit`, который -`mob_tick_one` взводит, когда кусок ушёл ниже поля комнаты. - -**Осознанное расхождение.** Оригинал через `mob_down_a_row` продолжает -симуляцию куска уже в нижней комнате; у нас он там не летит, а садится сразу. -Задержка получается «высота комнаты», а не «высота комнаты плюс путь до пола -внизу». - ---- - - -## BUG-GATE-FF-1. Ворота, открытые навсегда, нарисованы открытыми, но непроходимы — **ЗАКРЫТ 2026-08-10** - -**Симптом.** После BUG-LOOSE-BUTTON-1 ворота `23(0,9)` рисуются поднятыми, но -Кид в них упирается. - -**Корень — перегруженное значение.** `gate_modif` (pop_map.c) отдаёт `0xFF` -как сторожевое «тайла нет за краем уровня», а `trigger_gate` ставит тот же -`0xFF` как живой модификатор «открыто НАСОВСЕМ». В `gate_passable` стояло -`if (gm == 0xFF) return 0;`. До сегодняшнего дня значение в игре не -возникало — ветку по щебню никто не запускал. - -**Фикс.** Ветка убрана: она мёртвая (нас зовут только когда тайл ТОЧНО -ворота), а `(0xFF >> 2) + 6 = 69` и так больше любого `fph`. Остальные -читатели `gate_modif` трактуют 63 как «открыто» и правок не потребовали. - ---- - - -## BUG-TORCH-CHOMP-1. Чомпер стирает пламя соседнего факела — **ЗАКРЫТ 2026-08-10** - -**Симптом (пользователь).** Комната 23 уровня 4: факел есть, огня нет. - -**Корень.** Пламя рисуется в ячейке ПРАВОГО соседа (`x = COL_XH[col+1]*8 + 8` -= 232..248, `y` 33..50), то есть в тайле чомпера (0,7). `pop_chomp_redraw` -начинается с `pop_heal_off(224, 2, 32, 64)` — восстанавливает запечённый фон в -`x 224..255, y 30..93`. Пламя в фон не запекалось и рисовалось в -`GFX_BANK_SPRITE`, поэтому heal его съедал; вдобавок `pop_redraw_needed` идёт -ПОСЛЕ `pop_process_trobs`, так что стиралось только что нарисованное. - -**Тупик, в который я сначала свернул.** Вынес отрисовку пламени в отдельный -проход после `pop_redraw_needed` — стало хуже: пламя легло ПОВЕРХ челюстей -(правильный z-порядок существует только внутри общего обхода тайлов, где -`draw_tile_anim_right` идёт до `draw_tile_anim`), плюс проход индексировал -`trob_code[i]` параллельно `trobs[i]`, а `pop_process_trobs` в конце уплотняет -массив — индексы разъезжались, и чужой trob рисовался как факел (пламя из -головы Кида). Проход откачен. - -**Фикс (подсказан пользователем).** Пламя ЗАПЕКАЕТСЯ: `pop_torch_draw` пишет -в `GFX_BANK_NORMAL` (видео-ОЗУ + ОЗУ-копия). Это безопасно именно у факела — -все девять кадров лежат на общем канвасе 16×18 и упакованы `opaque=True`, то -есть кадр полностью накрывает предыдущий, протухнуть в копии нечему; и факел -всегда задний план. Тогда heal чомпера возвращает огонь сам, а челюсти -ложатся поверх — тот же z-порядок, что в оригинале, и без лишнего прохода. - -Пузырёк ЗЕЛЬЯ так нельзя: он ползёт вверх, себя не накрывает и требует heal — -`pop_potion_draw` остаётся в `GFX_BANK_SPRITE`. Столкнуться с чомпером в -одном тайле зелье не может. - - - -## BUG-GUARD-IX-1. «Зависание» при бое со стражем — затёртый IX главного цикла — **ЗАКРЫТ 2026-08-10** - -**Симптом (пользователь, уровень 4).** Дважды подряд при ручном входе в -комнату 18 со стражем игра «повисала»: картинка стоит, управление не -отвечает. Нажатие «2» ненадолго возвращало движение, потом всё повторялось. - -**Диагноз — НЕ зависание.** Главный цикл всё это время крутился (маркер -верха цикла тикал раз в кадр). Стоял отладочный стоп-кадр: локаль `frozen` -в `main` сама собой становилась ненулевой. - -**Корень — однобайтовый выход за границу локального массива.** У `main` все -локали лежат по `IX-1..IX-19`, и IX затирался: вместо `0xBFFA` в нём -оказывался `0xBF00`. Признак железный — в теле цикла обязано выполняться -`SP == IX-19`, а по факту было `SP=0xBFE7, IX=0xBF00`. Дальше `main` читал -`frozen`, `dbuf`, `front/back`, `dead_frames` и edge-флаги читов из живого -стекового мусора. - -Виновник — `check_chomped_guard` (pop_map.c): - -```c -uint8_t flags[COLL_N]; /* 14 байт в кадре функции */ -calc_coll_window(); /* окно СТРАЖА -> win_lo/win_hi */ -get_row_collision_data(Char.curr_row, flags); -``` - -`coll_row()` пишет по `flags + scan_off`, а число байт берёт из -`win_lo/win_hi`. `scan_off`/`scan_left0` выставляет `coll_scan_prepare()`, -которую звал ТОЛЬКО `check_collisions` — путь Кида. То есть ряд стража -писался **по смещению Кида, длиной стража**. Диапазон записи -`[kid_win_lo+2 … kid_win_lo+2 + ширина_окна_стража − 1]`; при обычной ширине -4 он уезжает за `flags[13]`, когда `kid_win_lo > 8` — то есть **когда Кид -стоит у правого края комнаты** (взаимное расположение Кида и стража ни при -чём, стража даёт только длину). Сразу за массивом лежит сохранённый IX; -затёртый младший байт и превращал `0xBFFA` в `0xBF00`. - -Перелёт в отрицательные индексы был невозможен: `scan_off = kid_win_lo + 2`, -а `kid_win_lo >= COLL_C0 = −2` из-за клампа в `calc_coll_window`. - -**Почему не ловилось раньше.** На уровне 1 (комната 3) бой идёт левее -середины — запись оставалась внутри массива. Ничего не портилось, но флаги -стража всё равно читались по чужим смещениям и с иксами Кида: тихо неверные -данные, без последствий (чомперов там нет). - -**Фикс.** `coll_scan_prepare()` в начале `get_row_collision_data()` — теперь -запись всегда ложится в `[win_lo+2 … win_hi+2]`, а `calc_coll_window` клампит -окно в `[−2 … 11]`, то есть индексы гарантированно `0…13`. Заодно чинится -сам расчёт: `scan_left0` задаёт x колонок, и чомпер-коллизия стража считалась -по координатам Кида. - -**Как ловилось (приём на будущее).** Детекторы `ix != 0xBFFA` на границах -фаз `PROF()` с починкой IX в верху цикла: игра остаётся живой, а первый -сработавший детектор называет фазу. Дальше — инструкционная трасса MAME -(`trace file,0`), включаемая на подозрительном вызове и выключаемая сразу -после, со стопом только на плохом кадре: в файле остаётся ровно тот вызов, -где IX испортился. В хвосте трассы видно `ld sp,ix / pop ix`, достающий -`BF00` вместо `BFFA`. См. memory `z80_profiling_method`. - -**Проверка.** Бой со стражем в комнате 18 уровня 4 (пользователь) — -детекторы IX висели без починки и не сработали ни разу; `make -C tests-host` -— все 5 наборов. - ---- - - -## BUG-GATE-SEAM-ROW1. Решётка в шве не анимируется, если ворота НЕ в ряду 0 — **ЗАКРЫТ 2026-08-10** - -**Симптом (пользователь, уровень 4, стартовая комната).** Плита нажимается, -ворота физически открываются (Кид проходит), но визуально с решёткой ничего -не происходит. Отдельно: чёрный треугольник над воротами (верх решётки под -ковром) статичен, тогда как в оригинале он ездит вместе с барами. - -**Корень.** Зонд на `pop_add_trob` показал `room=8 tp=19 type=1`: плита -комнаты 1 открывает ворота **комнаты 8 в (1,9)** — соседней комнаты, видимые -через ЛЕВЫЙ шов (бары ворот в col9 рисует col0 соседа справа). Наш -change-driven редрой шва смотрел только один байт `m[9]` (ряд 0), а -`pop_room_redraw_seam_left()` перерисовывал жёстко `draw_tile(0, 0)`. -Изменение приходило в `m[19]` — сигнатура его не видела. - -На уровне 1 та же связка комнат 6/8 работала только потому, что решётка -соседа стояла в **(0,9)**. Тайлсет ни при чём: в подземелье просто не -попадалось ворот в ряду 1 у шва (и треугольника не было — над решётками -всегда потолок). - -**Фикс.** -1. `seam_sig` — массив на три ряда, сравниваются все три; накопленная маска - изменившихся рядов живёт в `seam_rows` и переживает оба кадра - дабл-буфера. -2. `pop_room_redraw_seam_left(uint8_t rows)` берёт маску и перерисовывает - только помеченные ряды — не все три, чтобы не платить каждый кадр - анимации. -3. В маску добавляется ряд ВЫШЕ (`changed | (changed >> 1)`): верх решётки - (`draw_tile_anim_topright`, seg008:0568 — маска 68 + кадр - `DOOR_FRAM_TOP`) рисует не сам тайл ворот, а тайл над-справа от него, у - нас `(r−1, 0)`. Без этого бары ездили, а треугольник над ними стоял. - -**Проверка.** Уровень 4, стартовая комната: нажатие плиты — решётка шва -поднимается/опускается вместе с верхом (пользователь). - - - -## BUG-GUARD-COLOR-1. Страж и его полоса HP — всегда одного цвета — **ЗАКРЫТ 2026-08-07** - -**Симптом (пользователь, прогон уровня 2).** Комната 4: страж не того цвета, -что в SDLPoP, и полоса его HP тоже. - -**Корень.** Оригинал держит ОДИН набор спрайтов стража и подменяет 16 цветов -палитры: `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), а `guard_palettes` — ресурс 10 из `PRINCE.DAT` (7 палитр × 16 -цветов, 6-битные каналы). У нас `pop_pack_guard.py` брал `COLOR = 2` -константой (цвет стражей уровня 1) и запекал одну палитру в слоты -`0x90..0x9F`. Полоса HP рисуется тем же атласом — отдельного бага не было. - -**Фикс.** -1. `pop_pack_guard.py` выгружает ВСЕ 7 палитр в `pop_guard_pal.h` - (7 × 16 × 4 = 448 Б, записи `B,G,R,0` — формат `gfx_pal_load`); - каналы масштабируются `scale6to8`, как в `pop_pack_kid` для `kid.pal`. -2. `pop_guard_set_palette(color)` (`pop_gdraw.c`) заливает 16 слотов в ОБЕ - палитры дабл-буфера; `color == 0` — не трогать (так и оригинал). -3. Зовётся из `pop_guard_enter` сразу после чтения данных комнаты, то есть - ДО отрисовки — как `redraw_screen`. `pop_level_guard` цвет отдавал уже - давно, его просто никто не использовал. - -**Грабли, стоившие итерации (записать на будущее).** Первая версия передавала -`gfx_pal_load` указатель прямо на таблицу — а таблица лежит в rodata -БАНКОВОГО модуля, то есть по 0xC000+. `gfx_pal_load` отдаёт указатель в BIOS -(`$A4` через `rst #0x08`), а BIOS читает только `#4000-#BFFF` (корневой -CLAUDE.md). BIOS забирал мусор с чужой страницы, палитра уезжала в тёмное и -страж становился НЕВИДИМЫМ. Лечится копией записи в локальный буфер — стек -гарантированно в W2. - -**Проверено в MAME (уровень 2, ROOMNAV):** - -| комната | цвет из уровня | что на экране | -|---------|----------------|---------------| -| 11 | 1 | страж сине-фиолетовый, полоса HP синяя | -| 7 | 3 | страж оранжевый, полоса HP оранжевая | -| ур. 1 | 2 | охра, как было (регрессии нет) | - -Цвета сходятся с таблицей `pop_guard_pal.h`: цвет 1 = (72,145,255), -цвет 3 = (255,80,0), цвет 2 = (170,48,0). - -**Смежное, НЕ входит сюда:** палитра КЛАДКИ уровня 3 (в оригинале зелёная) — -другой механизм и другой ресурс, заведена отдельной задачей -[L3-COLOR](TASKS_OPEN.md#l3-color). - ---- - - -## BUG-SWORD-GHOST-1. Переход комнаты В БОЮ: Кид прячет меч и дерётся пустой рукой — **ЗАКРЫТ 2026-08-07** - -**Проверено в игре (пользователь):** «вроде проблема не воспроизводится — -всё корректно». Замер на замороженном кадре: оба персонажа в комнате 2, -ряд 1, колонки 4 и 7, `Kid.sword = 2`, `Guard.sword = 2`, луч видимости -прошёл, счётчик уборок меча за прогон — **0**. - -**Симптом.** Кида вытеснили из комнаты 3 в комнату 2 в бою, он спрятал -меч; страж вошёл следом, и дальше Кид «отбивался» без клинка. Зависимость -от расстояния между Кидом и стражем в момент перехода: близко — бой -продолжался нормально, далеко — меч убирался и страж не шёл, а в -промежутке страж приходил, но меч оставался в ножнах. - -**Корень — мнимая стена за краем комнаты, а не боёвка.** Ложным было -`can_guard_see_kid`. Луч видимости (`check_can_guard_see_kid`, seg003:688) -идёт по тайлам между колонками Кида и стража, а колонка считается из `x`: -`floor((x − 7 − 58) / 14)`. На всём диапазоне `x` 0..255 это даёт `col` -от **−5 до 13**, то есть колонка регулярно уходит ЗА комнату. Оригинал -такие колонки резолвит через `find_room_of_tile` (seg006:005D), гуляя по -`roomlinks` на любую глубину; наш `get_tile` знал соседей только на две -колонки (`−2..11`), а дальше отдавал `TILE_WALL`. Луч упирался в эту -мнимую стену, `can_guard_see_kid` падал в 0 — и `control_with_sword` -(ветка «противника не видно») штатно убирал меч. - -Тот же ноль не давал достать меч обратно: `control_standing` зовёт -`draw_sword` только при `can_guard_see_kid >= 2`. Отсюда и «драка без -меча», и зависимость от расстояния — на самом деле не от расстояния, а от -того, вышла ли колонка за кэш. - -**Артефакт (регистратор в памяти, снят с живой сцены).** Брейкпоинт тут -бесполезен — баг ловится руками и редко, поэтому в код были временно -вшиты байты «кто убрал меч и почему оборвался луч»; читались после сцены: - -``` -src = 2 control_with_sword, ветка «противника не видно» -why = 2 луч упёрся в стену -tile = 0x14 = 20 = TILE_WALL -col = 12 ← колонка стража ЗА кэшем (было −2..11) -kid_col = 8 guard_col = 12 -``` - -**Фикс.** `pop_map` кэширует `fg` соседних комнат слева и справа ЦЕЛИКОМ -(по 30 байт, раскладка комнаты) и резолвит `col` от −10 до 19 в реальный -тайл соседа; за этими пределами — по-прежнему стена (у оригинала там -следующий `roomlink`, у нас край кэша). Данные забирает сама -`pop_map_set_edges(left, right, up, down)`, читая уровень — отдельной -копии в приложении нет. Заодно `gate_modif` перестал читать за границу -массива модификаторов: openness ворот на дальних колонках берётся из -`room_modif` СОСЕДА (`pop_gate_modif`, им же пользуется луч). - -Цена: **+48 байт** в W2 (кэш 6+6 → 30+30), код W1 даже уменьшился. -Комнаты сверху/снизу оставлены как были (`above_fg` / `below_fg`): по -вертикали `curr_row` за −1..3 не выходит, мнимых стен там не возникает. - -Попутно вернули строку оригинала, потерянную при порте: `control_with_sword` -сбрасывает `holding_sword` для живого Кида (seg005:980) — от неё зависит -индикатор HP стража. - -**Регресс:** `tests-host` — все 5 наборов зелёные, характеризационные -трассы Кида не сдвинулись (`[phys] ok: 1723`). Новый набор проверок -`char_tiles_resolve_across_rooms` в `t_char` держит резолв колонок -−11..20 в тайлы соседей и стену на краю кэша — чтобы кэш нельзя было -молча сузить обратно. - -**Почему в SDLPoP не воспроизводилось** (пользователь пробовал): там этой -границы просто нет — `find_room_of_tile` уходит по `roomlinks` сколько -нужно, и луч никогда не встречает мнимой стены. - ---- - -## BUG-GRAB-1. Прыжок с места через провал в 3 тайла: зацепа нет — **ЗАКРЫТ 2026-08-05** - -**Проверено в игре (пользователь):** «зацеп работает». Физика была верна с -самого начала, чинить пришлось клавиатуру — [BUG-KBD-5](#bug-kbd-5). - -> **Итог 2026-08-05.** Физика тут ни при чём — виновата клавиатура. -> Зажатый Shift снимался автоповтором зажатой стрелки, поэтому к кадрам -> 102…106 (окно зацепа) движок видел Shift отпущенным. Полный разбор и -> фикс — [BUG-KBD-5](BUGS_CLOSED.md#bug-kbd-5); поведение Shift в MAME -> проверено замером карты `_kbdraw_down`. Осталось подтвердить сам зацеп -> живой игрой; версии 2 и 3 ниже проверять только если он всё ещё не выйдет. - - -**Симптом.** Уровень 2, комната 9. Перепрыгнув на (1,1), Кид должен -вернуться обратно: разбегаться негде, поэтому он встаёт на самый край -плиты, прыгает с места и **цепляется руками за (1,5)**, после чего -подтягивается. У нас Кид с зажатым Shift всё равно срывается. - -**Что уже точно известно (и не надо перепроверять).** - -1. **Физика прыжка у нас совпадает с оригиналом кадр в кадр.** Сверено по - логу SDLPoP против трассы харнесса при одинаковом старте `x=95`: - - ``` - кадр 16 18 22 23 24 25 102 103 104 105 - SDLPoP 95 97 105 112 121 126 128 130 131 133 - наш 95 97 105 112 121 126 128 130 131 133 - ``` - Совпадает и по `y`, и по колонке/ряду, и по приземлению на 107–108. - -2. **В оригинале зацеп срабатывает на кадре 106, а не 102..105.** - `check_grab` зовётся из ДВУХ мест: ветка «в воздухе» в `check_action` - (кадры 102..105) и `do_fall` (seg005) для `actions_4_in_freefall`. - Успешная попытка из лога: - - ``` - GRAB try f=106 x=135 y=166 col=4 row=2 fall_y=18 - GRAB probe x=127 col=4 through=0 front_above=3 modif=0 - GRAB can_grab=1 - GRAB OK dist=9 - -> f=91 x=136 y=181 col=5 row=2 act=2 (повис) - ``` - Наш `do_fall` (`pop_map.c`) `check_grab()` из этой ветки тоже зовёт — - то есть структура на месте, расходится что-то внутри. - -3. **По харнессу зацеп у нас РАБОТАЕТ**: окно стартовых `x = 91…95`, и - короткий шаг ставит Кида ровно туда (91 после первого нажатия, 95 после - второго). Зафиксировано тестом `t_grab`. - -**Отсюда главный вопрос был: почему харнесс говорит «работает», а живая -машина — «нет».** Расхождение между ними и оказалось уликой; версии -выдвигались по убыванию правдоподобия, и сработала первая: - -- **Shift не доезжает до движка — ПОДТВЕРЖДЕНО, это и была причина.** - Харнесс подменяет клавиатуру и потому этот путь не проверяет вовсе, а у - нас есть история проблем ровно с «Shift + стрелки» (KBD-1, BUG-KBD-3/4). - Замер в MAME: при зажатом Shift и зажатой стрелке бит `LSh` в - `_kbdraw_down` стоял в нуле. Разбор — [BUG-KBD-5](BUGS_CLOSED.md#bug-kbd-5). -- **Сцена харнесса не равна комнате 9.** Там изолированная комната - (соседи — стена), а в игре слева комната 8; кромки шва участвуют в - `get_tile`. Проверять чтением `Kid.x` в момент прыжка: попал ли он в - окно 91…95 вообще. -- **Расхождение в `check_grab`.** Наш вариант зовёт `determine_col()` - там, где оригинал зовёт `load_fram_det_col()` (перезагрузка кадра + - колонка). Для Кида это обычно одно и то же (`cur_frame` в фазе физики - принадлежит ему), но проверить стоит. - -**Инструменты готовы.** В SDLPoP включена отладка (пометка `DBG-GRAB`): -`JMP` — покадровая трасса прыжка/падения/виса, `GRAB try|probe|fail|OK` — -вход в `check_grab` и причина отказа. Снимается поиском по `DBG-GRAB`. - -**Найдено попутно, отдельным наблюдением.** После касания площадки на -кадрах 107–108 (x=140) оба движка снова падают, но X расходится: SDLPoP -уводит Кида на 134 (колонка 4), мы — на 141 (колонка 5). Похоже на разную -отработку `in_wall()` у стены (2,7). На зацеп не влияет. - ---- - ---- - -## BUG-GATEMOD-1. Ворота стартуют закрытыми, хотя в уровне открыты — **ЗАКРЫТ 2026-08-05** - -**Проверено в игре (пользователь).** - -**Симптом (пользователь, 2026-08-04).** Уровень 2, комната 13: решётка -между (2,5) и (2,6) обязана быть ОТКРЫТА в начале и захлопнуться, когда Кид -нажмёт кнопку (2,4) — после этого назад дороги нет. У нас она закрыта -сразу, кнопка бессмысленна, проход не работает. - -**Корень.** Модификатор тайла в ФАЙЛЕ уровня и модификатор в РАНТАЙМЕ — -разные величины; оригинал переводит их при загрузке в `load_alter_mod` -(seg008:198E), которую зовёт `alter_mods_allrm` из `load_level`: - -```c -case tiles_4_gate: *modif = (*modif == 1) ? 188 : 0; break; -case tiles_11_loose:*modif = 0; break; -case tiles_10_potion:*modif <<= 3; break; -``` - -Наш `pop_trob_modif` портировал из неё **только зелье**. Для ворот -`bg = 1` — это «открыты» (Table 8 спецификации DAT), а в рантайме открытость -измеряется высотой подъёма 0..188; мы клали в рантайм-модификатор сырую -единицу, то есть «закрыты на 1/188». - -**Фикс.** Ветки ворот и loose дописаны в ленивую инициализацию -`pop_trob_modif` (`pop_trob.c`). Ветка СТЕН не портируется намеренно: у нас -`pop_bg` считает связи кладки по типам соседей прямо при отрисовке -(`wall_modifier`), сохранённый модификатор стены не читается. - -**Что это ещё задевает.** Решётка (0,9) комнаты 5 уровня 1 тоже имеет -`bg = 1`, то есть обязана стартовать открытой — Кид сваливается в комнату 1 -именно через неё, и она захлопывается у него за спиной. Закрывает её -стартовый триггер `do_startpos` (seg003:167): для уровней с -`tbl_entry_pose == 1` оригинал ВИРТУАЛЬНО ЖМЁТ кнопку комнаты 5 (0,2) — - -```c -// Special event: press button + falling entry -get_tile(5, 2, 0); trigger_button(0, 0, -1); seqtbl_offset_char(seq_7_fall); -``` - -Замер в SDLPoP (лог по кадрам): `gate(5,0,9)` идёт `188 → 148 → 88 → 8 → 0`, -шаги 40/60/80 — это `gate_close_speeds`, то есть быстрое закрытие -(`trigger_gate` вернул тип 3). У нас этот триггер портирован, и закрытие -работает. - -Полный список ворот с `bg = 1`: ур. 1 комн. 5 (0,9); ур. 2 комн. 13 (1,5). -Остальные ворота уровней 1–3 имеют `bg = 2` → 0, и для них ничего не -меняется (при модификаторе 2 и 0 и отрисовка, и `can_bump_into_gate` дают -одно и то же). - -**Побочная находка: чит обхода комнат отматывал мир.** После фикса -пользователь увидел «ворота снова открылись», пройдя `+` в комнату 2 и `-` -обратно. Причина не в воротах: `ROOMNAV` звал `pop_trob_reset()` перед -`enter_room`, тот обнулял `room_seen[]`, и `pop_trob_modif()` перечитывал -модификаторы из уровня заново — то есть чит откатывал открытые/закрытые -ворота, выдвинутые пики и нажатые кнопки. Пока ворота с `bg=1` ошибочно -стартовали закрытыми, откат был не виден. `pop_trob_reset()` из навигации -убран: она обязана только телепортировать, исходное состояние даёт -перезапуск уровня. Замер, который это показал: `room_modif` комнаты 5 -после `+`/`-` = `00 00 0B 00 09 00 08 01 00 BC` — последний байт 0xBC = 188, -файловое значение. - ---- - ---- - -## BUG-GUARD-DEAF-1. Страж не оборачивался на вернувшегося Кида — **ЗАКРЫТ 2026-08-05** - -**Наблюдение (пользователь, 2026-08-05).** Уровень 2, комната 11, идёт бой -со стражем. Страж **выталкивает Кида в правую комнату 22**. Кид заходит -обратно в комнату 11 — страж стоит на (1,1), **повёрнут налево и Кида не -видит**. Предположение пользователя: это часть большой задачи «полностью -переделать поведение стража по образцу Кида». - -**Диагноз: большая переделка тут ни при чём, корень маленький и точный.** -Само возвращение стража в исходную позу — ПРАВИЛЬНОЕ поведение, порт верен; -не хватает ровно одного сигнала. - -Разбор по SDLPoP: - -1. **Выход Кида из комнаты «усыпляет» стража — так и в оригинале.** - `leave_guard` (seg002:02F5) складывает стража обратно в данные уровня - (тайл, `x`, **направление**, skill, HP), а `enter_guard` (seg002:0112) - при возврате поднимает живого стража **с УБРАННЫМ мечом** - (`sword_0_sheathed` + `seq_77_guard_stand_inactive`) и обнуляет - `is_guard_notice`/`guard_refrac`. То есть страж после возврата ВСЕГДА - неактивен и смотрит в запомненную сторону — у нас так же - (`pop_guard_enter`, `pop_guard.c:127`). -2. **Дальше решает `autocontrol_guard_inactive` (seg002:0876), и он Кида за - спиной ИГНОРИРУЕТ.** Кид вернулся справа, страж смотрит влево → - `char_opp_dist()` отрицательна → ветка `else if (distance < 0) return;`. - Единственный выход из неё — флаг **`is_guard_notice`**: «Кид нашумел». - При нём страж оборачивается (`move_4_down`). Направление в - `check_can_guard_see_kid` НЕ участвует вовсе, так что «не видит» — это - не про луч видимости, а именно про этот флаг. -3. **У нас `is_guard_notice` не взводится НИГДЕ.** `grep` по всем исходникам - roomtest: объявление (`pop_guard.c:20`), сброс (`pop_guard.c:47`) и одно - чтение (`guards.c:171`). Присваивания `= 1` нет ни одного — флаг мёртв, - поэтому неактивный страж не обернётся НИКОГДА, что бы Кид ни делал. - -**Где его взводит оригинал** (это и есть объём фикса): - -| место | событие | -|-------|---------| -| `seg006:633..641` — `play_seq`, опкод SOUND | звук `SND_SILENT`(0), `SND_FOOTSTEP`(1), `SND_BUMP`(2). `SND_DRINK`(3)/`SND_LEVEL`(4) — НЕ шум. Главный источник: шаги бега/приземления | -| `seg004:05F1` `bumped_sound` | удар в стену | -| `seg005:185,195` | мягкое и среднее приземление (**только `charid_0_kid`**) | -| `seg006:1294`, `seg006:1734` | Кид обрушил loose-плиту (зацепом и наступив) | -| `seg007:766` | нажата кнопка | - -У нас опкод SOUND в `play_seq` (`pop_kid.c:426`) просто съедает байт -аргумента — звука нет, и флаг вместе с ним потерялся. Именно эта строка — -90 % фикса: `SND_SILENT` называется «silent» потому, что звука не издаёт, -**но стражи его всё равно замечают**, и в `seqtbl` он стоит, например, в -`ready` (доставание меча). - -**Ожидаемое поведение после фикса.** Кид возвращается в комнату 11 бегом → -первый же `SND_FOOTSTEP` взводит флаг → страж оборачивается и достаёт меч. -Стоя на месте, Кид может подкрасться к стражу со спины — это НЕ баг, а -механика оригинала. - -**Оговорка (не проверено вживую).** Разбор построен на том, что страж после -возврата неактивен (меч убран, кадр 166). Это следует из кода -`pop_guard_enter`, но в MAME не снималось; если окажется, что меч у него -ОБНАЖЁН, то работает другая ветка (`autocontrol_guard_active`, где -`can_guard_see_kid == 2` направления не спрашивает) — и тогда корень другой. -Снять при фиксе: `Guard.sword`, `Guard.frame`, `can_guard_see_kid` сразу -после входа в комнату. - -**Фикс (2026-08-05): все пять мест портированы.** Опкод SOUND в `play_seq` -(`pop_kid.c`) взводит флаг для звуков 0..2; `bumped_fall`/`bumped_floor`, -мягкое и среднее приземление, обрушенная плита (все три пути `check_press`) -— в `pop_map.c`; щелчок кнопки — в `pop_trob.c`. Заглушка `is_guard_notice` -добавлена в `tests-host/stubs.c` (автопилота стража в наборах нет). - -**Проверено в игре (пользователь, 2026-08-05):** тот же сценарий — страж -выталкивает Кида в комнату 22, Кид возвращается бегом — **страж -оборачивается**. - -**Что осталось «как в оригинале» и багом не является:** подкрасться к -стражу СТОЯ по-прежнему можно (шумит движение, а не присутствие), и после -возврата в комнату страж всегда поднимается с УБРАННЫМ мечом в неактивной -стойке, повёрнутый туда же, куда смотрел при выходе Кида — это `enter_guard` -(seg002:0112), а не потеря состояния. - ---- - ---- - -## BUG-JUMPWALL-1. Недолетевший прыжок проходил СКВОЗЬ стену — **ЗАКРЫТ 2026-08-05** - -**Симптом (пользователь, приёмка уровня 3).** Комната 14: Кид на (0,8) -лицом влево, прыжок с места — пролетает сквозь кладку и падает в комнату 13 -на (2,3). Обобщение пользователя (оно и оказалось верным): «если Кид -прыгает, недолетает и должен врезаться в стену и упасть ВДОЛЬ неё, у нас он -летит по свободной траектории, как будто ему никто не мешает». - -**Корень — упрощение в `check_collisions`, про которое там же стояла -оговорка.** Оригинал (seg004:0004) каждый кадр считает флаги перекрытия для -ТРЁХ рядов (`curr`/`above`/`below`), а `move_coll_to_prev` (seg004:00DF) в -начале следующего кадра выбирает из них тот, что соответствует ряду прошлого -кадра. Так «прошлые» флаги остаются настоящими и на кадре СМЕНЫ РЯДА. - -У нас ряд был один, и на смене ряда `prev` заполнялся тройками («уже -перекрывал всё»), что подавляло бамп на этом кадре. В падении ряд меняется -почти каждый кадр, а переход флага 0→1 на стене приходится ровно на него — -поэтому удар не регистрировался и `bumped_fall` (который и гасит `fall_x`) -не вызывался. - -**Как ловили.** Не в MAME, а на харнессе: набор -[`tests-host/t_wall.c`](tests-host/t_wall.c) — комната 14 уровня 3 с -подложенным дном шахты, свип по всей ширине стартовой плиты (x = 177..196). -Из 20 стартовых позиций 4 давали проход сквозь кладку (Кид оказывался в -колонке 4 при стене в колонке 5). Первый вариант сценария (одна стартовая -X, из центра плиты) БАГА НЕ ПОКАЗАЛ — отсюда свип. - -**Фикс.** Три ряда как в оригинале + дословный `move_coll_to_prev` -(включая условия ±3 на заворот ряда при смене комнаты). Цена — 3×14 -`get_tile` на кадр вместо 14, банк 3 +115 Б. - -**Чем проверено.** `t_wall` зелёный; **все 1723 существующие трассы -`t_phys` не изменились ни на байт** — правка поведение-сохраняющая для -всего остального. В живой игре подтвердил пользователь: «ударились и -падаем вертикально вниз». - ---- - -## BUG-SEAM-WEDGE-1. Клин кладки в пустом углу (2,0) — **ЗАКРЫТ 2026-08-05** - -**Симптом (пользователь, приёмка уровня 3).** Комната 18: в тайле (2,0) -нарисован кусок кладки, хотя по данным там пусто. - -**Корень.** У тайла (2,0) «сосед снизу-слева» — это колонка −1 ряда 3, то -есть тайл ЧУЖОЙ комнаты (`room_BL` — левая от нижней). `load_rowbelow` -(seg008:368) резолвит его через `get_tile_to_draw(room_left, 9, 0, …)` и -подставляет стену ТОЛЬКО когда такой комнаты нет. Мы клали стену -безусловно, а `draw_topright` для стены рисует угловой кусок — он и был -клином. Для комнаты 18 диагональ — комната 13, её тайл (0,9) пуст, значит -рисовать нечего. - -**Фикс.** Ряд «снизу» стал одиннадцатибайтным: `[0..9]` — колонки комнаты -снизу, `[10]` — тайл (0,9) комнаты снизу-слева (дефолт «стена», если её -нет). Затронуты `pop_level.c` (заполнение), `pop_bg.c` (чтение), -`roomtest.c` (размер массива). - -**Проверено** в MAME: комната 18 уровня 3, клина нет. - ---- - -## BUG-LOOSE-3. Чёрный бар под упавшей плитой-потолком — **ЗАКРЫТ 2026-08-05** - -**Симптом (пользователь, приёмка уровня 2).** Комната 6: Кид сбивает -плиту-потолок (−1,2) — плита падает, но на её месте остаётся чёрный бар. -Два уточнения пользователя оказались диагностическими: - -1. «бар попал в фоновое изображение» — Кид прыгает поверх, уходит, бар - остаётся → чернота лежит в ОЗУ-копии, и heal возвращает её каждый кадр; -2. «когда возвращаемся в комнату после выхода — бара нет» → статическая - отрисовка комнаты рисует всё правильно, виновата ЗАПЕЧКА. - -**Корень.** `pop_ceil_bake_empty` стирал полосу потолка чёрной плитой -(`bar`, банк NORMAL — пишет и в ОЗУ-копию) шириной 64 px, а восстанавливал -только ДВА тайла ряда −1: `(−1,col)` и `(−1,col+1)`. Но куски тайлов -рисуются ВВЕРХ от своей нижней грани и свисают ВПРАВО, поэтому в стёртую -полосу попадает графика и соседа СЛЕВА, и тайлов ряда 0 — их верхушки. -Незакрашенным оставался прямоугольник ~13×3 px. - -**Замер (он же метод).** Плита роняется без игры — записью -`pop_ceil_modif[col] = 1` в отладчике MAME (это ровно то, что делает -`make_loose_fall` для потолка). Дальше попиксельная сверка скриншота -«после запечки» со скриншотом «комната перерисована заново» (выйти и -вернуться): различие локализовалось в прямоугольник **экранные x 230..255, -y 47..52** при полосе потолка y 44..52. - -**Фикс.** Запечка восстанавливает всё, чья графика попадает в полосу: -ряды −1 И 0, колонки `col−1..col+1`. - -**Проверено:** та же попиксельная сверка после фикса даёт **0 различий** в -полосе; пользователь подтвердил в игре. - ---- - -## BUG-RJUMP-1. Разбег-прыжок не берёт провал в три тайла — **ЗАКРЫТ 2026-08-04** - -**Симптом (пользователь, приёмка уровня 2).** Комната 1: Кид с разбегу -обязан перелететь колодец с (0,5) на (0,1) — у нас он долетает до колодца и -валится вертикально вниз. Комната 9: то же с (1,5) на (1,1). Обобщение -пользователя оказалось точным: **провал ровно в три пустых тайла наш Кид не -перепрыгивал никогда**, а провалы поменьше брал. - -**Почему «никогда», а не «иногда».** Суммарный `dx` последовательности -`seq_4_run_jump` (кадры 34..44) — 62 пикселя при ширине тайла 14. Это -ровно 4 колонки и меньше половины тайла запаса. То есть перелёт трёх -пустых тайлов возможен ТОЛЬКО если оттолкнуться почти точно от кромки; из -случайной фазы бегового цикла он не получается никогда. - -**Корень.** Оригинал именно поэтому и не даёт прыгать откуда попало: -`run_jump` (seg005:0AA8) перед стартом **выравнивает Кида по кромке**. - -```c -short xpos = char_dx_forward(4); -short col = get_tile_div_mod_m7(xpos); -for (short tiles_forward = 0; tiles_forward < 2; ++tiles_forward) { - col += dir_front[Char.direction + 1]; - get_tile(Char.room, col, Char.curr_row); - if (curr_tile2 == tiles_2_spike || !tile_is_floor(curr_tile2)) { - pos_adjustment = distance_to_edge(xpos) + TILE_SIZEX * tiles_forward - TILE_SIZEX; - if ((word)pos_adjustment < (word)-8 || pos_adjustment >= 2) { - if (pos_adjustment < 128) return; // ПРЫЖКА НЕТ - pos_adjustment = -3; - } - Char.x = char_dx_forward(pos_adjustment + 4); - break; - } -} -control_up = release_arrows(); -seqtbl_offset_char(seq_4_run_jump); -``` - -**У нас этой половины не было** — стояла заглушка с честным комментарием -«оригинал выравнивает Kid по краю пола (нужны tile-запросы) — это полировка -K3; K2b просто запускает run-jump». Полировкой это не оказалось: без -выравнивания три тайла непроходимы в принципе. - -**Две тонкости, которые легко потерять при порте.** - -1. **Беззнаковое сравнение.** `(word)pos_adjustment < (word)-8 || pos_adjustment >= 2` - означает ровно «`pos_adjustment` НЕ попал в `[-8,-1]`». Ветка - `pos_adjustment = -3` недостижима: `distance_to_edge ∈ [0,13]`, - `tiles_forward ∈ {0,1}`, значит `pos_adjustment ∈ [-14,13]`, а туда нужно - `>= 128`. В порте она записана как мёртвая, с объяснением. -2. **Отказ НЕ гасит `control_up`.** `return` выходит из `run_jump` до - `release_arrows()`, поэтому Кид бежит дальше с зажатой «вверх» и пробует - снова на следующем кадре. Именно так игрок и ловит фазу — просто - удерживая клавишу. Если погасить, прыжок у кромки станет одноразовым и - почти всегда неудачным. - -**Фикс.** Тайловая половина — `pop_run_jump_align()` в `pop_map` (там живут -`get_tile`/`distance_to_edge`), диспетчерская — в `pop_ctrl.run_jump`. -Разделение то же, что у `pop_jump_up_seq`: pop_map правит `Kid.x` напрямую, -и `pop_savekid_state` эту правку намеренно не затирает. - -**Проверка — на харнессе, а не в MAME.** Сценарий -`phys_running_jump_over_3tile_gap` в [`tests-host/t_phys.c`](tests-host/t_phys.c) -(комната с провалом в колонках 2–4). Было: прыжок со старта в кадре 34 при -`x=165`, кадр 44 приходится на колонку 3 — провал, падение. Стало: Кид -пробегает лишние 5 кадров, выравниватель ловит фазу, прыжок стартует при -`x=149`, кадр 44 даёт `x=87, col=1, row=1` — приземление на пол и бег -дальше. Остальные 8 сценариев набора не изменились ни на байт: правка -трогает только ветку разбег-прыжка у кромки. - ---- - - -## BUG-FALL-SWORD-1. Отход с мечом в провал: не та последовательность падения — **ЗАКРЫТ 2026-08-04** - -**Симптом (приёмка уровня 2, комната 4).** Кид с вынутым мечом отступает -от стража к дыре от упавших loose-плит. Наблюдение пользователя: он -«проваливается раньше времени», летит **с клинком в руке**, и падает **по -другим X**, чем в оригинале — «почти на целый тайл левее». - -**Разбор.** Сверка `seg006:1044 start_fall` показала, что наш порт -пропустил ТРИ вещи из оригинала, и все три бьют именно по этому сценарию: - -```c -void start_fall() { - Char.sword = sword_0_sheathed; // (1) меч В НОЖНЫ - inc_curr_row(); start_chompers(); - ... - } else if (frame >= 81 && frame < 86) { // (2) срыв при приземлении - seq_id = seq_19_fall; // после прыжка вверх - Char.x = char_dx_forward(5); - load_fram_det_col(); - } else if (frame >= 150 && frame < 180) { // (3) кадры С МЕЧОМ - droppedout = 1; - if (Char.direction < dir_0_right && distance_to_edge_weight() <= 7) - Char.x = char_dx_forward(-5); - seq_id = seq_81_kid_pushed_off_ledge; - } -``` - -У нас все они падали в общий `else` → `seq_7` (stepfall). Разница между -`seq_7` и `seq_81` в `seqtbl.c` и объясняет ВЕСЬ симптом: - -``` -seq_7 stepfall : dx(1) dy(3) | 102 | dx(2) dy(6) | dx(-1) dy(9) | dy(12) | dx(-2) set_fall(1,15) -seq_81 fightfall : dy(-1)| 102 | dx(-2) dy(6)| dx(-2) dy(9) | dx(-1) dy(12) | dx(-3) set_fall(0,15) -``` - -`set_fall(1, 15)` против `set_fall(0, 15)` — **горизонтальный дрейф**: в -`seq_7` во время всего свободного падения `Char.x` уходит на 1 в сторону -КАЖДЫЙ кадр. За два этажа падения это и есть тот самый «почти тайл». -Оригинал в бою падает строго вниз. - -**Сверка по числам** (лог SDLPoP `DBG shot`, кадры 102..105): - -``` -SDLPoP: 102 x=155 103 x=157 104 x=159 105 x=160 ← +2 +2 +1 = seq_81 -у нас: 102 x=151 ← seq_7 -``` - -Обратный счёт: у SDLPoP в момент решения `x = 150`, дальше -`char_dx_forward(-5)` при `dir=-1` даёт `+5` → 155. У нас решение при -`x = 152` — то есть **по самому правилу срыва расхождения нет**: обе -позиции лежат в колонке 7 (`dx_weight = x + 14`, кадр 157 имеет -`weight_x = 14`; колонка 7 — это `x ∈ [149,163)`). Двухпиксельная разница -— фаза отхода (шаг отступления `dx(-3)+dx(-2)` = 5 пикселей за цикл), а она -зависит от RNG стража и между движками совпасть не обязана. «Раньше -времени» — это не срыв не там, а seq_7 вместо seq_81. - -**Что подтвердилось попутно.** Старт уровня 2 у нас **байт в байт** как в -SDLPoP: `frame=15 x=107 y=118 dir=-1 col=3 row=1 room=5`. Таблицы кадров и -`seqtbl` вынуты из оригинального бинарника, так что расходиться могут -только РЕШЕНИЯ движка — искать надо всегда там. - -**Связь с [BUG-LAND-SWORD-1](#bug-land-sword-1).** Тот фикс (`land()` -даёт `seq_63` при вынутом мече) остаётся — он есть в оригинале, — но для -Кида он теперь почти недостижим: `start_fall` убирает меч в ножны, и после -приземления кадр 109 разбирает обычный `control_crouched`. То есть -настоящий корень вечного приседа был здесь, а не в `land()`. - -**Метод.** Пустышка `pop_dbg_trap()` в резиденте W1 (идея пользователя): -брейкпоинт на банковый код ставить нельзя — 0xC000+ это окно, куда мапятся -все банки, и точка ловит чужие функции. Оставлена в `pop_state.c` как -многоразовый инструмент. - ---- - -## BUG-CTRL-FRAME-1. Геометрия Кида считалась по кадру СТРАЖА — **ЗАКРЫТ 2026-08-04** - -Самый неприятный класс: не косметика, а неверные РЕШЕНИЯ движка, причём -плавающие — одна и та же поза Кида давала разный результат в зависимости от -того, в какой фазе анимации находится страж. - -**Симптом (приёмка уровня 2, комната 4).** Кид под сплошной плитой (1,6), -справа от него дыра от упавшей loose-плиты. По ↑ он то прыгает вверх -впустую (упираясь головой в плиту), то пытается зацепиться — но **не с той -координаты X**, с которой запрыгивает оригинал. Наблюдение пользователя, -оказавшееся точным: «похоже, дело в страже — в комнатах без стража то же -самое рисуется и работает правильно». - -**Замер (брейкпоинт на входе `pop_jump_up_seq`, состояние снято в момент -решения).** - -``` -_Kid frame=15 (стойка) x=156 y=181 dir=влево curr_col=6 curr_row=2 -кадр 15 Кида (kid_data.bin): dx=0 weight_x=3 -pop_gframe (кадр СТРАЖА, image 17): dx=-1 weight_x=8 -``` - -Считаем `dx_weight()` обоими кадрами: - -| | кадр Кида (правильно) | кадр стража (что было) | -|---|---|---| -| `dx_weight()` | 156+3 = **159** | 156+9 = **165** | -| `m7()` | (159−7−58)/14 = 6 ост. **10** | (165−7−58)/14 = 7 ост. **2** | -| `curr_col` | 6 | **7** ← намерено | -| `distance_to_edge_weight()` | **10** | **2** | -| ветка `jump_up_or_grab` | 10 ≥ 6 → шаг назад + зацеп | 2 < 6 → `jump_up_plain` | -| результат | x 156 → **160**, `seq_24/8` | x без изменений, **`seq_28`** ← намерено (A=28 на выходе) | - -Обе строки «намерено» совпали с предсказанием по кадру стража до единицы — -диагноз подтверждён, а не выведен. - -**Корень.** `cur_frame` (`pop_kid.c:200`) — ОДИН глобал на всех персонажей -(так и в оригинале), его владелец — тот, кто последним прошёл `load_frame`. -В нашем кадре последним тикает страж (`pop_guard_tick`), поэтому к моменту -управления Кидом там лежит кадр стража. А `kid_cur_dx/dy/flags` читают этот -глобал напрямую, и через него считается ВСЯ геометрия управления: -`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()`** (= `load_frame` + `determine_col`, seg006:0144) — -ДО `control()`. У нас этого вызова не было; в `pop_ctrl_tick` стоял -комментарий «кадр не перезагружаем — play_seq в kid_tick сделает это -следующим шагом», и он был неверен: `control()` читает `cur_frame` РАНЬШЕ, -чем `play_seq` его обновит. - -**Фикс.** `pop_load_fram_det_col()` (`pop_kid.c`) — порт связки; зовётся -после `pop_loadkid()` в `pop_ctrl_tick` и после `pop_loadshad_and_opp()` в -`pop_guard_tick`. Вторая половина связки (`determine_col`) выполняется -только на ветке Кида: `determine_col` у нас существует лишь для него — -`pop_map` работает с `Kid` напрямую, а не с абстрактным `Char`. - -**Что это объясняет задним числом.** Плавающее поведение прыжка — кадр -стража меняется каждый тик, вместе с ним `dx`/`weight_x`, и `distance` -Кида скакал через порог 6. А «голова Кида поверх плиты (1,6)», с которой -начался разбор, — не баг отрисовки: Кид просто оказывался в позе, которой -в оригинале в этом месте не бывает. - -**Урок.** Любой глобал, который в оригинале «принадлежит активному -`Char`», у нас обязан перезагружаться на КАЖДОМ входе в окно `Char` — иначе -между персонажами течёт состояние, и баг проявляется только когда в комнате -есть второй персонаж. Здесь такой глобал уже ловили однажды: см. запись -про кэш кадра отрисовки в `load_frame` (`pop_kid.c:342`). - ---- - - -## BUG-LAND-SWORD-1. Кид навсегда застревает в приседе после падения с мечом — **ЗАКРЫТ 2026-08-04** - -**Симптом (приёмка уровня 2).** Страж ударил Кида, Кид потерял HP, -провалился через loose-плиту на этаж ниже — и **сел в присед, из которого -не выходит**: клавиши не действуют вообще. - -**Что показало ЖИВОЕ состояние в MAME** (мост `mame-z80`, чтение по -адресам из `roomtest.noi` — окно застало багу в момент): - -``` -_Kid frame=109 x=144 y=181 dir=-1 col=6 row=2 action=1 room=4 - sword=2 (ВЫНУТ) alive=-1 curr_seq=0x2037 -_holding_sword=1 _can_guard_see_kid=0 control_x/y/shift = 0 -``` - -`curr_seq = 0x2037` — это внутри метки `softland_crouch` -(`SEQTBL_BASE + 1736 = 0x2036`). Смотрим `seqtbl.c:916`: - -```c -LABEL(softland) // seq_17_soft_land - act(actions_5_bumped), SEQ_KNOCK_DOWN, dx(1), frame_107_fall_land_1, - dx(2), frame_108_fall_land_2, - act(actions_1_run_jump), LABEL(softland_crouch) frame_109_crouch, - jmp(softland_crouch), // ВЕЧНЫЙ ЦИКЛ на кадре 109 -``` - -То есть `seq_17_soft_land` **не заканчивается сам** — он крутится на кадре -109, и вывести из него может ТОЛЬКО `control_crouched()`. - -**Корень.** `control()` (seg005:252) до `control_crouched` при вынутом -мече не доходит — раньше срабатывает ветка - -```c -} else if (Char.sword == sword_2_drawn) { - control_with_sword(); -``` - -а `control_with_sword` (seg005:964) в мирной обстановке -(`can_guard_see_kid < 2`, под ногами не loose) умеет ровно одно: если кадр -== 171 (стойка с мечом) — убрать меч. Кадр 109 он не знает. **Замкнутый -круг: последовательность ждёт control_crouched, а диспетчер туда не -пускает.** - -Оригинал такой позы просто не допускает — `land()` (seg005:176): - -```c -if (Char.charid >= charid_2_guard || Char.sword == sword_2_drawn) { - Char.sword = sword_2_drawn; - seq_id = seq_63_guard_active_after_fall; // боевая стойка -} else { - seq_id = seq_17_soft_land; // присед -} -``` - -**У нас этой ветки не было** — `pop_map.c land()` ставил -`SEQ_17_SOFT_LAND` безусловно. На уровне 1 не всплывало, потому что там -меч подбирается поздно и падать с ним особо негде; на уровне 2 меч у Кида -**с первого кадра** (`have_sword = level >= 2`), а в комнате 4 страж стоит -в одном ряду с двумя loose-плитами — сценарий собирается сам. - -**Фикс.** Порт недостающей ветки: при `Kid.sword == SWORD_2_DRAWN` -падение на один этаж даёт `seq_63` (боевая стойка), а не присед. Ветка -`charid >= charid_2_guard` нам не нужна — наш `land()` работает только с -Кидом. - -**Почему не задело падение на ДВА этажа** (`seq_20_medium_land`): там -`jmp` в конце нет — 29 кадров приседа и автоматический подъём -(`seqtbl.c:934`), поэтому оно развязывается само. Вечный цикл только у -`softland`. - -**Урок на будущее.** Симптом «персонаж не реагирует на управление» стоит -диагностировать не по кадру, а по `curr_seq`: адрес прямо показывает, в -какой метке `seqtbl` он завис, и дальше видно, кто обязан был его оттуда -вывести. - ---- - -## Проверено в MAME 2026-08-01 (ревизия L1-TRIAGE) - -Три бага стояли как **Critical** с 2026-07-21 и по исходникам выглядели -закрытыми, но переподтверждены не были. Прогон в MAME (roomtest, `ROOMNAV`, -чтение `_Kid` через мост) закрыл все три. - -### BUG-1. Боковой переход через ворота: Kid проваливается на row 1 — **ЗАКРЫТ (не воспроизводится)** - -**Был симптом:** при проходе через ОТКРЫТЫЕ ворота в соседнюю комнату -(через шов) Kid оказывался на ряду row 1 вместо row 0. - -**Проверка 2026-08-01, точный сценарий бага.** Комната 6, Kid на ряду 0; -осторожный шаг на кнопку (0,2) — решётка room8 (0,9) поднимается; удержание -← через открытый шов: - -| момент | `room` | `x` | `y` | `curr_col` | `curr_row` | -|--------|--------|-----|-----|-----------|-----------| -| на кнопке в room6 | 6 | 97 | 55 | 2 | 0 | -| после перехода | **8** | 181 | **55** | 8 | **0** | - -Ряд и Y сохранены, Kid стоит на полу ряда 0 (скриншот `triage_06.png`). -Провала нет. - -**Заодно снят и сам диагноз записи** — он был неверен. В записи стояло: -«Y/`curr_row` при боковом переходе НЕ репроецируются, в отличие от -`check_leave_below`». Это не дефект, а **точное поведение оригинала**: -`goto_other_room` (`SDLPoP/src/seg002.c:390`) для направлений left/right -меняет ТОЛЬКО `Char.x` (±140), а `Char.y`/`curr_row` трогает исключительно -для up/down. Наш `check_leave` (`pop_map.c:1402`) делает ровно то же. -Реальной причиной симптома была, судя по всему, кромочная коллизия — её -закрыл `char_x_forward_edge` (см. BUG-SEAM-PINGPONG ниже). - -### BUG-2. Возврат из комнаты назад: Kid отбрасывается обратно (ping-pong) — **ЗАКРЫТ (не воспроизводится)** - -**Был симптом:** после перехода в соседнюю комнату попытка сразу вернуться -приводила к тому, что Kid снова закидывался в ту же комнату — выйти нельзя. - -**Проверка 2026-08-01.** Шов room2↔room3 (ряд 1, без ворот — чистый -горизонтальный переход), с намеренным разворотом СРАЗУ после пересечения: - -| действие | `room` | `x` | `curr_col` | -|----------|--------|-----|-----------| -| старт в room2 | 2 | 86 | 1 | -| держим → | **3** | 122 | 3 | -| сразу держим ← | **2** | 195 | 9 | -| сразу держим → | **3** | 85 | 1 | -| сразу держим ← | **2** | 183 | 8 | - -Комната меняется РОВНО один раз на пересечение, туда и обратно, без -осцилляции. Механизм на месте: `pop_leave_timer` (порт `exit_room_timer`, -`pop_map.c:112,1553`) + `char_x_forward_edge` (`pop_map.c:365`). - -### BUG-3. Climb-up на тайл-кнопку: неправильная окклюзия — **ЗАКРЫТ фиксом от 2026-07-28** - -**Был симптом:** при подтягивании на тайл, верх которого — кнопка -(opener/closer), Kid рисовался ПОВЕРХ кнопки вместо того, чтобы быть -перекрытым её передней гранью. - -**Почему закрыт.** Запись требовала: «трактовать нажатую кнопку как -floor-тайл в climb-overlay, учесть подстановку из `get_tile_to_draw`». -Ровно это и сделано `tile_code_drawn()` — `climb_overlay_tile` -(`pop_bg.c:1346`) берёт ПОДСТАВЛЕННЫЙ код тайла, а не сырой. То есть -BUG-3 — дубль пункта «спуск с кнопки (room8, кромка (0,6))» из раздела -«Исправлено», заведённый до фикса. - -**Оговорка, чтобы не выдавать желаемое:** покадрово в MAME снимался СПУСК -(кадры 148..138). Подъём идёт через ту же ветку и ту же таблицу -`FLOOR_LEFT_OVERLAY[fidx]`, поэтому отдельного дефекта тут быть не может, -но визуально направление «вверх» не переснималось. Если при сквозном -прохождении (L1-PASS) увидишь Kid поверх кнопки на подъёме — заводи заново. - ---- - -## Косметика окклюзии — закрыта кодом, список отставал (сверено 2026-08-01) - -Четыре записи от 2026-07-22 висели как открытые Medium. Каждая описывала -недостающий кусок порта; каждый из них с тех пор написан, но записи никто не -снял. Ниже — что именно закрывает каждую. - -### BUG-CEIL-1. Прыжок вверх: руки Kid рисуются ПОВЕРХ потолка — **ЗАКРЫТ** - -**Был симптом:** при прыжке вверх (SEQ up, кадры 67..79) руки/голова Kid -заходили в полосу кладки у потолка (row −1) и рисовались ПОВЕРХ неё. - -**Чем закрыт:** `ceil_over_kid_tile()` (`pop_bg.c:1275`) — порт «нижняя грань -потолка и кадр плиты-потолка идут в FOREtable», т.е. поверх персонажа. -Зовётся из `pop_fore_over_kid` (`pop_bg.c:1575`) и из fore-прохода стража -(`:1607`). Комментарий на месте прямо называет причину: «иначе руки -прыгающего Kid лезут на кромку потолка». - -### BUG-CEIL-2. Тряска/разбитие loose-плиты в потолке (row −1) — **ЗАКРЫТ** - -**Был симптом:** loose-плита в ряду 2 верхнего соседа (room5 (2,5) → потолок -room6 над (0,5)) при прыжках Kid на (0,5) не тряслась и не разбивалась — -loose-состояние соседней комнаты не тянулось. - -**Чем закрыт:** отдельное состояние плиты-потолка `pop_ceil_modif[10]` -(`pop_bg.c:547`, ведёт `pop_map`) + пара `pop_ceil_shake_draw()` / -`pop_ceil_bake_empty()` (`pop_bg.c:815,827`): дрожание рисуется на текущей -странице поверх фона, а провал «запекается» в ОЗУ-копию, чтобы heal его -сохранял. В полосе у потолка виден только НИЗ плиты (`draw_tile_aboveroom`), -всё выше режется клипом `POP_YOFF` — как в оригинале. - -Из этого следует, что и запись «требует персистентного per-room modifier -соседей, это Фаза P0 gates_spikes_plan» больше не верна: понадобился не общий -механизм, а один массив на 10 байт под конкретный случай. - -### BUG-CEIL-3. Анимация ворот стирает потолок над ними — **ЗАКРЫТ** - -**Был симптом:** при анимации решётки шва полоса кладки у потолка НАД -воротами пропадала — чёрный `bar` по col0 стирал ряд −1, а redraw его не -восстанавливал. - -**Чем закрыт:** `pop_room_redraw_seam_left()` (`pop_bg.c:793`) больше не -трогает полосу потолка — `bar` идёт с `POP_YOFF + 3`, а не с `POP_YOFF`: -низ полосы кладки на room-space y=2, бары ворот начинаются с y=3 -(`gate_top_y = dby − 62`). Приятный побочный эффект: отпала необходимость -перерисовывать `draw_tile(-1,0)` на КАЖДОМ кадре анимации решётки — то есть -фикс не только косметический, но и вдвое дешевле прежнего. - -### BUG-OCCL-1. Тень дальней колонны перекрывает Kid — **ЗАКРЫТ** - -**Был симптом:** Kid у (0,4)-(0,5) частично перекрыт тёмной штриховкой — -боковой гранью ДАЛЬНЕЙ колонны, которая окклюдить персонажа не должна -(over-occlusion fore-слоя, рисовавшего fore футпринт-тайлов без учёта -глубины). - -**Чем закрыт:** разделение слоёв по признаку из оригинала, а не по нашим -соображениям о глубине (`overlay_mid_tile`, `pop_bg.c:1367`). Ключ, -подтверждённый трассой оригинала: `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`. - ---- - -## BUG-DOOR-CLIP. Подъём по лестнице двери уровня: нет обрезки по правому косяку — **ИСПРАВЛЕН 2026-08-01** - -**Был симптом (найден пользователем сразу после L1-EXIT):** при подъёме по -лестнице за дверью уровня (кадры 224..228) силуэт Кида вылезал ПРАВЕЕ правого -косяка проёма. По высоте обрезка была корректна. - -**Причина — недопортированная половина `clip_char`** (seg006:1231). Для -кадров двери оригинал ставит ДВА клипа, у нас был только первый: - -```c -if (frame >= frame_224_exit_stairs_8 && frame < 229) { - obj_clip_top = leveldoor_ybottom + 1; - obj_clip_right = leveldoor_right; -} -``` - -Отдельная ловушка: комментарий в SDLPoP говорит «frames 217..228», а КОД -проверяет `>= frame_224_exit_stairs_8`, то есть **224..228**. Портировано по -коду. - -**Почему именно обрезка, а не fore-слой:** створка и косяк уходят в оригинале -ЦЕЛИКОМ в backtable (`draw_leveldoor`, все `add_backtable`), то есть рисуются -ПОД персонажем и перекрыть его не могут. Единственный способ спрятать -поднимающегося — срезать сам спрайт. - -**Фикс:** -- libbgi: `gfx_blit_cols_part_w(..., uint8_t maxw)` — обрезка СПРАВА у - колоночного блита. Для column-major это ровно уменьшение числа колонок, - то есть внутри ядра механизм уже был (так же клипается край экрана: - `w = _bgi_maxx + 1 - x`), наружу не было выведено. Тело блита переехало - туда, `gfx_blit_cols_part` стал тонкой обёрткой (maxw=0) — тем же приёмом, - каким `gfx_blit_cols` уже обёрнут вокруг `gfx_blit_cols_part`. Работает и - при flip: первые `maxw` нарисованных колонок всегда ложатся в ЛЕВУЮ часть - футпринта. -- PoP: `pop_leveldoor_right` / `pop_leveldoor_ybottom` (порт одноимённых - глобалов) пишет `draw_leveldoor` в `pop_state` — их читает `clip_char` из - другого банка; `pop_clip_char_right()` отдаёт границу, `kid_draw` - превращает её в `maxw` и уводит эти кадры с noclip-пути на общий. - `kid_lw` (прямоугольник heal) тоже сужается — стираем ровно нарисованное. - -**Проверено в MAME:** `pop_leveldoor_right = 176` — ровно -`(draw_xh<<3) + 48` для двери комнаты 9; `pop_leveldoor_ybottom` = 112 у -закрытой створки и 69 у поднятой (сходится с формулой оригинала). -Отрисовка подтверждена пользователем на живом подъёме. - ---- - -## BUG-SEAM-PINGPONG (#4). Пинг-понг drawn_room у шва с закрытыми воротами — **РЕШЁН 2026-07-22** - -Настоящий корень найден потиковой трассой ЖИВОГО SDLPoP 1.23 -(lldb-брейкпоинты на leave_room/bumped/safe_step с логом Char + -char_x_left/right; fixes выключены = vanilla). Прежние гипотезы оставлены -ниже для истории — они НЕ были причиной. - -### КОРЕНЬ (подтверждён трассой + исходником) - -`set_char_collision` (seg006:0723): `char_x_right = obj_x/2 + 58`, где -`load_frame_to_obj` (seg008:1728) считает `obj_x = 2*char_dx_forward(dx) - 116` -и **добавляет +1** для кадров «чётного пикселя»: -`if ((sbyte)(cur_frame.flags ^ obj_direction) >= 0) ++obj_x;` -(бит 0x80 флагов кадра XOR направление; вправо: +1 если бит НЕ стоит). -Деление `obj_x/2` — C-усечение К НУЛЮ, поэтому при `e = x+dx <= 57` -(obj_x < 0, зона левого шва) поправка +1 даёт `char_x_right = e+1`, а при -e >= 58 формула сокращается к чистому `e`. - -Итог: Kid, осевший после отскока от ворот шва на x=57 (frame15, флаги 0x43 — -бит 0x80 не стоит), имеет **char_x_right = 58** и порога leave-left (<=57) -НЕ достигает. Наш движок считал передний край как `Kid.x + dx` без поправки -→ 57 → ложный leave → пинг-понг. - -Эталонный цикл SDLPoP (нормализовано по трассе): стойка x=61 → тап вправо → -safe_step(d=0) → step, на первом dx(1) x=62 → bump (edge-триггер) → align 61 -→ seq47 dx(-4) → **x=57** (скрыт за кромкой) → кадры 50/51/52 (cxr 61/60/58, -у всех бит 0x80 снят, e>57 — без сдвига) → стойка cxr=58 → leave НЕ -срабатывает; тап → safe_step d=3 → x=60 (1/3 видно); тап → step1 → x=61 -(2/3 видно); тап → bump → 57 … по кругу. Char.room и drawn_room НЕ меняются. - -### Фикс (pop_map.c) - -`char_x_forward_edge()`: `e = char_dx_forward(dx); if (((flags ^ (dir<0 ? -0x80 : 0)) & 0x80) == 0 && e <= 57) e++;` — используется в `char_front_coll` -(коллизия/bump/edge_distance) и в `check_leave` (порог ухода). Плюс порт -doortop-гарда leave-right из leave_room (тайл (9,row) = doortop → правого -выхода нет). `pop_leave_timer` (exit_room_timer) оставлен — он реален в -seg002/seg003. - -### Симптом (как выглядел) - -Kid стоит за решёткой закрытых ворот шва (левый сосед room8 виден в кромке -room6). При удержании/нажатии ВПРАВО экран пинг-понгует между двумя -состояниями: -- **A**: показывается room6, Kid у левой кромки за решёткой (спрайт на 2/3); -- **B**: показывается room8, Kid у его правой кромки. - -Эталон SDLPoP: drawn_room **всегда остаётся room6**, Kid осциллирует у -кромки (1/3→2/3→отступил→по кругу), в room8 экран НЕ переключается. - -### Инструментальный диагноз (watchpoint на pop_leave_dir) - -В момент лишнего свитча A→B: `pop_leave_dir=1 (LEFT)`, Kid **frame=15 (СТОЯ, -не transient!), Kid.x=57** (до репроекции +140). То есть: -- `char_x_right = char_dx_forward(kid_cur_dx()) = Kid.x + frame15.dx = 57+0 = 57`. -- Порог leave-left (взгляд вправо): `char_x_right <= 57` → срабатывает РОВНО на 57. -- Грань ворот (где их держит коллизия) = **61** (`wall_dist_from_left[1]=10 + - coll_tile_left_xpos=51`). Между 57 и 61 — **зазор 4px**: Kid НЕ удержан - воротами (d=61−57=4≥0 → check_bumped не бампит), но уже на пороге ухода. -- Kid оседает на 57 из-за recoil отскока: seq_47 = `act(bumped), dx(-4), - frame_50, 51, 52`; SEQ_DX(−4) двигает Char.x на −4 суммарно; frame_50.dx=4 - компенсирует ТОЛЬКО точку коллизии НА кадре 50, но при возврате в стойку - (frame15, dx=0) `char_x_right = Char.x = aligned−4 = 57`. - -### Что было ИСКЛЮЧЕНО (сверено с исходниками SDLPoP, НЕ причина) - -- Формула char_x: `char_x_right = obj_x/2+58 = Char.x+frame.dx` (seg006 - set_char_collision) — совпадает с нашим char_dx_forward. -- Позиция грани ворот: `get_left_wall_xpos = wall_dist_from_left[1](10) + - xpos_in_drawn_room(x_bump[9+5])+7 = 10+(184−140)+7 = 61` — совпадает с нашим - (`x_bump[−1+5]=44`, +7, +10 = 61). -- Порог leave: SDLPoP leave_room looking-right `char_x_right<=57` — совпадает. -- Данные кадров: frame_50 (image=49,dx=4,flags=0x67) и frame_15 - (image=14,dx=0,flags=0x43,sword=9) — БАЙТ-В-БАЙТ как в SDLPoP frame_table_kid. -- seq_47 (act bumped, dx(-4), frame 50/51/52) — совпадает. - -### Почему точечные фиксы НЕ работали - -- Гард в check_leave (подавить leave на закрытых воротах) — это ОТСЕБЯТИНА, - не SDLPoP (в leave_room такого нет); откачено. -- `exit_room_timer=2` (порт seg002 exit_room — РЕАЛЬНЫЙ механизм, оставлен как - pop_leave_timer): блокирует leave 2 кадра после входа в комнату. НЕ спасал: - положение Char.x=57 **устойчивое** (Kid стоит), а не transient. - -### Задел, который НЕ понадобился: порт coll_room + отложенный drawn_room - -Трасса показала, что в эталоне `Char.room`/`drawn_room` вообще не меняются, -поэтому план S2/S3 для этого бага не потребовался. Остаётся заготовкой под -стражей/двух персонажей в кадре: - -1. **Раздельные комнаты.** `Char.room` ≠ `drawn_room`. Уже есть `kid_room` - (S1) + рендер-смещение `pop_kid_set_render_dx(∓140)`. -2. **Коллизия по Char.room через coll_room (seg004, S2).** Портировать - `check_collisions` → `get_row_collision_data` → `get_left_wall_xpos`/ - `get_right_wall_xpos` → `curr_row_coll_room[]`/`_flags[]`, - `bump_col_left_of_wall`/`bump_col_right_of_wall` → `check_bumped_look_*` → - `bumped()`. Тайлы — из РЕАЛЬНОЙ комнаты колонки, НЕ из снапшота. -3. **Отрисовка по drawn_room**, персонаж со сдвигом ±140. -4. **Отложенная смена drawn_room (seg002/seg000, S3):** `leave_room` → - `goto_other_room` → `exit_room` (`next_room`) → `check_the_end`. - -Реализовано на 2026-07-22: S1 + `pop_leave_timer`. -Память: [[pop_seam_room_model]], [[sdlpop_odd_pixel_char_x]]. - ---- - -## Решено НЕ делать - -### OPT-1. Хирургический редрой левого шва (ворота соседа) — 2026-07-22 - -**Возможность:** `pop_room_redraw_seam_left()` (pop_bg.c) на каждое изменение -openness рисует `bar(BLACK)` по всему col0 + **полный `draw_tile(0,0)`** -(стены, `topright`, `wall_pattern` с `prandom()` — десятки блитов). Реально -анимируются только бары решётки — `draw_gate_back` (~9 `env_b`). - -**Стоимость:** seam-блок (синий io_border) занимает ~30-50% кадрового периода, -**но только пока openness меняется** — во время открытия и медленного -авто-закрытия (~5 сек после схода с кнопки). В покое — 0%. Замерено в MAME: -брейк на `_pop_room_redraw_seam_left` (0x5A54) срабатывает ⟺ сегмент дорогой. - -**Приём** (heal НЕ годится: печёные бары устаревшие, поэтому и стоит -`bar(BLACK)`+redraw): `bar(BLACK)` только по полосе баров (x=0, -gate_top..gate_bot) + `draw_gate_back(lmod,...)` + дорисовать статику тайла -(0,0), задетую полосой. **Пиксель-чувствительно** — обязательна выверка в -MAME по кромкам. - -**Решение:** оставляем как есть — стоимость транзиентная, в бюджет -помещаемся. Делать, только если упрёмся в кадровый бюджет на сценах с -воротами. - ---- - -## Исправлено - -- **Спуск с кнопки (room8, кромка (0,6)): Кид просвечивал в щель, ближняя рука - срезана до одного пикселя** — ИСПРАВЛЕНО 2026-07-28. Две причины: - (а) не был портирован `clip_char()` (seg006:1749) — верхняя обрезка спрайта по - `y_clip[curr_row+1]`, когда тайл над головой стена/пол; сделано - (`pop_clip_char_top` в pop_map.c + `gfx_blit_cols_part` в libbgi, heal чистит - уже обрезанный прямоугольник); - (б) `climb_overlay_tile` выбирал ветку `draw_floor_overlay` (seg008:1E3A) по - СЫРОМУ коду тайла — а нажатая кнопка в `get_tile_to_draw` (seg008:240) - подменяется на floor/stuck. Тайл-кнопка не проходил тест floor, уходил в - `draw_other_overlay` и закрашивал Kid ЦЕЛЫМ тайлом вместо узкой кромки - `floor_left_overlay[frame-137]`. Фикс — `tile_code_drawn()` (одна подстановка - на все слои). Проверено покадрово в MAME (кадры 148..138). **Этим же - фиксом закрыт BUG-3** (см. выше). - -- **Шов ворот жёг 50% кадра в покое (закрытая решётка)** — ИСПРАВЛЕНО - 2026-07-22. Причина — **баг кодогенератора SDCC z80** (memory - `sdcc_z80_cmp_store_a_bug`): `if (m[9] != seam_sig) seam_sig = m[9];` - компилировался в `sub (seam_sig)` (A ← разность) + `ld (seam_sig),a` — - сохранял РАЗНОСТЬ `m[9]-seam_sig`, не `m[9]`. `seam_sig` осциллировала - (напр. 25↔231), `m[9]!=seam_sig` истинно каждый кадр → `draw_tile(0,0)` - каждый кадр даже у неподвижной решётки. Фикс: store-до-сравнения - (`seam_sig = g;` из чистого `g` ДО `sub`), подтверждён в .asm. Прочёс всех - модулей PoP: других случайных compare-then-store нет. - -- **Пики: 2×полный `draw_tile` на кадр анимации** → хирургический редрой, - 2026-07-22. `pop_spike_redraw`: `heal_off` уже возвращает всю печёную - статику (база 127, пол, грани); поверх анимируются только два острия - (`SPIKES_FRAM_LEFT` в своей ячейке + `SPIKES_FRAM_RIGHT` в соседней). - Заменили 2×`draw_tile` на 2×`env_b` — пиксель-в-пиксель тот же результат, - в разы дешевле (шахта из нескольких пик больше не съедает полный кадр). - -- **Отрисовка нажатой кнопки (0,2)/(0,3)** — ИСПРАВЛЕНО. Причина: `fore_tile` - (pop_bg.c, fore-слой поверх Kid) рисовал переднюю грань КНОПКИ (bottom_id - 149) поверх уже нарисованной грани пола (43) — «остаток нажатой кнопки». - Фикс: `fore_tile` применяет ту же подстановку нажатой кнопки, что и - `draw_tile` (opener→floor / closer→stuck при таймере связи >1). Плюс - `pop_button_redraw` — wipe своей ячейки + правой грани (дальний угол в - 0,3), низ строго yb+64 (не залезать в стену ряда 1). - ---- - -## НЕ БАГИ (кривая картинка, но совпадает с оригиналом — НЕ чинить) - -- **Кадр падения с мечом: голова/руки поверх кромки пола — САМ РЕНДЕР верен.** - Сверено с SDLPoP v1.24 (2026-08-04): там кадр падения выглядит один в один. - ⚠ Речь ТОЛЬКО о виде кадра. Расхождение по X, которое было отмечено здесь - как открытое, разобрано и закрыто: [BUG-FALL-SWORD-1](#bug-fall-sword-1) — - дело не в моменте срыва (он верен), а в том, что мы играли `seq_7` вместо - `seq_81` и получали горизонтальный дрейф `set_fall(1,15)` за всё падение. - - Механика точки веса, чтобы не разбирать заново. У стоек с мечом она - ОГРОМНАЯ: - - ``` - кадр 158/170/171 (stand_with_sword): dx=0 weight_x=13 - кадр 157 (walk_with_sword): dx=0 weight_x=14 - кадр 15 (обычная стойка): dx=0 weight_x=3 - ``` - - `dx_weight()` = `char_dx_forward(dx − weight_x)`, а при взгляде ВЛЕВО знак - меняется — точка веса уезжает на 13 px ВПРАВО от `Char.x`. Замер нашего - кадра падения: `x=151`, взгляд влево → `dx_weight = 164` → `m7(164)` = колонка - **7**, а (1,7) — дыра. `check_on_floor` честно видит «под ногами не пол». - С обычной стойкой (weight_x=3) вышло бы 154 → колонка 6 → пол. - - То есть с мечом персонаж «стоит» на 10 px правее, чем выглядит, и кромку - переступает раньше. Данные кадров у нас совпадают с `frame_table_kid` - (seg006:127) байт в байт, `swordfight`/`back_with_sword` портированы дословно - (включая `control_backward = CONTROL_IGNORE` — один нажим = один шаг). - Чинить нечего. - - **Важное отличие от остальных записей этого раздела.** Там (труп стража, - падающая плита) картинка кривая, и совпадает с оригиналом лишь потому, что - оригинал сам так рисует — артефакт порядка midtable. **Здесь картинка - ПРАВИЛЬНАЯ**: Кид реально уже за кромкой, и спрайт перекрывает её ровно - настолько, насколько персонаж туда зашёл (наблюдение пользователя, - 2026-08-04). Геометрия и рендер согласованы; «неправильно выглядит» — - только если считать позой то, что видит глаз, а не точку веса. - - ⚠ **Не путать** с тем, что БЫЛО нашим багом рядом: расширение футпринта - перерисовки под клинок (`redraw_at_char`, seg003:0430) — его не было, и меч - оставлял след/лез поверх столба. Исправлено 2026-08-04. - -- **Голова стоящего Кида поверх падающей на него loose-плиты.** Комната 12: - зацеп не удался, Кид остался стоять, сбитая плита падает прямо на него — - голова рисуется ПОВЕРХ плиты. Сверено покадрово с SDLPoP v1.24 (2026-07-29): - там ровно то же самое. Артефакт оригинального движка (порядок midtable), а - не наш баг; «починка» увела бы от эталона. Отличать от соседних случаев, - которые БЫЛИ нашими багами и исправлены: вис/подтягивание на кромке плиты и - падение вместе с плитой — там плита обязана быть поверх Кида. - -- **Ноги стоящего Кида поверх головы лежащего трупа стража.** Комната 21: - страж убит, комната покинута и открыта заново (у трупа своя запомненная X), - Кид СТОИТ на одну колонку правее тела — его ноги рисуются поверх головы. - Проверено в SDLPoP (2026-08-03): там ровно то же. - - Корень — устройство движка, а не наш порт. `set_objtile_at_char` - (seg006:13F3) приписывает персонажа РОВНО ОДНОМУ тайлу, и ширина спрайта на - выбор тайла не влияет никак; дальше `redraw_needed_tiles` (seg008:1B06) - обходит тайлы рядами 2,1,0 и колонками 0..9, и кто позже — тот поверх. - Стоящий Кид укладывается примерно в колонку, поэтому у него это незаметно, а - лежащий труп занимает две-три колонки, но «принадлежит» левой. Всё, что - стоит правее, перекрывает выступающую часть тела. Механизма «широкий объект - участвует в нескольких тайлах» в оригинале нет — сверено с - `draw_objtable_items_at_tile`, `sort_curr_objs` и веткой - `tile_object_redraw == 0xFF` (последняя про оверлеи пола, не про персонажей). - - Отличать от того, что БЫЛО нашими багами в этом же месте и исправлено - (BUG-DRAWORDER-1): колонка трупа считалась из тайла вместо X, ветка - `actions_1_run_jump` не была портирована, окно fore-клипа затиралось стражем. - Если когда-нибудь захочется «починить» и это — минимальный вариант — брать - тайл по ЦЕНТРУ габарита, а не по левой границе; но это осознанный отход от - эталона, и в других позах порядок поменяется в обратную сторону. - -- **Комнаты 13, 18, 24 недостижимы в обычной игре** — свойство ДАННЫХ уровня 1, - не наш баг. Обход графа от стартовой комнаты (по `res2001.bin`, links @1952) - показывает: у всех трёх ссылки наружу есть, а на них не ссылается никто - (24: `L→9`, но у 9 `R=0`; 13 и 18 связаны только друг с другом). Признак - «комнату выкинули из компоновки, связи не почистили» — несимметричные ссылки - ровно у этих трёх, у остальных 21 симметрия полная: - - ``` - 13 L→22, у 22 R=16 | 18 L→15, у 15 R=12 | 24 L→9, у 9 R=0 - 13 R→16, у 16 L=22 | 18 R→12, у 12 L=15 - | 18 D→19, у 19 U=12 - ``` - - Следствие: в 13/18/24 возможен «мусор в шве» — наш рендер кромки читает - крайнюю колонку соседа ПО ССЫЛКЕ, а сосед этих комнат соседом себя не - считает. Приоритет багов в этих трёх комнатах — низкий (в игре не видно). - ---- - -## Проверено в MAME 2026-08-03 (прогон уровня 1: 11 наблюдений → 6 корней) - -Сырой список наблюдений с обхода всех комнат разобран по корням. -Проверка — MAME + мост `mame-z80`: чтение `_Kid` по адресу из -`.sprinter-cc-roomtest/roomtest.noi`, `ROOMNAV` для навигации, потиковые -трассы, скриншоты. - -**Оговорка о полноте проверки.** Каждый корень закрыт тем, что его СОБСТВЕННЫЙ -сценарий больше не воспроизводится; сквозного прохождения уровня и поиска -регрессий в соседней механике автоматика не делала. Чек-лист для ручной -перепроверки — в [`BUGS_OPEN.md`](BUGS_OPEN.md), раздел «Ручная перепроверка -фиксов». Особое внимание — порту `check_collisions`: он переписал ВСЮ -горизонтальную коллизию. - -### BUG-LVLSTATE-1. Уровень был немутабельным — **ЗАКРЫТ** - -**Симптомы:** выпитое зелье / поднятый меч / разбитая плита возвращались при -возврате в комнату; иногда кувшина нет, а пузырёк над ним крутится; в -комнате 15 раз в 1–2 с мигал контур кладки. «Первое остаётся, остальное -возвращается». - -**Корень.** Уровень лежал в EMM-странице только на чтение, `enter_room` -перезаливал `room_fg[30]` из неё при каждом входе, а персистентность держала -таблица переопределений на **8 записей** (`OVR_MAX` в `roomtest.c`) — девятая -и дальше молча терялись. Второй хвост: `pop_trob.c` брал тип тайла из -СТРАНИЦЫ (`pop_level_tile_raw`), поэтому заводил trob зелья/меча там, где -предмет уже поднят — отсюда пузырёк без кувшина и мигание от `animate_sword`. - -**Фикс.** `pop_level_set_tile()` пишет тайл ПРЯМО в страницу уровня (порт -`curr_room_tiles[…] = …`: `do_pickup` seg006:1671, `remove_loose` seg007:0EB8, -`loose_land` seg007:11E8). Таблица `ovr_*` удалена. Эталонная копия -foretable — в той же странице по смещению `0x1000` (страница 16 КБ, данных -2.3 КБ). - -**Проверено:** комната 22, зелье (0,6) выпито → выход в 23 → возврат: кувшина -нет, пузырька нет; ROOMNAV ставит Кида уже на (0,6), потому что тайл стал -полом. - -### BUG-RESPAWN-1. Респавн не перезагружал уровень — **ЗАКРЫТ** - -**Как в оригинале:** цикл `play_level` (seg003:57) на КАЖДОЙ итерации, в том -числе после смерти, зовёт `load_level()` — уровень читается заново. - -**Фикс.** `pop_level_reset_tiles()` (восстановление foretable из эталонной -копии) в `pop_start_level` рядом с `pop_trob_reset()`. - -**Проверено:** после смерти от стража зелье в комнате 22 снова на месте. - -### BUG-DEATH-1. Смерть от меча не доводилась до конца — **ЗАКРЫТ** - -**Симптом:** страж убивает Кида, тот «воскресает» на месте и его убивают -снова, по кругу. - -**Корень.** `hurt_by_sword` ставил seq_85 и обнулял HP, но `Kid.alive` -оставался −1, а `pop_kid_dead` (по нему главный цикл делает респавн) взводил -только путь пик/падения. В оригинале это первая строка `control_kid` -(seg006:0CD1): `if (Char.alive < 0 && hitp_curr == 0) Char.alive = 0;`. - -**Фикс.** Порт этой ветки в начало `pop_ctrl_tick` + `pop_kid_dead = 1`. - -**Проверено:** страж в комнате 21 убивает Кида → респавн в стартовой позиции -уровня (комната 1), цикла нет. - -### BUG-GATE-ANIM-1. Ворота в отрисованной комнате не перерисовывались — **ЗАКРЫТ** - -**Корень.** `pop_process_trobs` продвигал модификатор ворот, но пометки -перерисовки не ставил; ворота рисовались только при полной отрисовке комнаты -и в `pop_room_redraw_seam_left`. В оригинале `animate_door` (seg007:0522) -заканчивается `draw_trob()` (seg007:01E6). - -**Фикс.** Вид перерисовки `POP_RD_GATE` + `pop_gate_redraw(row,col)` в -`pop_bg.c` (wipe зоны решётки в ячейке ПРАВОГО соседа + `draw_tile`), пометка -из `pop_process_trobs` (текущая страница каждый кадр, обе — на последнем). - -**На уровне 1 это ровно ОДНА решётка:** room5 (0,5). Все остальные стоят в -колонке 9, их бары рисуются уже в соседней комнате — там работает -`pop_room_redraw_seam_left`. - -**Проверено:** кнопка room5 (0,4) — решётка (0,5) поднимается на экране. - -### BUG-COLL-1. Bump искал стену только в колонке переднего края — **ЗАКРЫТ** - -**Симптомы:** пробегание сквозь закрытую решётку (комната 12, room5 (0,9)); -влёт внутрь стены на длинном прыжке (комната 6). - -**Два корня.** -1. `check_bumped` брал ОДНУ колонку — ту, в которой оказался передний край. - Для решётки в col9 окно этой колонки всего 4 px (201..204), беговой кадр - его перескакивает — бампа нет, а `check_leave` тут же уводит в соседнюю - комнату. Оригинал (`check_collisions`, seg004:0004) считает флаги - перекрытия для ВСЕХ колонок ряда и берёт колонку с переходом флага 0→1. -2. Наш guard `action == FREEFALL || MIDAIR → return` в `check_bumped`. В - оригинале таких гардов НЕТ: `bumped_fall` (seg004:04E4) специально - разбирает `actions_4_in_freefall`. Отсюда влёт в стену в прыжке. - -**Фикс.** Полный порт `check_collisions` + `get_row_collision_data` + -`is_obstacle` + `bumped(delta, push_dir)`; колонки считаются от −2 до 11, -чтобы решётки СОСЕДНЕЙ комнаты (швы) бампили как свои; гарды в `check_bumped` -приведены к оригинальным (только вис и подтягивание). - -**Проверено:** комната 5, бег вправо в закрытую решётку (0,9) — Кид упирается -(x встаёт на 60 в системе комнаты 1 и дальше не растёт). - -**Остаток:** экран при этом перелистывается на соседнюю комнату, и Кид в шве -не рисуется — отдельный баг BUG-SEAM-DRAW-1 (модель straddle S3), см. -`BUGS_OPEN.md`. - -### BUG-STANDUP-1. Вставание из приседа у стены роняло сквозь пол — **ЗАКРЫТ** - -**Симптом:** комната 5, падение с кнопки (0,6) на щебень (2,7) с уроном, -присед — и при вставании провал в комнату 6. - -**Корень — лишний guard `if (Kid.action == ACT_BUMPED) return;` в -`bumped_floor`** (в оригинале, seg004:0520, там `if (Char.alive)`). Цепочка: -вставание двигает Кида на 1 px в стену → бамп → `bumped_floor` прижимает `y` -к полу, отменяя `dy(−2)` из начала `medland` → наш guard возвращает -управление вместо `seq_47`, `medland` доигрывает свои `dy(+1)`,`dy(+1)` уже -ОТ пола → Кид НИЖЕ пола → следующий бамп читает беззнаковую разность как -«высоко над полом» → `bumped_fall` → выпадение вниз. У оригинала `seq_47` -обрывает `medland`, лишних `dy` нет. - -Найдено потиковой трассой + временной диагностикой (колонка бампа = 8, тайл -после разрешения = 14 щебень, ветка = `bumped_floor`). - -**Заодно** убран полу-порт опционального `FIX_STAND_ON_THIN_AIR`: у нас была -взята только его первая часть (кадры вставания 110..119 требуют пол), без -парной правки seqtbl (`dx(1)→dx(0)`, `dx(−4)→dx(−3)`), которую применить -нельзя — seqtbl извлечён из данных оригинала. Вернулись к ванильному -`seg006:909` (только кадр 109). - ---- - -# Вторая волна прогона 2026-08-03 (вечер) - - -## BUG-KBD-5. Зажатый Shift снимается автоповтором стрелки — **ЗАКРЫТ 2026-08-05** - -**Симптом (пользователь).** Прыжок с места с зацепом (уровень 2, комната 9) -не выходит: Кид прыгает, но за кромку не цепляется. Дальше уточнения, -которые и указали на клавиатуру, а не на физику: - -- «отпускаю стрелки в полёте и жму Shift один — зацеп есть»; -- «жму Shift не сразу, а когда бо́льшая часть прыжка позади — зацеп есть»; -- «зажимаю Shift, потом стрелки — прыжок есть, зацепа нет»; -- в SDLPoP та же комбинация в том же порядке работает всегда. - -Обобщение: **Shift работал в одиночку и не работал вместе со стрелками.** -Ровно то же ломало и осторожный шаг — игрок держит Shift, тапает стрелку, а -Кид на каком-то тапе уходит в бег (это же поведение раньше описывалось как -BUG-KBD-4, см. ниже). - -**Замер (MAME, `roomtest`, чтение карты `_kbdraw_down` + breakpoint на выходе -из `in a,($18)` в декодере трамплина).** - -1. Зажать LShift → байт 2 карты = `04` (LSh взведён), поток `12 12 12 …` - (Shift автоповторяется сам, пока он последняя нажатая клавиша). -2. Добавить ↑ → байт 46 = `20` (↑ взведена), **байт 2 = `00` — Shift снят, - хотя физически зажат.** -3. Добавить ещё → байт 46 = `30` (обе стрелки видны, 3-key rollover в - порядке), Shift по-прежнему `00`. -4. КОРОТКИЙ тап ↑ при зажатом Shift — Shift выживает. Это и сбивало с - толку: бит сносился, но тут же восстанавливался, потому что после - отпускания стрелки Shift снова становился «последней клавишей» и его - автоповтор `12` взводил бит обратно за ~30 мс. -5. Сам поток при зажатом Shift и зажатой ↑: - - ``` - … E0 E0 E0 75 E0 75 E0 75 ← ни одного F0/12 - ``` - -**Причина.** Декодеры (`_irq_tramp.c`, `kbd_raw_poll.c`) делали из «fake -shift» ДВА вывода, и второй был неверен: - -- обёртка `E0 F0 12` / `E0 12` есть → Shift зажат → взвести бит ✔ верно; -- расширенный make **без** обёртки → Shift отпущен → снять биты обоих - шифтов ✘ **неверно**. - -Обратный вывод опирался на «клавиатура обёртывает КАЖДЫЙ расширенный код». -Замер это опровергает: клавиатура MAME-Sprinter (`pc_kbd ms_naturl`) обёртку -не шлёт вовсе, и уж точно её не бывает на typematic-повторах — а повторы идут -непрерывно, пока стрелка зажата. Значит каждый повтор снимал реально зажатый -Shift, и к кадрам 102…106 (окно `check_grab`) движок видел Shift отпущенным. -Отсюда и «работает, если нажать Shift позже»: бит успевал постоять несколько -кадров до ближайшего повтора стрелки. - -**Фикс.** Обратный вывод убран целиком — расширенная клавиша идёт обычным -путём и о состоянии Shift не судит. Прямой вывод оставлен (обёртка, если -клавиатура её всё-таки шлёт, подтверждает «Shift зажат» и стоит дёшево). -Состояние Shift теперь ведут его собственные make/break `12` / `F0 12` — -они приходят всегда. Заодно ушла ставшая ненужной переменная -`_kbdraw_fakesh`, и оба декодера стали короче (трамплину это на пользу: его -клавиатурный блок упирается в диапазон `jr`). - -Файлы: `libc/irq/_irq_tramp.c`, `libc/kbd/kbd_raw_poll.c`, `libc/kbd/_kbdraw.h`, -`libc/kbd/_kbdraw_state.c`, `libc/kbd/kbd_raw_sync.c`. - -**Чем платим.** Потерянный при Rx-overrun break Shift снять теперь нечем — -модификатор может залипнуть до перенажатия (это старый BUG-KBD-3). Размен -осознанный и решён в ту же сторону, что и раньше в `kbd_raw_sync`: **лучше -залипание, чем отвал** — залипший Shift игрок снимает нажатием Shift, а -сорванный посреди игры Shift в PoP стоит жизни. Вероятность overrun'а сильно -снижена дренажом FIFO опросом из главного цикла (`kbd_raw_poll`, KBD-1). - -**Проверено в MAME после фикса** (карта `_kbdraw_down` при `roomtest`): - -| действие | LSh (байт 2) | стрелки (байт 46) | -|---|---|---| -| зажать LShift | `04` | `00` | -| + зажать ↑ и → | `04` | `30` | -| держать 5 с (автоповтор идёт) | `04` | `30` | -| отпустить Shift, стрелки держать | `00` | `30` | -| отпустить стрелки | `00` | `00` | - -`make size-check`: 70 программ, роста нет. - -**Осталось наблюдением, не багом этой задачи.** В карте изредка остаётся -взведённым бит в PLAIN-половине для кода стрелки (напр. байт 14 бит 4 = -`0x74` «Right без E0»). Это потерянный префикс `E0` — след старого рассинхрона -FIFO. Игру не задевает (движок читает `KBD_RIGHT = EXT|0x74`, то есть -расширенную половину), но если всплывёт — искать здесь. - ---- - - -## BUG-KBD-4 (+ BUG-KBD-3). Shift: сначала залипал, потом стал отваливаться — **ЗАКРЫТ (с поправкой, см. BUG-KBD-5)** - -> **Поправка 2026-08-05.** Вывод «клавиатура обёртывает КАЖДЫЙ расширенный -> код, пока зажат Shift» оказался неверным, и построенный на нём обратный -> вывод («расширенный make без обёртки ⇒ Shift отпущен») убран — он ломал -> Shift вместе со стрелками. Разбор — [BUG-KBD-5](#bug-kbd-5). Проверка -> «пять тапов стрелки подряд» ниже проходила не потому, что вывод был верен, -> а потому что между тапами бит восстанавливал автоповтор самого Shift. -> Актуальное поведение `kbd_raw_sync` — вариант 1 (модификаторы не сбрасываем). - -**Симптом (вторая редакция).** Залипание ушло, но появилось обратное: при -зажатом Shift второй-третий-четвёртый тап стрелки отрабатывал уже не -осторожным шагом, а бегом. Для PoP это ХУЖЕ залипания: игрок рассчитывает -на короткий шаг, а Кид убегает в яму или на пики. - -**Почему обе прежние редакции были неправильны.** Это был размен между -двумя способами «починить» потерю байта при Rx-overrun SIO (FIFO 3 байта): - -1. исключать модификаторы из сброса — потерянный break Shift снять нечем, - Shift залипает навсегда (BUG-KBD-3); -2. сбрасывать всю карту, как DSS (`KBD_Receiver_Overrun` в `KEYINTER.ASM` - чистит и `KEYCTRL`, и `KEY_FLG`) — залипания нет, но Shift сносится - каждым overrun'ом (BUG-KBD-4). - -Причина, по которой вариант 2 бил так часто, замерена: при зажатом Shift -клавиатура обёртывает КАЖДЫЙ расширенный код служебной парой, поэтому тап -стрелки — это не 5 байт, а 10, и overrun почти гарантирован. - -**Что нашлось (замер в MAME, watchpoint на порт данных SIO + чтение карты -`_kbdraw_down`).** Эта самая обёртка — «fake shift» — и есть решение, а не -помеха. Клавиатура шлёт `E0 F0 12` перед расширенным make и `E0 12` после -его break, и шлёт ТОЛЬКО пока Shift реально зажат. Проверено обоими -шифтами: правый обёртывается своим кодом `0x59` (bit 1 байта 11 карты), левый -— `0x12` (bit 2 байта 2). Это непрерывное и прямое свидетельство реального -состояния Shift — единственное доступное, потому что опросить состояние у -PS/2 нельзя, а typematic повторяет только ПОСЛЕДНЮЮ нажатую клавишу, то есть -стрелку, а не Shift. - -**Фикс.** Оба декодера (`libc/irq/_irq_tramp.c`, `libc/kbd/kbd_raw_poll.c`) -читают обёртку в обе стороны: - -- увидели fake shift → реальный Shift ЗАЖАТ → взвести plain-бит (бит кладёт - общий писатель: достаточно обнулить префиксы, и он попадёт в plain-половину - карты как make; в расширенную половину не пишем — это не клавиша); -- расширенный make БЕЗ предшествующей обёртки → Shift ОТПУЩЕН → снять - plain-биты обоих шифтов. - -`kbd_raw_sync` вернулся к исключению модификаторов — теперь это безопасно: -состояние Shift подтверждается независимо от того, что съел overrun, а -залипание снимается первым же нажатием стрелки. - -**Побочно найдена и исправлена своя ошибка** в новом коде `kbd_raw_poll`: -после `bit 1, a` в `A` лежал `pending`, а не скан-код, — обычные -(нерасширенные) клавиши декодировались бы из мусора. Оба декодера приведены -к одной логике. - -**Раскладка трамплина.** Клавиатурный блок перевалил за 127 байт, а `jp` -внутри трамплина запрещён (код копируется в W2 побайтно, абсолютные -само-ссылки сломают копию). Поэтому префиксные обработчики переехали вплотную -к своим `cp`, а посередине тела стоят три ретранслятора (`tr_kbd_hub`, -`tr_hub_notkbd`, `tr_hub_dss`) — до них дотягиваются `jr` и сверху, и снизу. -В `kbd_raw_poll` такого ограничения нет, там три перехода стали `jp`. - -**Проверено в MAME:** - -- Shift зажат, пять тапов стрелки подряд → `LSh` остаётся `04` во всех пяти, - `ovr = 0`, расширенная половина карты чистая; -- штатное отпускание Shift → `LSh = 00`; -- искусственно залипший Shift (бит записан в карту отладчиком) → снимается - ПЕРВЫМ же тапом стрелки; -- `make size-check`: 70 программ, роста нет. - - -## BUG-RESPAWN-2. После respawn стражи остаются мёртвыми — **ЗАКРЫТ** - -**Вопрос из отчёта:** «после respawn — должны ли оживать стражники?» -**Ответ по SDLPoP: да.** Цикл `play_level` (seg003:57) на КАЖДОЙ итерации — -в том числе после смерти Кида — делает `load_level()`, следом `pos_guards()` -(seg003:83), а затем `Guard.charid = charid_2_guard; Guard.direction = -dir_56_none`. То есть стражи — такая же часть данных уровня, как тайлы, и -перезагрузка файла возвращает их всех. - -**Корень у нас.** `gstate_init()` (живая копия таблицы стражей: тайл, -направление, мастерство, поза трупа) вызывался ТОЛЬКО при загрузке уровня. -Рестарт возвращал тайлы (`pop_level_reset_tiles`), но не стражей: в `gstate` -оставался сохранённый `leave_guard`'ом `seq_hi != 0`, и `pop_guard_enter` -поднимал стража трупом с `guardhp_curr = 0`. - -**Фикс.** `pop_level_reset_guards()` (тот же `gstate_init`) + `pop_guard_reset()` -в `pop_start_level`, рядом с `pop_level_reset_tiles()`. Туда же уехал -`pop_loose_mob_reset()` — настоящий `mobs_count = 0` из `start_level`. - -**Проверено в MAME:** комната 21, страж жив (`alive = -1`, hp 3) → убит читом -`K` (`alive = 0`, hp 0) → Кид убит стражем → респавн → возврат в комнату 21: -страж снова `alive = -1`, hp 3. - - -## BUG-DRAWORDER-1. Кид рисовался поверх тела стража — **ЗАКРЫТ** - -**Симптом.** В оригинале Кид проходит ЗА телом убитого стража; у нас — перед ним. - -**Три разных корня, найденные по очереди.** Стоит того, чтобы перечислить: два -первых захода были неполны, и каждый следующий кадр от тестера вскрывал новый -слой. - -1. **Порядок задавался ролью, а не тайлом.** Мы безусловно рисовали - `pop_guard_draw()` → `kid_draw()`, то есть Кид был сверху всегда. В - оригинале никто не «поверх» по определению: оба персонажа попадают в midtable - при обработке СВОЕГО тайла (`set_objtile_at_char`, seg006:13F3), а тайлы - обходятся строго — `redraw_needed_tiles` (seg008:1B06) идёт рядами 2,1,0 и - внутри ряда колонками 0..9; кто позже, тот поверх. Совпали тайлы — решает - `sort_curr_objs` (seg008:203C): ниже по `obj_y` = позже. - Фикс: `guard_over_kid()` в `roomtest.c`. - -2. **Своя же регрессия: Кид полез поверх передних столбов.** Окно fore-клипа - (`pop_fore_set_clip`) одно на всех, и его ставит каждый, кто рисует - персонажа. Пока страж рисовался строго ДО Кида, к моменту - `pop_fore_over_kid` в окне оказывался Кид и всё сходилось само. Как только - порядок стал переменным, при «страж поверх» окно оставалось СТРАЖЬИМ, и - fore-проход Кида отсекался целиком. - Фикс: `kid_fore_clip_restore()` — окно восстанавливается явно, а не - «по счастливому порядку вызовов». - -3. **Колонка трупа была нулевой.** `leave_guard` сохраняет тайл как - `get_tilepos(0, row)`, то есть колонку 0 всегда, а `enter_guard` у нас брал - `curr_col` оттуда и следом перетирал X запомненным значением. В памяти это - было видно прямо: `col=0` при `x=95`. Оригинал (seg002:180) выводит колонку - ИЗ X: `Char.curr_col = get_tile_div_mod_m7(Char.x)`. У живого стража - незаметно (X там сама считается из колонки), у запомненного ТРУПА — ломало - порядок. Колонка нужна не только отрисовке: на неё смотрят коллизия и - `check_can_guard_see_kid`. - -4. **Ветка `actions_1_run_jump` оказалась не «упрощаемой».** Сначала я решил, - что её можно не портировать. Кадр в движении показал `ACT=1`: в беге - оригинал берёт тайл не из `curr_row`/`curr_col`, а из нижнего ряда и ЛЕВОЙ - колонки ГАБАРИТА (`char_bottom_row`/`char_col_left` из `set_char_collision`, - seg006:0723). При беге вправо левая колонка меньше `curr_col` примерно на - тайл — потому бегущий Кид и уходит за объекты справа. Отсюда «некоторые - кадры бега рисовали Кида поверх тела». Считается для ОБОИХ персонажей: - `enter_guard` ставит `action = 1` и стражу, односторонний учёт дал бы - перекос в другую сторону. Тонкость: `char_col_left` берётся по НЕ - утоньшённой границе — поправку THIN оригинал применяет только к `*_coll`. - -**Проверено в MAME:** после возврата в комнату у трупа `col=5` при `x=135` -(было `col=0`); Кид слева от тела — и стоя, и в кадре бега — рисуется за телом; -передние столбы снова перекрывают Кида. - -**Остаток, который НЕ баг.** Ноги стоящего Кида поверх головы трупа, когда он -стоит колонкой правее тела, — врождённое свойство движка (один спрайт = один -тайл при спрайте шире колонки). Сверено в SDLPoP: там так же. Подробности — в -разделе «НЕ БАГИ» выше. - - -## BUG-LOOSE-2. Осколки только от одной из двух падающих плит — **ЗАКРЫТ** - -**Симптом.** Комната 12, соседние падающие плиты (0,1) и (0,2): после -пробежки осколки появляются только на (1,2), на (1,1) — нет. В комнате 7 -(плиты 0,5/0,6 → 2,5/2,6) обе дают осколки. - -**Корень.** Гипотеза автора отчёта подтвердилась чтением кода: Кид уходит в -комнату 15 раньше, чем долетает плита (0,1). Наш падающий кусок был привязан -к ТЕКУЩЕЙ комнате — `pop_loose_reset()` на смене комнаты звал -`pop_loose_mob_reset()` и гасил всё, что ещё летит, а тайл под куском читался -из карты отрисованной комнаты. Кусок уничтожался, `loose_land` не случался, -щебень не клался. В комнате 7 обе плиты успевают сесть до ухода — потому там -и выглядело правильно. - -В оригинале `mobs[]` чистится ТОЛЬКО в `start_level` (seg003:88); `do_mobs` -(seg007:1063) прокручивает все куски независимо от `drawn_room`, `move_loose` -работает по `curmob.room`, а `redraw_at_cur_mob` (seg007:132C) сверяется с -`drawn_room` лишь для ОТРИСОВКИ. - -**Фикс.** У куска появилось поле `room` (`curmob.room`). На смене комнаты -зовётся `pop_loose_mob_room_changed()` — сбрасывает только heal-историю, а -полёт продолжается. Тайл читается через `mob_tile_at()` (своя комната — -`tile_code`, чужая — `pop_level_tile`). Приземление в покинутой комнате пишет -щебень прямо в данные уровня: сигнальная пара `pop_loose_landed`/ -`pop_debris_at` обслуживает только текущую комнату. Отрисовка и -`check_loose_fall_on_kid` (там оригинал начинает с `Char.room == curmob.room`) -— только для своей комнаты. Настоящий `mobs_count = 0` переехал в -`pop_start_level`. - -**Проверено вручную (2026-08-03).** Автоматикой гонку воспроизвести не -удалось: мост MAME шлёт нажатия рывками, и «уйти из комнаты раньше, чем -долетит плита» через него не набирается. Подтверждено живым прогоном. - -Это ровно тот случай, ради которого заведены host-тесты: на уровне логики -проверка занимает несколько строк — заспавнить кусок, сменить комнату, тикать -до приземления, проверить щебень в данных уровня. Стоит первым в -`../docs/host_tests_plan.md`. - - -## BUG-SEAM-DRAW-1. Кид, упершийся в решётку на шве, не рисуется — **ЗАКРЫТ (не воспроизводится)** - -**Как было заведено.** Комната 5: Кид добегает до закрытой решётки (0,9), -решётка его держит, но экран переключается на комнату 1, и в ней Кида не -видно — он стоит на `x = 57..60`, левее кромки комнаты (col 0 начинается с 58). - -**Проверка 2026-08-03.** Не воспроизводится: Кид упирается и встаёт на -`x = 61`, `curr_col = -1`, `room = 1` — и в комнате 1 РИСУЕТСЯ, за решёткой, -как и должен. - -Почему сходится: при `x = 61` персонаж уже внутри системы координат комнаты 1 -(`obj_x = 2*61 − 116 = 6`), поэтому никакого straddle-смещения не требуется и -обычная отрисовка справляется. Отрицательная `curr_col` тут не противоречие — -это правило `get_tile_div_mod_m7`: `(61 − 7 − 58) / 14 = −1`. - -**Что осталось за скобками.** Само переключение экрана штатное: `leave_room` -(seg002:423) уводит вправо при `char_x_right >= 201`, а левая грань решётки как -раз 201 — оригинал в этой позе тоже перелистнёт. - -И остаётся теоретический остаток: при `x <= 60` (`obj_x <= 4`) спрайт уходил бы -за левую кромку и обрезался. В текущей сборке Кид туда не встаёт, поэтому баг -и не воспроизводится. Полное лекарство — довести модель straddle (S3 в -`../docs/room_model_plan.md`): держать `Kid.room` отдельно от `drawn_room` и -рисовать со смещением ±140, как `xpos_in_drawn_room` (seg004:0405). Пока -поводов для этого нет — заводить обратно только по живому наблюдению. - ---- - -# Прогон всех комнат уровней 1 и 2 (пользователь, 2026-08-07) — ЗАКРЫЛ ТРИ РЕВИЗИИ РАЗОМ - -Результат прогона: **крупных багов нет**. Тем самым закрыты и переехали сюда -из `BUGS_OPEN.md` три накопившихся хвоста — чек-листы ручной перепроверки -фиксов, таблица обхода 24 комнат уровня 1 и таблица сырых наблюдений прогона -2026-08-03. Всё, что с прогона 2026-08-07 осталось открытым, — три записи -уровня 2 в [`BUGS_OPEN.md`](BUGS_OPEN.md) (BUG-GUARD-COLOR-1, -BUG-GUARD-SPLASH-1, BUG-CHEAT-FIGHT-1); ни одна из них не мешает играть. - -Сырые формулировки пользователя лежат рядом: [`bugs_level1.md`](bugs_level1.md) -и [`bugs_level2.md`](bugs_level2.md). - - -## Ручная перепроверка фиксов (2026-08-03) — ПРОЙДЕНА - -Шесть корней прогона 2026-08-03 были закрыты автоматической проверкой в MAME -(мост `mame-z80`: чтение `_Kid`, потиковые трассы, скриншоты) — этого хватает, -чтобы показать, что конкретный сценарий больше не воспроизводится, но НЕ -хватает, чтобы поймать регрессии в соседней механике. Отсюда список сценариев -ровно в тех формулировках, в которых баги были заведены. Прогон 2026-08-07 -прошёл все комнаты уровней 1 и 2 и ни одного из этих симптомов не показал. - -| # | что проверялось | ожидаемо | -|---|-----------------|----------| -| 1 | комната 22: выпить зелье (0,6), выйти в 16/23 и вернуться | кувшина нет, пузырька над пустым местом нет | -| 2 | то же для комнат 14 (0,5) и 17 (2,3) | так же | -| 3 | комната 15: подобрать меч (2,2), выйти и вернуться | меча нет; кладка на дальней стене НЕ мигает | -| 4 | комната 12: разбить плиты (0,1)/(0,2) и потолок в 16, выйти-вернуться | остаются разбитыми, проём не закрывается | -| 5 | комната 17 из 23: разбить (1,5)/(1,6), выпить зелье (2,3), вернуться | всё остаётся | -| 6 | **после смерти** зайти в те же комнаты | ВСЁ восстановлено (в оригинале смерть = `load_level`) | -| 7 | комната 12: разбег в закрытую решётку (0,9) с полушага | не проходит насквозь; перелистывание экрана штатно (см. BUG-SEAM-DRAW-1) | -| 8 | комната 6: бег справа налево от (0,9), длинный прыжок (0,6)→(0,7) | не влетает внутрь стены | -| 9 | комната 5: с кнопки (0,6) падение на (2,7), присед, вставание | остаётся в комнате 5 (проверено трассой: `fr=111 x=177` → `seq_47` → `fr=15 x=173`) | -| 10 | комната 5: нажать кнопку (0,4) | поднимаются ОБЕ решётки — (0,5) видно на экране, (0,9) проверять из комнаты 1 | -| 11 | страж (комнаты 3, 21) убивает Кида | смерть доигрывается, респавн в стартовой позиции уровня; цикла «убил-воскрес» нет | -| 12 | клавиатура: долгая игра с Shift+стрелка | ↑ и Shift не залипают; осторожный шаг не превращается в бег | -| 17 | **[BUG-KBD-4]** долго играть Shift+стрелка, много тапов подряд | Shift не «отваливается»; если однажды залипнет — снимается первым же нажатием стрелки | -| 18 | **[BUG-RESPAWN-2]** убить стража (комнаты 3/21), умереть, вернуться в ту же комнату | страж снова жив, стоит на исходном тайле, HP полные | - -Отдельно смотрели **регрессии от порта `check_collisions`** — он трогает всю -горизонтальную коллизию: бамп в стену на бегу и в прыжке, осторожный шаг у -стены, проход через ОТКРЫТЫЕ ворота, разворот в проёме решётки, переходы через -швы (старый BUG-SEAM-PINGPONG). Не всплыло. - - -## Обход всех 24 комнат уровня 1 — ЗАКРЫТ прогоном 2026-08-07 - -Инструмент: `#define ROOMNAV` в `roomtest.c` — `+`/`-` (цифровой блок либо -`=`/`-` основного ряда) переключают комнату по номеру (1..24, с обёрткой), -Kid ставится на первый пол. Номер комнаты — полосками в верхнем борте: слева -десятки, справа единицы (`||` `||||` = 24). Инструмент остаётся в сборке: -он же нужен для приёмки уровня 3. - -Таблица заполнялась по ходу отладки и осталась незакрытой на 19 строк из 24; -её закрыл ручной прогон всех комнат уровней 1 и 2 (2026-08-07, крупных багов -нет). Ниже — то, что таблица успела зафиксировать: это не «отчёт по -комнатам», а разбор корней, полезный при похожем симптоме. - -| комната | статус | что не так | -|---------|--------|------------| -| 1 | пофикшено | падающая плита (2,6): правая грань видна через пол (2,7) и перекрывает его переднюю грань — `mob_render` брал ряд соседа из `m->row` (счётчик, уже ушедший на ряд вперёд), а не из координаты | -| 5 | пофикшено | прыжок в решётку: Kid оставался стоять на 6 px ВЫШЕ пола и без приземления-приседания — от `bumped()` (seg004) был портирован только хвост (`seq_47`), не хватало `bumped_floor` (прижатие Y к полу + `seq_46_hardbump` на кадрах прыжка 24/25/40..42/102..106) и `bumped_fall` | -| 9 | сделано | дверь уровня (1,3)-(1,4) рисовалась чёрным проёмом: не был портирован `draw_leveldoor` (seg008:1D29) — створка (слайсы 33 + верх 34), лестница за ней (99/144) и анимация подъёма по кнопке (`animate_leveldoor`, seg007:05F1, modif 0→43). Спрайты 33/34/99/144 добавлены в атлас явным набором (render_room.py дверь не рисует) | -| 12 | пофикшено | вис/подтягивание на кромке loose-плиты: плита рисовалась ПОД Кидом. Не хватало двух кусков `draw_tile`: (а) `draw_loose` кладёт нижнюю грань плиты И в foretable (поверх персонажа), (б) `draw_tile_base` подставляет верх плиты из `loose_fram_left`, а в нашем midtable-оверлее стоял голый `base_id` (у loose он 0). Голова Кида поверх падающей на него плиты — см. «НЕ БАГИ» выше | -| 15 | сделано | меч (2,2) не рисовался: тайл 22 в draw_tile_anim не был портирован. Добавлены отрисовка предмета (chtab_1 id 10/11 на draw_main_y−3), подъём по Shift (check_get_item/get_item/do_pickup: присед → seq_91 pickupsword → меч исчезает с пола) и статус `pop_have_sword` | -| 13, 18, 24 | недостижимы в игре | свойство данных уровня, разбор — «НЕ БАГИ» выше | - - -## Сырые наблюдения (прогон 2026-08-03) → корень - -Одиннадцать формулировок с прогона свелись к шести корням; все шесть закрыты -и проверены в MAME — разборы выше по файлу. - -| # | наблюдение (кратко) | корень | -|---|---------------------|--------| -| 1 | кувшин выпит, а пузырёк рисуется / кувшин возвращается (14, 22, 17) | BUG-LVLSTATE-1 | -| 2 | меч возвращается в 15 / мигает контур кладки | BUG-LVLSTATE-1 | -| 3 | комната 12: пробегает сквозь закрытую решётку (0,9) | BUG-COLL-1 | -| 4 | после respawn плиты остаются разбитыми, зелья выпитыми | BUG-RESPAWN-1 | -| 5 | плита/зелье возвращаются и БЕЗ respawn (12→16, 17, 22) | BUG-LVLSTATE-1 | -| 6 | комната 6: длинный прыжок (0,6)→(0,7) — влёт в стену, респавн | BUG-COLL-1 | -| 7 | залипает ↑ | BUG-KBD-3 | -| 8 | залипает Shift | BUG-KBD-3 / BUG-KBD-4 (поправка — BUG-KBD-5) | -| 9 | комната 5: кнопка (0,4) не открывает решётку (0,5) | BUG-GATE-ANIM-1 | -| 10 | комната 5: с кнопки (0,6) на (2,7), присед — провал в комнату 6 | BUG-STANDUP-1 | -| 11 | страж убил Кида → Кид воскресает на месте и его убивают снова | BUG-DEATH-1 | - -Вторая волна того же прогона (вечер) дала ещё четыре наблюдения — BUG-KBD-4, -BUG-RESPAWN-2, BUG-DRAWORDER-1, BUG-LOOSE-2, — все закрыты, разборы выше. -Тогда же закрыт как невоспроизводящийся BUG-SEAM-DRAW-1 и заведён -BUG-GATE-PASS-1 (он закрыт ниже, 2026-08-09). - ---- - - -## BUG-GUARD-SPLASH-1. Нет «брызг» при попадании по стражу — **ЗАКРЫТ 2026-08-07** - -**Как было заведено (пользователь, 2026-08-07).** При попадании по стражу -должен рисоваться сплеш («звёздочка»), чтобы игрок видел, что удар дошёл до -цели, не переводя взгляд на полосу HP. У Кида такое есть, у стража — нет. - -**Корень.** `draw_hurt_splash` (seg006:16CE) в оригинале зовётся ИЗ ДВУХ -мест: `draw_kid` при `hitp_delta < 0` (seg008:1643) и `draw_guard` при -`guardhp_delta < 0` (seg008:1654). У нас была портирована только первая -половина — `kid_draw_splash()`; для стража вызова не было вовсе, хотя -`guardhp_delta` считался и уже использовался. - -**Фикс** (`pop_guard.c`, `pop_gdraw.c`, `roomtest.c`): - -- флаг `pop_guard_hurt` — симметрия `pop_kid_hurt`: ставится в - `pop_do_delta_hp`, когда `guardhp_delta < 0`, снимается главным циклом - после отрисовки (стража могло не оказаться в отрисованной комнате — - тогда брызги просто пропадают, как в оригинале); -- отрисовка — ВНУТРИ `pop_guard_draw`, между спрайтом стража и клинком: - `draw_guard` кладёт брызги в objtable сразу после стража и ДО клинка, а - порядок записей задаёт порядок рисования. Заодно это решает fore-слой: - `pop_fore_over_char` в конце той же функции накрывает и брызги; -- спрайт — `chtab_5_guard` `obj_id = 1`, то есть **image 1 нашего атласа - стража** (`res752.png`, звезда 28×26): у `add_midtable` аргумент - `obj_id + 1`, а `get_image` вычитает единицу обратно — тот же off-by-one, - что расписан в шапке `pop_pack_guard.py`. У Кида это image 218; -- смещения — три ветки оригинала: кадр 178 (chomped) брызг не даёт вовсе; - 185 и 106..110 (смерть/падение) — `obj_y + 4`; 177 (пики) — сдвиг НАЗАД - на 5; иначе `obj_y − 11` (у Кида 15: `((charid == kid) << 2) + 11`); -- свой прямоугольник heal по страницам (`qx_l/qvalid`) — брызги живут один - кадр, но стирать их обязана та страница, в которую рисовали. - -**Побочно исправлено:** `pop_kid_hurt` взводился при ЛЮБОЙ ненулевой дельте -HP, поэтому зелье здоровья (+1) давало Киду и брызги, и красную вспышку. -И `draw_kid` (seg008:1643), и `flash_if_hurt` (seg003:785) смотрят именно -`hitp_delta < 0` — условие приведено к оригиналу. - -**Проверено в MAME (2026-08-07)**, мост `mame-z80`, уровень 1, комната 21, -живой страж (HP 3/3): - -``` -bp 51BA ← адрес снятия pop_guard_hurt в main: срабатывает ТОЛЬКО - в кадре удара, то есть уже после отрисовки брызг -key k ← чит «убить стража» = guardhp_delta < 0 -→ state=stop PC=0x51BA, guardhp: curr=0 max=3 delta=0 hurt=1 -bp 8BDF (_gfx_set_visible_page) + out ← домотать до флипа страницы -snap ← звезда нарисована поверх стража -``` - -Следующие два кадра (обе страницы дабл-буфера) — чисто, следов брызг нет. - -Приём на будущее: снятие флага сделано **по факту** (`if (pop_guard_hurt) -pop_guard_hurt = 0;`), а не безусловно — это и экономит запись каждый кадр, -и даёт готовую точку останова для отладки в MAME. Условные брейкпоинты -мост не принимает (`bp ADDR cond` вешает плагин), поэтому точка останова, -срабатывающая сама по себе только в нужном кадре, — рабочий обходной путь. - -**Цена:** `_CODE` 24 179 → 24 209 (+30 Б резидента), BANK4 (`pop_gdraw`) -2407 → 3369 (+962 Б, банк занят на 20.6 %, свободно 13 015 Б). - -**Цвет брызг** зависит от палитры стража (слоты `0x90..0x9F`), поэтому он -изменится вместе с [BUG-GUARD-COLOR-1](BUGS_CLOSED.md#bug-guard-color-1) — -отдельной работы не требует. - - ---- - - -## BUG-GATE-PASS-1. Проход сквозь закрывшуюся решётку — **ЗАКРЫТ 2026-08-09** - -**Приёмка:** смоук-прогон уровня 1 (пользователь, 2026-08-09) — с проблемного -`x` Кид больше сквозь решётку не пробегает, пинг-понг у шва не вернулся. - -> **ВОСПРОИЗВЕДЁН И РАЗОБРАН.** Сценарий: уровень 1, комната 8, ряд 0, -> `Kid.x = 170`, лицом ВПРАВО, решётка шва (комната 8, колонка 9) ЗАКРЫТА. -> Разбежаться вправо и НЕ ОТПУСКАТЬ клавишу. Кид проходит сквозь решётку и -> убегает вглубь комнаты 6. -> -> Ниже — новый разбор; всё, что было раньше (гипотеза про x > 205), не -> подтвердилось и оставлено в конце для истории. - -### Корень: история флагов коллизии индексируется НЕ ТАК, как в оригинале - -Сняты две трассы — наша (брейкпоинт на резидентном `kid_tick`) и оригинала -(SDLPoP, собран с `POP_TRACE=1`, печать в `play_kid_frame` + маркеры в -`bumped` и `exit_room`). Стартовая позиция совпала до пикселя, поэтому -сравнение построчное. - -**Оригинал:** - -``` -LEAVE dir=1 x=61 cxl=184 cxr=201 room=6 смена комнаты — ШТАТНАЯ -BUMPED dx=-2 push=-1 x=63 tile=4 col=9 и СРАЗУ бамп о решётку, откат -KID f=15 x=60 act=0 col=-1 room=6 drawn=6 сел за решёткой, стоит -``` - -**Мы:** - -``` -x 170 -> 196 бегом, col 7 -> 8 -> 9, НИ ОДНОГО бампа -x=198 -> room 8 -> 6 -при удержании ВПРАВО доезжает до col 1 комнаты 6 и бежит дальше -``` - -Смена комнаты в этой точке правильна и у нас, и у оригинала: `leave_room` -(seg002:423) вправо блокируют ТОЛЬКО `doortop`-тайлы, решётки в списке нет, а -порог `char_x_right >= 201` совпадает с плоскостью ворот -(`x_bump[9+FIRST_ONSCREEN_COLUMN] + TILE_MIDX + wall_dist_from_left[1]` -= 191 + 10). Оригинал полагается не на запрет ухода, а на бамп СРАЗУ ПОСЛЕ -перехода. - -**Почему бамп есть у них и нет у нас.** `check_collisions` (seg004:0004) -хранит флаги так: - -```c -row_coll_flags_ptr[tile_col] = curr_flags; // индекс — колонка В РАЗРЕШЁННОЙ комнате -row_coll_room_ptr [tile_col] = curr_room; // и рядом — НОМЕР ЭТОЙ КОМНАТЫ -... -if (curr_row_coll_room[column] >= 0 && - prev_coll_room[column] == curr_row_coll_room[column]) { /* только тогда сравниваем */ } -``` - -То есть история привязана к паре **(колонка внутри своей комнаты, комната)**. -Решётка комнаты 8 и до перехода, и после лежит в слоте 9 с `room = 8` — -переход 8->6 историю НЕ рвёт, переход флага 0->1 виден, бамп срабатывает. - -У нас массив `coll_prev/coll_curr` индексируется колонкой ОТНОСИТЕЛЬНО -отрисованной комнаты (−2…11). При смене комнаты все индексы уезжают на 10, -сопоставить прошлый кадр с текущим нечем, и `enter_room` вынужден звать -`pop_coll_invalidate()`; тот ставит «прошлые флаги = уже перекрывал» (3), то -есть ПОДАВЛЯЕТ бамп на кадре входа. А к следующему кадру Кид уже за -плоскостью ворот — перехода 0->1 не будет никогда. - -### Фикс сделан 2026-08-09 (вариант B — сдвиг истории), ЖДЁТ ПРИЁМКИ В MAME - -Рассматривались два пути: - -- **A, дословный порт.** 10 слотов + параллельный массив номера комнаты, - индекс по колонке разрешённой комнаты, бамп только при совпадении номеров; - `pop_coll_invalidate()` тогда не нужен вовсе. **Не взят:** оригиналу - хватает десяти слотов только потому, что он перебирает узкое окно вокруг - Кида, а мы намеренно считаем все четырнадцать колонок (FIX_COLL_FLAGS, - чтобы не оставалось протухших ячеек) — при таком переборе колонки −2/−1 и - 8/9 сядут в одни слоты 8/9. То есть A тянет за собой ещё и сужение окна. -- **B, сдвиг истории (сделано).** Индексацию оставляем свою, но при - БОКОВОМ переходе историю не выбрасываем, а перенумеровываем на 10 слотов: - `check_leave` двигает `x` ровно на ∓140 = 10 тайлов, координата грани - (`pop_x_bump`) едет на те же 140 вместе с габаритом Кида, значит сами - флаги инвариантны — меняется только номер слота. - -Реализация: `pop_coll_shift(int8_t dcol)` в `pop_map.c` сдвигает -`coll_curr/above/below` (не `prev` — его на следующем кадре всё равно -перезапишет `move_coll_to_prev`), освободившиеся слоты = 3 («уже -перекрывал», бампа нет — ровно то, что в оригинале даёт несовпадение номера -комнаты). `enter_room_side` зовёт её сразу после `pop_map_set_edges`: -`side == 0` (ушёл влево) → `+10`, `side == 1` (вправо) → `−10`. Переходы -вверх/вниз и все прочие входы в комнату остаются на полной инвалидации — -там колонки не сдвигаются, но тайлы под ними уже из другой комнаты. - -Расхождение с дословным вариантом A записано в `../docs/impl_diff.md` (D-1). - -**Осторожно:** это сердце коллизии, вокруг которого разбирался -BUG-SEAM-PINGPONG (см. `BUGS_CLOSED.md`). Приёмка: - -1. Комнаты 6 ↔ 8 уровня 1, закрытая решётка, **обе** стороны. -2. Мелким шагом (упереться) И с разбега (не пройти насквозь) — сценарий - выше воспроизводится с `Kid.x = 170`, комната 8, ряд 0, лицом вправо. -3. Пинг-понг у шва не вернулся (экран не перескакивает туда-сюда). -4. `make -C tests-host` — прошли все 5 наборов (2026-08-09). - -**Инструмент для сверки готов:** SDLPoP собран с трассой, включается -`POP_TRACE=1` (правки в `seg000.c` play_kid_frame, `seg004.c` bumped, -`seg002.c` exit_room, чтение переменной — `main.c`). Сборка на macOS: -`make CFLAGS="-std=gnu99 -O2 -I/opt/homebrew/include -Wno-implicit-function-declaration" LIBS="-L/opt/homebrew/lib -lSDL2 -lSDL2_image"` -(в системе нет pkg-config, штатный Makefile без этого не собирается). - ---- - -### История: прежний разбор (гипотеза НЕ подтвердилась) - -**Статус: наблюдался один раз (2026-08-03), воспроизвести не удалось ни -тогда, ни прогоном всех комнат уровня 1 (2026-08-07).** Заведён, чтобы -наблюдение не потерялось; закрывать нельзя — ни как исправленный, ни как -«не баг», пока нет надёжного сценария. Оговорка: BUG-GATEMOD-1, из-за -которого решётка стартовала не в том состоянии, с тех пор закрыт, и -наблюдение могло быть его следствием. - -**Что наблюдалось.** Комната 5: Кид стоял НА тайле решётки (0,9) -и ждал, пока она опустится. После закрытия пошёл вправо — прошёл в комнату 1 -и упал на (1,1). - -**Что уже измерено и в чём загвоздка.** Сразу после наблюдения повторить не -получилось: в том же месте Кид стоит на `x = 196`, `col = 9`, и решётка его -ДЕРЖИТ — то есть штатно. - -Арифметика оригинала объясняет разницу. `is_obstacle` (seg004) ставит -плоскость блокировки в `x_bump[col + FIRST_ONSCREEN_COLUMN] + TILE_MIDX`, для -колонки 9 это **x = 205**. При этом «колонка 9» по `get_tile_div_mod_m7` — -это `x ∈ [191, 205)`. Пока `curr_col == 9`, Кид гарантированно левее -плоскости и обязан блокироваться; чтобы пройти, он должен оказаться **правее -205**, то есть уже на дальней стороне решётки, — и тогда уход вправо законен: -решётка закрылась у него за спиной, в оригинале она блокирует плоскость, а не -весь тайл. - -Отсюда рабочая гипотеза: в момент наблюдения Кид стоял правее 205 (успел зайти -по тайлу дальше, пока решётка была поднята), и поведение штатное. Но повторить -эту позу и снять `x` пока не удалось, поэтому гипотеза НЕ подтверждена. - -**Что снять в следующий раз** (без этих чисел вопрос не закрыть): - -1. `Kid.x` и `Kid.curr_col` в момент, когда решётка уже закрылась, а Кид ещё - стоит на её тайле — до шага вправо; -2. модификатор решётки (openness) комнаты 5, тайл 9 — `can_bump_into_gate()` - считает её препятствием только пока `(modif >> 2) + 6 < char_height`, то - есть пока она опустилась достаточно низко относительно РОСТА кадра; -3. `Kid.x` покадрово на самом шаге вправо — где именно перестал блокировать. - -Быстрый способ снять первое: отладочный стоп-кадр (**1** заморозить, **2** -продолжить), затем чтение `_Kid` из отладчика MAME; подгонка позы по пикселю — -читы `[`/`]`. - -**Возможный корень, если гипотеза не подтвердится.** Проверка идёт по колонке, -которая на шве уже принадлежит СОСЕДНЕЙ комнате (`curr_row_coll_room[]` в -оригинале); у нас межкомнатная коллизия на шве — исторически проблемное место -(ср. закрытый BUG-SEAM-PINGPONG). Второй кандидат — `char_height` в -`can_bump_into_gate()`: если он берётся не от того кадра, решётка может -перестать считаться препятствием раньше времени. - ---- - -## BUG-TORCH-CHOMP-2. Застывший чомпер накрывается пламенем факела — ЗАКРЫТ 2026-08-10 - -Симптом (уровень 4, скриншот пользователя): пока чомпер щёлкает, всё в -порядке; как только его анимация кончается, пламя соседнего факела ложится -ПОВЕРХ челюстей. - -Регрессия от фикса [BUG-TORCH-CHOMP-1](#bug-torch-chomp-1): там пламя -перевели на ЗАПЕКАНИЕ в фон (`GFX_BANK_NORMAL`), чтобы `heal` соседнего -чомпера не съедал огонь. Аргумент «запекать безопасно, кадры пламени -самонакрываются» верен для СОБСТВЕННЫХ пикселей факела, но не для чужой -графики в той же ячейке: пламя рисуется в клетке ПРАВОГО СОСЕДА -(`x = (col+1)*32 + 8`, seg008:560), и если сосед — чомпер, огонь ложится на -его челюсти. Пока trob чомпера жив, `pop_set_redraw(tp, POP_RD_CHOMP, ...)` -возвращает их поверх каждый кадр; как только анимация кончилась и trob -умер, возвращать стало нечем. - -**Как это устроено в оригинале** (и почему у него бага нет). -`animate_torch` (seg007:0241) заканчивается вызовом `set_redraw_anim_right()` -— он метит `redraw_frames_anim` у ПРАВОГО СОСЕДА (seg007:0101). А -`redraw_needed` (seg008:0178) рисует помеченный слой строго в порядке - -``` -draw_tile_anim_topright(); -draw_tile_anim_right(); <- пламя ЛЕВОГО соседа (факела) -draw_tile_anim(); <- СВОЯ графика тайла: челюсти чомпера -``` - -То есть челюсти возвращаются поверх огня **каждый кадр факела**, независимо -от того, жива ли собственная анимация чомпера. У нас пламя рисуется -напрямую из `pop_process_trobs`, минуя механизм пометок, — этой второй -половины не было. - -**Фикс** (`pop_trob.c`): после `pop_torch_draw` метим правого соседа -`pop_set_redraw(tp + 1, POP_RD_CHOMP, 1)`, если там чомпер. Одна страница: -факел анимируется каждый кадр, значит обе страницы дабл-буфера получат свою -перерисовку по очереди. Порядок сходится сам — блок факелов стоит ДО -`pop_redraw_needed`. Код соседа читается в том же префетче кодов тайлов -(`trob_rcode[]`), чтобы не свапать W0 второй раз за кадр. Банк 6: +104 Б. - -**Осознанное расхождение** — оригинал метит соседа БЕЗУСЛОВНО, мы только под -чомпера. Безусловная пометка = перерисовка тайла каждый кадр на каждый -факел; слой `draw_tile_anim` рисует ещё пики, зелье и меч (seg008:0644), -поэтому теоретически «застыть под пламенем» могут и они — заведено открытым -в `BUGS_OPEN.md` (TORCH-ANIM-RIGHT). На уровнях 1–4 такого соседства не -встретилось. - ---- - - -## T-2. Idle-skip — **ЗАКРЫТ 2026-08-08** - -Сделан как шаг 1 задачи [DRAW-COST](TASKS_OPEN.md#draw-cost) (коммит -`a25ce58`), и шире, чем формулировался здесь: пропускается не только Кид, а -ЛЮБОЙ персонаж, у которого с прошлой отрисовки этой страницы дабл-буфера не -изменился ни один вход отрисовки, — включая труп стража и ждущего стража. -Условие «обе страницы уже получили это состояние», которого требовала эта -запись, выполнено само собой: снимок входов хранится ПО СТРАНИЦАМ. - -Замер: комната 1.3 с трупом стража, Кид стоит — 210 % -> 116 % кадрового -периода, ноль вызовов `pop_heal_fast` за кадр. Контракт — шапка -`pop_cdraw.h`, разбор и что делать дальше — `TASKS_OPEN.md#draw-cost`. - -Связь с T-1 сработала как и предсказано: пока Кида не перерисовываем, heal'а -нет, стирать пики нечем. Но T-1 остаётся открытым — трогать пики -безусловно мы всё ещё продолжаем, когда Кид рядом ДВИЖЕТСЯ. - ---- - - -## MIRROR-FG-STALE. Зеркало и снимок комнаты — **НЕ БАГ, закрыт 2026-08-11** - -Заводился как краевой случай от [L4-MIRROR](TASKS_CLOSED.md#l4-mirror): -`place_mirror` (`pop_trob.c`) пишет тайл зеркала в ДАННЫЕ УРОВНЯ -(`pop_level_set_tile`), а коллизия работает со СНИМКОМ комнаты `room_fg`, -который `pop_room_load` делает при входе в комнату. Опасение: если тайл -поставлен, пока игрок УЖЕ в комнате 4, снимок останется старым и зеркало -будет невидимо для коллизии — Кид пробежит сквозь него, тень не родится. - -**Проверка пользователем (2026-08-11), чит ROOMNAV:** нажал кнопку открытия -двери, телепортировался в комнату 4 — **зеркала не было вовсе**; вышел из -комнаты и вошёл обычным путём — зеркало появилось и **непроходимо, кроме -правильного прыжка**, то есть коллизия видит его корректно. - -**Вывод: сценария нет.** Тайл ставится в момент, когда дверь ДОРИСОВАЛА -открытие (`animate_leveldoor`, переход `pop_leveldoor_open` 0/2 → 1, 43 -тика анимации) — телепорт успевает раньше, поэтому «постановки при игроке в -комнате» в этом прогоне не случилось вообще. А при обычном входе комната -рисуется целиком из данных уровня и `room_fg` снимается заново — графика и -коллизия согласованы по построению. - -**Решение пользователя:** телепорт по комнатам — ОТЛАДОЧНЫЙ режим; в реальном -прохождении дверь выхода стоит не в комнате зеркала, игрок физически не может -быть там в момент постановки, и зеркало с тенью появляются всегда штатно. -Чинить нечего. - -**Если симптом всё же всплывёт** (например, появится мод/уровень, где кнопка -и зеркало в одной комнате): лечение — обновлять в `place_mirror` не только -данные уровня, но и живую карту (в `pop_map.c` уже есть внутренние точки -записи `g_fg[tilepos]`, нужна публичная «поставить тайл в текущей комнате»). - ---- - -## SEAM-BUTTON-STALE. Кнопка в шве не меняла вид при нажатии/отжатии — ЗАКРЫТ - -> **Симптом (пользователь, 2026-08-11, уровень 5).** Кнопка нижних ворот -> комнаты 24 стоит в шве: в комнате 11 это (1,9), в комнате 24 — (1,−1). -> «Нажали её из комнаты 11, она проанимировалась — зайдя в 24, она ВСЕГДА -> нарисована нажатой, хотя по статусу отжимается. Не нажимали, вошли в 24 и -> наступили — ворота открываются, а визуально кнопка остаётся отжатой.» - -**Корень.** Change-driven редрой шва (`seam_sig` в `roomtest.c`) сравнивал -`room_modif` колонки 9 соседа. У кнопки `modif` — это ИНДЕКС LINKLOC, -константа уровня; само нажатие живёт в `doorlinks2` -(`get_doorlink_timer`, seg007:0BB6 — младшие 5 бит). Изменение в сигнатуру -не приходило НИКОГДА, редрой не заказывался, и шов оставался нарисованным -таким, каким был на входе в комнату — отсюда оба симптома сразу. - -Видимой разницу делает `draw_tile`: нажатый opener рисуется как ПОЛ -(порт `get_tile_to_draw`, seg008:253 — `pop_room.c:256`), а у пола правая -грань есть, у кнопки её нет. - -**Фикс.** `seam_row_sig()` (roomtest.c) подмешивает в сигнатуру ряда бит -«кнопка нажата» для тайлов `0x0F`/`0x06`. Оригинал шов в этом случае не -перерисовывает вовсе (`get_trob_pos_in_drawn_room` возвращает 30 для чужой -комнаты) — осознанное расхождение, записано как D-2 в -[`../docs/impl_diff.md`](../docs/impl_diff.md). - -Цена: резидент +200 Б (`_CODE` 24 739 → 24 939), куча W2 1795 → 1595 Б. - -**Проверено (пользователь, 2026-08-11):** «кнопка теперь отрисовывается -правильно в обоих случаях» — оба направления — нажать из комнаты 11 и войти в 24; -войти в 24 поверху и наступить на кнопку, стоя в шве. Картинка кнопки -обязана совпадать со статусом ворот. - ---- - ---- - - -## BUG-POTION-STRIPE. Узор паласа пропадал на месте выпитого зелья — ЗАКРЫТ - -**Наблюдение (пользователь, 2026-08-11, уровень 5).** «На стенке паласа есть -узор — синяя в цветочек полоса, она запекается в фон и должна быть всегда. -Но если на её фоне Кид (или Тень) выпивает зелье — полоса пропадает.» -Воспроизведено в комнате 14 (два зелья в ряду 2, колонки 4 и 5): после -каждого глотка узор исчезал в ячейке СПРАВА от выпитого тайла и **не -возвращался даже после выхода из комнаты и повторного входа**. - -**Корень.** У оригинала `curr_room_tiles` — это сама таблица уровня, поэтому -`curr_room_tiles[curr_tilepos] = tiles_1_floor` в `do_pickup` (seg006:1671) -виден всем в тот же миг. У нас `fg` живёт в ДВУХ местах — буфер отрисовки -(`g_fg`) и EMM-страница уровня, — и страницу правил главный цикл ПОЗЖЕ в -кадре, уже после `pop_process_trobs`. - -В этот зазор успевал проснуться trob поднятого зелья: код тайла он читает из -страницы уровня (`pop_level_tile_raw`), там ещё зелье — и `animate_potion` -крутил фазу пузырька поверх только что обнулённого модификатора, -`bubble_next_frame(0)` = **1**. Модификатор оставался единицей навсегда -(`room_modif` персистентен, `room_seen` не даёт переинициализации). - -Дальше срабатывало правило самого оригинала: для ПОЛА в паласе `modif == 1` -означает «узор не рисовать» — `if (num == !!level_type) return;` -(seg008:499, у нас `pop_room.c:322`). Отсюда и «не лечится перезаходом». - -**Как подтверждено (MAME, чтение памяти, а не рассуждение):** буфер тайлов -комнаты (`0xA342`) после подбора — `01` = пол на обеих позициях, всё верно; -а в модификаторах на позициях 24 и 25 стояли `01 01`. - -**Фикс.** `do_pickup` (`pop_map.c`) ставит тайл в странице уровня СРАЗУ -(`pop_level_set_tile`). Тогда trob того же кадра уходит в `default` и -снимается (`type = −1`), модификатор остаётся нулём. Дублирующая запись из -главного цикла убрана. Тем же фиксом лечится МЕЧ: там `animate_sword` -делал `--mod[tp]` из нуля и получал 255. - -**Проверено (пользователь, 2026-08-11):** «узор теперь на месте». - -**Ложный след, для протокола:** первым подозреваемым был `pop_floor_bake` — -после подбора он чистит `bar` шириной 60 px при 64-пиксельной колонке, и -уцелевший хвостик узора в 4 px выглядел как его подпись. Отвергнуто -перезаходом в комнату: полная перерисовка идёт мимо `bake`, а узор всё -равно не появлялся. - ---- - - -## SHADOW-FIGHT-L5. Кид и Тень вставали в боевую стойку в комнате с зельем — ЗАКРЫТ - -**Наблюдение (пользователь, 2026-08-11).** Тень выходит и пьёт зелье -штатно, но если Кид успевал подняться в ряд 0, пока она пьёт: (а) Кид -доставал меч, (б) Тень тоже вставала в боевую стойку и **рисовалась -спрайтами стража**. - -**Корень.** В `pop_check_can_guard_see_kid` (порт `check_can_guard_see_kid`, -seg003:702) первым множителем стояло `Guard.charid != 0`, тогда как оригинал -пишет: - -```c -if ((Guard.charid != charid_1_shadow || current_level == 12) && ... -``` - -То есть ТЕНЬ — боевой персонаж только на 12-м уровне; на 4/5/6 она Кида «не -видит». Это не косметика: `can_guard_see_kid >= 2` разворачивается в оба -симптома через одну ветку `control_standing` (seg005:352) — Кид достаёт меч -сам, а Тень с `charid 1 < CHARID_2_GUARD` и убранным мечом идёт в -диспетчере `pop_control` ТОЙ ЖЕ веткой, что и Кид; с кадра 150 боевые кадры -лежат в атласе стража. - -**Фикс.** Порт условия целиком + возвращён пропущенный множитель -`Guard.direction != dir_56_none` (им оригинал гасит выключенного -персонажа). Константа `SHADOW_FIGHT_LEVEL` в `pop_guard.h`. - -**Проверено (пользователь, 2026-08-11):** тень выпивает и уходит, боя нет. - ---- - - -## BUG-LATTICE-DOORTOP. Чёрная дыра на месте арки у ворот — ЗАКРЫТ - -**Наблюдение (пользователь, 2026-08-11).** Уровень 5, комната 12, тайл -(0,9): чёрный квадрат вместо арки. - -**Корень.** `draw_tile_base` (seg008:622) начинается со СПЕЦСЛУЧАЯ, которого -у нас не было: - -```c -if (tile_left == tiles_26_lattice_down && curr_tile == tiles_12_doortop) { - id = 6; // Lattice + door A - ybottom += 3; -} -``` - -У `doortop` своей базы нет (`base_id == 0`), поэтому рядом с аркой пара -«арка + верх двери» рисуется ОДНИМ спрайтом 6, опущенным на 3 px. Без -ветки тайл не рисовал ничего, кроме полоски `bottom_id` 85 (32×4). - -**Фикс.** Ветка добавлена в `pop_room.c` (draw_tile) И в -`toolchain/render_room.py` — последнее обязательно: `pop_pack_bg.py` -собирает атлас по обходу `render_room`, поэтому без правки скрипта спрайт 6 -просто не попал бы в `pop_env*.atl` (см. memory `pop_atlas_dynamic_ids`). - -**Как найдено — методика, годная для любого «не так нарисовано»:** - -1. `./prince megahit --screenshot-level` в SDLPoP рисует карту ВСЕГО - уровня (комнаты разложены по `roomxs`/`roomys`, клетка 320×192). -2. Вырезать нужную комнату и совместить со снимком MAME перебором смещения - (у нас совпало на `crop(101, 50, +640, +192)`, масштаб 2×1). -3. Разница по порогу яркости даёт карту расхождений: до фикса — белый - прямоугольник ровно в (0,9) и 1892 пикселя, после — 995 (Кид, факел, - статус-полоса, то есть динамика). -4. Что именно рисует оригинал в тайле — трасса `POP_TRACE_BT=1` (печать в - `add_backtable`, `src/seg008.c`, выводит room/row/col/id/размер). Она - сразу показала `id=6 w=32 h=63` рядом со «своим» `id=85 w=32 h=4`. - ---- - - -## BUG-BELOWROW-WALL. Жёлтые треугольники в шахте падения — ЗАКРЫТ - -**Наблюдение (пользователь, 2026-08-11).** Уровень 6, комната 3 (шахта под -комнатой 1): в нижнем ряду под пустыми тайлами (2,4)-(2,6) торчат жёлтые -треугольники. - -**Корень.** `load_rowbelow` (seg008:368) при ОТСУТСТВУЮЩЕЙ комнате снизу -подставляет РАЗНЫЕ кромки: колонкам 1..9 — `tiles_0_empty`, и только левому -краю (тайл из `room_BL`) — `tiles_20_wall`. У нас `pop_room_load` забивал -стеной все одиннадцать позиций `below_fg`, а стена в роли соседа снизу-слева -даёт `topright` стены — её грань и лезла треугольниками. - -**Фикс.** `below_fg[0..9] = 0`, `below_fg[10] = 20`. - -**Проверено:** комната 3 совпала с картой SDLPoP — 300 пикселей расхождения, -все они силуэт Кида. Опасение «не пропадёт ли законный треугольник в (1,3)» -снято тем же сравнением: грань стены в колонке 3 и скос у её основания на -месте у обоих. Оговорка пользователя: эталон брался из `--screenshot-level`, -живая сверка в SDLPoP — потом. - ---- - - -## BUG-BALCONY-RIGHT. Правая половина портала не рисовалась — ЗАКРЫТ - -**Наблюдение (пользователь, 2026-08-11).** Уровень 6, комната 18: «закрытый -портал показывается некорректно» — у арки балкона нет правой половины, -решётка обрывается, справа чёрный провал. - -**Корень — не в движке, а в УПАКОВЩИКЕ АТЛАСОВ.** `id 12` стоял в -`FORE_ENV_IDS` (`toolchain/pop_pack_bg.py`), потому что в `tile_table` он -числится `fore_id` ЗЕЛЬЯ (0x0A). Но зелье рисуется из `chtab_1` -(`add_foretable(id_chtab_1_...)`; у нас `pop_potion_flask`), а в `chtab_6` -под номером 12 лежит ПРАВАЯ ЧАСТЬ АРКИ БАЛКОНА 32x62, и она идёт в -backtable. Из-за списка спрайт уезжал в `pal_fore.atl`, а `pal_env0.atl` -получал дырку: `idx 12: w=0 h=0`. Движок берёт правую грань соседа из -env-атласа — и не рисовал ничего. - -**Как найдено.** Трасса оригинала `POP_TRACE_BT=1` показала в спорном тайле -`BT room=18 r=1 c=7 ch=6 id=12 w=32 h=62`; дальше — чтение каталога -`pal_env0.atl` напрямую (запись 12 пустая) и `pal_fore.atl` (она там). - -**Фикс.** `id 12` убран из `FORE_ENV_IDS`; в env-атласе он занял своё место. -Дифф комнаты с оригиналом 1151 -> 375 (остаток — Кид и пламя). -**Проверено пользователем:** «портал отрисовался корректно». - -**Урок:** список `FORE_ENV_IDS` собирается по `tile_table.fore_id`, но -`fore_id` НЕ значит «спрайт из chtab_6» — у зелья, меча и пламени он -указывает в `chtab_1`. Прежде чем вносить номер в этот список, проверять, -из какой таблицы спрайт берётся. - ---- - - -## BUG-MOB-MULTIROOM. Плита не пролетала несколько комнат — ЗАКРЫТ - -**Наблюдение (пользователь, 2026-08-11).** Уровень 6: плита (1,7) комнаты 6 -должна пролететь НЕСКОЛЬКО комнат и упасть на кнопку, открывающую решётку. -«Плита просто падает и ничего не открывается.» - -**Корень.** У оригинала кусок живёт в списке mobs и спускается ряд за рядом: -`mob_down_a_row` (seg007:1387) из НИЖНЕГО ряда переводит его в комнату снизу -(`y -= 192`, ряд 0), и так сколько угодно комнат. У нас кусок на границе -комнаты ГАСИЛСЯ, а «переход вниз» подменялся сигналом главному циклу — он -искал посадку в комнате снизу через `pop_room_col_landing`. Для колонки 7 -комнаты 7 (сквозная шахта) посадки нет — кусок исчезал. - -**Фикс.** Честный `mob_down_a_row` в `mob_tick_one` (`pop_room.c`); -приземление в НЕ отрисованной комнате сигналит комнатой и тайлом, а щебень -и кнопку ставит главный цикл (порт `loose_land`, seg007:11E8). - -Путь в этом сценарии: комната 6 (1,7) -> сквозная шахта комнаты 7 -> кнопка -(2,7) комнаты 11, а она по LINKLOC открывает ворота (1,9) комнаты 18 — те -самые у портала. **Проверено пользователем:** ворота открываются. - ---- - - -## BUG-MOB-STALE-PAGE. Улетевшая плита оставалась на одной странице — ЗАКРЫТ - -**Наблюдение (пользователь, 2026-08-11).** Сразу после предыдущего фикса: -«в одном из кадров дабл-буфера осталась падающая плита». - -**Корень.** Чистка прошлого кадра куска стояла под гейтом «кусок в -ОТРИСОВАННОЙ комнате»: `if (here && m->prev_y[pg] != MOB_Y_NONE)`. Как -только кусок уходил вниз через `mob_down_a_row`, гейт закрывался — и след, -оставленный им на этой странице, не стирал уже никто. - -**Фикс.** Гейт убран: `prev_y[pg]` — след ИМЕННО НА ЭТОЙ странице, стирать -его надо независимо от того, где кусок сейчас. Плюс `clean = 2` во всех -ветках гашения, чтобы дочищались обе страницы. **Проверено пользователем.** - ---- - - -## BUG-CHAR-STALE-PAGE. Тень застывала на двух страницах в РАЗНЫХ позах - -**Наблюдение (пользователь, 2026-08-11, редкий — пойман дважды).** Уровень -6, комната с тенью: «Тень застряла в двух разных кадрах в разных позициях». - -**Замер в отладчике (состояние было живым).** `Guard` = кадр 15 (stand), -x = 0x51 — то есть персонаж В ПОКОЕ. А слот отрисовки `pop_cd[OPP]`: - -``` -страница 0: x=25 y=85 w=34 h=38 valid=1 <- ЛЕЖАЩАЯ поза -страница 1: x=41 y=78 w=12 h=41 valid=1 <- стойка -``` - -**Корень.** В `pop_char_draw` (`pop_cdraw.c`) прямоугольник и `valid[dp]` -пишутся ВНУТРИ `if (w && h)`, а снимок состояния для пропуска перерисовки -(`cd_sig_make`) брался БЕЗУСЛОВНО, в самом конце функции. Если спрайт -кадра нулевой (пустая запись атласа), блок рисования пропускался — `x/y/w/h` -и `valid` оставались от ПРОШЛОГО кадра этой страницы, а сигнатура -обновлялась на текущее состояние. Дальше `cd_quiet` видел `valid = 1` и -совпавшую сигнатуру, то есть считал «на странице нарисовано ровно то, что -надо», и слот залипал навсегда: `heal` не звался, старый спрайт оставался. - -**Фикс.** `cd_sig_make` вызывается только когда кадр реально рисовали -(`if (w && h)`). Без снимка слот не будет «тихим», и следующий кадр -начнётся с `heal` — старое сотрётся само. - -**ОСТАЁТСЯ ОТКРЫТЫМ (в BUGS_OPEN):** почему спрайт кадра оказался нулевым. -Фикс убирает залипание, но кадр, для которого атлас отдал 0x0, всё равно -не нарисуется. Подозрение — конкуренция за окно W0 между `atlas_image` и -чтением данных уровня; проверять трассировкой маппинга. - ---- - - -## BUG-CHEAT-IMM-1. Кид залипал в боевой стойке без меча — ЗАКРЫТ 2026-08-17 - -**Наблюдение (пользователь, уровень 2, комната 4).** Кида с мечом сталкивают -ударами с ряда 1 на ряд 2; страж падает следом и оказывается у него за -спиной. Кид встаёт в боевую стойку, но: **меча в руке нет** (не -отрисовывается), и **к сопернику он не разворачивается** — на клавиши не -реагирует вовсе. «Иногда Кид успевает развернуться, а вот сейчас не успел». - -**Два симптома — один отказ.** Диспетчер `control()` (`pop_ctrl.c`, -порт seg005:252) уходит в `control_with_sword` только при -`Char.sword == SWORD_2_DRAWN`. При `sword == 0` управление проваливается в -обычные ветки, а среди них для кадров стойки с мечом (158/170/171) нет ни -одной — каждый кадр не делается НИЧЕГО. Отсюда и «не разворачивается»: -разворот к сопернику за спиной (`SEQ_60_TURN_WITH_SWORD` при -`char_opp_dist() < -4`) живёт внутри `control_with_sword`. - -**Корень — наш чит бессмертия, а не механика боя.** `hurt_by_sword` -(`guards.c`, порт seg002) перехватывался читом в самом начале и -**безусловно**, не глядя на меч, ставил `SEQ_74_HIT_BY_SWORD`: - -```c -if (Char.charid == CHARID_0_KID && pop_immortal) { - pop_char_set_seq(SEQ_74_HIT_BY_SWORD); /* ← без проверки sword */ -``` - -А `seq_74` — это анимация «получил удар **В БОЕВОЙ СТОЙКЕ**», её кадры -(150..179) все с мечом. В оригинале в неё попадают ТОЛЬКО из ветки -`sword == 2`; безоружного там убивают (seg002, дословный комментарий: -*«Being hurt when not in fighting pose means death»*, `take_hp(100)`). -Чит подменял эту смерть выживанием и оставлял Кида в кадрах с мечом при -`sword == 0` — состояние, которого в оригинале не существует и для которого -у диспетчера нет ветки. - -**Как доказано** (важно для метода — гипотез было три, и две неверные): - -1. Кодоген `draw_sword` проверен по `bank5_pop_ctrl.asm`: `ld hl,#_Char+12 / - ld (hl),#0x02` — store меча на месте, версия «SDCC потерял запись» - (ср. [[sdcc_z80_cmp_store_a_bug]]) отпала. -2. Watchpoint на `Kid.sword` в MAME: `0 → 2` при доставании и `2 → 0` в - `start_fall` проходят штатно (`start_fall` меч гасит и в оригинале, - seg006:1102). -3. **Решающее:** с ВЫКЛЮЧЕННЫМ читом (`pop_immortal = 0`) тот же сценарий - даёт корректную смерть — то есть порт совпадает с оригиналом, а баг - живёт только под читом. - -Грабли метода, стоившие времени: ловушки прошлой сессии печатали -`b@0x992A` **без** префикса `0x10000`, то есть мимо логического вида Z80 — -`charid/frame/dir` в той трассе были мусором, отсюда ложный вывод «записей -в `sword` нет вовсе». И ловушка на `Char.sword` бесполезна: `Char` — -переключаемое окно, у стража меч вынут, поэтому `2 → 0` там происходит на -каждом переключении окна. Ещё одно: `pop_char_set_seq` пишет -`ld ($992D),hl`, запись 16-битная — байтовое условие `wpdata==0xDC` не -совпало бы никогда. - -**Фикс** (решение пользователя: «повторяем поведение оригинала, наш иммортал -работает только во время боя, с мечом»). Спецветка чита удалена целиком — -она оказалась **избыточной**: под бессмертием `pop_take_hp(1)` и так -возвращает 0, и обычный путь сам приводит к `seq_74` с сохранённым мечом. -Осталось запретить читу действовать в безоружной ветке: - -```c -if (Char.sword != SWORD_2_DRAWN) { - uint8_t imm = pop_immortal; - pop_immortal = 0; /* чит не действует вне боевой стойки */ - pop_take_hp(100); - pop_immortal = imm; - pop_char_set_seq(SEQ_85_STABBED_TO_DEATH); -} -``` - -Глушится именно вызов, а не `pop_take_hp`: тот общий с физикой (падения, -пики, чомперы), и там бессмертие обязано работать при любом положении меча. - -**Подтверждение.** Пользователь, 2026-08-17: «всё сработало как надо», -проверено обоими концами — «во время боя удары, пропускаемые Кидом, урон не -приносят; если Кид без меча — смерть с одного удара». То есть чит в бою -работает как работал, а вне боевой стойки поведение снова совпадает с -оригиналом. -Взведена регресс-ловушка на сам инвариант: Киду пишется кадр стойки -(158/170/171), а `Char.sword != 2`. Условие смотрит на `Char`, а НЕ на -`Kid`: `ldir` копирует байт кадра (0) раньше байта меча (12), поэтому по -`Kid` ловушка слепа ровно в тот тик, когда меч теряется, и ложно срабатывает -на каждом законном доставании. - ---- - - -## LEVELDOOR-PALACE-CLIP. Кид скрывался за кромкой дворцового портала раньше времени — ЗАКРЫТ 2026-08-17 - -**Наблюдение (пользователь, уровень 4, обход после оптимизации фаз).** -Порталы, через которые Кид входит на уровень и уходит на следующий, в -подземелье и во дворце РАЗНОЙ ширины — дворцовый шире. В анимации ухода с -уровня 4 на 5-й Кид убегает по лестнице, и его контур обрезается не правой -гранью портала, а раньше, «как будто он прячется проходом». Диагноз -пользователя сразу верный: взята ширина подземного портала. - -**Корень.** `draw_leveldoor` (`pop_room.c`, порт seg008:1D29) считал правую -кромку проёма без дворцовой поправки: - -```c -pop_leveldoor_right = xh * 8 + 48; -``` - -В оригинале строкой ниже стоит (seg008:1429): - -```c -leveldoor_right = (draw_xh<<3)+48; -if (custom->tbl_level_type[current_level]) leveldoor_right += 8; -``` - -Это значение читает `clip_char` (у нас `pop_map.c:2668`) как правую границу -клипа персонажа — отсюда ранняя обрезка. - -Расхождение было **осознанным и отложенным**: в коде стоял комментарий -«+8 у palace-уровней (tbl_level_type) — на уровне 1 не применяется». На -момент написания дворцовых уровней в порту не было, и условие не дописали. -`tbl_level_type[4] = 1` (`pop_level_cold.c:52`), то есть уровень 4 — -дворцовый, и на нём это наконец проявилось. - -**Фикс.** `if (pop_palace) pop_leveldoor_right += 8;` Флаг `pop_palace` -выставляет `pop_bg_load(set)` из того же `tbl_level_type`, который читает -оригинал, так что эквивалент дословный и рассинхронизироваться не может. - -**Найдено попутно, НЕ починено:** -[LEVELDOOR-STARTROOM-WIPE](BUGS_OPEN.md#leveldoor-startroom-wipe) — в той же -функции оригинал в СТАРТОВОЙ комнате кладёт затирающий прямоугольник вместо -лестницы (и там своя дворцовая/подземная разница 48/39 и сдвиг 2 px), а мы -рисуем марш 144 безусловно. Заведено отдельным багом. - ---- - - -## SHADOW-STALE-FRAME. Тень мигала между двумя РАЗНЫМИ позами на страницах дабл-буфера — ЗАКРЫТ 2026-08-18 - -**Наблюдение (пользователь, 2026-08-18).** Уровень 6, комната 1 (Кид и через -большой провал — тень). «У Тени на двух экранах отрисованы разные позиции — -одна стоящая, одна бегущая; увидел сразу, как вошёл в комнату. Повторяется -нечасто, но очень неприятно». Гипотеза пользователя — «в один из экранов -попала случайная позиция Тени» — оказалась дословно верной. - -**Улика (живое состояние, снято через мост MAME до перезапуска).** Слот -соперника `pop_cd[POP_CH_OPP]`: - -``` -стр.0: x=41 y=78 w=12 h=41 valid=1 <- кадр 15 «стойка» -стр.1: x=25 y=85 w=34 h=38 valid=1 <- ЧТО-ТО ДРУГОЕ -cd_sig[OPP][0] == cd_sig[OPP][1] == { frame=15, x=81, y=118, dir=0, charid=1 } -``` - -Обе страницы «тихие» (снимок совпал с живым `Guard`), поэтому ни одна больше -не перерисовывается — мигание навсегда. Прямоугольник страницы 1 -восстанавливается однозначно: `bx = scr_x(obj_x) − w` и `top = obj_y − h + 1` -при `Guard.x = 81`, `Guard.y = 118`, `dir = 0` дают `dx = 3`, `dy = 4`, -`flags ≥ 0x80` и картинку 34×38. В таблице кадров ровно одна такая запись — -**`frame_tbl_guard[36]`, то есть кадр 185 «страж мёртв»** (`image = 33`, -`dx = 3`, `dy = 4`, `flags = 0xC9`; 34×38 — это размер `image 33` в атласе -КИДА, что сходится: набор спрайтов выбирается по `Guard.frame = 15`, а -`image` приходил из кэша). - -**Корень.** Оригинал грузит кадр В САМОЙ ОТРИСОВКЕ: `add_kid_to_objtable` и -`add_guard_to_objtable` (seg008:22F0/2324) первым делом после -`loadkid`/`loadshad` зовут `load_fram_det_col()`. У нас `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`, и кэш не трогается -вообще, сколько бы комнат Кид ни прошёл. - -Само по себе это моргнуло бы один кадр. Смертельным его делает **пропуск -неизменившегося кадра** (DRAW-COST): снимок `cd_sig` пишется по -`Guard.frame`, то есть страница запоминает «нарисован кадр 15», хотя на ней -чужой спрайт. Тень дальше стоит, снимок совпадает — страница не -перерисовывается НИКОГДА. - -**Фикс.** `pop_char_draw` зовёт `pop_load_frame()` сразу после -`pop_loadkid`/`pop_loadshad` — там же, где это делает оригинал. После этой -строки `image` однозначно определяется парой `(charid, frame)`, то есть -снимок `cd_sig` снова описывает ровно то, что нарисовано, и расхождение -«пиксели против снимка» становится невозможным независимо от того, кто и как -испортил кэш. Цена: +3 байта в банке 4, кадровый бюджет не трогает -(отрисовка и так пропускается у «тихого» слота). - -Заодно приведена к оригиналу ветка «кадра нет в таблице»: `load_frame` -(`pop_kid.c`) ставила `cur_frame.image = 255` и выходила, НЕ обновив кэш -владельца, — то есть вместо «не рисовать» рисовалась прошлая картинка. -Оригинал кладёт туда `blank_frame {255,0,0,0,0}` (`get_frame_internal`, -seg006:507). Выход из функции теперь один на обе ветки: +46 байт резидента. - -**Проверка (MAME, уровень 6 комната 1).** Брейкпоинт на трамплине -`___sdcc_bcall_ehl` с условием `hl == _pop_char_draw` и действием -«записать в `pop_gframe` кадр 185 и продолжить» — то есть кэш травится -ровно перед КАЖДОЙ отрисовкой персонажа. Слот при принудительной -перерисовке (`valid[0] = valid[1] = 0`) остаётся `12×41` на обеих -страницах, `pop_gframe` после кадра — снова кадр 15. До фикса та же -подмена дала бы `34×38 @ (25,85)` — ровно числа из улики. - -**Что осталось невыясненным.** Не удалось воспроизвести САМ МОМЕНТ порчи: -вход в комнату с заведомо испорченным кэшем (подмена `pop_gframe` в соседней -комнате + возврат читом обхода) самолечится — метка «фон трогали» от -перерисовки комнаты стоит на обеих страницах, и слот честно перерисовывается -следующим кадром. Значит порча случилась в кадре, где фон НЕ трогали, и -конкретный путь остался неизвестен. На вывод это не влияет: фикс закрывает -класс целиком, а не найденный путь. Ловушка на будущее, если симптом -всплывёт снова: `wp <адрес pop_cd[OPP].w>,4,w` — сработает на первой же -записи «чужой» ширины. - ---- - - -## BLUELINE-NOBLUE. Кусок синего узора на стене там, где оригинал его не рисует — ЗАКРЫТ 2026-08-18 - -**Наблюдение (пользователь, 2026-08-18).** Уровень 6, комната 1, тайл (2,3): -у правой кромки левого блока висит небольшой кусочек тёмно-синего узора, -которого в оригинале нет. Диагноз пользователя («первоначальная отрисовка -фона, фикс не должен повлиять на скорость») подтвердился полностью. - -**Корень.** Полоску рисует `draw_tile_right` (`pop_room.c`, порт -seg008:0666) — её кладёт ПРАВЫЙ сосед стены: - -```c -case tiles_20_wall: - if (tbl_level_type[current_level] && (modifier_left & 0x80) == 0) - add_backtable(chtab_6, 84 /*wall stripe*/, draw_xh + 3, 0, draw_main_y - 27, ...); -``` - -Проверка `& 0x80` у нас была, а вот значения, которое она проверяет, — нет. -Модификатор стены В ФАЙЛЕ уровня и В РАНТАЙМЕ у оригинала разные: -`load_alter_mod` (seg008:1216) переводит их при загрузке комнаты — -`*curr_tile_modif <<= 7`, то есть единственный бит уровня («no blue», -авторский флаг «не рисовать полоску на моей правой кромке») переезжает в -СТАРШИЙ. Наш порт `load_alter_mod` (`pop_trob.c`) ветку стен пропускал -НАМЕРЕННО, и в комментарии это было записано: связи кладки мы считаем по -типам соседей (`pop_wall_modifier`), сохранённый модификатор стены «не -читается вовсе». - -Ошибка в слове «вовсе»: у модификатора стены ДВА независимых смысла, и -из соседей выводится только один. `noblue` проставлен вручную в данных -уровня и не выводится ниоткуда. В комнате 1 уровня 6 стена (2,2) имеет в -файле модификатор 1, то есть ровно «без полоски»; без перевода отрисовка -видела сырую единицу, `(1 & 0x80) == 0` проходило — и полоска вылезала. - -**Фикс.** `case TILE_WALL: *m = (uint8_t)(*m << 7);` в порте -`load_alter_mod`. Перевод одноразовый, при первом входе в комнату -(`room_seen`), — кадровый бюджет не трогает вообще; +24 байта в банке 6, -резидент не изменился. - -**Проверка (MAME, уровень 6 комната 1).** Попиксельный диф скриншотов до и -после: изменения ровно в боксе `(344,199)-(358,206)`, 38 пикселей — сам -артефакт и ничего кроме. На подземных уровнях изменений нет по построению: -полоска целиком под `if (PALACE)`. - ---- - - -## SPIKE-BAKED. Выдвинутые пики консервировались в фон и оставались навсегда - -**Наблюдение (пользователь, 2026-08-18).** Уровень 7, комната 19. Пики стоят -в `(0,6)` и из-за изометрии частью выходят в ячейку `(0,7)`. Выдвигаются -корректно все; **прячутся корректно только те, что в `(0,7)`**, а в `(0,6)` -остаются два вида мусора: куски остриёв на фоне дальней стены и белые точки в -дырах пола. Пользователь сразу указал и на класс («первоначальная отрисовка -фона, фикс не должен повлиять на скорость»), и на причину («в `(0,5)` не пол, -а кнопка») — оба попадания верные. - -**Артефакты, снятые в MAME на живом кадре с мусором.** - -| что | значение | -|-----|----------| -| `pop_t_bg[6]` (живой modif пики) | `0x00` — пики УБРАНЫ | -| `pop_t_fg[0..9]` | `03 04 13 0F 01 06 02 04 01 03` → `(0,5)` = `0x06` closer-кнопка, `(0,6)` = `0x02` spike | -| мусор на экране | x 198..215, y 63..88 | - -Модификатор нулевой, а пиксели пик есть — значит рисует их не живое -состояние. И `pop_spike_redraw` каждый кадр делает `pop_heal_off(192, 62, -64, 40)` — ровно поверх этого прямоугольника. Мусор переживает СВОЙ ЖЕ heal, -а heal восстанавливает ОЗУ-копию фона: пики в ней и лежат. - -**Корень.** Кнопка при смене состояния метится `POP_RD_FLOOR`, а это -`pop_floor_bake(0,5)` — ЗАПЕЧКА, то есть рисование в банке, который пишет и в -ОЗУ-копию. Запечка перерисовывает свой тайл **и правого соседа**: - -```c -pop_t_bake_rest = 1; -draw_tile(row, col); -if (col + 1 < 10) draw_tile(row, col + 1); /* <- это пики (0,6) */ -``` - -`pop_t_bake_rest` для того и заведён — «в фон кладём состояние ПОКОЯ», — но -читал его ТОЛЬКО `pop_loose_frame`. Кадр пик считался прямо по месту, -`(m & 0x80) ? 5 : m`, в ЧЕТЫРЁХ слоях, и ни один флага не спрашивал. Так -выдвинутая пика попадала в ОЗУ-копию, и дальше heal возвращал её вечно. - -Всё сходится численно. Окно клипа запечки — `x 160..219` (ширина 60 от -кнопки), спрайт пики — `x 192..222`, `y 62..88`. Пересечение `x 192..219` — -это и есть наблюдаемые 198..215; а `(0,7)` начинается с 224, за окном -запечки, — потому там и чисто. Правая часть пики живёт только в видео-ОЗУ и -честно стирается. - -**Фикс (`pop_tile.c`).** Порт `get_spike_frame` (SDLPoP seg008:08A0), которого -у нас не было, — плюс наша платформенная оговорка про запечку: - -```c -uint8_t pop_spike_frame(uint8_t m) -{ - if (pop_t_bake_rest) return 0; - return (uint8_t)((m & 0x80) ? 5 : m); -} -``` - -Все четыре слоя (`draw_tile_anim`, `draw_tile_anim_right` соседа, fore-проход, -mid-оверлей) переведены на него. Заодно `pop_chomp_pose` тоже стал отдавать -позу покоя в запечке: у чомпера ровно та же схема, а рядом с кнопкой он -встретится так же легко. - -Одна точка вместо четырёх лечит ВСЕ пять путей запечки сразу, включая -`pop_room_draw` — вход в комнату с выдвинутыми пиками тоже консервировал бы их. - -**Цена.** Резидент +28 Б, банки без изменений; в кадре — один `call` на слой, -и только на тайлах-пиках. Скорость не затронута, как и предполагал -пользователь. - -**Проверка.** Пользователь прошёл сценарий вручную на пересобранном билде — -мусора нет. - -**Урок (записан в `pop_tile.h` над `pop_t_bake_rest`).** Флаг «сейчас -запечка» обязана спрашивать САМА функция «кадр по модификатору», а не -вызывающий слой: слоёв отрисовки у тайла четыре, и забыть один слишком легко. -Для новой анимированной ловушки это теперь обязательный пункт. - ---- - - -## DIED-ON-BUTTON. Смерть на кнопке не ломала её: решётка закрывалась обратно - -**Наблюдение (пользователь, 2026-08-18).** Уровень 7 начинается падением, и -если не зацепиться за кромку, Кид разбивается на `(2,1)` комнаты 3 — а там -кнопка открытия решётки. В оригинале решётка после этого открыта НАСОВСЕМ, -у нас отжималась. - -Баг был заведён ещё при закрытии -[BUG-LOOSE-BUTTON-1](#bug-loose-button-1) как «`died_on_button` (seg007:776) -не портирован», но оказался ДВОЙНЫМ — до недостающей функции ещё и не было -пути. - -### Половина 1: физика трупа выключена целиком - -```c -void pop_phys_tick(void) __banked -{ - if (pop_kid_dead) return; /* <- вся цепочка, включая check_press */ -``` - -У оригинала `play_kid_frame` (seg000:1231) гейт ровно один — `Char.room != 0`; -весь список крутится и на трупе (по «жив/мёртв» разведены `check_spiked`/ -`check_chomped_kid`, и то через `resurrect_time`). Ранний выход выглядел -безобидной экономией — «труп не шевелится», — но `check_press` на мёртвом -персонаже делает СОВСЕМ ДРУГОЕ, чем на живом, и именно это выключалось. - -Отсюда наблюдаемое: кнопка получала РОВНО ОДНО нажатие — в кадре самой -смерти, потому что `pop_kid_dead` ставит `land()` уже ВНУТРИ цепочки, после -раннего возврата. Решётка приоткрывалась и закрывалась. - -Снято в MAME до фикса: Кид `frame 185, action 5 (bumped), alive 0`, тайл -`(2,1)` = `0x0F` opener с модификатором `0x12`, openness решётки ползёт -вниз 146 → 112. Кадр смерти при этом проходит все проверки `check_press` -штатно (`action == ACT_BUMPED`, флаги кадра 185 = `0x49`, бит -`FRAME_NEEDS_FLOOR` есть) — то есть путь был закрыт ровно одним `return`. - -«Труп не шевелится» держится и без него, тем же, чем в оригинале: кадр -смерти не двигается сам, а уход из комнаты и сотрясение отсечены ВНУТРИ -`kid_phys` (там свой `if (pop_kid_dead) return;` перед `pop_check_knock`). - -### Половина 2: сам `died_on_button` - -```c -if (Char.alive < 0) pop_trigger_button(...); /* жив — нажатие на 5 кадров */ -else died_on_button(...); /* мёртв — кнопка СЛОМАНА */ -``` - -Открывалка превращается в обычный пол с нулевым модификатором, а связь -дёргается типом «щебень» (`TILE_DEBRIS`) — у `trigger_gate` это отдельная -ветка «открыть насовсем». Любая другая кнопка становится заклиненной -(`TILE_STUCK` = 5). Тайл пишем и в страницу уровня (переживает выход из -комнаты), и в живую `g_fg`, с пометкой на перерисовку своей ячейки и правой. - -Инфраструктура была готова давно: `TILE_DEBRIS` и ветка «насовсем» в -`trigger_gate` написаны ещё тогда, с комментарием-ссылкой именно на -`died_on_button` — из-за чего механизм и казался реализованным. Опасение -старой записи, что нужен «тайл заклиненной кнопки в атласе, а его там нет», -не подтвердилось: `pop_tile_table[5]` (stuck floor) заполнен и уже -рисуется — на него подставляется НАЖАТАЯ кнопка-закрывалка. - -### Половина 3 (вылезла сразу): таймер связи переживал рестарт уровня - -Пользователь поймал на первом же прогоне: **до первого падения кнопка -рисуется правильно, после — выглядит нажатой с самого начала уровня**, хотя -логически не нажата. - -`died_on_button` ставит таймер связи в 5 и заводит trob кнопки — но тайл к -этому моменту УЖЕ пол, и trob умирает в первом же обходе -(`default: type = -1`), ни разу не уменьшив таймер. У оригинала ровно то же -самое, только там `load_level()` перечитывает файл ЦЕЛИКОМ, а наш -`pop_level_reset_tiles` восстанавливал одну foretable. Остаток `dl2[18] = 5` -переживал респавн, а `pop_tile_code_drawn` рисует кнопку нажатой, пока -таймер > 1. - -Фикс — восстанавливать в рестарте и LINKMAP. Отдельная эталонная копия не -нужна: в странице уровня LINKMAP остаётся нетронутой, игра правит только -ОЗУ-копию `pop_dl2`. - -**Проверка в MAME (полный цикл).** - -| момент | тайл `(2,1)` | `dl2[18]` | `pop_kid_dead` | решётка `(2,6)` | -|--------|--------------|-----------|----------------|-----------------| -| труп на кнопке | `01` floor | 5 | 1 | `FF` открыта навсегда | -| после респавна | `0F` opener | **`00`** | 0 | `00` закрыта | -| второе падение | `01` floor | 5 | 1 | открывается заново | - -Плюс попиксельная сверка кадров: решётка, до фикса стоявшая опущенной, -поднята. Цена: резидент без изменений, банк 3 +193 Б, банк 8 +22 Б. - -**Осталось невыясненным.** На СТАРОМ поведении openness решётки замирал на -112 и дальше не убывал, хотя `animate_door` (совпадает с seg007:0522) обязан -досчитать до 0 и снять trob. С фиксом сцена так не воспроизводится -(решётка уходит в `FF`), проверить нечем; если всплывёт где-то ещё — это -отдельный баг, к `died_on_button` отношения не имеющий. - ---- - - -## GUARD-RESPAWN-COL0. Страж при возврате в комнату телепортировался в колонку 0 и падал - -**Наблюдение (пользователь, 2026-08-18).** Уровень 8, комната 24. Первый -вход из левой комнаты — страж около `(0,7)`, стоит. Кид уходит вправо и -возвращается — страж уже «в `(0,-1)`». Ещё раз туда-обратно — страж на -`(0,0)`, идёт биться, падает на два ряда и разбивается. Второй сценарий: -вход из комнаты 23, страж на `(0,7)` неподвижен; Кид уходит влево и -возвращается — страж падает на `(2,1)` и разбивается. - -Формулировка пользователя — «страж, если не может биться с Кидом, не должен -менять позиции; иначе он будет ждать Кида прямо на входе, и тот не успеет -достать меч» — совпадает с тем, что гарантирует оригинал. - -**Как это устроено в оригинале.** Позиция стража живёт в ТРЁХ полях данных -уровня, и колонки среди них нет: - -- `pos_guards` (seg003:0913), один раз сразу после `load_level`: - `guards_x[room] = x_bump[(tile % 10) + FIRST_ONSCREEN_COLUMN] + TILE_SIZEX`. - То, что лежит в `guards_x` В ФАЙЛЕ, оригинал ВЫБРАСЫВАЕТ (в комнате 24 - уровня 8 там 255); -- `leave_guard` (seg002:02F5) при уходе Кида: - `guards_tile = get_tilepos(0, row)` — колонка обнуляется НАМЕРЕННО, тайл - несёт только РЯД, — и `guards_x = Guard.x`, то есть настоящая позиция; -- `enter_guard` (seg002:0112) при возврате: `curr_row = tile / 10`, - `y = y_land[row + 1]`, **`x = guards_x`**, `curr_col = f(x)`. - -Носитель колонки — `guards_x`, и только она. - -**Что было у нас.** `pop_guard_leave` тайл обнуляла верно, а -`pop_guard_enter` считала X из `tile % 10` — из колонки, которой там уже нет. -Запомненную X брали ТОЛЬКО у трупа. Значит после ПЕРВОГО же выхода из -комнаты живой страж возвращался в колонку 0. - -Численно: тайл стража комнаты 24 = 6 (ряд 0, колонка 6), ряд 0 = -`13 00 00 00 0F 01 01 01 01 01`, то есть **колонки 1..3 ПУСТЫЕ**, а в -колонке 0 факел. Телепорт давал `x = x_bump[5] + 14 = 72`, а 72 по таблице -деления — это колонка **1**: страж оказывался над дырой, падал с ряда 0 на -ряд 2 (щебень в `(2,1)`) — два ряда, смерть. Оба сценария пользователя -ложатся сюда ровно, включая «упал на 2,1». - -**Фикс.** Два места, оба — приведение к оригиналу: - -- `pop_gstate_init` — порт `pos_guards`: при загрузке уровня `GS_X` - считается из колонки тайла, а не читается из файла; -- `pop_guard_enter` — `Guard.x` берётся из запомненной ВСЕГДА, не только у - трупа; колонка, как и раньше, считается из X. - -**Проверка в MAME** (уровень 8, комната 24, три входа через `enter_room`): - -| | room | x | y | col | row | alive | -|---|---|---|---|---|---|---| -| первый вход | 24 | 156 | 55 | 7 | 0 | жив | -| возврат | 24 | 156 | 55 | 7 | 0 | жив | -| второй возврат | 24 | 156 | 55 | 7 | 0 | жив | - -Позиция не меняется, страж стоит на полу. Host-тесты — 5106 проверок без -расхождений. Резидент без изменений, банк 8 −15 Б. - -**Что осталось невыясненным.** Наблюдение «страж был в `(0,-1)`» отдельно -не воспроизведено: телепорт даёт колонку 0/1, а −1 требует `x < 65`. -Вероятнее всего это кадр уже НАЧАВШЕГОСЯ падения (при падении X сносит), но -доказательства нет. Если `(0,-1)` появится на исправленном билде — это -отдельный баг. - ---- - - -## SEAM-FIGHT-FLICKER. Бой у шва: комната перерисовывалась то одна, то другая - -**Наблюдение (пользователь, 2026-08-18).** Уровень 8, шов комнат 24/18: во -время схватки со стражем экран постоянно переключался между комнатами — -«драться очень неудобно». Отдельно пользователь заметил, что уходит Кид, -даже когда просто держит «вверх» (защита), и предположил верную причину: -стойка защиты не одна, Кид периодически проходит через обычную. - -**Корень.** `leave_room` (seg002:0490) запрещает смену комнаты не только на -развороте, подъёме-с-зацепа (135..149) и вставании из приседа (110..119), но -и на ВСЕЙ боевой анимации — кадры **150..162** и **166..168**. У нас были -только первые три условия. - -Разрешены при этом 163..165, 169 (`begin_block`) и 170/171 (`stand with -sword`) — то есть уйти можно, но только на «легальном» кадре. Цикл защиты -(seqtbl: `readyblock` = 169 -> `blocking` = 150, петля) как раз проходит -через 169, а между атаками Кид возвращается в 158/170 — отсюда и ощущение -«стоять в защите нельзя». - -Побочный эффект известен и в оригинале эксплуатируется — **Trick 35, -«retreat without leaving the room»**: определённым ритмом «назад» кадр 170 не -наступает никогда, и Кид пятится из комнаты, не переключая её. В SDLPoP это -чинит `FIX_RETREAT_WITHOUT_LEAVING_ROOM`, по умолчанию ВЫКЛЮЧЕННЫЙ, — мы -портируем оригинал. - -**Сторона стража проверена отдельно, расхождений нет.** `play_guard_frame` -(seg000:0F48) ухода из комнаты не содержит вовсе (ни `leave_room`, ни -`check_leave_below`), физика загейчена окном `Char.x ∈ [44, 211)`. Комнату -страж меняет только через `follow_guard` (у нас `pop_guard_follow`) и -`check_guard_fallout` (у нас `pop_guard_fallout`) — оба портированы. - -**Проверка: покадровая сверка с SDLPoP.** Пользователь добавил в SDLPoP -вывод в конце `add_kid_to_objtable` (seg008:1679); мы навесили такой же -(`pop_dbg_kidobj`, см. ниже). Окно перехода совпало КАДР В КАДР: - -| | SDLPoP | наша | -|---|---|---| -| | `tilepos=10 frame=161 col=-1` | `tilepos=10 frame=161 col=-1` | -| | `tilepos=10 frame=160 col=-1` | `tilepos=10 frame=160 col=-1` | -| | `tilepos=10 frame=157 col=-2` | `tilepos=10 frame=157 col=-2` | -| | `tilepos=10 frame=158 col=-2` | `tilepos=10 frame=158 col=-2` | -| **смена** | `tilepos=18 frame=169 col=9` | `tilepos=18 frame=169 col=9` | - -Прогон уровня 1 комнаты 3 (1779 кадров): за весь бой комната сменилась -ОДИН раз; 34 боевых кадра прошли за краем комнаты (`col<0`), самый длинный -непрерывный прогон — 17 кадров, и комната при этом не переключалась — ровно -как в трассе SDLPoP. - -Прогон уровня 8, шов 24/18 (1433 кадра, 360 кадров с мечом): внутри боя -**4 смены комнаты на 387 кадров** — то есть примерно раз в 6 секунд, и это -настоящее выдавливание, а не мерцание. Все четыре пришлись на РАЗРЕШЁННЫЕ -кадры (170, 164, 170, 165); ни одной смены на запрещённом кадре во всей -трассе нет. Кид подолгу держался на `col=-1` и `col=10`, комната не -переключалась. - -**Инструмент (оставлен в дереве, выключен).** `pop_dbg_kidobj` -(`roomtest_cold.c`) + резидентный буфер `pop_dbg_obj` (`pop_state.c`), -включается `#define DBG_KIDOBJ 1` в `roomtest.c`. Считает то же, что -`set_char_collision` + `set_objtile_at_char`. Вывод забирает брейкпоинт -MAME на резидентном `pop_dbg_trap`; сборщик — `collect_kidobj.py`. - -Грабли, стоившие времени: - -- `#define DBG_KIDOBJ` сперва положили внутрь `#ifndef PROF_BORDER`, а - `PROF_BORDER` задаётся ключом сборки — блок не выполнялся, трасса молча - не включалась. Нужен свой `#ifndef`. -- Знаковые значения через выражения отладчика MAME получить не вышло: `b@` - связывается слабее `+`, и `b@ADDR + 128` читает ДРУГОЙ адрес; скобки не - помогают. Зонд печатает сырые байты, перевод — в сборщике. -- Кольцо `clog` не потребляющее, одинаковые строки от повторов не отличить — - в строку добавлен `totalcycles`, по нему и склеиваются чанки. -- Замерено: один зонд с коротким `printf` — **98 % полной скорости** - эмуляции, играть можно (в отличие от четырёх зондов профилировщика). - -**Осталось невыясненным.** Поле `cols` (`char_col_left..char_col_right`) у -нас систематически на единицу меньше, чем в SDLPoP, и дважды дало `0..-1`, -чего в оригинале не бывает. Все поля, которые считает ДВИЖОК (`tilepos`, -`frame`, `act`, `col`, точка перехода), совпали полностью — расхождение -только в величине, которую выводит сам отладочный помощник из ширины кадра -`pop_cd[].fpw`. Подозрение на паддинг кадров в атласе -(`png_strip_padding_tradeoff`) либо на мой пересчёт переднего края мимо -боевого `char_x_forward_edge`. Проверять отдельно. - ---- - - -## MOB-NEIGHBOUR-ROOM. Плиты нижнего ряда пропадали без кадров падения - -**Наблюдение (пользователь, 2026-08-18).** Уровень 11, комната 14: Кид бежит -по ряду проваливающихся плит, они трясутся и «падают сразу полностью -пропадая, а должен быть промежуточный кадр». Догадка пользователя — «для -плит на 2-ом ряду мы рисуем только тряску» — оказалась верной по симптому и -почти верной по причине. - -**Корень.** Порт `draw_mob` (seg007:13E5) был неполным. У оригинала кусок -рисуется НЕ ТОЛЬКО в своей комнате: - -```c -if (curmob.room == drawn_room) { if (curmob.y >= 210) return; } -else if (curmob.room == room_B) { if (ABS((sbyte)ypos) >= 18) return; curmob.y += 192; } -else if (curmob.room == room_A) { if (curmob.y < 174) return; ypos = curmob.y - 189; } -else return; -``` - -У нас на этом месте стояло `if (!mt_here) return;` — «чужая комната: считаем, -но не рисуем». - -Почему страдал ИМЕННО ряд 2: кусок спавнится на `y_loose_land[row+1]`, для -ряда 2 это **191**, а граница ряда `y_something[3]` — **188**. То есть на -ПЕРВОМ же `move_loose` он уходит в комнату снизу и у нас переставал -рисоваться мгновенно. Ряды 0 и 1 летят внутри комнаты, поэтому там всё было -видно. Промежуточных кадров ровно три — пока `|y| < 18`. - -**Фикс.** У куска появилась отдельная ЭКРАННАЯ координата `draw_y` (своя -комната / +192 из комнаты снизу / −189 из комнаты сверху, `MOB_Y_NONE` = -не рисуется). По ней идут отрисовка, `prev_y`/heal, порядок относительно -Кида, пометки соседа и оверлей; отбор в проходе отрисовки — по `draw_y`, а не -по комнате. Ссылки вверх/вниз кэшируются (иначе `pop_room_link` мапил бы W0 -каждый кадр). - -Вторым заходом пришлось поправить `mob_overlay_neighbour`: ряд там уже считался -по `draw_y`, а габарит спрайта и вызов `pop_mob_overlay_tile` остались по `y` — -для куска из соседней комнаты это расходится на 192, и пересечение с габаритом -тайла считалось заведомо неверно (порядок переднего слоя). - -**Цена — и урок про то, ЧТО именно дорого.** Замер 13/23: - -| секция | до | первый вариант | после гейта | -|---|---:|---:|---:| -| работа | 880 272 | 898 578 | **888 984** | -| зелёная | 419 562 | 445 248 | **427 242** | -| циан | 380 202 | 379 818 | 378 864 | - -Первый вариант метил «фон трогали» по ПРОШЛОЙ нарисованной позиции -безусловно. У куска, видимого из соседней комнаты, она отличается от `y` на -192 — объединение прямоугольников растягивалось почти на весь экран и тянуло -за собой лишние перерисовки. Гейт `m->room != pop_t_room` вернул 18 000 из -26 000. - -Я сперва списал рост на клипованный блит (кусок у кромки по определению -клипуется, а клипованный путь дороже: 14 088 против 10 422) — замер это -опроверг: дело было в пометке. Остаток +7 680 в зелёной (+1,8 %) — -собственно отрисовка трёх кадров на плиту; зелёная остаётся ниже растрового -кадра 430 000. - -Host-тесты 5106 проверок без расхождений (`pop_room.c` они не линкуют — -проверка визуальная, в MAME: уровень 11 комната 14, отделившаяся плита видна -три кадра). - -### Продолжение: в мид-оверлее не было переднего торца плиты - -Пользователь тут же нашёл следующий слой (2026-08-18, та же комната): правый -конец падающего куска рисовался ПОВЕРХ соседней плиты `(2,6)`, хотя торец той -плиты при этом лежал поверх куска — то есть порядок был противоречивым в -разных частях перекрытия. - -Сначала я проверил, не нужен ли клип объекта: `add_mob_to_objtable` -(seg007:1161) ставит куску `clip.right = 40`. Оказалось — **не нужен и в -оригинале не работает**: клип применяется только если -`chtab_flip_clip[chtab_id]`, а таблица (`data.h:196`, -`{1,0,1,1,1,1,0,0,0,0}`) для `chtab_6_environment` даёт **0**. Поле -выставляется и молча игнорируется, так что «клипа нет» у нас — верно. Пункт -MOB-CLIP-RIGHT можно закрывать как несуществующий. - -Настоящая причина — состав мид-оверлея. У оригинала это `draw_tile2()`: - -```c -draw_tile_right(); draw_tile_anim_right(); draw_tile_base(); -draw_tile_anim(); draw_tile_bottom(0); draw_loose(0); -``` - -а у нашего `overlay_mid_tile` последнего вызова не было. У loose в -`pop_tile_table` `bottom_id = 0`, поэтому передний торец плиты рисует ТОЛЬКО -`draw_loose` — и в оверлее его не рисовал никто. Добавлено; кадр берётся по -живому modif, чтобы дрожащая плита осталась дрожащей. - -**Осталось невыясненным:** в `overlay_mid_tile` по-прежнему нет полного -`draw_tile_right` (есть только случай пола, спрайт 42) и нет -`draw_tile_anim_right` — то есть анимированной правой грани СОСЕДА (пики, -ворота, loose). Симптома на это пока не видели; если всплывёт «правая грань -соседа под персонажем/куском» — смотреть сюда. - -### Вариант B: порт корзины 30 (2026-08-18) - -Реализовано то, что разобрано в коммите `272cf8f`. Кусок, у которого ряд -объекта вне 0..2 (`y_to_row_mod4` даёт −1 и «выше потолка», и «ниже пола»), -считается объектом ТАЙЛА 30 — а такие оригинал рисует ПЕРВЫМИ, до всего -обхода тайлов. Два следствия: - -- `defer = 0` — кусок идёт под всем, включая Кида (раньше сравнение рядов - давало обратное: «−1 обходится последним» читалось как «поверх»); -- оверлею отдаётся ориентир **3** («раньше любого ряда обхода 2,1,0») - вместо сырого −1, и в набор перекрываемых тайлов добавляется **своя - клетка** — у куска в обычном ряду её перерисовывать нельзя, там объект - вливается в midtable уже после частей своего тайла. - -**Замер 13/23 (3032 кадра, зонды фаз + детализация зелёной):** - -| секция | тег `mob-order-B-start` | после B | дельта | -|---|---:|---:|---:| -| работа | 888 984 | **913 848** | +24 864 | -| синяя | 159 804 | 159 810 | +6 | -| зелёная | 427 242 | **440 418** | +13 176 | -| циан | 378 864 | 393 000 | +14 136 | - -Период кадра не изменился: 4 растра в 23 кадрах, 5 в двух — как и до правки. - -**Зелёная вышла за растровый кадр** (440 418 против 430 000) при цели -400 000. Детализация показывает, где именно: - -| участок зелёной | максимум | -|---|---:| -| `pop_loose_tick` целиком | 185 826 | -| — из них `pop_loose_mob_tick` | **168 180** | -| — циклы по тайлам | 23 736 | -| — `check_loose_fall_on_kid` | 5 868 | -| тробы + `pop_redraw_needed` | 337 800 | -| шов и прочее | 11 964 | - -(максимумы взяты по разным кадрам, поэтому не складываются в общий). - -Основной кандидат на возврат этих тактов — задача -[HEAL-WIDTH](TASKS_OPEN.md#heal-width): heal'ы кусков и плит берут ширину по -клеткам, а фактический след — 58/57. - -### BUG-DBGSTART-LEVEL. Отладочный старт ломал переход между уровнями (закрыт 2026-08-20) - -**Симптом** (найден пользователем на сборке `make LEVEL=6 ROOM=1 POS=18`): -Кид проваливается в комнате 1 шестого уровня, уровень меняется на 7-й — и -Кид появляется не там: приземляется в комнате 1 на 2,8 вместо того, чтобы -продолжать падение по своей колонке. - -**Корень.** Отладочный старт (`DBG_START_ROOM`/`DBG_START_POS`, -`roomtest_cold.c`) подменял комнату и позицию БЕЗУСЛОВНО, а -`pop_start_level` зовётся не только на первом запуске, но и **на каждой -смене уровня**. В результате на 7-м уровне стартовой становилась -отладочная комната 1 вместо комнаты 17 из данных — и спецсобытие «вход -падением» (`set_start_pos`, seg003:0196: уровень 7, комната 17 -> -`goto_other_room(3)`) не срабатывало вовсе, потому что его условие -сравнивает именно `start_room`. - -Само спецсобытие было портировано и работало; сломан был вход в него. - -**Починка:** подмена применяется только пока `pop_current_level == -FIRST_LEVEL`. Рестарт ТОГО ЖЕ уровня (смерть, чекпойнт) отладочную -позицию сохраняет — ради этого она и перебивает чекпойнт. - -**Проверено в MAME:** после провала на 6-м `pop_current_level` = 7, Kid.room -= 16, то есть спецсобытие отработало (старт в 17, переход вниз). - ---- - - -## CUTSCENE-LTR. Переходы story должны проявляться слева направо — **ЗАКРЫТ 2026-08-26** - -**Было.** Полосовой переход (`transition_ltr` оригинала) стоял только на -«In the absence…»; «свадьба» → титры возникали мгновенно, а промежуточный -титульный экран отсутствовал вовсе. - -**Стало.** Переход выделен в общий `pop_screen_present_ltr()` (pop_ui.c) — -им пользуются story-переход интро, титры, таблица рекордов и титульная -картинка финала. Восстановлен и пропущенный шаг `show_title()` -(seg000:2045): между «свадьбой» и титрами показывается композиция с -логотипом и Jordan Mechner, тоже полосами, и лишь через 0x78 тиков идут -титры. - -**Грабля, которая стоила отладки.** Переход копирует страницу -акселератором, а тот читает ОЗУ-копию. `GFX_BANK_SPRITE` (NOSHADOW + -TRANSPARENT) в неё не пишет, поэтому переезжал один фон без текста (так вышла -пустая таблица рекордов, см. [HOF-ENTRY](TASKS_CLOSED.md#hof-entry)). Всё, -что должно проявляться ВМЕСТЕ с фоном, рисовать банком -`GFX_BANK_TRANSPARENT` — прозрачность та же, но копия обновляется. - ---- - - -## PV-MUSIC-STALL. В сцене с принцессой звучала только первая реплика — **ЗАКРЫТ 2026-08-26** - -**Симптом (пользователь).** «Музыка после открытия двери для входа Джафара -пропала» — дальше сцена шла в тишине до конца: не звучали ни реплика входа -Джафара (m53), ни его ухода (m52). - -**Корень.** Регрессия коммита `4086dde` («убрана промотка кадров»). До него -шаг постраничной подкачки стоял БЕЗУСЛОВНО в теле кадра сцены (плюс там же -вызывался `pop_music_service`). Тот коммит перенёс шаг «в паузу кадра» — -внутрь цикла ожидания тика насоса — и одновременно ввёл правильную защиту от -промотки (`if (snd_done > snd_want) snd_want = snd_done`). Вместе это и убило -подкачку: пауза в этой сцене почти не выпадает, цикл ожидания выходит сразу, -и шаг не вызывается. - -**Замер, который это показал** (адреса статиков `pop_music.c` вычислены от -public `_pop_mus_page` по смещениям из `.sym`): - -| | до фикса | после фикса | -|---|---|---| -| `ld_next` (прочитано страниц m53) | **0** за 20 с | **11 из 11** | -| `mus_ready` | 0 | 1 | -| что звучит | только m50 | m50 → m53 → m52 | - -**Фикс.** Один безусловный `pop_music_load_step()` в кадре сцены (шаг в паузе -оставлен: он бесплатный, когда время есть). Страница стоит 33 мс против -кадра сцены в 100-133 мс, а шкала работает по факту прошедшего времени, -поэтому сцена от этого не разъезжается. - -**Ловушка отладки, стоившая ложного диагноза.** Пока машина стоит на -брейкпоинте, значения в памяти застывают — картина «`busy=0` при `next=3` из -20» выглядела как обрыв чтения с ошибкой, хотя это был просто стоп-кадр. -Прежде чем толковать значения, проверять `status`: `state=run` или `stop`. - ---- - - -## FINAL-BANKCALL. Финал убивал программу: прямой вызов в чужой банк - -**Симптом (пользователь, 2026-08-27).** Пройденная игра доходила до -таблицы рекордов и умирала: программа исчезала, а машина следом вставала -намертво (`di; halt` по адресу 0x0000) либо уходила в reset. Из Flex -Navigator и из голого DSS — одинаково. - -**Что оказалось не при чём** (проверено, чтобы не искать заново): Flex -Navigator, звук и CBL (с выключенным звуком падало так же), потоковое -чтение победной темы, второй `open` поверх открытого файла, фейды с -клавиатурным диспетчером, чтение архива PV, межбанковый вызов -банк 10 → банк 11, переполнение банка (самый полный — BANK8, 15376 из -16384), утечка манипуляторов (`_fd_open_count` = 1) и утечка ссылок на -IM2-таблицу (`_irq_refs` = 2, как задумано). - -**Как искали.** Бисекция ключом `HOF=0/2/4/5` (пропустить таблицу целиком -/ только `hof_load` / без фона / без фейдов) сузила место до -`hof_draw_rows`, а точную инструкцию дала ТРАССИРОВКА MAME на узком -участке: `trace` включалась брейкпоинтом на входе в `pop_hof_show` -(0x44AB) и выключалась на процедуре завершения процесса DSS (0x1E56). -`history` для этого не годится — её 250 записей забиваются обработчиком -прерываний, пока процессор ползёт по мусору. - -**Корень.** - -``` -CD42: ld hl,$B8AB ← _gfx_bank -CD45: ld (hl),$58 ← gfx_set_bank(GFX_BANK_TRANSPARENT) -CD47: call $E503 ← ПРЯМОЙ вызов; по карте 0009E503 = _pop_text_map, банк 9 -E503: rst $38 ← а в W3 стоит банк 10, там пустой хвост (0xFF) -``` - -`pop_ui.h` объявлял группу `pop_text_*_mapped` БЕЗ `__banked`. Пока -`pop_hof.c` лежал в банке 9 рядом с `pop_ui.c`, прямой `call` был верен. -Когда `pop_hof`/`pop_config`/`pop_pal` перенесли в банк 10 ради разгрузки -банка 9, тот же `call` стал уходить в пустоту. Процессор полз по 0xFF до -0x0000, где ловушка DSS ставит B=0x27 и сворачивает процесс: закрывает его -файлы, возвращает контекст (байт 0x1E48: 2 = шелл, 3 = наша программа) и -восстанавливает страницы родителя. Именно это выглядело как «W2 подменили -у нас под ногами» — на самом деле подмена была уже уборкой трупа. - -**Фикс.** Группа помечена `__banked`; появилась -`toolchain/check_bank_calls.py`, встроенная в `app.mk` и валящая сборку -(проверено намеренной поломкой). Правило записано в `CLAUDE.md`. - ---- - - -## FINAL-HOF-GARBAGE. Таблица рекордов заливалась знаками вопроса - -**Симптом.** После фикса FINAL-BANKCALL экран HOF рисовался правильно, но -с началом ввода имени строку заливало `?` во всю ширину. - -**Корень — вторая мина того же класса, созданная первым фиксом.** Курсор -рисовался литералом: - -```c -pop_text_draw_mapped(POP_TEXT_BIG_DARK, x, baseline, "_"); /* ___str_3 в _BANK10 */ -``` - -Пока `pop_text_draw_mapped` был в том же банке, литерал был виден. После -пометки `__banked` трамплин на время вызова переключает W3 на банк 9 — -указатель показывает в чужой банк, функция читает оттуда байты до первого -нуля и рисует их знаками вопроса. Сходится и с наблюдением пользователя -«сначала рисуется правильно, потом мусор»: строка времени берётся из -локального массива (стек, W2 — виден всем), а мусор начинается с мигания -курсора. - -**Фикс.** Курсор строится на стеке. Вторая проверка -`check_bank_calls.py` предупреждает о таких случаях (проверено: на сборке -с возвращённым литералом даёт ровно одно срабатывание). - ---- - - -## KBD-ARROW-PHANTOM. Залипшая стрелка вешала ожидания насмерть - -**Симптом.** После игры (особенно 14-й уровень — он почти весь проходится -удержанием Left) текстовые экраны переставали прерываться, а «новая игра» -вставала намертво: `pop_new_game_load` начинается с -`while (kbd_raw_any_down())`. При этом ввод имени в HOF работал — -он опрашивает конкретные коды, а не всю карту. - -**Корень (подтверждён дважды: 0x72 24.08, 0x6B 27.08).** Стрелка идёт по -проводу как `E0 6B`. При переполнении 3-байтового FIFO SIO теряется -префикс, make садится в PLAIN-половину карты как код нумпада (0x6B = KP4), -а break приходит уже с префиксом и снимает бит в EXT-половине. PLAIN-бит -остаётся зажатым навсегда, `kbd_raw_any_down()` отвечает «да» вечно. -Доказано вмешательством: запись 0 в этот байт отладчиком мгновенно -перевела состояние 5 (LEVEL_LOAD) → 6 (PLAYING). - -**Фикс.** `kbd_raw_keypad_as_ext()` в libc: `kbd_raw_sync()` переносит -биты голых кодов нумпада в EXT-половину и гасит в PLAIN — make и break -начинают работать с одним битом. В PS/2 это и есть одни и те же -физические клавиши, поэтому нумпад заодно стал управлением (7/8/9, 4/6, -2 и 5 — вниз). НЕ лечит обратный случай (префикс потерян у break) — там -бит стоит уже в EXT и выглядит как реально зажатая клавиша. - ---- - - -## F10-GAMEPLAY. Выход по F10 не работал в игре - -F10 проверяется в idle-хуке, а хук висит на `gfx_wait_vsync`. Когда -игровой цикл перевели на пейсинг по лучу, он стал ждать через -`pop_wait_edge`, который звал `kbd_raw_poll` напрямую — хук не вызывался, -и F10 в геймплее умер (в заставках и меню работал). Фикс: `pop_wait_edge` -зовёт тот же `pop_idle`. diff --git a/applications/PoP/roomtest/BUGS_OPEN.md b/applications/PoP/roomtest/BUGS_OPEN.md deleted file mode 100644 index 9934154..0000000 --- a/applications/PoP/roomtest/BUGS_OPEN.md +++ /dev/null @@ -1,1149 +0,0 @@ -# roomtest — ОТКРЫТЫЕ баги и незакрытые оптимизации - -**Здесь ТОЛЬКО незакрытое.** Всё закрытое (и, что важнее, разбор корней) -живёт в [`BUGS_CLOSED.md`](BUGS_CLOSED.md) — прежде чем заводить новый баг, -грепни там по симптому. Сырые формулировки пользователя с прогонов — -[`bugs_level1.md`](bugs_level1.md) / [`bugs_level2.md`](bugs_level2.md). - -Приоритеты работ — в [`TASKS_OPEN.md`](TASKS_OPEN.md) (закрытые задачи с -протоколами — [`TASKS_CLOSED.md`](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), уроненная в прошлой комнате, исчезает без анимации падения | **мелкий** | открыт по решению пользователя: «пока пусть будет так» | - ---- - - -## 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 — -там уже закрытые случаи залипания того же битмапа. - ---- - - -## CUTSCENE-LTR. Переходы story через экран TITLE — ЗАКРЫТ 2026-08-26 - -Разбор и что именно добавилось — [`BUGS_CLOSED.md`](BUGS_CLOSED.md#cutscene-ltr). - ---- - - -## PV-MUSIC-STALL. В сцене с принцессой звучала только первая реплика — ЗАКРЫТ 2026-08-26 - -Разбор — [`BUGS_CLOSED.md`](BUGS_CLOSED.md#pv-music-stall). - ---- - - -## 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`, для roomtest 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`](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-roomtest/roomtest.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` в `roomtest.c`). - Известный баг этого чита закрыт — [BUG-CHEAT-FIGHT-1](BUGS_CLOSED.md#bug-cheat-fight-1). -- Комнаты **13, 18, 24 уровня 1 недостижимы** в обычной игре — это свойство - данных уровня (разбор — «НЕ БАГИ» в [`BUGS_CLOSED.md`](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`](../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` в - `roomtest.c`, маршрут CUTSCENE → LEVEL_LOAD → PLAYING); -- Restart Game через меню — уровень 1 с НОРМАЛЬНОЙ палитрой. - -**Контекст.** Маршрут demo_new_game (`roomtest.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`](../docs/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` (`roomtest.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 плана палитр. diff --git a/applications/PoP/roomtest/CLAUDE.md b/applications/PoP/roomtest/CLAUDE.md deleted file mode 100644 index fc00c5e..0000000 --- a/applications/PoP/roomtest/CLAUDE.md +++ /dev/null @@ -1,104 +0,0 @@ -# roomtest — правила подпроекта - -Живой прототип порта PoP: комната 1 уровня 1 (композиция тайлов в рантайме) -+ Kid (анимация seqtbl, управление, коллизия, падение, зацеп, fore-окклюзия, -проваливающиеся полы). Действуют правила корневого `CLAUDE.md` и -`applications/PoP/CLAUDE.md`. - -**Главное правило (из `../CLAUDE.md`): `../SDLPoP/src/` — источник истины.** -Перед реализацией ЛЮБОЙ функции и по ЛЮБОМУ вопросу «как в оригинале» — -СНАЧАЛА прочитай соответствующий код SDLPoP и портируй по нему; не пиши -по памяти и не угадывай константы/порядок слоёв. Расхождение с SDLPoP = -по умолчанию баг у нас; ОСОЗНАННЫЕ расхождения (платформа/бюджет требуют -иначе) перечислены в `../docs/impl_diff.md` — новое туда же записью, а не -только комментарием в коде. - -**НАЧИНАТЬ С `NEXT_SESSION.md`** — состояние окружения, задача на -ближайший заход и грабли последней сессии одним файлом. - -**Что в работе сейчас — `TASKS_OPEN.md`** (доска задач: приоритеты, критерии -готовности); сделанное с протоколами замеров — `TASKS_CLOSED.md`. Баги — -`BUGS_OPEN.md` (только ОТКРЫТЫЕ) и `BUGS_CLOSED.md` (закрытые + разбор корней: -перед заведением нового бага грепни там по симптому); сырые формулировки -пользователя с прогонов — `bugs_level1.md` / `bugs_level2.md`. План -следующих уровней — `../docs/levels_plan.md`. - -Правило разнесения: как только задача/баг закрыт — запись целиком переезжает -в `TASKS_CLOSED.md` / `BUGS_CLOSED.md`, а в открытом файле остаётся ссылка. -Открытые файлы обязаны читаться целиком за раз. - -## Сборка и запуск - -``` -make # собрать roomtest.exe (упаковав ассеты через toolchain/) -make run # exe + EXTRA_DATA на дискету + запуск MAME (см. корневой док) -make -C tests-host # модульные тесты движка под ucsim_z80 (секунды, без MAME) -``` - -Логику, которую можно проверить без железа, покрывать в `tests-host/` -(обвязка — `testkit/`, там же почему прогон именно под z80). MAME остаётся -для отрисовки, банков, таймингов и клавиатуры. - -`MEMORY=small`, `--gfx 256`. Ассеты (`pop_env0..4.atl`, `pop_wall.atl`, -`pop_fore.atl`, `kid0..27.atl`, `kid.pal`) генерируются python-скриптами -`../toolchain/` — Makefile дёргает их сам при изменении. Данные комнаты — -`room1_data.h` (fg=foretable code, bg=backtable modifier); данные анимации -Kid — `kid_data.h` (генерится `pop_extract_kid_data.py`). - -## Проверка в MAME - -Только через `toolchain/mame_interactive.py` или MCP-мост `mame-z80` -(см. memory `mame_autotest`, `mame_mcp_bridge`, `mame_hdd_test_disk`). -Эталон комнаты — `../toolchain/1.1-2.png`. Пересобрал HDD-образ → MAME -ОБЯЗАН полный рестарт (`mame_hdd_rebuild_restart`). - -**Тайминги моста** (не ждать дольше, см. `docs/mame-autotest.md` §10): -старт `run_bridge.sh` → 6 с → `go` → 8 с → `keyseq d:{ENTER}` + -`keyseq roomtest{ENTER}` → 5 с → программа работает. - -Отладочные тумблеры в живой сессии (`roomtest.c`): -**1** — заморозить кадр, **2** — продолжить (разбор позы/окклюзии); -**ESC** — выход. Читы (`pop_cheat.h`): **K** — убить стража, **I** — -бессмертие, **Shift+L** — следующий уровень, -**+/−** — обход комнат (`ROOMNAV`). Не чит, а штатная клавиша оригинала: -**Ctrl+S** — вкл/выкл звук (пурпурная палочка в борте = звука нет). Уровень грузится по номеру -(`pop_level_load_num`), на диске лежат все 15. - -## Модули - -| Файл | Роль (порт SDLPoP) | -|------|--------------------| -| `roomtest.c` | Главный цикл: дабл-буфер (2 страницы + флип на vsync), tick→draw порядок слоёв. | -| `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`, `seg008:22F0/2324`): спрайт, 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`). | - -## Порядок слоёв в кадре (важно для окклюзии) - -`roomtest.c` каждый тик рисует в СКРЫТУЮ страницу: `pop_char_heal` (стереть -прошлый кадр — по слоту на персонажа) → `kid_tick` → `pop_phys_tick` → -`pop_loose_tick` (loose СЛОЙ ФОНА — ДО персонажей, чтобы они были поверх -плиты) → `pop_char_draw`/`pop_char_fore` для соперника и Кида (кто позже — -тот поверх, порядок задаёт обход тайлов) → `pop_room_clip_borders` (чистка -бортов, гейт по флагу — только в кадрах падения) → флип на vsync. - -Отрисовка персонажа ОДНА на всех (`pop_cdraw.c`): слот выбирает окно -`Char`, набор атласов и heal-прямоугольники, всё остальное общее — как два -одинаковых входа `add_*_to_objtable` в оригинале. - -**Дабл-буфер:** у каждой страницы своя видео-ОЗУ и теневая ОЗУ-копия; heal -берёт чистый фон из копии ТОЙ страницы, в которую рисуем. Любой динамический -элемент (Kid, loose-плита, падающий кусок) обязан чистить свой прошлый кадр -на КАЖДОЙ из двух страниц — иначе остаток виден через кадр как **мерцание**. -Это типовой источник багов «остаётся кусочек» (см. memory -`pop_fall_debug_baseline`). - -## Ключевые memory - -`pop_kid_plan`, `pop_hang_state`, `pop_fore_layer`, `pop_fall_debug_baseline`, -`accel_vertical_copy`, `kbd_raw_fifo_drain`, `pop_check_sdlpop_first`. diff --git a/applications/PoP/roomtest/Makefile b/applications/PoP/roomtest/Makefile deleted file mode 100644 index b471b62..0000000 --- a/applications/PoP/roomtest/Makefile +++ /dev/null @@ -1,472 +0,0 @@ -# roomtest — проверка порта статического фона PoP (Шаг 3): реальная -# композиция слоёв (pop_bg.c) для комнаты 1. --memory huge --gfx 256. - -EXAMPLE := roomtest -HDD_IMG := $(CURDIR)/build/hdd/roomtest.chd - -# small: W1+W2 как единое 32КБ-пространство, DATA сразу за CODE (иначе -# CODE ~23КБ переполняет W1 и лезет в фиксированный DATA@0x8000 в huge). -MEMORY ?= huge # small-раскладка + банки кода в W3 (стражи) -# Резидента --w3 БОЛЬШЕ НЕТ: отрисовка уехала в БАНК 2 (pop_bg + pop_cdraw -# в одной странице — вызовы между ними прямые, если функция не помечена -# __banked). Так снято главное ограничение резидента: из банка его было -# не достать ни прямо, ни транзитивно, а банк→банк работает (трамплин -# сохраняет страницу окна на СТЕКЕ, проверено вложенной цепочкой -# банк1→банк2→банк1 в MAME 2026-07-30). Теперь в банки можно уводить и -# логику, которая рисует. См. docs/layout_plan_v2.md §2, memory -# sdcc_banked_call_rules. -# Банк 5 = pop_ctrl.c (MEM-BANK5). CODE и DATA делят одно 32-КБ -# пространство W1+W2, поэтому килобайт кода, уехавший в банк, — это -# килобайт, доступный данным. Кандидат выбран не по размеру, а по частоте -# вызова: диспетчер управления дёргается один раз за кадр на персонажа и -# горячих банк→банк переходов не создаёт (в отличие от pop_level, чей -# pop_level_tile зовётся из банка 2 на КАЖДЫЙ тайл). Шина control_* -# переехала в pop_state.c — см. там. -# --max-allocs-per-node: во сколько вариантов размещения регистров SDCC -# упирается на узел. У sprinter-cc дефолт 100000 (агрессивно, как fast-сборки -# библиотек) — на roomtest это МИНУТЫ на банк, потому что модули крупные. -# Дефолт SDCC — 3000, и для разработки его достаточно: разница в размере -# единицы процента, а сборка кратно быстрее. Замер (2026-08-05) — в шапке -# TASKS_CLOSED.md, запись BUILD-FAST. -# -# make — быстрая сборка (ALLOCS=3000) -# make ALLOCS=100000 — как раньше: минимальный код, для замеров размера -# и для «релизного» образа -# -# ВАЖНО: любое сравнение занятости банков имеет смысл только при ОДНОМ и том -# же ALLOCS — иначе сравниваются не правки, а уровни оптимизации. -# 6000, а не 3000 (решение пользователя 2026-08-25): сборка дольше секунд на -# двадцать, зато SDCC размещает регистры плотнее и резидент W1 худеет -# примерно на 150 байт — а это ровно те байты, которых не хватает куче. -ALLOCS ?= 6000 -# Build info не хранится вручную в C: ID и дата извлекаются Make из текущего -# commit. Переопределения удобны для воспроизводимой release-сборки. -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, И зависимости roomtest.exe. Раньше эти списки жили отдельно, -# поэтому добавленные pop_shadow.c/pop_sfx_cold.c компилировались, но make не -# замечал их последующие изменения и оставлял устаревший exe. -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 := roomtest_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 -# Банк 9 — оболочка: menu/settings/config/font, автомат, global timer, -# title, attract-demo и таблица рекордов. Здесь остаются небольшие редкие -# верхнеуровневые точки входа, но не рендеры крупных заставок. -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 -# Банк 10 — единый владелец экранных палитр/fade. Он намеренно отделён от UI: -# его будут вызывать и игровой lifecycle, и все cutscene-ветки. -BANK10_SRCS := pop_pal.c -# Банк 11 — тяжёлая кинематика заставок: интерпретатор сценария и renderer -# story/PV. Так анимации не вытесняют menu/config из банка оболочки. -BANK11_SRCS := pop_cutscene.c pop_intro.c -BANKED_SRCS := $(BANK1_SRCS) $(BANK2_SRCS) $(BANK3_SRCS) $(BANK4_SRCS) \ - $(BANK5_SRCS) $(BANK6_SRCS) $(BANK7_SRCS) $(BANK8_SRCS) \ - $(BANK9_SRCS) $(BANK10_SRCS) $(BANK11_SRCS) -BANK_FLAGS := $(foreach f,$(BANK1_SRCS),--bank 1=$(f)) \ - $(foreach f,$(BANK2_SRCS),--bank 2=$(f)) \ - $(foreach f,$(BANK3_SRCS),--bank 3=$(f)) \ - $(foreach f,$(BANK4_SRCS),--bank 4=$(f)) \ - $(foreach f,$(BANK5_SRCS),--bank 5=$(f)) \ - $(foreach f,$(BANK6_SRCS),--bank 6=$(f)) \ - $(foreach f,$(BANK7_SRCS),--bank 7=$(f)) \ - $(foreach f,$(BANK8_SRCS),--bank 8=$(f)) \ - $(foreach f,$(BANK9_SRCS),--bank 9=$(f)) \ - $(foreach f,$(BANK10_SRCS),--bank 10=$(f)) \ - $(foreach f,$(BANK11_SRCS),--bank 11=$(f)) -EXTRA_FLAGS ?= --gfx 256 -I $(CURDIR)/../poc/res/bg --max-allocs $(ALLOCS) \ - $(BANK_FLAGS) $(PROF_FLAGS) $(BUILD_FLAGS) -# Профилирование полосами бордюра (см. PROF() в roomtest.c) — ВКЛЮЧЕНО по -# умолчанию, пока идёт работа с производительностью; make PROF=0 выключает. -# Каждая фаза кадра красит бордюр в свой цвет, высота полосы на скриншоте -# MAME = время фазы в растровых строках. -PROF ?= 0 -PROF_FLAGS := -DPROF_BORDER=$(PROF) -# Стартовый уровень (отладка): make LEVEL=9 — начать сразу с девятого. -# Дефолт держим на отлаживаемом сейчас уровне; для «настоящей» игры — LEVEL=1. -# Константа объявлена в pop_tune.h (её видят обе половины главного цикла). -LEVEL ?= 1 -PROF_FLAGS += -DFIRST_LEVEL=$(LEVEL) -# Стартовая КОМНАТА и позиция Кида в ней (отладка): make ROOM=15 POS=2 — -# начать прямо в целевой комнате оптимизации, минуя проход уровня. POS — -# тайл 0..29, то есть row*10+col. Дефолт — сцена 11/15 (Кид в 0,2), по -# которой сняты замеры в docs/perf_l11_room15.md. make ROOM= (пусто) -# возвращает штатный старт из данных уровня. -ROOM ?= -POS ?= 2 -ifneq ($(strip $(ROOM)),) -PROF_FLAGS += -DDBG_START_ROOM=$(ROOM) -DDBG_START_POS=$(POS) -endif -EXTRA_SRCS := 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 - -BG_DIR := $(CURDIR)/../poc/res/bg -KID_DIR := $(CURDIR)/../poc/res/kid -LVL_DIR := $(CURDIR)/../SDLPoP/data/LEVELS -# 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 -LVL_DATA := $(foreach n,$(LVL_NUMS),$(LVL_DIR)/res20$(n).bin) -# ДВА тайлсета: pop_* — подземелье (уровни 1-3, 7-9, 12, 13), -# pal_* — дворец (4-6, 10, 11, 14). Раскладка id у них общая, различаются -# файлы и палитра тайлсета (*tile.pal, 32 записи). chtab_1 (pop_pot.atl: -# пламя, меч-предмет, зелья) от набора не зависит — один на оба. -BG_SETS := pop pal -BG_PAGES := 0 1 2 3 4 5 6 7 8 9 -# Двенадцать атласов на тайлсет идут подряд — код считает индекс как -# set*12 + страница, а упаковщик печатает константы в bg_arc.h. -BG_ATL := $(foreach s,$(BG_SETS),\ - $(foreach n,$(BG_PAGES),$(BG_DIR)/$(s)_env$(n).atl) \ - $(BG_DIR)/$(s)_wall.atl $(BG_DIR)/$(s)_fore.atl) \ - $(BG_DIR)/pop_pot.atl -BG_ARC := $(BG_DIR)/bg.arc -BG_DATA := $(BG_ARC) $(foreach s,$(BG_SETS),$(BG_DIR)/$(s)_tile.pal) -# ВСЕ 28 страниц Kid — иначе кадры торможения/разворота/приседа (image>23) -# не рисуются (отрисовка пропускает незагруженную страницу). Палитра — -# ЕДИНАЯ игровая (env+wall+kid). -KID_PAGES := $(foreach 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_DIR)/kid$(n).atl) -# Зеркальный ПО ВЕРТИКАЛИ набор (зелье инверсии, уровень 9): те же спрайты с -# развёрнутыми колонками. Готовит упаковщик, а не рантайм — разворот -# страницы стоит 0.59 с против 0.045 с на её чтение с диска (замер MAME -# 2026-08-12, протокол в pop_vflip.c). -KID_VPAGES := $(foreach 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_DIR)/kid$(n)_v.atl) -KID_DATA := $(KID_PAGES) $(KID_VPAGES) $(KID_DIR)/kid.pal \ - $(KID_DIR)/sword.atl $(KID_DIR)/sword_v.atl -# Таблицы анимации (кадры+seqtbl) — отдельный бинарь в EMM-странице, его -# делает pop_extract_kid_data.py (не упаковщик спрайтов) — своё правило ниже. -KID_BIN := $(KID_DIR)/kid.ani - -# АРХИВ KID. Шестьдесят файлов в одном: замер 2026-08-25 — `open` 51,4 мс -# против 32,6 мс на чтение 16 КБ, то есть на одних только открытиях набор -# Кида терял три секунды. Порядок в списке = порядок индексов, а их -# упаковщик печатает в kid_arc.h (константы ARC_KID_*). -KID_ARC := $(KID_DIR)/kid.arc -# В архив идут только атласы. Палитру читает gfx_pal_fload прямо в -# видеопамять, а kid.ani — host-тесты своей файловой заглушкой; ради -# двух файлов заводить обходные пути незачем. -KID_ARC_SRC := $(KID_PAGES) $(KID_VPAGES) $(KID_DIR)/sword.atl \ - $(KID_DIR)/sword_v.atl -GUARD_DIR := $(CURDIR)/../poc/res/guard -GUARD_ATL := $(GUARD_DIR)/g0.atl $(GUARD_DIR)/g1.atl $(GUARD_DIR)/g2.atl \ - $(GUARD_DIR)/g3.atl $(GUARD_DIR)/g4.atl \ - $(GUARD_DIR)/g0_v.atl $(GUARD_DIR)/g1_v.atl $(GUARD_DIR)/g2_v.atl \ - $(GUARD_DIR)/g3_v.atl $(GUARD_DIR)/g4_v.atl -# Скелет — ОТДЕЛЬНЫЙ набор спрайтов (SKEL.DAT в оригинале): его грузит -# уровень 3 (tbl_guard_type == 2), кадров меньше (28 против 34). -SKEL_DIR := $(CURDIR)/../poc/res/skel -# Зеркального набора у скелета НЕТ: зелье инверсии — уровень 9, там обычный -# страж (tbl_guard_type[9] = 0). См. pop_pack_guard.py. -SKEL_ATL := $(SKEL_DIR)/g0.atl $(SKEL_DIR)/g1.atl $(SKEL_DIR)/g2.atl \ - $(SKEL_DIR)/g3.atl -# Джафар — тоже отдельный набор (VIZIER.DAT): его грузит уровень 13 -# (tbl_guard_type == 3). Кадров столько же, сколько у стража (34), но -# палитра своя, из файла набора: curr_guard_color для не-стражей = 0. -# Зеркального набора нет по той же причине, что у скелета. -VIZIER_DIR := $(CURDIR)/../poc/res/vizier -VIZIER_ATL := $(VIZIER_DIR)/g0.atl $(VIZIER_DIR)/g1.atl $(VIZIER_DIR)/g2.atl \ - $(VIZIER_DIR)/g3.atl $(VIZIER_DIR)/g4.atl - -# ТЕНЬ — свой набор, запечённый упаковщиком (наложение спрайта на себя со -# сдвигом; наш блит так не умеет, см. ../docs/shadow_atlas_plan.md). Две -# половины в одном наборе: sk* — кадры вне боя (спрайты Кида), sf* — кадры -# 150..189 (SHADOW.DAT, это тоже графика Кида, а не стража). -SHADOW_DIR := $(CURDIR)/../poc/res/shadow -SHADOW_ATL := $(foreach 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,$(SHADOW_DIR)/sk$(n).atl) \ - $(foreach n,0 1 2 3,$(SHADOW_DIR)/sf$(n).atl) - -# Четыре архива, а не один: имена внутри наборов совпадают (g0.atl есть и у -# стража, и у скелета, и у Джафара), а константы индексов упаковщик делает -# из имени файла. Наборы всё равно грузятся поодиночке — по типу уровня. -GUARD_ARC := $(GUARD_DIR)/guard.arc -SKEL_ARC := $(SKEL_DIR)/skel.arc -VIZIER_ARC := $(VIZIER_DIR)/vizier.arc -SHADOW_ARC := $(SHADOW_DIR)/shadow.arc -GUARD_DATA := $(GUARD_ARC) -SKEL_DATA := $(SKEL_ARC) -VIZIER_DATA := $(VIZIER_ARC) -SHADOW_DATA := $(SHADOW_ARC) - -# ЗВУКОВЫЕ ЭФФЕКТЫ — оригинальная оцифровка, приведённая к частоте CBL -# (10 937,5 Гц, 8 бит моно). 8 страниц по 16 КБ; см. ../docs/sound_plan.md. -SND_DIR := $(CURDIR)/../poc/res/sound -# Число файлов задаёт упаковщик (POP_SND_PAGES в pop_sound_tbl.h): с -# 2026-08-25 звук не режется по границе страницы, поэтому страниц стало 10. -# Список ОБЯЗАН совпадать: недостающий файл — это молча пропавшие эффекты. -SND_PARTS := $(foreach n,0 1 2 3 4 5 6 7 8 9,$(SND_DIR)/s$(n).bin) -# На диск уходит ОДИН архив вместо десяти файлов: `open` дороже чтения -# (51,4 мс против 32,6 мс на 16 КБ, замер 2026-08-25), см. pop_arc.h. -SND_ARC := $(SND_DIR)/snd.arc -SND_DATA := $(SND_ARC) - -# МУЗЫКА: записи DOS-версии, приведённые к формату CBL -# (toolchain/pop_pack_music.py). Номера — оригинала, чтобы игровой код -# просил трек тем же числом, каким 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, 115 с / 1,2 МБ) — финалу нужен стриминг с диска. -MUS_DIR := $(CURDIR)/../poc/res/music -MUS_IDS := 24 25 26 27 28 29 30 32 33 35 36 37 39 40 41 43 50 52 53 54 55 56 -MUS_DATA := $(foreach i,$(MUS_IDS),$(MUS_DIR)/m$(i).bin) - -# UI: два исходных шрифта SDLPoP в одном EMM-атласе. Файл короче страницы: -# хвост использует menu для временного snapshot палитры (без расхода W2). -FONT_DIR := $(CURDIR)/../poc/res/font -FONT_ATL := $(FONT_DIR)/font.atl -FONT_DATA := $(FONT_ATL) -# TITLE: четыре готовые композиции `show_title()` до story/intro. Каждая -# режется на пять полос высотой <=49 px, так что runtime держит ровно одну -# EMM-страницу во время рисования, а не отдельный framebuffer. -TITLE_DIR := $(CURDIR)/../poc/res/title -TITLE_SCENES := 0 1 2 3 -TITLE_PARTS := 0 1 2 3 4 -TITLE_ATL := $(foreach s,$(TITLE_SCENES),$(foreach p,$(TITLE_PARTS),$(TITLE_DIR)/t$(s)_$(p).atl)) -TITLE_ARC := $(TITLE_DIR)/title.arc -TITLE_DATA := $(TITLE_DIR)/title.pal $(TITLE_ARC) -# PV/INTRO: story и fallback-стадии режутся на полосы. Для настоящего PV -# intro есть ещё чистый фон `b0_*` и шесть <=16K actor-атласов: они остаются -# в EMM на время сцены, а не перечитывают полный 320x200 кадр с HDD. -PV_DIR := $(CURDIR)/../poc/res/pv -PV_STORY := 0 1 2 3 4 5 -PV_SCENES := 0 1 2 3 4 5 6 7 -PV_PARTS := 0 1 2 3 4 -PV_ANIM := $(foreach p,$(PV_PARTS),$(PV_DIR)/b0_$(p).atl) \ - $(PV_DIR)/a0.atl $(PV_DIR)/a1.atl $(PV_DIR)/a2.atl \ - $(PV_DIR)/j0.atl $(PV_DIR)/j1.atl \ - $(PV_DIR)/j2.atl $(PV_DIR)/h0.atl $(PV_DIR)/t0.atl -PV_ATL := \ - $(foreach s,$(PV_STORY),$(foreach p,$(PV_PARTS),$(PV_DIR)/s$(s)_$(p).atl)) \ - $(foreach s,$(PV_SCENES),$(foreach p,$(PV_PARTS),$(PV_DIR)/p$(s)_$(p).atl)) \ - $(PV_ANIM) -PV_ARC := $(PV_DIR)/pv.arc -PV_DATA := $(PV_DIR)/story.pal $(PV_DIR)/pv.pal $(PV_ARC) - -EXTRA_DATA := $(BG_DATA) $(KID_ARC) $(KID_DIR)/kid.pal $(KID_BIN) $(GUARD_DATA) $(SKEL_DATA) \ - $(VIZIER_DATA) $(SHADOW_DATA) $(SND_DATA) $(MUS_DATA) $(FONT_DATA) $(TITLE_DATA) $(PV_DATA) $(LVL_DATA) -# Образ HDD для MAME: ресурсы раскладываются ПО КАТАЛОГАМ (8.3, как в DSS), -# чтобы корень диска не зарастал десятками .atl. -# BG\ — фон (env/wall/fore/pot) -# KID\ — персонаж (kid0..27, палитра, меч, таблицы анимации) -# GUARD\ — стражи (появятся здесь же) -# FONT\ — текстовый атлас pause/settings UI -# TITLE\ — нарезанные композиции оригинальной заставки -# PV\ — story и Princess/Jaffar intro, нарезанные полосами -# LEVELS\ — уровни -HDD_PACK_ARGS := $(EXAMPLE).exe \ - $(foreach f,$(BG_DATA),BG:$(f)) \ - KID:$(KID_ARC) KID:$(KID_DIR)/kid.pal KID:$(KID_BIN) \ - $(foreach f,$(GUARD_DATA),GUARD:$(f)) \ - $(foreach f,$(SKEL_DATA),SKEL:$(f)) \ - $(foreach f,$(VIZIER_DATA),VIZIER:$(f)) \ - $(foreach f,$(SHADOW_DATA),SHADOW:$(f)) \ - $(foreach f,$(SND_DATA),SND:$(f)) \ - $(foreach f,$(MUS_DATA),MUS:$(f)) \ - $(foreach f,$(FONT_DATA),FONT:$(f)) \ - $(foreach f,$(TITLE_DATA),TITLE:$(f)) \ - $(foreach f,$(PV_DATA),PV:$(f)) \ - $(foreach f,$(LVL_DATA),LEVELS:$(f)) - -include $(CURDIR)/../sdk.mk - -TC := $(POP_ROOT)/toolchain - -# Упаковщики создают сразу ГРУППУ файлов. Обычное multi-output правило -# заставляло GNU make при `-B` запускать один и тот же упаковщик отдельно для -# каждого .atl/.bin. Stamp-файл представляет один запуск генератора; все -# созданные им файлы зависят от этого stamp без собственных рецептов. -RESOURCE_STAMP_DIR := .resource-stamps -BG_STAMP := $(RESOURCE_STAMP_DIR)/bg -GUARD_STAMP := $(RESOURCE_STAMP_DIR)/guard -SKEL_STAMP := $(RESOURCE_STAMP_DIR)/skel -VIZIER_STAMP := $(RESOURCE_STAMP_DIR)/vizier -SHADOW_STAMP := $(RESOURCE_STAMP_DIR)/shadow -SOUND_STAMP := $(RESOURCE_STAMP_DIR)/sound -MUSIC_STAMP := $(RESOURCE_STAMP_DIR)/music -KID_STAMP := $(RESOURCE_STAMP_DIR)/kid -KID_BIN_STAMP := $(RESOURCE_STAMP_DIR)/kid-bin -FONT_STAMP := $(RESOURCE_STAMP_DIR)/font -TITLE_STAMP := $(RESOURCE_STAMP_DIR)/title -PV_STAMP := $(RESOURCE_STAMP_DIR)/pv - -$(RESOURCE_STAMP_DIR): - mkdir -p $@ - -$(BG_STAMP): $(TC)/pop_pack_bg.py $(TC)/render_room.py | $(RESOURCE_STAMP_DIR) - cd $(TC) && python3 pop_pack_bg.py dungeon && python3 pop_pack_bg.py palace - touch $@ -$(BG_ATL) $(BG_DIR)/pop_tile.pal $(BG_DIR)/pal_tile.pal: $(BG_STAMP) - @test -f $@ || { $(MAKE) -B $(BG_STAMP); test -f $@; } - -$(GUARD_STAMP): $(TC)/pop_pack_guard.py | $(RESOURCE_STAMP_DIR) - cd $(TC) && python3 pop_pack_guard.py - touch $@ -$(GUARD_ATL) $(GUARD_DIR)/guard.pal: $(GUARD_STAMP) - @test -f $@ || { $(MAKE) -B $(GUARD_STAMP); test -f $@; } - -$(SKEL_STAMP): $(TC)/pop_pack_guard.py | $(RESOURCE_STAMP_DIR) - cd $(TC) && python3 pop_pack_guard.py SKEL - touch $@ -$(SKEL_ATL): $(SKEL_STAMP) - @test -f $@ || { $(MAKE) -B $(SKEL_STAMP); test -f $@; } - -$(VIZIER_STAMP): $(TC)/pop_pack_guard.py | $(RESOURCE_STAMP_DIR) - cd $(TC) && python3 pop_pack_guard.py VIZIER - touch $@ -$(VIZIER_ATL): $(VIZIER_STAMP) - @test -f $@ || { $(MAKE) -B $(VIZIER_STAMP); test -f $@; } - -$(SHADOW_STAMP): $(TC)/pop_pack_shadow.py $(TC)/pop_pack_bg.py $(TC)/pop_pack_kid.py | $(RESOURCE_STAMP_DIR) - cd $(TC) && python3 pop_pack_shadow.py - touch $@ -$(SHADOW_ATL) pop_shadow_atlas.h: $(SHADOW_STAMP) - @test -f $@ || { $(MAKE) -B $(SHADOW_STAMP); test -f $@; } - -$(SOUND_STAMP): $(TC)/pop_pack_sound.py | $(RESOURCE_STAMP_DIR) - cd $(TC) && python3 pop_pack_sound.py - touch $@ -$(SND_PARTS) pop_sound_tbl.h: $(SOUND_STAMP) - @test -f $@ || { $(MAKE) -B $(SOUND_STAMP); test -f $@; } -$(SND_ARC): $(SND_PARTS) $(TC)/pop_pack_arc.py - python3 $(TC)/pop_pack_arc.py $@ $(SND_PARTS) - -$(GUARD_ARC) guard_arc.h: $(GUARD_ATL) $(TC)/pop_pack_arc.py - python3 $(TC)/pop_pack_arc.py --header guard_arc.h --prefix GUARD \ - $(GUARD_ARC) $(GUARD_ATL) - -$(SKEL_ARC) skel_arc.h: $(SKEL_ATL) $(TC)/pop_pack_arc.py - python3 $(TC)/pop_pack_arc.py --header skel_arc.h --prefix SKEL \ - $(SKEL_ARC) $(SKEL_ATL) - -$(VIZIER_ARC) vizier_arc.h: $(VIZIER_ATL) $(TC)/pop_pack_arc.py - python3 $(TC)/pop_pack_arc.py --header vizier_arc.h --prefix VIZIER \ - $(VIZIER_ARC) $(VIZIER_ATL) - -$(SHADOW_ARC) shadow_arc.h: $(SHADOW_ATL) $(TC)/pop_pack_arc.py - python3 $(TC)/pop_pack_arc.py --header shadow_arc.h --prefix SHADOW \ - $(SHADOW_ARC) $(SHADOW_ATL) - -$(BG_ARC) bg_arc.h: $(BG_ATL) $(TC)/pop_pack_arc.py - python3 $(TC)/pop_pack_arc.py --header bg_arc.h --prefix BG \ - $(BG_ARC) $(BG_ATL) - -$(TITLE_ARC) title_arc.h: $(TITLE_ATL) $(TC)/pop_pack_arc.py - python3 $(TC)/pop_pack_arc.py --header title_arc.h --prefix TITLE \ - $(TITLE_ARC) $(TITLE_ATL) - -$(PV_ARC) pv_arc.h: $(PV_ATL) $(TC)/pop_pack_arc.py - python3 $(TC)/pop_pack_arc.py --header pv_arc.h --prefix PV \ - $(PV_ARC) $(PV_ATL) - -$(KID_ARC) kid_arc.h: $(KID_ARC_SRC) $(TC)/pop_pack_arc.py - python3 $(TC)/pop_pack_arc.py --header kid_arc.h --prefix KID \ - $(KID_ARC) $(KID_ARC_SRC) - -$(MUSIC_STAMP): $(TC)/pop_pack_music.py | $(RESOURCE_STAMP_DIR) - cd $(TC) && python3 pop_pack_music.py $(MUS_IDS) - touch $@ -$(MUS_DATA) pop_music_tbl.h: $(MUSIC_STAMP) - @test -f $@ || { $(MAKE) -B $(MUSIC_STAMP); test -f $@; } - -$(KID_STAMP): $(TC)/pop_pack_kid.py $(TC)/pop_pack_bg.py | $(RESOURCE_STAMP_DIR) - cd $(TC) && python3 pop_pack_kid.py - touch $@ -$(KID_DATA): $(KID_STAMP) - @test -f $@ || { $(MAKE) -B $(KID_STAMP); test -f $@; } - -$(KID_BIN_STAMP): $(TC)/pop_extract_kid_data.py | $(RESOURCE_STAMP_DIR) - cd $(TC) && python3 pop_extract_kid_data.py - touch $@ -kid_data.h $(KID_BIN): $(KID_BIN_STAMP) - @test -f $@ || { $(MAKE) -B $(KID_BIN_STAMP); test -f $@; } - -$(FONT_STAMP): $(POP_ROOT)/toolchain/pop_extract_font.py \ - $(CURDIR)/../SDLPoP/src/menu.c $(CURDIR)/../SDLPoP/src/seg009.c | $(RESOURCE_STAMP_DIR) - cd $(POP_ROOT) && $(PYTHON) toolchain/pop_extract_font.py - touch $@ -$(FONT_ATL) pop_font.h: $(FONT_STAMP) - @test -f $@ || { $(MAKE) -B $(FONT_STAMP); test -f $@; } - -$(TITLE_STAMP): $(POP_ROOT)/toolchain/pop_pack_title.py \ - $(CURDIR)/../SDLPoP/data/TITLE/res51.png \ - $(CURDIR)/../SDLPoP/data/TITLE/res52.png \ - $(CURDIR)/../SDLPoP/data/TITLE/res53.png \ - $(CURDIR)/../SDLPoP/data/TITLE/res54.png \ - $(CURDIR)/../SDLPoP/data/TITLE/res55.png | $(RESOURCE_STAMP_DIR) - cd $(POP_ROOT) && $(PYTHON) toolchain/pop_pack_title.py - touch $@ -$(TITLE_ATL) $(TITLE_DIR)/title.pal: $(TITLE_STAMP) - @test -f $@ || { $(MAKE) -B $(TITLE_STAMP); test -f $@; } - -$(PV_STAMP): $(POP_ROOT)/toolchain/pop_pack_intro.py \ - $(CURDIR)/../SDLPoP/data/TITLE/res41.png \ - $(CURDIR)/../SDLPoP/data/TITLE/res42.png \ - $(CURDIR)/../SDLPoP/data/TITLE/res43.png \ - $(CURDIR)/../SDLPoP/data/TITLE/res44.png \ - $(CURDIR)/../SDLPoP/data/TITLE/res45.png \ - $(CURDIR)/../SDLPoP/data/TITLE/res51.png \ - $(CURDIR)/../SDLPoP/data/PV/res801.png \ - $(CURDIR)/../SDLPoP/data/PV/res805.png \ - $(CURDIR)/../SDLPoP/data/PV/res816.png \ - $(CURDIR)/../SDLPoP/data/PV/res851.png \ - $(CURDIR)/../SDLPoP/data/PV/res870.png \ - $(CURDIR)/../SDLPoP/data/PV/res901.png \ - $(CURDIR)/../SDLPoP/data/PV/res920.png \ - $(CURDIR)/../SDLPoP/data/PV/res951.png \ - $(CURDIR)/../SDLPoP/data/PV/res953.png \ - $(CURDIR)/../SDLPoP/data/PV/res959.png \ - $(CURDIR)/../SDLPoP/data/PV/res981.png \ - $(foreach n,802 803 804 806 807 808 809 810 811 812 813 814 815 817,$(CURDIR)/../SDLPoP/data/PV/res$(n).png) \ - $(foreach n,852 853 854 855 856 857 858 859 860 861 862 863 864,$(CURDIR)/../SDLPoP/data/PV/res$(n).png) \ - $(CURDIR)/../SDLPoP/data/PV/res952.png \ - $(foreach n,151 152 153 154 155 156 157 158 159,$(CURDIR)/../SDLPoP/data/PRINCE/res$(n).png) | $(RESOURCE_STAMP_DIR) - cd $(POP_ROOT) && $(PYTHON) toolchain/pop_pack_intro.py - touch $@ -$(PV_ATL) $(PV_DIR)/story.pal $(PV_DIR)/pv.pal: $(PV_STAMP) - @test -f $@ || { $(MAKE) -B $(PV_STAMP); test -f $@; } - -# Явные цели позволяют собирать ресурсы без `make -B`, который по смыслу -# принудительно пересобирает также libc, libbgi и само приложение. -resources-bg: $(BG_DATA) -resources-kid: $(KID_ARC) -resources-actors: $(GUARD_DATA) $(SKEL_DATA) $(VIZIER_DATA) $(SHADOW_DATA) -resources-sound: $(SND_DATA) -resources-music: $(MUS_DATA) -resources-font: $(FONT_DATA) -resources-title: $(TITLE_DATA) -resources-pv: $(PV_DATA) -resources-levels: $(LVL_DATA) -resources: resources-bg resources-kid resources-actors resources-sound resources-music resources-font resources-title resources-pv resources-levels -resources-rebuild: - $(MAKE) -B resources - -# Принудительная перелинковка только roomtest, без глобального `-B` и без -# принудительной пересборки библиотек/ресурсов. -relink: resources - $(MAKE) -W Makefile $(EXAMPLE).exe - -# HDD_PACK_ARGS содержит все эти файлы; make обязан подготовить их до запуска -# упаковщика и при чистой сборке, а не полагаться на старое содержимое дерева. -hdd: resources - -# sprinter-cc собирает все модули одним вызовом и depfile пока не выдаёт. -# Банковые .c берём из того же списка, что формирует --bank; любой локальный -# заголовок также обязан перелинковать приложение. wildcard намеренно -# широкий: цена лишней полной сборки ниже риска получить stale-бинарник. -LOCAL_HEADERS := $(wildcard *.h) -$(EXAMPLE).exe: Makefile $(PROJ_ROOT)/app.mk $(BANKED_SRCS) $(LOCAL_HEADERS) \ - $(BG_DATA) $(KID_ARC) $(FONT_DATA) $(TITLE_DATA) $(PV_DATA) $(LVL_DATA) - -clean-resources: - rm -rf $(RESOURCE_STAMP_DIR) - -.PHONY: resources resources-rebuild resources-bg resources-kid resources-actors relink \ - resources-sound resources-music resources-font resources-title resources-pv resources-levels clean-resources diff --git a/applications/PoP/roomtest/README.md b/applications/PoP/roomtest/README.md deleted file mode 100644 index 43afc35..0000000 --- a/applications/PoP/roomtest/README.md +++ /dev/null @@ -1,74 +0,0 @@ -# roomtest - -Живой прототип порта Prince of Persia: комната 1 уровня 1 отрисовывается -реальной композицией слоёв-тайлов, по ней бегает и прыгает Kid под -управлением с клавиатуры. Служит стендом для переноса механик движка -(анимация, коллизия, падение, зацеп, окклюзия, проваливающиеся полы) до их -выделения в переиспользуемый код. - -Правила разработки и порядок слоёв рендера — [`CLAUDE.md`](CLAUDE.md). - -## Быстрый старт - -``` -make # собрать roomtest.exe (ассеты упакуются автоматически) -make run # собрать, упаковать на дискету и запустить в MAME -``` - -Клавиши в запущенном тесте: ←/→ бег, ↓ присед, ↑+→ прыжок, Shift шаг, -ESC выход. - -**Отладка рисования:** -- **SPACE** — тумблер дабл-буфера (в однобуфере баги рисования видны без - мерцания-через-кадр). -- **1** — заморозить кадр (`frozen`: tick/draw/flip пропускаются, картинка - стоит), **2** — снять заморозку и продолжить. Приём для разбора - транзиентных артефактов (тряска/падение loose, поза Kid): поймать - проблемный кадр на `1`, спокойно рассмотреть/снять пиксели — в т.ч. - удалённо из MAME MCP-моста, пока анимация стоит. - -**Читы** (`pop_cheat.h`; в релизной сборке выключаются через `pop_cheats`): -- **I** — бессмертие Кида (toggle). **Работает ТОЛЬКО в бою, то есть с - мечом в руке.** Удар по безоружному Киду остаётся смертельным — ровно как - в оригинале (seg002 `hurt_by_sword`: *«Being hurt when not in fighting pose - means death»*, `take_hp(100)`). Ограничение не косметическое: анимация - «получил удар» (`seq_74`) состоит из кадров 150..179, которые ВСЕ с мечом, - и подмена смерти на неё оставляла Кида в боевой стойке при `sword == 0` — - состоянии, для которого у диспетчера `control()` нет ни одной ветки, то - есть с намертво зависшим управлением - ([BUG-CHEAT-IMM-1](BUGS_CLOSED.md#bug-cheat-imm-1)). На физику (падения, - пики, чомперы) бессмертие действует всегда, независимо от меча. -- **K** — убить стража, **S** — выдать меч, **Shift+L** — следующий уровень, - **+/−** — обход комнат. - -Эталон вида комнаты — `../toolchain/1.1-2.png`. - -## Модули - -- `roomtest.c` — главный цикл (дабл-буфер, порядок tick/draw). -- `pop_bg.c` — фон комнаты композицией тайлов + fore-окклюзия + loose-полы. -- `pop_kid.c` — анимация/движение Kid (интерпретатор seqtbl). -- `pop_ctrl.c` — управление с клавиатуры (диспетчер control). -- `pop_map.c` — коллизия с картой, физика падения, loose-полы. -- `room1_data.h` — карта комнаты 1; `kid_data.h` — данные анимации Kid - (оба генерируются скриптами `../toolchain/`). - -## Статус (2026-08-18) - -**Уровни 1-3 приняты, 4-12 прошли предварительный тест.** Работает: комнаты и -переходы, Kid (анимация, управление, коллизия/падение, зацеп/подтягивание/ -спуск, окклюзия), кнопки и ворота, пики, чомперы, проваливающиеся полы, дверь -уровня и переход на следующий, меч и бой, стражи с ИИ (включая скелета и -визиря), тень уровней 4/5/6, зеркало, HP и зелья, чекпойнты, дворцовый -тайлсет. - -«Принят» = пользователь обошёл ВСЕ комнаты уровня; «предварительно пройден» = -прогон по сценарию. Разбивка по уровням с найденными багами — -[`TASKS_OPEN.md`](TASKS_OPEN.md). Не сделано вовсе: звук, таймер/HUD времени, -меню и сохранения ([QuickSave запланирован](TASKS_OPEN.md#qsave)). - -**Что берём в работу сейчас — [`TASKS_OPEN.md`](TASKS_OPEN.md)** (сделанное с -протоколами замеров — [`TASKS_CLOSED.md`](TASKS_CLOSED.md)); открытые баги — -[`BUGS_OPEN.md`](BUGS_OPEN.md), закрытые с разбором корней — -[`BUGS_CLOSED.md`](BUGS_CLOSED.md); план следующих уровней — -[`../docs/levels_plan.md`](../docs/levels_plan.md). diff --git a/applications/PoP/roomtest/TASKS_CLOSED.md b/applications/PoP/roomtest/TASKS_CLOSED.md deleted file mode 100644 index b5a8e42..0000000 --- a/applications/PoP/roomtest/TASKS_CLOSED.md +++ /dev/null @@ -1,1377 +0,0 @@ -# roomtest — ЗАКРЫТЫЕ задачи (архив досок, обновлено 2026-08-11) - -Сделанное — с протоколами замеров, граблями и причинами решений. Файл -существует не ради истории: половина записей ниже — это ЧИСЛА (сколько тактов -стоил heal, сколько байт теряет клавиатура, почему `static inline` дорог) и -перечень того, что делать НЕЛЬЗЯ, потому что уже пробовали. - -Открытые задачи — [`TASKS_OPEN.md`](TASKS_OPEN.md); открытые баги — -[`BUGS_OPEN.md`](BUGS_OPEN.md), закрытые с разбором корней — -[`BUGS_CLOSED.md`](BUGS_CLOSED.md). - ---- - -## QuickSave - - -### QSAVE. QuickSave / QuickLoad — **РЕАЛИЗОВАНО И ПРОВЕРЕНО 2026-08-22** (коммит f4b4852, тэг v0.6-pop-quicksave) - -F6/F9 в roomtest. Формат снимка `'POPQ'` v3: заголовок -{P,O,P,Q,ver=3,level,reserved}, payload всех игровых переменных (Kid/Guard -через qs_char, HP, fight seed, читы speed/immortal/sound с range-валидацией -на загрузке), хвост = размер payload + XOR-контрольная сумма (старший байт = -инвертированный XOR). Носитель — файл на HDD: транзакционная запись -`unlink POP.NEW → bank_save_file → rename SAV→BAK → rename NEW→SAV` -с откатом; загрузка пробует POP.SAV, потом POP.BAK; при другом уровне — -`pop_next_level+pop_level_switch`. Сериализация через резидентные W0-примитивы -`pop_qs_*` (паттерн request-flag + `pop_qsave_process()` на границе кадра вне -Char-окон); сериализаторы добавлены в pop_map/pop_loose_mob/pop_trob/ -pop_guard_ai с восстановлением инвариантов (mobs_live, trob_drawn, redraw). -Дабл-буфер закрыт `pop_qsave_restore_room()`: полная перезагрузка комнаты + -перерисовка ОБЕИХ страниц + инвалидация кэшей спрайтов и HP. Три ГСЧ -(`pop_t_seed`, `trob_seed`, `pop_fight_seed`) — все в payload. -Файловое I/O — новые `bank_load_file`/`bank_save_file` libc (без правила W3). - -Критерий приёмки выполнен: сохранить, выйти по ESC, запустить заново, -загрузить — оказались там же; проверено в MAME. Полный разбор и формат — -[`../docs/quicksave_plan.md`](../docs/quicksave_plan.md) (теперь справочник). - ---- - -## Уровни: спецсобытия - - -### L8-MOUSE. Мышь уровня 8 — ЗАКРЫТА 2026-08-12 - -Спецсобытие комнаты 16: кнопка (0,7) открывает решётку (0,3), но с левой -площадки верхнего ряда до кнопки не дойти — между ними провал в три колонки. -Когда дверь уровня уже открыта, а Кид всё ещё в комнате 16, оригинал через -150 кадров присылает мышь: она пробегает справа налево, наступает на кнопку и -убегает. Порт по SDLPoP: `do_timers` seg003:0545, `do_mouse` seg003:0ABA, -`autocontrol_mouse` seg002:07EB. - -**Ассетов и отрисовки не потребовалось вовсе.** Кадры 186..188 лежат в -таблице КИДА (`load_frame` seg006:0293 — общий case), их images 130..132 уже -упакованы в `kid16.atl` (idx 2/3/4: 17×4, 19×4, 12×10) — `pop_pack_kid.py` -берёт все `res(401+N).png`, что есть в `data/KID`. Отрисовка подхватывает -мышь сама: `pop_frame_tbl_is_guard(24, …)` отдаёт 0, значит и таблица кадров, -и набор атласов берутся кидовские, и палитра (0x70) тоже. - -Что написано: - -| где | что | -|---|---| -| `guards.c` | `pop_check_mouse` (условие + рождение), `autocontrol_mouse`, ветки мыши в `autocontrol_opponent` / `play_guard` / `pop_check_can_guard_see_kid` | -| `pop_state.c/h` | `pop_leveldoor_open` стал **word**, как в оригинале (data.h:361): в него же считается задержка, байт переполнился бы через 255 кадров и обнулил признак «дверь открыта» | -| `pop_guard.c` | `leave_guard` не записывает в данные уровня тень и мышь (seg002:02F5) — раньше эта проверка отсутствовала вовсе | -| `pop_cdraw.c` | полосы HP у мыши нет (`draw_guard_hp`, seg000:1159) | -| `pop_tune.h` | числа события (уровень/комната/задержка/пороги X) | - -**Правило появления — мышь ОДНОРАЗОВАЯ (сверено с оригиналом).** Счётчик -живёт в `leveldoor_open`: 0 на старте уровня (seg003:97), 1 — когда створка -двери уровня доехала доверху (seg007:456, то есть Кид нажал кнопку в комнате -3), дальше `++` каждый кадр, пока Кид в комнате 16, и вызов ровно при -`== 150`. Отсюда три следствия: - -- второго раза НЕТ: счётчик растёт дальше, а сравнение строгое. Не успел - пройти решётку, пока она открыта — мышь больше не придёт; -- выход из комнаты 16 счётчик не сбрасывает, а ЗАМОРАЖИВАЕТ: ушёл на сотом - кадре, вернулся — ждать осталось пятьдесят; -- обнуляет только `start_level`, то есть смерть/рестарт уровня — но он же - возвращает тайлы и модификаторы, так что дверь придётся открывать заново. - -Значения `0x4D` (музыка уровней 4 и 6) и `2` (победа над Джаффаром, уровень -13) в ту же переменную к уровню 8 отношения не имеют — там счёт идёт -монотонно 1 → 150. - -**Чего в условии НЕТ (проверено грепом по всем упоминаниям `mouse_level` / -`mouse_room` / `do_mouse` — единственная точка запуска это seg003:545):** -оригинал не смотрит ни на состояние решётки (0,3), ни на то, с какой стороны -провала стоит Кид, ни на то, прошёл ли он её уже. «Кид заперт за закрытой -решёткой» получается САМО: находиться в комнате 16 двенадцать с половиной -секунд можно только топчась там, а стоит уйти в комнату 4 — счётчик -замирает. Обратная сторона той же простоты: вернувшись в комнату 16 с ЛЮБОЙ -стороны, игрок доигрывает накопленное ожидание и может получить мышь, даже -если решётка в этот момент открыта. - -Тупика при этом нет: провал в колонках 4..6 ведёт на нижний ряд комнаты -(`debris` между колоннами, а колонна — проходимый пол, seg006:0628), оттуда -низом через комнату 4 с шипами есть путь к двери уровня в комнате 3. Цена — -падение на два ряда, то есть −1 HP. Мышь экономит эту единицу, а не спасает -от тупика. - -**Тонкость, на которой легко сломать уход:** `seq_107` прыгает на метку -`Mscurry1`, стоящую ПОСЛЕ опкода `act(actions_1_run_jump)` в `seq_105` — на -обратном пути кадры бега проигрываются, а `action` остаётся стоечным. Именно -этим оригинал отличает «бежит к кнопке» от «убегает»: условие исчезновения -висит на `action == 0`. - -**Проверено.** Хост-набор `t_mouse` (17 проверок, единственный, куда -линкуется `guards.c`) — рождение по таймеру, замороженный счётчик вне комнаты, -требование открытой двери, нажатие кнопки на пробеге, разворот и исчезновение, -невидимость для Кида, необращение в «стража комнаты». Регресс проверен -откатом: без ветки в `autocontrol_opponent` падают две проверки ухода. -Живьём в MAME (комната 16 уровня 8, `leveldoor_open` взведён через мост): -мышь появилась (`charid = 0x18`), добежала до колонки 7 (кадр 188, x = 164), -решётка (0,3) поехала вверх, мышь ушла вправо и погасла (`direction = 0x56`). - - -### MEM-COLD1. Холодная половина главного цикла — в банк 8 (2026-08-12) - -К уровню 8 куча в режиме huge упала до **879 Б**. Убирать из `roomtest.c` -оказалось нечего — отладочное там всё живое (обход комнат, стоп-кадр, читы, -профиль), — поэтому вынесли в банк то, что отрабатывает раз на уровень или -только по клавише: `pop_start_level` + `find_start_level_door`, чит-навигацию -по комнатам и лейбл номера комнаты (`roomtest_cold.c`, `--bank 8`, `n_banks` -поднят до 8). - -Результат: `_CODE` 25653 → 24825, **куча 879 → 1707 Б**, банк 8 занят на 5 %. -Кадровый путь не тронут: за обычный кадр в банк 8 уходит ноль вызовов, при -смене комнаты — один трамплин. - -Что осталось в резиденте намеренно: `enter_room_side` и рабочие массивы -комнаты (их читает и главный цикл, и холодная половина), `guard_over_kid` с -хелперами (зовётся каждый кадр). Следующий шаг — [MEM-COLD2](TASKS_OPEN.md#mem-cold2). - ---- - -## Приёмка уровней - - -### ПОЛИТИКА ПРИЁМОК — решение пользователя 2026-08-11 - -**Уровни 1-4: smoke-тесты пройдены.** Полные прогоны (обход всех комнат -каждого уровня) делаются **по готовности ВСЕХ уровней**, а не по одному за -этапом — отдельных задач `L3-PASS`/`L4-PASS` больше нет. - -Основание: сквозные обходы дорогие, а половина находок на неполном наборе -уровней всё равно оказывается «механики ещё нет». Smoke (пройти уровень от -старта до двери) остаётся обязательным на каждом новом уровне — он снимает -блокеры, а не косметику. - -Уже сделанные полные обходы уровней 1 и 2 (ниже) остаются регресс-базой. - - -### L1-PASS. Сквозное прохождение уровня 1 — **ЗАКРЫТ 2026-08-07** - -> **Smoke 2026-08-05 (пользователь): успешный.** От старта до выхода с -> уровня одним заходом — меч подобран, **оба стража побеждены в честном бою** -> (без читов), выход отработал корректно. Ценность прогона в том, что он снял -> главные риски этапа 1 разом — боёвка, предметы, переход с уровня — и стал -> регресс-базой для уровня 2. -> -> **Полный обход комнат 2026-08-07 (пользователь): крупных багов нет.** Этим -> же прогоном закрыта [таблица обхода 24 комнат](BUGS_CLOSED.md#обход-всех-24-комнат-уровня-1) -> и [чек-листы ручной перепроверки фиксов](BUGS_CLOSED.md#ручная-перепроверка-2026-08-03) -> — обе уехали в `BUGS_CLOSED.md`. - -Приёмка этапа 1 и одновременно регресс-база для уровня 2: от старта до двери -уровня одним заходом — подбор меча, страж, кнопки/ворота, пики, loose-полы, -зелье, падения. Точки, где смотрели внимательно, — закрытая косметика -окклюзии (потолок при прыжке вверх, шов при анимации решётки, грани дальней -колонны) и подъём на тайл-кнопку (оговорка к BUG-3 в `BUGS_CLOSED.md`). - - -### L2-PASS. Приёмка уровня 2 — **ЗАКРЫТ 2026-08-07** - -> **Smoke 2026-08-05 (пользователь): уровень 2 пройден.** Из smoke пришли -> BUG-LOOSE-3 и BUG-GUARD-DEAF-1 — оба закрыты (`BUGS_CLOSED.md`). Следом -> прогнан smoke уровня 3. -> -> **Полный обход комнат 2026-08-07 (пользователь): крупных багов нет.** -> Открытыми с этого прогона остались три записи в -> [`BUGS_OPEN.md`](BUGS_OPEN.md): [BUG-GUARD-COLOR-1](BUGS_CLOSED.md#bug-guard-color-1) -> (страж всегда одного цвета), BUG-GUARD-SPLASH-1 (нет брызг при попадании по -> стражу — **закрыт 2026-08-07**, разбор в -> [`BUGS_CLOSED.md`](BUGS_CLOSED.md#bug-guard-splash-1)) и -> [BUG-CHEAT-FIGHT-1](BUGS_CLOSED.md#bug-cheat-fight-1) (наш чит `+`/`−` в бою -> отнимает управление). Ни одна играть не мешает. - -Ниже — **карта содержимого уровня, снятая прямо с `res2002.bin`**. Она -осталась в архиве не как отчёт, а как справочник для повторных прогонов и -для сравнения с SDLPoP: если механика в таблице есть, а в игре не сработала — -это баг, а не «так задумано». - -**Стражи — 5, в комнатах 4, 7, 11, 15, 24** (skill 1/2/1/1/3, цвета -1/3/1/1/6 — цвет мы пока игнорируем, атлас один, см. -[BUG-GUARD-COLOR-1](BUGS_CLOSED.md#bug-guard-color-1)). Заметить: страж -комнаты 24 со skill 3 — первый по-настоящему опасный. - -**Кнопки и что они открывают** (декодировано из LINKLOC/LINKMAP): - -| Кнопка | Тип | Цель | -|--------|-----|------| -| к.9 @ряд1,кол1 | RAISE | **дверь уровня** к.23 @1,3 — то есть выход | -| к.11 @1,1 | RAISE | ворота к.18 @0,9 | -| к.18 @0,7 | RAISE | ворота к.7 @0,9 **и** к.18 @0,9 (две сразу) | -| к.18 @0,2 | DROP | закрывает ворота к.7 @0,9 | -| к.13 @1,4 | DROP | закрывает ворота к.13 @1,5 | - -**Ловушки и предметы по комнатам:** - -``` -к. 3 loose@2,2 зелье@2,5 (здоровье) -к. 4 loose@1,7 loose@1,8 + СТРАЖ -к. 5 дверь уровня @1,2-3 — ВХОД (захлопывается за спиной) -к. 6 пики@1,3 loose@1,5 зелье@2,7 (здоровье) -к. 7 ворота@0,9 пики@2,7 + СТРАЖ -к. 8 зелье@2,2 (здоровье) -к. 9 кнопка RAISE@1,1 loose@2,6 -к.10 пики@2,2 -к.11 кнопка RAISE@1,1 + СТРАЖ -к.12 зелье@2,6 (здоровье) loose@2,8 -к.13 зелье@1,3 (ВРЕДНОЕ, −1 HP) кнопка DROP@1,4 ворота@1,5 зелье@1,8 -к.15 + СТРАЖ -к.18 кнопка DROP@0,2 loose@0,4 кнопка RAISE@0,7 ворота@0,9 зелье@2,6 -к.19 пики@1,4 -к.20 ЗЕЛЬЕ@1,2 — БОЛЬШАЯ СКЛЯНКА (+1 к потолку HP) пики@2,6 пики@2,7 -к.23 дверь уровня @1,3-4 — ВЫХОД -к.24 + СТРАЖ (skill 3) -``` - -Отдельно проверялось то, что появилось именно на этом уровне: -- **большая склянка** (к.20) — потолок HP становится 4, индикатор рисует - четыре деления, и это HP **переносится на уровень 3**; -- **меч уже в руках** с самого старта (`have_sword = level >= 2`); -- **выход через дверь к.23** вживую (кнопка в к.9); -- **смерть/респавн** возвращают на уровень 2, а не на 1. - ---- - -### Справка: связность комнат уровней 1–3 (снято 2026-08-05) - -Обход графа `roomlinks` (@1952, по 4 байта на комнату: L, R, U, D; 0 = нет -соседа) от стартовой комнаты — тем же методом, которым на уровне 1 нашлись -13/18/24 (см. «НЕ БАГИ» в [`BUGS_CLOSED.md`](BUGS_CLOSED.md)). Скрипт разовый, -в репозиторий не клался: чтение трёх массивов, BFS и проверка симметрии. - -| уровень | старт | недостижимы | признак | -|---------|-------|-------------|---------| -| 1 | к.1 (0,0) | **13, 18, 24** | ссылки наружу есть, обратных нет | -| 2 | к.5 (1,3) | **нет** | граф полностью симметричен, все 24 достижимы | -| 3 | к.9 (2,4) | **23, 24** | то же, что на 1: односторонние ссылки, обе комнаты **полностью пустые** | - -``` -ур.3: 23 L→4, у 4 R=22 | 24 L→2, у 2 R=7 - 23 R→22, у 22 L=4 | 24 R→7, у 7 L=2 - | 24 U→16, у 16 D=0 - на 23 ссылается только 24, на 24 — только 23; тайлы обеих = все empty -``` - -То есть на уровне 3 это даже более чистый случай, чем на уровне 1: там в -брошенных комнатах была геометрия, здесь — пустота. Практический вывод тот -же: **в 23/24 возможен «мусор в шве»** (наш рендер кромки читает крайнюю -колонку соседа ПО ССЫЛКЕ, а сосед соседом себя не считает), приоритет багов -там низкий, в игре их не видно. - -**Уровень 2 — недостижимых нет, но есть три КОЛОДЦА без выхода** (единственная -связь — вверх, откуда Кид падает): - -``` -к.10 U→4 пики(2,2) + пол — падение из комнаты 4 -к.14 U→21 шахта 2 тайла шириной, дно = обломки -к.17 U→15 то же -``` - -Это не баги данных: в 14/17 попадают только падением насмерть, а из 10 -(если выжил) выхода нет вовсе — так в оригинале. При приёмке не считать -«застрял» багом. - ---- - -## Уровни и механика - - -### L4-MIRROR. Зеркало уровня 4 и тень — **ЗАКРЫТА 2026-08-11** - -Пять шагов из шести сделаны и **проверены пользователем в MAME 2026-08-11**; -шестой (вид тени) осознанно отложен. - -| шаг | что | коммит | -|---|---|---| -| 1 | зеркало в атласе: `tile_table[0x0D]` база 75 / фронт 77, `MIRROR_ENV_IDS` + `FORE_ENV_IDS` в `pop_pack_bg.py` | `1b2111f` | -| 2 | постановка тайла: `place_mirror` в `pop_trob.c` по переходу `pop_leveldoor_open` 0/2 → 1 (`animate_leveldoor`, seg007:0457) | `1b2111f` | -| 3 | отражение (`check_mirror`, seg003:0798) — отдельной функцией `pop_mirror_draw`, со своим heal | `8f0362f`, `3913f1e` | -| 4 | прыжок сквозь зеркало и рождение тени (seg004:0239 + seg003:0798..08A9 + seg002:081D/1131) | `844fa6d` | -| 5 | клип тени слева `obj_clip_left = 137 + (mirror_column−4)*32` (seg008:1699) + новый примитив `gfx_blit_cols_part_wx` в libbgi | `8f0362f` | - -**Почему зеркало пришлось добавлять в атлас явно:** тайла 13 нет ни в одном -уровне статически — проверено перебором всех 15 `res200N.bin`, ноль -попаданий, поэтому `render_room.py` его не видит и на месте зеркала был бы -чёрный провал (memory `pop_atlas_dynamic_ids`). - -**Почему отражение — отдельная функция, а не третий слот `Char`:** это -структура самого оригинала — отражение идёт сокращённым путём -`load_frame_to_obj` + `add_objtable(4)`, без клинка, брызг, пропуска кадра; -гейтить всё это в общем теле `pop_cdraw` значило бы добавить ветки в самый -горячий путь. Побочный эффект — [FORE-DUP](BUGS_OPEN.md#fore-dup): передний -слой тайла рисуется дважды, когда футпринты Кида и отражения накрывают один -тайл (всегда, они стоят на одном тайле). Картинку не портит, тратит такты. - -**Почему левый клип сделан примитивом libbgi, а не «нарисовать и вернуть фон -поверх лишнего»** (подсказка пользователя): для column-major левая обрезка -стоит ровно столько же, сколько правая — колонка это непрерывный кусок ОЗУ, -меняются стартовая колонка источника и экранная X. Внутри это уже было (так -клипается левый край экрана), наружу не было выведено. - -**Отложено — вид тени** (шаг 6): в оригинале она рисуется ДВУМЯ блитами -одного спрайта, `blitters_2_or` на месте и `blitters_3_xor` со сдвигом +1 px -(seg008:1602); у нас пока обычная копия, то есть тень выглядит вторым Кидом. -Разбор, замеры и варианты — [`../docs/shadow_render.md`](../docs/shadow_render.md); -блочные операции акселератора под это в libbgi уже есть (`tests/accop`). - -**Краевой случай проверен и закрыт как НЕ БАГ** — -[MIRROR-FG-STALE](BUGS_CLOSED.md#mirror-fg-stale): тайл ставится в момент, -когда дверь дорисовала открытие, а в реальном прохождении игрок в этот момент -в другой комнате; при обычном входе комната и снимок `room_fg` берутся из -данных уровня разом. - - -### L3-CHOMP. Чомперы — **СДЕЛАНЫ 2026-08-08** - -Коммиты `dc0bd47` (анимация, отрисовка, смерть в челюстях), `4d4323f` -(передние зубья через `pop_fore_b` + ветка в `draw_tile`), `db4106a` -(перед чомпером Кид разбегается сразу, без осторожного шага — `safe_step` по -оригиналу), `1461ed5` (фикс регрессии, см. ниже). - -Портировано: `animate_chomper` (seg007:0448), `next_chomper_timing` -(seg007:0F9A — 15,12,9,6,13,10,7,14,11,8 по кругу), `start_anim_chomper` -(seg007:08C7), `start_chompers` (seg007:0F13) — все в `pop_trob.c`, -состояние в `room_modif`, как у пик и ворот. Смерть: `SEQ_54_CHOMPED` / -`FRAME_178_CHOMPED` + `check_chomped_guard` для соперника (`pop_map.c`). -Отрисовка — `pop_chomp_pose` (`pop_bg.c`) и холодная перерисовка тайла в -`pop_room.c`. Ассеты упакованы явным набором кадров (`CHOMPER_BOT_IDS` -101-105, `TOP` 111-113, `FORE` 106-110 + кровь 114-123 mono-силуэтом). - -**Грабли, стоившие регрессии (`1461ed5`):** `start_chompers` вызывается на -смене ряда персонажа, а у нас `seqtbl` читается ЧЕРЕЗ ОКНО W0 — прямой вызов -из `play_seq` переключал окно посреди чтения байткода. Вызов отложен через -флаг `chomp_pending` (`pop_kid.c`) и делается ПОСЛЕ цикла интерпретатора. - -Хвосты в багах: [BUG-CHOMP-JUMP-1](BUGS_OPEN.md#bug-chomp-jump-1) (низкий, -маловоспроизводим) и закрытые BUG-TORCH-CHOMP-1/2 (пламя факела и застывший -чомпер — [`BUGS_CLOSED.md`](BUGS_CLOSED.md)). - - -### L3-SKEL. Скелет уровня 3 — **СДЕЛАН 2026-08-07, принят smoke-прогоном** - -В данных уровня 3 стражей нет вообще (`guards_tile` пуст во всех 24 -комнатах) — единственный враг это скелет, и он не «страж из данных», а -**спецсобытие** `check_skel` (seg002:1044): в комнате 1, когда -`Kid.curr_col` == 2 или 3 и дверь уровня открыта, тайл `tiles_21_skeleton` -(комната 1, тайлпос 15) стирается в пол, а на его месте поднимается -персонаж — `charid_4_skeleton`, меч сразу вынут, `seq_88_skel_wake_up`, -skill 2, HP 3. Ещё два тайла скелета (комнаты 17 и 19) — декорация, они не -оживают. Атлас — `pop_pack_guard.py SKEL` → `poc/res/skel/g0..g3.atl`. - -Проверено в MAME: бой, падение в пропасть, окклюзия (2026-08-07); принят -smoke-прогоном уровней 1-4 (2026-08-11). **Этот же механизм** — -«спецсобытие порождает персонажа в слоте соперника» — база для тени уровня 5 -([L5-SHADOW](TASKS_OPEN.md#l5-shadow)). - - -### L3-CHKP. Чекпойнт уровня 3 — **СДЕЛАНО 2026-08-06** (коммит 0cd6b2d) - -`level3_set_chkp` (seg002:0665): флаг взводится, когда Кид уходит **ВЛЕВО ИЗ -комнаты 7**; `do_startpos` (seg003:141) по нему подменяет старт на комнату 2, -тайлпос 6, направление влево и снимает loose-плиту (комната 7, колонка 4, -ряд 0). Константы — в `pop_tune.h` (`POP_CHKP_*`), механика `hitp_beg_lev` -была сделана раньше в L2. - -**Тонкость, на которой сначала ошиблись:** `level3_set_chkp` вызван из -`leave_room` ДО `goto_other_room`, поэтому `Char.room == 7` — это комната, ИЗ -которой уходят, а не в которую входят. Поймал пользователь прогоном в -SDLPoP: смерть В комнате 7 вернула его в стартовую 9, а плита осталась цела. - -**Проверено в MAME:** вход в 7 флаг не ставит, уход влево — ставит; респавн в -комнате 2; после обычной смерти (без чекпойнта) плиты восстанавливаются как -раньше. Вживую в игре (не читом) — потрогать на приёмке уровня 3 -([L3-PASS](TASKS_CLOSED.md#pass-policy)). - -### L2. Переход на уровень 2 и его игра — **МАШИНЕРИЯ СДЕЛАНА 2026-08-04** - -Порт `levels_plan.md` §2 (шаг 1). Что появилось: - -- **Номер уровня стал состоянием.** `pop_current_level` (порт - `current_level`) в `pop_level.c`; `pop_next_level` больше не флаг, а - НОМЕР — `END_LEVEL` его инкрементит (как `++next_level`, seg006:662), а - главный цикл срабатывает по расхождению `pop_next_level != - pop_current_level` (порт `play_level_2`, seg003:0386). -- **Загрузка по номеру** — `pop_level_load_num(n)`: `LEVELS\res20NN.bin` - (fallback `a:\`), старая EMM-страница отпускается ТОЛЬКО после успешной - загрузки новой (нет файла — играем дальше на текущем). На диск кладутся - все 15 уровней (34 КБ). -- **Потабличные различия** (`data.h:840..848`) — таблицы по 16 в - `pop_level.c`: `tbl_entry_pose` (поза входа), `tbl_guard_hp` (HP стража, - ушло из хардкода `3`), `tbl_guard_type` (**−1 = стражей нет**, иначе на - 14/15 они полезли бы из данных комнат), `tbl_level_type` (тайлсет — - пока только читается). -- **`find_start_level_door`** (seg003:02E6) — на уровне 2 это НЕ косметика: - стартовый тайл (комната 5, ряд 1, колонка 3) — правая половина двери - уровня, и без `modif = 43` + `add_trob(...,3)` Кид материализуется внутри - глухой створки. Тип 3 = «быстро закрыть»: дверь захлопывается за спиной - за три кадра, как в оригинале. -- **HP через уровень** — `hitp_beg_lev` (seg003): рестарт уровня - откатывает HP к нему, пройденный уровень подтягивает его к `hitp_max`. - Заодно реализована **большая склянка** (`add_life`, тип зелья 2: +1 к - ПОТОЛКУ HP до 10) — на уровне 2 она есть, комната 20. -- **Меч** — `have_sword = level >= 2` (play_level, seg003:106), а не - жёсткий ноль. -- **Чит Shift+L** (seg000:698) — `pop_next_level = pop_current_level + 1`. - NB: `L` без Shift занят отладочным «осторожным шагом вправо» - (`pop_ctrl` `KBD_DBG_STEPR`); с Shift шаг тоже пройдёт, но уровень тут же - сменится — на практике не мешает. - -**Проверено в MAME (2026-08-04):** старт уровня 1 → Shift+L → уровень 2 -рисуется правильно (комната 5, большие колонны — новые для нас тайлы 8/9 — -на месте, дверь захлопнута, HP 3) → влево в комнату 4, страж на месте → -Shift+L → уровень 3 (комната 9). То есть цепочка загрузок работает -повторно, а не только один раз. - -Контент уровня 2 (сквозное прохождение, выход через дверь) закрыт отдельно — -[L2-PASS](#l2-pass). Цвет стража из данных остался открытым багом — -[BUG-GUARD-COLOR-1](BUGS_CLOSED.md#bug-guard-color-1). - -### L1-START. Старт по данным уровня — **СДЕЛАНО 2026-08-01** - -Старт и оба рестарта (смерть, выпадение из уровня) сведены в один -`pop_start_level()` — порт `start_level` + `do_startpos` + `set_start_pos` -(seg003): комната/тайл/направление берутся из `pop_level_start_*`, направление -инвертируется (`~start_dir`), поза входа — из `tbl_entry_pose`. У уровня 1 -это «падение внутрь» плюс нажатие кнопки room5(0,2) — то самое, что -захлопывает решётку за спиной. Проверено: старт даёт room 1, col 0, падение -на row 1 — как по данным уровня. - -`#define ROOMNAV` оставлен ВКЛЮЧЁННЫМ осознанно: это наш чит, которого в -оригинале не было, — как и `S` (выдать меч), `K`, `I`. Все они со временем -съедутся в общий блок читов, разрешаемый в настройках (решение 2026-08-01). - -### L1-EXIT. Выход с уровня (дверь уровня) — **СДЕЛАНО 2026-08-01** - -Портированы: ветка двери уровня из `up_pressed` + `go_up_leveldoor` -(seg005:0482/0574) — в `pop_leveldoor_enter()` (`pop_map.c`, тайлы и -геометрия) и `up_pressed()` (`pop_ctrl.c`, только последовательность); -опкод `0xF1 END_LEVEL` в `play_seq` теперь инкрементит `pop_next_level` -(порт `next_level`), а главный цикл по нему перезапускает уровень — ровно -та точка, куда `levels_plan.md` §2.2 подключила загрузку уровня 2. - -Открытость двери проверяем по `modifier >= 42` (ветка `fix_exit_door`), а не -по ванильному `leveldoor_open`: с ванильным условием можно войти в ещё -ползущую створку. - -**Грабли, которые стоили отдельного разбора:** `go_up_leveldoor` сначала -писал `Char.x`/`Char.direction`, и оба присваивания молча терялись — окно -`Char` вокруг диспетчера возвращает назад ТОЛЬКО `curr_seq` и `sword` -(`pop_savekid_state`). Направление оставалось «вправо», а все `DX` -последовательности seq_70 отрицательные, поэтому Кид уходил ИЗ проёма влево. -Вывод на будущее: геометрию персонажа в этом порте меняет `pop_map` (пишет в -`Kid`), а не диспетчер. - -✅ Косметика тоже закрыта: BUG-DOOR-CLIP (обрезка силуэта правым косяком -проёма) — недоставало второй половины `clip_char` (`obj_clip_right`) и -обрезки СПРАВА у колоночного блита. В libbgi добавлен -`gfx_blit_cols_part_w(..., maxw)`; разбор — в [`BUGS_CLOSED.md`](BUGS_CLOSED.md). - -### L1-TRIAGE. Ревизия багов — **ЗАКРЫТА** (часть 1 — 2026-08-01, хвост — 2026-08-07) - -✅ **Три Critical'а прогнаны в MAME и закрыты** (протокол с числами — в -[`BUGS_CLOSED.md`](BUGS_CLOSED.md), раздел «Проверено в MAME 2026-08-01»): -BUG-1 (провал на row 1 при переходе через открытые ворота) и BUG-2 -(ping-pong при возврате) **не воспроизводятся**, BUG-3 (окклюзия climb-up на -кнопке) закрыт фиксом `tile_code_drawn` от 2026-07-28. Заодно снят неверный -диагноз BUG-1: репроекция Y при БОКОВОМ переходе — не наш пробел, а точное -поведение `goto_other_room` (`SDLPoP/src/seg002.c:390` меняет только `x`). -Список разделён на [`BUGS_OPEN.md`](BUGS_OPEN.md) (открытое) и -[`BUGS_CLOSED.md`](BUGS_CLOSED.md) (закрытое + разбор корней). - -✅ **Косметика окклюзии тоже закрыта** — BUG-CEIL-1/2/3 и BUG-OCCL-1 были -починены кодом ещё в июле, а записи никто не снял: `ceil_over_kid_tile` -(`pop_bg.c:1275`), `pop_ceil_modif` + `pop_ceil_shake_draw`/`_bake_empty` -(`:547,815,827`), `bar` с `POP_YOFF+3` в `pop_room_redraw_seam_left` (`:793`), -разделение слоёв по `add_backtable` vs `ptr_add_table` в `overlay_mid_tile` -(`:1367`). Разбор — в [`BUGS_CLOSED.md`](BUGS_CLOSED.md). - -✅ **Хвост закрыт 2026-08-07:** таблица обхода 24 комнат уровня 1 — прогоном -всех комнат уровней 1 и 2 (крупных багов нет), см. -[`BUGS_CLOSED.md`](BUGS_CLOSED.md#обход-всех-24-комнат-уровня-1). - ---- - -## Отрисовка и память - - -### DRAW-CHAR. Отрисовка — ОДНА на всех Char — **СДЕЛАНО И ПРОВЕРЕНО 2026-08-08** - -> **Итог.** Отрисовка персонажа сведена к одному набору функций над `Char` -> (`pop_cdraw.c`, банк 4) — как физика после GUARD-PHYS. Было два -> независимых куска кода: `kid_draw`/`kid_heal`/`kid_draw_splash`/ -> `kid_fore_clip*` в резиденте W1 и `pop_guard_draw`/`pop_guard_heal` в -> банке 4, каждый со своей математикой кадра, своими heal-прямоугольниками -> и своим проходом окклюзии (`pop_fore_over_kid` / `pop_fore_over_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_hurt_splash` (seg006:2003) разводит Кида и остальных ровно -> двумя числами: chtab/image и подъём `((charid == kid) << 2) + 11`. - -**Что теперь одно на всех.** - -| Было (два места) | Стало | -|---|---| -| `kid_draw` / `pop_guard_draw` | `pop_char_draw(who)` | -| `kid_heal` / `pop_guard_heal` | `pop_char_heal(who)` | -| `kid_draw_splash` / ветка брызг внутри `pop_guard_draw` | `cd_splash` (порт целиком, все три ветки кадров) | -| `kid_fore_clip` + `kid_fore_clip_restore` / хвост `pop_guard_draw` | `pop_char_fore(who)` | -| `pop_fore_over_kid` / `pop_fore_over_char` | `pop_fore_over_char(ch, …)` | -| `kid_fp_*()` / `pop_guard_fp_width()` | `pop_cd[slot]` (структура в `_DATA`, читается из любого банка без трамплина) | -| `pop_kid_set_render_dx` | `pop_char_set_render_dx(who, dx)` | - -Слот (`POP_CH_KID` / `POP_CH_OPP`) выбирает ровно то, что различается в -оригинале: окно `Char`, набор атласов и кадр (`kidp`+`kid_frame` / -`gp`+`pop_gframe`), heal-прямоугольники по страницам дабл-буфера. - -**Что при этом ПОЧИНИЛОСЬ (расхождения, которые и были ценой дублирования).** - -1. **`clip_char` у соперника не было вовсе** — теперь общий (в оригинале он - в обоих `add_*_to_objtable`). -2. **Клип полем 192 у Кида не было** (`reset_obj_clip`: `obj_clip_bottom = - 192`): его спрайт рисовался на полосе HP, а следы за него подчищала - полоса `pop_room_clip_borders` по флагу `pop_clip_sprite`. Теперь - персонаж режется полем, как в оригинале, и `border_dirty` остался только - у падающей плиты. -3. **У Кида не было ветки брызг «мёртв / падение 106..110»** (`obj_y += 4`) - — была только у соперника. -4. **`char_width_half` СТРАЖА считался по спрайту КИДА**: `set_char_collision` - и `check_spike_below` (`pop_map.c`) читали `kid_fp_width()` безусловно, а - `pop_guard_fp_width()` использовался только для порядка отрисовки. - Теперь метрики берутся по `charid` активного персонажа (`CD_ACT`). -5. **Расширение перебора тайлов окном спрайта** (клинок и брызги уходят за - габарит кадра) было только у соперника — теперь общее. Заодно оно - перестало быть четырьмя лишними аргументами: окно fore-клипа уже лежит в - `pop_bg.c`, и проход читает его сам. - -**Цена / выигрыш (ALLOCS=3000, замер до и после):** - -``` - было стало дельта -_CODE (W1) 24 881 20 524 −4 357 ← куча 2 023 → 6 333 Б (+4 310) -BANK2 pop_bg 15 310 15 045 − 265 ← свободно 1 074 → 1 339 Б -BANK3 pop_map 8 608 8 713 + 105 (CD_ACT — индекс по charid) -BANK4 pop_cdraw 4 625 5 905 +1 280 (сюда переехала отрисовка Кида) -итого −3 237 -``` - -То есть слияние двух копий дало ~3.2 КБ, но **главный выигрыш не в банке 2, -а в резиденте**: отрисовка Кида уехала из W1 в банк, и куча выросла втрое -(2 023 → 6 333 Б). Банку 2 при этом досталось всего +265 Б свободного -места — **под [L3-CHOMP](TASKS_CLOSED.md#l3-chomp) этого мало**, разгрузку -`pop_bg` придётся делать отдельно (свободные номера банков — 7+). - -**Проверено:** `tests-host` — все 5 наборов зелёные, трассы физики не -изменились (`[phys] ok: 1723`, тот же эталон; `[char] ok: 65`). В MAME -(свежий образ, полный рестарт): уровень 1 — комната 1 (Кид, кладка, -факелы, окклюзия колонны), падение на нижний ярус, комната 3 — страж своего -цвета с клинком и своей полосой HP, бой и смерть Кида. Артефактов -отрисовки и мусора на полосе HP нет. - -**Приёмка пользователем пройдена 2026-08-08.** Прогон вживую: отрисовка -персонажей, окклюзия и бой — без регрессий. - -Единственное наблюдение — **циан-полоса профиля (спрайты + fore) выросла**: -проход над Кидом теперь расширяется окном спрайта, как раньше только у -соперника, то есть перебирает на колонку-другую больше. Решение -пользователя: **в пределах допустимого, оптимизация — ОТДЕЛЬНОЙ задачей** -([DRAW-COST](TASKS_OPEN.md#draw-cost)). - - - -### MEM-BANK2. Разгрузка банка 2 — **ЗАКРЫТА 2026-08-08** - -Банк 2 (`pop_bg.c`) упирался в потолок: 90.8 % ещё до чомперов. Разбор по -символам показал, что он держит ДВЕ разные вещи — горячий fore-проход (каждый -кадр) и холодную отрисовку тайлов (вход в комнату, точечные перерисовки), — -а общие «листья» (`blit_b`, `tile_code`, `tile_table`, `wall_pattern`) нужны -обеим. - -**Ключевое ограничение платформы** (записано в memory -`sdcc_banked_call_rules`): писучие данные банка лежат в `_DATA`/W2 и видны -всем, а `const`-таблицы — в СТРАНИЦЕ банка, и из другого банка их не -прочитать вовсе. Плюс трамплин выбирается ОБЪЯВЛЕНИЕМ: пометить лист -`__banked` — значит заставить платить и горячих вызывающих (654 такта на -круг). - -**Шаг 1 — общие листья в РЕЗИДЕНТ** (`pop_tile.c` + внутренний `pop_tile.h`). -W1 замаплен всегда, поэтому и горячая половина, и будущая холодная зовут их -прямым `call` и читают таблицы напрямую — без дублей и без трамплинов. -Уехали: `blit_b`, `env_b/wall_b/fore_b/pot_b`, `tile_code/tile_mod/ -tile_code_drawn/wall_modifier`, `heal_off`, `get_loose_frame`, -`potion_flask`, `pop_fore_set_clip`, `pop_room_set_below/above` + таблицы -`tile_table`, `COL_XH`, `WALL_FRAM_*`, `SPIKES_FRAM_LEFT`, -`LOOSE_FRAM_LEFT/BOTTOM`. Заодно три функции перестали быть `__banked` — -минус трамплин на кадр. - -**Шаг 2 — дедуп внутри банка.** `wall_pattern` (808 → 394) и `wall_rnd` -(786 → 654): четыре ветки по виду стены (SWS/SWW/WWS/WWW) отличались ТОЛЬКО -набором кусков и числами в одной и той же серии `prandom` — сведены к -таблицам `WP_PARTS` и `WR_RULE`. Порядок вызовов `prandom` сохранён -дословно (иначе разъедется раскладка кладки). - -**Грабля, пойманная замером:** после выноса `tile_table` из `static const` в -глобальную SDCC перестала сворачивать `tile_table[10].fore_x/fore_y` в -константы и стала читать их в рантайме — `potion_flask` раздулся 115 → 265 Б. -Лечится макросами, которыми инициализируется та же запись таблицы (одно место -правки, дублирования данных нет): 265 → 230. - -**Замер (ALLOCS=3000):** - -``` - было после ш.1 после ш.2 -BANK2 14 815 12 460 11 942 (90.4 % -> 72.9 %, свободно 4442 Б) -_CODE 20 064 22 591 22 556 (куча 6793 -> 4301 Б) -``` - -С релизным `ALLOCS=100000` банк 2 будет ещё примерно на 500-600 Б меньше -(замер до разгрузки: 14 815 -> 14 176). - -**Проверено:** `tests-host` зелёные; в MAME комната 1 уровня 1 после дедупа -совпала с дорефакторным снимком **попиксельно (0 различий из 227 520)**, -комната 3 (сплошная кладка, все четыре вида стены и марки) — та же раскладка -кирпича. - -**Шаг 3 — ХОЛОДНАЯ половина в банк 7** (`pop_room.c` + внутренний -`_pop_bg.h`). Граница проведена по частоте вызова, а не по размеру: - -``` -pop_bg.c (банк 2) ГОРЯЧЕЕ fore-проход, оверлеи, кладка, клип каждый кадр -pop_room.c (банк 7) ХОЛОДНОЕ draw_tile, точечные перерисовки, mob, раз на комнату - загрузка атласов / на событие -``` - -Обратных зависимостей нет вовсе: холодная половина зовёт горячую ровно в -трёх местах (`wall_pattern`, `wall_pattern_reset`, `draw_gate_back`) — и -через ТОНКИЕ `__banked`-обёртки, а не пометкой самих тел. Тела остаются -непомеченными, поэтому fore-проход, который зовёт `wall_pattern` до девяти -раз за кадр, платит ноль. Общее состояние (`pop_loose_modif`, -`pop_ceil_modif`, `obj_row/obj_col`) — писучее, лежит в `_DATA`/W2 и видно -обеим половинам; `obj_row/obj_col` для этого перестали быть `static`. - -**Цена трамплинов:** полная отрисовка комнаты — ~40 вызовов через границу -(по два `wall_pattern` на стенной тайл), 26 000 тактов = 0.06 кадра РАЗОВО -на вход в комнату. Анимация пик/ворот — 2-4 вызова на кадр, ~1 %. Ровно -тот порядок, который был признан приемлемым при постановке задачи. - -Заодно удалена мёртвая `potion_bubble` (169 Б): её работу давно делают -`pop_potion_flask` (резидент) и `pop_potion_draw`, вызывающих не осталось. - -**ГРАБЛЯ, стоившая прогона: `n_banks` объявляет САМО ПРИЛОЖЕНИЕ** -(`roomtest.c`), а не `sprinter-cc`. Восьмой банк без правки этой константы -собирается и линкуется без единого предупреждения, `_bank_pages[7]` -остаётся 0xFF, и трамплин уводит в мусор: exe грузится, но программа встаёт -намертво ещё до первого кадра — на экране остаётся приглашение DSS. -Диагноз снят дампом `_bank_pages` из MAME (`FF EC ED EE EF F0 F1 FF` — -семь страниц вместо восьми), а не гаданием. Предупреждение об этом в -шапке `roomtest.c` было — и всё равно не сработало, потому что число банков -живёт в двух местах. - -**Замер (ALLOCS=3000):** - -``` - было ш.1 ш.2 ш.3 -BANK2 14 815 12 460 11 942 5 872 (90.4 % -> 35.8 %) -BANK7 — — — 6 035 (36.8 %) -_CODE 20 064 22 591 22 556 22 556 (куча 4301 Б — не тронута) -``` - -**Проверено (шаг 3):** -- `tests-host` зелёные; -- построчная сверка `pop_bg.c` + `pop_room.c` против дорефакторного - `pop_bg.c`: НИ ОДНОЙ строки логики не пропало и не изменилось — - расхождения только шапки, включения, три обёртки и удалённый мёртвый код; -- в MAME **8 комнат** (стартовая + 7 по `ROOMNAV`) сняты до и после и сверены - попиксельно по активному экрану 320×256: различия ТОЛЬКО в фазе анимации - (пламя факелов, кадр Кида), статическая кладка совпадает пиксель в пиксель; -- живой прогон: переход между комнатами, бой со стражем, решётка ворот, - окклюзия Кида ближней колонной — как на дорефакторном билде (сверено - контрольным запуском HEAD-бинаря с тем же вводом). - -**Что осталось в запасе, если места снова не хватит** (не делаем, пока нет -причины): вынести из РЕЗИДЕНТА холодные части — инициализацию в `main` + -`enter_room_side`/`pop_start_level` (~1.7 КБ), загрузчик атласов Кида -(~1.2 КБ), `pop_room_load`/`pop_level_load` (~1.4 КБ). Итого ~4-4.5 КБ -резидента, то есть куча 4301 -> ~8.6 КБ. Целиком `pop_level.c` в банк -нельзя: его `pop_level_tile` зовётся из банка на КАЖДЫЙ тайл — только -расщепление модуля. - -### CLIP-1. Аудит блитов: где клип не нужен — **СДЕЛАНО 2026-08-01** - -> **Итог.** Heal Кида и стража переведены на выбор ядра по тому же тесту, -> что давно стоит у блитов. Замер в MAME (счётчики `totalcycles` на входах -> `kid_heal` и `kid_tick`, то есть вся группа heal за кадр; комната 1, Кид -> стоит, стража нет): -> -> | путь | тактов на кадр | кадров в выборке | -> |------|----------------|------------------| -> | клипающее ядро (как было) | **26 200** | 149 | -> | noclip (стало) | **15 848** | 239 | -> -> **−10 352 такта на кадр, то есть −39.5 % с группы heal** (≈0.49 мс при -> ~21 МГц). A/B честный: оба замера сняты в ОДНОМ прогоне, вторая половина — -> с пропатченным в памяти условием (`jr nz` → `jr` в `pop_heal_fast`), то -> есть на той же геометрии и в той же сцене. -> -> **Размер: −362 Б суммарно** (не плюс!): `_CODE` 25 289 → 25 306 (+17), -> BANK2 13 792 → **13 676** (−116, свободно стало 2708 Б — это тот самый -> тесный банк из рисков `levels_plan.md` §5), BANK3 6512 → 6249 (−263), -> BANK4 без изменений. -> -> **Грабли, стоившие двух пересборок** (вынесено в память -> `sdcc-static-inline-double-cost`): первым заходом хелпер был `static -> inline` в `pop_bg.h` — и SDCC 4.5 И встроил его тело (181 Б) в каждое -> место вызова, И оставил отдельную копию в КАЖДОМ TU, который видит -> заголовок. `pop_guard_heal` раздулся с ~60 до 663 Б, итого +1091 Б в -> `_CODE` и +636 Б в банке стража. Лечится обычной функцией в одном -> резидентном модуле (`pop_draw.c`, W1 — из банков это прямой `call` без -> трамплина, как у `pop_sword_draw`). -> -> **Проверено визуально:** обычная ходьба, прыжок, спуск и позиция «за -> решёткой шва» (straddle — там как раз работает клипающий фолбэк) — -> артефактов и следов нет. - -**Ниже — исходная постановка задачи (что и почему смотрели).** - -**Зачем было сейчас.** Кадр занят на ~86 %; подготовка клипающего варианта -стоит ~5.6 К тактов на вызов, а общее ядро против линейного — 13 288 против -4 617 тактов на спрайт 32×3 (`libbgi/include/gfx.h`). Это самая дешёвая -оставшаяся оптимизация: не переписывание логики, а выбор ядра. - -**Что чинили:** - -- ✅ `kid_heal()` и `pop_guard_heal()` звали `gfx_heal` — **всегда с - клипом**, хотя `gfx_heal_noclip` существует и `heal_off` в `pop_bg.c` им - уже пользовался. Это heal 2–4 прямоугольников КАЖДЫЙ кадр; замер общего - ядра — 11 658 тактов на heal 22×22. Теперь все три идут через общий - `pop_heal_fast` (`pop_draw.c` + `_pop_draw.h`). -- ⛔ `pop_room_clip_borders()` (`pop_bg.c`) — `gfx_heal(0,0,320,…)`: - **оставлен клипающим осознанно**. Полоса шириной 320 не лезет в 8-битный - параметр noclip-ядра, а бить её на два куска по 160 нет смысла: гейт - `border_dirty` пускает туда только в кадрах падения, и выигрыш подготовки - тонет в цене самих 320×28 пикселей. Причина записана прямо в коде, чтобы - не «оптимизировать» повторно. - -**Что обязано остаться с клипом** (зафиксировано комментариями в коде): -- кромочные тайлы фона (`blit_b` в `pop_bg.c`) — тайл у края экрана режется - по построению; -- спрайты при straddle (`kid_render_dx = ∓140`, комната Кида ≠ отрисованной) - и при падении ниже поля — фолбэки в `kid_draw`/`pop_guard_draw`; -- борта поля (`pop_room_clip_borders`) — см. выше. - -**Что уже было правильно** (шаблон, который и распространили): блиты Кида, -стража, клинка и брызг спрашивают `pop_onscreen_cols()` и уходят в -`gfx_blit_cols_part_noclip`. Отдельный случай — `pop_kid_img_blit`: noclip -БЕЗ проверки, потому что единственный вызывающий (полоса HP) рисует по -фиксированным координатам; это тоже помечено в коде. - - -### MEM-BANK5. Разгрузка W1/W2 новым банком кода — **СДЕЛАНО 2026-08-05** - -Итог: `pop_ctrl.c` уехал в банк 5, куча 180 Б → **2298 Б** (после снижения -`--max-allocs`, см. [BUILD-FAST](#build-fast), — 2751 Б). - -Вопрос 2026-08-04: «надо делать новый банк?». **Да, и он лечит именно то, -что жмёт.** В нашей раскладке (`MEMORY=huge`, small-вариант) CODE и DATA -живут в ОДНОМ 32-КБ пространстве W1+W2 — карта сборки на тот момент: - -``` -_CODE 0x4100..0xAA00 26880 Б -_DATA 0xAAD0..0xB9B0 3808 Б -_BSS 0xB9B8..0xBADA 290 Б -куча 0xBADA..0xBB00 38 Б ← упёрлись сюда, добавляя отладку -стек 0xBB00..0xC000 1280 Б -``` - -Поэтому **каждый килобайт кода, уехавший в банк, становится килобайтом, -доступным данным**. Отдельного «дефицита W2» у нас нет — дефицит один. -(38 байт кучи не опасны сами по себе: malloc'ом мы не пользуемся, страницы -берутся через `mem_alloc_block`. Опасно то, что следующая структура -данных упрётся в стек молча.) - -Резидентный код по модулям (из `.sprinter-cc-roomtest/*.rel`): - -| модуль | _CODE | как часто зовётся | в банк? | -|--------|------:|-------------------|---------| -| `pop_kid.c` | 6287 | `load_frame`/`play_seq` — 2×/кадр (Кид + страж) | частично: холодная половина (загрузка страниц спрайтов, `pop_kid_load`) — да; движок кадров — нет | -| `roomtest.c` | 3893 | main-loop | нет (точка входа, зовёт всех) | -| `pop_trob.c` | 2526 | `do_trobs` 1×/кадр, но `pop_trob_modif` — горячий аксессор | кандидат, если вынести аксессор в резидент | -| `pop_level.c`| 2292 | `pop_level_tile` — из `pop_bg` (банк 2) на каждый тайл | **нет**: банк→банк на каждый тайл убьёт отрисовку | -| `pop_ctrl.c` | 2189 | `user_control` 1×/кадр | **взят** — дёшево и безопасно | -| `pop_guard.c`| 923 | 1×/кадр | нет смысла | - -Критерий кандидата — **не размер, а частота вызова и отсутствие горячих -банк→банк переходов**; `pop_level` показывает, что большой холодный на вид -модуль может быть горячим аксессором. Следующий шаг (расщепление -`pop_kid.c`) — в [`TASKS_OPEN.md`](TASKS_OPEN.md#mem-next), брать по факту -нехватки места. - - -### BUILD-FAST. Сборка 10 минут → 1:48 — **СДЕЛАНО 2026-08-06** (коммит 0cd6b2d) - -`--max-allocs-per-node` — во сколько вариантов размещения регистров SDCC -упирается на узел. У `sprinter-cc` дефолт 100000 (агрессивно, как fast-сборки -библиотек), и на крупных модулях roomtest это МИНУТЫ на банк. Дефолт SDCC — -3000, для разработки его достаточно: разница в размере — единицы процента. - -Итог: сборка с нуля **1:48 вместо >10 минут**, куча 2751 Б вместо 2298 -(на 3000 резидент иначе не влезает — замер: конец `_HOME` 0xBC69 при стеке с -0xBB00). В Makefile это `ALLOCS ?= 3000`: - -``` -make — быстрая сборка (ALLOCS=3000) -make ALLOCS=100000 — как раньше: минимальный код, для замеров размера - и для «релизного» образа -``` - -**ВАЖНО:** любое сравнение занятости банков имеет смысл только при ОДНОМ и -том же `ALLOCS` — иначе сравниваются не правки, а уровни оптимизации. - - -### DBG-CHEATS. Отладочные читы SDLPoP — **`[`/`]` СДЕЛАНЫ 2026-08-05**, остальное — оценка - -Мотив прямой: мост MAME **теряет нажатия при быстрой отправке**, поэтому -подогнать Кида в нужную позу для отладки автоматикой нельзя — именно на это -упёрлись BUG-LOOSE-2 и BUG-GATE-PASS-1. - -**Сделано:** - -- **`[` / `]` — сдвинуть Кида на пиксель влево/вправо.** Оригинал — - `../SDLPoP/src/seg000.c:1828`: - ```c - if (key_states[SDL_SCANCODE_RIGHTBRACKET] & key_state) ++Char.x; - else if (key_states[SDL_SCANCODE_LEFTBRACKET] & key_state) --Char.x; - ``` - У нас: коды PS/2 set 2 `[` = **0x54**, `]` = **0x5B** в `pop_cheat.h` - рядом с `KBD_CHEAT_KILL/IMMO/SWORD`; обработка — в том же блоке читов - `roomtest.c`, **по фронту** (`*_prev`, как у остальных), иначе одно - нажатие уедет на десяток пикселей. Работает по `Kid.x` напрямую: - геометрию персонажа в этом порте меняет не диспетчер (см. грабли L1-EXIT). -- **Shift+L — следующий уровень** (вошло в L2-машинерию). - -**Остальные — оценка, а не обязательство** (актуальный статус — -[`TASKS_OPEN.md`](TASKS_OPEN.md), «Отложено осознанно»): - -| Чит | Вердикт | -|-----|---------| -| **T — таймер** | **не сейчас**: таймера уровня у нас нет вообще (Фаза 6), чит пришлось бы делать вместе с механикой | -| **F — остаток feather-fall** | **вместе с Shift+W**: ветка `JMP_IF_FEATHER` (опкод `0xF7`) в `play_seq` есть, но не проверена ничем; индикатор без самого зелья бесполезен, а пара «включить + видеть остаток» закрывает ветку целиком. Перо — уровень 7, так что не срочно | -| **Shift+F9 — quickload с тем же уровнем** | **самое ценное и самое дорогое**: это сериализация `Char` + `room_modif` всех комнат + trob'ов + стражей (`levels_plan.md` §4). Даёт воспроизводимый регресс «вот кадр, где баг» вместо ручной подгонки | - ---- - - -## KBD-1. Shift + стрелки: нажатия теряются — **ПРИЧИНА НАЙДЕНА, ФИКС ПРОВЕРЕН** (остаток отложен) - -> **ПОПРАВКА К ПОСЫЛКЕ (2026-08-05).** Ниже «fake shift» подан как -> установленный факт («при зажатом Shift PS/2 удваивает трафик»). Прямой -> замер потока байт это не подтвердил: клавиатура MAME-Sprinter -> (`pc_kbd ms_naturl`) обёртку `E0 F0 12` / `E0 12` не шлёт вовсе — при -> зажатом Shift поток на стрелку ровно `E0 75 E0 75 …`. Значит удвоения -> трафика в связке Shift+стрелка нет, и мотивировка «поэтому FIFO -> переполняется» отпадает; сам ФИКС (плотный опрос `kbd_raw_poll`) остаётся -> верным и нужным — переполнение вызывает не Shift, а короткая жизнь -> импульса IRQ (пункт 3 гипотезы) плюс DI-окна графики. Разбор и следствия -> — [BUG-KBD-5](BUGS_CLOSED.md). -> -> **Итог (2026-08-01).** Причина — не наш код и не DI-окна графики: импульс -> запроса прерывания здесь теряется примерно в 44 % случаев, и трёхбайтовый -> FIFO SIO переполняется. Лечится ПЛОТНЫМ опросом: `kbd_raw_poll` повешен -> idle-хуком на ожидание кадра (`gfx_set_idle_hook`, новый API libbgi) — -> процессор всё равно проводит там ~42 мс из 60, крутя опрос луча. -> -> **Проверка в roomtest тем же счётным методом: 35 нажатий Shift+Home → -> 35 make, ноль потерь** (до фикса — 9 из 10). Боевой сценарий тоже: -> четыре Shift+→ подряд дали четыре осторожных шага, `Kid.x` 114 → 147. -> Цена: `_CODE` +170 Б, кадровый бюджет не затронут (опрос стоит в -> простое). -> -> **ОСТАТОК (ручная проверка пользователем, 2026-08-01): «стало значительно -> лучше, но иногда при зажатом Shift стрелка всё-таки пропускается».** -> Ощущение, не замер — счётчики на 35 нажатиях подряд потерь не показали, -> значит остаточная частота заметно ниже прежних ~15 %. **Задача осознанно -> ОТЛОЖЕНА до финальной полировки всей программы** (решение пользователя); -> сейчас клавиатура пригодна для работы. -> -> **Где именно осталась дыра — чтобы на полировке не начинать с нуля.** -> Idle-хук покрывает простой, то есть ~2/3 кадра. Оставшаяся треть — это -> занятая фаза, и там DI-окно одного accel-прохода доходит до ~650 мкс при -> допуске FIFO ~300 мкс. Пачка байт, целиком попавшая в такое окно, всё -> ещё может потерять байт — ровно «иногда». Порядок действий, если -> вернёмся: -> 1. Вернуть вызовы `kbd_raw_poll()` между блитами занятой фазы (они -> бесплатны; сами по себе не помогали, но вместе с хуком закрывают -> именно этот зазор) и при необходимости внутрь тайловых циклов -> `pop_bg` — тогда слепым остаётся только тело одного блита. -> 2. Мерить тем же счётным методом (см. ниже), а не на ощупь: скриптовые -> нажатия ровнее человеческих, поэтому набирать выборку от 50 нажатий. -> 3. Если и это не добьёт — остаются два рычага вне нашего кода: Scan Code -> Set 3 через BIOS `$EA` (убирает «fake shift» в корне, но в MAME -> непроверяемо — обратный путь к клавиатуре не разведён) и общий -> `m_irq_off_timer` в драйвере MAME. - -**Симптом (пользователь, 2026-08-01).** Залипаний почти нет, но при -УДЕРЖИВАЕМОМ Shift первые один-два нажатия ← дают осторожные шаги, дальше -нажатия ← не отрабатываются, пока Shift не отпустишь. - -**Рабочая гипотеза, с которой начинали.** Три факта складывались в одну -картину: - -1. **PS/2 Set 2, «fake shift».** При зажатом Shift нажатие РАСШИРЕННОЙ - клавиши (стрелки — `E0`-коды) обрамляется фиктивным отпусканием/нажатием - шифта: нажатие ← шлёт `E0 F0 12` + `E0 6B` = **5 байт** (без шифта было - бы 2), отпускание — `E0 F0 6B` + `E0 12` = **5 байт** (было 3). - (**Опровергнуто замером 2026-08-05, см. поправку выше.**) -2. **Приёмный FIFO SIO — 3 байта.** Пачка в 5 байт переживает только то, - что мы успеваем вычерпывать её по ходу. Потерянный make стрелки = - «нажатие не отработало»; потерянный break = залипание (его лечит - `kbd_raw_sync`, но ценой сброса всех немодификаторных клавиш). -3. **Импульс IRQ клавиатуры в MAME живёт 32 такта CPU.** - `mame/sources/MAME/src/mame/sinclair/sprinter.cpp`: `on_kbd_data()` - выставляет `m_irqs->in_set<1>()` НА КАЖДЫЙ принятый байт (то есть старая - запись в `docs/TODO.md` «MAME не даёт per-byte INT» — неверна), но тут же - заводит `m_irq_off_timer` на 32 такта, а `irq_off()` снимает линию. - **Если в эти 32 такта мы под `DI` — прерывание пропало насовсем**, байт - остаётся в FIFO до следующего IRQ (следующий байт или кадровый 50 Гц). -4. **Наши DI-окна длинные.** Ядра акселератора держат `di` на ВЕСЬ блит - (`libbgi/bgi256/_bgi_blit_cols_raw.c:47` — «один DI на весь блит»); - порядок цены прохода — 13.6 К тактов (`libbgi/include/gfx.h`), это - сотни микросекунд, на порядки больше 32-тактового импульса. - -Логика ввода (`pop_ctrl.c`, порт `read_user_control`/`safe_step`) сверена с -SDLPoP и корректна: `safe_step()` ставит `control_forward = CONTROL_IGNORE`, -и это снимается в `read_user_control()` при ОТПУСКАНИИ стрелки — то есть -повторные тапы ← при зажатом Shift обязаны работать. Не работали они -потому, что до нас не доезжал либо make, либо break стрелки. - -### ЧТО ИЗМЕРЕНО (сессия 2026-08-01) — гипотеза про DI НЕ подтвердилась - -**Методика.** Симптом «нажатие не отработало» переведён в счётчики, чтобы не -спорить с глазами. Нажимается **Home** — тоже расширенная клавиша (тот же -`E0`-префикс), но игрой игнорируется, поэтому Кид стоит на месте и рельеф -комнаты на результат не влияет. Брейкпоинты с действием -`{ b@ADDR = b@ADDR + 1 ; g }` (счёт без остановки машины) в трёх точках: вход -клавиатурной ветки трамплина, чтение порта 0x18 внутри drain-цикла, запись -make-бита для кода `0x6C`. Скратч-байты — хвост `ovr_tile[]`. - -**Симптом воспроизведён скриптом:** при зажатом Shift 10 нажатий → до -декодера дошло 9 make-байт. Без Shift потерь нет — ровно как сообщил -пользователь. - -| Прогон | make дошло / нажато | overrun | -|--------|---------------------|---------| -| игра идёт, `kbd_raw_poll` ВКЛ | 9 / 10 | 3 | -| игра идёт, `kbd_raw_poll` ВЫКЛ (патч `ret` в точке входа) | 9 / 10 | 4 | -| игра ЗАМОРОЖЕНА клавишей «1» (блитов нет вообще, значит и длинных DI нет) | **8 / 10** | 6 | - -**Вывод 1: наши DI-окна ни при чём.** В замороженном кадре, где блитов нет -и прерывания разрешены практически всё время, потерь НЕ меньше, а больше. - -**Вывод 2: `kbd_raw_poll()` в той расстановке бесполезен** — 9/10 и с ним, и -без. Причина понятна задним числом: шесть вызовов стояли В ТЕХ ЖЕ точках, -где прерывания и так разрешены. Вызовы из `roomtest.c` убраны; сама функция -в libc оставлена — она корректна и нужна как заготовка под «плотный опрос». - -**Вывод 3 (главный): байт теряется НИЖЕ нашего кода.** Счётчик чтений порта -0x18: 5 нажатий Shift+Home должны дать ровно 50 байт. Насчитано **49** — и -ровно один make потерян. То есть до процессора байт не доехал вообще, -декодер тут ни при чём. - -**Вывод 4: прерывание на байт теряется примерно в 44 % случаев.** На тех же -49 прочитанных байтах — только **28 входов** в клавиатурную ветку трамплина -(1.75 байта за вход). Байты копятся в трёхбайтовом FIFO вплотную к потолку; -одна неудачная пауза — и байт потерян. - -### ПОТОЛОК ПЛОТНОГО ОПРОСА ИЗМЕРЕН — приём лечит полностью - -`tests/kbdpoll` — программа, которая не делает НИЧЕГО, кроме -`kbd_raw_poll()` в бесконечном цикле (ни графики, ни vsync, ни вывода: -любая работа разредила бы опрос и испортила замер). Это физический -максимум плотности. Тот же счётный метод, те же брейкпоинты-счётчики. - -| Прогон | нажатий | make дошло | байт прочитано / ожидалось | -|--------|---------|-----------|-----------------------------| -| контроль: Shift зажат 4 с, нажатий нет | 0 | 0 | 0 (Shift сам ничего не шлёт — автоповтора у модификатора нет) | -| Shift + Home | **25** | **25** | **250 / 250** | - -**Ни одного потерянного байта.** Для сравнения: в игре при шести вызовах -за кадр терялся 1 байт из 50. При такой частоте потерь вероятность -случайно получить ноль потерь на 250 байтах ≈ 0.6 %, так что результат не -совпадение. - -**Вывод: опрос — рабочее решение, вопрос только в ПЛОТНОСТИ.** Нужно -опрашивать примерно раз в 0.5 мс (≈10 000 тактов), а шесть вызовов за -60-мс кадр давали один раз в 10 мс — в двадцать раз реже необходимого. - -**Где взять частоту:** логический тик = 60 мс, из них ~18 мс занято -работой и **~42 мс процессор простаивает внутри `gfx_wait_vsync`**, опрашивая -луч. Опрос там стоит ноль и покрывает две трети периода с запасом. -Остаётся слепым только тело одного accel-блита под DI (до ~650 мкс) — -разорвать его нельзя (см. «что НЕ делать»). - -**Почему нужна именно такая частота** (вопрос «PS/2 же не даёт больше 30 -нажатий в секунду»). Частота опроса определяется НЕ темпом нажатий, а -темпом байт ВНУТРИ одного нажатия и глубиной FIFO. Клавиатура выдаёт байты -со скоростью провода: 11 бит на байт при ~10–16 кГц = ~0.7–1.1 мс на байт. -Воронка — 3 байта. Значит между двумя вычерпываниями имеют право прийти -максимум два байта, то есть вычерпывать надо не реже чем раз в ~1.5–2 мс -(0.5 мс взято с запасом). **Даже ОДНО нажатие в секунду переполнит FIFO**, -если в эти несколько миллисекунд его никто не разгребает. Замер это -подтверждает: 1.75 байта за одно вычерпывание — уже 58 % ёмкости. В норме -разгребает прерывание; опрос понадобился только потому, что ~44 % импульсов -здесь теряется. - -**Альтернатива, которая убирает опрос совсем — уменьшить трафик, а не -ускорять разгребание.** BIOS `$EA` (`FN_KBD_OUT`, `docs/new/09-input.md` -§9.2) шлёт байт НА клавиатуру, то есть ей можно скомандовать: -- **Scan Code Set 3** — нет ни «fake shift», ни `E0`-префиксов: make = 1 байт, - break = 2; -- либо хотя бы отключить typematic (`0xF5`/`0xF7`). - -**Но проверить это в MAME НЕЛЬЗЯ:** в `sprinter.cpp` подключено только -направление клавиатура→SIO (`m_kbd->out_data_cb() → rxa_w`); обратный путь -(SIO→клавиатура) не разведён вовсе, так что команда просто уйдёт в никуда. -Плюс пришлось бы переписать все наши константы кодов под Set 3. Значит это -кандидат на «когда дойдём до реального железа». - -**Что сделано по этому плану (2026-08-01):** -1. ✅ Idle-хук в libbgi: `gfx_set_idle_hook(fn)`, вызывается в цикле ожидания - луча внутри `gfx_wait_vsync`. Приложение ставит туда `kbd_raw_poll`. - Полезен не только нам — любой программе даёт «качать» что-то в ожидании - кадра. Осторожно с регистрами: цикл ждёт на BC-таймауте, вокруг вызова - нужен push/pop, а сам таймаут в итерациях станет длиннее по времени. - **Важно про цену: это НЕ новая нагрузка.** Опрос ставится ровно туда, - где процессор и так впустую крутит `in a,(#0xFE)` — 42 мс из 60. - **Ограничитель области, если «постоянный опрос» не нравится:** потери - случаются ТОЛЬКО при зажатом модификаторе (замерено), значит хук можно - взводить лишь пока нажат Shift/Ctrl/Alt. -2. ✅ Перемерено в roomtest тем же счётным методом: **35/35**, потерь нет. -3. ⏳ Вызовы в занятую треть кадра НЕ возвращены: одного idle-хука хватило. - Держать в уме, если на реальном железе или на более тяжёлых сценах - (несколько стражей) потери появятся снова — накрыть блиты дешевле, чем - изобретать что-то новое. -4. ⏳ Ручная проверка пользователем — «стало значительно лучше, иногда всё - ещё пропускает»; остаток отложен (см. врезку в начале записи). - -**Куда смотреть дальше, если плотного опроса не хватит.** -1. **Драйвер MAME — НЕ ТРОГАЕМ** (решение пользователя: пересборка MAME на - его машине занимает часы). Для протокола, подозрение осталось: - `sinclair/sprinter.cpp` держит запрос от клавиатуры ровно **32 такта - CPU**, и `m_irq_off_timer` — **один на два источника** (`irq_on()` экрана - заводит его же, `irq_off()` гасит разом обе линии). То есть кадровое - прерывание способно обрезать клавиатурный импульс — правдоподобное - объяснение «44 % пропущенных импульсов». -2. **Реальное железо.** Если п.1 — чисто эмуляционный артефакт, на железе - проблемы может не быть вовсе. Проверять при первом прогоне на живом - Sprinter. - -**Про совпадение кадрового и клавиатурного прерываний** (вопрос -пользователя, 2026-08-01). Документация Sprinter: оба приходят с вектором -`0FFh`, различать по биту приёма байта в порту клавиатуры — «не пришёл, -значит экран»; совпадение возможно, но «исключительно редкий случай» -(в новой версии обещают развести жёстче через ПЛМ). То есть наш трамплин -делает ровно предписанное. Известный побочный эффект: при совпадении мы -обслуживаем клавиатуру и `reti`, пропуская кадровую цепочку и DSS — на -потерю байт это не влияет (линия кадрового остаётся взведённой и вызывает -повторный вход), но кадровый тик может пропасть. Отдельная мелкая правка. - -**Что НЕ делать (проверено, стоило времени):** -- **Снимать `di` в accel-ядрах libbgi нельзя.** Патч `di`→`nop` в - `_bgi_blit_cols_raw`/`_bgi_heal_rows_raw`/`_bgi_blit_rows_raw` прямо в - памяти **уронил машину в перезагрузку**. То есть режим «акселератор - работает при EI» из `docs/new/06-accel.md §6.6` в этой прошивке/эмуляции - недоступен — вопрос закрыт артефактом, а не рассуждением. -- Дробить DI-окна по колонкам смысла тоже нет: см. вывод 1. - ---- - -### L5-SHADOW. Тень уровня 5: крадёт зелье — **СДЕЛАНА 2026-08-11** - -Единственная новая механика уровня 5 (тайлов новых нет вовсе, см. цель выше). -Тень появляется в комнате 24, дожидается, пока откроется дверь, идёт к зелью, -**выпивает его** и уходит за левый край. Боя нет. - -**Как это в оригинале** (всё сверено по коду, `custom->*` — это дефолты 1.0): - -| что | где | суть | -|---|---|---| -| появление | `check_shadow`, seg002:0064 | при СМЕНЕ КОМНАТЫ: если `current_level == 5` и `drawn_room == 24`, и тайл (кол 3, ряд 0) всё ещё `tiles_10_potion` — породить тень | -| порождение | `do_init_shad`, seg002:0000 | `memcpy(&Char, init_shad_5, 7)` + `seqtbl_offset_char(2 /*stand*/)`, `charid = charid_1_shadow`, `demo_time = 0`, `guard_skill = 3`, `guardhp_* = 4`, `saveshad()` | -| данные | `init_shad_5` | `{0x0F, 0x37, 0x37, 0, 0xFF, 0, 0}` = frame 15, x 55, y 55, direction 0, curr_col −1, curr_row 0, action 0 | -| поведение | `autocontrol_shadow_level5`, seg002:1157 | в комнате 24: пока `demo_time == 0` — ждать, пока дверь (кол 1, ряд 0) не откроется (`modif >= 80`), затем `demo_index = 0`; дальше каждый кадр `do_auto_moves(shad_drink_move)`; при `Char.x < 15` — `clear_char()` | -| движения | `do_auto_moves`, seg002:1089 | крошечный интерпретатор: `demo_time++`, по таблице `{time, move}` выбирается запись, `move` = 0 nothing / 1 forward / 2 backward / 3 up / 4 down / 5 up+forward / 6 shift / 7 move_7; −1 = ничего, −2 = конец | -| таблица | `shad_drink_move` (data.h:866) | `{0x00,0} {0x01,1} {0x0E,0} {0x12,6} {0x1D,7} {0x2D,2} {0x31,1} {0xFF,−2}` — 8 записей по 2 байта | - -**Что из этого у нас уже есть:** - -- **механизм «спецсобытие порождает персонажа в слоте соперника»** — - `pop_check_skel` (`guards.c`, зовётся из `roomtest.c` в тике); тень - уровня 5 садится на тот же шов, только условие другое; -- **тень как `charid_1_shadow`** — заведена под уровень 4 - ([L4-MIRROR](TASKS_CLOSED.md#l4-mirror)): своя ветка ИИ - (`autocontrol_shadow` + `autocontrol_shadow_level4`), выбор таблицы кадров - Кида (`pop_frame_tbl_is_guard`), отрисовка спрайтами Кида; -- **питьё зелья** — `SEQ_78_DRINK` и `get_item` в `pop_ctrl.c` (Кид уже - умеет); тень «нажимает» те же кнопки через автодвижения; -- **зелья как trob** — фаза пузырька, тип в старших битах (`pop_trob.c`). - -**Что писать:** - -1. `do_auto_moves` + таблица `shad_drink_move` + `demo_time`/`demo_index` — - интерпретатор ~30 строк, кладётся рядом с `autocontrol_shadow` в - `guards.c` (банк 1). «Движения» — это те же переменные управления, что - заполняет `read_user_control` (`pop_ctrl.c`), так что `move_*` сводятся к - присваиваниям. -2. `do_init_shad(init_shad_5, seq stand)` — общий порождатель тени; пригодится - и на уровнях 6 и 12 (`init_shad_6`, `init_shad_12` — те же 7 байт). -3. Ветка `check_shadow` для уровня 5 — по образцу `pop_check_skel`, вызов из - того же места тика. -4. `autocontrol_shadow_level5`. -5. **Ветка ТЕНИ в `check_guard_fallout`** (seg002:0241): тень падает, только - если она в свободном полёте (`action == 4`), и тогда - `loadshad(); clear_char(); saveshad()`. Сейчас в `pop_guard_fallout` - (`pop_guard.c`) есть ветки стража и скелета, а тени нет — комментарий там - обещает её «вместе с L3-SKEL», но она относится именно к тени. - -**Чем подтверждать:** smoke уровня 5 — дойти до комнаты 24, увидеть, как -тень выходит после открытия двери, выпивает зелье (тайл зелья исчезает) и -уходит влево. Сверять последовательность движений с живым SDLPoP на том же -месте — таблица `shad_drink_move` короткая, расхождение будет видно сразу. - -**Оговорка по виду:** тень пока рисуется обычной копией спрайтов Кида, то -есть выглядит вторым Кидом — это [отложенный](../docs/shadow_render.md) -вопрос, к механике уровня 5 отношения не имеет. - ---- - -### GUARD-PHYS. Страж живёт по тем же правилам, что Кид — **ЗАКРЫТА 2026-08-11** - -> **Что уже работает** (решение пользователя: переносим физику на `Char`, -> без предварительных замеров — иначе третий-четвёртый экземпляр той же -> логики неизбежен). -> -> - **физика переведена на `Char`**: `pop_map.c` целиком работает с активным -> персонажем, а кто в `Char` — решают окна `loadkid`/`savekid` и -> `loadshad`/`saveshad`, как в оригинале (seg006:809). Два входа: -> `pop_phys_tick` (порт хвоста play_kid_frame) и `pop_guard_phys_tick` -> (порт play_guard_frame) — списки вызовов отличаются ровно тем, чем в -> оригинале; -> - **`take_hp` стал общим** (`pop_take_hp` в резиденте `pop_guard.c`): урон -> идёт тому, кто в `Char`, по `charid`. Раньше у боёвки и у физики были -> свои копии, причём у физики неверная — правила `hitp_curr` мимо дельты и -> про не-Кидов не знала; -> - **порт веток по charid в `land()`** (seg005:173): страж гибнет с двух -> рядов, тень падает как с одного, у не-Кида приземление даёт боевую -> стойку; `check_guard_bumped` (seg004:0522), `droppedout` + -> `guard_follows_kid_down` (seg002:09F8), `check_guard_fallout` -> (seg002:0241); -> - **`pop_savekid_state` снова полное `Kid = Char`**, а -> `pop_load_fram_det_col` пересчитывает колонку ЛЮБОМУ персонажу — обе -> заплатки существовали только потому, что физика знала один `Kid`. -> -> **Проверено:** `tests-host` — трассы Кида не изменились (`[phys] ok: 1723`, -> тот же эталон), новый набор `t_char` (32 проверки) покрывает ветки по -> персонажам; в MAME проверено, что игра жива (респавн, бег, падение, -> приземление). Цена: `_CODE` +80 Б, банк 3 (`pop_map`) 7973 → 8485 -> (51.8 %), банк 1 (`guards`) 2042 → 2156. -> -> **Проверено пользователем 2026-08-07:** страж СПРЫГИВАЕТ ЗА КИДОМ на ряд -> ниже — связка «ИИ + физика» работает вживую, не только в тестах. -> -> **`follow_guard` портирован и проверен в MAME (2026-08-07):** уровень 1, -> бой в комнате 3, Кид отступает влево — страж приходит следом (`Guard.room` -> 3 → 2, X перенесён через шов), ровно как в SDLPoP. Условия отбора -> покрыты тестами `t_char` (7 сценариев: пороги 91/165, «не бой», мёртвый, -> вверх/вниз, занятая соседняя комната). **Сцена вскрыла отдельный баг — -> [BUG-SWORD-GHOST-1](BUGS_CLOSED.md#bug-sword-ghost-1): при переходе в бою Кид -> прячет меч и дальше дерётся пустой рукой.** -> -> **Осталось (потому и запись открыта) — ревизия 2026-08-11 по коду:** -> 1. ~~`check_chomped_guard`~~ — **сделан** вместе с -> [L3-CHOMP](TASKS_CLOSED.md#l3-chomp) (`pop_map.c`); -> 2. ветки `check_guard_fallout`: **скелет сделан** (возрождается в комнате 3, -> `pop_guard_fallout` в `pop_guard.c`), **ветки ТЕНИ нет** — падает только -> в свободном полёте, `loadshad/clear_char/saveshad`; идёт в -> [L5-SHADOW](#l5-shadow) п. 5 (комментарий в коде обещает её «вместе с -> L3-SKEL» — устарел, это про тень); -> 3. страж, нажимающий напольную кнопку, вживую не проверялся (код — -> общий `check_press`). - -**Почему это была ОДНА физика, а не «сделаем стражу свою».** В оригинале -слот `Guard` — не «стражи», а все НЕ-Киды: тем же `play_guard_frame` ходят -**страж** (charid 2), **скелет** (4, ур. 3), **тень** (1, ур. 4/5/6/12), -**визирь-Джаффар** (ур. 13), **толстяк** (FAT, ур. 12) и **мышь** (0x18, -ур. 8) — `tbl_guard_type` = {0,0,0,2,0,0,1,0,0,0,0,0,4,3,−1,−1}, а -`autocontrol_opponent` (seg002:628) разводит их ТОЛЬКО по ИИ. То есть -второй экземпляр логики пришлось бы делать не один раз, а пять. - -**Гард по X: страж не уходит САМ — но его МОГУТ ПЕРЕНЕСТИ.** Поправка к -формулировке, которая была здесь раньше («страж не покидает комнату ни в -каком виде») — она неверна, контрпример дал пользователь: в SDLPoP страж из -комнаты 3 оказывается в комнате 2 вслед за отступающим Кидом. - -Разделять надо два разных механизма: - -- **своим ходом — не может.** Физика персонажа слота Guard обёрнута - `Char.room == drawn_room` и `Char.x >= 44 && Char.x < 211` (seg000:1252), - и никакого `check_leave` в его списке вызовов нет. Провалившегося ниже - комнаты убирает `check_guard_fallout` (seg002:0241) — вниз он не уходит. -- **следом за Кидом — переносит движок.** `exit_room` (seg002:03C7) - вызывается ПОСЛЕ того, как комнату сменил Кид, и решает судьбу стража: - - ```c - if (Guard.alive < 0 && Guard.sword == sword_2_drawn) { // жив и В БОЮ - if (guards_tile[kid_room−1] >= 30 || // в новой комнате - guards_seq_hi[kid_room−1] != 0) { // своего живого нет - if (ушёл ВЛЕВО) { if (Guard.x >= 91) leave = 1; } // страж далеко — остаётся - else if (ВПРАВО) { if (Guard.x < 165) leave = 1; } - else if (ВВЕРХ) { if (Guard.curr_row >= 0) leave = 1; } // всегда → не идёт - else { if (Guard.curr_row < 3) leave = 1; } // вниз → не идёт - } else leave = 1; - } else leave = 1; - leave ? leave_guard() : follow_guard(); - ``` - - `follow_guard` (seg002:039E) стирает `guards_tile` в ОБЕИХ комнатах - (0xFF — «стража здесь нет», чтобы он не раздвоился) и гонит стража через - `goto_other_room` в окне `loadshad`/`saveshad`. - -**Что это значит для нас.** У нас в `enter_room` (`roomtest.c:265`) стоит -безусловный `pop_guard_leave()` — то есть всегда ветка `leave_guard`, и -страж ВСЕГДА остаётся. Портировать надо сам `exit_room`-выбор: условия -«жив + меч вынут + в целевой комнате нет своего стража + он у нужного -края» и `follow_guard`. Пороги 91/165 — это «страж у того края, в который -ушёл Кид»; вверх и вниз оригинал не пускает никогда. - -**Чем подтверждать:** уровень 1, комната 3 — начать бой, отступить влево в -комнату 2: страж обязан прийти следом и продолжить бой (как в SDLPoP на -скриншотах пользователя). Обратная проверка: если в соседней комнате СВОЙ -живой страж — переход не происходит. - ---- - -### L6-SHADOW. Тень уровня 6 роняет решётку — СДЕЛАНА 2026-08-11 - -**Сцена (уровень 6, комната 1).** Кид жмёт `opener` (1,7) — решётка (1,2) -поднимается; он разбегается, прыгает через четырёхтайловую пропасть и -цепляется за порожек решётки, чтобы подтянуться. В этот момент тень делает -ОДИН осторожный шаг с границы (1,0)-(1,1) на `closer` (1,1), и решётка -падает. Обе плиты комнаты ведут на один тайл 12 (проверено по LINKLOC). - -**Порт.** Две точки, обе в `seg002.c`: - -- `check_shadow` (seg002:0090): условий на содержимое комнаты нет — тень - встаёт при КАЖДОМ входе в комнату 1. `do_init_shad` переписан под - таблицы (`init_shad_5`/`init_shad_6` — первые 7 полей `Char`, как - `memcpy(&Char, source, 7)` у оригинала); -- `autocontrol_shadow_level6` (seg002:1064): `Kid.frame == 43` (кадр - бег-прыжка) и `Kid.x < 128` -> `move_6_shift()` + `move_1_forward()`. - Это Shift+вперёд, то есть ОСТОРОЖНЫЙ ШАГ, а не прыжок; кадр 43 — - условие на КИДА, тень им только триггерится. - -**Звук** `sound_25_presentation` не портирован (звука нет вовсе); флаг -оригинала `leveldoor_open = 0x4D` не воспроизводим — чужую переменную -занимать незачем. - -**Проверено в MAME (пользователь, 2026-08-11):** «Кид прыгнул, зацепился, -Тень сделал шаг, решётка упала». Тем самым закрыт и остаток -[GUARD-PHYS](TASKS_CLOSED.md#guard-phys): нажатие плиты НЕ-Кидом работает -(`check_press` в `guard_phys`, без гейта по `charid`). - -**Фон комнаты сверен с оригиналом** методом из -[`BUGS_CLOSED.md`](BUGS_CLOSED.md#bug-lattice-doortop): 539 пикселей -расхождения, и все — силуэты персонажей (тень пока рисуется палитрой Кида, -см. [`../docs/shadow_render.md`](../docs/shadow_render.md)). - -**Цена:** банк 1 2755 -> 2877 Б, резидент без изменений. - ---- - -### Переход 6 -> 7 падением — СДЕЛАН 2026-08-11 - -Уровень 6 не заканчивается дверью: Кид проваливается вниз из комнаты 1 и -этим попадает на 7-й. Порт двух половин: - -- **выход** — `leave_room` (seg002:0504) для направления «вниз» на уровне 6 - из комнаты 1 возвращает особый результат −2, а главный цикл (seg000:0893) - разбирает его как `Kid.y = -1; ++next_level`. У нас это ветка в обработке - `pop_fell_out` (`roomtest.c`), и она ОБЯЗАНА идти раньше связи вниз: у - комнаты 1 сосед снизу есть (шахта, комната 3), и без спецсобытия Кид - улетал туда, а оттуда — в рестарт уровня; -- **вход** — `set_start_pos` (seg003:0196): на 7-м уровне Кид ставится в - комнату 17, после чего экран сразу переводится на комнату ПОД ней - (`goto_other_room(3)`: y −= 189, ряд пересчитать) — он влетает сверху. - -Константы `FALLING_EXIT_LEVEL/ROOM`, `FALLING_ENTRY_LEVEL/ROOM` — в -`pop_guard.h`. - -**Проверено пользователем в MAME:** «переход на уровень 7 сработал». - -**Остаток на следующую сессию:** влетая в комнату, Кид должен уметь -зацепиться за край пола, мимо которого пролетает — этой механики у нас, -похоже, нет (см. `NEXT_SESSION.md`, п. 1). - ---- - - -## HOF-ENTRY. Таблица рекордов — **СДЕЛАНО И ПРОВЕРЕНО В MAME 2026-08-26** - -Порт двух показов оригинала. `pop_hof.c` был написан раньше, но никуда не -вызывался: после финала автомат уходил мимо него в title, а на титрах экран -получался ЧЁРНЫМ. - -**Где это в оригинале.** `show_title()` (seg000:2060) показывает -сохранённую таблицу между credits и attract-demo — 240 тиков, переходом -слева направо, без fade. `end_sequence()` (seg001:5F1) после HAIL проявляет -титульную картинку (240 тиков), при подходящем результате гасит её, показывает -таблицу с золотой полосой ввода, принимает имя, ждёт 120 тиков, снова -проявляет титульную картинку и лишь потом дослушивает победную тему -(`while (check_sound_playing() && !key_test_quit())`, seg001:637). - -**Два корня, из-за которых экран был пуст.** - -1. `story.pal` — 256 записей, и запись 0x3F (цвет глифов шрифта, - `POP_FONT_COLOR`) в ней ЧЁРНАЯ. Текст рисовался, но был не виден. - Теперь генератор кладёт в 0x3F золотой 0xB7 оригинала. -2. Полосовой переход копирует страницу АКСЕЛЕРАТОРОМ, а тот читает ОЗУ-копию - (`gfx_copy_page`: «копируется чистый фон без спрайтов»). `GFX_BANK_SPRITE` - — это NOSHADOW + TRANSPARENT, то есть в копию он не пишет, и на титрах - переезжал один фон без строк. Лечится банком `GFX_BANK_TRANSPARENT` - (0x58): та же прозрачность 0xFF, но с записью в копию — страница - собирается ЦЕЛИКОМ до показа, как offscreen оригинала. - -**Что появилось попутно.** - -- `s5` в архиве PV — фон таблицы: рамка story + логотип PRINCE OF PERSIA на - y=24 (`HOF_POP` оригинала — тот же спрайт res54, что и в титрах). -- Отдельный индекс палитры под фон текстовой рамки (`POP_PAL_STORY_BG`, 16). - `load_title_images(bgcolor)` красит его в #100060 на титрах и в #800000 в - финале; раньше фон был ремаплен в индекс 9, а тот занят самой титульной - картинкой (8265 пикселей), и подменить его было нельзя. -- Второй набор глифов в `font.atl` цветом `POP_FONT_DARK_COLOR` (0x3E). - У SDLPoP шрифт — маска, и `show_hof_text` рисует текст дважды разным - цветом; у нас цвет запечён в пиксели, поэтому «другой цвет» = другой набор. - Каталог SPA1 держит счётчик картинок в одном байте, поэтому тёмный набор - обрезан по '_' (32..95): 190 + 64 = 254 из 255 возможных. -- Общий `pop_screen_present_ltr()` в `pop_ui.c` — им теперь пользуются и - story-переход интро, и таблица, и титульная картинка финала - ([CUTSCENE-LTR](BUGS_CLOSED.md#cutscene-ltr)). - -**Формат `POP.HOF`** — 6 записей (как `MAX_HOF_COUNT`), имя до 15 символов, -XOR+инверсия в хвосте; версия 2. Проверено на образе: после ввода имени файл -128 Б, `PHOF`, запись читается обратно и показывается на титрах. - -**Что проверено в MAME** (сборка `LEVEL=14`, вход в комнату 5 читом обхода -комнат): цепочка HAIL → титульная картинка → таблица с полосой ввода и -временем справа → ввод имени → титульная картинка; на следующем запуске -таблица показывается между credits и demo. diff --git a/applications/PoP/roomtest/TASKS_OPEN.md b/applications/PoP/roomtest/TASKS_OPEN.md deleted file mode 100644 index 76f4304..0000000 --- a/applications/PoP/roomtest/TASKS_OPEN.md +++ /dev/null @@ -1,1511 +0,0 @@ -# roomtest — доска ОТКРЫТЫХ задач (обновлено 2026-08-11) - -Что берём в работу сейчас и в каком порядке. Каждая запись: что сделать, -почему именно сейчас, чем подтверждать результат. - -- закрытые задачи с протоколами и замерами — [`TASKS_CLOSED.md`](TASKS_CLOSED.md); -- открытые баги — [`BUGS_OPEN.md`](BUGS_OPEN.md), закрытые с разбором корней — - [`BUGS_CLOSED.md`](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`](TASKS_CLOSED.md#pass-policy); отдельных задач - `L3-PASS`/`L4-PASS` больше нет. Уже сделанные полные обходы уровней 1 и 2 - остаются регресс-базой; -- **[L4-MIRROR](TASKS_CLOSED.md#l4-mirror) закрыта**: зеркало, отражение, - прыжок сквозь него и рождение тени проверены в MAME. Хвосты — вид тени - (отложен, [`../docs/shadow_render.md`](../docs/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`](../docs/perf_green_phase.md), -[`../docs/perf_cyan_phase.md`](../docs/perf_cyan_phase.md), сцена и метод — -[`../docs/perf_l13_room23.md`](../docs/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`](../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` — самая большая функция `roomtest.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`](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`](../docs/perf_l11_room15.md) -§7), плюс визуальный прогон боя и прохода Кида под факелами: персонаж не -должен оставлять хвостов и не должен просвечивать сквозь пламя. - -**Место в очереди — [`../docs/perf_registry.md`](../docs/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](../docs/perf_green_phase.md) (пометка соседа узкой полосой).** -Это две половины одной темы, и брать их логично вместе: G8 про ширину -ЗАПЕЧКИ соседнего тайла (60 вместо 28 нужных, да ещё `draw_tile` соседа -дважды на пометку), HEAL-WIDTH — про ширину HEAL'ов (64 вместо 58/57). -Числа там уже разобраны: 60 = 32 свой тайл + 28 собственный свес, и для -запечки САМОГО тайла 60 минимальны — сужать можно только пометку СОСЕДА. - -**Место в общей очереди — [`../docs/perf_registry.md`](../docs/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`](../docs/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, без двери) | `roomtest.c` + `pop_start_level` | seg000:0900 | - -Осознанное расхождение: мигания Кида спрайтами тени во время вспышки нет — -запись в [`../docs/impl_diff.md`](../docs/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`](../docs/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-roomtest/`): - -``` -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`, `roomtest.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` дверь - уровня, `roomtest.c:473/663/887` (читы и старт), `pop_level.c:131/132` - (имя файла уровня), `roomtest.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`](../docs/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` по адресам из -`roomtest.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`](../docs/perf_l13_room23.md); -фазы — [зелёная](../docs/perf_green_phase.md), [циан](../docs/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`](TASKS_CLOSED.md#qsave); -формат снимка и разбор SDLPoP — -[`../docs/quicksave_plan.md`](../docs/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 мс**. У нас `roomtest.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`](../docs/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`](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`](TASKS_CLOSED.md#mem-bank5). - ---- - -## Отложено осознанно (не брать, пока не появится причина) - -- **KBD-1, остаток** — «иногда при зажатом Shift стрелка всё-таки - пропускается» (ощущение пользователя, не замер: счётчики на 35 нажатиях - подряд потерь не показывают). Решение пользователя 2026-08-01: отложено до - финальной полировки. Где именно осталась дыра и что делать, если вернёмся, - — в [`TASKS_CLOSED.md`](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`](BUGS_CLOSED.md). - ---- - -## OPT-BLIT — цена отрисовки: чистка кодогена (СЛЕДУЮЩАЯ СЕССИЯ) - -> **2026-08-17: контрольный замер снят, задача разложена по фазам.** -> Работа теперь ведётся в трёх документах, которые живут между сессиями — -> туда же писать результаты каждой правки: -> -> - [`../docs/perf_l13_room23.md`](../docs/perf_l13_room23.md) — сцена -> (каскад 6 плит, ур. 13 комната 23), рецепт воспроизведения, зонды, канал -> `clog`, сводка по кадрам, разбор габаритов спрайтов; -> - [`../docs/perf_green_phase.md`](../docs/perf_green_phase.md) — ЗЕЛЁНАЯ -> фаза: пик **805 000** при бюджете 400 000; позиции G1..G6; -> - [`../docs/perf_cyan_phase.md`](../docs/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-roomtest/*.asm` — исчез ли `ld iy,#-N / add iy,sp / ld sp,iy` -и сколько осталось `-N (ix)` в теле. - -### б) Проход по asm за неоптимальным IX-доступом - -Механическая проверка, даёт больше всего за единицу усилий: - -``` -grep -c "(ix)" .sprinter-cc-roomtest/*.asm # где сгущается -grep -n "ld iy, #-" .sprinter-cc-roomtest/*.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/PoP/roomtest/_pop_bg.h b/applications/PoP/roomtest/_pop_bg.h deleted file mode 100644 index 5ff9578..0000000 --- a/applications/PoP/roomtest/_pop_bg.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * _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/PoP/roomtest/_pop_config.h b/applications/PoP/roomtest/_pop_config.h deleted file mode 100644 index cbd7e74..0000000 --- a/applications/PoP/roomtest/_pop_config.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * _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/PoP/roomtest/_pop_draw.h b/applications/PoP/roomtest/_pop_draw.h deleted file mode 100644 index 1cbe465..0000000 --- a/applications/PoP/roomtest/_pop_draw.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * _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/PoP/roomtest/_pop_kdraw.h b/applications/PoP/roomtest/_pop_kdraw.h deleted file mode 100644 index f3988d2..0000000 --- a/applications/PoP/roomtest/_pop_kdraw.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * _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/PoP/roomtest/_pop_kid.h b/applications/PoP/roomtest/_pop_kid.h deleted file mode 100644 index ceb017e..0000000 --- a/applications/PoP/roomtest/_pop_kid.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * _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/PoP/roomtest/_pop_level.h b/applications/PoP/roomtest/_pop_level.h deleted file mode 100644 index 8bfabae..0000000 --- a/applications/PoP/roomtest/_pop_level.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * _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/PoP/roomtest/_pop_sfx.h b/applications/PoP/roomtest/_pop_sfx.h deleted file mode 100644 index b765214..0000000 --- a/applications/PoP/roomtest/_pop_sfx.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * _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/PoP/roomtest/_pop_tile.h b/applications/PoP/roomtest/_pop_tile.h deleted file mode 100644 index 2069088..0000000 --- a/applications/PoP/roomtest/_pop_tile.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * _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/PoP/roomtest/bg_arc.h b/applications/PoP/roomtest/bg_arc.h deleted file mode 100644 index 581908e..0000000 --- a/applications/PoP/roomtest/bg_arc.h +++ /dev/null @@ -1,34 +0,0 @@ -/* 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/PoP/roomtest/guard_arc.h b/applications/PoP/roomtest/guard_arc.h deleted file mode 100644 index f930261..0000000 --- a/applications/PoP/roomtest/guard_arc.h +++ /dev/null @@ -1,19 +0,0 @@ -/* 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/PoP/roomtest/guards.c b/applications/PoP/roomtest/guards.c deleted file mode 100644 index 8126101..0000000 --- a/applications/PoP/roomtest/guards.c +++ /dev/null @@ -1,1164 +0,0 @@ -/* - * 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/PoP/roomtest/kid_arc.h b/applications/PoP/roomtest/kid_arc.h deleted file mode 100644 index db3d846..0000000 --- a/applications/PoP/roomtest/kid_arc.h +++ /dev/null @@ -1,67 +0,0 @@ -/* 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/PoP/roomtest/kid_data.h b/applications/PoP/roomtest/kid_data.h deleted file mode 100644 index 09debc0..0000000 --- a/applications/PoP/roomtest/kid_data.h +++ /dev/null @@ -1,77 +0,0 @@ -/* 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/PoP/roomtest/pop_app.c b/applications/PoP/roomtest/pop_app.c deleted file mode 100644 index 3dcaf14..0000000 --- a/applications/PoP/roomtest/pop_app.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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/PoP/roomtest/pop_app.h b/applications/PoP/roomtest/pop_app.h deleted file mode 100644 index 87c3dbb..0000000 --- a/applications/PoP/roomtest/pop_app.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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/PoP/roomtest/pop_arc.c b/applications/PoP/roomtest/pop_arc.c deleted file mode 100644 index 12ad845..0000000 --- a/applications/PoP/roomtest/pop_arc.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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/PoP/roomtest/pop_arc.h b/applications/PoP/roomtest/pop_arc.h deleted file mode 100644 index 20cd249..0000000 --- a/applications/PoP/roomtest/pop_arc.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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/PoP/roomtest/pop_bg.c b/applications/PoP/roomtest/pop_bg.c deleted file mode 100644 index d7b1bca..0000000 --- a/applications/PoP/roomtest/pop_bg.c +++ /dev/null @@ -1,1176 +0,0 @@ -/* - * 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 roomtest: ряд 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/PoP/roomtest/pop_bg.h b/applications/PoP/roomtest/pop_bg.h deleted file mode 100644 index c35bb14..0000000 --- a/applications/PoP/roomtest/pop_bg.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - * 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/PoP/roomtest/pop_cdraw.c b/applications/PoP/roomtest/pop_cdraw.c deleted file mode 100644 index 9f9ec6f..0000000 --- a/applications/PoP/roomtest/pop_cdraw.c +++ /dev/null @@ -1,1058 +0,0 @@ -/* - * 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/PoP/roomtest/pop_cdraw.h b/applications/PoP/roomtest/pop_cdraw.h deleted file mode 100644 index 8049dc7..0000000 --- a/applications/PoP/roomtest/pop_cdraw.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * 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/PoP/roomtest/pop_cfg.h b/applications/PoP/roomtest/pop_cfg.h deleted file mode 100644 index 78eefe9..0000000 --- a/applications/PoP/roomtest/pop_cfg.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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/PoP/roomtest/pop_char.h b/applications/PoP/roomtest/pop_char.h deleted file mode 100644 index b12e3a7..0000000 --- a/applications/PoP/roomtest/pop_char.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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/PoP/roomtest/pop_cheat.h b/applications/PoP/roomtest/pop_cheat.h deleted file mode 100644 index 4b24108..0000000 --- a/applications/PoP/roomtest/pop_cheat.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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/PoP/roomtest/pop_config.c b/applications/PoP/roomtest/pop_config.c deleted file mode 100644 index e5a2d3f..0000000 --- a/applications/PoP/roomtest/pop_config.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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; /* пока roomtest — 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/PoP/roomtest/pop_ctrl.c b/applications/PoP/roomtest/pop_ctrl.c deleted file mode 100644 index a3dc5dd..0000000 --- a/applications/PoP/roomtest/pop_ctrl.c +++ /dev/null @@ -1,863 +0,0 @@ -/* - * 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/PoP/roomtest/pop_ctrl.h b/applications/PoP/roomtest/pop_ctrl.h deleted file mode 100644 index 8964ae0..0000000 --- a/applications/PoP/roomtest/pop_ctrl.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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/PoP/roomtest/pop_cutscene.c b/applications/PoP/roomtest/pop_cutscene.c deleted file mode 100644 index 7c61cca..0000000 --- a/applications/PoP/roomtest/pop_cutscene.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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/PoP/roomtest/pop_cutscene.h b/applications/PoP/roomtest/pop_cutscene.h deleted file mode 100644 index d49303b..0000000 --- a/applications/PoP/roomtest/pop_cutscene.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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/PoP/roomtest/pop_demo.c b/applications/PoP/roomtest/pop_demo.c deleted file mode 100644 index 4aaa18c..0000000 --- a/applications/PoP/roomtest/pop_demo.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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/PoP/roomtest/pop_demo.h b/applications/PoP/roomtest/pop_demo.h deleted file mode 100644 index e4c0002..0000000 --- a/applications/PoP/roomtest/pop_demo.h +++ /dev/null @@ -1,17 +0,0 @@ -/* 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/PoP/roomtest/pop_draw.c b/applications/PoP/roomtest/pop_draw.c deleted file mode 100644 index 6be995e..0000000 --- a/applications/PoP/roomtest/pop_draw.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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/PoP/roomtest/pop_flow.c b/applications/PoP/roomtest/pop_flow.c deleted file mode 100644 index b96fa98..0000000 --- a/applications/PoP/roomtest/pop_flow.c +++ /dev/null @@ -1,48 +0,0 @@ -/* 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/PoP/roomtest/pop_flow.h b/applications/PoP/roomtest/pop_flow.h deleted file mode 100644 index 47022cc..0000000 --- a/applications/PoP/roomtest/pop_flow.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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/PoP/roomtest/pop_font.h b/applications/PoP/roomtest/pop_font.h deleted file mode 100644 index 1ef09fc..0000000 --- a/applications/PoP/roomtest/pop_font.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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/PoP/roomtest/pop_geom.c b/applications/PoP/roomtest/pop_geom.c deleted file mode 100644 index 318e24a..0000000 --- a/applications/PoP/roomtest/pop_geom.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * 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/PoP/roomtest/pop_geom.h b/applications/PoP/roomtest/pop_geom.h deleted file mode 100644 index 67ef250..0000000 --- a/applications/PoP/roomtest/pop_geom.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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/PoP/roomtest/pop_guard.c b/applications/PoP/roomtest/pop_guard.c deleted file mode 100644 index 099e852..0000000 --- a/applications/PoP/roomtest/pop_guard.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * 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/PoP/roomtest/pop_guard.h b/applications/PoP/roomtest/pop_guard.h deleted file mode 100644 index b0f8aa9..0000000 --- a/applications/PoP/roomtest/pop_guard.h +++ /dev/null @@ -1,393 +0,0 @@ -/* - * 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). Нужна ЧИТУ - * навигации (`+`/`−` в roomtest.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/PoP/roomtest/pop_guard_cold.c b/applications/PoP/roomtest/pop_guard_cold.c deleted file mode 100644 index 9b74cbb..0000000 --- a/applications/PoP/roomtest/pop_guard_cold.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * pop_guard_cold.c — переходы стража МЕЖДУ КОМНАТАМИ (банк 8): страж идёт - * за Кидом (follow_guard, seg002:039E) и вход стража в комнату - * (enter_guard, seg002:0112 + pos_guards, seg003). - * - * Обе зовутся РАЗ НА КОМНАТУ и обе — из roomtest_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/PoP/roomtest/pop_guard_pal.h b/applications/PoP/roomtest/pop_guard_pal.h deleted file mode 100644 index 8e394f4..0000000 --- a/applications/PoP/roomtest/pop_guard_pal.h +++ /dev/null @@ -1,36 +0,0 @@ -/* 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/PoP/roomtest/pop_hof.c b/applications/PoP/roomtest/pop_hof.c deleted file mode 100644 index 778838a..0000000 --- a/applications/PoP/roomtest/pop_hof.c +++ /dev/null @@ -1,530 +0,0 @@ -/* - * 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/PoP/roomtest/pop_hof.h b/applications/PoP/roomtest/pop_hof.h deleted file mode 100644 index dbe34be..0000000 --- a/applications/PoP/roomtest/pop_hof.h +++ /dev/null @@ -1,21 +0,0 @@ -/* 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/PoP/roomtest/pop_intro.c b/applications/PoP/roomtest/pop_intro.c deleted file mode 100644 index 1a17e39..0000000 --- a/applications/PoP/roomtest/pop_intro.c +++ /dev/null @@ -1,1817 +0,0 @@ -/* - * 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_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())`), и - * пока музыки у нас не было, паузы стояли укороченными «на глаз». Теперь - * они равны реальной длине наших записей (pop_music_tbl.h, тики = блоки * - * 128 / 10937,5 * 60): m50 — 831, m53 — 985 тиков. */ -enum { - PV_MUS_2_START = 12, /* frame(2), затем «принцесса ждёт» (m50) */ - PV_MUS_3_LOAD = 60, /* подкачка следующей реплики, m50 звучит */ - PV_M50_END = 846, /* 12 + 834: конец m50, кадр становится 8 */ - PV_WAIT_END = 886, /* +frame(5) -> створка ворот */ - PV_GATE_END = 934, - PV_DOOR_END = 958, - PV_TURN_START = PV_DOOR_END, - PV_WALK1_START = 998, - PV_DIALOG1_START = 1046, /* Джафар входит — реплика m53 */ - PV_MUS_4_LOAD = 1100, /* подкачка m52, Джафар стоит на месте */ - PV_WALK2_START = 1222, - PV_DIALOG2_START = 1462, - PV_RAISE_START = 1742, - PV_STEPBACK_START = 1749, - PV_MAGIC_START = 1868, - PV_EXIT_START = 2031, /* 1046 + 985: конец m53 */ - PV_MUS_4_START = 2073, /* +frame(6) -> «Джафар уходит» (m52) */ - PV_GLASS_DONE = 2241, - PV_SLUMP_START = 2304, - PV_ANIM_TICKS = 2500 -}; - -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/PoP/roomtest/pop_intro.h b/applications/PoP/roomtest/pop_intro.h deleted file mode 100644 index 105d960..0000000 --- a/applications/PoP/roomtest/pop_intro.h +++ /dev/null @@ -1,29 +0,0 @@ -/* 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/PoP/roomtest/pop_kboot.c b/applications/PoP/roomtest/pop_kboot.c deleted file mode 100644 index 9405989..0000000 --- a/applications/PoP/roomtest/pop_kboot.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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/PoP/roomtest/pop_kdraw.c b/applications/PoP/roomtest/pop_kdraw.c deleted file mode 100644 index dcc6fdc..0000000 --- a/applications/PoP/roomtest/pop_kdraw.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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/PoP/roomtest/pop_kid.c b/applications/PoP/roomtest/pop_kid.c deleted file mode 100644 index e2d8d71..0000000 --- a/applications/PoP/roomtest/pop_kid.c +++ /dev/null @@ -1,470 +0,0 @@ -/* - * 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 проваливался сквозь пол. - * Артефакт: .sprinter-cc-roomtest/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/PoP/roomtest/pop_kid.h b/applications/PoP/roomtest/pop_kid.h deleted file mode 100644 index 0578d87..0000000 --- a/applications/PoP/roomtest/pop_kid.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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/PoP/roomtest/pop_level.c b/applications/PoP/roomtest/pop_level.c deleted file mode 100644 index 4edd4f4..0000000 --- a/applications/PoP/roomtest/pop_level.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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/PoP/roomtest/pop_level.h b/applications/PoP/roomtest/pop_level.h deleted file mode 100644 index 5c359c9..0000000 --- a/applications/PoP/roomtest/pop_level.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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/PoP/roomtest/pop_level_cold.c b/applications/PoP/roomtest/pop_level_cold.c deleted file mode 100644 index 5ba3b67..0000000 --- a/applications/PoP/roomtest/pop_level_cold.c +++ /dev/null @@ -1,326 +0,0 @@ -/* - * 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/PoP/roomtest/pop_level_qsave.c b/applications/PoP/roomtest/pop_level_qsave.c deleted file mode 100644 index 02c5e6b..0000000 --- a/applications/PoP/roomtest/pop_level_qsave.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Сериализация уровня для 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), см. roomtest. */ - 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, roomtest_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/PoP/roomtest/pop_map.h b/applications/PoP/roomtest/pop_map.h deleted file mode 100644 index e34c3d9..0000000 --- a/applications/PoP/roomtest/pop_map.h +++ /dev/null @@ -1,285 +0,0 @@ -/* - * 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/PoP/roomtest/pop_menu.c b/applications/PoP/roomtest/pop_menu.c deleted file mode 100644 index 3bfae66..0000000 --- a/applications/PoP/roomtest/pop_menu.c +++ /dev/null @@ -1,767 +0,0 @@ -/* - * 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); -} - -/* В roomtest ещё нет автомата 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/PoP/roomtest/pop_menu.h b/applications/PoP/roomtest/pop_menu.h deleted file mode 100644 index ef12301..0000000 --- a/applications/PoP/roomtest/pop_menu.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * pop_menu.h — контракт pause menu (план docs/menu_settings_plan.md §10.3, - * этап MS0). - * - * UI НЕ вызывает gameplay-модули напрямую: меню формирует КОМАНДУ, а - * исполнение остаётся в главном цикле (roomtest.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; - -/* Флаг запроса в roomtest.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/PoP/roomtest/pop_music.c b/applications/PoP/roomtest/pop_music.c deleted file mode 100644 index b6848df..0000000 --- a/applications/PoP/roomtest/pop_music.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - * 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/PoP/roomtest/pop_music.h b/applications/PoP/roomtest/pop_music.h deleted file mode 100644 index 9182d4a..0000000 --- a/applications/PoP/roomtest/pop_music.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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/PoP/roomtest/pop_music_tbl.h b/applications/PoP/roomtest/pop_music_tbl.h deleted file mode 100644 index 94c7e00..0000000 --- a/applications/PoP/roomtest/pop_music_tbl.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * pop_music_tbl.h — каталог музыкальных треков, СГЕНЕРИРОВАН. - * Источник: applications/PoP/PoP1_DOS_music/flac (записи DOS-версии). - * Генератор: toolchain/pop_pack_music.py. Руками не править. - * - * Нумерация id — оригинала (SDLPoP data/music/names.txt). pages — - * сколько кусков m_.bin лежит на диске, blocks — длина трека - * в порциях насоса по 128 байт (в байтах не влезло бы в uint16). - */ -#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/PoP/roomtest/pop_pace.c b/applications/PoP/roomtest/pop_pace.c deleted file mode 100644 index 0377422..0000000 --- a/applications/PoP/roomtest/pop_pace.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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/PoP/roomtest/pop_pace.h b/applications/PoP/roomtest/pop_pace.h deleted file mode 100644 index be70a04..0000000 --- a/applications/PoP/roomtest/pop_pace.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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/PoP/roomtest/pop_pal.c b/applications/PoP/roomtest/pop_pal.c deleted file mode 100644 index ef1924d..0000000 --- a/applications/PoP/roomtest/pop_pal.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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); -} - -/* Слот «вне игрового поля» (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/PoP/roomtest/pop_pal.h b/applications/PoP/roomtest/pop_pal.h deleted file mode 100644 index fe2f00e..0000000 --- a/applications/PoP/roomtest/pop_pal.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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; - -/* Слот 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/PoP/roomtest/pop_qsave.c b/applications/PoP/roomtest/pop_qsave.c deleted file mode 100644 index 5a48df4..0000000 --- a/applications/PoP/roomtest/pop_qsave.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - * 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 "roomtest_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/PoP/roomtest/pop_qsave.h b/applications/PoP/roomtest/pop_qsave.h deleted file mode 100644 index 564c296..0000000 --- a/applications/PoP/roomtest/pop_qsave.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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/PoP/roomtest/pop_qsave_coldio.c b/applications/PoP/roomtest/pop_qsave_coldio.c deleted file mode 100644 index 511db08..0000000 --- a/applications/PoP/roomtest/pop_qsave_coldio.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Холодная часть ввода-вывода 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/PoP/roomtest/pop_qsave_io.c b/applications/PoP/roomtest/pop_qsave_io.c deleted file mode 100644 index cc4fc28..0000000 --- a/applications/PoP/roomtest/pop_qsave_io.c +++ /dev/null @@ -1,34 +0,0 @@ -/* Резидентная часть 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/PoP/roomtest/pop_redraw.c b/applications/PoP/roomtest/pop_redraw.c deleted file mode 100644 index d8e3018..0000000 --- a/applications/PoP/roomtest/pop_redraw.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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/PoP/roomtest/pop_redraw.h b/applications/PoP/roomtest/pop_redraw.h deleted file mode 100644 index 7ab303f..0000000 --- a/applications/PoP/roomtest/pop_redraw.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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/PoP/roomtest/pop_room.c b/applications/PoP/roomtest/pop_room.c deleted file mode 100644 index 23e4f7a..0000000 --- a/applications/PoP/roomtest/pop_room.c +++ /dev/null @@ -1,2009 +0,0 @@ -/* - * 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. Куча roomtest не - * используется (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 (roomtest обновляет из - * 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/PoP/roomtest/pop_settings.h b/applications/PoP/roomtest/pop_settings.h deleted file mode 100644 index bd2889e..0000000 --- a/applications/PoP/roomtest/pop_settings.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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, /* текущее поведение roomtest (план §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/PoP/roomtest/pop_sfx.c b/applications/PoP/roomtest/pop_sfx.c deleted file mode 100644 index 0afa57c..0000000 --- a/applications/PoP/roomtest/pop_sfx.c +++ /dev/null @@ -1,310 +0,0 @@ -/* - * 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/PoP/roomtest/pop_sfx.h b/applications/PoP/roomtest/pop_sfx.h deleted file mode 100644 index 42adc10..0000000 --- a/applications/PoP/roomtest/pop_sfx.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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/PoP/roomtest/pop_sfx_cold.c b/applications/PoP/roomtest/pop_sfx_cold.c deleted file mode 100644 index 425a8e4..0000000 --- a/applications/PoP/roomtest/pop_sfx_cold.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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/PoP/roomtest/pop_shadow.c b/applications/PoP/roomtest/pop_shadow.c deleted file mode 100644 index 721dcb2..0000000 --- a/applications/PoP/roomtest/pop_shadow.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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/PoP/roomtest/pop_shadow.h b/applications/PoP/roomtest/pop_shadow.h deleted file mode 100644 index 7463e2f..0000000 --- a/applications/PoP/roomtest/pop_shadow.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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/PoP/roomtest/pop_shadow_atlas.h b/applications/PoP/roomtest/pop_shadow_atlas.h deleted file mode 100644 index 7c46fe7..0000000 --- a/applications/PoP/roomtest/pop_shadow_atlas.h +++ /dev/null @@ -1,71 +0,0 @@ -/* 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/PoP/roomtest/pop_sound_tbl.h b/applications/PoP/roomtest/pop_sound_tbl.h deleted file mode 100644 index c6d9fb7..0000000 --- a/applications/PoP/roomtest/pop_sound_tbl.h +++ /dev/null @@ -1,113 +0,0 @@ -/* 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/PoP/roomtest/pop_state.c b/applications/PoP/roomtest/pop_state.c deleted file mode 100644 index 4304f34..0000000 --- a/applications/PoP/roomtest/pop_state.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * 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/PoP/roomtest/pop_state.h b/applications/PoP/roomtest/pop_state.h deleted file mode 100644 index ae167a6..0000000 --- a/applications/PoP/roomtest/pop_state.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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/PoP/roomtest/pop_status.c b/applications/PoP/roomtest/pop_status.c deleted file mode 100644 index 98daa86..0000000 --- a/applications/PoP/roomtest/pop_status.c +++ /dev/null @@ -1,585 +0,0 @@ -/* - * 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 байт, и семь из них просто переехали сюда из прежнего - * ярлыка комнаты в roomtest_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/PoP/roomtest/pop_status.h b/applications/PoP/roomtest/pop_status.h deleted file mode 100644 index f75fef0..0000000 --- a/applications/PoP/roomtest/pop_status.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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/PoP/roomtest/pop_tile.c b/applications/PoP/roomtest/pop_tile.c deleted file mode 100644 index da9fdab..0000000 --- a/applications/PoP/roomtest/pop_tile.c +++ /dev/null @@ -1,624 +0,0 @@ -/* - * 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/PoP/roomtest/pop_tile.h b/applications/PoP/roomtest/pop_tile.h deleted file mode 100644 index 8de2a70..0000000 --- a/applications/PoP/roomtest/pop_tile.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * 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/PoP/roomtest/pop_tile_cold.c b/applications/PoP/roomtest/pop_tile_cold.c deleted file mode 100644 index f4daa3f..0000000 --- a/applications/PoP/roomtest/pop_tile_cold.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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/PoP/roomtest/pop_timer.c b/applications/PoP/roomtest/pop_timer.c deleted file mode 100644 index 58ed531..0000000 --- a/applications/PoP/roomtest/pop_timer.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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/PoP/roomtest/pop_timer.h b/applications/PoP/roomtest/pop_timer.h deleted file mode 100644 index b17c97b..0000000 --- a/applications/PoP/roomtest/pop_timer.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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/PoP/roomtest/pop_title.c b/applications/PoP/roomtest/pop_title.c deleted file mode 100644 index c9a474f..0000000 --- a/applications/PoP/roomtest/pop_title.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * 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/PoP/roomtest/pop_title.h b/applications/PoP/roomtest/pop_title.h deleted file mode 100644 index 0f15dd9..0000000 --- a/applications/PoP/roomtest/pop_title.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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/PoP/roomtest/pop_trob.c b/applications/PoP/roomtest/pop_trob.c deleted file mode 100644 index 948a2d2..0000000 --- a/applications/PoP/roomtest/pop_trob.c +++ /dev/null @@ -1,929 +0,0 @@ -/* - * 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/PoP/roomtest/pop_trob.h b/applications/PoP/roomtest/pop_trob.h deleted file mode 100644 index eb7d998..0000000 --- a/applications/PoP/roomtest/pop_trob.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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? Для перерисовки ворот соседа через - * шов (roomtest не дёргает шов, пока сосед не анимируется). */ -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/PoP/roomtest/pop_tune.h b/applications/PoP/roomtest/pop_tune.h deleted file mode 100644 index 3b92b65..0000000 --- a/applications/PoP/roomtest/pop_tune.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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 хардкод; это НЕ комната - * назначения — см. roomtest.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 при сборке). Живёт - * здесь, а не в roomtest.c: старт уровня уехал в холодную половину цикла - * (roomtest_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/PoP/roomtest/pop_ui.c b/applications/PoP/roomtest/pop_ui.c deleted file mode 100644 index d747212..0000000 --- a/applications/PoP/roomtest/pop_ui.c +++ /dev/null @@ -1,434 +0,0 @@ -/* - * 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 секунд восемь»). - * Теперь ступень пересчитывается только когда она СМЕНИЛАСЬ. */ -static void fade_run(uint8_t frames, uint8_t up) -{ - uint8_t i, shown = 0xFF; - - if (!frames) { - pop_ui_palette_dim(up ? 0 : POP_PAL_DIM_BLACK, 0); - return; - } - 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(); - } - /* Довести до края: целочисленное деление могло не дойти до 8 (или 0). */ - if (shown != (up ? 0 : POP_PAL_DIM_BLACK)) - pop_ui_palette_dim(up ? 0 : POP_PAL_DIM_BLACK, 0); -} - -void pop_ui_fade_out(uint8_t steps) __banked -{ - pop_ui_palette_snapshot(); - fade_run(steps, 0); -} - -void pop_ui_fade_in(uint8_t steps) __banked -{ - if (!steps) { pop_ui_palette_restore(); return; } - fade_run(steps, 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/PoP/roomtest/pop_ui.h b/applications/PoP/roomtest/pop_ui.h deleted file mode 100644 index 6cd40a4..0000000 --- a/applications/PoP/roomtest/pop_ui.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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; - -/* Полная экранная страница: 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/PoP/roomtest/pop_vflip.c b/applications/PoP/roomtest/pop_vflip.c deleted file mode 100644 index 4b8e138..0000000 --- a/applications/PoP/roomtest/pop_vflip.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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/PoP/roomtest/pop_vflip.h b/applications/PoP/roomtest/pop_vflip.h deleted file mode 100644 index 81fefec..0000000 --- a/applications/PoP/roomtest/pop_vflip.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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/PoP/roomtest/pv_arc.h b/applications/PoP/roomtest/pv_arc.h deleted file mode 100644 index d7b9dae..0000000 --- a/applications/PoP/roomtest/pv_arc.h +++ /dev/null @@ -1,92 +0,0 @@ -/* 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/PoP/roomtest/roomtest.c b/applications/PoP/roomtest/roomtest.c deleted file mode 100644 index 7048a9c..0000000 --- a/applications/PoP/roomtest/roomtest.c +++ /dev/null @@ -1,967 +0,0 @@ -/* - * roomtest.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 "roomtest_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 в roomtest_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 = roomtest_cold (ХОЛОДНАЯ половина главного цикла: старт уровня и - * отладочная навигация — см. roomtest_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` - * (см. .sprinter-cc-roomtest/roomtest.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; - - /* Загрузка ресурсов, палитра, первый экран — В БАНКЕ (roomtest_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; - } - /* Читы — В БАНКЕ (roomtest_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: одно нажатие = одна - * комната. Сама навигация — в банке (roomtest_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/PoP/roomtest/roomtest_cold.c b/applications/PoP/roomtest/roomtest_cold.c deleted file mode 100644 index e48fbeb..0000000 --- a/applications/PoP/roomtest/roomtest_cold.c +++ /dev/null @@ -1,1269 +0,0 @@ -/* - * roomtest_cold.c — ХОЛОДНАЯ половина главного цикла, собирается в БАНК - * (--bank 8=roomtest_cold.c, окно W3). Здесь всё, что отрабатывает раз на - * уровень или только по отладочной клавише; кадровый путь остался в - * roomtest.c. Зачем разделили и что осталось снаружи — см. roomtest_cold.h. - * - * Правила банка те же, что у guards.c: состояние — в W1/W2 (данные этого - * модуля тоже, --bank-data не включён), звать можно резидент W1/W2 и другие - * __banked-модули через трамплины. - */ -#include -#include -#include "roomtest_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/PoP/roomtest/roomtest_cold.h b/applications/PoP/roomtest/roomtest_cold.h deleted file mode 100644 index d1fe99b..0000000 --- a/applications/PoP/roomtest/roomtest_cold.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * roomtest_cold.h — контракт между главным циклом (резидент W1/W2) и его - * ХОЛОДНОЙ половиной, вынесенной в банк (roomtest_cold.c). - * - * Зачем. В режиме huge под кучу остаётся то, что не занял _CODE+_DATA, и к - * уровню 8 запас упал ниже килобайта. Главный цикл при этом наполовину - * состоит из кода, который отрабатывает РАЗ НА УРОВЕНЬ (старт/рестарт) или - * вообще только по отладочной клавише (обход комнат, лейбл номера) — держать - * его в дефицитном окне незачем. Кадровый путь (tick/draw/флип) остался в - * резиденте целиком: цена выноса — один трамплин на вызов, а он тут ноль раз - * за обычный кадр. - * - * Что ОСТАЛОСЬ в roomtest.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; - -/* Клавиши служебного слоя. Живут здесь, а не в roomtest.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/PoP/roomtest/shadow_arc.h b/applications/PoP/roomtest/shadow_arc.h deleted file mode 100644 index 09a719a..0000000 --- a/applications/PoP/roomtest/shadow_arc.h +++ /dev/null @@ -1,41 +0,0 @@ -/* 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/PoP/roomtest/skel_arc.h b/applications/PoP/roomtest/skel_arc.h deleted file mode 100644 index eca2b21..0000000 --- a/applications/PoP/roomtest/skel_arc.h +++ /dev/null @@ -1,13 +0,0 @@ -/* 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/PoP/roomtest/tests-host/.gitignore b/applications/PoP/roomtest/tests-host/.gitignore deleted file mode 100644 index 567609b..0000000 --- a/applications/PoP/roomtest/tests-host/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build/ diff --git a/applications/PoP/roomtest/tests-host/Makefile b/applications/PoP/roomtest/tests-host/Makefile deleted file mode 100644 index 039ee15..0000000 --- a/applications/PoP/roomtest/tests-host/Makefile +++ /dev/null @@ -1,122 +0,0 @@ -# Модульные тесты движка roomtest под ucsim_z80. Обвязка общая (testkit/), -# здесь — только сами наборы и список модулей, которые в них линкуются. -POP_ROOT := $(abspath $(CURDIR)/../..) --include $(POP_ROOT)/local.mk -SPRINTER_ROOT ?= $(if $(wildcard $(POP_ROOT)/../../bin/sprinter-cc),$(abspath $(POP_ROOT)/../..),) -ifeq ($(strip $(SPRINTER_ROOT)),) -$(error Задайте SPRINTER_ROOT — путь к установленному Sprinter-CC) -endif -TESTKIT := $(SPRINTER_ROOT)/testkit -ENGINE_DIR := $(abspath $(CURDIR)/..) - -# 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$(ENGINE_DIR)/../poc/res/bg # pop_bg_atlas.h (генерит pop_pack_bg.py) - -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: блоб таблиц анимации ------------------------------------ -KID_BIN := $(abspath $(CURDIR)/../../poc/res/kid/kid_data.bin) - -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/PoP/roomtest/tests-host/README.md b/applications/PoP/roomtest/tests-host/README.md deleted file mode 100644 index 838f8ba..0000000 --- a/applications/PoP/roomtest/tests-host/README.md +++ /dev/null @@ -1,121 +0,0 @@ -# tests-host — модульные тесты движка roomtest - -Прогоняются под `ucsim_z80`, обвязка общая — `testkit/` (там же объяснение, -почему под z80, а не хостовым gcc, и грабли). - -``` -make host-tests # из корня, вместе с остальными -make -C applications/PoP/roomtest/tests-host # только эти -make -C applications/PoP/roomtest/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`. Кадр гоняется ровно в том порядке, -что и в главном цикле `roomtest.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/PoP/roomtest/tests-host/bank_stub.s b/applications/PoP/roomtest/tests-host/bank_stub.s deleted file mode 100644 index 5e7921a..0000000 --- a/applications/PoP/roomtest/tests-host/bank_stub.s +++ /dev/null @@ -1,25 +0,0 @@ -;-------------------------------------------------------------------------- -; 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/PoP/roomtest/tests-host/gen_kid_blob.py b/applications/PoP/roomtest/tests-host/gen_kid_blob.py deleted file mode 100644 index 1974d6b..0000000 --- a/applications/PoP/roomtest/tests-host/gen_kid_blob.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/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/PoP/roomtest/tests-host/golden/crouch.h b/applications/PoP/roomtest/tests-host/golden/crouch.h deleted file mode 100644 index 62fdebf..0000000 --- a/applications/PoP/roomtest/tests-host/golden/crouch.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Эталон трассы: 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/PoP/roomtest/tests-host/golden/fall_ledge.h b/applications/PoP/roomtest/tests-host/golden/fall_ledge.h deleted file mode 100644 index 450567d..0000000 --- a/applications/PoP/roomtest/tests-host/golden/fall_ledge.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Эталон трассы: 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/PoP/roomtest/tests-host/golden/jump_up.h b/applications/PoP/roomtest/tests-host/golden/jump_up.h deleted file mode 100644 index 98ddae4..0000000 --- a/applications/PoP/roomtest/tests-host/golden/jump_up.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Эталон трассы: 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/PoP/roomtest/tests-host/golden/loose.h b/applications/PoP/roomtest/tests-host/golden/loose.h deleted file mode 100644 index d1df6d1..0000000 --- a/applications/PoP/roomtest/tests-host/golden/loose.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Эталон трассы: 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/PoP/roomtest/tests-host/golden/rjump_gap3.h b/applications/PoP/roomtest/tests-host/golden/rjump_gap3.h deleted file mode 100644 index d2c141c..0000000 --- a/applications/PoP/roomtest/tests-host/golden/rjump_gap3.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Эталон трассы: 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/PoP/roomtest/tests-host/golden/run_right.h b/applications/PoP/roomtest/tests-host/golden/run_right.h deleted file mode 100644 index 96c4fd6..0000000 --- a/applications/PoP/roomtest/tests-host/golden/run_right.h +++ /dev/null @@ -1,26 +0,0 @@ -/* Эталон трассы: 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/PoP/roomtest/tests-host/golden/stand.h b/applications/PoP/roomtest/tests-host/golden/stand.h deleted file mode 100644 index 8610c7c..0000000 --- a/applications/PoP/roomtest/tests-host/golden/stand.h +++ /dev/null @@ -1,14 +0,0 @@ -/* Эталон трассы: 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/PoP/roomtest/tests-host/golden/step_shift.h b/applications/PoP/roomtest/tests-host/golden/step_shift.h deleted file mode 100644 index c6c0891..0000000 --- a/applications/PoP/roomtest/tests-host/golden/step_shift.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Эталон трассы: 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/PoP/roomtest/tests-host/golden/turn.h b/applications/PoP/roomtest/tests-host/golden/turn.h deleted file mode 100644 index 66f5dbd..0000000 --- a/applications/PoP/roomtest/tests-host/golden/turn.h +++ /dev/null @@ -1,18 +0,0 @@ -/* Эталон трассы: 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/PoP/roomtest/tests-host/kidblob.h b/applications/PoP/roomtest/tests-host/kidblob.h deleted file mode 100644 index 11f58aa..0000000 --- a/applications/PoP/roomtest/tests-host/kidblob.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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/PoP/roomtest/tests-host/record_golden.py b/applications/PoP/roomtest/tests-host/record_golden.py deleted file mode 100644 index b21db80..0000000 --- a/applications/PoP/roomtest/tests-host/record_golden.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/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/PoP/roomtest/tests-host/scene.c b/applications/PoP/roomtest/tests-host/scene.c deleted file mode 100644 index f1aaeab..0000000 --- a/applications/PoP/roomtest/tests-host/scene.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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); - - /* Порядок ровно как в главном цикле roomtest.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/PoP/roomtest/tests-host/scene.h b/applications/PoP/roomtest/tests-host/scene.h deleted file mode 100644 index ba2dafd..0000000 --- a/applications/PoP/roomtest/tests-host/scene.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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/PoP/roomtest/tests-host/stubs.c b/applications/PoP/roomtest/tests-host/stubs.c deleted file mode 100644 index 338cabb..0000000 --- a/applications/PoP/roomtest/tests-host/stubs.c +++ /dev/null @@ -1,413 +0,0 @@ -/* - * 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" - -/* Сериализаторы приватного состояния входят в те же крупные модули, что и - * физика/ИИ. Сами 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/PoP/roomtest/tests-host/stubs.h b/applications/PoP/roomtest/tests-host/stubs.h deleted file mode 100644 index ed71b8e..0000000 --- a/applications/PoP/roomtest/tests-host/stubs.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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/PoP/roomtest/tests-host/t_app.c b/applications/PoP/roomtest/tests-host/t_app.c deleted file mode 100644 index 997ac99..0000000 --- a/applications/PoP/roomtest/tests-host/t_app.c +++ /dev/null @@ -1,135 +0,0 @@ -/* 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/PoP/roomtest/tests-host/t_cfg.c b/applications/PoP/roomtest/tests-host/t_cfg.c deleted file mode 100644 index 761648b..0000000 --- a/applications/PoP/roomtest/tests-host/t_cfg.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * 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/PoP/roomtest/tests-host/t_char.c b/applications/PoP/roomtest/tests-host/t_char.c deleted file mode 100644 index f213c60..0000000 --- a/applications/PoP/roomtest/tests-host/t_char.c +++ /dev/null @@ -1,525 +0,0 @@ -/* - * 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/PoP/roomtest/tests-host/t_cutscene.c b/applications/PoP/roomtest/tests-host/t_cutscene.c deleted file mode 100644 index e36ede6..0000000 --- a/applications/PoP/roomtest/tests-host/t_cutscene.c +++ /dev/null @@ -1,46 +0,0 @@ -/* 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/PoP/roomtest/tests-host/t_demo.c b/applications/PoP/roomtest/tests-host/t_demo.c deleted file mode 100644 index 85d28c3..0000000 --- a/applications/PoP/roomtest/tests-host/t_demo.c +++ /dev/null @@ -1,89 +0,0 @@ -/* 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/PoP/roomtest/tests-host/t_flow.c b/applications/PoP/roomtest/tests-host/t_flow.c deleted file mode 100644 index 36a9aad..0000000 --- a/applications/PoP/roomtest/tests-host/t_flow.c +++ /dev/null @@ -1,52 +0,0 @@ -/* 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/PoP/roomtest/tests-host/t_gate.c b/applications/PoP/roomtest/tests-host/t_gate.c deleted file mode 100644 index 0340fdf..0000000 --- a/applications/PoP/roomtest/tests-host/t_gate.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * 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/PoP/roomtest/tests-host/t_geom.c b/applications/PoP/roomtest/tests-host/t_geom.c deleted file mode 100644 index 0702db5..0000000 --- a/applications/PoP/roomtest/tests-host/t_geom.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 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/PoP/roomtest/tests-host/t_grab.c b/applications/PoP/roomtest/tests-host/t_grab.c deleted file mode 100644 index 0d64355..0000000 --- a/applications/PoP/roomtest/tests-host/t_grab.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * 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 */ - -/* Один кадр + переход в комнату снизу — ровно то, что делает главный цикл - * roomtest.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/PoP/roomtest/tests-host/t_jaffar.c b/applications/PoP/roomtest/tests-host/t_jaffar.c deleted file mode 100644 index 08bf622..0000000 --- a/applications/PoP/roomtest/tests-host/t_jaffar.c +++ /dev/null @@ -1,349 +0,0 @@ -/* - * 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/PoP/roomtest/tests-host/t_mouse.c b/applications/PoP/roomtest/tests-host/t_mouse.c deleted file mode 100644 index f3deb63..0000000 --- a/applications/PoP/roomtest/tests-host/t_mouse.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * 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(); -} - -/* Один полный кадр соперника, как в главном цикле roomtest.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/PoP/roomtest/tests-host/t_phys.c b/applications/PoP/roomtest/tests-host/t_phys.c deleted file mode 100644 index 143c51c..0000000 --- a/applications/PoP/roomtest/tests-host/t_phys.c +++ /dev/null @@ -1,380 +0,0 @@ -/* - * t_phys.c — характеризация физики Кида (pop_map + pop_kid + pop_ctrl). - * - * Назначение набора — СТРАХОВКА ПОД РЕФАКТОРИНГ, а не проверка - * правильности. Эталоны здесь сняты с текущего билда, то есть - * консервируют и текущие баги; правильность по-прежнему проверяется - * против SDLPoP. Смысл в другом: перенос физики с `Kid.` на `Char.` - * обязан быть поведение-сохраняющим, и «трасса до == трасса после» ловит - * ровно тот класс ошибок, который там возможен. - * - * Поэтому при осознанном изменении поведения эталон ПЕРЕСНИМАЕТСЯ (см. - * SC_RECORD ниже), а в коммите должно быть написано, почему трасса - * поменялась. Молча обновлённый эталон обесценивает весь набор. - * - * Снятие эталона: - * make -C applications/PoP/roomtest/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/PoP/roomtest/tests-host/t_shadow.c b/applications/PoP/roomtest/tests-host/t_shadow.c deleted file mode 100644 index 2c96934..0000000 --- a/applications/PoP/roomtest/tests-host/t_shadow.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * 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/PoP/roomtest/tests-host/t_timer.c b/applications/PoP/roomtest/tests-host/t_timer.c deleted file mode 100644 index b28ad27..0000000 --- a/applications/PoP/roomtest/tests-host/t_timer.c +++ /dev/null @@ -1,116 +0,0 @@ -/* 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/PoP/roomtest/tests-host/t_wall.c b/applications/PoP/roomtest/tests-host/t_wall.c deleted file mode 100644 index 8086372..0000000 --- a/applications/PoP/roomtest/tests-host/t_wall.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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/PoP/roomtest/title_arc.h b/applications/PoP/roomtest/title_arc.h deleted file mode 100644 index e02e7f2..0000000 --- a/applications/PoP/roomtest/title_arc.h +++ /dev/null @@ -1,29 +0,0 @@ -/* 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/PoP/roomtest/vizier_arc.h b/applications/PoP/roomtest/vizier_arc.h deleted file mode 100644 index 8f30b67..0000000 --- a/applications/PoP/roomtest/vizier_arc.h +++ /dev/null @@ -1,14 +0,0 @@ -/* 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/PoP/sdk.mk b/applications/PoP/sdk.mk deleted file mode 100644 index ea08f79..0000000 --- a/applications/PoP/sdk.mk +++ /dev/null @@ -1,11 +0,0 @@ -# Привязка архивных PoP-проб к установленному Sprinter-CC. -POP_ROOT := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) --include $(POP_ROOT)/local.mk -SPRINTER_ROOT ?= $(if $(wildcard $(POP_ROOT)/../../bin/sprinter-cc),$(abspath $(POP_ROOT)/../..),) -ifeq ($(strip $(SPRINTER_ROOT)),) -$(error Задайте SPRINTER_ROOT или создайте PoP/local.mk) -endif -PROJ_ROOT := $(SPRINTER_ROOT) -export SPRINTER_ROOT MAME_HOME MAME_BIN MAME_ROMPATH MAME_DSS_IMAGE -export MAME_SYSTEM_HDD_IMAGE MAME_BIOS CHDMAN_BIN -include $(PROJ_ROOT)/app.mk diff --git a/applications/PoP/third_party/16x16-RPG-characters/LICENSE b/applications/PoP/third_party/16x16-RPG-characters/LICENSE deleted file mode 100644 index 604209a..0000000 --- a/applications/PoP/third_party/16x16-RPG-characters/LICENSE +++ /dev/null @@ -1,359 +0,0 @@ -Creative Commons Legal Code - -Attribution-ShareAlike 3.0 Unported - - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR - DAMAGES RESULTING FROM ITS USE. - -License - -THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE -COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY -COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS -AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. - -BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE -TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY -BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS -CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND -CONDITIONS. - -1. Definitions - - a. "Adaptation" means a work based upon the Work, or upon the Work and - other pre-existing works, such as a translation, adaptation, - derivative work, arrangement of music or other alterations of a - literary or artistic work, or phonogram or performance and includes - cinematographic adaptations or any other form in which the Work may be - recast, transformed, or adapted including in any form recognizably - derived from the original, except that a work that constitutes a - Collection will not be considered an Adaptation for the purpose of - this License. For the avoidance of doubt, where the Work is a musical - work, performance or phonogram, the synchronization of the Work in - timed-relation with a moving image ("synching") will be considered an - Adaptation for the purpose of this License. - b. "Collection" means a collection of literary or artistic works, such as - encyclopedias and anthologies, or performances, phonograms or - broadcasts, or other works or subject matter other than works listed - in Section 1(f) below, which, by reason of the selection and - arrangement of their contents, constitute intellectual creations, in - which the Work is included in its entirety in unmodified form along - with one or more other contributions, each constituting separate and - independent works in themselves, which together are assembled into a - collective whole. A work that constitutes a Collection will not be - considered an Adaptation (as defined below) for the purposes of this - License. - c. "Creative Commons Compatible License" means a license that is listed - at https://creativecommons.org/compatiblelicenses that has been - approved by Creative Commons as being essentially equivalent to this - License, including, at a minimum, because that license: (i) contains - terms that have the same purpose, meaning and effect as the License - Elements of this License; and, (ii) explicitly permits the relicensing - of adaptations of works made available under that license under this - License or a Creative Commons jurisdiction license with the same - License Elements as this License. - d. "Distribute" means to make available to the public the original and - copies of the Work or Adaptation, as appropriate, through sale or - other transfer of ownership. - e. "License Elements" means the following high-level license attributes - as selected by Licensor and indicated in the title of this License: - Attribution, ShareAlike. - f. "Licensor" means the individual, individuals, entity or entities that - offer(s) the Work under the terms of this License. - g. "Original Author" means, in the case of a literary or artistic work, - the individual, individuals, entity or entities who created the Work - or if no individual or entity can be identified, the publisher; and in - addition (i) in the case of a performance the actors, singers, - musicians, dancers, and other persons who act, sing, deliver, declaim, - play in, interpret or otherwise perform literary or artistic works or - expressions of folklore; (ii) in the case of a phonogram the producer - being the person or legal entity who first fixes the sounds of a - performance or other sounds; and, (iii) in the case of broadcasts, the - organization that transmits the broadcast. - h. "Work" means the literary and/or artistic work offered under the terms - of this License including without limitation any production in the - literary, scientific and artistic domain, whatever may be the mode or - form of its expression including digital form, such as a book, - pamphlet and other writing; a lecture, address, sermon or other work - of the same nature; a dramatic or dramatico-musical work; a - choreographic work or entertainment in dumb show; a musical - composition with or without words; a cinematographic work to which are - assimilated works expressed by a process analogous to cinematography; - a work of drawing, painting, architecture, sculpture, engraving or - lithography; a photographic work to which are assimilated works - expressed by a process analogous to photography; a work of applied - art; an illustration, map, plan, sketch or three-dimensional work - relative to geography, topography, architecture or science; a - performance; a broadcast; a phonogram; a compilation of data to the - extent it is protected as a copyrightable work; or a work performed by - a variety or circus performer to the extent it is not otherwise - considered a literary or artistic work. - i. "You" means an individual or entity exercising rights under this - License who has not previously violated the terms of this License with - respect to the Work, or who has received express permission from the - Licensor to exercise rights under this License despite a previous - violation. - j. "Publicly Perform" means to perform public recitations of the Work and - to communicate to the public those public recitations, by any means or - process, including by wire or wireless means or public digital - performances; to make available to the public Works in such a way that - members of the public may access these Works from a place and at a - place individually chosen by them; to perform the Work to the public - by any means or process and the communication to the public of the - performances of the Work, including by public digital performance; to - broadcast and rebroadcast the Work by any means including signs, - sounds or images. - k. "Reproduce" means to make copies of the Work by any means including - without limitation by sound or visual recordings and the right of - fixation and reproducing fixations of the Work, including storage of a - protected performance or phonogram in digital form or other electronic - medium. - -2. Fair Dealing Rights. Nothing in this License is intended to reduce, -limit, or restrict any uses free from copyright or rights arising from -limitations or exceptions that are provided for in connection with the -copyright protection under copyright law or other applicable laws. - -3. License Grant. Subject to the terms and conditions of this License, -Licensor hereby grants You a worldwide, royalty-free, non-exclusive, -perpetual (for the duration of the applicable copyright) license to -exercise the rights in the Work as stated below: - - a. to Reproduce the Work, to incorporate the Work into one or more - Collections, and to Reproduce the Work as incorporated in the - Collections; - b. to create and Reproduce Adaptations provided that any such Adaptation, - including any translation in any medium, takes reasonable steps to - clearly label, demarcate or otherwise identify that changes were made - to the original Work. For example, a translation could be marked "The - original work was translated from English to Spanish," or a - modification could indicate "The original work has been modified."; - c. to Distribute and Publicly Perform the Work including as incorporated - in Collections; and, - d. to Distribute and Publicly Perform Adaptations. - e. For the avoidance of doubt: - - i. Non-waivable Compulsory License Schemes. In those jurisdictions in - which the right to collect royalties through any statutory or - compulsory licensing scheme cannot be waived, the Licensor - reserves the exclusive right to collect such royalties for any - exercise by You of the rights granted under this License; - ii. Waivable Compulsory License Schemes. In those jurisdictions in - which the right to collect royalties through any statutory or - compulsory licensing scheme can be waived, the Licensor waives the - exclusive right to collect such royalties for any exercise by You - of the rights granted under this License; and, - iii. Voluntary License Schemes. The Licensor waives the right to - collect royalties, whether individually or, in the event that the - Licensor is a member of a collecting society that administers - voluntary licensing schemes, via that society, from any exercise - by You of the rights granted under this License. - -The above rights may be exercised in all media and formats whether now -known or hereafter devised. The above rights include the right to make -such modifications as are technically necessary to exercise the rights in -other media and formats. Subject to Section 8(f), all rights not expressly -granted by Licensor are hereby reserved. - -4. Restrictions. The license granted in Section 3 above is expressly made -subject to and limited by the following restrictions: - - a. You may Distribute or Publicly Perform the Work only under the terms - of this License. You must include a copy of, or the Uniform Resource - Identifier (URI) for, this License with every copy of the Work You - Distribute or Publicly Perform. You may not offer or impose any terms - on the Work that restrict the terms of this License or the ability of - the recipient of the Work to exercise the rights granted to that - recipient under the terms of the License. You may not sublicense the - Work. You must keep intact all notices that refer to this License and - to the disclaimer of warranties with every copy of the Work You - Distribute or Publicly Perform. When You Distribute or Publicly - Perform the Work, You may not impose any effective technological - measures on the Work that restrict the ability of a recipient of the - Work from You to exercise the rights granted to that recipient under - the terms of the License. This Section 4(a) applies to the Work as - incorporated in a Collection, but this does not require the Collection - apart from the Work itself to be made subject to the terms of this - License. If You create a Collection, upon notice from any Licensor You - must, to the extent practicable, remove from the Collection any credit - as required by Section 4(c), as requested. If You create an - Adaptation, upon notice from any Licensor You must, to the extent - practicable, remove from the Adaptation any credit as required by - Section 4(c), as requested. - b. You may Distribute or Publicly Perform an Adaptation only under the - terms of: (i) this License; (ii) a later version of this License with - the same License Elements as this License; (iii) a Creative Commons - jurisdiction license (either this or a later license version) that - contains the same License Elements as this License (e.g., - Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible - License. If you license the Adaptation under one of the licenses - mentioned in (iv), you must comply with the terms of that license. If - you license the Adaptation under the terms of any of the licenses - mentioned in (i), (ii) or (iii) (the "Applicable License"), you must - comply with the terms of the Applicable License generally and the - following provisions: (I) You must include a copy of, or the URI for, - the Applicable License with every copy of each Adaptation You - Distribute or Publicly Perform; (II) You may not offer or impose any - terms on the Adaptation that restrict the terms of the Applicable - License or the ability of the recipient of the Adaptation to exercise - the rights granted to that recipient under the terms of the Applicable - License; (III) You must keep intact all notices that refer to the - Applicable License and to the disclaimer of warranties with every copy - of the Work as included in the Adaptation You Distribute or Publicly - Perform; (IV) when You Distribute or Publicly Perform the Adaptation, - You may not impose any effective technological measures on the - Adaptation that restrict the ability of a recipient of the Adaptation - from You to exercise the rights granted to that recipient under the - terms of the Applicable License. This Section 4(b) applies to the - Adaptation as incorporated in a Collection, but this does not require - the Collection apart from the Adaptation itself to be made subject to - the terms of the Applicable License. - c. If You Distribute, or Publicly Perform the Work or any Adaptations or - Collections, You must, unless a request has been made pursuant to - Section 4(a), keep intact all copyright notices for the Work and - provide, reasonable to the medium or means You are utilizing: (i) the - name of the Original Author (or pseudonym, if applicable) if supplied, - and/or if the Original Author and/or Licensor designate another party - or parties (e.g., a sponsor institute, publishing entity, journal) for - attribution ("Attribution Parties") in Licensor's copyright notice, - terms of service or by other reasonable means, the name of such party - or parties; (ii) the title of the Work if supplied; (iii) to the - extent reasonably practicable, the URI, if any, that Licensor - specifies to be associated with the Work, unless such URI does not - refer to the copyright notice or licensing information for the Work; - and (iv) , consistent with Ssection 3(b), in the case of an - Adaptation, a credit identifying the use of the Work in the Adaptation - (e.g., "French translation of the Work by Original Author," or - "Screenplay based on original Work by Original Author"). The credit - required by this Section 4(c) may be implemented in any reasonable - manner; provided, however, that in the case of a Adaptation or - Collection, at a minimum such credit will appear, if a credit for all - contributing authors of the Adaptation or Collection appears, then as - part of these credits and in a manner at least as prominent as the - credits for the other contributing authors. For the avoidance of - doubt, You may only use the credit required by this Section for the - purpose of attribution in the manner set out above and, by exercising - Your rights under this License, You may not implicitly or explicitly - assert or imply any connection with, sponsorship or endorsement by the - Original Author, Licensor and/or Attribution Parties, as appropriate, - of You or Your use of the Work, without the separate, express prior - written permission of the Original Author, Licensor and/or Attribution - Parties. - d. Except as otherwise agreed in writing by the Licensor or as may be - otherwise permitted by applicable law, if You Reproduce, Distribute or - Publicly Perform the Work either by itself or as part of any - Adaptations or Collections, You must not distort, mutilate, modify or - take other derogatory action in relation to the Work which would be - prejudicial to the Original Author's honor or reputation. Licensor - agrees that in those jurisdictions (e.g. Japan), in which any exercise - of the right granted in Section 3(b) of this License (the right to - make Adaptations) would be deemed to be a distortion, mutilation, - modification or other derogatory action prejudicial to the Original - Author's honor and reputation, the Licensor will waive or not assert, - as appropriate, this Section, to the fullest extent permitted by the - applicable national law, to enable You to reasonably exercise Your - right under Section 3(b) of this License (right to make Adaptations) - but not otherwise. - -5. Representations, Warranties and Disclaimer - -UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR -OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY -KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, -INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, -FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF -LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, -WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION -OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. - -6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE -LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR -ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES -ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS -BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -7. Termination - - a. This License and the rights granted hereunder will terminate - automatically upon any breach by You of the terms of this License. - Individuals or entities who have received Adaptations or Collections - from You under this License, however, will not have their licenses - terminated provided such individuals or entities remain in full - compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will - survive any termination of this License. - b. Subject to the above terms and conditions, the license granted here is - perpetual (for the duration of the applicable copyright in the Work). - Notwithstanding the above, Licensor reserves the right to release the - Work under different license terms or to stop distributing the Work at - any time; provided, however that any such election will not serve to - withdraw this License (or any other license that has been, or is - required to be, granted under the terms of this License), and this - License will continue in full force and effect unless terminated as - stated above. - -8. Miscellaneous - - a. Each time You Distribute or Publicly Perform the Work or a Collection, - the Licensor offers to the recipient a license to the Work on the same - terms and conditions as the license granted to You under this License. - b. Each time You Distribute or Publicly Perform an Adaptation, Licensor - offers to the recipient a license to the original Work on the same - terms and conditions as the license granted to You under this License. - c. If any provision of this License is invalid or unenforceable under - applicable law, it shall not affect the validity or enforceability of - the remainder of the terms of this License, and without further action - by the parties to this agreement, such provision shall be reformed to - the minimum extent necessary to make such provision valid and - enforceable. - d. No term or provision of this License shall be deemed waived and no - breach consented to unless such waiver or consent shall be in writing - and signed by the party to be charged with such waiver or consent. - e. This License constitutes the entire agreement between the parties with - respect to the Work licensed here. There are no understandings, - agreements or representations with respect to the Work not specified - here. Licensor shall not be bound by any additional provisions that - may appear in any communication from You. This License may not be - modified without the mutual written agreement of the Licensor and You. - f. The rights granted under, and the subject matter referenced, in this - License were drafted utilizing the terminology of the Berne Convention - for the Protection of Literary and Artistic Works (as amended on - September 28, 1979), the Rome Convention of 1961, the WIPO Copyright - Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 - and the Universal Copyright Convention (as revised on July 24, 1971). - These rights and subject matter take effect in the relevant - jurisdiction in which the License terms are sought to be enforced - according to the corresponding provisions of the implementation of - those treaty provisions in the applicable national law. If the - standard suite of rights granted under applicable copyright law - includes additional rights not granted under this License, such - additional rights are deemed to be included in the License; this - License is not intended to restrict the license of any rights under - applicable law. - - -Creative Commons Notice - - Creative Commons is not a party to this License, and makes no warranty - whatsoever in connection with the Work. Creative Commons will not be - liable to You or any party on any legal theory for any damages - whatsoever, including without limitation any general, special, - incidental or consequential damages arising in connection to this - license. Notwithstanding the foregoing two (2) sentences, if Creative - Commons has expressly identified itself as the Licensor hereunder, it - shall have all rights and obligations of Licensor. - - Except for the limited purpose of indicating to the public that the - Work is licensed under the CCPL, Creative Commons does not authorize - the use by either party of the trademark "Creative Commons" or any - related trademark or logo of Creative Commons without the prior - written consent of Creative Commons. Any permitted use will be in - compliance with Creative Commons' then-current trademark usage - guidelines, as may be published on its website or otherwise made - available upon request from time to time. For the avoidance of doubt, - this trademark restriction does not form part of the License. - - Creative Commons may be contacted at https://creativecommons.org/. diff --git a/applications/PoP/third_party/16x16-RPG-characters/README.md b/applications/PoP/third_party/16x16-RPG-characters/README.md deleted file mode 100644 index b3b78a0..0000000 --- a/applications/PoP/third_party/16x16-RPG-characters/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# 16x16 RPG characters `v3.0` - -16x16px RPG character sprite sheet for up to down games. - -This asset pack has been downloaded from https://route1rodent.itch.io/16x16-rpg-character-sprite-sheet - -Adapted from opengameart.org 's "[NES-Style RPG Characters](https://opengameart.org/content/nes-style-rpg-characters)" and "[More NES-style RPG Characters](https://opengameart.org/content/more-nes-style-rpg-characters)", with additional content made by [@route1rodent](https://route1rodent.itch.io). - -## License - -16x16 RPG character sprite sheet (c) by @route1rodent - -"16x16 RPG character sprite sheet" is licensed under a -Creative Commons Attribution-ShareAlike 3.0 Unported License (CC BY-SA 3.0). - -You should have received a copy of the license along with this -work. If not, see http://creativecommons.org/licenses/by-sa/3.0/. - - -## Credits - -Maintained by @route1rodent: - -- itch.io: https://route1rodent.itch.io -- Twitter: https://twitter.com/route1rodent -- Github: https://github.com/itsjavi -- Blog: https://blog.itsjavi.com diff --git a/applications/PoP/third_party/16x16-RPG-characters/sprites/old-style/02-bard.png b/applications/PoP/third_party/16x16-RPG-characters/sprites/old-style/02-bard.png deleted file mode 100644 index 98bf1c7..0000000 Binary files a/applications/PoP/third_party/16x16-RPG-characters/sprites/old-style/02-bard.png and /dev/null differ diff --git a/applications/PoP/toolchain/1.1 b/applications/PoP/toolchain/1.1 deleted file mode 100644 index b71abdc..0000000 Binary files a/applications/PoP/toolchain/1.1 and /dev/null differ diff --git a/applications/PoP/toolchain/1.1-2.png b/applications/PoP/toolchain/1.1-2.png deleted file mode 100644 index 9c8a8e9..0000000 Binary files a/applications/PoP/toolchain/1.1-2.png and /dev/null differ diff --git a/applications/PoP/toolchain/pop_extract_font.py b/applications/PoP/toolchain/pop_extract_font.py deleted file mode 100644 index 6c6d284..0000000 --- a/applications/PoP/toolchain/pop_extract_font.py +++ /dev/null @@ -1,208 +0,0 @@ -#!/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 - -ROOT = Path(__file__).resolve().parent.parent -SRC_SMALL = ROOT / "SDLPoP" / "src" / "menu.c" -SRC_BIG = ROOT / "SDLPoP" / "src" / "seg009.c" -OUT_DIR = ROOT / "poc" / "res" / "font" -OUT_ATL = OUT_DIR / "font.atl" -OUT_H = ROOT / "roomtest" / "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/PoP/toolchain/pop_extract_kid_data.py b/applications/PoP/toolchain/pop_extract_kid_data.py deleted file mode 100644 index 88071bb..0000000 --- a/applications/PoP/toolchain/pop_extract_kid_data.py +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env python3 -""" -pop_extract_kid_data.py — извлекает данные анимации Kid из исходников -SDLPoP в C-заголовок roomtest/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 - -SRC = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "SDLPoP", "src") -OUT_BIN_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), - "..", "poc", "res", "kid") -OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), - "..", "roomtest", "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/PoP/toolchain/pop_pack_arc.py b/applications/PoP/toolchain/pop_pack_arc.py deleted file mode 100644 index fd12db3..0000000 --- a/applications/PoP/toolchain/pop_pack_arc.py +++ /dev/null @@ -1,123 +0,0 @@ -#!/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/PoP/toolchain/pop_pack_bg.py b/applications/PoP/toolchain/pop_pack_bg.py deleted file mode 100644 index 75d8d58..0000000 --- a/applications/PoP/toolchain/pop_pack_bg.py +++ /dev/null @@ -1,628 +0,0 @@ -#!/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->позиция. - -Выход: в applications/PoP/poc/res/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 (экстрактор + каскад загрузки) - -OUT_DIR = os.path.join(HERE, "..", "poc", "res", "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(os.path.join(OUT_DIR, "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/PoP/toolchain/pop_pack_guard.py b/applications/PoP/toolchain/pop_pack_guard.py deleted file mode 100644 index 8598f9e..0000000 --- a/applications/PoP/toolchain/pop_pack_guard.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/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. - -Выход: poc/res/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 - -# Набор спрайтов соперника выбирается ТИПОМ УРОВНЯ (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 = os.path.join(HERE, "..", "SDLPoP", "data", "GUARD") -PAL_BIN = os.path.join(HERE, "..", "SDLPoP", "data", "PRINCE", "res10.bin") -OUT_DIR = os.path.join(HERE, "..", "poc", "res", "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 = os.path.join(HERE, "..", "SDLPoP", "data", src) - OUT_DIR = os.path.join(HERE, "..", "poc", "res", 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(os.path.join(HERE, "..", "roomtest", "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/PoP/toolchain/pop_pack_intro.py b/applications/PoP/toolchain/pop_pack_intro.py deleted file mode 100644 index d529d77..0000000 --- a/applications/PoP/toolchain/pop_pack_intro.py +++ /dev/null @@ -1,349 +0,0 @@ -#!/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 - -ROOT = Path(__file__).resolve().parent.parent -TITLE = ROOT / "SDLPoP" / "data" / "TITLE" -PV = ROOT / "SDLPoP" / "data" / "PV" -PRINCE = ROOT / "SDLPoP" / "data" / "PRINCE" -KID = ROOT / "SDLPoP" / "data" / "KID" -OUT = ROOT / "poc" / "res" / "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/PoP/toolchain/pop_pack_kid.py b/applications/PoP/toolchain/pop_pack_kid.py deleted file mode 100644 index d8f1412..0000000 --- a/applications/PoP/toolchain/pop_pack_kid.py +++ /dev/null @@ -1,219 +0,0 @@ -#!/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. - -Выход: poc/res/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 - -KID_DIR = os.path.join(HERE, "..", "SDLPoP", "data", "KID") -OUT_DIR = os.path.join(HERE, "..", "poc", "res", "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-страницы (см. roomtest/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 = os.path.join(OUT_DIR, "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/PoP/toolchain/pop_pack_music.py b/applications/PoP/toolchain/pop_pack_music.py deleted file mode 100644 index 0d6748b..0000000 --- a/applications/PoP/toolchain/pop_pack_music.py +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env python3 -"""pop_pack_music.py — музыка PoP в сырой поток для CBL. - -Источник — готовые записи DOS-версии (applications/PoP/PoP1_DOS_music, -формат flac). Оцифровки музыки в самой игре НЕТ: в 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 - -POP = Path(__file__).resolve().parent.parent # applications/PoP -SRC_DIR = POP / "PoP1_DOS_music" / "flac" -OUT_DIR = POP / "poc" / "res" / "music" -OUT_H = POP / "roomtest" / "pop_music_tbl.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: - """flac -> сырой 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}.flac" - 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: - wanted = [int(a) for a in sys.argv[1:]] or sorted(TRACKS) - 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 — каталог музыкальных треков, СГЕНЕРИРОВАН.", - " * Источник: applications/PoP/PoP1_DOS_music/flac (записи DOS-версии).", - " * Генератор: toolchain/pop_pack_music.py. Руками не править.", - " *", - " * Нумерация id — оригинала (SDLPoP data/music/names.txt). pages —", - " * сколько кусков m_.bin лежит на диске, blocks — длина трека", - " * в порциях насоса по 128 байт (в байтах не влезло бы в uint16).", - " */", - "#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)}", - "", - "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)) - print(f"итого {total / 1024:.0f} КБ в {OUT_DIR}") - print(f"каталог: {OUT_H}") - - -if __name__ == "__main__": - main() diff --git a/applications/PoP/toolchain/pop_pack_shadow.py b/applications/PoP/toolchain/pop_pack_shadow.py deleted file mode 100644 index aac74fb..0000000 --- a/applications/PoP/toolchain/pop_pack_shadow.py +++ /dev/null @@ -1,292 +0,0 @@ -#!/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 - -Выход: poc/res/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 - -DATA = os.path.join(HERE, "..", "SDLPoP", "data") -OUT_DIR = os.path.join(HERE, "..", "poc", "res", "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(os.path.join(HERE, "..", "roomtest", "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/PoP/toolchain/pop_pack_sound.py b/applications/PoP/toolchain/pop_pack_sound.py deleted file mode 100644 index a7d022f..0000000 --- a/applications/PoP/toolchain/pop_pack_sound.py +++ /dev/null @@ -1,208 +0,0 @@ -#!/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 КБ. - -Выход: poc/res/sound/s0..sN.bin (по 16 384 Б) + roomtest/pop_sound_tbl.h -""" -import os -import struct -import sys - -HERE = os.path.dirname(os.path.abspath(__file__)) -DAT_DIR = os.path.join(HERE, "..", "MSDOS") -OUT_DIR = os.path.join(HERE, "..", "poc", "res", "sound") -HDR = os.path.join(HERE, "..", "roomtest", "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/PoP/toolchain/pop_pack_title.py b/applications/PoP/toolchain/pop_pack_title.py deleted file mode 100644 index 0f739d9..0000000 --- a/applications/PoP/toolchain/pop_pack_title.py +++ /dev/null @@ -1,137 +0,0 @@ -#!/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 - -ROOT = Path(__file__).resolve().parent.parent -SRC = ROOT / "SDLPoP" / "data" / "TITLE" -OUT = ROOT / "poc" / "res" / "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/PoP/toolchain/render_room.py b/applications/PoP/toolchain/render_room.py deleted file mode 100755 index b441515..0000000 --- a/applications/PoP/toolchain/render_room.py +++ /dev/null @@ -1,850 +0,0 @@ -#!/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 - -ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -LEVEL_DIR = os.path.join(ROOT, "SDLPoP", "data", "LEVELS") -VPALACE_DIR = os.path.join(ROOT, "SDLPoP", "data", "VPALACE") -VDUNGEON_DIR = os.path.join(ROOT, "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() diff --git a/applications/PoP/toolchain/room12_debug.png b/applications/PoP/toolchain/room12_debug.png deleted file mode 100644 index 71a96af..0000000 Binary files a/applications/PoP/toolchain/room12_debug.png and /dev/null differ diff --git a/applications/PoP/toolchain/room12_render.png b/applications/PoP/toolchain/room12_render.png deleted file mode 100644 index 6761022..0000000 Binary files a/applications/PoP/toolchain/room12_render.png and /dev/null differ diff --git a/applications/PoP/toolchain/room1_debug.png b/applications/PoP/toolchain/room1_debug.png deleted file mode 100644 index 9c30c66..0000000 Binary files a/applications/PoP/toolchain/room1_debug.png and /dev/null differ diff --git a/applications/PoP/toolchain/room1_render.png b/applications/PoP/toolchain/room1_render.png deleted file mode 100644 index 16a3d76..0000000 Binary files a/applications/PoP/toolchain/room1_render.png and /dev/null differ diff --git a/applications/PoP/toolchain/room6_debug.png b/applications/PoP/toolchain/room6_debug.png deleted file mode 100644 index 299d8b0..0000000 Binary files a/applications/PoP/toolchain/room6_debug.png and /dev/null differ diff --git a/applications/PoP/toolchain/room6_render.png b/applications/PoP/toolchain/room6_render.png deleted file mode 100644 index 078b2ec..0000000 Binary files a/applications/PoP/toolchain/room6_render.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r0c0_00.png b/applications/PoP/toolchain/tiles_room1/r0c0_00.png deleted file mode 100644 index 3f83ec2..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r0c0_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r0c1_00.png b/applications/PoP/toolchain/tiles_room1/r0c1_00.png deleted file mode 100644 index 6925c1b..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r0c1_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r0c2_00.png b/applications/PoP/toolchain/tiles_room1/r0c2_00.png deleted file mode 100644 index ab917a9..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r0c2_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r0c3_01.png b/applications/PoP/toolchain/tiles_room1/r0c3_01.png deleted file mode 100644 index 009f8a1..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r0c3_01.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r0c4_01.png b/applications/PoP/toolchain/tiles_room1/r0c4_01.png deleted file mode 100644 index 53030d3..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r0c4_01.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r0c5_01.png b/applications/PoP/toolchain/tiles_room1/r0c5_01.png deleted file mode 100644 index 50f344a..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r0c5_01.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r0c6_01.png b/applications/PoP/toolchain/tiles_room1/r0c6_01.png deleted file mode 100644 index 53030d3..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r0c6_01.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r0c7_01.png b/applications/PoP/toolchain/tiles_room1/r0c7_01.png deleted file mode 100644 index ebed99f..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r0c7_01.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r0c8_14.png b/applications/PoP/toolchain/tiles_room1/r0c8_14.png deleted file mode 100644 index dab7a5d..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r0c8_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r0c9_14.png b/applications/PoP/toolchain/tiles_room1/r0c9_14.png deleted file mode 100644 index abd7d75..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r0c9_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r1c0_13.png b/applications/PoP/toolchain/tiles_room1/r1c0_13.png deleted file mode 100644 index 097334b..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r1c0_13.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r1c1_13.png b/applications/PoP/toolchain/tiles_room1/r1c1_13.png deleted file mode 100644 index b4b0c0c..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r1c1_13.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r1c2_01.png b/applications/PoP/toolchain/tiles_room1/r1c2_01.png deleted file mode 100644 index a6cf256..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r1c2_01.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r1c3_03.png b/applications/PoP/toolchain/tiles_room1/r1c3_03.png deleted file mode 100644 index 9e269c0..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r1c3_03.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r1c4_00.png b/applications/PoP/toolchain/tiles_room1/r1c4_00.png deleted file mode 100644 index d47d89f..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r1c4_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r1c5_14.png b/applications/PoP/toolchain/tiles_room1/r1c5_14.png deleted file mode 100644 index 73b2d40..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r1c5_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r1c6_14.png b/applications/PoP/toolchain/tiles_room1/r1c6_14.png deleted file mode 100644 index 59e1bcb..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r1c6_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r1c7_14.png b/applications/PoP/toolchain/tiles_room1/r1c7_14.png deleted file mode 100644 index da81a02..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r1c7_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r1c8_14.png b/applications/PoP/toolchain/tiles_room1/r1c8_14.png deleted file mode 100644 index f668ced..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r1c8_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r1c9_14.png b/applications/PoP/toolchain/tiles_room1/r1c9_14.png deleted file mode 100644 index 64ade2d..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r1c9_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r2c0_14.png b/applications/PoP/toolchain/tiles_room1/r2c0_14.png deleted file mode 100644 index e2909b4..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r2c0_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r2c1_14.png b/applications/PoP/toolchain/tiles_room1/r2c1_14.png deleted file mode 100644 index a8f1180..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r2c1_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r2c2_14.png b/applications/PoP/toolchain/tiles_room1/r2c2_14.png deleted file mode 100644 index 162c6b2..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r2c2_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r2c3_14.png b/applications/PoP/toolchain/tiles_room1/r2c3_14.png deleted file mode 100644 index 48ec563..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r2c3_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r2c4_0E.png b/applications/PoP/toolchain/tiles_room1/r2c4_0E.png deleted file mode 100644 index 88b0c98..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r2c4_0E.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r2c5_03.png b/applications/PoP/toolchain/tiles_room1/r2c5_03.png deleted file mode 100644 index a5077bf..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r2c5_03.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r2c6_0B.png b/applications/PoP/toolchain/tiles_room1/r2c6_0B.png deleted file mode 100644 index 8c01774..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r2c6_0B.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r2c7_01.png b/applications/PoP/toolchain/tiles_room1/r2c7_01.png deleted file mode 100644 index 0352215..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r2c7_01.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r2c8_01.png b/applications/PoP/toolchain/tiles_room1/r2c8_01.png deleted file mode 100644 index 992c339..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r2c8_01.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room1/r2c9_14.png b/applications/PoP/toolchain/tiles_room1/r2c9_14.png deleted file mode 100644 index 0f30768..0000000 Binary files a/applications/PoP/toolchain/tiles_room1/r2c9_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r0c0_0B.png b/applications/PoP/toolchain/tiles_room12/r0c0_0B.png deleted file mode 100644 index b4b0c0c..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r0c0_0B.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r0c1_0B.png b/applications/PoP/toolchain/tiles_room12/r0c1_0B.png deleted file mode 100644 index b4b0c0c..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r0c1_0B.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r0c2_0B.png b/applications/PoP/toolchain/tiles_room12/r0c2_0B.png deleted file mode 100644 index b4b0c0c..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r0c2_0B.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r0c3_03.png b/applications/PoP/toolchain/tiles_room12/r0c3_03.png deleted file mode 100644 index 4907ae2..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r0c3_03.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r0c4_03.png b/applications/PoP/toolchain/tiles_room12/r0c4_03.png deleted file mode 100644 index 38f9eff..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r0c4_03.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r0c5_00.png b/applications/PoP/toolchain/tiles_room12/r0c5_00.png deleted file mode 100644 index cbd163c..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r0c5_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r0c6_00.png b/applications/PoP/toolchain/tiles_room12/r0c6_00.png deleted file mode 100644 index 619fde0..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r0c6_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r0c7_00.png b/applications/PoP/toolchain/tiles_room12/r0c7_00.png deleted file mode 100644 index f349fa3..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r0c7_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r0c8_00.png b/applications/PoP/toolchain/tiles_room12/r0c8_00.png deleted file mode 100644 index da1faf7..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r0c8_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r0c9_14.png b/applications/PoP/toolchain/tiles_room12/r0c9_14.png deleted file mode 100644 index 18e6ee3..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r0c9_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r1c0_00.png b/applications/PoP/toolchain/tiles_room12/r1c0_00.png deleted file mode 100644 index e1d87df..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r1c0_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r1c1_00.png b/applications/PoP/toolchain/tiles_room12/r1c1_00.png deleted file mode 100644 index b1f0d1f..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r1c1_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r1c2_00.png b/applications/PoP/toolchain/tiles_room12/r1c2_00.png deleted file mode 100644 index edad94c..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r1c2_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r1c3_14.png b/applications/PoP/toolchain/tiles_room12/r1c3_14.png deleted file mode 100644 index f37308d..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r1c3_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r1c4_03.png b/applications/PoP/toolchain/tiles_room12/r1c4_03.png deleted file mode 100644 index f803b06..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r1c4_03.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r1c5_00.png b/applications/PoP/toolchain/tiles_room12/r1c5_00.png deleted file mode 100644 index 3e1cab3..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r1c5_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r1c6_00.png b/applications/PoP/toolchain/tiles_room12/r1c6_00.png deleted file mode 100644 index 22b5242..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r1c6_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r1c7_00.png b/applications/PoP/toolchain/tiles_room12/r1c7_00.png deleted file mode 100644 index 6c165a2..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r1c7_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r1c8_01.png b/applications/PoP/toolchain/tiles_room12/r1c8_01.png deleted file mode 100644 index e8dd78b..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r1c8_01.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r1c9_03.png b/applications/PoP/toolchain/tiles_room12/r1c9_03.png deleted file mode 100644 index 21e4ddb..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r1c9_03.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r2c0_00.png b/applications/PoP/toolchain/tiles_room12/r2c0_00.png deleted file mode 100644 index 6925c1b..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r2c0_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r2c1_00.png b/applications/PoP/toolchain/tiles_room12/r2c1_00.png deleted file mode 100644 index 6925c1b..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r2c1_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r2c2_00.png b/applications/PoP/toolchain/tiles_room12/r2c2_00.png deleted file mode 100644 index fb8f938..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r2c2_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r2c3_14.png b/applications/PoP/toolchain/tiles_room12/r2c3_14.png deleted file mode 100644 index 5f433ef..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r2c3_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r2c4_14.png b/applications/PoP/toolchain/tiles_room12/r2c4_14.png deleted file mode 100644 index 5200f26..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r2c4_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r2c5_00.png b/applications/PoP/toolchain/tiles_room12/r2c5_00.png deleted file mode 100644 index f38ac21..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r2c5_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r2c6_00.png b/applications/PoP/toolchain/tiles_room12/r2c6_00.png deleted file mode 100644 index 22b5242..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r2c6_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r2c7_00.png b/applications/PoP/toolchain/tiles_room12/r2c7_00.png deleted file mode 100644 index 4d6d208..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r2c7_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r2c8_14.png b/applications/PoP/toolchain/tiles_room12/r2c8_14.png deleted file mode 100644 index a8270a9..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r2c8_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room12/r2c9_14.png b/applications/PoP/toolchain/tiles_room12/r2c9_14.png deleted file mode 100644 index a5679c6..0000000 Binary files a/applications/PoP/toolchain/tiles_room12/r2c9_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r0c0_00.png b/applications/PoP/toolchain/tiles_room6/r0c0_00.png deleted file mode 100644 index 4e28319..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r0c0_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r0c1_00.png b/applications/PoP/toolchain/tiles_room6/r0c1_00.png deleted file mode 100644 index ebeda3f..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r0c1_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r0c2_00.png b/applications/PoP/toolchain/tiles_room6/r0c2_00.png deleted file mode 100644 index 619fde0..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r0c2_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r0c3_00.png b/applications/PoP/toolchain/tiles_room6/r0c3_00.png deleted file mode 100644 index f349fa3..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r0c3_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r0c4_00.png b/applications/PoP/toolchain/tiles_room6/r0c4_00.png deleted file mode 100644 index 6925c1b..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r0c4_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r0c5_00.png b/applications/PoP/toolchain/tiles_room6/r0c5_00.png deleted file mode 100644 index 6925c1b..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r0c5_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r0c6_00.png b/applications/PoP/toolchain/tiles_room6/r0c6_00.png deleted file mode 100644 index ebeda3f..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r0c6_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r0c7_00.png b/applications/PoP/toolchain/tiles_room6/r0c7_00.png deleted file mode 100644 index 619fde0..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r0c7_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r0c8_00.png b/applications/PoP/toolchain/tiles_room6/r0c8_00.png deleted file mode 100644 index f349fa3..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r0c8_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r0c9_0C.png b/applications/PoP/toolchain/tiles_room6/r0c9_0C.png deleted file mode 100644 index ae06f2d..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r0c9_0C.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r1c0_01.png b/applications/PoP/toolchain/tiles_room6/r1c0_01.png deleted file mode 100644 index d4c3d9c..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r1c0_01.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r1c1_01.png b/applications/PoP/toolchain/tiles_room6/r1c1_01.png deleted file mode 100644 index 3129728..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r1c1_01.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r1c2_01.png b/applications/PoP/toolchain/tiles_room6/r1c2_01.png deleted file mode 100644 index b4b0c0c..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r1c2_01.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r1c3_13.png b/applications/PoP/toolchain/tiles_room6/r1c3_13.png deleted file mode 100644 index b4b0c0c..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r1c3_13.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r1c4_01.png b/applications/PoP/toolchain/tiles_room6/r1c4_01.png deleted file mode 100644 index ee3ea4c..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r1c4_01.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r1c5_13.png b/applications/PoP/toolchain/tiles_room6/r1c5_13.png deleted file mode 100644 index 3129728..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r1c5_13.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r1c6_01.png b/applications/PoP/toolchain/tiles_room6/r1c6_01.png deleted file mode 100644 index b4b0c0c..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r1c6_01.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r1c7_0B.png b/applications/PoP/toolchain/tiles_room6/r1c7_0B.png deleted file mode 100644 index b4b0c0c..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r1c7_0B.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r1c8_01.png b/applications/PoP/toolchain/tiles_room6/r1c8_01.png deleted file mode 100644 index ee3ea4c..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r1c8_01.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r1c9_04.png b/applications/PoP/toolchain/tiles_room6/r1c9_04.png deleted file mode 100644 index 203b8dd..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r1c9_04.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r2c0_14.png b/applications/PoP/toolchain/tiles_room6/r2c0_14.png deleted file mode 100644 index b5e9c6d..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r2c0_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r2c1_14.png b/applications/PoP/toolchain/tiles_room6/r2c1_14.png deleted file mode 100644 index 07cfa61..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r2c1_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r2c2_14.png b/applications/PoP/toolchain/tiles_room6/r2c2_14.png deleted file mode 100644 index d221b74..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r2c2_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r2c3_14.png b/applications/PoP/toolchain/tiles_room6/r2c3_14.png deleted file mode 100644 index 48f4307..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r2c3_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r2c4_14.png b/applications/PoP/toolchain/tiles_room6/r2c4_14.png deleted file mode 100644 index 835fcde..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r2c4_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r2c5_14.png b/applications/PoP/toolchain/tiles_room6/r2c5_14.png deleted file mode 100644 index d3b2af0..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r2c5_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r2c6_14.png b/applications/PoP/toolchain/tiles_room6/r2c6_14.png deleted file mode 100644 index 60d7d8d..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r2c6_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r2c7_00.png b/applications/PoP/toolchain/tiles_room6/r2c7_00.png deleted file mode 100644 index b9d7c8b..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r2c7_00.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r2c8_14.png b/applications/PoP/toolchain/tiles_room6/r2c8_14.png deleted file mode 100644 index abc0826..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r2c8_14.png and /dev/null differ diff --git a/applications/PoP/toolchain/tiles_room6/r2c9_14.png b/applications/PoP/toolchain/tiles_room6/r2c9_14.png deleted file mode 100644 index a41d144..0000000 Binary files a/applications/PoP/toolchain/tiles_room6/r2c9_14.png and /dev/null differ diff --git a/applications/SprPoP/.gitignore b/applications/SprPoP/.gitignore deleted file mode 100644 index fe4480c..0000000 --- a/applications/SprPoP/.gitignore +++ /dev/null @@ -1,79 +0,0 @@ -# =========================================================================== -# SprPoP — .gitignore приложения -# =========================================================================== -# Пока SprPoP лежит внутри репозитория C-Compiler, git читает этот файл как -# правила ДЛЯ ЭТОГО ПОДДЕРЕВА, и он дополняет корневой .gitignore тулчейна. -# Когда SprPoP выделят в самостоятельный проект, файл станет КОРНЕВЫМ и -# продолжит работать как есть: все пути написаны от корня SprPoP и ничего -# не знают ни про applications/, ни про раскладку тулчейна. - -# --------------------------------------------------------------------------- -# ЧУЖИЕ ДАННЫЕ — в репозиторий не идут никогда -# --------------------------------------------------------------------------- -# assets/orig/ — оригинальные данные игры (копирайт). В репозитории вместо -# них лежит ЗНАНИЕ, откуда их взять: tools/fetch_orig.py (`make fetch`) -# качает SDLPoP и записи саундтрека, MSDOS/ пользователь кладёт сам (он -# необязателен). Скачанные архивы кэшируются в assets/orig/.cache/. -# -# Шаблон со ЗВЁЗДОЧКОЙ, а не `assets/orig/`: git не заходит внутрь -# игнорируемого каталога, и исключение ниже иначе не сработало бы, а -# README.md — единственное, что отсюда ОБЯЗАНО быть в репозитории: без -# него в чистом клоне не написано, откуда брать данные. -assets/orig/* -!assets/orig/README.md - -# Уровни res20xx.bin ложатся на диск ПРЯМО из оригинала, конвертации нет — -# то есть это тот же копирайтный материал, только в раскладке диска. -# Остальное содержимое assets/packed/ версионируется намеренно: без него из -# чистого клона не собрать игру, а исходников (assets/orig/) может не быть. -assets/packed/LEVELS/ - -# --------------------------------------------------------------------------- -# ВЫХОД СБОРКИ -# --------------------------------------------------------------------------- -# build/ — .exe, раскладка диска, hdd/, промежуточные атласы, stamp'ы. -# Правило без слэша спереди намеренно: под ним и tests/host/build/. -build/ - -# Промежуточный каталог sprinter-cc. -.sprinter-cc-*/ - -# Артефакты компиляции, которые SDCC кладёт рядом с целью — в корень -# приложения. Только корень: единственный версионируемый ассемблерный -# файл (tests/host/bank_stub.s) лежит глубже и под правило не попадает. -/*.exe -/*.asm -/*.lst -/*.lk -/*.ihx -/*.noi -/*.sym -/*.map -/*.rel -/*.cdb -/*.mem -/*.rst - -# Хостовые тесты и утилиты собираются нативным cc. -*.o -*.obj -*.dSYM/ - -# --------------------------------------------------------------------------- -# ОС / редакторы / локальные настройки -# --------------------------------------------------------------------------- -local.mk -.resource-stamps/ -__pycache__/ -*.py[cod] -.DS_Store -._* -*~ -*.swp -*.bak -*.orig -*.rej -.vscode/ -.idea/ -.kilo/ -.claude/ diff --git a/applications/SprPoP/CHANGELOG.md b/applications/SprPoP/CHANGELOG.md deleted file mode 100644 index a0cde2d..0000000 --- a/applications/SprPoP/CHANGELOG.md +++ /dev/null @@ -1,56 +0,0 @@ -# SprPoP — CHANGELOG - -Заметные изменения по релизам. **Версия, дата и коммит каждой записи -берутся из git** — из аннотированного тега, а не проставляются руками, -поэтому расходиться с историей они не могут: - - git for-each-ref \ - --format='%(refname:short) | %(creatordate:short) | %(*objectname:short)' \ - refs/tags/<тег> - -Заголовок записи = `<имя тега> — <дата тега> — <коммит тега>`. Коммит — -дереференс аннотированного тега (`*objectname`), то есть та ревизия, на -которой релиз собран: по ней воспроизводится ровно этот билд. - -Здесь только то, что видно ИГРОКУ или меняет способ сборки/запуска. -Разбор причин живёт в сообщениях коммитов, открытые баги — в -`docs/BUGS_OPEN.md`, закрытые — в архиве roomtest. - -Файл начат с релиза v0.9.6; более ранняя история — в `git log` и в -досках задач. - -## v0.9.6-hang-sound-button — 2026-09-02 — `aaa480d` - -Три бага, найденные в живой игре и проверенные в MAME после исправления. - -- **Зацеп срывался на следующем кадре** (регресс, тянулся с 4e12aa5). - Кид цеплялся за кромку, поза зацепа рисовалась — и он тут же падал. - Ловилось на длинном прыжке уровня 3 (комната 7) и в attract-демо, где - Кид вместо подъёма на кромку (0,2) улетал на пики. Причина: страж - «падения сквозь стену» принимал кладку под кромкой за стену, в которую - персонаж влетел, и отменял только что состоявшийся захват. Теперь - удачный зацеп отменяет этого стража. `38fb3c0` - -- **Непрерывный взмах клинка у безоружного Кида.** На подъёме шёл - нескончаемый свист меча, которого у Кида нет. Причина: в порт не - доехали два условия оригинала, включая гард, который автор SDLPoP - подписал прямым текстом как «фикс зацикленного звука взмаха». `cb995bf` - -- **Кнопка под Кидом заливалась кладкой** на дворцовых уровнях. Кид - подтягивается на открывающую кнопку, и кромка, которая его прячет, - нарисована кирпичом (уровень 4, комната 18). Причина: подстановка - спрайта «кнопка без пола слева» действует только в подземелье, и это - условие было потеряно в перерисовке поверх персонажа — при том что в - статической отрисовке комнаты оно есть. `8548aa9` - -Сопутствующее, для разработки: - -- В доске исправлено толкование звука 11: это индикатор ПРОМАХА, а не - «любого укола». Прежняя запись описывала наш собственный код вместо - оригинала, и из-за неё в CLIMB-VS-GUARD зря отодвинули версию про порог - дистанции. `400f5cb` -- Новый набор host-тестов `t_hang`: мерит не «вис наступил», а «вис - ДЕРЖИТСЯ» — прежние наборы регресс зацепа не видели именно поэтому. - Входит в `38fb3c0`. -- В `CLAUDE.md` записан путь запуска на HDD-образе: игра лежит в - `D:\GAMES\SPRPOP`, а не в корне диска. `aaa480d` diff --git a/applications/SprPoP/CLAUDE.md b/applications/SprPoP/CLAUDE.md deleted file mode 100644 index ac39351..0000000 --- a/applications/SprPoP/CLAUDE.md +++ /dev/null @@ -1,241 +0,0 @@ -# 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) — ЕДИНСТВЕННЫЙ -авторитетный источник того, как оригинальный движок это делает.** В -репозитории его нет: заполняется `make fetch` (`make fetch-check` — что уже -на месте). Правило без исключений: - -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`), а в открытом файле остаётся ссылка. Перед заведением -нового бага грепни архив по симптому: корень мог уже разбираться. -Открытые файлы обязаны читаться целиком за раз. - -**CHANGELOG. Ставишь релизный тег — добавь запись в `CHANGELOG.md`.** -Версия, дата И КОММИТ в заголовке берутся ИЗ GIT, руками не -проставляются: - - git for-each-ref \ - --format='%(refname:short) | %(creatordate:short) | %(*objectname:short)' \ - refs/tags/<тег> - -Заголовок = `<имя тега> — <дата> — <коммит>`. Коммит обязателен: тег -можно передвинуть или переименовать, а ревизия, на которой релиз собран, -должна оставаться в записи однозначно. Берётся именно `*objectname` -(дереференс аннотированного тега), а не `objectname` — последний вернёт -хеш самого объекта тега, а не коммита. - -В запись идёт только то, что видно ИГРОКУ или меняет сборку/запуск, — по -одному пункту «симптом -> причина одной фразой -> хеш коммита». Полный -разбор живёт в сообщении коммита, дублировать его сюда не надо. - -Индекс документации с отметками актуальности — **`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 test-tools тесты упаковщиков на хосте (форматы, которые читает Z80) -make fetch скачать внешние данные в assets/orig/ (SDLPoP + музыка) -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 — необязательно). Вне репозитория — копирайт; вместо них в репозитории лежит `tools/fetch_orig.py`, который их качает (`make fetch`). Правила — в `assets/orig/README.md` | -| `assets/packed/` | то, что ложится на диск игры, уже в раскладке диска (`BG/`, `KID/`, …). В репозитории — иначе из чистого клона не собрать | -| `tools/` | конверторы ресурсов. Раскладку путей знает ОДИН файл — `tools/paths.py`; менять пути нужно там, а не в отдельных упаковщиках. Адреса ВНЕШНИХ источников — так же в одном: `tools/fetch_orig.py` | -| `docs/` | планы, доски, справочники; `docs/PoP/` — форматы ресурсов оригинала | -| `tests/host/` | модульные тесты движка под ucsim_z80 | -| `build/` | выход: `sprpop.exe`, каталоги ресурсов, `hdd/`, промежуточные `atl/` | - -Звуковые ЭФФЕКТЫ по умолчанию берутся из SDLPoP (`SND_SRC=sdlpop`) — сборка -обязана работать без оригинального дистрибутива DOS. У кого лежит -`assets/orig/MSDOS/`, включает его набор явно (`make SND_SRC=msdos`): там -оцифровка полнее — в SDLPoP пуст звук 48 `spiked` (насаживание на пики). -Разбор — `docs/sound_plan.md`. - -Музыка собирается из одного из четырёх наборов записей -(`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`). - -**Игра лежит на образе в `D:\GAMES\SPRPOP\`**, а НЕ в корне диска (так с -обобщения HDD-сборки, коммит ea8efdb — один образ рассчитан на несколько -приложений). `dir D:\` показывает только каталог `GAMES`; запуск из корня -отвечает `Bad command or file name`. - -**Тайминги моста** (не ждать дольше, см. `docs/mame-autotest.md` §10): -старт `run_bridge.sh` → 6 с → `go` → 8 с → `keyseq d:{ENTER}` + -`keyseq cd games\sprpop{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_sfx.c` + `pop_sfx_cold.c` | Звук: насос CBL в резиденте + холодная загрузка набора. Раскладка эффектов НЕ компилируется в EXE — читается с диска (`SND/snd.idx`, формат `PSI1`); см. `docs/sound_plan.md` §10. | -| `pop_music.c` | Музыка (банк 9): загрузка треков, кольцо для финальной темы. Длины и длительности — из `MUS/mus.idx` (EMM-страница + accessor `pop_music_info`), в EXE их нет; см. `docs/sound_plan.md` §11. | -| `pop_snd_tbl.h` + `pop_snd_data.c` | Тип записи набора и инварианты (рукописный заголовок) + резидентные `pop_snd_tbl`/`pop_snd_page`/`pop_snd_pages`. Размер записи 5 байт — часть дискового контракта, проверяется статически. | -| `pop_map.c/.h` | Коллизия с картой + физика падения/приземления/стены + loose-полы (`seg005/006/007`). | - -## Банки: две мины, которых компилятор не видит - -Обе стреляют молча, обе поймали нас 2026-08-27 (разбор — в -`../PoP/roomtest/BUGS_CLOSED.md`, поиск «FINAL-BANKCALL»). - -**1. Прямой вызов в чужой банк.** Трамплин SDCC выбирает по `__banked` -в ОБЪЯВЛЕНИИ, а не по тому, где функция лежит. Функция без пометки -зовётся прямым `call` — верно ровно пока вызывающий и вызываемый в одном -банке. Перенесли модуль ради разгрузки банка — и тот же `call` уходит в -пустой хвост чужого банка, процессор ползёт по 0xFF до 0x0000, DSS убивает -процесс. Ловит `toolchain/check_bank_calls.py`, она встроена в сборку и -ВАЛИТ её (проверено намеренной поломкой). - -**2. Указатель на данные своего банка, отданный в чужой.** Обратная -сторона: пока обе стороны в одном банке, указатель на литерал или -const-таблицу работает; пометили функцию `__banked` — и трамплин на время -вызова переключает W3, а указатель показывает уже в чужой банк. Так экран -таблицы рекордов залило знаками вопроса: в `pop_text_draw_mapped` (банк 9) -уходил литерал `"_"` из банка 10. - -**Правило:** всё, что уходит указателем в другой банк, обязано лежать в -W2 — стек, `_DATA` или копия. `const`-таблицы и строковые литералы -банкового модуля наружу отдавать нельзя (то же, что уже записано про -`pop_tile.c`). Вторая проверка скрипта предупреждает о явных случаях, но -она эвристическая и гарантией не является. - -## Порядок слоёв в кадре (важно для окклюзии) - -`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/LICENSE b/applications/SprPoP/LICENSE deleted file mode 100644 index a738358..0000000 --- a/applications/SprPoP/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Sprinter C Compiler contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/applications/SprPoP/Makefile b/applications/SprPoP/Makefile deleted file mode 100644 index 14abc9b..0000000 --- a/applications/SprPoP/Makefile +++ /dev/null @@ -1,624 +0,0 @@ -# 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 run-hdd запустить MAME с локальным образом игры -# make resources перегенерировать ресурсы из assets/orig/ -# make music-mp3 музыка из другого набора записей (flac|mp3|ogg|mt32) -# make clean снести build/ (ассеты не трогает) -# make distclean clean + снести упакованные ассеты assets/packed/ - --include $(CURDIR)/local.mk -SPRINTER_ROOT ?= $(if $(wildcard $(CURDIR)/../../bin/sprinter-cc),$(abspath $(CURDIR)/../..),) -ifeq ($(strip $(SPRINTER_ROOT)),) -$(error Задайте SPRINTER_ROOT или создайте SprPoP/local.mk) -endif -PROJ_ROOT := $(SPRINTER_ROOT) -export SPRINTER_ROOT MAME_HOME MAME_BIN MAME_ROMPATH MAME_DSS_IMAGE -export MAME_SYSTEM_HDD_IMAGE MAME_BIOS CHDMAN_BIN -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 ?= 10000 -# 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_app.c pop_timer.c \ - pop_ui.c pop_title.c pop_demo.c pop_flow.c -# Банк 10 = палитры + РЕДКОЕ С ФАЙЛАМИ. pop_config (POP.CFG) и pop_hof -# (POP.HOF) переехали сюда из банка 9, когда тот дошёл до 98,5% (247 байт -# свободно). Критерий тот же, что и у остальной раскладки, — частота, а не -# размер: обе вещи работают раз за партию и упираются в диск, где `open` -# стоит 51 мс, так что трамплин банк→банк на их фоне не существует. -# Отдельный банк для этого не нужен: в десятом лежал один pop_pal.c на -# 485 байт, то есть 3% страницы. -BANK10_SRCS := pop_pal.c pop_config.c pop_hof.c pop_path_bank.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_snd_data.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) — -# список ОБЯЗАН совпадать: недостающий файл это молча пропавшие эффекты. -# Страниц звука СТАТИЧЕСКИ НЕ ПОСЧИТАТЬ: их число зависит от источника -# (SDLPoP даёт 9, MSDOS — 10, см. SND_SRC). Поэтому списка s0..sN здесь нет, -# а архив зависит от stamp'а упаковщика и берёт страницы из каталога уже -# после того, как тот отработал (см. правило SND/snd.arc ниже). -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 -# Источник оцифрованных ЭФФЕКТОВ (digisnd1..3): sdlpop (умолчание) или msdos. -# Умолчание — SDLPoP, потому что он выкачивается автоматически (`make fetch`) -# и сборка обязана работать вообще без оригинального дистрибутива DOS. -# Наборы НЕ одинаковы: в MSDOS оцифровка полнее (в SDLPoP `spiked` (48) -# фактически пустой, `sword_vs_sword` (10) короче — разбор в -# docs/sound_plan.md). У кого дистрибутив есть — `make SND_SRC=msdos`. -# Источник входит в имя stamp'а, поэтому смена SND_SRC сама делает звук -# устаревшим, как и смена MUSIC_FMT. -SND_SRC ?= sdlpop -# 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 есть и у стража, и у скелета, и у Джафара), а константы -# индексов упаковщик делает из имени файла. -# README для игрока идёт НА ДИСК рядом с программой, В КОРЕНЬ. Русский -# обязан быть в CP866: DSS и любой местный просмотрщик читают именно её, -# UTF-8 покажется кракозябрами. Имена по 8.3, язык — суффиксом, а не -# расширением: расширение .TXT должно остаться, чтобы файл открывался -# просмотрщиком как текст. -DISK := README_E.TXT README_R.TXT \ - 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 SND/snd.idx \ - $(foreach i,$(MUS_IDS),MUS/m$(i).bin) MUS/mus.idx \ - 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 -# pop_sound_tbl.h здесь БОЛЬШЕ НЕТ: раскладка эффектов уехала на диск -# (SND/snd.idx), и EXE от звукового набора не зависит — пару -# snd.idx + snd.arc можно менять без перекомпиляции. -# Ни звукового, ни музыкальных заголовков здесь БОЛЬШЕ НЕТ: раскладка -# эффектов и длины треков уехали на диск (SND/snd.idx, MUS/mus.idx), и EXE -# не зависит ни от звукового набора, ни от набора записей. -GEN_H := $(addprefix gen/,$(GEN_ARC_H) pop_bg_atlas.h kid_data.h \ - pop_guard_pal.h pop_shadow_atlas.h pop_font.h) - -PACKED_FILES := $(addprefix $(PACKED)/,$(DISK)) -STAGED := $(addprefix $(BUILD_DIR)/,$(DISK)) - -EXTRA_DATA := $(STAGED) -# Запись вида КАТАЛОГ:файл кладёт файл в подкаталог HDD_DEST_DIR, голый путь — -# непосредственно в HDD_DEST_DIR. Проверка на слэш ОБЯЗАТЕЛЬНА: без неё -# `README_E.TXT` превращался -# в `README_E.TXT:build/README_E.TXT`, то есть упаковщик заводил каталог с -# именем файла и клал файл внутрь него (поймано пользователем на готовом -# образе). -HDD_PACK_ARGS := $(BUILD_DIR)/$(EXAMPLE).exe \ - $(foreach f,$(DISK),$(if $(findstring /,$(f)),\ - $(word 1,$(subst /, ,$(f))):$(BUILD_DIR)/$(f),\ - $(BUILD_DIR)/$(f))) -# Всё дерево SprPoP лежит рядом с EXE, но не меняет дефолт глобального -# упаковщика: остальные приложения по-прежнему попадают в корень HDD. -HDD_DEST_DIR := games/sprpop -# Образ живёт в build/; run-hdd передаёт его MAME напрямую как hard2. -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 - -# ВАРИАНТЫ РЕСУРСА (источник звука SND_SRC, набор записей MUSIC_FMT). Оба -# варианта пишут в ОДИН выход — build/atl/sound, build/atl/music, — поэтому -# одного stamp'а на вариант мало: вернувшись к прежнему варианту, make видит -# его stamp свежим и не делает НИЧЕГО, а в выходе лежит чужое (поймано на -# sdlpop -> msdos -> sdlpop: заголовок остался с 10 страницами вместо 9). -# Лечится файлом с именем текущего варианта: он переписывается только при -# смене (cmp), а его свежесть делает stamp устаревшим. -.PHONY: variant-check -variant-check: ; -# СМЕНА ВАРИАНТА СНОСИТ STAMP'Ы СВОЕГО СЕМЕЙСТВА, а не полагается на то, -# что make заметит свежесть variant-файла. Причина — секундная -# гранулярность mtime: упаковка укладывается в одну секунду с записью -# variant, и stamp того же имени считается актуальным. Возврат к прежнему -# варианту (`sdlpop -> msdos -> sdlpop`) тогда ТИХО оставлял чужой набор. -# Нет файла — правило обязано выполниться, и время тут ни при чём. -define variant_rule -$(STAMP)/$(1).variant: variant-check | $(STAMP) - @echo "$(2)" | cmp -s - $$@ || { echo "$(2)" > $$@; rm -f $(STAMP)/$(1)-*; } -endef -$(eval $(call variant_rule,sound,$(SND_SRC))) -$(eval $(call variant_rule,music,$(MUSIC_FMT))) - -$(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)) -# ЗВУК — ОСОБЫЙ СЛУЧАЙ: упаковка, сборка архива и копия индекса делаются -# ОДНИМ рецептом, а не тремя правилами через таймстемпы. -# -# Почему. Упаковщик и копирование укладываются в одну секунду, а make -# сравнивает mtime с секундной гранулярностью: цель, сделанная в ту же -# секунду, что и её зависимость, считается актуальной. При смене SND_SRC -# это давало ТИХО НЕВЕРНЫЙ результат — `sdlpop -> msdos -> sdlpop` -# перепаковывал build/atl, но в assets/packed оставался прежний набор -# (10 страниц вместо 9; поймано 2026-08-31, с паузой в секунду не -# воспроизводится). Внутри одного рецепта сравнивать нечего. -$(STAMP)/sound-$(SND_SRC): $(TOOLS)/pop_pack_sound.py $(TOOLS)/pop_idx.py \ - $(TOOLS)/paths.py $(TOOLS)/pop_pack_arc.py \ - $(STAMP)/sound.variant | $(STAMP) check-orig - cd $(TOOLS) && $(PY) pop_pack_sound.py --source $(SND_SRC) - @mkdir -p $(PACKED)/SND - $(ARC) $(PACKED)/SND/snd.arc $$(ls $(ATL)/sound/s*.bin | sort -V) - @cp $(ATL)/sound/snd.idx $(PACKED)/SND/snd.idx - @touch $@ -$(eval $(call stamp_rule,music-$(MUSIC_FMT),$(TOOLS)/pop_pack_music.py $(TOOLS)/pop_idx.py $(TOOLS)/paths.py $(STAMP)/music.variant,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,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,$(foreach i,$(MUS_IDS),$(ATL)/music/m$(i).bin) $(ATL)/music/mus.idx,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, -# которую печатает сам упаковщик звука. -# Зависимость на stamp, а не на список страниц: сколько их будет, известно -# только после запуска упаковщика (источник задаёт число). Порядок страниц -# в архиве обязан быть ЧИСЛОВЫМ — pop_snd_tbl адресует их индексом, а голый -# ls даёт s10 перед s2. sort -V, а не `-t s -k n`: буква s есть и в пути. -# Пара snd.arc + snd.idx согласована по построению: её делает ОДИН рецепт -# stamp'а выше (см. там же, почему не раздельными правилами). Здесь -# только восстановление, если файл снесли руками. -$(PACKED)/SND/snd.arc $(PACKED)/SND/snd.idx: $(STAMP)/sound-$(SND_SRC) - @test -f $@ || { $(MAKE) GENERATE=1 -B $(STAMP)/sound-$(SND_SRC); test -f $@; } - -# Файлы, которые ложатся на диск как есть, — простое копирование из 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))) -$(eval $(call copy_rule,MUS/mus.idx,$(ATL)/music/mus.idx)) -# Уровни идут на диск ПРЯМО из оригинала: формат res20xx.bin наш загрузчик -# читает как есть, упаковщика для них нет. check-orig здесь тоже order-only — -# без оригиналов взять уровни неоткуда, и сказать об этом надо внятно. -LVL_ORIG := assets/orig/SDLPoP/data/LEVELS -SND_ORIG_MSDOS := assets/orig/MSDOS -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)))) - -# README: перевод строки CRLF (текст читают в DOS-подобной среде) и, для -# русского, перекодировка в CP866. iconv БЕЗ -c намеренно: потеря символа -# должна ломать сборку, а не молча портить текст (memory -# iconv_cp866_workflow; //TRANSLIT на macOS падает). -$(PACKED)/README_E.TXT: dist/README.txt - @mkdir -p $(dir $@) - @sed 's/$$/\r/' $< > $@ - -# ПОРЯДОК ВАЖЕН: CRLF ставится ДО перекодировки. BSD sed в UTF-8 локали -# отказывается работать с байтами CP866 («RE error: illegal byte sequence»), -# а с валидным UTF-8 — работает. -$(PACKED)/README_R.TXT: dist/README.ru.txt - @mkdir -p $(dir $@) - @sed 's/$$/\r/' $< | iconv -f UTF-8 -t CP866 > $@ - -resources-readme: $(PACKED)/README_E.TXT $(PACKED)/README_R.TXT - -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 $(PACKED)/SND/snd.idx -resources-music: $(foreach i,$(MUS_IDS),$(PACKED)/MUS/m$(i).bin) $(PACKED)/MUS/mus.idx -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/SDLPoP — генерировать ресурсы не из чего." >&2; \ - echo " Скачать: make fetch (подробности — assets/orig/README.md)" >&2; false; } - @test "$(SND_SRC)" != msdos || test -d $(SND_ORIG_MSDOS) || { \ - echo "SprPoP: SND_SRC=msdos, но нет $(SND_ORIG_MSDOS)." >&2; \ - echo " Дистрибутив DOS не качается — положить руками, либо собирать" >&2; \ - echo " со звуком из SDLPoP: make SND_SRC=sdlpop" >&2; false; } - -else -# =========================================================================== -# Обычная сборка: assets/packed/ и gen/ — готовые входы -# =========================================================================== -# Ни одного правила с таймстемпами: пока файлы на месте, make их даже не -# рассматривает. Пропал файл — доделываем ровно его, рекурсивным вызовом в -# ветку генерации (она подтянет и заголовок, и оригиналы). -$(PACKED_FILES) $(GEN_H): - @echo "SprPoP: нет $@ — генерирую ресурсы" - @$(MAKE) GENERATE=1 $@ - -RES_TARGETS := resources resources-readme 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 $@ - -# Музыка из конкретного набора записей. Каждая цель пересобирает музыку и -# сразу раскладывает её в 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 - -# --------------------------------------------------------------------------- -# Внешние исходные данные (assets/orig/) -# --------------------------------------------------------------------------- -# В репозитории чужих данных нет — есть tools/fetch_orig.py, который знает, -# откуда их взять, и таблица источников внутри него. Качаем только то, -# чего нет; `FETCH_ARGS=--force` перекачивает заново, `FETCH_ARGS=--ref v1.23` -# берёт конкретный тег SDLPoP. -# make fetch SDLPoP + записи музыки -# make fetch-sdlpop только SDLPoP (data/ + src/-эталон) -# make fetch-music только записи саундтрека (43 МБ) -# make fetch-check что уже на месте -# make fetch-list таблица источников -# MSDOS/ не качается — это копирайтный дистрибутив; он НЕОБЯЗАТЕЛЕН, всё -# нужное (уровни, оцифровка) есть в SDLPoP. См. SND_SRC выше. -FETCH := $(PY) $(TOOLS)/fetch_orig.py -fetch: ; $(FETCH) all $(FETCH_ARGS) -fetch-sdlpop: ; $(FETCH) sdlpop $(FETCH_ARGS) -fetch-music: ; $(FETCH) music $(FETCH_ARGS) -fetch-check: ; @$(FETCH) --check -fetch-list: ; @$(FETCH) --list -FETCH_TARGETS := fetch fetch-sdlpop fetch-music fetch-check fetch-list - -# Тесты инструментов (хост, питон): форматы, которые читает Z80. Секунды, -# ни эмулятора, ни оригиналов не нужно. -test-tools: - @cd $(CURDIR) && $(PY) -m unittest discover -s tools/tests - -# Перелинковка приложения без пересборки библиотек и ресурсов. -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) \ - $(FETCH_TARGETS) test-tools relink distclean run-hdd diff --git a/applications/SprPoP/README.md b/applications/SprPoP/README.md deleted file mode 100644 index 33f9859..0000000 --- a/applications/SprPoP/README.md +++ /dev/null @@ -1,82 +0,0 @@ -# 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 -make run-hdd MAME_HOME=/путь/к/MAME/runtime # запуск с локальным CHD -``` - -Чужих данных в репозитории нет — есть адреса, откуда их взять. Чтобы -перегенерировать ресурсы (или получить эталон механики), сначала: - -```sh -make fetch # SDLPoP + записи саундтрека DOS-версии в assets/orig/ -``` - -## Что где - -``` -src/ рукописный C: главный цикл sprpop.c + движок pop_*.c -gen/ генерируемые заголовки — руками не править -assets/orig/ оригинальные данные игры (вне репозитория, качает `make fetch`) -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 run-hdd` | передать локальный CHD в MAME как `-hard2` без изменения установки эмулятора | -| `make fetch` | скачать внешние исходные данные в `assets/orig/`: `fetch-sdlpop`, `fetch-music`, `fetch-check`, `fetch-list` | -| `make resources` | перегенерировать ресурсы из `assets/orig/` | -| `make music-mp3` | музыка из другого набора записей: `music-flac` (умолчание), `music-mp3`, `music-ogg`, `music-mt32` | -| `make SND_SRC=msdos` | оцифровка эффектов из дистрибутива DOS вместо SDLPoP (нужен свой `assets/orig/MSDOS/`) | -| `make -C tests/host` | модульные тесты движка (секунды, без эмулятора) | -| `make test-tools` | тесты упаковщиков на хосте: форматы файлов, которые читает Z80 | -| `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`. Заполняется он одной -командой `make fetch` — качается SDLPoP (ресурсы + исходники-эталон) и записи -саундтрека; адрес архива музыки скрипт берёт из самого SDLPoP. - -Оригинальный дистрибутив DOS (`assets/orig/MSDOS/`) **не нужен**: и уровни, и -оцифровка звука берутся из SDLPoP. Он даёт ровно одно — более полный набор -эффектов (в SDLPoP пуст звук `spiked`, 48); у кого дистрибутив есть, включает -его явно: `make SND_SRC=msdos`. Подробности — `assets/orig/README.md`. - -## Происхождение - -Выделен из `applications/PoP/roomtest` 2026-08-26; та папка заморожена и -служит архивом закрытых задач, багов и исполненных планов. Референсные -реализации (Apple II 6502, Princed Resources, mininim) остались в -`applications/PoP/`. diff --git a/applications/SprPoP/assets/orig/README.md b/applications/SprPoP/assets/orig/README.md deleted file mode 100644 index 360080d..0000000 --- a/applications/SprPoP/assets/orig/README.md +++ /dev/null @@ -1,82 +0,0 @@ -# assets/orig — оригинальные данные - -Здесь лежат **исходные** данные Prince of Persia, из которых `make resources` -делает ресурсы Sprinter-версии. В репозиторий этот каталог не идёт — -материал копирайтный (см. `.gitignore`). Вместо данных в репозитории лежит -**знание, откуда они берутся**: `tools/fetch_orig.py` и таблица источников -внутри него. - -## Как заполнить - -``` -make fetch # SDLPoP + записи саундтрека (всё, что качается) -make fetch-check # что уже на месте -make fetch-list # таблица источников с адресами -``` - -Тонкости: - -``` -make fetch-sdlpop только SDLPoP -make fetch-music только музыка (43 МБ) -make FETCH_ARGS=--force fetch перекачать заново -make FETCH_ARGS="--ref v1.23" fetch-sdlpop конкретный тег SDLPoP -``` - -Скачанные архивы кэшируются в `assets/orig/.cache/` — повторный `make fetch` -сеть не трогает. Версия SDLPoP записывается в `assets/orig/SDLPoP/.fetch.json` -(ветка/тег, sha, дата), и следующий `make fetch` по ней понимает, последняя -у нас версия или нет. - -Собрать игру без `assets/orig/` **можно**: упакованные ресурсы лежат в -`../packed/`, а генерируемые заголовки — в `../../gen/`. `assets/orig` нужен -только чтобы ресурсы **перегенерировать**. - -Исключение — `LEVELS`: файлы `res20xx.bin` ложатся на диск игры как есть, без -конвертации, поэтому `assets/packed/LEVELS/` тоже вне репозитория и заполняется -из `orig` командой `make resources-levels`. - -## Что и откуда - -| Каталог | Что это | Где взять | -|---|---|---| -| `SDLPoP/data/` | Распакованные ресурсы DOS-версии: PNG-спрайты (`VDUNGEON`, `VPALACE`, `KID`, `GUARD`, `SKEL`, `VIZIER`, `SHADOW`, `TITLE`, `PV`, `PRINCE`), палитры, `LEVELS/res20xx.bin`, оцифровка `DIGISND*.DAT` | `make fetch-sdlpop` — (GPLv3) | -| `SDLPoP/src/` | Исходники SDLPoP. Нужны не только упаковщикам (`menu.c` и `seg009.c` содержат встроенные шрифты, из `seg005/seg006` берутся таблицы кадров и `seqtbl`), но и как **эталон механики**: расхождение в поведении всегда сверяется с ними | тем же `make fetch-sdlpop` | -| `PoP1_DOS_music/` | Записи музыки DOS-версии в четырёх наборах: `flac/` (по умолчанию), `mp3/`, `ogg/`, `ogg_MT-32/` (исполнение Roland MT-32). Имена треков во всех наборах одинаковые; какой брать — задаёт `MUSIC_FMT` | `make fetch-music`. Адрес архива скрипт читает из самого SDLPoP (`ReadMe.amigaos4`, секция «AUDIO IS SLOW/AWFULL»), запасной — popot.org | -| `MSDOS/` | **Необязательно.** Оригинальная поставка PoP 1.0 для MS-DOS. Используется только `digisnd*.dat` со звуком, и только если попросить: `make SND_SRC=msdos` | Не качается — дистрибутив игры, положить руками | - -## MSDOS: зачем он и почему без него живётся - -По умолчанию **всё** берётся из SDLPoP — и уровни, и оцифровка звука. Так -сборка работает на чистой машине сразу после `make fetch`. - -`MSDOS/` даёт ровно одно: более полный набор оцифрованных эффектов. Из 31 -звука 28 совпадают побайтно, а три — нет, и все три лучше в дистрибутиве: -`spiked` (48, насаживание на пики) в наборе SDLPoP длиной 7 сэмплов, то есть -его нет; `sword_vs_sword` (10) короче; `sword_moving` (11) — другая запись. -Разбор — `docs/sound_plan.md`. Включается это явно: - -``` -make SND_SRC=msdos resources-sound # перепаковать звук из дистрибутива -make SND_SRC=msdos # и собрать с ним -``` - -Источник входит в имя stamp'а, поэтому переключение `SND_SRC` само делает -звук устаревшим — `-B` не нужен. - -## Кто что читает - -| Упаковщик | Читает | -|---|---| -| `pop_pack_bg.py`, `render_room.py` | `SDLPoP/data/{VDUNGEON,VPALACE,LEVELS}` | -| `pop_pack_kid.py` | `SDLPoP/data/KID` | -| `pop_pack_guard.py` | `SDLPoP/data/{GUARD,SKEL,VIZIER,PRINCE}` | -| `pop_pack_shadow.py` | `SDLPoP/data/{KID,SHADOW}` | -| `pop_pack_title.py`, `pop_pack_intro.py` | `SDLPoP/data/{TITLE,PV,PRINCE,KID}` | -| `pop_extract_font.py` | `SDLPoP/src/{menu.c,seg009.c}` | -| `pop_extract_kid_data.py` | `SDLPoP/src/` (таблицы кадров и `seqtbl`) | -| `pop_pack_sound.py` | `SDLPoP/data/DIGISND*.DAT` (`--source sdlpop`, умолчание) либо `MSDOS/digisnd*.dat` (`--source msdos`) | -| `pop_pack_music.py` | `PoP1_DOS_music/<набор>/` (`--format flac\|mp3\|ogg\|mt32`) | - -Точная раскладка путей — в `tools/paths.py`; менять её нужно там, а не в -отдельных упаковщиках. Адреса внешних источников — в `tools/fetch_orig.py`. diff --git a/applications/SprPoP/assets/packed/BG/bg.arc b/applications/SprPoP/assets/packed/BG/bg.arc deleted file mode 100644 index cc36258..0000000 Binary files a/applications/SprPoP/assets/packed/BG/bg.arc and /dev/null differ diff --git a/applications/SprPoP/assets/packed/BG/pal_tile.pal b/applications/SprPoP/assets/packed/BG/pal_tile.pal deleted file mode 100644 index df429b2..0000000 Binary files a/applications/SprPoP/assets/packed/BG/pal_tile.pal and /dev/null differ diff --git a/applications/SprPoP/assets/packed/BG/pop_tile.pal b/applications/SprPoP/assets/packed/BG/pop_tile.pal deleted file mode 100644 index e62c573..0000000 Binary files a/applications/SprPoP/assets/packed/BG/pop_tile.pal and /dev/null differ diff --git a/applications/SprPoP/assets/packed/FONT/font.atl b/applications/SprPoP/assets/packed/FONT/font.atl deleted file mode 100644 index a8a98e1..0000000 Binary files a/applications/SprPoP/assets/packed/FONT/font.atl and /dev/null differ diff --git a/applications/SprPoP/assets/packed/GUARD/guard.arc b/applications/SprPoP/assets/packed/GUARD/guard.arc deleted file mode 100644 index ad3aed9..0000000 Binary files a/applications/SprPoP/assets/packed/GUARD/guard.arc and /dev/null differ diff --git a/applications/SprPoP/assets/packed/KID/kid.ani b/applications/SprPoP/assets/packed/KID/kid.ani deleted file mode 100644 index ec368af..0000000 Binary files a/applications/SprPoP/assets/packed/KID/kid.ani and /dev/null differ diff --git a/applications/SprPoP/assets/packed/KID/kid.arc b/applications/SprPoP/assets/packed/KID/kid.arc deleted file mode 100644 index 112e465..0000000 Binary files a/applications/SprPoP/assets/packed/KID/kid.arc and /dev/null differ diff --git a/applications/SprPoP/assets/packed/KID/kid.pal b/applications/SprPoP/assets/packed/KID/kid.pal deleted file mode 100644 index bfbd2cd..0000000 Binary files a/applications/SprPoP/assets/packed/KID/kid.pal and /dev/null differ diff --git a/applications/SprPoP/assets/packed/MUS/m24.bin b/applications/SprPoP/assets/packed/MUS/m24.bin deleted file mode 100644 index d861766..0000000 --- a/applications/SprPoP/assets/packed/MUS/m24.bin +++ /dev/null @@ -1 +0,0 @@ -{}|{~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 deleted file mode 100644 index 6559022..0000000 --- a/applications/SprPoP/assets/packed/MUS/m25.bin +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index ac75614..0000000 --- a/applications/SprPoP/assets/packed/MUS/m26.bin +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 7472064..0000000 --- a/applications/SprPoP/assets/packed/MUS/m27.bin +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index fbdd262..0000000 --- a/applications/SprPoP/assets/packed/MUS/m28.bin +++ /dev/null @@ -1 +0,0 @@ -{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 deleted file mode 100644 index 744fa8d..0000000 --- a/applications/SprPoP/assets/packed/MUS/m30.bin +++ /dev/null @@ -1 +0,0 @@ -~}~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 deleted file mode 100644 index 44fa7c7..0000000 --- a/applications/SprPoP/assets/packed/MUS/m32.bin +++ /dev/null @@ -1 +0,0 @@ -û~~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 deleted file mode 100644 index 5c9c638..0000000 --- a/applications/SprPoP/assets/packed/MUS/m33.bin +++ /dev/null @@ -1 +0,0 @@ -{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 deleted file mode 100644 index 40340d0..0000000 --- a/applications/SprPoP/assets/packed/MUS/m35.bin +++ /dev/null @@ -1 +0,0 @@ -}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 deleted file mode 100644 index 66f4253..0000000 --- a/applications/SprPoP/assets/packed/MUS/m36.bin +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index dcce0f0..0000000 --- a/applications/SprPoP/assets/packed/MUS/m37.bin +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index cdd7951..0000000 --- a/applications/SprPoP/assets/packed/MUS/m39.bin +++ /dev/null @@ -1 +0,0 @@ -~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}||||||||||||||||||||||}}}}}}}}}}}}}}}}~~~~~~~~~~~~~}}}}}}||||||{{{{{{{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 deleted file mode 100644 index 018cc3b..0000000 --- a/applications/SprPoP/assets/packed/MUS/m40.bin +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index fae6352..0000000 --- a/applications/SprPoP/assets/packed/MUS/m41.bin +++ /dev/null @@ -1,2 +0,0 @@ -ĺ}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 deleted file mode 100644 index 0aba339..0000000 --- a/applications/SprPoP/assets/packed/MUS/m43.bin +++ /dev/null @@ -1 +0,0 @@ -{{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 deleted file mode 100644 index 00a7ce1..0000000 --- a/applications/SprPoP/assets/packed/MUS/m50.bin +++ /dev/null @@ -1 +0,0 @@ -}~}}~||||~}~}}|~~}~~~~~}~}~~~~}~}}~~~~~~}~}~~}~~~~}~}~~~}~}~~~~~~}~~|~}~~~}~~|~~}}}|~~~~~}~{~}{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 deleted file mode 100644 index f9fa0ff..0000000 --- a/applications/SprPoP/assets/packed/MUS/m52.bin +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 5d9b1ae..0000000 --- a/applications/SprPoP/assets/packed/MUS/m54.bin +++ /dev/null @@ -1 +0,0 @@ -~}||{{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 deleted file mode 100644 index 8188946..0000000 --- a/applications/SprPoP/assets/packed/MUS/m55.bin +++ /dev/null @@ -1,3 +0,0 @@ -~~~~~}}}{{|}{|}||~~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 deleted file mode 100644 index 5268478..0000000 --- a/applications/SprPoP/assets/packed/MUS/m56.bin +++ /dev/null @@ -1 +0,0 @@ -{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/MUS/mus.idx b/applications/SprPoP/assets/packed/MUS/mus.idx deleted file mode 100644 index 9da6442..0000000 Binary files a/applications/SprPoP/assets/packed/MUS/mus.idx and /dev/null differ diff --git a/applications/SprPoP/assets/packed/PV/pv.arc b/applications/SprPoP/assets/packed/PV/pv.arc deleted file mode 100644 index a5de9d7..0000000 Binary files a/applications/SprPoP/assets/packed/PV/pv.arc and /dev/null differ diff --git a/applications/SprPoP/assets/packed/PV/pv.pal b/applications/SprPoP/assets/packed/PV/pv.pal deleted file mode 100644 index ec99c2b..0000000 Binary files a/applications/SprPoP/assets/packed/PV/pv.pal and /dev/null differ diff --git a/applications/SprPoP/assets/packed/PV/story.pal b/applications/SprPoP/assets/packed/PV/story.pal deleted file mode 100644 index 1cb8dc0..0000000 Binary files a/applications/SprPoP/assets/packed/PV/story.pal and /dev/null differ diff --git a/applications/SprPoP/assets/packed/README_E.TXT b/applications/SprPoP/assets/packed/README_E.TXT deleted file mode 100644 index a867d67..0000000 --- a/applications/SprPoP/assets/packed/README_E.TXT +++ /dev/null @@ -1,134 +0,0 @@ -=========================================================================== - SprPoP -- Prince of Persia for the Sprinter Sp2000 -=========================================================================== - -A port of Jordan Mechner's Prince of Persia (1989) to the Sprinter Sp2000. - -The graphics are the VGA ones from the MS-DOS release, in 320x256 with a -256-colour palette; the music and the sound effects are the digitised -originals, played through the CBL channel. All fourteen levels, the title -sequence, the story screens, the intro with the Princess and Jaffar, the -ending and the hall of fame are in. - - -RUNNING IT ---------------------------------------------------------------------------- -From the DSS prompt, on the drive the game sits on: - - sprpop - -The game needs its data directories next to the program: - - BG\ background tiles KID\ the prince - GUARD\ guards SKEL\ the skeleton - VIZIER\ Jaffar SHADOW\ the shadow - SND\ sound effects MUS\ music - FONT\ text font TITLE\ title screens - PV\ story and intro screens LEVELS\ the fourteen levels - -If a directory is missing the game still starts, but whatever lived there -will be silent or invisible. - - -CONTROLS ---------------------------------------------------------------------------- -Running and climbing - - Left / Right turn, or run - Up jump, or climb up - Down crouch, or climb down - Down + Left/Right hop - Shift pick things up - Shift + Left/Right careful step - Home or Up+Left jump to the left - PgUp or Up+Right jump to the right - Up while running running jump - Shift while falling grab onto a ledge - -Sword fighting - - Left / Right advance or retreat - Shift strike - Up block - Down put the sword away - (Shift draws it again) - -While the game runs - - Backspace or Esc pause and open the menu - Space how much time is left - T keep the timer on screen - F6 quicksave - F9 quickload - Ctrl+A restart the level - Ctrl+R back to the title screen - Ctrl+S sound on / off - Ctrl+M music on / off - Ctrl+V show the build version - Ctrl+Q or F10 quit - -F10 works everywhere, including the title sequence. Any other key during -the titles or the story screens skips straight into the game. - - -CHEATS ---------------------------------------------------------------------------- -The cheats of the original, and they are on by default in this build. You -can turn them off on the Settings screen. - - Shift+L go to the next level - + / - one minute more / less - K kill the guard - Shift+I turn the screen upside down - Shift+W slow falling - Shift+S restore one hit point - Shift+T one more hit point (up to ten) - [ / ] nudge the prince one pixel - -Two cheats of the original are NOT here yet: R (resurrect the prince) and -H / J / U / N with Ctrl+B (look at the neighbouring room). - -Debug keys of this port, which the original has no equivalent for. They -all sit under Ctrl so that no plain letter is taken away from the layout -above. - - Ctrl+I immortality: off / in combat / almost everything - Ctrl+P speed mode - Ctrl+F freeze the frame, and unfreeze it - Ctrl+D debug bar: level, room, speed, sound, timer - Ctrl++ / Ctrl+- jump to the next / previous room by number - - -FILES THE GAME WRITES ---------------------------------------------------------------------------- - POP.CFG settings - POP.SAV quicksave, and POP.BAK the one before it - POP.HOF hall of fame - -All of them appear next to the program, and all of them can be deleted -- -the game will simply start from its defaults again. - - -WHAT IS NOT FINISHED ---------------------------------------------------------------------------- -All fourteen levels have been played through. The port is still under -active development, so rough edges do turn up; the ones we know about are -tracked in the project's own bug list. - -Missing against the original: the R cheat (resurrect the prince), the -H / J / U / N and Ctrl+B cheats for looking at neighbouring rooms, and the -screenshot key (F12). - - -CREDITS ---------------------------------------------------------------------------- -Prince of Persia was created by Jordan Mechner in 1989. - -The mechanics of this port were checked against SDLPoP -(github.com/NagyD/SDLPoP), which is the authoritative reading of how the -original engine behaves. The graphics come from the MS-DOS release, the -sound effects from its DIGISND files, and the music from recordings of the -DOS soundtrack. - -The port itself is built with sprinter-cc, our SDCC-based toolchain for the -Sprinter Sp2000. diff --git a/applications/SprPoP/assets/packed/README_R.TXT b/applications/SprPoP/assets/packed/README_R.TXT deleted file mode 100644 index af46062..0000000 --- a/applications/SprPoP/assets/packed/README_R.TXT +++ /dev/null @@ -1,130 +0,0 @@ -=========================================================================== - SprPoP -- Prince of Persia Sprinter Sp2000 -=========================================================================== - - Prince of Persia ठ 孥 (1989) Sprinter Sp2000. - -䨪 VGA-ᨨ MS-DOS, ० 320x256 ன 256 -梥⮢; 몠 㪮 䥪 - ਣ ஢, १ - CBL. ୠ ஢, ⠢, ࠭ ਨ, -㯫 ਭᮩ ஬, 䨭 ⠡ ४म. - - - ---------------------------------------------------------------------------- - ப DSS, ⮬ ᪥, : - - sprpop - -冷 ணࠬ ⠫ 묨: - - BG\ ⠩ 䮭 KID\ ਭ - GUARD\ ࠦ SKEL\ ᪥ - VIZIER\ SHADOW\ ⥭ - SND\ 㪮 䥪 MUS\ 몠 - FONT\ TITLE\ ⠢ - PV\ 㯫 LEVELS\ ୠ ஢ - - - ⠫ ࠢ , , -, . - - - ---------------------------------------------------------------------------- - - - / ࠢ - 릮 - - + /ࠢ 릮 - Shift । - Shift + /ࠢ ஦ 蠣 - Home + 릮 - PgUp +ࠢ 릮 ࠢ - 릮 ࠧ - Shift 楯 ୨ - - - - / ࠢ 㯠 㯠 - Shift 㤠 - - - (Shift ᭮) - - 室 - - Backspace Esc 㧠 - ஡ ᪮쪮 ⠫ ६ - T ঠ ⠩ ࠭ - F6 ஥ ࠭ - F9 㧪 - Ctrl+A ஢ - Ctrl+R ⠢ - Ctrl+S / 몫 - Ctrl+M 몠 / 몫 - Ctrl+V ᡮન - Ctrl+Q F10 室 - -F10 ࠡ⠥ , ⮬ ᫥ ६ ⠢.  㣠 -⠢ ࠭ ਨ ய᪠ 稭 . - - - ---------------------------------------------------------------------------- - ਣ; ⮩ ᡮથ 祭 㬮砭. 몫 -࠭ ஥. - - Shift+L ᫥騩 ஢ - + / - / - K 㡨 ࠦ - Shift+I ॢ ࠭ - Shift+W - Shift+S ஢ - Shift+T ਡ ஢ ( ) - [ / ] ਭ ᥫ - - ⮢ ਣ : R ( ਭ) H / J / U / N - Ctrl+B (ᬮ ᥤ ). - -⫠ ⮣ , ਣ . -Ctrl, ⮡ 㪢 ᪫ . - - Ctrl+I ᬥ⨥: 몫 / / - Ctrl+P ० ᪮ - Ctrl+F ⠭ ᭮ - Ctrl+D ⫠筠 ப: ஢, , ᪮, - Ctrl++ / Ctrl+- ३ ᫥ / । - - -, ---------------------------------------------------------------------------- - POP.CFG ன - POP.SAV ஥ ࠭, POP.BAK - ।饥 - POP.HOF ⠡ ४म - -  冷 ணࠬ,  㤠 - - 祭 㬮砭. - - - ---------------------------------------------------------------------------- - ୠ ஢ ன. த ࠧ, ⠪ -客 ; , , ᯨ᪥ - ஥. - -⨢ ਣ 墠⠥: R ( ਭ), ⮢ H / J / U / N - Ctrl+B ᬮ ᥤ ᭨ ࠭ (F12). - - - ---------------------------------------------------------------------------- -Prince of Persia ਤ㬠 ᠫ ठ 孥 1989 . - -堭 ⮣ ᢥ﫠 SDLPoP (github.com/NagyD/SDLPoP) - -ᠬ 筮 ⥭ ⮣, ᥡ ਣ . 䨪 - ᨨ MS-DOS, 㪮 䥪 - 䠩 DIGISND, 몠 -- ᥩ 㭤४ DOS-ᨨ. - - ᮡ࠭ 訬 ஬ sprinter-cc ᭮ SDCC Sprinter -Sp2000. diff --git a/applications/SprPoP/assets/packed/SHADOW/shadow.arc b/applications/SprPoP/assets/packed/SHADOW/shadow.arc deleted file mode 100644 index 1c0f9b1..0000000 Binary files a/applications/SprPoP/assets/packed/SHADOW/shadow.arc and /dev/null differ diff --git a/applications/SprPoP/assets/packed/SKEL/skel.arc b/applications/SprPoP/assets/packed/SKEL/skel.arc deleted file mode 100644 index be05df5..0000000 Binary files a/applications/SprPoP/assets/packed/SKEL/skel.arc and /dev/null differ diff --git a/applications/SprPoP/assets/packed/SND/snd.arc b/applications/SprPoP/assets/packed/SND/snd.arc deleted file mode 100644 index b4c5a58..0000000 Binary files a/applications/SprPoP/assets/packed/SND/snd.arc and /dev/null differ diff --git a/applications/SprPoP/assets/packed/SND/snd.idx b/applications/SprPoP/assets/packed/SND/snd.idx deleted file mode 100644 index 950900d..0000000 Binary files a/applications/SprPoP/assets/packed/SND/snd.idx and /dev/null differ diff --git a/applications/SprPoP/assets/packed/TITLE/title.arc b/applications/SprPoP/assets/packed/TITLE/title.arc deleted file mode 100644 index 3cf9f33..0000000 Binary files a/applications/SprPoP/assets/packed/TITLE/title.arc and /dev/null differ diff --git a/applications/SprPoP/assets/packed/TITLE/title.pal b/applications/SprPoP/assets/packed/TITLE/title.pal deleted file mode 100644 index 2b69603..0000000 Binary files a/applications/SprPoP/assets/packed/TITLE/title.pal and /dev/null differ diff --git a/applications/SprPoP/assets/packed/VIZIER/vizier.arc b/applications/SprPoP/assets/packed/VIZIER/vizier.arc deleted file mode 100644 index ffb7818..0000000 Binary files a/applications/SprPoP/assets/packed/VIZIER/vizier.arc and /dev/null differ diff --git a/applications/SprPoP/dist/MUS/m24.bin b/applications/SprPoP/dist/MUS/m24.bin deleted file mode 100644 index b880d4b..0000000 --- a/applications/SprPoP/dist/MUS/m24.bin +++ /dev/null @@ -1 +0,0 @@ -}~~}}{|w{|vw}~{{|~}y{zywuw|}y|~{z{yyx{w{zy{||{zvy~{{|zv{{vx~}{{}~{z{{yvux}|x}~~|~z{yyy{v{{z{||zxu{{{{zy~zuy}|{{~|||{yxw{|xx|~}{{zu|}}~}yw|x|~~|y||zz~}~~~}~|vy}{x{~~~~y~}~}{w~{|{||y}~z{}|}~|v}|wy}}~|zx|}xy~|y{yy~zyy|{z|z}zw|y|{~zy~|xv{}}ysy|vuz~}{x~{uw|~~~~}{~~{~}~~||xx{|~~~zxy{z}wx~{wz|{{~}}zy|}{~wvyz}y}y~|~wu||x}~xx}|~~xzyuuxvx|{|~x}|z{zx~}}~x~z{xr{uzz|zz}~uw}{xy|~yzuy|}|qu~{~~~~|y}~w}|}|~{|z{~~vx|}}{|z{~|~y}}tu}~xyvy~z}~~}z}}{{uz{xz~y}~}||}ww}xtz}}{y|wuwv}yy~xuuwwuwxxw|w{xo{xz|xy|vy}x{v||}|yw|~||z|zwvx}wwy{us~{z{|wmjprxx{|}x|u{|okonv}wsx{vrtrkxvxz}vsz~wx}tqsx||~xnmuwu~w{{xx~xwtor|}||}t~{{vxstu}}y|~|~yw~xvyxz{ys{}uz|{wv}{~}|zvruy{|{~~y{}xwvyt|xvq{xw}~v{xu}}uxzvuuv|x}y|xwxvwyz{xuxz~zxzz|v~}|tw{|w}t~{yz|{wt~{uy}x|wrt{}~{|zvv{zvvos{~su}|yty}{wwy}{v~y|~z{wtz}|}}xx|xz{x~zwysz{{zt|vuzwvx}z{{zyx{xzz{|w}~}qv~xw{v{~y}~}vzysqwx{y}~}{|}}w|yvswy}yp{u|wxw{r~}vs|~zvz{jwzttz~{s{utzww}xjikw|wy~{|z{zr}yv~~}~xxwqxxv}z~{vs}{~}t~~}}qmrvz~touzvwpu|~unwqprmlryz~~{}xuzwy}~}uv~iglkxzx{}snlutty~~|xw}y{x}}~{z||{xyufophw~}{wu~zvxrprllvv~{wzy{psvrpaaouyyz}xkmqiiv||{}vxz{{~y~zs}tvskjmpjnsv|y~x}}zzz{}knrggopxyyyvzwljmpgchrqu{{rqs~r{wrpmurs{|xw{txrrspsx~~upyvt{zy~}}|xt{{vxnuurqvuou||}}xxz|}~}oq~stubc[etwxzqovnkf`klt}~~}urxzorujegmquuw~xyuywlkrpu}|~y~wnvu{{{|}zyqnlnqohfqq|}~}z~}}}||zv||}|~u|{|tkc^`jt~vqjpmjlleesoo~}z|}vxlv}ztovtsrrqquzsy|x}ttwxtxxz|}yrxzswzsq|{t~zx~|ry}|zwnipvqu|xx~~zstty~~z{rq|~|}wfcbchmqyst|qdgfciirq~{xtowyrmlmqnkkryw{{z}}}|sw{qox}|~~}wz}~}vqxquqvsr{vvxwsppxnprwsz{}y~{z~v|~yyop~}}{~zwyy~y}piggccewzs|spvmelnfiu}ywlxvtlmwzxxvou}yrw|urx|zz|xz{}{u|~||zquwsxvprxxv{vz}~|zxz~wuyzttzzy~sz|z}z||xqruuwsu}}vx}yvzpsrmebbkz{nkqilgikkzzzxuojllmnmq~y~|z{v}unm|~xx~}zy|vw{qpulqmww{}{||{z{yzwv|rkq|{{{~z{u~{qwot{}~|xtrnkjsut}}}xpqupnv~}zz}~}zwssknqvxww{z{{spkmz{xx|z{wzxtsvzsu{tqqrliqurv~y}|upmx}uroy}y{~yxuvqnnrx~~{yyxuqnqwvpxy}ttsqxqwzzz}|||ysmlmoqv{}~|x|ujoupqx~~}x}zuwy|~|||unsqlrnwut}|}~pltrosyzy~wz~{x{rqsvz~~}{{{zx{susu{z}y}}xy||~}vy{x|toqstuuy}}ztnnxsifhtv}}}y{|ytstv{~|xstrljorontz}|~{u{|pjhqsr{y}z||~ysnmot||}}}{xywqtxvtx{{~~x|~~~~|xwwzytrqru{zx|~{ypuyqomlkr~{|zyyywwyxruumsosuntxz|~xv}vrqnls{||{zqy}vttsrqt|z||}|~~}}}}zzwpvuopzy{o|z~yvvvsppv||z~|xrt|sghleklq~{vtstx}~|ztmmrrnmqvyyz}}z}~~{xz~}qmqkoro{{}~x{}zvtrqruvr|}}zv~~~}|~~xrnkhs~{yzvt}~}}|{zxwxztv|w~{{xr|wqkhknmnvw}{zzyyz~~yywpuqrvqswxxxwuuxzw}|{yyz}~zrlmrrqu}suzywtuvvvwy{zy||}xx~~~}|}||ujgsvuy|zq}}xxuuxyyyz}~{y}}|ysw~xprnimgiopx}~}{z|~}{utusonswyyxxwssvow~~|wuvx{}|sssmstu}~y{{yxvuuvzz|~y~~uxzyyz|~~yrjmsmjrwyzx}zvwwxy|~}|~xz}yu}{wsooqqnmqx~}~}}}}}~}z}|y{uwxuwzzxvsqomkrxvz}}|z{}}yxywttx~wuuurqrsw|~}~wvvvvy}ztyyqmlknw||}tvtrsvz~{vqr|{uuwvqtrpvtsvz~}}}}~~y~~~{xxy{{yxuqmkhqvory{|~~|~~yy{~yxwuuuwz}|yzxxwtsuy{st|wolnnlsz~zyyywwy|zwqw}ywvustwywvwxyyz|~~|{|~ws|zz~}zyyywurminrkimqvz~~~xxwsrstw{{sx|yvtvz|x~{vrolmquy{z{xvvwy}}spxxssvxuxzw{|yxxwwvwy{~{}{z{}}{ywtqmiprigkmpty}||}}|~}|ywvwz|}|{x|}zvtux}}uw~ztstpmpnpx|~~}{z{}~wz}wsrrrvyzzz{||{zxvuvuz||}|x~wvxxz}{xvtplprkhhijmry}|}{z~~{wvuvx{|||~|vz}wvy||yxxxy~}z{vsqqqpmmpw~}{z{}zy~ytsrrvvvz|||}|{yvuspq|~~~xttw{}zwvskmvpjihggjou{}{yw~~}{zyyxy}}|{|~vrrw}}}{yyz{|}x||xvsstppqoosx~~~~~yrppruvvx{~}zxwqks|y{~}}~|xutwwy|yvqlqsmjihggimpu{|tv~~|{zz{{{z{}~zx}xsqtx{~~zy{{|}|zvttuutsrrstvy~|wrqssuy{|~}{wqntvsty~~}yvvvwxz|vmpxrlkjihghjnsw|~yz~wz|z|~~}|~}|}}|{ywz|xusstw{|~||~|x}zwttuttwyxxwvvw{zvttttvy|~yqs{tnorvz~zyyvvz}xruxqmkjjijjklnsx{|~|yvxz|~~~~}zuqx~||{yyxvy~}}~}}zyvw~zwvuvwxyz|}}|zxwxyx{||{xvx{}~~yv{}upoopty~~~~}||}|v|{qlkkkkkmnpqrruy~}~{wvvx{}xrqz}xwwy{}~~}}~||}}|wqt~{zywwxxxz}}}~~}|zvrs~~y}|{{}~~xxxsqppqty~~~~}{somlllmoopprrrtxzzy~~|}{yxwy}~xqu|yuuvwz~~}}~{sow|ywwwwxyz{||}~~~~}{wpr|~}}|~}}~{x|wusrqrsw{z~~~}|uqommmnprsrrqrsuust~|{}}zyz||us{|vrrrux|~~}~}ttzyuuvvwyyy{}}}}~~~}ztrxyuvz~~}}}}}~~|wyzwvusrrsuy}|{~~~~|{{wtqonnooqrsrqrrpnls~|{z||{{}zuy~ytrqruwy~~~~~zv{{tqqsvxzz{{|}}}}}}{vpvztpqv|}|~~}}}|xx|ywvutsrrsw{{~}zv|{xurpooopqqrrrrpmjkv}|{{{{zy{|{|zz}vrqrstvy|~}~}{}zsppsuvwz|}}}}}}}|xttzwqnnrv|}}~}{wu{~zxxwvuutttuw{~~||~~{vv~~}{xusqopqrrrsrpmjgmuutvy{|{z{}}|vsrsssuy|}{yxxrpoqsuwz|}~~~~}}zusz}wqnnnqv|~~~~zuv}{yxxxxwwvwwwwx||zz|~ytw{{}}|zxvtssrrssrqmihospoquy||}}~}xuttttuwz}|ut}}wsqppruy|~~}|xtu||wtrpooqu{{ux~|xwwxxyyz{{zzyyyz{|||z{|~{wz~zwwy{|}}|zyxwutsrpkgjqpkkloty}}zxwwvwy{}zy~}z{}zwtsstvy{}~|xsw~|wuuttstux|{~zvuuwxy{|}~~~}{zywts{}}~}{zutuwz}}zxvuspkinrokjjkosx}~z|~|{{zz{{|}~|yxy}}}{yvvvxy{|~~~{vu{}yvuvvwwwwxy||wuuvxz|~~}{wrqy~zwutuwz~|ywtoijpqmjiijmpuz}y}~}}~~~~~|~zxwy|}}|zxwwxy{||yuw}|xutuvwxyzzzz|~ywvvwy{}~~{upsz{z{~{xvuuvy|}ytnjospljiijlnqv{||z~~}~~}zy{xwwz~~}{yxxyzzyuv|~yvttuuwxyz{{zz|{xvvwy{}}ytrxzwuwz~{ywvuvx{~~ysmmsrmjihiikmpsx|{{~~}~~~}zw{}zxvvwz~|zxxxxwusy|wutttuvwxyyyxxz|~}zxwwxz}~~{urvzwsrtx|~|zxwvwxz}~yrotupkihhiijlorvz~}z~~||~}zvw}|{ywvwy}~|zxwvtru}}yvtttttuwyzzyyxxwwx|}zyxy{|~~zuv|{uqpqtx|~}{zxwwxz|ysuxtnjiihiijlnpsvz~{|~|~~{{|~|xuy|{{{{yxwx{~~~~}zwtppw}zvuttttuvwyz{{zywusry~|{{|}~~{wuz~ztqppruy}}{~}|{zzz{|}~zx|}voljjiijkmoprtwy||{{|zz}}{{|~|wv}}zyz{||||{{|~ytopwyvtttttuvwyz||||{yupow~~~~~{wx~zvsrqqruy~{z~~~~~}}}~~~|y|xsomlkklmnpqrstuvvuy|{{}}}}}xy{xxxz|~~wqsxvrqqstuwxz{||}}~}{wssy{xx{~}{xuy~zwuttttuwz~}|~~~~}||zww~|xurpooopqqrrrsssqpv~}}~{~{xxxy{~~~{wz{vqooprtwyz|}}}}}}{vtw|zvtuy}}zvu{}zxwvvwwwxy{~}}}}{wsu}~|zxvusrrrrrrrrrqolmu|}}~{yxxy{}|yy|}}wroooqtvy{||}}}}{yuv|}zvtsuy~~ytw}~{yxwwxxyyyzz{}~|{|~~zusx}}{{zzyxvutsssrrrqpljnuxwxz}|zyyz{}~zvz~xspoopruwz{||}||yvtx}{wtsrsvz~xw|~{ywwwwxxyyyzzz{}}{{{}}xtv{|zyyzzzzxwvutsrrqomijqutstwz}|{zyz{|~|wv|}xtqpoprtwy{|||{yvtv||xusqqqsw|}y{}ywwwwxxxyyyyyyz{{{|}{z{|~|wv{}zxwxz{{zzywvusrqpmjimrrpoqtw{}}|{zz{|~{wz~}||~zvsqppqsvxz{{{zwtty}zvtrqqqrtx}~{xwvwwwxyyzzzyyxxwwz~|{z{}{xz~|xvvwyz{||{zxwusrolikprommoqux|~}|||}{z~~zxx{~|zwtsrrsuwxzzzyvtw||xutssrrrsvy~{xwvwwxyz{{{{zyxvtsx}||}~|{|xuuuwy{}~~~|zxusokjmrpmkklnqtx}~}~~~~}{wvwz}~|ywuttuuwxyxwtty}zwuuuuuuuuvwy}~zxwwwxy{|}}}|{yvrrw}~}|~}yvuuuwy|~}zvqllprolkkkmoruz~|{}~~|}~{xwwy}~|{ywvuvvutrtz|ywuuuvwxxyyyyz|~}zyyyz{}}~~}|zwssx{yxy|~}~{ywvvvxz|~~ztoptsolkkklnpsvz~}||z}}{zz{}~|ywutrootzzxvvvvwxy{{||{{z{{{|}|{||}~~~~}zvtv{zwvvx{~{}|{yyxxyz{}~{vsvxupmlklmnpruwz}~~~~zz~}||}~|zwtpmnuyxvuuuvwxy{||||{zywuw~~}}}}}}|xuuz|yvttux{~{z}|{zzzzz{|}~~zwx{zupnlllmnpqsuxz}~~~~}~|y|~~}}~|xsolqvwutttuvwxy{||||{zwtrv~~~}|zvuy}|xusstvy}|y|~}||{{{{{{||}~~|yy}~ztqnmllmnoqrtvxz{|{|}||~~}~{{~~~~~~~}|}~|xrnpuwtrrrstuvwyz{{{zywspqx~~~}|zwuw}}yvtsrsuw{}zz}|||||{{{{{{|||{yy}~ytqomllmnopqstuwwwx}~{zz|~~~~|}|}}~~~~~}||}}xrqtwtrpqrstuvwxyz{{zxuqpsyzyz}~}{yvv{~|xutsrstvy|}z}~}|||}}}}}}||{{{zyx{~yvsqonmnnopqrrssssv}~|zzz|~}|||}~~}||}}}zuvywspoopqstvwxyzzzywtqrvyvuux|}zwux}~zwvttsstuwz}~}{{{|}}~~~}|{zxvv{~{xvtrqppppqqqqqqopt|~~}|{|}~|||}~~|zxx}|z|}yspnnoprtvwyzz{zywtsuzyusrtw{{wvz~}zwvvuuuvvwyz}}{{{|}~~|zwtu{~|{zyxwvutsrrrrqpommsy{{{}~~}}}~{wvz}xsqoopqsuwyzz{zywtty|{wusstvz~zy}~|ywwwwwxxyyyz{|}~|||}~~{wtvz|zyyzz{{zzyxvutsrpnkmrvwvwy|~yx||xusqqqrtuwxyyyxvtuz|{xvuuuvx{~~~|yxwwxxyzzzzzzzzzzz~~~~{wvy|{xwwxz{|}}|{ywvtrplknsutstvy|~~{|}{|~~|yvtsrrstuwxxxvttx||yxwvvvvwy{|yxwwxyzz{{{{zyywvw}~{xy|}zwvvwyz|}~~}{ywurollpssqqrtvy}~}~}~{yy{~}{ywutssttuvvutruz{zxvvuuuvvwy|}zxxwxyyz{{{zzywutw~}zz}|ywvvvxz|}~~}{xurnlossqooprtwz~}|}~~~}zxxy|~|zxvutttttutsrsx{zwvuuttttuvwy|}zyxxxyyz{{{zywtsty~~{z}|xvvuvxy{}}{xuqnorsqnmmnprux{|z{~~|ywwwy|~|zywutttssqrqz{}|~|}}{{~xz{{|wx{}}{wttqluvz~z~~}v{x~z|}}xtzrosx{xssvyur}xx|{qjmuyvrmefprjgpy}|~{vyw~wqyxwy}{x{xz}z{~|}~|{||omooy{sry}}}qox~mmit{}{yzy{zwmuzxymnmq|yzy~{twzyxxxr|}~tx}spyss}qm{vtuzzxzv{}xuxvx|}|{~}y~|zrqxx}|yzz|vop|yxv~t}}y|sxzvvz|~~wy~~}|u|~|}~||utswnrztozyw|~sr~~y}~{|}{uyuvw{||{|}||yxzuwynlupp}|{||{{zpt~xxuw|}~y{|~zy~vv{}~oiurlsokpuz||}v{|~y{vx~ux{sqtlpvppsjportz|v|z{xux{}|{v~{{wzxs{{v~}}}~|z{ur{|tl|wlsqkmswz|yxyzwywwmx}wxx{{yvttyiivwptpqxwy{}yyy{{wxrz}vx{v}~{{w~qmptnirtow~{{|s~zvzs|~}}y|~y|vu||xx{zy~r|}~suxqmmly{vxvpyvzz~vivzijleadhkmllxvz|ooztkkqvqs}}zrvypklemrwuxwqvqlnswy{x~}}uo|yx}z}}yvvwyr|xxzyxu~y}|vy~zsx}ww|xvvx~zlntthldekmprrpu}zsy|trplstkibbkkuy}uy|~yw{wwwz~wz{y|vsrt{}tw}x|xtuquzxsr{v{y|{v|~~{}~|zu~{tnmxvxvyupogaacdedjtourvsvxmfiskjnmfiqnnx}{zz~}stp~~z~~|wzspysxvz|{zronmjilwr{}|}wv}ztx~~zwvw{~tvnx~w{t{||~xyux~zvtz~xu{zsqsolwukcihfifoku~{{xx{zwqnnonnozz{wv~yvxsnrvpow||zy{~~xz}{zzzxptyoegolvw}{xtxywspqtuvw~}}z||xrolhfhqmsxz|}|snrrkksxyyz}z{vqwwqosuvutv~~||ysyymdhkgolvuy}xyxx~~~zuttsst|{~vs}}z}zux~{w{|xuuvskphv~{|}||y~{y~~}}|}~{zvs{yogjpkus}|{wrooppszy~~~tr{wturlkrqlovxvsrrtsjniu~v{{~~{yz{yzwvsyyrnvutzwzwx|urtojhhikpz{{{|y||}|vt|x}~z~}y|~zxx{~yv|{v}{tnpslrosxrx||{{zvpkqyqmrsommnoqwyszy{|}u{}vuwtoowyvywwx~|ryuruxz|}}unnwunjjuwz~z|}zy{}}y}~{}~zusstx~~{y}~yw{~~{z{{wllrutkqqnqtwxy|yuy{umfjtphdclklvt}tt{pruspoqtx~{}|y{|xv{|~}~}uqy|{{w|~w|||}|{{|vlqyqmflyxzqq{wmswwxv}~}z|vvspkiqsprzxpruxsqwuutruyqjsuy}|}yy}}}y{zzuuruwvyvy}|}rqnlnginks{w}}xz~|~ww}wr}xuuvx||w{}yv|~}~{ommx|zw}|}~vvvrvrhfmmepss}|z|w{npwqllpmhjkqy|stw{xyz|zxvy|xwz|}zsuv{v|wzyvss|}vqsq{sqzyls~ytx|yvwzwyr}zyrw}wrllnmqtz~{~|{}}}wx~~zxvitux}uswvpk|rstwytqqjnyurtqutk|ms}{zt~~yrvumjlrnwx{szoenkkghoz|}ssvrrtzz}{x}tktxrq}yz~vq~}vy{|x}xo}{|urkq|qlqjilbu{ujkx}sz|svyo|u{ro|wnp}xlplhjlmwyu|{ttnfo|qklonpwz}{|tuylppiquuy{zx{}}xmpwrlhhjkklnq{yy~}yzw{~~rpswxpx{w|nqup~{{~|~}~upnvztsusxogy|{xywunmroehtpszyz~}xsptld_ecekrytx||~yz{zqsrpqu}~~~pxjg~vpzyx{wv{}}~{stvqjegpwtswqp{t{|zyyz|~|{{rz}wwvyy}{}zvvxy~}vzwy|ysow}roy{yv~~~|vvrqmmgfqy~jcswo^`b_mmvw|}nuynswx~{yupsrw~~{ypwsywonottu||zpqzv~{q{zz}}||mswpjorpp|||~z{y|}y{~xwvenyqnryy}}|{qw{{uuyyvs}~svu|umqy{~vqrmp~zy|zwtlo~t{uvv~~~~zry{{zsr||xztrrt|}}|~{wu{~vzurx~~|z{wm{|{}~x|ysvtkrl{~~~qnngpzusznpx~~{vytudkikvrop{ysinvwyvyx}vxv|zyuvkqhpxzu}~yw|{s~~v~|{{~yonxpwvq{uszoupnjo~~yyt{rxyysu|||}|wtssyt~v}z}wumsyx~~vyrqlt{y}}~~~wzyzrnxuu~y~vvxsoejolilmcmirvz}~zso~t}z}|v||}uwyw~xwvu{wywrsslvqpurhtqeolnkltzu{lsuuovxz~v}v|zqosintquw}xrhonp|u}{}zyy}~|zpuys|~z}~zyoy{m|yw{~zxnliovxwmtlk}qt~~|}~zzs{ys|zuvx}xx{t}qrtyzx|xtrt|y|}v|yrwwooxijlkjhno|zns|fgkkfkpr{~u{yz~tpwoltqsx|prtnzzw|}{zwrtox}~z{yzy{{~xtyv|~}twwxtopipwtjim_qwku|~}}xxxy}}~|}xv{}}z|~|uxzvwrpnqurxv}{{yqxqlppgmofknrpovvo|{jlnooy{xuwzmkqhhqquy|vxzwkqsp}~{|~}zw{y~{~tpunnwuvx|~uyxx}}}|wxru~|omnauynv}~zsw{sqzuu}zys{|{~tsv{x{toutrsw|zy||svxq|vmrnprosv{zzoxymllmkty{xjtztmtsmw|}}|zz{w|xxyx{~~~~~|vyorqlwzzz}yyywuurzzpjkhrwuvzs~v}~z~{s|}w~z~yy~xzwpx{xwy~z{z~yy}uusq|niokmqtv|~}{vqpqq{o|~wlpojrxyz}wx{xzy||}~lzxqglkiv~{{uuy}|~}~zxspqvvvux|o~wpwut~|ywyy|yxw|x{tysoxzxw}|||xxrx{joonntv}xxxqkghhoyzxn}|qkpjkvy}{v{~{~|{}|{|z~~}wp}qpsmr|}~|||}y~z{z|z|w{yrohorprsw|{xuxyt|w~||vyxz|}vnsutrwvv{x{~|ssplvlmopptu|~u~unkllt{}uw}tnogfnsw||yplx}{|{|}}}{}z~}{sx~xqnlchpuy~}zyy|~yz||uvz{jq|stuw{|s{zxzvpw{}}zvyru~|rzsrtw~~}{pgjllmrpw}|zxwptvsqtsury}ozxjhggnpry}xt}wtwtigmqtz{tv}}w|{y{yzv}~{zxvu{x{xv}srvrhipv{z}}}}~~~|{uz{yslq}qk|wrtux{~{}vx|}}~yuyut}}}yu|z}yrxruy~|zmlnnnpnqyy|~{vvplrrkopqrr}~xumlknroy|}pv{pegknrxx~~~|wsv}~|z{vu}~x}|}{y~{~~y~u{whovnejpuy~z|}wuv{~~{yrxsvwuvw{}{{pxypsxzz{tuvruy{~yyvtvytzwkklnnolryx{yprwtossuwv{{|ojgglooy|{zkqxpegmrw|}~{xz~x}~{|{z{w}~zxy{w~~zw~uhmvrilry}z}|ypqxrwzuuux|swwx~}z}s||u}~|}}}~|vvvy~yy~roqrrsqqzvytmttprptszy}urnoryx|onvvlinsw{}~|yxy{v||z~{}~}}}zzsgfopiiov{y}wsvy~zyzy|~zqp{~xx}|xzp}~xyuutx}tqyzrnpqqrrtzxws|wvvtxw}zuqmnqww|xnluvomqux|~|z}~|{~|~~zy{}z~v}|ofdnqlnt{~|uqqz~yzzz|~{ss}}{|xuvwy|z}xwvtw{}ts|{tsttssryw}{ruzstptsx}~{vqpqvv|xonvtnnqtvy|~}~}~v}}|y}{~|{|y~~xv~ujcemnlpuz}~x}~~{ts}|{|~~}tps{{z{}}{wwvo{}w~|xt~wuvx|}~uqsvtponnmmttvxt|ztrvty|~{z}tolmnnu{~}wpllnomorux}|{yxywzxy}{|xvspuuy{y|xrzylhghlpruy||xy~}|wlv}{{{yy~~}wwvw{~~}{xyupuvz{v{{~{nw{wwxzy}zyvrsuqnlkmssu~|sx~spsruxyu{uqpqouy}utokmqppruz~}}||}xwuwtwvv{}{wsorsvzy{{uu|rjjfdiprtwx{|vsw{|vq~{y|{~z}zvwusx~}{xuusnmv}wwz}}vp{}{ywxwx~{wvqknqokjjorrw}x~suutxy}}x}ytpnmntv||xtsmgjqqqsv}}~|vxvtwvx{~}}wsnmqquwvxvpwxljjfciswy{|~wv~|}su~|xx|{~zyyzwty|yxuoq}{y{uyz{rrwvzywwxz{zxsmouuronrusz{zxqrsrwz~~z{wrppry|zwuohjruuvx}{~~}wwwuxy{~{vsrvvx{xxuqzvlkjgcgsz{|}~ux{{~w|~}~}|zwvvuru{ywns|}|yy}}xyuryzyxz|}yxvtoovyvrprtq|{vuww{}vzxsqrtz~yvtqkjrxxxz~|{xzx{}~}zvsruvvywuprzsljjifhs}x}{x~yxz|}xxxxvx}ypz}~yww{|~x{qnvyyy{}~{zywrqx|zvtuusxutww|zuyzwuvx~~zxvsnlsz{{|||xxyw{}}|xutwvxywtowyplkkjgis}|wz~{z{|xywwyywx|yqo}||yx{}{|{oov{zz{~{zxxwtpntywtsuqxywxxzvswyvtuy|~~}zxwuqlkrz|||{yywxz{}zy||wrprrsutrnoxrkjkkihkvw|z{}{z|~}~}~~|zvy{wyz{zxz{tox||~yvxyzzv|umpw|{z|~~y~|yyywsonsxwvvtv~xvvuvz}|rrvyvuy{~||yxvrnkls|}}{{z{}{ywtttuw{ywzytpqprsrqokstlkllkjjox}vt{yv{|~~}{}|zyvt}}wxzzzyxx}zsnq{|}|yyzz{~}v{|spsz~||||~~|{ww~wxxwspljotvust{}zzxxz|wrquxwxxz~~}zz~wwusoljlt}|}yyz{~|zvttstw{|vsuuqomnponmiksnijkkklnqzztx|xy~|}~~~{{{zxvrv~ywy{{{{zz}zspx}zy|zzy{}wu|xqqrx|}|{|~}{zvz{xyywtrnmpvyxvw~|~{ywxz}wsquyz{{}}z~|xxvtqonou}{yxy{~|xutstux||wssutrpqrqpmiotljkllmnqtzyu{zvy}}~}|yvrz~wwyz{|}|{}zrpz|{yy~|{{}xy~wsqqu|~~}{xu|xwxxwutqnovzyux}{yz}ytqrx|}~~~{yzxwvtsrqqt}|{z{}~{xvwxy{}}wrqtuusqrqokiqrkjjkmortv{}z|x{~|~~}zus|}wwx{}ut~||z{~|{{|~w{|trporz|wv~yyyzzzzxtsw||w|~}|z{~{vrry~{{|zyxwwvvtu}}|{|~~{xwxz{|~~ysquyxvttspjktqlklmoruxy{}~zxz~~{uw{wxy|~~~tx~z{{~~}|yztsrqrz~ztx{xxxyzzzxtru{{w~}~{wrrx}x}}yxwwvvwwvv}}{{|{xxyz{|~|wrorwxtrrokgnrljjlmpsw{|}}yy||~{vrz}wwxz}~zv~{~~}}}}~vz}ussrqrz~{vt|~xwxyyyzyxtrv{{~}|~|yurry~yxyxwvuuvwxxz{yxxz~~~|wuuuwy|~~}ytolqwvtspmhhqniiklnqvz}~}~yvx|}~~}{wru}xuwy{}}zyyv|{|}y}~}vu}yttttrt{~zvqv}wtuvuuuutsqquz}|~~|zwtrsz}yu{wttsrqrtwyz|{wvvwz}z}{vtstvy|~~||yupkjoturolgdkngfhjlosx|~~}|}}}vw{}{|{|xsqy{uuxz|}{vuv{||y~~{vqw{tstttstz{wsrzztttutttttssru{~z~zxwtstz{wv~{tssrqrsvy|}}xvuuwy}|x{|yusrtvy|}||~{xtpljntvsokfgokfgikmquz~|yyyvx|~|}~||wrt|xtvxz|~zurt~~}zx}~ztszystuvuuuz{wrt|wssttttttuttrtz}{~}{ywvuuz{uy~xssrqqrtw{}~}yvvvx{{xy|{wuuvx{|}{}zwtpmkmsvtojeioifgiknrw|}xy{yz~~{~}wrw|wuvy{~~wqt{x{~zsu{vsstuvvvy}wrx|vtuvvwxxyyxwusy~{||zyxxxz{v|xuuttuvx{~|}zyy{~|ww{|zyxxy{||z~|ywtrooswvqkgmpjiiknqv{yz|z{~~{yu{}wwxz}yqs|yzzuy|vssuwxyyz~~wt{{vuuwxyz{{zzyuqx||}{{{{z{zx~}wuuuuvxy|~{~{zz}}ywz}}|{z{||{{~{ywusqoquuqjhomihilosw|x|}|}}|ww}zvwx{~xpv}zz}vtzxsrsuwyzzz||uw|yvuvwyz{{{{zyvs~||~|{{{|{{~y{{wuuuuwx{}}}{z{}|xvx||{zyz{{y}~|zxvusqoqutoimpkiijmpuz~}x~}}~~{}w|~xwxy||tqz}{{}xrvyurstvxz{zy|yuz{utttuvwwxxyyxux}}~|{z{||{{~|y~|usrqqrtw{~~~{yxz}~zvtvz{zyz{{yz~{ywusrpnmorqkjolgghjmqv{~xy~}}~~{|~yy~zvvwy{~|vrw~}||{vqpwuqqsuvxyyyxz}|wx|vrrrrrsstvwxyxy}}|zyyyz{{{{}z|~wrqppprtx|}yxxz|{xtruyzyyzzyx}~yxvtrqpomlnpoknoiggiknsx}zw{{z~~|z}}}z~~xvwxz{}|xtt{}~||}xsnsvrprtvxyzzyxy{zx|{trqqqqqrsuwxyy}~~{zyyz{|}|{|~}{|tqpoooruy}|~|yxy{}}{wsrty|{{{zxx{xvusrqponllnmlqmgfgiloty~~yz}yy~{{}|}||}wvwxz|~|xtx~|~}||wqouuppqsuwy{{zxwyxz~ysrqqqqrtuwyzzz}zyyyz|}~}{{||~ztqpopqtx|~|~~{zz|~{xtrsy}}|{ywz{xwutsssrpmkllpsnihiknrw|{~~xx~~z}|{{}xwxz|~yv{~z|~~yrrwtpprtvy|~~}zwww}zussstuwxyzz{z{}{{{}}zyz{usrqrtwz}~{}~}}}zwuuy~~|zw||zxwvvvutrokijrtniijloty~~xw}~z{xy|yxy{~yx~y{xrtxsppruwz}~zvsvyvttuvxyz{{||z~|{{|~}yvyzusrstvy|~z|~|zxuux}|yw~{yxwwwwwusojgkutnkjknrw|xw|}{zv{}zy{|}x{}z{}urwvqpqsux{~~zuqxxutttuwxyz{{zz}{{{}~|wt{xtrqrsvy|{xz~}{yxuux}{x{{ywvuuuuutrmhenvqljjlosx}~{wx}|}wu~{yyz|~}xx}{}wqsvqnopsux{}~~|wrq|{vtrrrrsuvxyzz{yxxy{}~ztu{urpppruy}}xvy}}}|zxwustw}~zyzvusrqqrrrqojehrtnljkmpty}|~}xwy~~zvy~|zzz{}~~~}{xv}}}~zsqusnnoqsuxz{|{ytpu~}xurqoopqrtwxz||yxwxy{}~~{wsx}xtqooprvz~zwux|~|~~{zywvutux}}z}}wusqpppqqqpmhekrqnlllnrvz}|{{yy{~}yv|~|{zz{||}|zxvx~~~}}}wqsupmnoqsvxz{|{xsqx|zwurpoooqsuwyzzxwwxy{}~{vtz}zwtqppqtw{zwvx}~||{zxwwvuuw|}{zvtrqppqqqqpmhgmpoonmnptx|~{}{zz|~yx~~}|||}~~~}{xv{~~}}|wsvupnnprtwy{|}{xttyzyxvtrqqrstvxy{{yxyz{}{ww{zyxvsrrsvy||ywx|~}}|{zyyxwwwz}}}{xvtssssttsqnijnnnooopsvz~}}||}zz~}}~~}yx}~}{wvxupoopsux{|}}|ytvyxwyxvuuuuvwxyy{}{zz{}~{wy{xwxwutuvx{~|yy}}~~|{{{{zyxwy{{~{xwvuuuuuutrnjmolmpqqsuy|~}~~{}{|}yy}{yvxztpooqtwz|}~~|xuyzvvxyxwvwwwxyyy}}{z{|~~zx|{vvxxvvvwy||yy|}}||{{{{zywxy|{xwuuuuuutsplkonklorstwz~~~}|~z{~~zw{{xuvzxrooprtwz|}}|zvw{xtuxxwvvvvvwxyz~{yyz{}|xz}xstvwvuvxz}}{yy|}|~|{zyzzz{zywwy|xvtsrsssssqnknqmilpstvy|~}}}|~~||y{}{yvw|wsrx{upnopqtvyz{{zwv{{trsvvussssuvwy~~zxwwxz|}~~|yy}{tqruvuuvx{~~{zxwx|}{~zyxwwxxyzzyww{~xurqpoppqqpnklqojilqtvwy|~~|z}||~|{|~zx{}|{ywuu{}xsoszyronoprtvxyzywux|xrpruusrqqrtuwz{xwvwxy{|}|zx{~xrpqtvuuwy|~~|zyxxy|||{xwwvvwxyzzyvv}|wsqonnnooonlkosniimrvxy{}~~|yy}|}{{}|~yx{}|zxvtv~|vpntzxroooprtvxyyxvv{|wrpqtutrrrstuw}|yxwwxz{|{zxy}}xrpqtwwwyz|~~}{zyyy{}{yxwwwxyzzywvw}yurqpoooonmlnrsolkmsxz{|}}}{z|~~}{|~|z|~|ywuuy{vqotxxurqqrtuwwxxwwy}}yurruwwutsstuy|zyyzz{{zzy{~~zvsruxyzz{|~~|{zz}|{zzyyyyyxwvx}}yvtsrqponmmorsqonosx|~~~}{}}~|yxx{~zvsrtvwwvuuvwwxxxxwxz||zxvuwxyxwvuvx|~}||{{{zzz|}}{xuuvxz{|}~~|{zz{}~}{zzyxxwwvwz}|zwutrqpnnoqrsrqqruy|}|~}zyz||yvtstuvwwwwxxxxxxxxy{||zywwwxxxwwwxz~}||{zzzzz||{zxvvvxy{|~~}{zzz{|~~|{zyxwwwwxy|~~|zxvtrqpooprsssrstwz}}}~~|zz{}}zwuttuuvwxxyyyyyyxxyz{|{zyxwwwwwwwxz~~}|{{zzzz{|{zywvvwxz|~}|{zzzz{}~}{zxxwwwxy{|~~|zxvtsrqppqrsssstuwy|~~~}|{z{}}zxvuuuuvwxyyyzzyyyyzz{{{zyxwvvvvvwx{~~}||{{zz{{{{zyxwwwxz|~}|{zzz{{|~~}{zyxxxxyz|}~~|{ywutsrrqrssttuuvwy{}~~~~}|{{{}~}{yxvvuvvwwxyzzzzzzzz{{{{{zyxwvuuvwx{}~}}|||{{{{{{{zyxwwxy{}~|{{zz{{|}~}|{zyxxyy{|}~~}|zywvutssssttuuvvwy{|~~~~~~}|{{|}}{yxwvvvvwxyyz{{{{{{{||||{zyxwvvvvwy{~~~}}|||||||||{zyyxxxy{}~}|{{{{{|}~~}|{zyyyyz{|}~~}|zyxvuuttttuuvvwwxz{}~~~}||||}~|zywwvvvwwxyzz{{{{|||||||{zyxwwwwxy{~~~}}|||||||||{{zyyxyy{|~~|{{{{{{|}~~~~}|{zzyyyz{|}~~}|{zywwvuuuuvvwwxxyz|}~}||||}~~}{yxwvvvwwxyzz{{||||}}}}}|{zyxxwwxy{~~}}|||||||||||{zzyyyz{}~~}|{{{{||}}~~~~}|{{zzyzz{|}~~}|{zyxwvvvvvwwwxxyz{|~~~}||||}~|{yxwwwwwxyzz{{|||}}}}}}}|{zyxxxxyz|~}}}||||}}}}}||{zzyyz{|~~}|{{{{|||}}~~}}||{zzzz{{|~~~}|zyxxwwwwwwwxxyyz{|}~}||||}~~}{zyxwwwxxyzz{{||}}}}}}}}|{zyxxxxyz|~}}}||||}}}}|||{{zzzz{|~~}||{{{|||}}}}}}||{{z{{{|}~~}|{zyxxwwwxxxyyyz{|}~~~~}||||}~}|zyxxxxxyyz{{||}}}~~~~}}|{{zyxxxyz|~}}}}}}}}}}}}}||{{zz{{}~~}||{{||||}}}}}}||{{{{{|}~~}|{zyyxxxxxxyyyzz{|}~~~~~~}||||}~~|{zyxxxxyyz{{||}}~~~~~~}}|{zyyxxyz|~~}}}}}}}}}}}}||{{{z{{|~~}|||||||}}}}}}|||{{{{|}}~~}|{zzyyyyyyyyzz{{|}~~~~~~}||||}~~|{zyyxxyyzz{{||}}~~~~~~}}|{{zyyyyz|~~}}}}}}}}}}}}}||{{{{|}~~}|||||||}}}}}|||{{{{{|}}~~}||{zyyyyyyyzz{{||}~~~~~~}||||}~}|{zyyyyyzz{{||}}~~~~~~~~}||{zyyzz|}~}}}||}}}}}}}}|||{{{||}~}}||||||}}}}}|||{{{{{||}~~}|{zzzyyyyzzz{{|}}~~~~~~}||||}~~}|{zyyyyyzz{|||}}~~~~~~~~}||{zzzz{|~~}}}||}}}}}}}}|||||||}~~}}||||||||||||||{{{{||}~~~}|{{zzzzzzz{{{|}}~~~~~~}|||}~~}|{zzyyyzz{{||}}~~~~~~~~~}||{zzz{|}~~}}}}}}}}}}}}}||||||}~~~}}||||}}}}}||||{{{{||}~~~}}|{{zzzzzz{{||}}~~~~~~~}|||}~~}|{zzzzzz{{||}}~~~~~~~}||{{zz{|}~~}}}}}}}}}}}}}|||||}}~~}}}|||}}}}}}|||||{{||}}~~}}|{{{zzz{{{{||}~~~~}}~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m25.bin b/applications/SprPoP/dist/MUS/m25.bin deleted file mode 100644 index ec33c2b..0000000 --- a/applications/SprPoP/dist/MUS/m25.bin +++ /dev/null @@ -1 +0,0 @@ -t}vx}zx|zwq|q|urtsvo}~rz}v}v}~}z{n}yt}{yyzrs}rp~|wyzt{|~{x}uq{sxypzz||r~m|{s{|}v{zy~}{{|zwvrw}x{z}yn~|ny}}}t~r}|uyz{v{p}sxqz{~ursxys}zy~u~w||pus{yyzqvzqt~|~x~zw~}zx~ys|sw{rt}{ys~n|}xrzy|zy}|z{x}tqu~y~|z||o}|~~pz}}~wv|}v~|zt}p{wtp~~qyq~wy|p|y}v}|}~y~zzz|ox}wy{}xtw|xt~||}}vu{~yzyr~v{yuxy}vrw~t~vtz|~v}~|~wzztsrz{y|r{|z}sz}~~}{z{~x{~|v~ry|vo~{s{r~wx~p}||w|{zx}xz|ox}wz{}zv~uyyx|{|y~v|y~xty~s|xx{wt{{r~sw{}}w|{|~rr}xynj~kz}wpqkt~~uupxyw|{uss{}~~{p}~ztqzvl}|c}or{yuyyzwx}xzzneyr{tx~niks~tt{|yu~{~y{}xzq|vt}wzzp~yy}|xt~ywyrr|{y{z{~uzqvxkyx}qfzyqxtrr~}~v}}~{xwytvzpxh||w|qsy|tltvzxwssukpx}m{n{pzy|~}yk{z{}}{t|ylo|x|}{vz{yvw~{}qr}{xvs~uly{{x{~yq}n{|okqs{o~tz~z|pzlv|~~{|}z}vw~p{}}{}~{q~ttqj|sw|yvzyswsk|u{{x|z{vmz}}|~{wl|qy~twut}{{p{uzt}xvo~vg|y~oyrruk{{~wy}{vtt}|oyr|lx}ny|rn}v}t||~x~u~zxvx|{iy~|zxln~{zt{mp|~nv|{~|tquy{xnxx{{yvx|{}}{{}}{~~ryuw~vxqsiv~yxxprs|lwysy|mulzfpztu}iwrw}rsn~z{zt~~fz||y{kf~ruvywxzuyrusvoyuu{riu}}q{pw|xq~jyx|}vysy~wq~ruidz~~r~wxn}sk~{rr_u~hwozz}iozl~xz}kw~~}z{|q|mq~wuzq|yqrvzsip|rluovruyx}uukklur{uu}y}zrzz{ipdxulp|||zfpv}zs~~sxpqryoyxwxox}wcttrzzvnysv~wwv}|{~xtsz}|{~v}p|}}~wss~t|{|swt{|{vvu{n|vxn~n~pq|wmz~vwvpx{oxqw~on|mzu{x{~x~w{}n{sys{~t|ykw{~yvytytryyr~~hutxvs{{|nyy|}ww|{zzy{}xwvu{t~roowszusuv~}xrny{{zuz{xzhrnt}py|y{zzs~yuwv{s}{{zxvxxv|zoqphnj[ZYraZjcwsuu|z{ytnpsco[dxyxjhfmf_|~jy~siw}sfp}qjond`cytv|mefjieq}{yvzpzn{j|fmqSkr|x}~~yxm}k|l|vxsx~id^gagtw~wqzuvfzt^leszyzqpqzn}{tkdb\liuyv|rydj{lbqpp{zvg]WRisripfne^lX_o_nombak^~fapiiZ`hff[VlgNWqqiwwwznqv|qxjokpq\rmrfjmYllfrqtt|yssjqvl]`ktnl}|~vopXdxflktww|oo|uowu|vsmt|nubcxnpttyphkfnW]mngiu[f^gpanmascldsud{knfoxewzoxokqx{}}wrr|yvrsiso}oamlrv{u}wv~}w}lxwlo[tgeslsp~~zy{mn^WaNNVjnyiimqW[noietugvm_oie]]m_mbeubvusplseoyrdeai_Tee`WSXlpv}y~}ycnx{lh|srtuxrsismatjru^tovt|spoumeUbpjei}xouqy}v~juih\`rhv|}ppqimaTa`khz~r~}otvnxghtkk^or`lZinisinjmtn~xz{wtv~upq~vyv}{zrfnccmktlmwk}z|~ytyxusgrxpnt{|x{{vl}|yz}y~wy~qz{x~}s}xjmqxqfpj}wprw|um}~yzu|mmasygtcrxq}ruzp~yrwvjy~wkak|w~klqwvvrt}~nt}z}s~qgqyun{x|y~rfpvqnnsioykowkc]yzqpz{~xu}|slrfdttu{uxnamtfeik^bi\ch]SMfVYdN_^^kkdYekhtwwsp}~vxv~tp{wxsyy|~zxpxx|xwy}|t}{lv|l[dzhsogxru}zvty~~ns}ruw}uhXZUGO[eaZpk~||w~trzo`UkhYeYfoiosrhjqmwvk_jzqbt}mlo}{~~~|v|wkdi]Yfnqemrn}zyy}z~z}u{wxvmzttthnnr{xsm{zxv}ty|zvk_e`U^eia`shyw~|sqxxorzphjifmxnorh\Zpfcp`egamwmajjcoyrh^lrkft{pfdqqs{oo|z~y{w|}}v}}vxnhoqk^mmu{rtwmluzmnsutzvnvsaVhhSdc`kddqwqovsmurjckti_`mf^Zahbdmickusrznx|msmx|ru{||s~{nv||wleha[imdVcldvkv~qpzyjgktxihlsgbgkhp|qqwslslu}t|ywxsx~rpu~xy~}vtyo{rs|yz{|}{t|~~~~{{y~yt~owtnpjfmqlbstl}q|wrszukkmqy|trqkdmxgprhqsls~}z}~y~y{y|yx{ysv~|wqu{}xpcltujfptuohff`^b^TTibq{x}yzn}vt}xxzu|vv|xwvtzwxvsxzsitt}}|~~xzuwtv}wu|}y{~zxyvrq}xpr~yzxuz|xy~w}~|vzus{zufhnrphcedceb\Zhhawrx||zwxymihkqsmlohaipaek`djcbkmmqsomkebfvvpko}{wstsqvwx}}{||xzwz}~ytsokljcaqrm}|~}zxy~zurkgnr`ck_ckgfostwzvrpkghrpkddnnkgeb_cghnwwqx~rz~py}yzyw{}qnuwviglppkhifbcaZ[llfyqszz|xx{zuty~qpr{xnjjnuzvttpox}ox~uyzw~~yy|~{y{|{}ytw}~wy{y~zx|~|~~~tx~xmpuyxuturqqkfn|szy}|ssv|ulhhlqpmkihnwngusowwpsz}~{xyzsz~zvrnotvx}z}{|}y|xsmknqpkcbmqsidinplhgfeeb]^pyrx{{|||zy}z{~yw|}|}}{ztpw|~~ww{{xvuyyp|zs{}usxz|~}zvrpr|{tpy~zwstx{x{xwyt|~{xx{}zsihprpf^afihghigfc_cqqj{zt|z}{snorspmlkowuipvklsngkosxzwsmjmu~ztps~}yxz~~}zy}}~~|tnsz}|rorvwusrokf`_itlt}yvuz|pkumgqumjoty|}yuqorzysmhnuvtplhimqvwqmv|rvvt}}xpqwyvkefillllkgd_]eogevrn{{xwxz{}xwzyropsusrpqw~yq{|sxzuw||zy|{y|rqtxz{||zwsptx~}~~~xsw|~|rmmpstuvtpkffownpyw{yzzrmlnnljjmswoitumrzvqsx}~zvuxyrsz}|ytpoquy{wqu~xt}stz{yuqnnnke^\bhlibadhjkkjgc^Z_kohv{xwz|~|xrpx|w}|||zxxyu{wt||uqsvxyxuqpqw~}wrpv|}zwtux|~{srzzqv~unrx{{ywwvtoidgmome_^adfghgea^`jrkjxxs}{zzywutw|xqz}srzyroquxyxurru{~zut{~~~yz|{wz~}~~|{yvqkintxxrllmopqqojd`cnups{ry}srz{urtw{}|yvvx}~xrosxyxvsrsuxxtnpyyqwytx}xroquvtmgffhikkjhdbeosklzxrx~~zz|{wuutsstx~xs|wv~ytux{|{yxy~}}~{vtx||zrmmoruwxwusrx||vqpuxyunihjloppoljjqzyqy|y}yy|{vrpnmlmouyvor{yrx|vtvyzzwusux}|vpllquvtrqrtwxuppxzqpzwmkmonligfc`[Y]dikgb`adfghfda_cnwtu~}wuvxyz{zxuu{|~}|zz{wx}tw}|uqqsssrpoqv{{wropuy{zxwxz{ytnnvwnmvumimqssrqpmidachjje_\]_acdecbbhrvpo|y{~}||}{wzx}wvwxyxwwx|}yx}}~~~{{{vuwz|~~}zy{}yz~~{}ywyzzxvsqmhefkqutpljklmmkifceltsnw|~wzzwxz|~}}}{vvz|}|{yyyywrnryxps|ztsw|~~~}|yuqpswywrlhghjjjigefltsloz{tv~}}~|zyxy|~vwwv~}xvvwwwvwz~}yy}{|~z~xy~|xtqswzzwqmlmoqrrqpqv~}z~~|~}yurqtxywrmjijlllkihkrxuos}|vx|{~~zwuttux{|xqs||tt||wuuvvuttvy|{vpkikoqrqppqqpmjltwpmtwpihjkjhfc_\XWZ`ehhda`acdedb``dlpmlw~}ywwxyyxvuw}||{x{uz}xsrqqqppruxzxtomorvxxxxwwuqlkquqilsrkhilnnnljgdabeikjfa^]^_abbabfmsrlp{}xx~||{zzz{|~}{}~~~}~{yz}}xuuvwxxxww{|}xuux{|}~~~{xzx{wqppqpnljgdcejpstqmjiiiihgeeipuuqw{~~~zz|~~}|zxsopv{vps{{uqsvxyyxwtqoorvxwsnkiiijjjjkouyvpt}yx}~~}xy{w{}ywwvvwx{~}ywy|~zwy{uz~xttwxyxwuronoruwvsomlmnopqqty}|}yyz{{zxurpopsvwuplihhiiiijmrxytqx~xy~|zz}~|zz{}}wuz~ww~|xvusrrstvwtpjfefilmnoooolijpuskjoqjb_^___^\ZYXZ_dikjfdbbbbbaacgnuvsw~~}|}|xwwwwxxx{}y|xv{}yusrqqqsuxyxupmlnpstuutrpmijospihnqmgdefggfeca``cfiigda__`abcdglrvtps|~{~}~~||{|~~~}{ywwy|~{wutttuuvwz~|zurqsuwxyzzywuux~zuv{{tnjiihgfdbbcfkorsqnkjihhgggjoturqz}{wsqsx{vpqx{wrpqrtttsqpopsvxxuromlklllmptyzvrv~|y{|y}{x{|zyxyy{}}zwuvxz|}}}|ywtuz~|usx}{vrqqrrrqonmmpsuvuromllmnopsx}{|}{xwxxwvtrqpqsvwvsoljiiiijknsvvqos{|xuw|}||~~yx|{x{}zxwvuuvwwtplheefhijkkjigfinrqkhlpnhb_^]\[ZXWWY]bfiihfeddcccceimqpmoy~}}}|}~~}||}|zyxxyy{}~|}~xx}|ywuttuvxyywsomlmnpqqqpnkihlqqlghmplhdcdddcbaaabehijhfcbabbcegkpttqot|}z{~~|}{zzzzyxwvvxz}~~|yvuttttuw{}z~~{wspopqstuuutsrty}}wrrwwsmhedccbaaacfjorsrpnlkjiiijmquusrx~|yvsswzzupqwzwspoppqppoooqtvxxwtrpooopqsvz}|xvz}}|{~~yy~~|{{{|}|zwutuvxxxxwusrsw{ztprxzwronnnnmllklnqsuutrpoooprsw{~~~{}~yvttssrponoprtutromkjjkkmosvxuqqw}}yvx||||~}z{{y{|ywutssssrolhdccdfgijiihghkpqmfehkic]ZYXXXXXY[^bgjlmkigfeeefhkptvtty~{}~{z{{{{{zz{}}{zz{{}}}{xy}~{ywvvvwxyxwtpmlllmnonmlihhjnplfejnmiecbbbbaa`abegjjjhfeddeghkoswwtru}}}~}{~|yxwwwwvutuwz{||zxvtsttuwy}zz~}}}}{yuqonnoqrsttsrqsw{ztoostpjfcbaaa`aacgkoqsrpnmkjjjklosutrsz|ywvy||wqqvywspoooooonnoqsvxxwusqpppqrtx{~}ywz}}~}~z|~|{{{|~~{yvttuvwwwvusqqswzwqorwwsomllmllkkklnqsttrponnopqsw{~{}{}{wuuttsqpooqrtuuspnlkkkklnquvuqosz{xuux|}|}}}~z|~{ywuttttsqmjfdddfghiihgfgjnqnhehkjf`][ZYYXXXY\_dhjkkihggffghjmqsqorz||~zyyyyyyyxy{}}|{z{{|~{z}|zxwwwxyyyvspmlmmnoonmkihhkoojefkmkgdcbccbbaabcfhjkjhfeeefgikosvvsqv|~{|~}~{}|ywwwwvutttvxz{{zxutssstuwz~~{x|~~~}|{{{{yvspmmnoqrsssrqpruywrmmqqnhdbaaa``abdgkorsrqomlkkkkmpsvvtty}zxwz}|vqrvywronnooonnnoqsvxxwutrrrrsuwz}{y}~{{~|{{{|}~~}{xussstuvvutrpoptwvqmotusolkkkkkkjjkmprttsrppppqrtw{~~~|y{|wtsrrqqonnnprtttrpnlkkllnpswxurrv||yvw{~~~~~|y{~}zwtsqppqppmjgeddeghjjjjhghjnojdbege`\YXYYYZZ\^aeilnnmkjhhghhjmrvwvuzz{{xwwxxxxwwxz}~}|{{|}~}z{}zywwwwxyxwtqnllmmnnnmkiggimnkfeilkhdbbbcbbbabcfiklkjhggghiknquxyvtw~~~~}~}y|~zwvvvuutsstuxzzzxvusssstvx{~}yy~~|{zyyyyxurommmoprsssrppptwvpkknolfca````aacehlorsrqomllkklnqtwvtu{~{z|~ytswywsponnonnnnnpsuwxwutsrrssuwz}{y}|}}|{||}~~}zwussstuuutsqooqtvtompttqmkjjkkjjjjkmprssrpooooprtw{~}{}z{~yvttssrqpooprtutsqomllllmoruwurpty{xuvy|~||}ywussrrrqomigeefghjjjjihhjmolgdfihd_\ZZZZZZ[\_bfjlmlkjiiiiijlortsqs{|{|xwvvwvvvvvxz}}|zzzz{|~}|~{zyyyyzzywtqommnooonmkihikonjffkmkgdccccccbbcegjllkjihhhijlnruxxutx~}}~}~}z{~zwvuuuttsrstvxyyxvtsrrrstvy|~|ywz}}|zxwwwwwvtqnmlmnpqrrrponortsmiilmiea`__``aacdhkoqrrpomllkllnqtwwuuz||~yuuxzxtqoooooonnoqsvxxxvutttuvwy|~|}}~}|{{{|}}|zwtrqrrssssqpnmnqtsnklprpljiiiiiihijknqrsrqpoopqrtwz~}|~}~{xz~~{wtsrrqqponoprtuusqonnnnopruxywttx}}zxy|}z{~~|yvtrqppqqpoljhgghjklmlkjhhjmmidbdfea][Z[\]]^_adhknppnmkjjjjkmoswxww{~yz}|yvutuuutttuwy|}~}|zzzz{{}|}~|zyyxyyzyxurpnnnoooonljihjnolgfjmlifdddddddddfhkmnnmlkkklmoqtx{{ywz~}}|yz~|xvttttsrrqrtvxxxvusrrrrstvy|}{xx}~|~}zxvuuuuuuspnllmnoqqqqonmmprqkggjjgc`___``abcehkoqrqpnmllllmoquwvuv{}xwz|zvsqqppppoopqsvxxxvutttuvwy|~|~}||{|}}}{yvtrqrrsssrpomlnqspljmppmjhhhhhhhghiknpqqponnnopqsvy|~|y{~~}yz~|yvttssrqpppqsuvutrqoonooprtwyxtsw|}{xx{~}}|ywutssssrqoljhhiklmmmmkjiknolgdfhgd`]\\\]]]^`bfilnoomlkkkkklnqtutsu{|{~~zwuttttssssuwy{|{zyxwxxyz|~~}|{zz{{{zxuspoopppponmkijlookghkmlhfeeeeeeeeegilnnnmlkkklmoqtxzzxwz~}~}yz~|yvutttsrrqrsuvxwvusrqqrrsuwz|{xvz||~~~{xvutttuutrpnllmnopqqpomllnpoieehifb_^^_``abcehkoqrqpommlllmoquwxvvz}xx{|zvsrqqqqpppprtvxyxwvuuuvwxz}}~~}|{{{{||zxvsqppqqrqqonlklnqplijnomjhggghhgghhjmoqqqpooopqrtvy}~|{}|}{xy|}zvtsrrrqqppprsuvvtsqpppqqrtwz{yvvz|z|~}z{}}{xvtrqqqrrrpnljjjkmnnnnmkiijllhcadfda^\\]^_`abdgjmpqqpnmlkklmnqtxyxx{~zz}~|xutsssssrrsuwy{{zyxxxxyz{}}}{zzyzzzzxvsqoooopponmkiiknolhgjmligeeefffeffhjlnoonnmmmnoqsvy||{y{~|}{xz}}{wuttsssrqqrsuwwwvtsrrrrstvy{|zxx}~|~~|}}zwuttsttttrpnmmmnopqppnlkkmnmhddggeb_^^_`abbdfiloqrqpnmmllmnoruwwvw||{~}yvtsssrrqqqrtwxxxwvuuuuvwy|}|~~}|{{{{|{zxusqqqqrrqpomlkloqojiknoliggghhhgghikmopqponnnopqsvy|}{y{~{{~}zwvvvvuttstuvxyyxvutssstuvx{|{xwz~{|~{|~~|zxwvuuvvvtspnmmmnoooonlkjknolhfgjjgdbbbbcccdegilopqponnnnnopsuxyxvx}~~}zxxwwwvvuvwxz|||zyxxxxxy{}~|~}}~|{zzzz{{zxvsqppppqppomlklnpplijmonkihhhhhhhhijmopqqpoooopqrtwz||yx{|}|zxxxxwwvvwxz{||{zyxwwwxyz|~~{z}~~|{}~|zyxxxxyyxvtrppppqqqponmlmoqpliilnmjhggghhhiikloqsssrqqqqqrsux{||zy|}}}zxxwwwvvvwxy{|||{zyxxxyz{}}|~~~|{~}|{zzzzzzzxvsrqqqqqqponmmnqrqnklpqpnlkkkkkkklmoqstuttsssstuwy{~~|{~|}}zyyxxxxwwxy{|}}|{zyxxyyz|~~{{~~}}}|yz|~}|{yxxxxyyxwusqppppqqppnmlmoqqnkjmopnkjiijjjkklnprtuutssssstuwy|~~|z}~~|{zyyyyyyy{|~~~}|{{z{{|~}}}~}{{}~}|{zzzzzzywusrqqqqqqponnnpsspmlorrpnlllllllmnoqstuuuttsttuvxz}~~|z|}~~|{zzzyyyyz{}~~}|{zz{{|~~||~~~}|z|~~|{{zz{{{zywusrrrsssrqpoopstspmnqsrpnmlmmmmmnoqsuvvvuutuuuvxz|}~|z{~~~}|{zzzyyyz{|~~}|{{z{{|}|{}~}~}~~{{}~}||||||{zywutssssssrqqpqsuvspoqtusqonnnnnoopqstvwwwvuuuuvwy{}~~|z|~~~|{{{zzzz{|}~~}|{{{{|~|{~~}~~}}}{{|~~}|{{{|{{zxwutssssssrqqpqsuuspopstsqponnnnoopqstvwwwvvuuvvwy{}~}{z{}||{{{{{{|~~}||||}~}|}~~}}|{|~~}||||||{zxvutsttttsrrqqsuvvsppsuutrpppppppqqstvwxxwwvvvwwyz|~~}{z}~~~}||{{{{{|}~~}}|}}~}|~~~~}}~{{|~~}||||||{zxwutttttttsrrrtvwvsqqtvvusqqqqqqqrstvwyyyyxxwxxyz|}~|{}~~~}|{{{{{{|}~~}}||}~}|~}}~~|}~{z|~~}}||||||{zxwuuttttttssssuwxwtrsuwwvtsrrrrrrstvwyzzzzyyyyyz{}~||~~}|||{{{|}~~~}}}~~|}~}~~~~}|}|zz|~~}}||||||{ywvutttttttssstvxxurrtwxwusrrrrrsstuwxyzzzyyyyyz{|~}|}~~}||||||}~~~~~~}}~~~~~~}|}}{{|~~~}}}}}}|{zxwvuuuuuuttttuwyxvssuwxxvtsssssstuvwyz{{zzzyyzz{}~~|{}~~}|||||}}~~~~~~}}~}~~~~}}~|{{}~~}}}}}|{zxwvvvvvvuuttuwyyxvttwyyxvuttttttuvwyz{{{{zzzzz{|~}||~~}}|||||}~~~~~}|~}}~~~~}|}~{{|~~~~~~~}|{zxwwvvvvvvuuuvxzzxutvxzyxvuutttuuvwxz{{|{{zzzz{|}~~|{}~~~}}}}}}~~~~~}|~}}~~~~~~}|}~{{|~~~~~~~}|{zxwwvvvvvvuuvwyzzxutvxzzxwvuuuuuuvwxz{{||{{zz{{|}~~|{|~~}}}}}}~}|~~}~~~~~~}|}|{|}~~~~~~}|{yxwwwwwwvvvvwxz{zwuuxzzzxwvvuuvvvwxz{||||{{{{{|}~~||~~~~~}}}}}~~}}~}~~~~~~~~}|}|{|}~~~~~}|{yxxwwwwwwvvwwy{{zwvvxz{zyxwvvvvwwxyz{|}}}|||||}~~||~~~~~}}}}}}~}}~~}}~~~}~~~}|}~|{|}~~~~~~}|{zyxxwwwwwwwwxz{|zxvwy{{{yxwwwwwwxyz{|}}}}}|||}}~~|}~~~}}}}~~~}}}}~~~~}}~~}||~}{{|~~~~~~}}{zyxxxwwwwwwwxy{|{ywvxz|{zyxwwwwwxyz{|}}~}}}}}}}~}|~~~~}}~~}}~}}~~~}}~~~}||~}{{|~~~~}|{zyxxxxxxwwxxz{||zwwx{||{zyxxxxxyyz{|}~~~}}}}}~~~}|~~~~~~~~~~}}}}~~~~}}~~~}|}}{|}~~~}|{zyyxxxxxxxxy{|}{ywxz|||{zyxxxxyyz{|}~~~~}}}}~~}|}~~~~~~~~}}~~}}~~~~}}}~}||~~|{|~~~}|{zyyyyyxxxxyz|}}{yxy{|}|{zyyyyyyz{|}}~~~~~}}}~~~}|}~~~~~~~~~}}~}}~~~~}}}~~}|}~~|{|~~~}|{zyyyyyyxxyy{|}}{yxy{|}|{zyyyyyyz{|}}~~~~~}}~~~}|}~~~~~~~~}}~~}}~~~~}}}~~~}|}}||}~~}|{zzyyyyyyyyyz|}}|zyyz|}}|{zzyyyzz{|}}~~~~~~~~~~~|}~~~~~~~~}}~}}~~~~}}}~~~}|}~}||}~~}|{{zzyyyyyyyz{|}~|zyy{}~}}{{zzzzz{{|}~~~~~~~}}~~~~~~~~~}}~}}~~~~~~}}}~~~}||~}||}~~}||{zzzzyyyyzz|}~~|zyy{}~~}|{{zzz{{|}}~~}}~~~~~~~}~~}}~~~~~~}}}}~~~}|}~~|||~~}|{{zzzzzyyzz{|~~}{zyz|~~}||{{zz{{||}~~}}~~~~}}~}}}~~~~~~}}}}~~~}|}~~|||}~}||{zzzzzzzz{{}~~}|zz{|~~~}|{{{{{{|}}~~}}~~~~~~}~~}}~~~~~~}}}}~~}}}}~||}~~}||{{zzzzzzz{|}~~}{zz|}~~~}|{{{{{||}~}}}~~~~~}}~}}~~~~~~~}}}}~~~}}}~}||}~~}||{{{zzzz{{|}~~|{z{|~~~}||{{|||}~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m26.bin b/applications/SprPoP/dist/MUS/m26.bin deleted file mode 100644 index a6aa344..0000000 --- a/applications/SprPoP/dist/MUS/m26.bin +++ /dev/null @@ -1 +0,0 @@ -{vspooqsvz~~|{zz{}~~|zz{{xvvwy}{tov~zyyz{|}}uv~zvtqopqtx}~sqyxz{yspqx}tu|{|{|||||{}~z}wvwy{{zyyz|sqwqrvz}zyusw{ysqwxpr}||}|xzvuwyzzywwxy{|~tp|yux|{{}ww|}xrs{||}}ztop{~}}{z~~xuuvxxwutttuwxy{}~|uqw|{~}}|~~wsw~|zzzzyvqnowzwwxy|{wuuuvusrqpqrstuwyyuqt~|vu|{yyyxurnnt~|utuwz{wtssssrqooooprsuwxvrr{yvz~zyyxvronr|}~~urrtx}|wtrrrrrponnnoprtvwvsqw}wy|zyxvsonqy}|vqprv{}xtrqrrrponmnopqsuvvsquyx~~{yxvspnow}|xqpqty~{vsrrrrqpnnnopqsuwwtqs}zx}~{yxvtqnot~}{~yrpqsw}}xusssrqponnopqsuwwtpqz{x||yxvtqnns|}{}zsppsv{~zvtssrqponnopqsvwwtppx{x||zxwuronrz|{}zspqruy~|yvutsrponnopsuwyxtonv~{x||zxwurpnqx{{~zsqqrtx}~zxvusrpooopqsuxyytomt~~{x{|zxwuspopv~{z}zsqqrsv{|ywutrqpoopqsvxzyuomr~}|y{}zywvsqopu~~{z}{tqqrsuz~zxvtsqpoopqsvxzyvplq}~}}}yz}zywvtqopt{~{z|}uqpqrty~|ywutrqpppqsuxzzwqmoy}}zz~|zxvtrppry{z{wrpprtw|}zxvusrqpqqsuwyzwrmmv}|{y|}zxvurpoqw|zz~yspoqsv{|ywutrqqqqstwyyxtnlr}|~~z{|ywusqopu}}zz||upnoruz}zxwusrqqrstvxyxuploz}|}{z}}zxvtqppsz~|z{xqnnpty~~{ywvtsrrrrsuwxxvrmmu~|}~z{|ywtrpoqw}{z||tomnrw}}zxwvtsrrrstvwwvsomq{~|||z|{xuspopu||zz~xqllou{~|yxwutsssstuvwvtqnov}|~zz~}zvsqopsz}{z||tnkmry}zywvutssstuvvvurons~}|~|z|{xuroorw~{z{~~wpklpw~~{yxwvutssttuvvuspnqy~|}{{~}yvrpoqv}~|zz}yrlknt||zyxvuttsttuvvuspnpv~|}|z}~}{zz|~~}|}~zpifhnu|~}~~}{xtttvxy{zxrkd`gzyu~~y~unhccjwvzssuwvronsz~{|urqppqsvz~{ochyv{{~}|}{xu|ytqpppt}~|wwy{{wronpwzs~wpprtuwyzzzzywridn|zx}}{{}wspmklqxvy{wz~zutuy{{xy~zrklrusqqrrqmgciy~{{|~xop~}}|}}~}|tt{}unjjjlmot~{wsrw}{tqrqlhlu~}zy{~}x~{wyypquurruz~~{z~~|}{rkloppt{|vrmjgfgilqttnggp|zy{uqs|}xsrx~~{|~z|{vrooopponoomggq~~{wrorz}|~xrqw{upligginu{|xz|yvsruyuxzwspooonmklqz}z{|wsqqsx}yruyy~~|xuqmjhhlw{urtyztnlmmjilqutstwz{{{{~zz{z|~|xtrrstvwz}xx~xokjhfhpy~|ytpljiiknqrpkgku~{y}{ww{}xqoquvwz|wronoquxyvogdkx}zwsrwyv{}|{|~~zwy}}zvspnnoprttqnq}{tpqxyz}}zvrqomkjjpy~||}}{{{z|~zvttuw{}xz~|z{}~}zyxvsnjjp{zvvy|zsljiiimtyyvqnmnrv{~yu{|}yusqonou~{z~xromkknuyzxvtronmmoprqmgelz~}}{z{~{{|tmjknt|}zwtsrrtvxwrlip}{wtru}|tsy|ywy|wwx{|xvutssstuurmily}yxz}xy}}zvrolihlt~}zwuv|}wuvz|zyxxyz|}|xuz|xwz~zvtqops{~{xxz}|tlgfhmty{yurpnmmnrw{{vt{{~}yurnmov}}volmpuz}{xusrqqqpqqrrokfgr}{xwx||{~|smjkpw~|zxwvuvvwvrnkp}}yvtv|yuuy||{{{zyxy~~wssw}|xvuuuvwwupkjs}zz~xvz~~zvrolkms|~{xutv{~}xtsux~~}{{z{|||yurv{yy}yvsppt|~zxwx{~}wnigimsy|zxtrpnnnoqsvwvss{|xurpqv~~wpmmou{}yurpppqrttspkgit{yvvz}}{snmnpv{~|{yyxxxyzywrmls}zwuw}xvx|}{xvtttuw}{urrty|zxwxyyxuoloy}zz~~xx{}}{yvsqnmou}~zwvw{}ztomnqv{}||}~~{vrry|z{~~{xussv}~yvvy|}xrmjjknsx||zwtrqpqrstspmlr}|ywtstx}}|uqoopty~}yvsqqqsttrnijozsuy{{}ww|~w}skacjckqs|}{xqqjq|}y~txqqx~vv{{~txwuwstqyx{~yppnuz{uyzkj`\kz}vvmq|ztfjnlqv}|yr{~~rqukkjhvupxwstqtoqxw{t|{{t~zuxx~{~yu}xtloj_]Z`ckzyz~y|zkY\qyr~vqdhkgjpz~zwseg|zulfmkfgejhilelqspogn}~y~|ttpyymf_beky{}h_n{s~zyw}||jm{oncgjcadly~mgw{vovtmmkqquypny{wtsjmwx|~}}y~v{wljcmsnjcaadqswtjuziry~zsvwmorjedgryzeYd~trohonfebgjoxrous|z|zqqyxy}v}ytiotqrpomirrspeox`^^g|~x{z|wzsv{tmjjqusad}vuupwxpnknpt~{xzz}~vyvmkrolpt|sip}zoruqqswyv{wtwksme^]o|ssmqxslieiiv~vb_s{ljnktwqpnqsv~}~|~urxupqs~wgissunlmqwv|wnu{qbikc[S]go|~zqt}{vtprox}~nizmgjhsytrprtu}{yyusu}{wpkqoiijrx}jg}}wrqs{}y{~yintkeZ_bdp|yw~vjirrnnlnkptq{sqrgifqzxxwy||~|uz}xrwuoomsv~hat~uzuwzrkhhor}wxyw}{kowokacc_fqxtupvvsusvswysx~pqrba[cmmnpruu}zu}zpp||ytzyrrptv|piz}qxw{|tpnsu~|psvmkbdbZY_o~oi|wqvuqttywz}vxtv{if_dlkmqtzz|zv|||ytzyqompqu~~nfutdijq|ztqmpq{w{{qqillb\\etsk{~xzwrrsxx{wv}}zkmzgbY[caaeksv}|{{~zxtrtvywo~{ggfmz|ywrsqy~|{~qvxjicgi`YTU_lvm{~xvu{}~normb`ecacgqv{stklzsx{uplmoqzyyxrmhdftzz|z|x}}~szsohlpjc]Z\bvtkwxtwwvrort{~njtqhehdacemr|wg`s{}{xtssu~~}xsrjc`hnptvzyz}y}~w|{}tjovrhiolhea`_jtzoy}wvu|wwx|xo~vunkmg`_agjv}wdTZz}}~xuvvvv}~}|tvmfklkrx}wjk|zrpqmjkiidhjk|{jnxpolt{{|yxxy}xwvsqvsjgdimws^Zn~|~ywy~~||nwxpghgacht{~}vjh|~xx{unmosqusmryz}tjg^_onsyz}vlpokdl}}xyr}x{w|xwo[_v}}zzyysx}qooeda`ky~ribq|lktkiagzw|}xnq~winv}rqkl~wjotpurnsu~||rjcktompl~zvx~xd^n~|ipvzpyqujhjs|zpzzy|zxndhqv||pfk^bt~}sdm}{{{~ppyy~wy{zwyyzx}uvrukbbgmpns^bhyusps|zddrip~}u|y{}}trsxsiomfmqd`lry{sph[Uix{}ptruyzt||mjtuje^`o{|m~umlgj`]alye{tmnt}|ujau{qginv}xuocbsvskicgiy{ojguohmlrko{iiq~|xqxy}lnvwtyrpln}uvxwxxx|zwpnkjmy|svkgZcglv}fgllirwwruuwtz{t|wzu}w|{ytorqv~~}zv{zofkhlv~re~usjpshkrt~{dSfwot|r|wxxpuvptpjx|gzyrtqpkedhmql}ph_\Zjs~pcuyzyx||ysylgetmjntxzu{~|}|oojj\OPZd~kpzlmq|stzz}}lrx}}ukioszx||y}lty{w}ywqigjbety}efdhkffnvxu}ghdtytoprsns~zmlrww}qpnwztulnqkx||kfgkrz~vnyqlqwnjrsug}h_]emlqsy|rmeezwtllphv|xvtzvjgmqp}{tmusjroifhsmUao_[gw~q|zsmxtkotxswspnwplhe`impysuj`V]]buyubi|y|}yy{jmsrehmrwvw{}|~tmhih`SQT]l|mnoglwssw|}unzyvz~yrqnq{~x|ymcrqvsurjor~soebb`fnt~tzmgghdmrq|y|etznsw{{}~}{q{zsipxy~ztljptr~mvy~{}vkltwmrxib\jebm{|n\^ytquu{uy}wt{srzzy|vv{plyusupifkp~kd{rYXYiwppyywuuokpvzvmeotmfflsps{xrjgfZS\imaxyts}~~~zqdutrx{oiosvznjjegb^bjlwu{dbbkjkrt|t~qlq||{wxzzw{rft|vpt{|twxyvpnqjdcls|st|oahhbgpwooskgsvyz~{z{uunz~yu{wmsquto}uv~|unpy|ngmjbbfimowcVnvhoz~xx{v~rqwpox|zv~q~{qqrnknnpxzrcxtdXZcez{zoaruswxpqwz}rvkehgkljrwszprni_XUZdyfnyqqp{xvz~~rcmyyx~{okqy}|r~ssigjnlkfnty|uf^amacmz~z~nkfvtlrxzqvt~uglt}}ui|}x}nowutsyogen|trmzimoaeiwv}wkhifkemt~z~}wkdl{{xrsnxqms|vvpzynuqtuzmf}qlulgjmlqoYoq`^py~s~wyskzqlj|x{twzrpxumoodqoxyxdhc`\ai~~vmeqvukwzxru}~~ypnzkdlhklywzx~omwul\\UTevqxmshqzvrmw~yyojxujyzkomvz||veh~}thr{tqloksvyvpjcdejdp{{wrejnrhit~zp||udwlsxsszysiu~~yzx~sslgkxv{wr~vppkgltvsjbfkgcr{|xdVmvvttvwxsuzzv}ysvwp|rw~{vdqvquxfimrtxnlysVW]x~}~so{vwutnuonnxvjlp{wqmsxkt|~|suegdU\gxd^ltx~twx}t}wmlxvzomjekp}jhzrl`d`_f}}tuh`okgq}zvkxquwtvyqwxu|wqluzzkp}xsuxvnqqd]_niu~i}ooiiitru~}p~uwir{wz{mkkqxxtr{ttlw|ooukysrrnz~ujztuoghmphuzjansoz|~y{~|}wvusyxmounptxwwoxrmv~moriXW]jwyrpcutx{ujsvzv~txqkidrvwy|~}|uuvsje[]h}joxspfi{z{~werxusxpommxvqvhnrtptljp}y|~mc`hfentnnyyuw{~~~{|glsy}vyvotxqpvogbdvtxxsxrpfioz~zrwmnxmnxxi\_~zvuvzus{}|xxnl~wvyw}mbzxltsfhrtrzgvyf`fvzt~xx{nvvrlt|~~oit|wsq|uvygutfd[Y]qwh_s|{{twwwsn|~}{vz|mrheixzy~}ktyugfaZeoqxyemhhs}~u}~ynlxwmuzqqqsw|km{ikxwxnsmoxx}}vmea`jbl}|xthopupowx|uf~xlr|t|vkswxxtzqphegtuxuv~ynligmt~wwqgkmihwys`Vnxwwx{zxttx{ur~{vrt~wqzt|}ufw~uqutfikos{qt}qZZaz~}z{|oqxwttruqqv~xwulqv}yuswwow{~wt}qdd^V]mo_f|nvuvprsur|}uksz|zpniiquykq|sjaa]`jyxofeliowxtv~rkvoqppw}zqvu{zvkh{}pkv~|sstnu~}|rmj`_fhjzwvsmmlllrlsws}{njk{x||wrjkp|yv{xwqojlmz{zl~uyuiljmuy}ztxvijjggl|~laatvzuxy~}~xx{{rv{qvvtq~{u|{lsxyywtstimsvwurzkbTZcv~wxqcj{puwkm~z{~||jtykiyqpwvq}zrqwnimi\Z_ewav~nmjjv|nsyuw}ypawzs~~|oitxy~{ywlkohrtoiokp|seb\hheo|~|igkjnx|z~rovw{zsxu~xnqpx~xke[exowtz}qogalir{ot|smsjowdVezou~~vmp}s{zmq{ysnmuw~lk}yjquomsqqvl{wedfr~|u{wunt|sxsop}~zrkptrmn|{}~kwyndZ^^hzqh_p~xt~xv{~xw}|zwwu}vmijv{|z~orstnjaejkx{~{ifdcfs}vxljz~zw{x{wwt~xntsrux~xvvvs|}|ojb\echv}s{{o`lruwz{zrup|~wsptu~zzuvrjgnst}qq}}yxtldit{||wn|ojptjv}{iXhx|||~|uu|xz|xytpqyy{vsv|kszyxuxqkq{wzynv}c\Zctwvtrier|tnytsv}}}|lmlqy{w|v{pqm`Y[[qn`cu{nhv{z}wutnu}tlnlq~}wrslvmeeebvvoeZhd_roqzslvzvzwvzvzgk|tpw{quqjrxmkaZdgdytnypjmnfvxwvotmnhy}zyxudeguu{{oqhelrs~o|vy{x{qjopo{|zzvzpgijfqvjb_pmpvy~zpxuvxyypxtquw|q||{rsz~xsvtgmqmxru{k]WX^|uulgkusuyrputs|{{|vo~ooy}vyv}}vz~v{xnrh\[UXtxffruvskx|tsyrw~||tho}yw|powy|wttoytoptpch``sywwl_^ipgrzz~xwqogqvpy|x~|qfrx~zqvusqfn|qla]gqqtyyo}{nhnlhkzv}xynjegp{~xqb]cx|u|~xtofpz{~~wv|yry}vgotptwv~vqylhhcl~ulgpojftwvx}y|ysuyqssnw}xy{}|{s|trxsotnuun|p^_a^c|trfg|}||qyvptrt~|vyoovvspw|}|yzqpprcbfcdunxvimfgv{xw~z~ujosvrt}zzv}}stpztlpwtnwtot|~~vqhbdidryzxlhmjqtrwtr}rwzy~}sqns~wywtzphjorywnzz~vkijmv|}xxykptqn|}we_mvyzy}zwuy|yw~~xutsywwtx~|pxtrumqv{z}yqj|}ja[gz{vpsmhy|wttxvww~y|~|xspwwvvz||ttspi`]`tubcxrot~zzxy|tkjr{yuqvz||qlnussnhjw|wve`jggmywq|zvpqxz}}rt~ypw~~trt}}~sc`flnt||qp{nifhmy|rryrkwynkbjz{{|}}qnpvyzuzwz|xolklu|qmz|wtronsrgiwpkv~z{vzzxw~z~xuqstu~~wzv|~wxzttw{vxxy~yqytjb^[izzmgei{||~{wttx{zx}u|~wqmoyvzxsukgf^]i}}rmtutjdovy{|~}|mepzwzwupw~~tm~zosvqosiho}|}vc`deblr~{w}xtqowsr{nl|y~u{}~|tkos{zsdafgmqy~wowhiolnswxytqghpz}~qe]Yq~}|{ypkqpw{{|s|yvvv}wmjssu}{tpz{wsongepz}vniuqa`t~zytuxorzvwrprvs}zy}~~{vy}yxruuqnywu~j_`VWgz|sf`ixzwtqvtpy|}}{rmxwsqny||y{}znutkbc[Yevxp|yqiegsyx{uw}{~lm}|yto|~zurr~~uphj{mqwromjejsy}reab`goy~|wysqjloqnw~~~xpsyoqw}zw~|slihp||ujcefmy~{usyxmmkkruw}~qmicjoyobZ[o}}{xrqps{u|urtrx}usss}xt{yzuqnmglr{uot|wn]Wd|{rqutiu~usvopsrx}}}{szww{{x{xv{xuw{}zrec\T]mxe^q{txx~zutyvw}}xupky~}z{wtz}~||vwwlqpkhg`cn|{kghkins~vrwqtsqy{~zpkm|srxvxwqnst{widfhnqv~srxyppklpuu}tw~rmoy~wqlfbt}xz{vpqpv{{ur~x}yrtpqx|{qu}xrqmlquzwidmuppy~zw{yx}~ursvtxz~~{}ywtutuu|xls~rmca`jyyohfl}zxuqvz~~{|yxrz|tru||}}~~snnkkhddjx~ysxwsmmsz~~}|vil|z{yw|xoz{onoqtvqoov}|ogcfjpv{y}~qmousw}pr{~{xx{}spox|{~~tkgisv|}yt~wqlintz~yt}|xtsou~zna[j|{ytqy}|~vyts}~{utqt||rw{vuuutuuw|vsqr{thdmy{suyqoq~|xwvus~|z~xuyxwvu{}}}|~u}vmg]^dqyidkw|||}yy}{y~|~|yupq|yqtw{}|}~oturmibchp~{vmiimq{y|lmv}{x~}{w|~}ukn|qtxwxqnmmozmjfcjlr}vyuniquvw|{v|yvtw}|{rqjfm}xqmihouvyr~|tojjpwx}{yypjkmo|{lf\^r|zsquzw~~{~zyut{~xvoqxyvx~}wxqlmor~}vuuzxl\\gvyqsokjv~zyutxtv}{yvxytyw{~z|ztwuwxohaZX`lrdhszysv|xxywx||}xyvos~}ztrvx}}xruy{uttone``bkznidjinz~~wqzztruwy|xx{~umeqzuuwxrkkijuyniecglo}~ywv~wsppqovw{~x{sjlox}~rmi^`lsplimsuxwxyz}xwspruu{zw{{xskgikr~zladhmovzrttux}}ruuruz|~x{~}zx}wvxonppvoswk`X\bvunicj|}y{wrswuz}|zyuqxsv|{}}~y{yx}{xplg`[[^mznmwsqrsxy~}|{ztmt~yzz~ytv|~xplm}vrqnpkgffkshcbkns}yy~~sttttwy~yoqxzw}zy{ysqkho}|ungdluwvpr}ytnouu{}y{zqmmju~}voeX\q~}upr{}~y}vqv~~}yvw|~yt~xwspnppu}umhmvngfmzyuvxsqtyrsrru}}{y|wz}}zx{yyz|{{ypic]_dqxjdkz|{wvzz~ysow|vvw~}vplkigdfjqy{xomlnstu||xwzz~{slo}zrpqtrrpqs{~uniglnu~{tvwopqtvz|try|uw~|z|{xvplinz}}z{vqorwzuo{}|yupotx|yy~yroqrrz}|xoiadqy~ywzz{{yxuw|~|zx|~yy{xtvrqtyy~}pkny}ugbbl}{unlkt}zutyxy{||~|vwyywyzyx{|z~tpnjc_`g|~nkr{}vv|z{zy|ypjp|yxxx|{yuxvomnqljgek{|vjgmknxzuz|xrsyz|~~xqlw{rpvzwrqkluypghlnq|}wuxnmmloy{ys|yqmp{y~vqhacj|woortw}{z|~}}zwnnppu~zwy~|{toljnw~~wnedlqpx{xwtvxxyz|yxvxy{~~xuyw|~{}|wxywrrnr{xqxxha]^g{xphfo{z{zxxvx{~~{yy{vuywvwxz|z}~}{wv{}}xsnd`^[cvvruyvtoqxyz{|{}|rlo|z{}vvwywqnlr|wrtqkgfbesthfhjmuz~|}{~zwtswuw~~|~|vmrz|x|~xz{wsojekt{sigilqw{xwyyposrsvzztnijpz|xpe[Ze|}uoqquz}}{||wyy|zuqwxy}wsz~zwpnkioy{unmnoidfvzurpror}|ywusww}|~|y|{{||ywvtquz{}yla^Z\hz{pgdp|~~zzzxy|~{wtr~|vvtx~yvxw|urqneb`^et}|qkkkq|zux||xy|~yzy{vmjn|ysuuqllghoz|siefjnz{tpyxuttzzz|wz}|vot|~}~{vrledeo}zqllnt|vt}}|}zwuux~}}{~}tqnlnx{tkc\bluwtruy~{vsstz~}~{wz|{{ussstyvoqxvnd``m}wpjlmwzvvtww}~xv|{{z~~||{{{~vpkfa^_fx{njoy{yz}||~~uqv~}{{{{|yupsxqmmjifejvwlinosz}~|{zxxxz}vomv|~wuttrpkkpz|ulimru}}yrpyywtuuy{~}w{{uqov}{xtqibaex~}vstx{}xy|z}~~yyxxz~yv}}ytponpu~ypjdekqps~|ywyxxzywtwx{}~}~|xwvtusw{sqw|wlgbbes}ulioy}zxvw{~zxsrv}yy|~~}}}{yz}}{rolfedbfqyxz|ywru{~~}|~ypoxz|~}{|}wtrnsunomlllhkq~{olkmqx}}{}|uqqtsx}xrlu~|~|tvuturmjlqyqnmpvz~zyx~|tqpmqsw|~}y||vqmosz|{xwpf`]hzvttz|~ywz}z||vstsvx{zx}{trqolnsz{spmnptlgmzxuusot}||yyyy|~}zzzzz~~z|}}}zyvtwussw|{xzwkd_\do~|pilv~}ywx{z{~~}xuomt}zww||~}|~zxuvz~~xqtpkeb_clz}womptx{{y~|tox~|yy||}{yzwpjjvwrturoihhltyqmjinry{zxwrortvx||ywx~~vttx~{z}{xrkheem}{upmlqw{{x{|vqporwz~}zzvpkjnqz}xpd__gu|vstxx|}{|zxvz}zxvvy~~uw~}xvrnmqs{xqrwytk`_hv|snkjn~}zvyxx{|~~{{vx}wzz{}}||wwwxyxohc]Zal}wlmw{xxy}|~{ussy}zxuw{~{ytoov|tsolifbckwtlhjmry}{ywx~~zzyz}}~~}|~soov}zxwuuqlhimx}rkiins{yrs{|xxuvy{~{z~tposz|{vne^[by}vqosw{|zvyzyxzz{~yts}xuqoorzyrlefklkpz{urtsuz~yvtsv|~{z{}{{zxvww{xv|ynf_]bm|}qgekwzyyyz~|xx{~{ww{}{zw{ysoiea_cky{yyyvsqt{~|vu}}}}{{z{|upkm|~wuqoljghku}qkhkpx}yurv|yz{zz}}wqu{}yx{}yvtqmhhir}|tolnt{}urv}zxvy|~~{ysonnt~~xtnf^Zas~yusx|}xuv|}}|~~zx||xsqopruxniimqkfhrxsstqu~}wvuuw~~~{ww||yyvuuwx~|tkfa]bjx~rjku~}yy{{~{uqs}}{~}ywutv|tqlifbaekw~ysrtw|zw}~zwxy{|toiju}urpnmhhims{tqnnsv}~|zzy~vrrqtvy}|vruzzvvv{}xvusmjifhp}{xurrvz{xuy}xsrstv{|~zslkmpuxwqjcably~yxxz|}|}~~}||}wwxxy}}~{~|wtrmmorv}qnosvrjdgn}{vroqw~{xxy|{~{{wvutt{}~|yxvtsutx}yqlgb`bhvvrv{~{|~}zzyz}wsopw}{}~}zxvsqrt}|upnmkfedhrwqprrw|~z{{wsuyz}~~xtpmpz~ysrtsokjhmwztooqsw}{z~ysrppsy|{xx|upps{}xzytmib`ft{tstvy~~{{~{ytsttv|yy~{xtommpx|xvpifimpsz~ywttuwy~}}zz|}~~{yzyz~~|~~zxxxssrsyxsy|tjcacjyxqkkt~}||}~|yvvvx~}zz{{~}}~}zyvwz}|xrnhcabfr~{}}wtsuzztrs}}zz|~}xrnnsyvtqpkihhmz}tmjmpv~}vuz}xyzz|}zvty}~{y}~{yxtpmhfir{xqmoswxy~|yvuwz|~|zyz|usrpv}~zunh`\ds{usuvz|wvyy{zz|}xux}{xutttv~yrmnrslggnyzsonmq{~}zwwz}|||{}~~~{{{zx|xqkd_`eqvkipy}}}}|xvy|ywy{~zvssv|vqokgccfo}wonosz}{{yvv~~~}}}}{volr{yvtsokhhkuzqkjnrz~wrpw~zzz}{w{{wrtz~{ysnic`cm}wrorv{{z||{||{z{}~yxz|upnnow|vnd_^eovyutux}~yvuvz~~zwy~{zurqrsxzrqsuqjcbeq|tnils}yxwxy}~|xux~~{wwuuw{~tmgb^^bl~~uprw|~~~||~~ytu|}~{z|~zvqnlmv}uolidcbdm|voosu{~|{|}}}wvwvy|}~yslimv|wsrokhfektyqmorw||wut{ywtrsvw{~yy~xrmmr{yxtnic^_fv|wsuvz~{y{|xxwuwy{}|~ztmjhipx{uohcagnrv{zxwyyz}~yxx{}}|yzyyyxxzyvspmllryvrvzwmebbgr|wqnr||yxxw{~|xtqos{}|yvtqprvzwqlfaaadn{~z}}zy|~~~}||}||trv}|y|~}~zwrmkkp|zsonjgffgmw{trqtx|}|~{vrrsty~}vqmnv{{|yttqomjhhmtzusruy}{xy~~wtrqrtw{}||xronpu||xvqnga_dr}yxwz}~}z|~~~ywwuvy{~}z}{vroonouzwplkoqplkp|yussrw~}||}}~|{yyxyyz||zxuuuttwz}|}~ztlfcafq~}tor{~}xsqqv~}{}~~|||yvuuty~}tqplhfdemxyussw|~ywv{~}~~}|wqnpw~wstspmkilqz{tqpqt{zwx|xvvw|~~zuv||xvz}|zyvrniedgrzvttw{}~|{xwwwz~ywx~|wursu{}ztngcdkqw~zvuvxz~~|zyz||{||~||~|{{xwvvx{xssvxtnhfgp{zqkkp{~}}{|~|}}|{~|zz}}~}|z{z{~xsmifcdjv}xwwyxx|~~{wsv~|{z{|yuros~zvrplkhgjr~}soopv}~yvuu{~zuru{|}{xuspmhgjqz{sonqv}{vw|~{{{|}~~}|}|wsquz~yupjb]^hzzustw{{xyz{z|}~{wvy}xusqqsy|unjghjklr}zuqrsu|}yxvx|~{z{~|zxvstuyzz|zsjd`ahtxoikt}}{{z{~{ywz~|{}~}yusssw{xrlfb``fp}|{zwvv{}}~~}yy|}|||{{|}wplijt{vsnjfdcgnzyronrx}zwvx|yyxxy{}{vqptvvvy~{wsokfccfo|~xspptz}xvy}{xwxwy{}xqljlpy{upja\[ap}yutv{~}{z}~|{z{zz||}}|y{}vqmjikpxxpkijkkhjs~ytttux{xxxz~~{xvvtty|wtqnmmqw~|vme`^ajv~vopw}|{yz{}~zuqqx}ywsomlou{zsoiea_ahr~{wxz}~~~{|~{xwwy{~|wqkggn{ztqnjgdehnw{vtstx|~{zy|}xssrtvy|}xtsuwustw|{wtpmgedeju}ywvvy}~zyz}zvttuvy{~~wqljmqv|uqkd_bgq{~{yyz{~~~~}~~{yyyxz{{}~}~}xsqmlnrv}zqnpsrojgjq||vrqt|~{{}~|zwussu}}zwvtrstvy~xqkgdcdkuzx{xtrsz~|zvtrpos{xroljiggjp{}wstwz}z{|~{yyz}{wrnot{}xurqpmkjkoy{wstwz}zz~~zwuvx{~|xvy~~{vuuy|xtrnhc`cp}ywy{}}~}{zyxy{~~{y|}ywuttux~~wplihloprwywvsty~|{|~~~~}|}}~~~|zxyyxxyz}zvx{{vojfgkt}tnov}}zwuuz|{|}~~}~~}{zyxyz|~xtpliffjq{|xvx|}zxttx~}~|wsopv|xuspnljknu{uqqty~zuty|{|~~zwvv{~~|{|~|yvsokhggnx}xtsuy|}~|zyxy|}zxy{|wtttw|{vqjd`aiu~xuvwz~zxyy{{{|zxx~~{xusttv|}upnoqpkhjr|ysomov|zy{|~~~~{z}~}{xwvvvy~|unhcadkw}toqw|}}}~|zywx~|{|~{xuqoory{vplgcbdit~wrsuy}{yyxx|~}||}||~}xrmjmu}|wsplgedgozwqoptx}yvuy}zyyz{{}|xxzzwsqqw}|xtohc`_dn~ztrruz~}~~|{zzz{}|~~}|}{smjjlrzytld__cmvzvutw{}zyz{~~~}|||zzyxx{{wrmkjlqy|ussrpidcgp~}vqnow~{zyy{}}ywtsu{~zvqommptzxohb^^bjxzvvy}~}|}~{xvy~~||||~zuplihkr~zsnjeaabhs~wtux{~}|{{}{wvvwy{}zrmijov{}wrokfdcelvztrtw{~zwxz{yvttwwz}~{{|ysomnt|}wtoic_^bm}~zvwx{}~|zywwyz{}~xsnkjkpxyrlfddhlqw{yxwxz|}{z|~~}zyxxwwx{|xtqnmmqw~ywxwrlecejs|vsrw~}||~}ytrsw~|yvsqppsvzyrmhdbdgny}||~|yy{~}}~zvrnklqzysoligghkr|zvuwz}{y|{ywxz||wrpruy{{}zurolihginv|ywxz~|z~~{yxxy|~}|}{vsstv{zupkfcbeo{{yz{~~}~}|{{|~~}}yvsrsuwzxqlkmppmmow}xtsuz~}~}{zzzy{~|zyxvwxyz}}xsnkhgioyyuv{|yusuz~}|zyxvutv{ytonkihimu~yxxz~~{zxwy~~zwtqptz{vsrpnlkkov~{vuvx}{xz~{yyz}~{xuv{~~|zxz~|yvtplhdditzxxy|~|{zyyy{~}yx{~{wuttv{zvpkfegmsy~zwvvxz~~|{{||{||}~}~|zywvuuwz~|vtuwvqliiltwqmpw~|}~~}|{zzyz|~~~~}}{ywvvvx{}}vqkgedgp{}{{{{{|~}}|zwuuz~}}}|ytpnmpx~yuqlhgegmw{trsu{zwww{}~}}~~}wsoptxz||xuplhedhnwxsqrvz|zz}{zyz{|}~~~ytooqv||xrmf`]_hw{wttvz~}|||{z{{{|}~}}{y{}wqnkkotz|vojgfikns|{vsrtv{|zzz|}~~~~}|{zywwz{vsomlorx}||{tkdabhr~ysnov~~{{{|~|zwuv{{wsollmpv{|ungb__dmx~|zz|~}}}}}|{z|}{{z{|}~ytmhfgnzzupjfbacir~ytrsw|}{xwx}|yxwwxz{}|wrppsttvz~yupjfbachqyussw||yz~~|yxxxxz{}|uokjmryyrke_\_gu~zwvwz~~||}~}{{zzzzzzzy}~wqmjilpwwplkkkjiks~yursvz|zzz|~{yvutv{~yuqnmmptz|umfbacju~xssy~}}}~~zwuv{{wsomlmpwwqlgcbbgpz{yz|~|||||}~|{z|}~}wrlijowzuqlifefkr{{wvvy}}yvw|{yyzz}zvvxxwutvz{vqmhebben|{yxz}~}|z{{}~~~{urpprw|xqjeabgnu|}{yyz|}}}}|||||||}{xurrqtw{}vrqrqnkikox~xsru|~~}zxvvx~}{ywutuvxz~yrnigfgks}}}~~}zwvx}~{yvsqpps{}wrnkiiikpx{xxz~}zxy|}||}~ytqopv{~{vspmlkiknu}{xxz}~}}|zyyz}~{yyz}}zywvw{{vrnjfcchs|zz|~~~~|{zzz{}~}~|xutttvy}{tnjjjmopsy~yvvuw}}}~~}|{z{{|}}~|zxwvuuwx{}zyyywrmihjpy{usv{~|ywutw}~}{zywutttwz~{wsnjhfhmu|zy|}{ywux|}zxtpnnszzwtpmjhhkpyzvtvx}}zy{}zz{}~~zwuuvxxxxz}~{xtplgeegny{wtvx||zxxyz|~~~~ytppqsx~zupjdabhszvvwy|~~|||{{zz{||~~~}{{}~yuqonoqv|~xtqponkilr{{urqsx|{|}}}|||{zyyy{~{wtqonpsx|~woidbdjtyuux}~}}}~}|yxww{~~{wsolklpvytnidbbgoz}xvwz~}|zyzz|}||{|}}}wqmjkpw}{vrmhdcdir|~xtstx||yyz~}{zyz{{|}~|yyywspprw}{vqkea_aht{wtuw{~~}|{{z{|||}}|}}ysmkknsy~wqjdabgnv{wuuwz~|zz|}~~}||{zyxxz~|wsollmqw}}xvurnieeiq|}vrprx~|{{|~}zwvwy~{xspnmnrv{zqjeaacju}zy{|~}|||~~}{zz}~~~|wrmjikqz~xrlhdbcgnyzvux|~{xvwz~|{||}{vpmnqux{}xrmieddgmwzvuv{{xz}~}|||}~~~~|wsqqsx~}vpjd`^_guzxwz}}}~~~~}~~~~~||}~xtpooqu|~unhffhjmrzzwvvx|}{{}~|{yxy{}yvsqqsuy|zwsmifeipz~xssw~}}~~{xvw{|yvsrqqsvz~vpkgeegmu~}~~}||{zy|~vxtuttqxzz~|tmmdaafl{|tpy{xvms}}|{}{wzx{z{y{vged]]_en~toz}|{~~~~xmtyz~yyy{zxx{{}~~{skad`\\`fsso~|yxz}{}|ymv}|v{|yxxyy}uy{|z|mg`d_]\bixrpwww|~|{||znzyzw}|yvwvyzu{{ysjdae_\]dl}pstww}{{{~~vozzw~{xvwuyxtz{|{nicde^]_fq|mw}sxx|z{{~sr}zxyzxvvtyuuz{x}kicec^]ajvvl|xuxy|{|{~~rwzzw{~zxvutxsvy{wxjibeb^^em{qnvwy|}{|{~}r{xzw|~zwvttxrvy{~wtkgbe`^`hqnpuyy~}{{{|zrxyw~}ywvtvwqwx{~|xpjebd_^akuzjtu{z|{z{{ws}wxx}zwvtwuqvw{~yzmjcbc^_dnyujy}w|||{y|~zuuywwz|zwvtwsrvw|~w~|khaba]_gq~pk}{y}~~{zx|{zrywxv{{ywvtwqruw{~wxie_b_]aku{lnz|~}zyx|z||r}vxv}{yxvuvpsuw|xshc^a]^cnyxjq{|yxw|y}zrvww~{yxuvuotux|}zng`]_\_eq|uiu|~{ywx{xws|vwx}|yxuwspuuz}{{kf^]_\`hu}rkz~zxvy{yvuywwy}{yxuwrquv{~y}zid\^]\akw|ol~~yxvzyztwxxv{|zxwuxrsvx|yvgbZ^\]cnzzmn}xwvzy|szwxu|~{yxvvxruwz}xrf_Z^[^eq~xlq{xwv{x~}s~xwv~}{yxvxwrvw{~xnd][]\`hv~ulv~ywvw{yytywx~|zxwuxurwx|{x}jb[[]]blz}sm{}xwuy{zwu}ywz~|zwwuxsswy~yyxf`Y\]_eq~|rp{wvuzz|uw|{x|~{ywutvqtwzvzrd^Y^^ajv|rs~yvvv{{~t{||y~~zwvsstpux|t{~mb\Z_`eo{{qw|xvvw}||t|{y~}yvtqsspvy~zr{zh`[[`biszq|zwvvy~}yu}{{{wsqorpqw{tq|ud_Z]cfmxxs}yvvw|~wx}{|zuqonqorx}pr{pb^[aeir|vu~|xvwx}v|~z}}wrommpntzymt{la^\cgmv~tx}}zwwxy}v~{}{tpmkmonv|skvxh`^_gkr{|t}{}~yxxx{~|x~}z}xrnljmnox}~nlxue`^bjnvzt~}{~}zxxy|~zz~|z|vpljimnqyykozrc`_elrywu~}{}zyyy}~}y~}z{{toljjnns{ujr{na``hou|}uw~|}}zyxy}||x~}z{ysnkjknnu|qjvzia`akqw~{s{~|~|yxxy}{}~x||y{wqmkjknnv}}nlxwf`_cmry~ys}{xwwx|z}{y|{y{vqmkilnox~ymnztc`_ent|}wt}~zwwvy{y}yz|zyzupmkimmqywkpzpa__gpw~~|vw}|xvuuyzz~w|~{yyytpljjmms{tjryk`__irx~|uz~{wutuzy{~v~}{xzxsoljknnu|qjtwh_^`lsz~{u~~~yutsuzy||u|{xzxrnljlnow~}ojuue^]bmt|~{v~~}wtrsvzz~zu{zxzvqnljnoqy{lkvrb]]dnv~zx~~{urrrvz{xv{yyzuqnlkops{yjlwo`]]fowzz~~ytqqrxz|vx~{xyytpnlmppv}uhnwk_]^hqyz~~}wrppsy{~~t{~{yzxspmlmpqxqgouh^]`kt}z}{tqpqtz||t~}{yzwromlnqrz}mgpse^]cnv~|~|ysppqv|}yt~{yzuqnlkoqt|yjfqob^^gr{~~|{vroprx}wv~{yysolkkoqvufgql`_akvzzuqpqt{~vy~zywrnkjkpry~pchqi``dpz}yxsqpsw}~v}~yxuokiikqs|zjaipf`bivzxwsqruy|w}xwrlihhlquue`kndbfo{~xxvrrtw{{z{vuojgfhnsy{paammdeju|wxustvx}~{~yurlhefipu}vj_dolfio{zwxvuvxz}}}{}uroieefjrw{re^gqkhmu}xxyxwxyz}{|~|yrplgddgmuzwnb_krkkqz}|xz{xxyyz{yz|~~}vqojfdfipw}|tk`borlnu~|~{y{{yyyxyywz|~ztomieegksyzrh_errmqx~|~|z|{yxxwxwvy|~xqnmhffimuz~wpf`itqnt{}~~|{}{yxwvwvvz|}|vpnlhggjow{}voealuppv}|~|{|zxwvuwvw{~}ztomkhghkqx}{tlccpuprx|~{{{ywvuuvux|~}ysnnkhhilsy~zskaerupsy}~{{zwvutuvvz~|~xrookhhjnuzyriagstpu{}zzxvuttvww|{~wqonkiikov{xpf`itrpv}|yyvttttwwyz|vqpnjiilqw|~wod`ktqqw~zxxtssstxy|}y{vqpnkikmry}}vnbamtprxywurrrsvyz{x{vqqnkjlntz|ulabnspsy}wvsqqrsw{|yyzuqqmkjlov{{si_cpspu{|vtrpprty|wzztrpljklpw|xpf^eqsrw~ytsqpqsv{~}w}ysroljkmry}~vnc]gsssy~wsrooqtx}{w~xrqnkikmtz|tk_]jtsv|{trqopsuz~{x}vqpliikov|yqg]^muuy~ysrqpqtw|z{{tpnjhikpx~}vnd[apww}~~}vqqqqsvy}z~xqnkhghlr{zrj`Zeuy{~~~ztqrrsuwy}~{~uokiffinu~|wnf][jy|~~~xsrssuvxz}~}{rkigefjpy~yrjc[_q~~|}~}wstuuvwxy||vnhgedgmt}zuog`[dx||~~{vtvwwwwwxzz}|qjfedeipx}wrke_^l~{|}zwvyxwwvuwwx}~vmfdddgls{ztpid_bt~}{~|~zxxzywvutuvx}~rjddefjpv~}vrnhdah{~}||z{xyzxvtsrtuy}}{ngdefhmsyztqlgdcm~}|}~z~{yzzwusqrtuz~~}vlfdegjou{}xsplhees~~|~}{~{yzyvsqprtv|||sjedgilqw}{wrpkhehy~~|~|}~zyzwtrpprtx}z}~qiefhjnsy~~zuqokgek~~}||}zyyvsqppsvz~y~{oifhjlpuz}ytqojgfn~}}||xxwtqppquw}{xynigikmqv{|xtrnjggr~|}~zwwurqppswzyxwnjhjlnrw||xtrnjghu~|}~yvvtrqqruy}xyvnjjkmotx|~|xtqmjgix~|}|wutsqqrsx{}vzuokklmpuy}|wtqmifj{}|~zussrqqsvz~{v{uollmnquz~{wtqlifl~||~xtssqqrtx}yv}~uolmnorv{~zvsokhfo~{{}~wrrrqrtv{xv}tommnosw|~yurnjgfr}z{~{uqrqqsux}~wx{tomnnptx}}xtqmifhv}{y{~xsqrrrtwz}wzyrnmmnpuy~{vrpkhfj{~|yx{~vrqrrsvy||w|wqmmmnqv{~ytqnjhfn|zxx|{uqrssuwz}{x~~uolllosx}|wrolihhs}yxwy~~ytrstuvxz~{yzrljklptz~ztomjhhlzzwwwz|wtstuvwy{~yzvojikmqw||wqmkiijp|xvvw|}{}xuuvwwxyz}y{|slhilotz}xsnkjijmw}yvuvyz{|xvwxwwwxy|}|w{~xnigimrw}~ytokiijmrzvtvw|}x||yxxxwvvvxz|yvz{rjggkpu{~zuqmiikmrx|xutwyzx}zxyxvtttvy{~vtyxohfhnsy~~{vrnkikmpv~zwuux|yy}{yywtsstvy{{ssxulggkqv{|xtpljjlptz}yvuwz~|x{}zyxusrrsvy}wpswrjghnty~|yuqnkjkosx}{xvvy|{x~}zxvsqqqswz~}sosvpihjqv{~zwspmkkmrv{zxvwz}zy|xwurppqtx|ypnsunijmsx}|xuqnlklosx}}zxwx{~~z{{wusppprvz}vnnttnkkpuz~}yvspnllnrvz~|zxwy|~z}~yvtqppqsw||sloutnlmrw{~~|xuspnmmosw{|yxwy|~z~|xusqppruz~zqlpvtomotx|~}{wtrpnnnquy}|yxxz}~{{vsrppqtw|xolrwtonpuy|~~|zvtrpooorvz}|yxx{}}zytsrpqsvz~vnmtyupprvz|~~}|yvtrqpopsw{~{ywx{~|z}wssqqrux}}umnvzuqqswz|~~}|yvusqppqtx{~~{xwy|{z{usrqrtw{{smpy{vrrtxz|}}|{xvusrqpruy|~}zxwy|z{yusrrsvy}yrmr{{vsruxz|}}|{xwusrqqsvy||ywwy|y{}wtrssux{xqnu|{vssvxz|}||zxwusrqqswz}{xvwz}}w{{vsrstvz}~wpow}zussvxz||||zxwusrqqtx{~~zwuvz~{w|zusstux{~}vpqy}yustvxz||||zxwtsqprux||xvuvzyv}~xtstuwz}|upr{}xtrtwy{|}||zxvtrpprvz~zvttw{~vv~|wttuvx{}{tot{{vsrtwy{}}|{ywurpopsw|}wtstx}{tv{wtuvwy{~yrou{ytqruxz|}}|zwusponptz~zurrtywsx{wuvwxz{}~wpnv{wrqsvz|~~}|yvspnmmpv|}wsqrv{|us{{xvwxxy{|~{tmovxtqruy|~~|zvspmllnsyytqqsx~ysu~{xwwwwxy{}xpknvvsqsw{~}zwspmkjlpv~{vrqru{}wsx{xvvvvwxz|~|tlinuurruz~~|xtpmjijmsz}ytqrtx~{vu|zwvvuuvwz}ypihoutrtx}}yvrmjhhkov~{wsrsv{~yvw~yvuuttuwz}}ulfhqvttv{{wsnjhgimsz~yussux}}yvz}xutsssuw{zqieirvuvy~}ytpkhggjov|xustw{}yx}{wtsrstvy}~vngdktwvx|{wrnigghlrzzwutvy||yyzusrrstwz~{skeenwyy{~}ytokhfginu}~ywuuwz~|z{~xtrrrsux|xpidgqyzz||wrnjgghkqx}ywvvx||z{|wsrrrtwz~}vnhdit{{|~~zupliggimt{|ywvwy}|y|{urrrsux|{tmgekw|}}|xsokhghkpv~|ywwx{~|y}ytsssuwz~yslggny~~~{vrnkihilrx{ywwx{~{y}~xtstuvy|~xrlhhr|~}zuqmkiiknsz{ywwy|~yx}}xttuvx{~}xrlhku~|xtpmkjjlpu|{ywwy|}xw}{wuuvxz}|wqlimx~{wsomkjkmqw}{xwxz}{ww~{wuvxz|~zvqlkpz}yvromkkmosy~{xwxz~zvw~~zwvwy{~}zuqmls}~{xtqomllnpuz}zwwx{~}xuw~}ywwy{}}yuqmmv~}zwspnmlmnqv||ywvx{|vtx|ywxz|~|yupmox~~|yvspomlmorx~{xvvx|zusy{yxz|}~|ytpmqz~~}{xurpnmlnpty~yvuvy}}xssz{yy{}~~{xsomr|~}|zwtronllmpu{{wutvy~{vrt|~{yz|}~}{vrnmt~~}}|yvtqnmklmqv}yuttv{~ytrv~~|{{|}~}ytplnv~~}{yvspmkjkmrx|vtstx||wssy~|z{||}~~~{wrmknx~}{xuqnkihjnt|xtssvz~~zvsu|~{zz{{||}|xtokjp{~{wsokiggjow|vsstx|}yutw}zyyzzz|}zvqlhir~}zuqlifegkrzytstvz~|yvuz|yxyyyz|~}xsnigku|xsnjfddhmu}vssux|~|yvw}zxwxxyz}zupkhgmy{vqlgdcdipyzustwz~|ywx}ywvwwy{~}wrnighp}~ytniecceks}xttvy|~|ywz{wvvvwy|zuplhgjt|wqlfcbcgnw~wuux{~~|yx{zwuvwx{~}wrnjgglxztnidbbdiq{|wuvy|~|yx}~yvuvwy|{vqmigho|}xrlgcabelu{vvx{~~{yx~}xuuvxz}~ytplihjr{vojebacgoxzwwy|~{xy~{wvvwy||wsoljilvztmhdbbdjr{zwxz}~zwy{xvwy{}{wromkkny}xrlgcbcflu~zxy{~}ywy{xwxz|{vrpnllq{|vpkfccdhow~zxy||xvy{yyz{}~zvsqonns~ztnifcdfjqz~zyz}{wvz{yz{}|yurqoopv~xrmhedehmt}~zz{~}zvv{|{|}~{xusrppry|vqkhedfjow}zz||xuu{||}~}zwusrrqt{ztojgeehlrz}z{}{wtu|}}~|ywutssru~}xrmigfgint||z{}}zusv}~~}{xvuutstw{vqligfhkpw{z{}|xtsw~|zxvvuutuyytokhggilry~zz{}~zvssy}{ywvvuutu{}wrnkhggjnt{|zz{~|xurt{~|zywvvuttv}{vqmjhghjov~{yz|}zwsrv~~}{yxvuutttwyuqmigghkqx}yxz|~|yussx~|zywuttssty~xtpkhffhlrz{xx{~}{xust{~}{ywutssrsu{|wrnjgeeinu~~zxy|~|zwutw~~~~~~}{xvtrrrrsv~{vqlhedeipx|yy{~~}|zwuuy~}}~~~|ywtrqqqrtxztojfdcfkr{{yz|~}|zwvv{}|}~}{xurqppqru{~xsmhdbcgmu~zz{~~~}|zxvw}~||}~}zvsqoopqsw~}vpkfcbchpy~~}{{}~~~}|zxwx~}||}~{xurpnopruy{tnidabdks|}}}|}~~~~~}zxwy||}~}zvspnnoqsv{yrlfbabfmv~{}}}~~~~~}zxwz~||}|yuronnprux~~vpida`bhpy|z}~~~~}zww{~||~{xtqonoqtvz|tmgc``djs}{z~~~~}zww|~|}~zvspoopsux|zrkfa`aemv~yz~~~|ywx}~}~}zvsqpprtwz}xpida`bgoy|xz~|ywx~~~|yvsqpqsvx{~~vnhcaadir|{xz|ywy{xusrqsuxz||slfbabeluyx{~{xvy}zwtrrrtwy{}yqjebachoy~xx}}zwvz|yvtrrtvy{}~woidbbejr||xy~|yvv|}zwusssux{|~|tlgcbcgmv{xz~{wuv}~{xvtsstwz|~yqjfccejqyzx||yvtx|ywusstvy|~vniecdglt|zy~}{wuty}zwutstuxz}|slhedeiow~yz~|yvtu{}{xvtsstvy|~~yqkgeegkrz}y|~|zwutv~~|ywtsstuwz}~~}~wojgefint}{y}~|{xvtty|zwussstvx{}~~}}|tmifegjpwzz~~}{ywutu|}{xvtrrstvx{}~}|}zrlhffhlrz}yz~}{zxvuuw~|ywusrrrtvx{}}||}xqkgffint|{x{~}|zyxvuvz}{xvtrrrrtvy{}}||~~vojgefjow~yx{~}}|{zyxwvw|}zxvtrqqrtvy{}}|||tmheegkqy|ww|~}}||{zyxwwx}~|zxusqpqrtvy|}}}}zrkgeegmt|yvw}~}||||{zzyxwy~|zwurqpprtwz|}}}~xpiedehov}wux~}||{||{{zyxxz~|zwtqpopqtwz}~~~~vngdcejqyzuuy}|{{|||||{yxx{~|yvsqooprux{~~|skfccfls|~xtu{}|{{{||}}|{zxy|~|yuspooprvy}yqidbcgnv|vsu|~|{z{{|}~~}|zxy}~{xurooopsw{~wngcbdipyzusv~~|zz{|}~~~~|zyy}~{xtqoooqux|}ulfcbeks|ytsw}{zz{|~~}zyz~~{wtqpopsvz~{skebcfmu}wssy|{zz{}~}{yz}zwtqpprtx|ypidbchox|vst{~|zzz|}}{y{|yvsqpqrvz~~}vngccejr{zusv}}{zz{}~~|zy|{xurppqtw{~~~ztlfccgmu~yutw}{zz|}~|yy}~zwtqppruy}}||xqjeceipy}xuuz~|{z{}~}{yy~|xurppqsw{~|{zunhddfls}|xuw}}|{{|~~|zyz~zvsqopruy~}zyxrlgdeiow{xvy}|||}~~}{yy{|xtqpopsw{{xxupjfegls{~{xx{}||}~~}{zyy}}yurpnoquy~}xvvsniffiov~{yy}~}}}~~~}|{zyy{{wspnnosw{zvuuqmhghlrz~{yz~~~~~~~~}|{zyyy||xtqnnnqty}|wtttplihjov}}{z{~~~~~}|{zzyyy{~zuqnmmorvzzustsokijmry}zz}~~}}|{zzyyyyz}{wsommmpsx||wsstrokjkou||zz}~}}|{zyyyyzz{~}yuqnmmnquy}zusstrnkkmqw~~{yz~~}|{zzyxxyz{{}|wspnmmorvz~}xtsttrnkknsy|zyz~}|{zyyxxyz{{|~~zvrommnpsw{~|wtsttqmklpu{~{yx{}|{zyyxxyz{||}~}yuqommnpsw{zvssutpmkmqv}}zxx|~|{zyyxyyz{|}}}|xtqommnqtx|~zustusolknrx~{xwy~}{zyxxyyz{|}}}}{wtqommoqux|}yusttrnkkntz|ywwy}{zyxxyyz{|}}}}}~{wsqnmnoruy}}xtsttqmkkou|{xww{~|zyxxxyz{|}~~~}}~zwspnmnorvz~|xtrssoljlpw~}zwwx|}zyxxxyz{|~~~}}}zvspnmnpsw{|wsrrqnkjmry|ywwy~|yxwwxy{|~~}}}zvsponoqtx}{vrqrpmjjms{~zxwwz~zxwwwyz|~~|}}zvspooprvz~zuqpqoljkou}}zxwx|}ywvwxy|~~|}}yvspoopsw{yspopnkjkpw|yxxy~{xvvwxz}}|}|yurpooqtx}}wqooomkjmsz~zxxx{~zwvvxy|~|{}|xtqoopruz~|upmnnljkou}}zyyz}}zvuxyy|}}|}yvuqmnqsu{xsolkmljkrzzxz||~{vuxzz{~}{y~{xwsonoorx|wsmjlkiiox~{y{~}~|wuy{z{}}}~{x{|zzvqnmmnu{xpjjjihmu{}z|~~~xvz|z{{{{}~{yz|}xrnkklr|}tlhghhmtx}|~~~~{y|}zzzzz|~|z{zsnkjjpy~xohefhnsuy~~}~~{}~{zyzy{~}|{||tokiinw{~|sjedgnstw{~|}~~|zxyy{~|{|{}~vokihmtx{~vlgdemssuw~}|}}}zxyy{~|yy{{~xplihlruwzyojedlqssu{}|~}~zyzy{~}zwxzzzqmjhlortv}{snhejorqsy~}|~}zzzy{}|yuwyz~~~tplilnpprz~~vrlhilppqw{~|~}{{{z|}|xtvxy~~xsoklmnmov{yvpkjjlmpvy}~}~|||z||{xsuwx}~|xrnnmmklsx~|ztpmjjintw}~}|~}}{}||xsuvx|}|vqpnmjjptz~}xtqlifjquz~|~~|}}|xsuvw{~}{uspnjhnqw~{xupkefnrx}~{}}~}|ytuvvz}~|zwspkhlosz~{ytnfdint{||~~~|ztuvvz|~~|~{wsmhkmpw|~}xsjdfipx|||}zuuvvy|~~|~{vpjjlmty}~}wofefjsy}~|{}{vuwux|}~|}ztmklkqvz|}{tkgeenuy|}}{~{xuvuw|}~}}yrnmkntwz{~zqkgchptxz}{|}yuuuu{}}~~}wqommsuwz|~wpkedkotx{~|{~~{wuuty}}~~~|uqnmqttxy}|uohdhkoux}~|z|}}yuutx}|}~~~~ztqnqsruwz~yslfghjptz}z{|{vutv|{|~}~~}xtpqsqsuw|~~wqjigfkpv{{z{~~xvtu{{{}}~||wrrsprsuy|{unlidgkqx}|zz|zwuuzzz|{}~}zustppqrvz~yspledfltz}zz{}yvvzyy{z|}~~xvuqppptw~}vtpgdcfnv|zz|xwzyyzy{{{xwqonnqt|zxskfbciq}}{zzy{yyyxzz}~|yrpmmorz}{wnibaely~{z}{}zyxwyy||tpllmow}zrkdbbgt}|{~~{zxwxw{~~~~wqkklnuz~|unfdaeox}}~}{wxxw{|||{~zrlkjlsw|~~ypifbdks|~~}~||xyxwzzzyx|}~tmlilqty{|smiddhmw}~}{~~}yyx|{xokdh~}s~{~{vf}zy{}|w|r~|nl}zzs}qe~}ffgfmu|z|idzy||vn{~vjosot{zxut|kq}|{syflvrijfnyz{~o^m~zkx}~}pystov}|uq}~}ne|}wprb{r~|mliszyyyytb^z~{wwl}~}xqwtrzzuwtxtei}rnmjzsyqmpvvu~}rj\fvusv~|}ztu~yvxyur|z{rkbrmnnv~vxvsrtutuxmc\qrvv~|{|vs|{|{ywsszunfc}kqq|~xwswustsxrj_`pzx{z}~rvzyvuqw}{pkbkykvr}}tzuzrrsr|lf\l|s|x{z{xq{}vvsq|x~~lhcy}vouu|}yuywqsqr{}ic`y~{uz|z{~ss~{vvqrvyifi|vpt{{ww~xtrrovzvfch}zt{~z~{rv|yvspxu~tggq}tox|}~t{|vtrop}xrfepvu|}~yo{~{ytqrs|phjxor}|{rxwsoosu~oghyqy|}tn{|uqqv{q|mil~lw~}tswxnnpyzu}lhk~q}~zmt{zpqr}su{kjqymzzp{wslpruy{jhs{q~~sm}ztpruqxxjkzvm|~trtompwt|xhk{vpwpqurpsy~q|uhp~nnxtvxqons{trgpmu~ytqwwurqu}|s~ojt|gw~zwt}rsopt|w}lirzj~}zwtruvqruyw{lkv~si~~{zvwssppvzxzihw~sm|zxwtwuvqpywxygk~~{on{z{ywz|stnp{wzucm}}ou~wxxwvz|wvnr|t{qcs{zky{x{zzv~yvqmr}uzkct}zowwxyvt{xqnq~}uwig{{vnwxz|vwytolq~ttfh~}xr}vvzwrxyuojr{rsen{up}vz|vt{ttkivxqqcs~tu{uzwss{vtijwtrogz{pxzy{vss}tphl|sqmizzq~zywtpr}uohl~qqom~~vp|zvtouzrmgp~ornm|wr|vtpmxxrlgt|nunpxuq{vton{tqjizynujryutwtplo~tqil|vqvkwwrvwuomqroipuqsjy~wsz}urmkrrnkqvrrm}{ts||vqnlv|onlsuppl~zuuzrnlk{{oomtspqovtuzrokm~woplxppopuvw~vpmipuqolzmqot~tvx~upmju~sqnm~}lqmw}uw||rokkxtrlozkqo|zsw{rokn{~tqkrwkpp~zt{~xqmkp}toktujqsws}}wqmmr~rmlvsiqtwvzvolnsqmmx~qisw~uwyuonpt}~nmn{~oity~u{xsmopv}|mmn}}~ljw}|t~wsnqqy|ymmp}|ilx|v}vqnpp}}wmmq}zhn{zw{wqpqr}tmlt~wfq}zyzvppot~tmlw~|tft~zzyvqqpx}rll{~{rgv~{|xtpopz}qjn~}xphz}z|xuqor||~oiq{woi~~zvtons}}~ljtxvnk~yvuopu~}{ilvvwmn~yvsmpt|xhnx|uwkr|z~wrnqu{~sho|zvvkv{||vpnov{}qjqxvulz{}|upnnyyzojt~xvum|~|~ztoln}xyokw|xvum}{~xsnjpuymmz|xwtm~{vskiruylp||wxro}yvrhjtyuwls~|xyps{yungkvwwvmu{yxowx{tlglzuwunv|yvoy|w~}pigm~uxunx~~xvo{yxynigpsztn{}wum~~}xytmggrt|qp}{wsl~zwzrmfiu}v{os}zwpn~~|yw}pkfjv|xynt~zunr~|zwv~okhjy{wxntxrnt}{zvy{mkgj}zwwlw~urny{zxu|xmlflvwulz|srmzzww~unkeouxrn|yrqo}{yvxtpheqtxpq~}wrpr|{xwytnffssvos{wqrv|zwwxtlfgv}qvou}zupsy|yxwyrkegzzqvox~{ztqu{{zxuzojdi~wpvo}}{yrsv}z{xu|~miblsquq{{xrvw{{uu}zlgbpqrtt}{{vsvw~|ztvwkddspstx{{yvuwx~}xtvuicfv}osv{z{xwuwz}wtvsgchz{ntx|~zzxxtx|{uswpfcj~zmvy~}zyyxtz}zusy~meclxmxz|zyzwt{}yts{~~kedpun{z|yzzuu||xts~~{ieduso|z{y{ztw}zxrt~xhegzrq|{zy{xty~zxrv~~ugei~~rs|}yz{wtz~zvrx}}sfem}qt}~y{{vu{}zurz|{qffp|qt~~y{yvv}|yts|zzpfht|pu}z{xvv~{xtu~xxofkw{pv}zzwvv{vtvvxngmz{oy}{ywvvyutxtwlhp}zn{}zxwux~wtuytwkjqxn~}zxwtz}utu|{tvjlsvp}xxus}{tuv~xtukmutq|xxttxtt~y{yrw{snxu|z}{rfbzsz{vzxt~omomrxp|_ruqptyzqw~zh~zqqpxuwq`xomtx{pzwjg}xrrzyl~gqsmlr|ytxytcy{vtyolsfpmnwyqswghxyvxgz}omwrms{suwvm_x~}stqm~xnsppzwrvyq_cqsg{ytnzsluysxwrj]rxqpo|wnzxtsvqrtl^crqo}}sp~utwuqsnf\m|mrt~xr|wwvtsq}~vjb_slty~~~otwztror~mi]llvt~~}vqyz~wqplz|tic_snyw|{}~pu|tqlp~mi\mmws~{ytp{wpnnw{vkdc~wqxt}z{msslkoy~ofcntvvzx|rk{{omls}zzhffy|qyt{zyjvslnlwqggjurvw{~~}nl|~ymnjt|y|jkgvqvr~~~~vju~}qmkkxrijkwqtx}~~zooz}xqmhs{}mkgrqpt~|zsotytojm||xlkj{zlry}|spnz{xskjsx}rkjorlw}|usqpxzokm{w{kkhx|it{|vqsn{y{vllqw~uihjqjy~~yrropw{pnmzv~mjev|js{|trqmy|{vmlpx|xhejrky~vssoqzzrnlvx~pedvymp}yssopyywpmp~~z{hbl~uku~tuqor|xunlsy}safvyrl}wutorwxyrlmxx|fan|ykr|tvpps~xyoln|{u_fs{uh}vvsnrwwzsmkvw{ham||mo|vvpprxzojlwu`fr~xi|wvrprvz{vlhuyxk`j{|om~wtpqr}y{pikuudcpykxzvrrqt~zvkft{tpah{}smxssrqz{zpfjuti`oyot|tsuqsyvkfsrscd{}vm~xsuspx~wqfj~wro^oztp~stwqryukeqptea|~zqxxrwtpvurfiz{pq^m|ynsvwpq}ztlforsia{}ssytxsottrfhw}nram|m~twvpp{|smemuolcxupzvwrosuqhguopgk|o{~xxuqoyrmejxmoguwp{xvrprypietqmmj~{rw}{wsqnxsnei|kqjqwq~ysrnp|okdtslrh|zus~usplwsnfi~lrmnxr}yrrlooketvmshzzwrtrnktumgk~pqnlxszyrpkm~okgsznrjw{vs~uqljryljkzunolvuxzrnjkqlkppolsztu}vnkhr|lnkwzkpl~uvw{qlhisnmmplnpztv}vmjfs~opiu|iol{uux{pkfjwqml}qinpzsv}vnhgrtpjr|iknyut|ysjfk{~qmkyrenp{rz{xngho~wokn}hiov~tvztihixpnixsdnoys}|znigmxnjo{hhow|ux{ulgguqljwrdlq~xv|yrjek|{mkp}ylepyzxx{wnffprkmu|ueiu|yx{yujdgy}kmo|yocqyzzwzzpfclrkot|wfhw{zxz{wjcdw}lnp}zpcqy~{yw}}{qf`ktlou}wihw}zxx~|xkacu}omq}{ren{{ywy{se_k~ylowwmfv~~yxu~|xk_cs|~rls|{tgl{{xuwztc`i|{|lowwpet}yws~}xk_dpzukt{{vjiz~{wswzscah{z~npwysfp|yts~wkaco|}xmt{|wmfw|vrwzreafzz~row{uhk|yrs}ulban{znr{ypes}trw{ph`ey{|vnv~}vjg|yqt{tnd`my|qpzzreo~srw}pj`dx}yznt|vld{xquztod_mw|unx{tfk~tsvpl_dvw}oq{wocxzstyvof_ky{xlv~|tii~vsuqlactw~qnzxqft{usxzng`j}|zzls~}tmh~xttskcdqy|tlxyrip{vsynhchzx{np}~unh|}yttwjfdn|yvlvzqlm{wrzoiefwxyqn}~uojw|ztuzkidkvxlt{qnl~{xs{qkgduywtl{~uplt{zuv}mkcitxnr~{qom~zxu{ulhcr{tuly}upnt{yvw~pkch|tvpp~zrop~zxwzzngco}rumw~|vopv{yxx|ujdfxvsro}yros~yyxxngckqtov~zwosw|yzw{~yiecuxoto{yspw{y{xvogbhprqv|zwpuw|{zuz{idasyntq~zytrwy{|vu~qf`g~qptxzywsuw}xsw|ib`p{mtu|yxvtvy{ts|td`e|snxyzywvsw~~wqt~jaan~mty}{yxxsuyyspzuc`d|um{y}zxyvrwz}unr~~ja`nnu{|{xyyrswyomx~vc`e|xo{y}yyyuptz~skq||l`bpst|{{yzvqpvwmmvzwd`iz|qyz}~z{xsnrz~pko~{zo`epvr|x||yuomw~ulktxyebjxqwz{~}{vqlrz}pkn~zzqagoypzx~}xsmmw~vmjuwyhcjvrsz{zunlqz~qkn}|xtdgo{{nwz}wplmvxmktxxmcktto{{zrllo|rkozvwffox}mu}~}vmllvznmsxxqcjqtl||zpllo}tmoxuyhelv~ls~}tmllv|pmr{wtcgo~uk{~yolko}uonwtsokos}jsngoszs{~amtsoutzrp}tlt}iimtszyYwsmlsv}pyzq~xdy~rsrv}or]pkms{~qzt~mawptzlo^plow~wq}rudcyvv}tj|ld~qnnu|u{|ryqbi~|vzmiynk{ojo||s{svk[nrugqxsrurns|vs}zzpf\w}ougwystwsmvyru||upb^}~opf~zut}usowvr{}so^azmoizxvsrqzxu{slYcwmonzuvstvztu~qfXluoqsxsyuttwvx}}}qdXquppt~urtuwxxy|n^Zy~rpsy}sv~vwwxyvz|i\_}}trt{~}pw~xxvxvtwye]e|~rru~~yn~|xutyuvvuc_iz}pru~xpyxuwzpxxoa^o~z}otx|uryytxvn}zicawz|}puy|ssxxsyroz|idb}{{nu~}{rvxwuxotzxgbd{~yoxzsuxwwtmwvugai}zo{yptxxtoo|zsufcn}xn~~xmxvxqoq~tuvdcr|to~wk~ywmorpwrbfx{rssj}zrlpsm{pdh~~ot~|mn|ymmpw}o|ndi~ouxls{uloozyr|kbl~muslwyrlnpxr{icr~iw|pmztpmnsvu|eduye}}vpnqqmottxxbhw}sf||uqp~qrmpts{sbjykj~~xupsyrpmovu{pci{~gn~~zwtqxvrqomyvzmaiyet~{xwts{vsrkl~tyj_n~wd||xxvts|supiosze_r}seywvuurtxmip{uw_au|ojuvuxtrwwjjpxur]e{{np}uuvxqtxsijqwun]hyot{ttxvoyvqihtvsk[m}xowysvyrp~tpggwsrh]u|znzwsywpq}sneh{pre`|}zkuuysnrxsldk~nqbf~xk~txwqntvsidl{moaltn|vytpmvuqgdoxlndp}rq{xwrolztofdtvkneryqt|yuqnmrneezsjofuwqw~~wsqlnplcgplpfzvqz~urojq}njammnof~|vq~|srnjtzmibr~lqmhzvqysrlkwwlgdv|lrjmyvrvspkk{tkfhz~zmqiqxususnjlqjfk|~zmpjvwtv}trljopjgm~~zlokyvuxztpkjrnjho~~yjon|tv{zsokkw~lkiq}whqp}sw}}yrmkm|}lkit}sgsrzsy|ypllo|lkix|phtswt{|xnllrzlki}|~mjuutv~|vlmmu}ykjk|}kluw~sy|rkmoy{wlin|{jmt|{s{{olnp||ukis{yinuxt~xmmmr|tkjwzxhmw~wutmmnt|rjly~zwfn{{wv|rmmov|pjn{|yudqzxwwrmmpx|njn|zzrbu~{wzurmnq{}lkn~y{mby}{v}srlor~|kknyyie}~{vtqlos~zkjpywfi~zw}tolou}xjhsxtel~zy{snmow|vghx~urep}{zzsoloy{sek||sqdu~{{xsnlpzyndnxrqdzz}vsllpzzieptsofyvrjlo{{{fgs}sumi}yvpjknzveiwztull|{uojin~ysfl{ytulo{{snhfqzwreoyuvjr{y}qmdetuxofs}yvvhx~yzyqiafxsxmiu}zxsg}}v|wpeag}sxmlv}{xpiyuumb`i~sxmmy~}vnj~uvrja`n{syln|{tmk{txoh`asxuzjpytkm~wtz~lg^dwvxwhtwshq|vt~yke_hzv{tiv~vqgv}ytuvjcai}v{qkw|tng{~{xsxsicbjwzoky{rmh}zvr|phdcmuynl{zpli{yusniebptxln~~woll~{xsvljcbtsxjq}tnkp|zwty}ljadx{svit{rnkv{zvu{zmi`g{xstjw~yqnm}zyvw{ymg_ivsqkz}wonpzxww|xle`ktrqm}~{vnot~yxxw~~wjcanrrpn~yumqw|yyyv}uhcbqpqoq|xsmty{yzxw}sgbcumqotzwrowzzz{wx~ofadz}kroy}yvqqy|y{zvzmfaf~zjrq}|xuqszy|zv}~kf`jvjsszwtqu|z|yx~|ke`nsjtvxvtrv}~z|xyzjdarpjvywvsrw}{{yzxibdwnkw{~wussy}{zz{~~wgbg{~mmy}|wtss{}zzz{}~uebj|lo{{vtts~}zzzz{}rccnzkq}zvtts|y{y{z}nbeqyjtyutsu{yzx}y|jbguxjwyutswzzywyygcizvjyxutszyzwxxvfdktk{~wuss{yyvzwseen~sm~}wvst|}xwu|}uqefr}rn{wusu}|wuv~yspdhv|qp{wttt{utxurodky}orzvtstystyqsmen||nuzvtrtvruzotlgpzmxxvspw~sttokgflntotvlWn_\lyen|x{tPe]Xcovtixkho|Si][kq{ti~wuwwTk`Zcouq`pmq~xUxccigs{izpstYte_eruzm\vst}sYzlkdaojxlq|r_v~kfhtuxjY~xs|zo\tur^_m~hwoqzqbitnkvqwxkXy~s|~um`pw\bk{hxuqyqaa{voxowsnXss~x{qlcm{^ejyg{|zsy}r`^~tymwsoYoswxolfk}cgjygw|tx{s_^wymw~xqZjt~zxuonjj}hgjyir}uy{t_]zulv{zp]fv~xyspplhkekymm|xz|u`]~}qntx|oacy}wypqrogodlx|qh|{|}~tb]v~nppw}ofa}|zxyortqfwrcmxyvd|~~yre]olrmw|nja}yzxnruseoscnyxza}yrh\hlskvznnbw{vnswudhvdpzx|a{zqj[cmsjuyprdv{upuywbdxeq{z|cu{|}rkZ_prksvrthz~w{srvzwabzhr|||zfox~{tk[]splpttvls}xzstw{va`}kr}y~xkiwxvj\\vmnnswxql{ywrvz~uc_|ps}yvodvxxj]\ykokq{zvg{ytsz}sf_suszytsa~xxyi`^}{jpiq~|zc|yqu}pj_j|txzst`|{z}xzicaw}joiq}b}~xnvom_bv}v|~ssaxx}zxyjgdr~jnir|aywlxno`]yzu}{trcvw~xyymkhmlkkt|duwkypoaY}wuyupguw}vyyrnmkoinx{gpxmytobWysuwvokuy|vxyxsqjriq|zllzqxxnbYpruvwopu}xuyy~wskw~tjtxpi|xw}na[hqu|vwpuuttyy|ummwmwwuezw~na]crvxwwryvrsz{vlexpzuxbux~na_atwtxwt|uprz}wlazt}uzap{obabvxqyxwwpp{yk_|x~vybk}}qdbdzxoyz{yqoz{j`|{wweg{}rfdg~vnx|~{|to{}hcwyytheyx}shfjtnw~~xvoz|gemyzpkcwu{ukhmyrows~yp{{ggey{mmctsxvmjouppvo}{q{zig_|{zkodsrxwpmqrpqvm~|s|yle[y}xjpg}rs}xxsorprrvl{~v|xncZv~ukpjzssxyxuqrptrw~lx~z}xp`Z{vrmpmwvrtywxsrpvqxzot~}zo^[t{w}qoopuzpqzwzusqxyryvro~~|n]\oxxzpooss~noyw|vspp{rzsuk{l^\jvyvqpovrloxw}vsmj}s{qwg~zi_\gvysrppwrkpvw~wsjfu{|rve{z~h`\eyxqsoqxrlquw~wrgew{yutew{|{h`]c|upsosxsoqtx}xpdez{vwqgsx}wj_]brrrotxu|sqsx}ylde}}{uyoio~x~tl^^bprqouxvuvpty|zieeu|~vymlk{y|rn]_cpspowxwo~zpty|zfgco}{xwmnhy|zrp]_c}qsopxywj|pvz}zdiaky{unnfx~wso]`exusoqxzvi}qwz}wdj_hy|sonf~zuuo^`fuxrorx{ti~rx{~vfj_e|{}qqmf{}tvoa`gq{ppsy|qk~sy}tjh`cy}|qqlgy~uvncahn~pquz{omy}uy~~rme`a|w{qqkiv{vvngaikprvzznps}xz}qqcb_uvyrqjktywtnjcjhzpsx|yorn}{z{rtac]owytokmswxsonekfurtz}wpsj}~zztvac]jzxvokprvxspsikdquv|~trtg~{xvvcc]f}|xxnmssvwrrvmjbnyx}rtsf~}wyuea^dzwynnutxusuxrh`m}z~~qvrgx{v|th`_dxwyppwvzssx{wh`k}}rwphrwu~rla`ewvytsyyy|qt|}|hajz|twnkltuqnbbhvvyxvy|s~quibhszwvkmirt~rpeek|wwy}{y}o~rvkeelxzuiohqt|sqhimywxyy~m~uxlicfw}tgoiqtwtrkmpvwxzz|lxzmlabw~sgnlstsusnpqtyy||xm~|}on_`y~rimp~vtnvtrsrs{z~~tpz~rn_^}|~qjlt|zskuvuuts~{qsu|un_^w|plkw{~riuxywvrw|nuq{ym`^r~zpnlzzphsz|xwqq}lwm~z|jb_o}wrpm{znhr|yxolmxjz{}ic`lvsqo|znip}zymkpvhwz|}hdbkutsq|{njo{ykj}sshsv~{iedjuutu{}ojo}wik|woiptxjfej}uuvwz~}rkotily}zjjn~sulffkzwuwyyx~ukpqjlr~{gll{u~snfgkxwty{xr}wlqnljlzfmjywzrofhk}xxtz|x~n}ynr~lnfg}welixzvrogikzyxt{}y{l~yos}}kobe{tgljy{rrohijw{wt|}zwmyqt}xmn_d|rhkk}zosnjijt~vt|}zroytu|spk^b}~qjjl}xnslkhjqvu{~xosyxwv|psg^ax}|rjimzuorklgjl~wuz~vnurxzw}{ovd__t}xsjiowtpplmhjg{ywz~rnwmy}x}wqvb`]pvujipusromoiiby{xz~oqvjyz|sttaa[muvjiqtusnmojg_w}yz|nsshz|zqvqca[j~vvjjrsxsnnold^v{{ynupi|xqxme`[h|xvkkss|toppma_s}{vpunk|~vsykh_\f|yumlst|sorro_`o|sstllvsuxjj^]d|ztomtuuspssp]bj~|qvrkmqqxvkk^]c~{tqnttpsqtup\dez|pxokmmrzslk__c~{tsptsmtsvvo^eaw{qzmlmkszqnkaadz{uvrtqlvuwxnbf^s{rzllmjvyppjccev{wyvtomxvyzmfe\n{uylklj}ywnqjffgs|y|ytmp{xz{lkd[j{ywmkkl{|tmqjiiin|{~|sks|~~z{zloc[e|}tnkjn~{rmqllklj}~vxjXvxvxttgzlj]cnsagko}Gyqkchkz|n{zgni{n\[X[lwtWgvTZ]humlxllmfKslolzyvzpww}gobU`t`T}zlxlich{o~ro~YUosmt{v~{{uu|yo{c}nfr~n[h~}q|ugdt{~izrvbRg{kwv~xz~xy}dml|wa[o}twtxomq{~zmyxua\xprzzu}{opxxog{|radz}zupzjx~~qor~xe[`u|y~~r{|xnt}vidg~u{xsh}zuu||n~}}yifivsx}{vmzss|qjyyihp}xumrls~wou~zt`dm}|w~wvrvt~xlu~zugiq{~wlqnoo{uk|tifwv~|}ttvtjoyid}{whizuwtwver}ih|k`e|zzz{zxrp|xdg|kgkx|trusqoxphpghf}wxsux~unm|~ddl|glk}ywy|njp~{ijo|}qbcdxuqy~uzp{yfgt|~pichs}uxuz|ss{twyvis{qdZkvznruwvvrqjhmw|mdbw|{rv~yqtzsqmtkw|{{b^Y{wplz|y{~twmpb}{vfc\rtqt}|wvnw}oqswitdZ\sxjs{z|zqxqjtzmkc}qebnxzn}}|zpkztns~rld|i]^lxrm}~zyu}wpyxjbhx{zjdgo|qqx}uxrqj~tkvttioy{gaaq~vtuuuzxvkyil{jm[q}vwhljyz{xwxxyskj|mpwor\u}ylff`yz}suz{{xutzpt}}jfX~vw|moje~s{rwuuwpnr{nozona~w~{pl_ansqx{uut{qhrqidZt}vnhl|qut|uopvwlvrqg]}|v~vqeaftnvzw}yx}uolkZ_}r|ytihk}nqsvzsznyspos`i}tzoaalpoqx||l}zhr{jlVlwv~phku~qqu{}zxd}p}yqo[s}r{rhcfu|mpz~k}ytumcWzqqmjmyrjqw{{tf|zyrid}tpicg{wmqvxjzvv~olcb}~uwnlqvotzwoirqje~wwvofiosmu|uo{~pmc`}~ssjqsikv{xppwtietqfmnnqy|or}wpodf|xmuuoux{euvvsfjxulhuv|ovf}tul_m~smo{ypiszxc}ndu~pjnwxts{weuvfe{}~vtx}|rvzmdyxij{~xumlt|~ty~nk|yqeh~rpr|~nt|tjpuljnpp{xu{vlt~zyiih}t{zx{vurgv{{mlhysprt~w|{~wk|xwhdgtsu{xtuyztpmlfntrux{v|~zyrqztaeu~w|yx}xulp}qciwxwvrvvw}z{~|rsz~f_d|{zvzx}yrwzsntjghwwwwu|sy{wqt|u^dj|ysyz}okr}tcjnvuwuwtxyq}~}qru~xm]em}zwz{zstqszirsremrw|vtvrqo}x|zotpuyddjs{||}xvvus{niiryzhmjw}s}qtrprrywv~spkuxwsde`}twsvxwtkk{}hlfy~|ljg~yrqmomr}vukqf~}|syqddeyzzyvw|xqutaiavtijhyzopmnnwx}|uujnfxu~i`_essqsxy{nly~ihgi{niel}mplmrtqz{mpir|{rvdb_m|s{vz|{v|xy|wafcxyvkges|{nlnktutp}}hqiz{y~m`Z]vtrpt}zzq~zss|}pbod|ugde{nnkrtopm~}rlwg}}y{zg_`btwz|xunxz}~|fgkb}|kgfhzsimqvssnn|nupgv}rb][g|otx|zypswpv|~ugrfg~zleeqtlstupnhw}zryhl|t}jc^ctvvxwly|y|}skm^m|njem{~yhouuwqsg}utdpzyxga\iztzz|~wyhss{~~snn`q~sjit}{qvtxvmncxurburzreahr~~z~wshz~nmf\z|ukirwpptw{sqnkxwka{w|medlp}{yxqq}uz||mqfb~ysqvwzww|zppit~twheyryjhktx}{ymzton^c~oqry{rqy}xqum}sci{zxjmmtw}w{o~~~qsjbn~y{v|zx|{vquiwwkfqnyrnoq}z}{m~qocat}xusxxxswz{uvwo~wjgyyrpnuxuz}xqyokfj}~{yx||~{tyoq|sojmtvyqprzvqwqnhekwvx|vvsv~zyyp}y}plpz|prruq~zr||kqfmu}z{tyz{xsmzrukqu}v~rqrpyvxpysonfmuzytu|wvqu~{yrp|yppy~trlrvw}rp~~|otmko~|y|z}v{{tgmu|qnqy}upmnvzxkq{pxgil~uru}}yqt}{kktnpnslmoxv}lpt{}sxtenmyz{|yx{|rdks|uknmx}rjlnw}yykrwvzicblytsw~|~tuw~xlhtzqifrnllrutxnqwz{zvwccdww{}|zy|x~yubmn~zfif~zvqhnqrvvx~mxntyjZ^`zrxxy|xzypqx{yqjukreehzonpnopnz}xsxmxtvc_blx{yvty}wtzyxkhmj|zigjs~utkrmopqqy|sspkssn\\crxytwvxs|rkryz{hqhl~ygikvxqojjinmznxgsxlvh_fgx~~yvttpusw{~pbl^yv~mjnlw|pnmjmoqp|~ynr`yspsaabezxtsvvwu}ulox}kik_}|pmjlpoijjkov~n}{kn[ckstzhM~lXUYl}xzw`gr{udafQFN_w||{vwcZsoyuoVhVTbyy~z|tdh|yw{hhx^MRgz}mwm^Vlvwzv}idxtc\jywx{{rmdj~xvqikhRYr|lxc]Sh|rxugnqrcpxw}wyzhfcf~~x~moprbgy{qy\]Sjm~w~~nboq}p{}}wy}xhb__}z|qqr~znnz{}wxt[_Uin|{|zqhbpwwu}l]Z\|~}t~{{snuyry~}|qwc]Sgsy~tpjbizwwv^V\}wy~|z{tmxty{ouiUPd|y}|tslcd~~{y{aT^~~v}yvzztjwzxquoPRdy|wwwnca}|yxaO^xyss{zvkv|xusqOWdw}syuldc~z}{`N^v}~ot~{uks}x|{rrTYcz|rytoff{~|ycR^sxpw}{uoq~{|{pm[Xc|~|uswtnffo}}qjV\qrrv{|vslz|t{jhbVcxzxuwuvnidg{~}kqY[ontszzwsexxpfghWcrzvwxttnlagv~jq\]opvszyxmbxtrjhkX_q{ywvrrqj^hw~mpd[ovvttxwheuss|}qfn[]ryx~uyppqd`k~|~{pnhXoyyvtqyqfhs}vr}zwfq]_nytvxyloj_bl{tmh[nxw~xrrujghv~xu||{zjm`]evuv}umkc`cp}{hj`fuxtspkhfmx{{w~|igfWduzu}roc`^es|z}~cn_cuzqtjgfgos|t~~gfgTcs}x}tm_]^ip~yyyz~cm`bvssiedikqzv{}kdfU`w~rj^[bim~zvuy}egd_xvpibfjfs{vvz}}q_fVa|oj]\dcl}~stwfcf^{{xmibiecr}utx}|t\fZd|oj]_a_l{rrvh^gb{zzkifjacp}usv|v^b`ex}qg`a]^jy~qoxm[hiw|~kjjf_bm|wqv|v`]hetztef`Z^fw~so|qYioqzjmla^`jzxpy{wdZndqyyfk^Y]dv}sp}uZhqmvnol``^g|zxq|{uh[peny|}kl\Y\bs|vs}v_frlt~upk``]ez|wt|~{tn^lhm{vpj\ZZ`q|xw{~whbpnu{}oia_\dyvx{||tscfmm|~rwg_ZX_p~|zy~zq_lrzywohd_\dyw{y|{tvi_qnxr}gbZW^p~|w|x_gwvtqgf]]fxz|x}{vvn\rptu|heZU_p}w}|ccyrtvhfZ^hx}x~~|xur]pqq~ywlfYUap~y~}javrx~{jdX_hw|y~}~zutajrr~~tsfXVao{~{|p`pt}ylaY`hw|{}|}{tthcrus~ycXW`o~zz{vahxvn^[`iv}}zy|utm\ryu{~aZX_n~v{{z`b}to]^^hu~vw~vtoXq|xw~a\X_m{r||za_~v}o^_]hs|sxxtmYn}tze]Y^kxr{~vd^|zur`^]esxsx{rl_jpvk\Z\j}utzqi^z}mvc_]aq~tuv~}nldflso\\Yhxuuy}nn_x}jxf`[`lwtvu~{kmhflqs_[Xeqwvyzopds}jwl_[]duxwx{wmmnfqoxaYX]lwyx~zpqinlwq\[V]s{yxw~{upnse|vn{aYUViy{u~{qtklowr\YOYp|wvyvusrseyzpydXQSf~yu{~{vslkust^UNVo{wtsttvtqhrpthTPQexww{}|ypphznv^RMTpyyqpruzrqkkoqkRQQe~xvtz|xnqg}}kv_QMUnzxoopxzqqkhpnkSPSexxutx}vophw~krbPPWi}vpmqywromg~~tilUPWbtwvsx}vonkrkmfPSWdtqltxusmog}~yelXRZ^rvury{vnmnpmhjSVUb|tqnuutrkoi~|{cj]UZ\oxut|zumlproekYWT`wtrqustqknn{|}efdWX[l|sw|}zumjrutbkaVT^svsttrsolmty~}hbkZW\i~sy|{{umjsywbkhUU\oyuvrrrnlmyw|m`n]U\f{u{z{|tnju}~~yeimVWZk~wxqsqmln|x{sanaV\cyy{y{|uolt|}zkgoZXYhyxqtpmmo{{~ywejfX\axzx{|vqns}|zqeoaYYf{wstomnpy~{xzkfj[Z`wyxz{vspr}|yvfkgYYewurnnoqwxwzqcl_Xauzyyxwurr{}yziglYXe}xvomooru|wwyvbkbVbt|ywuywsryy{mdnYYf{yvmnpprtzwxxyehdVbsytszxusx~z{rck\Zfz|ulopqrtyyxxzhddYbrxrtyywsw|{ucf`[fy~rlnprru{zyyyzm^d\arvqty{wtu}~zxc`d\fwonnqsruv}{z{zzpZc`artrty|xurzzyc\g]fuoomrssss||{}{zqXbcbqusty{xtoz{wdZh`esqonqrsps|{}}|xoZ`gcoysvyywpoz|sgZhdcrspooqonu{{~}~uo^]jcm|~txwxtlpzzqkZhfao}}vrnmoknvz||~}rpb]kckv~xyuunkq|wqm^ff`jy|{smkiiox{z{~zsqg]iefr{xtoikr}wrpbag]fx|qngdho{|vzzxuql[hebq|yrigiu~zxstc\gZdw~qlbcfq|zuwwyvunZee`oxogekv~zyxwvdZd[cxrjbagrzytsvxywn[_e`ounfdlt~zxxyztgV`]b{qhbaiqxzqptx|vo]Ygapsoefkq}~wwx|{siS]`b|rhbbinvzoosz~vo^Ugdqtoghioz}vvx~{skTYcezugddhlt}xnns|}voaSeiqvojhgnw}vuz~zsjWUfhwyhgffjqzwomt|{vmcSboo}ypngfkt}vv||yrj[Rikt~~iifdioyvpmu{zwlfU`sn|{}rqfeir}wx|{yrj`RhmqznkfdhmxwqmvyzvlgY]upzzvrgegp~}yy|zyrjdUeppwtlfcfkvzrouwyvmh_\rsx|yrieeo}~{{zzxqkg[aqp~v{lfcdjvsqsvyunie\nwvy}rkedn|}{z{xqljb]qrzx|mhdciuvsruytojj_h{vwsnfen||z{wqmli[ovv}xqidbiuyuquwspkmdc|xxuqffn{~{|vqnlm^kxuvujdaju~wqttrqmoiayz~|wqffn{~{ztrpmpbfwuwzjc`iuxrrqqqnomas{{{pggn{{xsrpnpgbtx{}~kb`iuzspoqqpoodk}y~nhho{{utsqppl_p{zlabhu|tmmqrqqlkr{kdmr|xbN_{rjhSVy|yusa[r~pSkwwrofSgwmfZg{ytUI|pddch`Zr}z}uieuvw{]Q{~lhnxxhi~~txrhbiz{zrmSWyg^agbcq~{}jfipx|j][qipoelzvi_hn||yj{r\^bvc_gdjy{}cekly}xs~uqpixiqtou{x~z^kjn{ilow{fefsvjnhkuw{kshjq}zmtw{qojz|xwsy{{}nfjci{wpsejp|rhkx~sjbiv}zqpcbwsxszy}sv{xuxv{}n{qnoakitsar{{ugy~|yklptyx{upfWh{jwn|zs}~vrmnsujj]_txi~kn~yxqgoqlinmrqi^frvvw}{|xwysnhywx}gaakwys}jq~y~xpnwrkkdf{gb`dv|{w|yyqw~y|sg\mru\ghqpr{kz~zpnn~}nlbbw}rajdpxy~{zwyyxu{lYbywgfomwqzup{w|ullu}w~ub\gxjjjkuw}z}zsvstvu\^mutcgmr{mytmv~xmgqrzf\ep|~kllm}w~u}yruuk{~h`fuxmxgprtzk}hi}vlpnrqbdjx~z~gmgo|xmx}ttujsqc_h}kttepo{~{oqwaw{mjgi}hgfo}ruglm|x}{myvumopgdq}jyogouzvtrmf{wqfeurkfgpysfkpt~wwwuzyklu{ocgtzlxkgsvt~jjnnxk^m|vphdkwvlfoltoy~z{wmjw|{jcj|ylzjowpwaqrqr`dtvzqggn}uhlki~~gyz~rjsytfdovozfomqraynvzg`itvyheisyx~hlgtrnxvjovzmjkxmsxgmm{khxkz{peimzxreimxx}hhiyqtyynmsuzolkxpyuekt~zihtq~vkjgmuljkpzztahq{~|qs}pkqp|{wqko{qznbpw{z{hj{u|vnliey}rrmlr|xnboq}z}iw~unqpu~{njpmxfdrs{x^s}sypokeovwqhjsu~fgnr{wf~qplovkitzitblrz}|ob~xsunoflzwzlik{~sxejl}|sq{wqml{ofh|rkqdlt}wmlusuqijrz|thfovqucjwzxuw}zwnjvxgdphkqbq~wzqoptsiiorvochtmwrbu}vv{w|rjry~~rcgr~brjg}~~yir{oxmlnlt~sgcktkxhlz}}pxylpqv~wgcfytfsju~}}~~zfvystongjtvnggkzlvmt{}mutqppuyqgalpfwow{w{}rb|xqwpgcg|noedtrm|mwz}tm{wyokjtsqafzeo|ozux||}yeiryvj_]p{pmdnzjxxmvx{lxw|tkbi{uidn~ygywouwywwocsx}r`Ucuyqmlq~}k{rjzvwm~y|pabo}ypkitui~olzxwz}xoqe~y|eWZhsuonsxwm}gr~~}uwwm{yecfrtsjot}ooygv{zzz~yqtmj{p]Y]qtuoom|porh}}yuw{tvynechysqmmr~lntm{}{wzttttms}sh]Ydxtwoiqilrp~wpxy}szwrngfn}qsilz}bptp}zxqpvswk|rqf[]izwtgkwy_ssxwmryz~qnulhhqwtlhtp`ysx}zontvvvmnpb]_rwwjip|lczu|mou{}|t{ptkhiyuogow{hg}u|}qlqtwvupzpna\dxtojntzgh~xrnqx||{syqskgoykjls~~ugnutloqwvxtnwok^^jwlomqz~wfpyyqprzzvpvrrikpy~fmnxxvevuyoopsxvzlpsog^bj|yjpmu~|xex~wqpuz|nwstnjjoqinq}ztf}z~rqnquxxvhzqoc_`nqopqy~tg{wprv|}ym~rsmjgvmknv{rkxwsqntvyurlpmb\`xqppu|}rl~}vqux|xxqrskflljozzro|tvsorwyxruook^Yhoorx}sq||{suvzyx|rqpffuiks}}{stvuwppvxystwn~ngY^qkru{suwxwxwxu|{r|okcl{}gpu|rxsyspuwwuqytrylaYev}{mwv}qzx|zyvutvvyvmffrulswp|uwpuuvtrtzq|sg\]h~~xsvw|n}{}~wurty}wqiektupsy|m~uuuvstqtuzumc\_n|yuuw}|m{{tqqwyzjggl}uptzym~tyvsqpqsuwfc\c{~wuuwvm~~~wpnsv}xzgifspqs}rqxyzrpmorr}uud`\o~str{ot~yqlpsywrhgl~mpu|owz}uokmpry|uoc^evqru|{oy}skmnv}vogkuyjrzzzo|}zqjjmny}wuiadkzynuw~{zmvljkruvhkqwolv||zynyvjiilu|~pz{cffn}qruz}}vr|okgnzp|xkrqy~}krx~{|uxvoihhrx{}zq}tfffp~qty}{v~~vmejs{~}zq~xoqqv}zzmw|}z|xxqnhgmrwwzyrqeffsw~pz~~~z~x}|shhmtzxwstoon{r~vr{|||wqtlijlqrvuxidaj{vxwz|~}~y|zojhnqu{t|pmkt}uw}}y{{~~w|ssnjhkjszv|zb_es}zy{}wz|~~y|xnhijny}u~hkpxywy~yyy~~{szsumifdlw}t~pZckyzx}xyz|}x{|vkhdgs{urfpst{{yx|~qwwwrle_eqz}t{b]isyy}xzy|}~~}t|rjabkv{w}kks{v}}|x{{}xpwwqg]aivyqr_boz{vyzz{}|zzx}yob]eo|wszjox~vz~{zz|~xxruwl`^dn|rmp^iu~|yt|{||{~zx}|zwf^`iuntyjuz|yx}|yz|wyyvurc__ht}~hpmaow~ry}~}{|yv|}ym``cnxyjzwnx|w~w{yywx}x|{ria_bmwvgsjer{|s|~{|xvzrf``gq{tm}vqzu|zzyuw{yvlea`fo{}siujhv}xv~~|yty|yld`bkt{sous|}p~|~zut{}znie`bjs}||qkwhlwzs~x~ytv|pkc`enx~{}qrswz{}m~vsx}zhibaemv||noxgot|~s|{|vv{~nj`bhrz|}oxsxxvp{sv|zxhg`dhpw{~ltwhntyx|~yuz|mf`ejty}}o~svysuwuz}ythdbfjrv~}nxugnxw{~{vy||kdbfluz|r~qv}rx{uxzwqedefkqy}pzqeq|v|wwz|vifcfmt}}szpzoxwy~ywlggdfkr}|ozlgutxwx{}qkebfmw{sut|~o{yw{t{~ijecflv~woxhlww~|vxx{pkbbepzvusx~zwzwxr~zlibbdoxqothp~{xwv||pg__gs{qvt{{}~xw~~rzje__gr|josiw~yxvz|kc\`jul{~uz|x|zttga[aivzespn}yzvy}ug\[bm|vl{}wz|{~w}lc[]bn~ofvqyzyyy|{~~l_Z]du~qp~v}zy|}x~d^[]fv|jh{v}wy}{yyy~a]Z_lz~}nvzwzwuxwr__[bmx}xdrwy{~~yusyo_]]gpz~}~yn~t}vprz~v}i`_agox{~{qg{}xv{snsz}h^`diquz~}xtz|wxnmt{vszeacdhns||ynjyxwtnnt{zzbcdfjms|}vyz}}}}pmovmwvdgdeflv||yjp~t}}~xpnpzu}scfefemv}t}y~~uonr{wm{rffdbdpx~|tgr|x~xtpou}t~pdfdbfpy}q}|xtppv|}to~oef``hs|xpcw~}~ywtqsw{vkee`cjt~xmyxsrsw~}}rujfa^dlx~|vhd~~vxttsyxz|gd_afny}pqxxutsy{}|rzzfb^agr{~zpck~wyvttzx|wb__citz~xoxxyvsuwy|r|v`^_dlv{|skdt}xzvtuxu|p\_afnv|yvpzzutsu|{q}m\_ahqx|xolf}{~|{zussyt{g[`ckqx}{ywr~|xurqwxrze]`dlszztpljx||xtps~}tua]afmszzz{xx~|wsns{uqta_bhovzwtqmqy}|wqnwyqp_^cjovxy|}z}{uoow~qnq`_djqwxxvsov{}{tnq{tnn^_flqvw|}|zqnszkmo^aflrwy{wvrz||xpou~nol\bhmrvz~~}wopv~{gol_ehmsw{|xzrtjnps{~{vwosysxsqsvy~\R{VO[eWWapwsw|mzhm{qnkr_VrZXb^Y[u~vx{i[fuaz}ukizsJWk`YVXcsonjqytyruuv}ylTVpeeh`ThsceppzjjxrsvrqymU\bqXeigfapzommgcmmlo|yjQSanhas_^el{wq~ed_hxsv]WJUy~lzcbaq}y~vbppqxz~z}xidHNp|qrdqjsqvbbbcffxusrsURE[o}xaggqxxzna]dv{v~}}xr_@Kbt}neeh|pwaiprqtlws{y~kgS@I`jorot{t|qsfajrpk}qoqwsSLNa}ihen|yymbbai|{snts}ukoJCAZppfqxtkummsvwugp|{uyvVL@\ultrt~}xnrmbelsefatyx~rtu]MQI\~}njt}t~tz|pnddtubu}ytxyr^G@Tyswmk{htnfgntzun_VcppyvvYYFB]}z|xuqcx}sfewxtgmorw|pmhgRJVrzkm}z|gfafi|w\]Zjvvymo|ueYBCViyxdensvrwy~yqfh^x~owwqfLG^u~{{nwqvqhdlxs``dUadpsu~~she\TQYbz~tmykqlpnr}ood_opzz|x~~|{bMKV_{~z{qrioakv}q]gSRbit{vw|yfeYOTclu}jngi{vysvymbwef`gzwq}wzsmc^V[h|rysi_ipvtjiNWS\}nquv~rufYPYZZ]tu]ghjcjoZccqwwyxonddca]~ws}gtqorvwz_hfYUJWxwnpw~ypk^fhf^STv}yzkl`i~kue]^\gx{x|~srni`IQzwhmeh|vcn[RMVc|qr|wrfaggejUYjqyobekvweiaeZ^j}}rzpqvgGS_lsyxons}{qvn[VLT^y}tmupnpof_CV`mqj`fnxyfXTTkzyuuqx~vnTYQe}kij}gp\^[T]go~uqlp~}q\LTIZuxwlu|~zg]W[gr}}uos{_ROK_tzrqpz{qw^[SYipn}x~yt{||qkY]OIZp}ulrz~y`c`cmtx~tur}ceJAKmnwx}jyta_adgckuyvux|{iQUBKWo}~|~wzmb_estxw~{|zzqlmJIOitpq~sw`]bljmgg[ozt{|d`Y@CKloqxslpljhppstrkmYHMRg~z{}thebinqna]smz|yuns^kUGHThq}pmnwzsmo`dvzw{|rv[OFMe~~pymogntqifVgjh~~olr}og`FHNXhvy{nwsxuttmZokqv|mxs_TPTarxvfdfwzsqaP]Rgznpvqs]PHIRj{mvksxysnZ`pbsyxwtyaZV[Yam~|ooilrtvx\^YO]irltsoo`XNQWak{}yxhon~xlVcW\q{}}uf^VRWcotwsnumqvuucXhTVZjspmurwcYQVY]_fhwsqps}hbcR^bt|v}rrc\VYX]gnsijw~zsZbXJU\mtpwye]TSS^ebb~zjwv|s[ja^_`r~x~togceb\VO`{{xvinwv{rfkQPOQd|v{y~npe]WZ\ZOhzrpqmziceSY\fv}useea_`ZMdj~tywuwyvwbjf\ZOPcz{vtxlj]add_PKc`yz{oo}qu`UUV]rvuvyolgc_JKdfvxqp{sdl\XRU[jt}x~|wi_bbadRX[Wq}xvv{yspacZX]povmjrvn`DPPWs|ts~}zowo_XPRVgx~urypljfc[IZV_lv}tsv{|i`XT_js{yxyylmtrnbTXGQcy}xtltbf`YX[dmi|ztrjissmVOTHR`y{y|~phVU\do}x|tstwy~vpYVPJT]t|z~wwq_bUXdhifawv|~uqpunfYaRJLWnv{lncadflkkvsosxs^`HDCTwntid\^ebgdj|xx}upqu~wbW[GOP[q|xqjccnnmbo|{|}zw}~{gjkNJERjv{fcelmnicTdlwqrrwdd[EFFYvuxzzsupkfb_wr{wvq{fl[LOQZmzlhdimqraYh\iv{itxs^lZNKQ[lxxsorz~snYYgc{~~|{ot\SKNXm{xqsoqvtliUbf[kx|mkrskeMKP\cnyqvv}}ysXd`\k~}jqna[W\\`mzklkwzujT_QVi|~vs{sxcVNNV`lzw}voz~xbWgY_m~~rw`ZW`eddlg}ttuxztyeabPS]syt{upq__V\_^ahhsnp{caWLZou~ucb[_a`hecqw{xmXf[TVduwy}uj[VWcfbg\c|ywvyzkkTQTe{~pzfiffig^]Mf|~vsu}iebMNUixv}he[[^^beVmpsyuxlmcWVZjzywcc_irlfXKb`t~z}}wks]SNS^qqohicdf__NSlfu}wu{trWQO_n}xqlfchjefPX[Xo||~ikh[\TY`ly}{ii_`ilf\H[UYo~{~|p\SOXexwwjqlmojfTGZR_m~{|}ms^ZSU_houn}q|tnbbgcdVW\MXcu~}~gg[X[eqwtonifmwsjNRMEUhq}vc_RUYboppsyntqnnkd\KYRT[bquhaU\elrht~wwkgnpmbY_GFM]tyq|ghe^\]_dZrzxqqkjrsmXTUFMRcv}ytaXZ_goh~wussuxrkX]TJNN[o}ydg\\dhi`Vpny|{w|vpnrndYeUKILZnxvxlfdad^b{yzuoos{}nacKHCJ^vwxqk^]cbe\fpfuursuz}{q\[]KSOS[k}{ysjhnliYgll{}xuvxucniSNAFVjmnkkllf^Rcajvwumqw|ycjWIKEQdr~|yromg^^pflr~uzsboZSRMPZdw{rlhhnsr_\aT_f|tyuz~}lcoZTMIP^gwzyxx~xlV\`[mtz|ursYTMKRajrtz|ussspiWhbX_cvvpozmmgRPQW]chhl{{w}u^hYT^j{hrlb]XXXZ_`suuqy~ziYdRS_l|zy{}t}h[RPU\emdr}x~j`jYZ`n~|rwa\Y`feb`Sjy}{|}yzihgTSWeyqqtcd]^`^]]Xw|~zw}om_OSbts~yfe_acbe_Vomx}~~q_i`ZZ`lvrq`^\djeeZWkgwy{bXT\j|r{jfjkmmc_NWhgyqmkUTVeqyqrpkgecc`cVcgdpvzra`\eouieciqrlbPZTYo|mzj][X`irowrglopoibTN`]fs~m_Y\irw{y{pmnmnih[\^RYfunr_baciklqmxpkhknrnfSYUO[nzqj`^cgtzzmlovvuoaPZRRZjwo^\[cnnrjn|rzyrrqsplg[XbRSXg{zvqojiigkdxtmlovxt_]ZFIPiz{nhb_beglfy{y{}kknwzyrfQYSOV[kztqhflmmgdyu}~|utssqf^jVLHM_uyrhjhhjfeZatr~}pnmquywc\]IJJXk{wlggdgaqvq{zuw{}yp]b\MQLS_pokechomeXgdguvyuvuvuk\j]PMISapz~{wsldZYmju}tuvz}rflTLHFScrytjehhg]el`fm|}rqqzyc`dOSQRX`jtu~{wtwsjYbb_kv|yxtdonZVHEMXjwwvsrqqj_Ud^ajq|{ppu{{jq^NNIN[enkt|xun`_m_ben}~req^XXRPSV`b||vonqusbbcSZ^myz|{{mku`[SKMV\fgpZb_Xeis~swt\YQMQY]^_{~ytsqi]ncZ[ZfvytzmsiYXUVY[[X_xx{wgmVSY_pylynfaYRSTURf{yy}zf`hUY]blyyz}zxj^URRZabXgljznoo[[Y]k{{wwb`\_aa]SH`ep|}}|krhTTT[l}}nxvhi_]ZZ[TSnhmw~swdUV\fs}xzid\^aadWScWdtrgrd^[\`js||toda^ejfcPTbXgwj^TW`o~xmjkjlld^KV_XdsuuqZXWblrxvprsqnjgggdaTc_W_mx|kibdhksrxnheiptpcR[NLZrrrc`[`glwqytyrfkqtuogUO^V\dtxkecjppujyvrrqpmj[_cPQVh~u{fefilmjjayqkhmrvunX^YKS_v{{wqmjhkljurrz|zuhS_VPV^n~}hcbgorrgevow|oqrxxsoaYhWSV^m}}|ux{zvnhZexw~xtvxxzfbgNKM[n}yqmmlkii`qtowsjjr~|v[]\LT[fs||xvxyyuj]nijx|um\ldUTSYdqonpvvrn]Ykfozytnrxz}rcmXLMTcs|~{sl\eqgov~}|gbhUSWZ`flsvwooprtm]hg]fr{zy|}xraim[VUV]eou~}{~xeVebdq{~{ujtjWRPT]djj{zxsmh\aqfdgo}ztv~|ogo_UWZ\abcd{~vehhZ^fv}thmvh`ZTQSU[i~z|}yrdZfd^ekt}xuvvurzt_URQW^a`i|~~vifrh``do{wpul_]]ZWRNPe|{xusqikn^Z\cr~}xrnrzrg_XRRRT^t}|ykmi]_dkrw}{znaZWTVTQXiqxvohgrpd`]`hq|~}yw{via`_[WQO[kqz|ziZX[eqzyzzrjd[SOJM^lpwwnmoe`bdgils}yuw{mb\XVXWZdffny|}od^[^gm|}sy}vzp_u|oojdztwvssiiilj}~|}}}k^}rpwsttmkolr}vuyxxvt\\x~mbcffq|tuwvs}|~r]\||pk`PIcyqkoz}|y}ufivz|sjq}xtshfwwxrgRIUhutusecjibUVYS\mw{x||puxy|~{_]emutzxnz~ydPQXmzlQ@GTew~vkx{xwuztsykbgj~svzqls~t]Za]dnppjpsd^[VRQ`kfluvqktxmouuvz|opohpxz|}{t^SKFNgyeZWRYn|}{zt{|vu|rg^e{{umfg|ywicjolhqzqhgc[U\fccknoowzpnrqrw~mlrsrv|yhbWHCNj|~tp_PZgrzwywuw}wd^hx|zy|jah~|lklhjoxvopj_]ahc_ghdht~vmqnjn}uuoms~~qjdTHFUfowc^[\j{{pqy~ujfnpu~~uvuifm||xtghpyzptteadmlbeg_bjwzqrlcisuioz|sn\OMOV[lvkdVWkunt|}~vtqkjv|zvntqmqqq}qily|uuslggppkmg``frrpumccn~uov~|weXQVWOZkt~p_X\s|wt}}yz|y}ocklnu{omx}suwimzwrow|vzvklnutkrnbaeqpiroeafx}{zvw~|{oaYZ\POX_m~k_^iv~|}y{xxjdcdky~vnv}~yoiipxxyyxyxtkt~wqnolahrnkhjjaist{{~|x~w|~uvkag[TQIT[lkepkt|zzxxrf]`^r|{tzzvpndekw~twwyss}trnokjsnhhdffiqmpzzz{xryuonleVPQMOaxupojq~vzxzrjgXXgpvt|uupz}kjh[k|rx|t~}swtmzvonekd]onklk~}wxo|~qoyk`TNUJQiqpljq~z|t~}plhY_ihnpyrmpy~xokfbmx|{wx}vy}xtwzkikgcckldgpv~|{yu|xtrkYVRLTXamsrepu{wxwxjciabdeurxuhspzywfhphou~v|vww|z{pkodjlafcfnl~~u~yig^XQJZ\Xesvjmt{~{{}|~vkgmeZ_flou}~zpfjw~slmpjep{|~yv}x|tinpkb[cecr{{nabYNWVY_`r|{shm}{{y{z}xpuhed\cdm|txz~}~{xihvsz}y~vtvhloku|{{t~|wzrssgg`W^bnxx|wiZXXYTR_egtump}}|xw~wz~vjfgd]cqusrvyw}utsikswvrzslmnpo|~y{{yv~}llh^XSbnkxwi`V^ZOWV^jq~swu}~zuw|~|ywnl_adeonquinw}zwwhfliptx~urnenuz}|}}uof\ZY]ekuz~vke`XVWSRZl|vt~{xtx|}|vxse_^imfmqliix{opf`hmzyt|shhk{|z|~lc`\bX]omt~}vpkfSVXMRZm~}vyzuzz}|zw|~txob`akkelihklw}wpob]gryuuxohjpyy|z~{lbdc]Y]fkswymaYQROSckx}txw}{|}}x~unmcegbjfbhgrso~~zlid_ottzprpiqloyrx~~z{nhk]]ZVfn}xidTNRS_agy|xty}yzv|~~{nkkig_`e``gptmoy|}tifhinqtuopurmgkqntyzvje[XYWi|z|ve_UJZ]Zabt{{z~zsw|x{y{mhtib]Xd]_pmokizzzvfkqfknqwmu|njcfpjxzzgaYYYXo|}}ypg_UQ[\X\cqz|y{~{~|x{z}|vqnpi\[Z]cdkkhkjq}rpmogboqsxuypdgdkvv|h\^U[chyz}yrl]_XT[V^fkuu||vw|{v|nhg[]X[kfdgdlglyxkifajmv}sqogfbkxsu{yn`ZX^dbly~{tjgb[PPZ[bo~~vz{~wzztdadY[beja`hdkqu|vobdf^lwwwkmpdkoqxqwvukY`_\a_q}~z}|npdZTJUZasw}yzy{{x}{zsk^`_`f`dhabdksnuzof^acgpprqikoqrnsyuxtokb\YZ^ao}ztg_UPJRgnt~~v~xz}uvt||ywlg[]habdcicdojmop|}xgaZbkbiolnhoxoqqqzw~|svn[ZVU_f{~}kdWOOQ`kq|vt|vyurv|zvmgd`bcbb_dkkijkljszvlb`acedfikqqquroou~xohXQSYir}xaXRPXVbrr{{u|znuux{pokjh\bc[`bmrfjidlpynpgea]gb^hluslupkquyplYOT[inwxdXVVVU\hox}|}xnq{|z}~|tormf_[]]`hmpnecdju}ypprf`_]a_gsrrnilkpyz{~rf_RX`cps{~z{uj\]USWTbpz{|xptz||w}xuvjf]S[^ekhok`cepwx{zstreaYYbemrprhdkouuw{y|mfb\[]dloy|yzuqe[URRS`u{~z{x}}yzyxrj`ZR[fdgfgi`enoux|zqpc\[[knlrmlgbprospyzyymhk]Z_amp}|z~z|u`YPNSTiy||v|w|z{|~uod_XVacab`fgellmruxnkb]]donkmljfgmlhjmtzztoj^V]cmxz}}u`SRMT]fvy~|wy{}rw}yrlg\_[[b\`ccokgjfou}xikd^felmfnlkofhhcjkt|nh]U^cq}}z{sbURPZ_amv}{xx}zst{wmhdb\UZ]_chpndcfluwmfgfeedjkjproh_cgfnv|}qcaZXgqz|z}y|ojYRXW\]au{xzxxqy}plla]SR^]eljne`fhv~xsfikaa^enjrunj__ggqwx|uga_[emrxx}~|}~tld[XVVZ_j{~{uvw~}}}|}uldZURTbikkhjeajs{|tskgd[\_essqpgd^^mqrvu}~wnc_fadmoxy}z~zpkk[TUS]bqxx|tx|x{{qg_UUR[ihkjfjgiqr{}|yxsd`]VY_lvqplfecirruuvsjeed`bioy|z{wtmeYPQUaoz~~ywsrxx}}o_ZTX\^hjfhgopjpty|~}rl]VXV_hlvoijfkjisrqw{}zpnfad]alu{|~{||{ohaPNU_nszyvrjqz~}uc[YZ][afehlptnlnr~~vm`TV[^chopjkmmkhjnou~}}{qib^^_jx|~}zzth^WOWahsx|{~}snmn|ysk^`\Z]X_ghqpqrgir|}xiaXU^]`ffopnrlkhbjqwrj`Y`blx|~y}{te][VZ_eoq{|}yqoqt|}wtpg`[YWU\hoponmght|sjb^Z[^^afowsolfb`guz~}te_[blmv|{|yzng^`]W^ahqy}wsryuy{{ywxh^YRUV^nnomhkilz~ytlidY[[[civ{qme_`_kuvzz{oe`aknnuvz{|}|{~}vnhebZVY_ity{{zvw}}~|ueXTQU\bkkhiglpqx}ywtjbVV\]itxzmfc]beirqtzzvpfigkoksxz~}~vy{}y}wrpeaXQZ`lz}~~zyst~zsbWUS[^^fffiirrotx}uh^SU\cotvvlfcbeccjmsz{vsnkggkmr{|wwwx~}~z{~xpe[XTXenwz~w}ysut}zohYV[Z]Z\ffjporlmy~~vg]XR^hkrpsrggfbb\cmp}vzsjfbjpr{qtwx||{pdYZX[glrvyx{}wsuu}ypka\\YYXZcjoqnmlltti_ZW[dhjnqtqlhb]\_it|yukbbfpy|}xupv~}~}rf_Y^aailqy}y~vury{~{vmmdZYTVY\kqoohlnpvld[]YZbckqt{sid\]^du{~{rmbacl{|||wwtu|xtjc^_dabglu}ztuxzxz~|yvrmaWTSW^emomjimu{she_ZUT]dkv{zrf`^_gmv|}wjfaakqy|xyxx{x{~{~xooide`cc`iq|yvytvsrz{|yni]RTV_femnillv||~rleZVOP]gtzyxlbabilmwz}{lcddhlrzxw{}~zvyy{~|wolnlgb`aaep|}wuqoos~{ti`XSZ_cfehkkquy~}sf^RMLTfpvzuqjdhijmlu|ypfdifinpyz}~|vpw{~zsrnpqhd][cfq|xtnlps~|uh`]X\^`c`ckotuv{y}xi]QLOUcmsvrrpkjhhigo|{smhgffhmwysns~|vssurpj`[[cnry{omlry{|qiaba[^^]`bmurtrtz}}i[TOTW^loqtsvogfacgnxujeeago{{rpntywvyypnf]\^iqqvy|yporxyy|wpkgfa[YX[afosqons}}~yi\WVWW\fmrwywnd`^bjr}~tiaabjwzvqqtv~{|{xngd]`eionpw{zsxyyzuyxrqid_UVY]ghlojlnw}}wia_WVUYhpy}wrg]]^fnpy~{qc_chu||wusvtsy|zsifdceegjjow|{xvvuy|yrh`[VW^dgffijls|yrf_VQTZhw||tke\]cgkkp{yme_got||~wxwsqlr{~}~urkfkgdcaimq}~zvrww|ype\\X[bbdaahjpwzxh]SPUZjxzyqkhabedfflyyohciorvz~~|xqlinz|zvrplkha_ahpv|vstu{~{ne_\`_`a^_`doruwy{lZTQT^gsxtsmjkdca_eiu~smjejmpy}~xoikkw~yxturif_\afrzy||~xrsv}{yphc_dc^]\_bensqrt}|}qaVRT\agpstrppja^^bir{unfaelt~xpkimt~|wyytla^]^hqx{xz~|uqx{|ztrlffdcaZ\^aiknqmrx}||sj^UWWZ_cotvysoh__bhtx}qh`[dnysolntu}|{|upe[\_fnry{x}|zwvxwwtoomkgba^Z]ciljkmmt~{|yoh`YUTV[cpz|yrlf`cjqvzwk`ZZgv|sqsrtuz{xpg`Z`fipsw|}{~{turqqnrohd^]_^eiikfgmr~~|{umj_UROU_jy}zukhgflorvw{o`ZZapz|xvrqqr}~wpgc``dgjns|}wqoortspjb]\aefhhfddlu|~zumdWMMR^kt|zrmjlnlnopu}}mc\_cjw{~~vsmlqyuokgga`edjs|}wnnquxrnh^]^diffdadfmvz~}zneWLMS`jpwvqomppkkjlt~}ogcbbfpw}unikr|}uppmha\^bjvzupqwyxrid__dfhf``aekmswz{|}zm`WOQZ`imptqrsonhdhlwsngaablxqkjnx~~yvume]V\doz~~yywz|yvledcefdd_]`djjjoryz{tg_ZVY\afjpuwupkgbcirzyoe_^cpwnkosx{zti_YW`lsy{}}|||uqkfihec_``^ehggcjs||{~zrhba[XZ]ekr{xsleecfnrw{zmb\`fu|xpprqtv{zqe^ZZdlpuv{~~{xtrnkkgb_]`cegfcaaht}zsnid[UW]fovzvmhdehjmopv{jb]aku}{xrpnntzznh`^_ahkovz~~|yvwtuoif^]^`hhdc^_afv}~|wtlc[SU\fruvtliginljklt~}ld`dlqyunkkrz{qkfca]`flu}~~{wxyxrg`^]_eijd_^^bhq{~~|~~wocZVS[fnsqookloooigkq}uheeglowzojko{~xpmg`\X_jr||zz}zri^]^aghhf__`cjlr{~{}}{qf[WVZcimolnpqrpnkgjq{}tkfcchny}umkpx~|ztlc[WXap{|y{~{ukc\\ceffdc`afhjkoy}zz|~{th_YX]]bgilmqwusplmlq{}vib_^fo}yuprxz}qg]UW\iv}~{{|{vmea^beddbbefhihhhp~z{~ysjb^\]]]`cioswwsolmruz|qc[[amy{xxwwyz|}xj^XW]eoy|~~||}tniedaab_achmjhgceky~xokfca]][Yahqvuurlmouzy|~~teZ[`mw~|~{vvux~yk`[[_cirw}}{tpnkga]]]`fkolgcadmx|vollhb]YXY`krvtpnmntwzzxzqf]]eltz}}vqsw~unda_^acjt}{zxttnf_XY]bjlljb`agqx}wtqopib[UX]eorspklnswvxvsy|picdgkruz~yrot{{vqke_\\_hv}{|~{umbZVYbgjjgeaafkpv~}zwwwsof]XW]ejopmkinsstrqrs|{rojfgiov|wsqqz}~|zwlb[VZ`m|}~{rf]ZX`ffgdcdcfiimq||||~yqja\[]eijlkklmrronlosx~yskddhpz{utux{}|{z}xk^WV[epz}xyznf_]^`cbabcgiffehp{~}wleb^acfhgilmqpoojjlpx{|siadjt|zwx{yxxwyzwi\XX_hmty|xuxyoidb`]]^`cgkkebafp||sjedcdccdehmrsqkiijntz|{~~qhcclv~}{|||yttvy~uka[]`eilt{{xvx|tnkd_YW\_eklke_`do}zrmgghda^_fipuspheikqwxzxzrjfelty|{|{xssw{wohb```benyzy{~|vpg^XTW_fkkhea`enxysnligc^[]clrusnifgntwxwx{umhjlosuxz}}wuuw~~{ztole_]\ahs~~vkaWUU[fijieedfmryzvrpnfa\WY^hrssojiinuvwww|yrmlmkmorx}~yvx{~yx{ywsmc\Y\do{~{{|}qd[UV[`fhfffillorw~~ywvpi`YWW]fmrrnlkmpquutw{}vsplkhiov~yxy|xx{|{||}xph\X[bnw~|}ww|vh^ZY\]`cbdhlpnmnpy|yrj`VUX]dinpmnoqrqqqrw}~|xrlhffmw|yz}}zxy~~zy}zqg^Y\dlsx}}{zvv}~ulb_^\\[\aekopojinw{sh^XV[_bfhlopttqokmrxzsleeioy~zz|~zzz|{z{zpfa^`eiorv{{yxz{upid_ZXVYahnonkghoz{pf_[[]^_aelsvvsnjgks{zphegouz}|zx{}|z{}~~ungedcdfinu|~|}}|xvoe^VSUZekmmighjt}wngba][\[`hqyyuphghnw|uliiouvxxxz{}zy|}}{y~ysokjfa`agoyymaXSTY`fihfehlqw}~uplhb[WY]epwzvojfhlqvwx|ysnoqsvtuwy~~zwxz|vux|{yvuoic\]ait{~{wj]WUX^`ccceglqrux}yvqg_WTY`ktwwrljilmmpqu|~xvttsrrruz~{xvvz}wstz~z{}|wof_[]emuy{~z{~{{ti_ZZ[]\\`bfloqoou}zqf[VU[elrtssonnmkhiot}zzxtqnosw~}wuvx~zutu}}||~wnd`^`gmqstz~wy}|}~tme`^[YWW]cinnnmmt}|pe[WW\dimoruttrmhefmv~|ysnlnt{zvtw|~{wvy|}~wnfbbegklmqw}zy|~~xqmg`[VTVZckmmkjnr}sg_ZZ[]bejpuzysngdfkv~xrkjms}~xvuw}~|||{}|||woiffihghiov~}}~}{xtoe\URTYaikkiims|xmd`][YY]clu|}xpiefls{xojhjqx~|ywxz|~~||}}zx|{tpljkjigdfks}~|y{||{tlbWSTY`cghgilt|{tlfb[WST\fr{}|ulijnsw|zohgiouy|zxz{~}{{zz~zuv~~ysooonlhfceks|{wuw}~xndZUVZ^abcehnu{}xtne]UOOVcox|zvqmoqstvzyoigknruvxz|~zvwzytu|}vrpqsplgccfluz~yttw}yod^ZZ\]^]^diqvy|}}{th]SMOUbmtxwvussrqppuyqlkmnnprw}|wst{yux~{ustutpjdadjpuxyz|xtv{~vngca^\ZXY\clruwx{~vi[SORY`joruwzyurnkntztrnlkjlrz~xssv~}zy|zxxyvrmecchnprssv{}yy|~zsojhe^ZVTX^gnpqru|~tf[UTWZ^ekqw|~zsmiiow~zunighnwyusvz~~~~~}~}~}ztmhdeilonlnqx|yxuspjcZTTV]cgkklpwyof^\YXY[clu~|ulhgksy|sjegmvzwvxzy{{{~}zz}|vnihhklkkijnt|~~|yy{zwpg^WTV[`bcdglt~~wpjd_YUUZer{~xqkikptvzyohejsz~{{{zxutw{zux~yrmjlmkjgfhkrwz{yz|~{zz|~wnc]YX[]^]\agoy~yuqg^VQU\hu{}ztqnmooppt~wmiinuy|}~~ytpqw}}wtxwrpnonifdfjotwvuuw|~zz}~uld_^]]\YXZ_hqx}{ui]TQT]hqxxwvttrpmklr|xplmnqtv{~wpopw{xvz}xutsokfbdgmstusrtw~~|||tlgddb^ZVVY^hptx{wl_VSU\djptvxyzwplhjq{|uqolkmpv~xqoqw~|z{}{yxuoidbchnstrpqsy~~~xsokjhe`YUUX_flprv}~znc[VWZ^bgmty~~{umjkpz{wqkgeiq{zursx{}}}}~~|wqibachorsrpquz~~{vqponlgb\VVY_dgjmqy|rha\ZYXZ]dnx~yrmmrx}uld`bjvywxyyyz{}~zric`cimpqqqrw}}~~ytqprrnib\YX\`bddfmu{tmgd_YVTW^iv~ytqquy|ync^`gr|}|zxvux}{|{ridcdhklmmquz~~|z}}wrrsttoib]\]_```_bju~ytqnicZSPS\hs{~|xvvwxyz{zmc_ahox|{vrrv~|{}{qkgggghghlry}~}zxy}{utuwvrmfa_`cb`^[\ajvyxwvsldZQPT]hpwyxxy{|ywvv|zmfceilrw{yrprx}z}zrnlkgdccgnv|~{xvw{{xyzyuoidbceda\YY]dmu|{xz|ztkaXRSYahmqtwz}~|wrqu~zpkighimu}|tpqv|}|}{vtpjd_^cjrxzyutux}|xqjgfgheb]XX[ahlrw}|{|zqg^YXZ^bfinu{{wqory}vqkfdfnzwssvyzz{{||vne^\`hotutsrtx{{zy|zrmjjkjga\YX[`dfhmv~}ulea`^]^^biqz{uqpqw|zqhbcit}xvwwvutx{~tja^^dkoqpqsuxzyvstz~vronomhc][\]`aaacjv~xrmigd`[Z\cku|~{vrqsuxz|{nfbemw~{xtqprx~}qhbacfikknqvzzyuqps{{wtsroke`^^`b`_\\ajwyuspokf`YX\cnuyzwutuxyxwx}{ngdgou{~wqmnry}|}rkfeeddegmsy|zuqnpt|}zyxtnhb_`bdd`\Y[`jv}xuuusnf^YW\dmrtuuuwz|{wtu{}rjhjmpsx}xqnotz~}z{~wpmjeb^`eltz|{uqprw{}}{umea`befda[Y[`iqy{xvvxunf]ZY]dimopsvz~}{wsuzwqmkjjlpx{spquz{{zy{|xtng`\[ajsz|{xttvy||~}vnfaadfgea][\aglqw~}yxyxvpg_[[^adgikou|}yww{|wsnifdhpzyuuxyyxvxz~{tkb[Z]fpvzzxxx{}|{z|xogcbegfd`^^`cgilov|{{zuoga^^`aabcgmt|}zy{~yric`dlw}|{{ywutv{xmc\Z^elruvxz~}ywxxpifffeda__adfggfiq|~ztlfbabaa_]_eluz}}zyz~}tja_clv}}ytrsv|znd^]`ehmpsx}{vtxxqmkigc`]^aehhecbfp|yqjfeeeb_\[_fouxyxwy}~sibafmty|}vqqsy}xnfbbbcdfkqy}wttyyurpke_\\_ehigb_bhs~~unjijie`[Z]ckqttstx|{qjggknrux}yrqsw{{{{{~vplhea_^bjt}~xttw|~{wqha[[_dhheb__emu}zsponlhb\Z]cimooopu{}~xronlllnryzustwyxvux}}xvpib[Z]dox|}zwuuxy{}vlc]\_dffc`_`djoty~yvusoic^]_ejklkkosy~~}zz}{xuqlijnv~|xwwxwurqsz~ype]XZ`iqvwvuvxxxvuw~sib`acdc`__bfhjknu~}|xskda_bfijhhilrwz{yxx{}wpigjqz~|{|zwsonpu~ynb\Z]dimpqtvy{yuqot|qkfedb`^]_bgihggktxphcbcfhggefjpuwwvuvzvnihlt{|upmnqw}xmd_^acfgjotz}}yrnnt|uolhd_[[]afiifcdiswngddggfdcehmsuusqsv|unjjouy{{|~|upnptxz|~ypiecbaabfmu|~ysopu~{vrld]XY]chjhebcir}xoigggfca`ciostrporx~~uomnpsuuvy}|vrqsvwwvx~ztolhd`]]bjt|~zvssw|}xof]XX\cghgdcejryzrmjigc`]]ahorrqnnqw~~vqoprqppqu{~ywvxywurrx{xusoha[Z_fpy}}{yyy{}~~ti_YX[`dfeddglquy}wsplga\YZ_gmqqomnrw||wsrsrpmlnrz~{z|}{xsppt~}{z{xskb[Z^fnuyzz{}~}|wkb\[]`ababeiorsuw}}yuph`YWZ_glnnmmoty|}~~zvvvurokilrz}}~}wrnou|}z{}~{ulc]\`flpsux}~zy}wmea`__]\^agmqrqqt{yqg^XW[afijjlnswyyxy|~zxyyvqlijnu{~}|}|upoqw||{~{tjd``cfijlqx}wx}~vpkgd_\XX]cjoppnnt}{pe]Z[^bdefinswxwtsu|}}}ytnjjntx{{yy{~xsrsuxz|~~xpjfeeddcejr{}xvy}xuqkd\WUX_glmlklowxme`___``aflswxuqoqv|vpllpuwxwvwz~~xvuuwvuuw~ysomliea^_dlu|~}zwwy~~{si_WUW]dhihhjov}tmhfc`]\^dlsxwtomot{~wrpqtwxwttvz{xwxyxuqoqx~zwutrnh`\\`hpuxwvvwz}~{pd\XX\`bccehnuy|}vrnhb\X[aiqvvsonosx{}~ztstwxxvssvz~}zxy{}{wqllr{{yyyxtld][^djoqrsux{}|{|xmc^\]^^]^`elrvwxz}wpg^XX]emrtsqpqsvwvvz~xuvxyxussuy~{xxy|~{tnlmt}}zz|}ysjc_^bfhjjlqv{}}zwxwohcb_\ZX[`gosttuy{qf]YY^fknopqsuwvtrrx~zxyzxtrqtx}~{wvx|~ysnmpu{}|}~xqidccefeefjpx}~{xvxysnjf`ZVUY`inqqqsxrg^Z[_bfhjnrwzyvqoqv}|{xuqopu{~~|xvvy~}xtrqtuwy||wpkhghgecacipx}}{wwy{xuqjaZTTX_glnnosyvkc^]^^_adkry|{vqnpv~|vqmlntz~~{wvwz~}ywwwxwutu{{upmklljfb`bhow{|{yy|}|{{wpf\VUX^chjkmry{rjea^[YY^eox||xsprv}}voiilrx{|yvvx|~~{yy{}|yuqqvysomnoolgb`bhouxyyy{|{|~~zsi_YXZ^acdfkrz~wqlf`YUTYbmvzzwtsuy}~umhhlrvxxwwx{~}zxy{{toot}wqooqrpkfbadjorttuy}~zz}{sib]\]^^^_cjr{{xsjaWRRWaksvwvvvy{||}~tlijnrtttux|}zvvy~ysopv~}vqqrsrnidcehlnooosz}{}yrjeca_]ZY[bktz~~vl`VRSZbinqsuy|}|zxy~|snmnoppprw|~zutv{|vrrty}{vtuusojfdfillkijmt||upmkid^YVW\entxz|ui^WVX]afimsy}|xttx|vsrpnllou|zutuy~{wuuvxyz||{zyuojgfhklkgeeipw|~}|}|xusroiaZUUYahmpsv||pf_\\\]^bhpx~}wrqt{}ysnjjnu}zvuwz{|zxwwxzywuuz|wpjhhkmliebcgmruvvvy|yyyyune\WVY_cfhkpxwojeb^[Y[blv}|wsqsx~xpjhls{|yxyzzxutux{|{vrqu}zrljkmnmidbbejnpooqu|~{{}~{tkb\Z[]_``cjr}~ytpjc\WW\epx|{xutuxz|}umjkpw|~}|||}}{wsqsw{~|xroqwwpmmoomieccfillkikpw~~}yqic`__^\[\ajt~}zumbZUV]fouxwwwyzzywx|tnlnruxyz|ysppsy}~{uqpsz~wrpqpnjfddgjlkhfgjqy~}wpkgfea]YWY`jt{xncZVX^ekortwz}~{wtuzwqopqqrsv|yspqtz}|yussv{~yvtsplgddfjmmjfefkrx|~zupmlkic]WVY`hpwz~{pe]YZ]adglqw}}wtty{vsqolkmrzztrrvy{zwuuwz{||}~{yvpjecdglnnkgeglrvyz{~xspppplf_YWY^ekptx}tjc^]]]]_ckt|zvvz}ytnheflu~}xvvxyywvvwz}}{yx{~yrjdachlonkhhjnsuvvx}}vrpqsrnha[Y[_dgims{}umhdb^[XY]fq{{yy}~wnfbcis|}{z{{yvssvz~~{wvy{ridbdilmliiknqssrru|}urqstsnha^^_bccdgoy|uqmkg`ZUUZdox}~}{|~xneaciqx|}}~|wspqv|}xvv{{qieegijiggjnqsrpnot||vtuvuqkebbcdca__enz~yvusoh`XSU[emtxyz|xmfdfkptwy|{uposx}}yvvy~zqliiigfdehmrtrolkou~}zyywrmheegge`\[^fp{|zzzvpg]WUY_florv{|~vnjjklmosxwqoqvz|zxvwzytqolgc`aekqsrnjilqx}~ytmihijjgb\Z[bjry}~|ulc][\_begkpx}z{~xsqnkiimu}ysqruwvuttw{|yuohb^_djopnkhhjosuwz}uokkmnlhb\Z\aflpt{xphcbaaa`aelu}{yz~~ztmhfjq{|wuvvurpprw|~||}ulc__cilmlihhknoooqwyronppnic_]^adfgjpz{tnkihea^\_fox~~{z{~|rjfgmv~~}|{yupmlouz~~|zz~}ribacfijhghjmnnljjnwxtrsrpkea`acccbbfo{}wsponje_[[`hqx{{zz|{qigipv{~~~yrljlqw{|zxy||rjedfffedfimppmhfgnwzxwurmgdcdffd`^_eo{{vuttqkd]Z\bipsuvx|~{rljlpsuwz~yqlkmrwyxwvy~}unkigda`bfkqrqlgfhnw}zvpjedegihd_]^dnxzwvwvrkc^\^chkmorw}~~|upnnoooqv|{sonpsuuutuzytqmic^\_elrsqmihkpuz~xpiddfikie`]_dksy}yyywrkd_^`ceggimt|yusqnkijow~xtrsttsqqty}{xsld][]ckpsromlnruwy}zqiedgjkiea`afkosw}~|{wrkeaacddcbdiqz|zxtniefkt}{yxxwtpnosz}wne][]cinoonoqsuuttw{qjffhjifcabeikmnqw}woiecdeec`_agpy|vngcdjs{~~~|xrnlntzyoe_]_cgjkknquxwtqpu~{rljjjhfcabfikkjimu}umhfghgd`]]biqw{|}~vmfdgmsx{|}~wpllpvz}}}}yogbabdddeintxyvqnou{uqomiea`aejlkifglv{rljjkjgb^]`flqtuw{|sliilortux}zrmmosvxxxz~uojgfca^_cjqwxuqmmpx~zwsmf`^`eilkhddhpzwqonnlhc_^aejmnnpu|xroooponosz|uqoqsssrsv}{vspkf_[Z^fnsusolloty~yqha_aeijhecdgnuz}wutrnicaadhjkjilqy|xwvurnllqx|wtstsqnlns{}{xri`ZY\cjopnllmprtuxvlebcfhhfdcdhmqsvz}zvpjecdgjkigfhnv|}}}zvpkjnu|}zyxxuqkhinv~zpf^Z[`eijjjknqrqoot}tlgfggfcabeimnooqx|tlfdfikkifdfkrw{||}{tnklqx|~~}||}}ztmhgjpw}yof`^_bcddfinrtroklr}~uoljhea_`chlnmkkow{rjffikkifdeinsvvvx|yrnmptxyyyz}{slhilqvy{|xpidcca`_`djquurmjlr|zvrnhb^\_djmmkiinw|rkhhjkifddfkprrrrv|~xroprtuutux~{tnkloqssux~xrnkhea][]bjquuqnlnt{|xqia\[_ejmljijowuolkjifbacglpqonos{|vsqrttsqpqv}~wsqqrrpoos{~yusqnic]Y[`hpturpprv{~vlc][^chjjiikpv|{tpnkgb_^agloomlmryyurstutqnmot{~zwwwvsolkpx{xwwvsme]YZ`gmqrqqsvz|}~yne_]`cefffhmsx{}~{wrle_\]aglnmkjmry|vssuwvtolknt{~~|z{||ytnjjnv|xwyzzvne^[]afjlmorvz}|{z~zpgcbbcbbbdipuyyyz~}ulc][^chkkjijotz}yutvxxvqmkmrwz|{zz|zrlikpw}~ywy|}{ume`_aceffhmsy}}zww}yqlhgda^]_dlrvwvvxvja]]`eghggimrwyyy{~xvwyywsomnqvxxwvw{}wpllosvy{~|yz}}xqkfeddca`bgov{{yvtxytqnic]Z[`hostsru{rhb`bcdddehnsvvusuz~{{|{xspnptwxvtsty~~xspoprrrsw~}}~ysolkjgc^[\aiqvwussv|}zvof^YY^eknoopsyzpjgfecaachntvtqoqv~}xsqrtxyxurqsx}|wtrssrommqy~ytrqqokd]YZ_flpqpoquz}uja[[^cgiijmryytplgb^^agnsusomot{ytrtw{{yurqtx|~}zwuvwvsojhls~yvuvwuoh`ZZ]bgijjloty|}~|rha__accceiov|~ztld^\^elqsronosx|~|vsuy{|zvsrux{|zwvvxzzwqkghnv{wvxzyume_]^acddeglrwzzyz{qjfdca_^_cjry|}|}vlb]\`gloonnpsvyyyz~yuux{{zvtsuy{{yustx{}{vojhjpv{yxy{{xrkebbbca`_afntxywvxztolie`[Z\cksxyyz}wlc^^bfijklnrvxxvuv|zwxyzxvssuy||zvsruz}~{uplknqtvx|{z{|{wqkhggfd`]\_fntwwuux{xurmf_YX[bjqtvvy~zofaabcddfiouyzwtrt{~{zywtqpqvz}|ytrrvz~~{vrpqrrrqrv}~}}|ytolkkkjf`\[_emsuuuvz|{zxrjaZXZ`glpqsw~ulgeca_^`eltyzxtrtz}zvplloty||xtrsvz||zvuuvwuronqx|wrmllnomhb]\`elprrsv|}|}~|vmd]Z\`ehjlpv~zsnje_ZX[ajsxywtsv{{tnijnty{yvtsuy{{ywuvx{zwrmknv}vpllnqqnhb__bgjlmnqw~|}}wnf`_`bcddhnv~zuog^XUY`iquuttuy~|slijotwxvtuwy{{xusux|~|wpllpw~|tnmnqrpkfbacfhhhhkqx|umhedcb`^`enxzqf\VVZahnpqruy}{qlkmqstsstx{}|xtqrv{~}ytomosx|~zspprrplhedfhhfdbdjrz~xrnlkhd`\[_gqy{pd\YZ^cfikoty~}{z}ysppqqqppsx}}ytpqty||yuqpqtvwwy}yvuusplhfgijifa_`elty{|}yussqnha[Y[bkrwz}wld_^_`abejry~~|xvx~{xvtqnmnsz~ytqquxzyvsrrtvusrsy}|zvpkggillid_\]binrstw}zxxyxslc\Y[`gkorv|}tmhfc`]]`gpx}}zvtv|~ysmkmszzvttvxwurqruwwuqnot}yqkhiknnje`]]aeijkmqx}{|}}xqha]]`cfginv|wsoic]Z\bkty{yvuwzwoklqw}~{xwxyxvrooquxyvqmlpx~vnjjmookfb__bdeddejr{~~|vngcbbcbabfnx|yri`ZY]dmsvvuvx{~unlnsx{{zyz|}{xsnlosxzxtomnsz}uomnoolhdbcdfec``cjs|zsmjhgfb_\^dny|ti_ZZ^ejnprtx|}{{vpoqsuvvwy}}wqmmpuxyvrooqvz|~~vrqppmieddgihea^^cjsz~}wronnlhc][]clv}vkb]]_cfhjnsz|yy~xtrrrqppsx~~wqnnquwvtqqruxyyxz~zvusokfccfjljfa^_cjqvy|zuqpqrpkd^[\bjqw{yogb````achpx}zz~}yvtqmjjnu|ztqqtvvtrqruxzyvtuy|zvqjdbcgkmlhdaaejortw|yspqstrmf`]^bhmqtyzrkhec`][]clv}~|||wqjffjrz~zwvvwvtqpptx{{yursxyqicachlmkgdceilnnoryxsqrturlfb`behjknt}ztpnkf`ZWY`jsz}}|}yqidejqx|}{zyz{yvqnmpuz|{wsrtyzqhcceikjhfegjllkjkpxxttuutojfdegggffjr}~zwvsng^XVZajqvxy|yoheglquwxy{~~ztnlmrw{{xtstw|ypifghihedegkmmjgfipy{yxwtpkhhijjgcacis~}||zvne\XY^dimpsx~wokjlopqruz~vpllpuxxvtsuy|~~}}wqmlkiebacgknmjecdjrz~{vplijlmlic__dlv~{tjb^]_bdfhlsz}wrqponlmqxxrnnpstsrqrvz}}{yz}}xusojd`_afkmlhcabgmsw{yqlkmopnic__cjqv{woheddcbabgnx~~~|yvrmjjowytqqrrpnmnqv{}|ywx|~{ume_^afjljfbacfjlnqv}tonorrojdaadhloqv}zsnlkifa^^bis{~}~}wojils{|xwvusokijouz|{xvw{|sjb`aehiheccdghhghluztqrsspkfddfhiiils~}wtrqokd^\^dmty{{{}~umjkpv{}}|{{|{wqkggkqvyywuvy|ridcdffecbcfijhecdju|wvutqlhfgijjgeejs~{xwwvqjc]\`fmqtvx{}tnlnruwxxz}~xpjghmruvutux}|slhggeca`bfjlkgcacjt|zwsmiggjlmjfccir|{yzzwqjc__bfjlmpu{}vqpprrqrtx~yqkiknqsrqrv{}}}vqnlid`]^bglnlgcbeks{~yrkgfhlnnjfcchpy}||{wqjdabdffggkqz~xutsqollnu||upnopqponqu{}{{~zwtqle_[\aglnmifehlrvz|skfehlonkgdeiouz~~|wqjfdeffdbbfmw~zyxvrmigjqy}xusssqnkkou||z{~~||zvof^[\`fkmkihilorsux~tkggilmliffimqsuw||uojghhhfb__clu}}|}|yslgfipw}~|zyzyvqlijnu{}zz}~~~ypf_\^bfhihhjmqrrppt}tmjjkkjhffhlpqqqrx{smjjkkie`]_dlty}~~yrkgglqvyzzz|~~{unihkpvz{{z{~~|}xoga`acdddehmqsrommr}~vqonlifddhlpqpnmqxxqmmnolhb^^bhmrtwz~wpkjlorstuw|zrkhinruvvwz~}{|}unhfedb`^`djpsrnkjmuzwtojebbflpqoljms|~vqqqqnida`cgjlmosz{uqppqqpoptz|uokkmpqpqsw}}{|ytpnkgb]Z[`gmpplihkqx{tleabfkooljikqx|xvvtojecdgijihimu}ywwwurnlmry|vqooonmkknu}~}|ywvrld]YY^ejlkhggjoswzzpgccgkmljhhkpuy|}zvpkgfhkljgeeiqz~||}}zuoklpw}{wutsrojgfipy}}}}yri`[Z]bfggffhknppqu|wnhghjjigfhlptuvw{zsliiknnkgdcfmtz~~yrmloty||zxxxywrlfceksz~~~~}~zqg`]^`bbbbcgkoonllr|wplkkigddfkpstsrtzxpkiknomieceiotwxz~~}wqoosvxxwvx{}}yqjedgmswyz|~|}xphdbba`^]_cinpolikq|zurokgcabgmrsrposzwollnonkgdehloqrsv|{vrqsuutsrtx}zrkggjmprsuz|{}~xqliheb^ZZ]dkoqnkilr{|xsle`_bhnrrpoos{zrononkhedgjmonmnsz~yuttvvuroorw}{uollmnnmnqw~||~|wspomid]YX\cjopnllntz~wof`^`floponpt{wsqolhdbcgknnljkowzvttvxwtpllpv||wtrrrpmjils|~}}yvtttrnf_YX\bhlnmmnrw{~{qha_afjlllmqw|~yvqkea`chlnmjhiov|wssuxywsnklpv{}|ywwwxvrmhgkrz~yuttvwuog`[[^cgiijlpuz||}}rjdbdfgggimsy~zrjc_`dimmjhgjpw}ytrtxzzvqmlnswxxwvwz|{xqkgglsz~{vstwyyunf`^_acccdglsx{{yy}|sliggfdbcgnv{~}|}|qha`bgjkiggimswy{}~wttwzzwsonpsuvussuy}|voihjoswyz}xuuwz{xrlfdccba^^agouyywuw~ztqnlgb_^biqxzzyz}zngcdfhhgefinrtutvz|xxy{zwtqqsvwvsppsx}}xrmklnpqrtx~|xwy{|ytokiigd`[Z\bjquusrtz|zwrkd^\_fnsvvvw|vnjiihfddfjossqopu}~}~}zwtsuxzyvqnnrw|}{vronoonmlns{||}~|xtpooolg`ZWY_flnnnorxxof_]_ejnpprv{~wspmidbbflqsrolmqy{vtux{}|wroorwzzxurqrrqnjghnw}xtrrttqjb[XY]bfghhkpv{vld`adghijmry}xrkd``dkprqnllou{|wtuy}}ytqqsvxxusrsuvtpjeeiqzytstwxuog`\[]`aaacgmty{{|~tlgeeeecdgmu}|rib`bgmppnlloswz|}zutw{~~{vtsuwxvsqpsvyxtnheflrx|~|vttwyxtmfb````^\]agouxxxy|upmkhea_afow}}rhbadhkllklnrvwwwx}yvvy||zwuuwyzxtpnpty{ytnihjnqtvx||wuvxyvqlhfeec_[YZ`hotvvvy}xuspjd_\_enuz|}}tjedeghgghlquwwtsu{|yyzzxuttvz|{wrnnquy{xtollnppppsx}yxxywtokjjjie`ZXZ_gnrttuy}{zxtme_\]ckrvxy|ypjgfecabdjqvxwtrty|{zwtpoquz||wronquyywtqpqsrqnlns{|{{yvqljkmnmib\Y[`fkopruz}|}|xqha]^cimpsv{~wqmie`]]ahpuwusrtz{vpllouz|{wrppsvxwurrtvwuqljkqy~ztnjjlopnic^\^bfijknt{}~yqicabegikmrz}xsme]YZ_gostsrsv|}unjkouyzxurrtvwvsqqswzzvpkilryzrljknqqmhb``bdeeeglt}~xpjfeffedeiq{~wmc\Y[ahmpppruz~|smkmruwvustwyyvronqvz{ytnkkotx{|~xqllnppnjfcceedb``emv~{upmlkieb`bis}wlb\\_dgijlotz}~}|~zroorttsrsvy|{wrnlotxzxtpmmpsuuvx|~vqpqqpmifeghhea]\_fow|~|wsrrqmha^^dlu}tjcaaccdegmsy}}zxy~zvuutrpprw|~}ysnmosvwurpoprssqqsy|xwvtqlhfgjkjf`[Y\biosux}}ywwxvqib]^bipux{zqligdb``dksy|zwuv{}zvrnorx~~ytpoqstsqonpsutqnmou~~|xrlhgilnlg`[Y\`ehjlpw{z|}{umea`bgjmpsy{vrnhb^\`govyxvtuy~zsnmqw}{vsrstsqnlmptvurmklry}unihjmomhb^\]`bbbdhoy~~yrkfdefgghkq{|wog_[]bjqtuttuy}xqnoty}}{xwwxwuqljjnswwsokkntz|smjkmonjea``bb`^]`fpz|vpljiigebchq{yoe^\_djnppruy}~}wqpruxyxwxy||zuojikpuwuqnlnrvyz{}|tomnonjgdcdeec_[[^fpyytpoonlhc_`foz|qga_adghjlpu{~|{~ytstuutstw{{unjjmqtuspnoruvvuvy~}vsqpokgdcehihd_[Z_fnuz~}wrqqsroic_`env}~tkeccccbcfmu|~{{~|xvutqnmot{}wqmlorssqooruwwurqtzzxvsojebbeilkga]]`flqux}|uqprutqkeaaekrwz~wpkhfc`]]ahrz~||}{xtnjikry~}xsqqrsrpnnptxzxtqpry~}{wqic`bfjlkgb``cgkmory{tqqtvuqkfcdhloqtxxtqnjd^ZY^fpx|}||~{tmgfjqw||zwuuvvtplklqvzzwsppsy~yqhbacgjkieccegiiijnw{ustvvsnjggiklllnu~zxvsld]XY_gotwxz}|skghlrvxxwwxzzxrmiimsxzxtqqsw{}}}}yphddfhigecdgijifdfmw}xwwvsokijlmljhhlu~~}ztlb\Z\bgloqtzzrlkmprsstvz}~|vojhkpuwvsrrtxz{zxy}vojiihgdbbdhkkidaafoy~|xsnkjlopnjfdgnxzrib^_adfginu}xsqqqqooptz~xqkiknqrqpprvy{zwuv{{urpnkfb__chkkhc__cjqx}|unllorrojecels{~vnheeedbacir{~{yxuqnlnszyrnlmoonllnrw{{xusu{|zxtnga^_chkjfa^^bfkorwwpmnqtsojfdfkptw{ysomlie`^_emv}~~{tnklqx~~zvrqqpnjhgkpvzzxtsty{tkc__bgihea_`beghilt~uqqstspkhghkmooqv|xutsoib]]ahpwz{{|yrllouz}|zxwwwupjfdgmsxywtstx}|ricacegfdaabdffdcdis~wuuuspliikmnmlknu{zzyvpha]^ciostvy~xqnorvxxwwxz|zvnhddhnsuusstx|~}|}{rjfeffdb``bfhhfb`bhs|zxuqliilopolihlt~||}|wpga_aehklnrxysqrstsssuy}}woiffjnqrqqsw{~~|yx{{toljhea^]_dikjfa_bir{~ztnihjnqrokhhlt|~|wohdcdefffinwzvuutromnry~yrljjlnonmosx}~zwvy~|wtqokf`\[_djlkgcbdjpw|}umhfimqrpliimsy|vpjgfggecacis|{yyxuqlijov|{uqoooomjjlrx~}zvux}~{yxwtog`[[^dikjheehlqtwzvmhgimppnkjkotx{}{tokjjjhd`^`gqz}||}{wpjginu{}|yvuuusokggkry}~|xvwz}}zzz|{xqh`\\_dghgffilpqqru|wniikmnljikosvwwx{yrnmnnmhc^]`hpw|~~}~}voihkpuxxxwxyzytnheglsy{zxwx{~~{xxz}~xpga^_bddccehmpqpmnr{xqnmmljhginruvtstz~vqopqplfa^_ciosvy~}{tnklorssstwz}}yrkffiotvvvwy}~zwvy}}vnhdccca_^afkpqokjls}zvsqmhedgmruurppu}{usstsoidaadhjlmpv~xsqqrrqpoptz~|unihjmoqqqtx~zwvy}ysnkjhd`\Z]binomiginw|wpidcflqsspnosz{xxxuqkgffhjihhkqz{xwxwurnmotz~{vpmlmmmlkmqw~~zxx{~zvusqnha\Z]bgkkiggimsx|~vnhginqrqonpty}}{wrnkkmnmkhgiow~}}}~}ytpnoty|}zwsrqqolihinu{}{z{}}zyyyxsme`]^bfhhgggjnqtux}~uolmoqqpnoquy{}}ztpooqrqmjhimsy}~}xspqtwzzywvvvusokhhkqw{|{zz|~~|zz{||yskebaceffeegknpqqru|~wsrrrponoquxzzzz~~xsqqstsqmjjlquxz|~|wtstvxxwvvwyzxtokijnruwxyz|~|zy{}~|xrlhffffeddfjnqqqpqu|{ywuromnptxzzyxz~}wtstuuspnmnpsuvwy}~{wvvwwwvuuvy{{yupmlmprttuwz}}zyy{}~{wsolkjifdcdhlprrqprv|}{wsonoruxyyxxz~~yvuvvtrpooqstuuux|}zyxyyxvtstwz||zvspppqrrrsuy}~|zyz{}|zwtrponkgedeimprrqqsw|~zuqopruxyxxy{|yxwvtrppqstutstvz~{zyyzzxvtstvy{|zxvutttsqpqtx|~}{zyz{{zxvutssqnjgefimpqrrsux||wsqqsuwwwxy||zyvsqpqrtvutttvz|zyyz{zxvtsuwy{{zxxwwwusqpqswz|}|{{{||{ywvuuvurokihikmopqrtwz|~~yvttuvvvvxz}~zwtqqrtvvvttuw{~~{zzz{{zxvtuvxyyyxxyzzyvtqqrtwyz{z{||}|zxvvvwwvspmkkkmnnoprux{|}~{ywwvvuuvx{~{wsrrtuvvuuuwy|~}{z{||{zxwvwxyyxxxyz{{yvtrrsuwxxyz{}~}{ywvwxxwuspnmmnmmmnpsvyz{{}}|zywuuuwz}~zwutuvvvuuvwy{|}~}||}}|zyxxyyyxwwxy{||zxvtttuuvvwy{}~~}{yxxyyxwtrqpponmllnqtwxyy{}~|ywuuvy{}}~~}zxwwwvvuvwxz{{{|}~~~}|{zzzz{zyxwwyz{{zxwvuuuuttuwz|~~}{zzzzzywutssrqomllnpstvvwy{~~{xwvwyz{||}}{zyxwvvvxyz{{z{|~~}|{{|||{yxxxyz{{zyxwwwvutstvy{}}}||||{{yxvvuutsqommnpqsstuwz|~{yxxyzzz{|~~}{zxwvwxy{{{zz{}}|||}}}|{yyyzz{zzyyyyxwvtstuwz{||||}}}|zxwwwwvusqonoppqrrtvxz|~~|{zzzyyz{}~|zxwwxyz{{z{{}~}||}}}}{zzzz{{zzyyzzzywvttuvxyzz{|}~~}{zyxxxxvusqpppqqqqrtvy{|}~~}|{zyyz|~|zxxxyzzzz{{|}~}}}}}}|{{{{{{zzyyzz{zyxvuuvvwxxyz|}~}|{zyyyyxvutsrrrqqqqrtwxz{|}}|zyyz|}}{yyyyzzzz{|}~~}}}}|{{{{||{zzyzz{{{zxwwvwwwwwxz|}~}|{zzzyyxwuutttsrrqrsuwxzz|}~|{zz{|}~~|{zzyyyyz{|}~~~}}|{{{{{||{{zzz{{{{zyyxxxwwwwxy{|}}|{{zzzyxwvuuuutssrstuwxyz{}~|{{{|}}~}|{{zyyyz{|}~~}|{zz{{||{{zz{{{{{zzzyyyxwvwwyz{||||{{{zyxwwvvvvutssstuvwxy{|~~}|||||}}~~}|{zyyyz{|}~~~~}|{z{{{|{{{{{{{{{{zzzzzyxwwwwxzz{{{||||{zxxwwwwvuutttuvvwxyz|~~}}||||}~~}|{zyyz{|}~~~}|{{{{{{{{{{|||{{{{{{{zzxxwwxyyzz{{||||{zyxxxxwvvuuuuvvvwxy{|~~~}}||}~~|{zzz{||}}~~}|||||{{{{{||||{{{{{{{zyyxxxxyyyz{||}||{zyyyxxwvvvvvvvvvwxy{|}~~}|||}~|{{{{|||}}~~}}}|||{{|||}||{{{{||{{zyyyyyxyyyz{|}}||{zzzyxxwwvvvvvvvvwxy{|}~~}}}}~}}|||||||}~~~~}||||||}}}|||||||{{zzyyyyxxxyz{|}}||||{{zyxxwwwwwvvvvwxxyz{|~~}}}~~~}}|||||}~~~}||||}}}}|||||||{{{zzzzyyxxyz{{||||||||{zyxxxxwwwvvvwwxxyz{|~~~~~~~~~}}|||}}~~~}|||}}}}}||}}}||{{{{{zzyyxyyz{{|||}}}||{zyyyxxxwwwwwwxxxyz{|~~~~~~~}}|||}~~~}}}}}}}}}}}}}}|||{{{{zzyyyyyzz{{||}}}}|{{zzyyxxxwwwxxxxxyz{|}~~~~~~}}}}}~~~~}}}}}|||}}}}}}||||||{{zzyyyzzzz{{|}}}}|{{{zzyyxxxxxxxxxxyz{|}~~~~~~}}}}}}~~~~}}}|||||}}}}}}||||||{{zzzzzzzzz{||}}}|||{{zzyyxxxxxxxxyyz{||}~~~~~~}}}}}~~~~~}}}||||||}}}}}}}}}||{{{zzzzzzzz{{||}|||||{{zzyyyyyyyyyyzz{{|}~~~~~}}}~~~~~}}}||||}}}}}}}}}}}||{{{{zzzzzz{{|||||||||{{zzyyyyyyyyyzz{{||}~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m27.bin b/applications/SprPoP/dist/MUS/m27.bin deleted file mode 100644 index 2db2ee0..0000000 --- a/applications/SprPoP/dist/MUS/m27.bin +++ /dev/null @@ -1 +0,0 @@ -|vplnqvz|~~~ztrrrrrsuwxx}~}|zxvwytsvwwvuuvxvsxxwy{|}~}vvussrrsvzz}|yvvuutqqtqpssstsstvvswxuwxxwuutuvrptrppprssrpttqsuuvvwxy}{}~{zyyzywtuvqqsrrrstvz{|~~{zxvvwwtvzxwyyxwvutuspssrrsuxyyxz}yx{yyyyxz}}|}|zxwvqqrpooqrtsrsvsqttsstuvz{{~zy{{zy{}~~{|}|{{zz|}{~}~}~}{zywwxwux{yxzzxvtsrsoloonmnpstssvxssvuvvvwy|{|}~}}|~~}|{{{}|{{~~}||~~{yzyxvuuturrwyvvxwvtsrrtnmpponpruutuxytuwwxwxx{|z~~~~}~~||||}{|{{~}|{|}~}|}wyxxwuutttqtyxvwwwvstsurmqrqpqsuvvvw{wuxxyxxyz|{||~~~}|||}|{|z}}|{||~~}|}yxyxxuuutursxywwwwwuutuuoqssrrtuvwww{zuxxyyxzz||{}~~~|}|}|z~}y}}|{||}~}|}zwyxyvvvuursxzxxxwxuuuvwprttssuvwwwx{{vxyzzxzz|{{|~~~|}|}{z|y}}}{{}~}|}~yxyxzvvvuvrtz{yyyxyvwvwwpsuutsuwxwxy}{vyyzzyzz|zz~|}}|}|}zz{z}}}z|}}|~xyyyzwxvwurw{|xyzyxvxvxuqtuvstvxywy{}xvyx{yyzz|y|~~}~}|}|}|y}z|}|{~~|~}x{yzxxwvxsty}|x{zywwxwxrrtvwsvwyxw{}}wxyyzyzy{{x~~}~~}~|}{}|y~z|}}}~||y{yzxxwwxsuz~|y|zyxyywwrstwwtvwzww|}|wyyxyyyx|zy~~~~~}|}||z~{y}{|}}}~|}z{yzyywwytv{|y|{yyyzxwsstwxuwwzxw}~}yyyxyyzx|{x~~~~~~~~{|}|z}|y~~{|}~|~{|yyzzxwzuvz~~y||yyz{xwtstwzvxwyyv|~}zyzwxzzx{|y}~}}{{|}z{}y|}z~~}}}}}zx{wvz{~}|}}y{|wy|}z}{z|}|yxwww}~|~~}~}y|yvz~|}~{xz{xwzwvz~~y||y{}}ywvuux{xxwxxty|zywwssvvsuxtw|~~~|}~|{xuwxutxvuz}~||{~|z|~}xwvuuy{xxwxvv{||zzxuvyxvzzx|}{zyx{~{zyyxuz|{zywstwwsvxux~|{{z|~}|||y}~}yx{|yz}z{~|~|{}}|{{}|xwvuv{zxwwxtw{{zyyuswxvuzwx}~}|}~{}{y|~{z~|{~}}{|{}~yz~}z}zy|~|wvvuu{{xwvxuv||{zzvtwzwuzyx}~}}~|}}z}|z}|~~}|z{z}~yz}|y}zy|~|wvvuu{{xxwyuw}||{{wtxzwv{yx}~~~~|~|z}|{||~~~}|z|z~}yz~{y}yy|{vvwvw}{yxxyuy~|||{vuy{ww|xy~~}{z{z}{~~~}~}~|z~|{{y|y||xz~~yvwwuz~zyxywu|}}}}zuw{zvz{x|~~~~}}|~z|~z~{~~~}{{{{|zz}~x}{x{~xwwwv|}zyxzwv~}}}}yux|yv|zx}~~~~~~~}}}}y}}z}{~~~|{z{{~yz~}x}zx|}xwxww}~zyxzww~~}}}xux|yv|zx}~~}~~~~}~~}~}|~y|~z~{~~~}{{{|}zz}x}{x{xwxxw}zzxzxv~~}~~zuw|zv{{x|~~~~~~}~~|~~{y{y~z}~}~}}||}z~|}y|yz}xz~{wxywz|zyzzv|~~}vv{|wy}xz}~~}~~~~~}}}~~{~{y}{z{{}}~|zz||z{~}x~zx|xwyyx{zy{xw~zux}{v|{x|}}}~~~~}|}|~zyzz|z|}}}|~{z}{z|{y~xy~}wxzyy~{zz{wzxuy}yw}yx~}}~|}~}|}|~}~}{~x{~y~}y~}}~|~|~~|~|{~z}|z{}z~}|}|x~wx~~x|}x{~|~}|~}|~||}z|~z{}{}}||}x~xx}yz~yz|}~|{|~|~~{{{zz|{|}||~y|yw|zy~zz|}~|{|~|}~{{{zz}||}||~z|zw{zy~zy|}~{{|~~|}z|zzz}||}||~z{zw{{x~zy~||~{{|~~}}z|z{z}}|}||~z{zw{{y~zy~||~{{|~~|}z|zzz}||}||~z{zw{{y~zy||~{{|~|}~{{zzz}||}||~z|yw|zy~zy||}{z|~|~~{{{z{~|~}||~y~xw|yz~yz|}}{~{|~|~||z|~z{~|}|}}y~wx~x|}x{}|}||||~||{~y}|z|~{~}|~}y}wy~x}|x|}|}|~{~}}}|z}y~|z|~|~}|~|z|wz}x~{x}||}{~{~}~}|z}y~{z}}|~}|~|{{wz}x~{x}||}{z~}~}}z}y~{{}~|~}|~{{{w{|y~zx~||}{z|~~}}z|z~{z}~}~}|~{{{w{|y~{x~|{}{z~}~}|z}y~{z}~|~}|~|{{wz}x~{x}|{}{~{~}~}|z~y~|z}~|~}|~|z|wz~x~|x|}{}{}{}}}~|z~~y}}z||}|~}z}wyy}}x{}{||{|{}|||y|~z|}}|}~zxx}y{~xz{||z~z|~|~}{{{{{~}~}||~{}yw|{zyx~{{}zz|~}}z|y~{{}~}~}|~|{|w{~y~{x||{}{}{|~}|{}y|}z||}|}~zxy~y|~xz~{||z~y|~|~~{{{{{~~~||{}zw||zyx~~|{}zz~}~}}z}y~|{}}~}|~}{|wz~y~{w||{|{|{|~}~|{~y}}{}}}|~~z~wyy}}xz}{||{|z}~|~||z|{|}}|}zxx}z{~xy~{{|z~y|~|~~{{z{{~}|}{~yx}{{yx~{{|zy|~|}~{{z{{~~~}||}zx||zyx~{{|zy~|~|}z|z{{~~~~}|||zw|}zzx~~{{|zy~}~}}z}z|{~~~~}|||zw|}zzx~~{{|zy~|~|}{|z{{~~~~}||}zx||zyx~{{|zy|~|}{|z{|~~~}}|~yx}|{yx~{{|z~y|~|~~{{{{|~~|}|yx}{|~xy~{{{z}y}~|~|}z|{|}}|}{~xyz}}wz}z{{{{{~}|{~z}}{}}}|~~{}wzy~|w||z|z|z|}~}|{~z~|{}~}|}||w{~z~{w||z|z}z|}~~}}{~z~|{~~}|}}{w{~z~zw}~{z{y}y}}~}}{~z~|{~~~}|}}{x|~z~zw~~{z{y~y~}~}}{}z~|{~~~}|}}{x|~z~zw}~{z{y}z}}~}}{~z~|{~~}|~}{w{~z~{w}~|z{z}z|}~}|{z~}{}~}|~~||w{z~{w||z{z|z|}~~||z}}{}~}|~|}wzz}|wz}z{zz|z~}~|}~{|{}~~|}|xy~{|~xy~zz{y}y}}|~~||{||~~}}}yx}|{~yx~{z{yy~}~}}|~{~||~~}|~}{w{z~{w||z{z|{{~~~~||{}~{}~~|~|~xy{}}wy~~zzzy}y}}|~~}||||~~}}}zx}}{~yx~~{z{y}y}}~}}|{~}|~~}|~}|w{z~{w||z{y{z{~~~~||{}~|}~}|~}}xz{}|wz~zzzz|z}}|}~|||}~~|}}xy~||}xy~zzzy}y}}|~}}|||~~}}}yx}}{~xx~{zzy~y}}|~}}|||~}}}yx}}{~yx~{zzy~~y~}}|}}~{||~}}~zx|~{~yw~~{zzy}y}}}}}}~{}|~}|~zx|~{~yw~~{zzy~~y~}}|}}~|||~}}~zx}}{~yx~{zzy~~y~}}|~}~|||~}}}yx}}{~yx~{zzy~y}}|~}}||}~~|}}yy~||ytosxzuuwz~{wutptvtvusot|{yvu{zyyxvppvttvwtswzvw|yy}~yxvrwwuvuqou~ysrnpusrsrqknsrtuurtxztuyutwzxswupsvvqopqpvvqonqqqyzz{zvsx}}}{{zx|{tqoqppxxxyxsrx}~xz}{{{zvwzztvxsswzwswuruxxtsuvv}|wvvyxy~xv{~|{y~|{~|x{xtvywrqrrrxwrppsrs{{||{uty~|}|~|}|~~~||~~~{w{{~zw}|yyx{}wy}{|}|xx{|vwyttwzwrvtpsvvqprsrxzurruvv}~~}xuy|}z}x{{}{vy}|zvvuu||{|{ztsyxxyyutwyusvtqtxxstvqruxtrsutx}yutvxw|{vx}|~z|zy}}{}wv}{~|vttsw{yzzyvqvxvwxvsuxxsuvrswyvswuruxxtsuvv}}xvvyyzyv{||yyz{|{vw~}z~yutssyzyzzytqwwvwxvsvywsvurtxzvuyutwzxuuwwy}ywx{z|~xw|~}{z|x{z|yuz}{{~wttrtzzyzywrsxwwxxuuxyutwtswzzuxxtvy{wvwxx}|yxz{z|wz|~}y}yy~|{{vv~~}z~zussrxzyzzyurwxwxywtwzxtwwtvz|xw{wvy|zwwyy{~zyy|{~~xx}~}zzzx}}{|wv|~~z}|vttrw{yz{zvrwywxyxuwzyuwxuvz|yw{xvy|{wwyyz{yy|{}yx|}{y{x|}{|wu|~~z}}vttsw{z{{zwswyxyzxvwzzuwxuvz}zx{ywy|{xxzz{{yy||~yx}~}~zy~zy}}{|wv}~{~|vuttx|z{|zwsxzxyzxvx{zuxxvx{}zy|xxz}{xxzz|{yz}|~xx~}}~yz~~~~yz||{vx~}|zvutu{|{||zuuzyyzzxwz|xvzwvy}}y{|xy|}zxyzz}zy{||{wz|~{x}~~}|x|{}yv{|}yvutw|{|}|ytw{yz{zwx{{wxzww{~|y|{xz}}yyz{{}zy||}zw{|~zx~}~}|~~yx{}|z~~|}~|xwuw{zz{{xuz~~}~{z{z{{xxzz|~xv{}|{~yxvx|z{|{yuz~}z{}|xwxxy~zxwzz{}wtx|{xyvrx||yz}vttsv|{|~~|x{}~z~}}xutsu{{{}}{wz~|z|}|~xtv{|xywsv}~|{{wwvx~~~zz~~~~xx}{zztv}}}{y}zutttz}|~~~zy~zy~~|}xx~{yyy}}z~}|~}~}}}}}~~{z}}}}wtw|{wxurw|}z{yvuuw}~~~y{{}~z||zzz|{~}|~~{}~}|~~{{|{}|yy{{}{utx{ywxtsz~}~z}yvvvz~}y}|}z{{z|{|}}}|~|{~}{|~|z{{{}zxz{{}xsvz{xxwsv~|||xwwx~{{|}{|}{{z}{~|~{}}{|~}|{}}zz{{}{yy{z}{uty|yxxtt|~|ywww|}z~~}||{~{{z~~z~|~{}}{|}}{}~{z{{||yy{{}|vtx|zxyut{|zxxw{}z~~~}||{~{{z~~{~|~{}}{|}}{}~{z{{||yy{{}|vuy|zxyuu|}zxxw|}z~~~}|{{~{{z~{~|~{}}{|~}|{}~{{{{}{yz|{{uvz|yyyuv~}~zxxx}|{~~~{}~z}|{z{||}}}|~|{~}{|}{{||~{y{||~xuw||yywty~~|yxwz~z}~~~{~~|z|{z}{~}~}}~{|}~{}|{|||}zz||}}xuy|{yyvu{~{yxx{~z~~~~}{~~{z~|{z}{~}~|}~{|}~|}|{||}}zz||~}wuy|{yyvu{~{yxx{~z~~~~}z~~~{z|{z}{~}~}}~||~~|}}{|||~{z||}~yux||yzwuz~|yxxz{}~~~~~z|~~|y}|{z{||~~}~}~~~~~~~|}}xy}|{{wy|zyy~}{}}~}~~~~}}yy|}{|xy|zzy}~{}}~}~~~~}}{x{~~{|yx}~{zy{|}~~~}~~~~~~|~}yz}~|{{xz|{zz~}|~}~~~~}}zy|~}{|yy}{{z||~~~~~~~~~}}~|y{~}{|zy}~{{z{|}~~~}~~~~|~}yz}~{|{y||{z{}|~}~~~~}}~yy}~|{{y{|{{{~~|~~}~~~~}}zy|~|{|zz}{{{}~|~~}~~~}}{y|~|{|zz}|{{}~|~~~~~~~}}{y|~}{|zz~}|{{}|~~~~~~~}}~{y|~}{|zz~}||{}~|~~~~~~}}~{y|~|{|z{}||{}~|~~~~~~}}{z|}|{|z{}||{}~|~~~~~~}}zz|}|{|z{}|||~~|~~~~~~~}}~}z{}~||{z|}|{|}}~~{{|}~z}{{{~||~}zwwz|~~x{z~|}y{}~}{||}}}~|~|y~{~~}z}|y}yxy~}~~|zyy{|{|{}}{||||}}{yzz|}zv|y}~z|~~|z{}~}|~|}}~~zyy~|}{}}z~u{|}}|{{{{|z{~~}{~z{{|~z}|~{u}~|~|~|{~|zzz{{w}}x~}z{yz~~zvwz||{zsx~~}}}|wx|}~{t{yw~zxyx{zy||zzuwyswystv{|tuww{zvxy~xwyw|wvzz{}xz{swxtvstz~~z}~yzvr||vw~yyyw{xvyxy{uy|x~~xwrt|~~{z|}{}yuv{wwwv|wwzy{|txws{|z{yz}~{vry~~}y}w{{xutzwx{yz{uy|x~|vvux{ysuvtwwx|tu|yyyy}~yx{yy|wxyu}~z|xw}~ttwuxvqy{{ywusy~wv{zz}xw{tz|xywx|y{}zyvpy{z~}}xy~zuxxw{ww{uz~zxuu|}uw{z|}w}~{uy~|x|}zx}}yztt{v|~yyvv~~z{|wxxt}|sszwwzz~xtyyy}yz|w|}z{wv~|wz}{}|v{}ss{xvtqu~~{}ywytvyu}||vv~}~vx{x{zvx{ywtqw|}{}}xxvs{{yzvx~}wz|xzwuztz}}yvz}z~~x|~~~x~zy|xwsv|x{{x{ww}w{|xyvwy}tuxuz}z|tw}~{vy~|x|{z}wxzx~~vt|zsw{vxvs|x{}zzww~zxy~{}}v|usyx{~~~||{xxx}zz{{|}vu}~}x{wy~v|zsy~zxtyz}zu{yyzsw~{y}{}{w~}s|xs}{|xyvy~zxyu{ytuvuxz~zw~~uwz~|}}~vx{|xv~swywvxvwxst~u}yt|zx|}zv~~~xz~}|x|~~xyvmv|}x{xwzvx{qn{yy~|{~|wwz{}z}{|vpz{xwy}xw}vwzqqqszyuruyyy|xsuzy}}xrvw}~|{}wssw|{yzvspvyzwu}vt||{wopwwv~~wy~~vzxzy}~x}~y{{usy~|wwpnyuu{sv||}}|zwxuy~~}y~}~z~|||yxyopx}~}~yuy}|zrrrtzxvvxy|}}}|z}x}wt~}}{|}xw}}zuow|zyvwuxzw|vt}y~~|zwzzwv}}||x|}{yz}|zqvwv{xuz{z~vmuzv{|{~|z}x|~|{wz}{}~{|ztv~u|{ttrsy|wv}~}xtvy{}yxz{xwz|{|xux{}yz~{zuv~|toy{{{qqx{}xu}{}}~{{~~z{yx}zwwywv}~{zx}tu}x{z}squ{~|~|qx{}~w|w~y{~|yzwwz}{wtvxyz|~~|qsxvz{}suyyx~{{~zz}~}w{|{y}~yuyvuzy{x|zv~qtrrz|~|}xvvz|}qvywyz~|z}|}yy~x~ytz{vrwu{upsw~|u{{urrx|tz|z{uy{srxywvr~y}|}|}t|t~xtrvz}{wlpzppzuwuu|}r}}|zz{}s|yv{x||quxz{u|{~|utoo~}s{}xxuxwk{{}|y|zz}zw|xxu~ss|vz}{~xops{~}v}ttqsv{xxz}vk}}{zu|{vwx{zqy|z|tsz~{~{}xqt|tqzuwtu~}rz}{}~xwy}}s{y~xzsuz{|yx~{wuoq|{z}xvtv~|nl||z~~~w~z~|yw~x~w{sy~{tyxqrxz}zoyxsqnv~y~|zv{yow{{}zz{x}z}wu~y}wzvsnvzxv~vpw~zlw|y|zt~{tu|x{}u{~owyt{u}ztwtp{~{~wfkolklfp|}{wyho{tsvkgrssuuu{x~y~zvkdoqpo}~u}whoqmnpjmvvqryju~qq}{wytr{zxsvwkv|tdhsqpqljwuop}tp}v}}}ztwyvxqpzxvuqlzyokuxy|ys}|wxxsstorzx}z}xwwjqurqqmowwxzv|woxxwxvt{{xy|xz~}u~zyvmrwsuvzmw~yoovutokluwyyyyz{{vxz~~{z~||xvv{|}|wv{~{ty|{z|rmwwtx{qu||y|px}vjmtooromwxyx}yo|zot}|||vw~ztw{{~zx}|x|v|~~u{}yy~xtywvvsjowvvx~u~ztsxussolrsvtuxr|{rs{xsttq{}~yy}~}{wx}|yyvv~}~{~ty{wvvor}}zxuszwqoussywnv|{zx|rx|vmotqrwuqz~~~{t}v{||{x|zx}|}~zz}}~{yzt{~||ztz}~|vx}yuwvusojrwwvw~x}~{utxusurltxzv{|v}w|}utyuv}{~x~|zs{|wxztx~y~xw{wuvsq|}zx|zqxyuoqutyzrr|}|y~yu}yusvutxyty{}x}zx~|y{}z~{z{|||w}}~x|x||zzxy}~z}xvzrvwtsrlqxxxyz{~ywzvuwsmv{zx{~yz|{tv{xv~}}{~{y~xy~yvzyt{y~xxyuuxuv~z{z}xr{yurvxyzxsx}|y~ww{zwswvvzzv}|zz{|~yy~}x~}~|~|~~xy~{z}~{{|x{}x}~{|}v|uwztstppxyz{}x|wyxuvuou|zzz{~{yuw|zw~~~~~{~~yy~~{{|xvz{u{|zyzvvzyx|{}zzxs{zwtw|zxyvy}}z~wv}|xuyxw{|xyz{{z|xz|v~}}~|}~xwz||~|x}y|~|z~z|wxzusutqxz}~x}yyyuuusv|z|z}}xwwx{|x|~}}}~{x|~}{ywy|xy~}xyxvy~yw|}}y{vw|zxx~|wy{x|~~|z|tz{wwzyy}|{~zz{~{{{z~~xy~z{|{~|{y~|~}{||wz}|~~~}zz~{~~}}~~~~y{}zxw}wvzyxx}|}zuz{{y|}}{y}~z{}xw{{}~yy{zwsuuvz||}}w~}xyxu{{yz}wy}{z~{|xy|yxzyx{}z|yvxy|wz}yvuvwxxvt{~~~wz|yxyvtvusz{yy|{~zz}yz}{{xsvzvw||}|}~vuzyxz{wz}||~y|~|}~}{~zy|}|~{{~|||tsvux{{{y|wy~}xwzyz}}~}~}~}}|}}~{z}}zzzx~}}~||}~{z||{|{~~|}xwxzwvxzwz~|yuz~{yyzwvxxx|{||~}~}{|~z}}|~z~~y~}~|}}}}{ww||}|}~~}{y{ysuz|{z|x}~}}~~usxyw|}}{{|yzxz|z||}}|~yy|z||z|}{z||yxy{{~zx~}~~yzxxyzywx{yzzxy{}{{{ywy|zz{}~~}~}}~}}~}~z~~}z~}}||}~{zzvw{|}~|~}}{zzwsx}|{{|z~~ytw|zy}~~{||||yz}}{|z~}~{x{}{}~{z~~~|y|{yz|}~~|y|}zxzz{zzyx||z~zw||~~|{{{xx}|w{}~~}~~~}~{|{}|}~~}~|~zyzww{}~{}}|zywuz}||{}|~}xvy}{y}~}{|}}{z{~}|z{~~~~yy|~{~~z{~}|y{{z|~~{||~~~yw{|{z|yy|}|}yy~|}}{{{yz~{v{~~~~~~~~}}~|{{}~~}}}}zyzyw{~~|}~~ywxxy|}}{}}}{xxz|}z{}}}{~~|{{|~~~~|{}~~}{y{~}}~}{~|~}|{y|{|~{|~~~}{xy}|{||{{}~~yz}~|~~|{{{z}|xx}~~}~~{~}|}||}{}}}}zxzzx|~}~~~yvxzz|~}|~~~yx{{{}{z|}}|~~y{|}}~~~~}|}}}|{z{}~~}}~{~~|{y{|}{{}}zyy}}|||}{|}z{~~}|~}{{|{|{yx{~~~}}~}{~{|~|~|||~~|{|||~zy}~||y}}}~}{}~~~|y|~~}~~||~~~z{~~}|x{~}}}||~}z{{|}}}}|}~}{|}~}~{|~}||}~|yzz}~~~||}}~|{}~}}~~|z}~~~|z||}~|||~~~{{|}~{y|}}|y}~}~~{{~~|y{~~~~}{}~zz~~}{x|}~}|}|~{z{}}|~}}|}|{}~~~~{z~}~|||~zy{|}~~~~~||~~~|{|~}}~}z|~~}{{|~~{{|~~~~~~}{{~|z{~~}~}z|}}~}z|~|zz}~~~~||}~{y|~|x{~}~|}}}|z{~~|~}}|}}{}}~{y~~~}||}|~zy{~}~~~}}}|~}{|~~}}{z~~}}|{|~y{}~~~~~}{{}{{}~~~}z{~~}}|{|~~zz{~~~~}|{~~|{{~~~zz~~}~~}|}}}zz|}~~~}|}}||~~~~yz~~}{}}}}yy|~}}~|}|}{z|~~}~z|~~~}|}|||yz~~~~}}{||{}~}~}}z|~|||||~}zz|~~}}}{}~{{|~~~~z{~~~}~}}}}|zz|~~~~~|}||}~~~zz~~}{|}}}zy{~~~}||}|z{~~~~{{~~~}||}|~~zy{~~}}|~}{{~~}~}zz~~~|{|}|~{z{~}~}}||}}z{}~~~|z}~~~}|}}}~{z{}~~~~~}~~{|}~~~~}y{~}{|}}|zz{~~~}||}~}{{~~~~{{~~~~}|{}}~~zzz~~}|}~~{{}~~~|y|~~~{{|}}}{{}~}~|||}{z|~~~~y{~~~~|{}~}}yz|~}~}}}}|z|~~~yz~~}|{|}~zz{}}~}||}{{}~~~~{{~~~}}|{|}~zz{}~~}||~~{{}~~~|y|~~~~|{|}~|{{}~}|||~{z|~~|y|~~~}|{}~~{yz}~~~}}}~~{z}}|y|~~}{||}|z{}~}}}|}|{|~~~~|y}~~}|{|}~{zz}~~~}}|~~{z|~}|y{~~~}}{|}~{z{}~~}||~~|{|~~~|y|~}}|{|~|zz}~~~}|}~{{}}|y|~~~~||}|~{z{~~~}}|~~{{}~~~~zz~~~|{{}}zz{~~~}|}|z{~}~z{~~~~}|||}}zz}~~~}|}|{|~~}y{~~}|{|}~}zz|~}~}|}|{|~~~}z{~~~}}||}}|zz|~~}|}|{|~~~}y{~~}|{|}~}zz|~~~}}|}{z|~~~z{~~~~}|{}|}|zz}~}}|}|z|~~~}y{~~~}|{|}~}zz|~}~}|}|{|~~~z{~~~}}||}}}zz|~~~~|}|{|~~~~zz~~~}{{|}~{z{~~~}||}z{}~{z}~~}}|{}}~{z{~~~}}}~}{{}~~~{y|~~~}|{|}~|z{}~~~~}|~{{|~}y{~~~~}||}}~|zz|~~~~}}|{|~~~zz}~}{{|}~{z{}~~}||~~{z}~~|z|~~~}|{|}~|zz}~}|~{{|~~~y{~~~~|{|}~}{z|~}~}|}|z|~~zz~~~}}{|}}~zz{~~~}}}}{{~~~{z|~~}|{|}~{z{}~~~||~~{{|~~{z}~}}{{|}|zz}~~}}|~~{{}~|y{~~~~}|{|}~|{{}~~}}|~~{z|~|z{~~}|{|~~|zz|~~~}}~~{{|}|y{~~~~|{|}~|zz}~~~}|}{{|~~|y|~~~}|{|}~|zz}~~|vppsuux{}~|ztonnooqvxxz~}yxwyz|zvtvxvuuusqqsvwyzz~~|vqopoprvxwz|xurqrstvsqqsutuutrprtvvvvuvvwxyvpnqrrqpommnnppmmnrsuy|}|}~}zwussrsqmlnpqsx{zz|}|{xtstvvvvtuuuwxytoqtutttsstuvxvttwyz|~~}}}|}}|vrruttsqpnopqspnnpstw{}||~}z{~~~~}|{{|}~{yz{~~~|||~~}~~}}|yvuvxxwvuvvvwwwqnorsrrqoopqstrpqtvwz~~}~}|~~|~}|zyxx{|~{zy{{yyxvsqsuuutsttuvwwrnorsssrrqrtuwussuxxz~~~}~~~{}~}{zywwyz|}zxxyzxxxwsqrsuuttsuuvxxuposuuuttrstuxxutuxy{~~~~{{}}{zxwvwy{}|yxxzyxxwurqsuuuutuuvxyxspquvuvuttuvxyxuuwzz|~~~~~~{|~}{yxwvxz{}{xxxzyxxwurrtvvvutuvwyzxsqswwwwvuuvwyzwvwy{{~~~~|{|}|zywvwxz|}zxxzzyxxvsrsvwwwvvwwyz{wrsvwxxwwvvxy{zxwx{{}}}~~~{{}}{zxwwxz{}|yxy{zyyxussuwwxvvwwyz{ztsuxxxxwvvwyz{xwxz|}}}~{{~}{zyxwxz|~}zyz|zzzyvssuwxxwwwxy{|zvsuxxyxxwvxyz|yxxz||~}}~~||~~|{yxxxz|~}zyz{{zzzwttvxyxxwxxz{|{vtvxyyyxwwxy{{ywxz|}}}||~~|{yxxy{}}zz{|{z{zwttwyyyxxyy{|}{vtvyyyyxwwxz{|yxx{|}~}}~|}~|{zyy{|~|zz||{{{yvtvxzyyxxyz{}|yuuxyyzyxwxyz|zxxy||~~~|{|~}z{}~~~}|{{}~}{z|~~~}}}~|}}{zz{||xvy}}{z{|~}zyz}}~~~zz||{{zxwxyz{xvuwzz|~~}~||}}~zyz}~~~{wwyyyxwvuuwxywuuwyz|}|}~|||}~~zz{|{zywwxz|}|}}}~~~|zxwxz{~~|{|~}}~~~}{||}~|yxy{{|}||~}}}~|{}~}}{zzz}~|{}||~~}|}~}~|z{}|{zxwxz|~|}}|}}}~}|~}{{}~~{y{}~~}|zz{|}}zyxz||~}|}}|||}}zz}}|{zyxz}~~~~~||~~~|z{~}}~~}xxz||{{zxyy{|{xwwyz{}~{{}||||~}z|~~}|{zz|~~~||~}~~{z|~~|}~~{wxz|{{zyxxyz|zwvwzz{~}{|~~}}|~||}~}|{z{}}|}~~~~~|z{}~~~}|}~}xwy{{{zyxxyz{{xvwyz{}~~||}~}}}~||~}|{{|~||~}~~~}zz|~~~}|}}~~ywx{{{{yxxxz{{ywwyz{}}|}~}~}|}}|{{|~~||~|~~~~~}zz|~~~}|}}~~zwxz{{{yxxyz{|ywwxz{}}|}~~~~}}~}|{|~||~~|}~~~~}zz|~~}|}}~~ywy{{{{zxxyz{|ywwy{{}}}~~~~~}}~}|{}~{|~~|~~~~~~|z{}~}}}~}xxy{{{{yxxz{|{ywxz{|~~}}~~~~}~~}|{|~|{|||~}~~}~~{z|~~}}~~{xx{||{zyxyz{|zxxy{{}~}~~~~}~}|||~~|{}~|}~}~}}~~{z|~~}}~{xy{||{zyxy{|}zxxz{|~~}~~~}~}|{}~{{}~{|~}~}}~~~{z|~~}}~{xy{|||{yyy{|}{xxy{|~~}~~~}~|||~|{}{xy|~}}~~}|{zyz}|~||{z|~~{}{}~~~~~~|{{|~~~~~}~}}~}}{{{}~~||}{}~~~~~~~}||~~}}|{{zyyy{~~yz~}{yyz{~~~~~}|{zyz~~|}|{z{}}|~{}~~~~~}|{{}}~~~}}|~~}||{|}~~||}{|~~}~~~}}}~~}~}|{{zyyy{~~yy~~{yyz{~~~}|{zyz~~{~|{{{}}{~{|~~~~}|{{~}~~~}~~|~~}}||}~~}{|}{{}~}}~~~~~~}}~|{{{zyyz}}yz~~|zzz|~~~}|zyz|}|}|{{|~~~~~||}{|}~~}{{}}~~}~}|~}}}}~~~{{~{z|}}}~~}~~~}}~}{{{zyyz|~zy}}zzz|}~~~}{zz|~|~||{|}~~~~}||~{|}}~}{{}}~~~~||~}}}~~~}{{~|z{}}|~~~~|}}~~|{{{{yy{}~yy}}{zz|~}~~~}{z{}}|~}|{}~~~}}}{}}{||}~}||~}~~~~}|~~}}~~~|z|~{z||||~}~}|~~}~~|{{|{zyz|~{x{~|{z{~~}~~~}|z{}~{~~|{|}~~}}}|||{{|~~~||~}~~~}}||~~}~~}{{~}zz||{}~~~~||~}~}{{{|{zz{}~zx|~|{{|~}~~~~}|{|}~{~~}|}~~}|||{|~{{{|}~~}}~}~~~}||}~~~}~~}zz~}zz{{{}~~~~|{~}~}|{{|{{z{}{x{}|{|~|~}~~~|{{}|}}|}~~~|||||~|{z{}~~}~~}~}|||~~~~}~~{y|{z{{{|~~~~}{}}}}|{{{|{{{|~}yy}~}|{}}|~}~~}|{|~~{~}|}~~}{{|||~|{z{}~~}}}~|||}~~}}~}{y|{z{{{|~~~~}{}~}~|{z{{{{{|~~zx|~}|{}}|~}~~}}{|~~{}}|}~~}{{|||~~{{z{|~~}~~}~||}}~~~}~}zy}~{z{{{|~~~~~~|{}}~}|z{{{|{{}~}yy}~}|{~|}}}~}}||}~}|}|~~~|{{||}}{zz{}}~}~}||}~~~}~~~~|y{~|z{{{{}~~~}{|~}~|{z{{|{{|~~zy|}}{}}|~}~}}}|}~~{~}|}~~|{{||}~}{zz{|~~~}~}||}~~~}~~~~~|y{~|z{{{{}~~~}z|}}~|zz{{|{{|~~zy}~}}{}||}}~}~}|}~}|||~~~|{{||}|{zz{}~~}~||}}~~}~~~~}zy|~{z{{{{~~~~~~|z~}~}{z{{{|{|}|x{~}}||}|~}}}}}|}~{}~|}~~}{{||}~~{{z{|~~~}~~}||}~~~}}~~~~~|yz~}zz{{{|~~~~{{~}~|{z{{|{{|}{x|}}|}}|~}}}}}|}~~{~}|~~~|{{||}~}{zz{|~~~}~~}||}~~~}}~~~~~|yz~}zz{{{|~~~~{{~}~}{z{{{|{|}|x{}}||~{~}}}}~|}~|}~|}~~}{z||}~~{{zz{}~~}}||}}~}}}~~~}zy||z{{{{}~}~~}z|}}}|zz{{|{|}~~zy}~}}{}||~|}}~}|}~{~~|~~~|{{||}~~{{zz{}~~}}||}~~}}}~~~}zy|{z{{{{}~}~~}z|}}}|zz{{||||~~zx|}}|}}{~|}}}~}}~{}~|~~~|{z||}~~{{zz{}~~}~}||}~~}}}~~~}yy}~{z{{{{~~}~~~|z}}~}|zz{{|||}~~yy}~}}{~wsqonorv{~|~}xsoonnnruy}~|{zzzzxuutssuxzyz||zzxvzxwzzxvusvy|}zywtpniinrrpnnpppqtvyyyyz|{uswzyvursuvusrqqqqqrpoopqrtxyy}~}{wsvxywutuuuvy{~||}}}}|{|{yxtpswzzwvwvtqqrrtuvvwxvrty||{zx{~}}|}|{zz{zuty|}|{xxyxusqrsssttrrsuwy|~}|}}~~~~~{}~~}}yy~~|y}|z{|||{zvtttuvxzyxz{{zzwux{{wsqrrqnoprtuwz||xtwz{z{zz||{yxz}}~~~~|~zxxxwvvvvwwxy||ytv{||}}|~~}zwuuvxyzxuuuuvx{|xwxwuutrvz|{xvxyyxxyy{{|~~|||}~|yxxy|~zxyzzyyxz}{zxwwxvutsstwxurtutrrpquwusqqrtvwwtrrsvx~|{~{yxy{zvvxyxyyw{|{|{zyzzwurrtvz}||~{wtrmnswwsponnnnqrstuwz}~|}~~~|}}||~|{xvz}}|yvvwz|{zz|~}y{~{xuvwwvtrrrstvxvvsrrtxzy{~~}yz~~}}}}~}~zz}~~~}{{}~|}}yuwz}|{yxwusrttutrpqttqqw{|{{yy{{zyz{zz{||zz{|}}~~~yw{~}|||~}~}}|xx|~zxz{ywwyyyxvusrrsssuxyx{|}|{vuy{{xvvxvtrtvy|}|xz}|}}|{z{}}}~}}}~z{}~{xwyywtssuxy{{{zwtw{}~|}~|xtsvyzzzzyzzz|}}yyzzyzzxz~}|~}zy{|~}~~|zyz|~zzyyz{|zwx{{z{|{~~zyz{zywwxwwx{}{usvwvvwvy{{xvwy{}}{yvwwx{~|~~|yyz}~ytuy{z{{|}{zz{}~zuttvx|}{yvsquz|zvstuvutttwy{|~}{{}~{zz}~|yy~~|yvvy{{z{|}~|~}||ywz{{wtssuxz}{xvvwwy}~zx}}~{|}~}{~~~~|}||~~}z|~}}}~{yy|}}{}~}~~{||y|zuttsrvz~||}}}|yyz}~~}|{|yxwttuz~~~~|z|~~y{~}zwxz{{{z{}}}~~zvvz~|yxyxyzzzyzyvtw{}zwuuy{{{|}~~}xtuwzz{z}}}~}}}}~{xy{~~||{xwvy{{}{yxy|}}}xrsx||{{}}{yzzzzz}|yy~~~|yxz~~zwz{{{|{{||zyyz}|{yxwuuvz~~}}~||}|}|~}~~}|||~{vx}~~~}~{zy{}|zxvx{}|xwxyyvsrtxzz|{{~~~~{{z|~}}}zx{~~|||zzz|{z|~~~}~~|{}}zvtvxz}}{xxxxwx|~|}zxv{~}}~|{{|~~~~}|~~}~~~}|~~{{{}~z{ywz|yyz{|}zxvwxxz{zz|~}{wx|{y{}}zwy|~~{yz}}~}}~}~}}}~{|}|}{{zz|}}||~}yutvz}}xuv{}xxxxwy{~~|~}zx{~}~zxy|~~~~|~~xxz}}{xvxz}|{{~~zx{}{yxyywwz{~~~}zx{|yz|~~}{xz~{z{}}~{z~~}~~~z|~|~~~}{z|~~~}~~{xx|}{|~~}}~}~|yyz|}~}}~}{ywvy{}~|zz{~~~~}}~~{z{~yy~}z|}~~|~}}|{{yz}~}}z{~~|yyy{~}~}|{wx{|}}}~|xuuxy|~~|~~~~~|||~}}~~~~~}}~{{|~{~}{|}}ywx{}~wy}~~~zvvxz}}{{{||zz|}zz{~}~|{||}}~|~zz~{zz}}}|||}|||zyy}~{||}~|zyz{yy|{z~{yy}}||~yxzz|~{}~{}~~}}~~~}}~|||~~}}}z{}{}|wuwz{~|vty~{||||z|}}~~}{{~~ywy|}~}~~~~~}y{~}zwy~}zzz{}}||}~|{~}{||}zwxz|~zxz||{}}|yy~{{}}|~}z|~~~~||{{}~~~}}}}}}}{xz~~||}}}~{y}}|~}~~~~|xy|~zzzz|~{z}~~}~~~~~{{~~z~||~{|~~{{~~}}zz|}zyz{{~z{~~~}}~~}}~~}}}yz~||}|{}~~}~}||}}{~~~yy{}}y{}}{{|}|~~~}|}~}}||}}z|}|}~}yx{~}~~}|{{~zx|~{xz}~~}}~||}~}{|~}}~~|}}{{z{~}{{}}}~xz~~z{zz|}||}||}}~}{}}~}~~|~~}}~|{z{{|{xz~}~|}{yz{{}}zz~~}~}~~}~~}|}}{}}z{~~~zyz|}yx}~~~~~}{z}}}}{yz{~|{{{}}{|}~}}}~~}}}}~~~~~~|{{~~yz~zx{~~~}~||{{|z{||~}{|}}}~||}{}||~{}~|~}|y{}~|yz|}}}~~~|yz}{{}|{}{z}}|~~}|wz||}|z|}xx}~~~}}}|~~zz{~~}}~}|~~}}|}~~|z{~}~|{z{}~|yz~~~~}~}{z}~}~}}zz~{~~~~|z}~{{{yz~{y~|zz}~}|{z}{yz}~}~~~~}~~{z|}}}~~|~{yz~yw{~~~~~}yy|}}{yz}~~~~~~}||~~~|}|}~~z}~~}~}zz~}}|||~}z}}{{}~|||{{~{yz~|~~|}}}~|x{~}~{z{}}zz}zy{|~|zz{}}}{z{}|}}~}~|}~{|}~|yy}|{~~{{|~||~~~|zz|~|||{{~|y|~~~~|~~~~~~}~~||~~~}{{~|z{~~|{|~~~~~}~{z{}|||{~}{}~}{|~}}|~~{|zx{~~~|{~|yz}{~~|z||z|||~||}|}}~}~|yy|{{}~}}|}}|xz~~~}zy{~~~}}|z{}||}~~~}||}||~}{|~}|zx|}yz|~~~|y{|z{~~|}}}~~~}|}~{z|~}{|{}}{z||wx}}||~~}{y|~}zz|~{z|~~~}{|~{|}~{{~|x{|z{}~|xy}}}~~{{}}{{}~}~~~~~~}~~|{|~|}~||}~}|}}yx{}}~~~}}||}|z{~~}~~~~||~|}{z}}}~zy{~~}~~~{z}{z}~zy{}~}~|{||z{~~~}}~}~}~~~~~~}{{}|~~}}}~}~~zwy}~}||~}||}~}}{z||}}~}}}~}~|}~}~{y|}~}{z}~~}}|||}|z|}}}zy{~~}||||||{}}}~~}}~~zxy|~zx{}~}{z}zxy|~}}}~}{||~}}|{}~}~~~~}}~~}~zz}}{yz}}|{|}~~~|{~}}~~|z|~~z{~~}~}{z}~{{}}}~z{~}}~}}}}~|zyz}|z{~||~}||}~~~~~{xy|~}{|}~~|z|~}~~~~}yy||{{{|}yx{~~~}}~~}~~|{}|~||~}}}}{|~}~}}}z{}}{y{{}|{|~~~~}}~}||}~}}~{z|~}~|{|}}|{{}}~}yz|zy|~}zz{}~}}}~||}~~|}}{{}}}~{y{~~~~}|||~~ywz}|z{~~}}}~~~|{{~~~}||~~~}~~~~zzz|~~}||}~~}yz~~zz{|}|zz|~||}~||}}|}~~~~~}~}|~zyz{}~yy}~}||~}yxz|}|{|}}|}~~}||}||~~{{|~~~}yz|}zx{}}}|}}|}~~zz|~~{{|}~}{z{}}}}}}|}~~~|{{|~}|zz~}zz}~|~~~~}|||}~zyz|}}~}}}~}vomoqrtux||wrolkmqsux|{{{|zz{zxuqpsuwwwwvtsrty{}|{}|xvwvurqrux{~xtrpnoquvvvvtplmqwyywvvwwvvvwvuuvtsrrqonnnopqromlnswz|~zvsrrtvxxwtpnnpty~~{xz}~~}|{zxttvuttuvvusopsvwwtstwyxx{}|xwz~~}yvwz{zxxxwvtstutqqqqpprstwyz{|}~}||~}yy|~~|yz}}|{z{}~}|{|||{z{}|{|}}}~zxwxyvuvvvvvwxwsoprrrrsuvvtrsux{|{yxwwx|~~}}~~|{|}~~zvvy{{{|~~|zyy|~}xtrqsux{|{ywvtqonquwwuttttsqpquxzz|}|yz~~~~{y{~~~|zxz~~{wvvwxwxyzzyxvuvvvxxvuvvtqpopsuwxvsqqqrtxz{{{{yxvw|}}}~~~~~}}~}}|}~~{wuvwzz{||yutssvy{{yvtrrsuwvtrrtuttuwyyzzywwy{{zxz|~}|{|||~~}~|{{|{yz|~~|yxyyyxyyyyyyyxvtqpqtwyyz}}{xwttuwy{|yvuuuwy|}{yvvy~~~~~~}|}~}~~yvy{|}}}|yvsstwz{zxvusqsw{~~}|{xursuwvwxyzzzyyyxxxxxxyyzyyz}~{{{~}}}{yxz|~~}|{zyyxvuvwxxxyzzxy}~{xxyyvuvwxyyz{{yustvwxzzzzzyyy{}~~}}|zyz}~~~}|||}}|yvvxyz|~~}~~{yxz}}zwutuwz}~{zzyvutuvyzyyzzyxutux{}}|}}|}~|yz|}{{|~}zyyyxvwz|}}}|zwvvyzzzz{zwtrruwz{zywwwvvy}~~~~{yxz~|{|~~}|{|~}zyxxz{}~~|ywvuux|~|ywvwxyywwvwxxyzzyz|}}{yz}~{z|~~{y{}}{|~~~~~}~}||}}}{|}~|zzyy{||{zyy{}}}yurruwz|~}zvtw{}{zzyyz|~~{yy{~|zz|~{zywvvwxz~~}zwwz|~}}~~~~~}}}|zywvz}}zxwyyz}~|~~zwuuvwy{~~}zxx{~~|zywvwz~||{ywwxzz{z{|||zwttvxxy{||zzz{}~~~}{|}~~}zyyz|~}{xtsux|~}||}~}}~~~}{zyyz{{{yxwvvwy|~~~~|{|~|{{}~||~}{|}}}}}{yxxyzyxwwxxxwuvz}~~}|}~{z|}z|~~{yz{~~}~}||}~||{zzz{zxuuvxyzzyyxyyyz}|zxy|}}||~}}}~~~}~~}~}}~{wwwx{~~}zxwvvwyzz{{zzz{{{}~}}|zyz}}{{|~~|}}zy{||yz~~}}||~}|zyyz{||}|||zwutvxz}}|zyyz|}}{yyzz}~}||}~}}~~}||}~~~}~~}~~|{|~~zyz{{|}}|zxwx{}{zzzz|~~~~}zxvx{{zz{{}~~~|{zyz{}~|{z{~~~|}~}|yxz|~~}{z{}}}|{{}}||{{}~}yvvy|{ywwxz}}{yxy|~~~~~~~}zxy|~|yxxxy||yxz~}}|{zz|}~~}~}|{zzzyzzyz{|{zxx{~}||~}~}~~||~}{zz{|}}~~~~~~~}{{||{{~}{{|{{{||{yvuwy{||zz{|||~|yy|~}||~}|~~~~~}|}~~}}}}}~}}}~|ywvwz}~}||{zxvwy|~~~~~}}~|yz}~|{{|~~}|}~~~~~}{{{}~{{|~~~}}||~~~}{xxy{}~}zxwwxz}}zy{}~|||||}~}}~~}}}~}zy{}~}~}|}~~}}|~~{{|~}{zyz|}}}|zxxxz~~~}}|}~}{{{zzz{|~~~}~}zxxz}~}{{~}~~}}~}}}~~~}|{yz{|||{{|~~}{z{}~|||}~~{zyy||{yxy{}~}|{z{|}}}~~~~~~~}}~|ywy}~}~}{{|~~~~~|z|~}~~~{z{}}zyz}}}~{zzz{~}~}|{z{}~~}}||z{}~~}{zzz{}~{zyz|~|}|zz|~||~}}}}~}|}~~~~~}}|}}}~||}~~}{yy{~~~~||||}}}~}~~~{zz}~}~}{yz|~~~~~~||}~|zz~|{zz{|~}~|~~}{{}~~||{{{z|~|{{{}~~|{zxy}~~}{z|~~zz|}|}~~~~~~}}~}}|}|}~~~~}~||{z{{||zz{}~}~}}{{{{{{~~~}|{}~~~~~}|yxy{}|}~~~~~|}~~{z|{zy{z|}~}}}}{{}~}}|}|{|~}}|||~~~~~}|{z{~~||~|{}}yy{}~~~}}~~~}~~~}~}|}}}~}}~|||zz{|}zzz{}}~}}~|{{{{|~}{|}}}|ywxz||}|{|~|{~|zyyyy}~~{}~~~}{}~|}}}zz|}}|zz{~~~~}}{zz}~|~}||}~~}yy{~~}}~~~}|||}}~~~~}}}{|{}~|zzy{~~~||}}||||}~{z|~~}}}|yxy|~}~{{}}zz}~~{zyzz|~}|~~~~||}~~||||{z||~}|zz{}~}|zyy|}}}{{|}~~{z|~~~~~~~~~}|~}}}~~~~||~~}{{z||zzz|~~||~}|||{|~~|{}~~~~|xxz}}}~~~}||~~{y|~~~{zz{z}}~}~~}|z|}~}||}}{|}~}}|z{}~~~~~}|yy{}{~~|{|}~|y{}~~~~~~~~~~}|}~}}~~~~~~|{~~|zzz|}{{z|}}~}}~~|||{{|~||~~~~~}zwxz||}~~~}{|~}{z}~}{z{zz~}~}~~~~~|{}}}}~}{}}~~~{z{~~~~~~~~~|zz|||~~||}}~~zy{~~}~~~~~|~~}}~}~~~~||~~|z{z||{zy{~}}}|~~}||{{}~||~~~}~}{wwy|}~~}{{}|z|~}{zzzz}~|}~~~}{|~~}}}}{|||~~|zz{~~~~~~}zyz}~|}}|||}}}zz|~~~}~~~}|~}~~}~||~~|{{z||{zy{~~~||~~}||{}~|{}~}~}|xxy|}}~~||}|y{|~~|{{{z|~}}~~~|{}~~|}}~|{||~~~{zz}~~~|yy{||~||||}}|z{}~}~~}||~~~~~{||~|{zz|||zz{}}~|}~~}|{{}~||~~~~~{xxy||}~~~~|{}~~|y{|~~|{|zz}~~~}~~~}{|}~}|}~~||||~~~{z{~~~}~{yz}~{|~~|}|}}~{z{~~~~~~~~}{~~~}~~|||}|{zy|~}|{yz~}}~}|~~}|{{}~}|~}~~}{xwy||}~~}{{}}~|z{|~~|{{zz|}}~~~}|}~}}~~|}||~~}zz|~~~~}~}zy{~}{|~}}||}}zz}~~~~}}|}~}~~~~|}|~~|{z{~~|{yy|~~~~|}~}{{}}|}}~~~~{xwy||}~~}{{|}~|z{|~~|{{zz|}}~~~}|}~~}}~}||{|~~~|zz|~~~}|yy{~|{}}||||}|z{~~~~~~~~}|}~}~~}|||}|{z|~}|{yz}~~~}|~~}{{}~||~}~{xxz||~~}}{{|}~{z{|~}||zz|}~}~~~}|}~~}}~}||{{~~~}zz|~~}~}zyz~}{|~~xsnoqqruw{|zxvvvvvvvttw}{yyzysprssqpqqrtuvy{}{upmlnrvxy~}zwtrruvsqvzzvroljjkmoqrsttssuuwz}ztqrrqpnmmmoqrrsuwz|~~|yy}}|{||{yuspoprvxxz~~{vspmlmopqsttsssssw{|xsrtvvvvwxxz{||}|}~~}}~{vtvxxvvy{zxvvvvwyywusrruwz{z}~{zz{}~}}||}~zz}|yz|{xxy{~|yyy{|~}}~~yxz|zwwyyyzz|}}}ytonrttstxzzyyyz{|~}{yvuvx{}~~|||~{z|}zxyz|}{yvuuvwyzy{~zuuxxustvwxyz|}~}zuplnstssw{}}}~~~~{xwxy{|{}~~~|~}zyyz}~|yvtrrsuwyy{}{vuwywtsttuwwxyz{|}|zxspqvxwwy{}}||||}~{xwyz|}}}}zz|zwvwx{}}{xvttuuuwwwz{{ytrtvvtsuuwxxyz{|~|ytpquxwwy{~~~}zxy{}~~~}~~~{{}{xxxy{}|zwusstuuwxy|}}{vtuwwttvwyz{{}~|yspquxwwz|~{xzz{||}~}}~~~~}~}zyyy||{yusqrtuvyz|~~~{vuwxxuuuvxyzz{{}~}|ytsvz{zz|~~~}~}yy{{|}}}}}~{{}|ywxxy|}|ywutuvvwxx{||zwtuvxwvwwyz{z{|}}|wssw{{z{|~|yz{|}}~~|||}~}}~~~||~}zyyy{}|{xvtsuvvxy{~|xvvxywwxxz|||}~}{wrsw{zy{|}yz{{{{|~||||~~}~~}~|{zz{}|zwusrtvwyz}}ywwyzxxyxy{{{{|}}{xuuz}}{|}|yz{{||}}||{}~}~~~}{|~}{yxxz}}|zxutuvwxyz}~~|xvvxyxxzzz||||}~~|yutx|}|{|~~zz{|||}~||{|~||}}~~}}~||~}{zyz|}|zxvttvwyz{~{xwxyyxz{{|~~~~}{wtv{||{|~}z{{{{{}~{{{{~}|}}~yttvz|}}~~|}}zz{zz{|}~|yxxxxww{~}{ywwwx|~zuqprtvwz|~}|~~zz}~}|{{{~}yvvy{{ywvvy{{zyvsssuwxyz}~}zwvvxyy{}}}~~}}|{|~{xuqrtvwxy|}|zz}}zxy{|{ywvwzzyywtstvxz|}|zyz|||~}~}}}|yxxwz~}|zywx{{zxz||yutuwxxy{||zxz}~~~||~~}ywwy{}~||~~~~}~~||{zzy{}zvuutvz}~~}{zyyy|}|z{}}zwuvxzz{|}}{yy|}}~}}zz{}~}}~}|||zzyxxx{~~{wuutuy|}~}{{zzz}~}{|~~{xuuxzyyz{{ywx|||~~}{{|~~}}|}~~|yyxwwxz}|yutuuvz}~~}{zzyz|}{z{~|ywvyzyyz{zxwx{||}{{}~~}}~}{zzyxxy{~{xuuuuw{~}{{{{|~~|{}}zxwx{|{{||{xxz}|{|~~~}{}~~}~~|}~~|zyzyxy{}|xvuvvy}~|{{{|}~~||~}ywwxzzzz{zywwz~~{{|~~~|{}~~~~|}~~|zzzxwy{}~zwuuvwz~~|zzz{|||{{}}zxxyz{zz{zxwwz~~{{}}|{}~~~~~|}~|{zzyxy{}{wuuvwy}|{{{}~~}}|}~zxxy{|||}{zxxz}~{z|~~~~}|{}~}}}||~}zzzzyy|~~zwvvwy|}{{|}~~~}}}{xxyz{{z{{ywwy||z{}~~~}|||~}}~~|}|z{zyxz}{wvvwy{~~{zz{|}}||{}~zxxzz{{{{zxwwz~}{z}~~|{{}~~}}}|~~{{{{zy|~~zvvvwz|~|{{}~~}}}}yxz{{}|}}{ywy||z{}}~~~|{{}~||}||~}{{{zzz}~zwvwx{}}{{|}}}}}|xxzz{{{|{ywwz~~{y{}~~}~~|{{|~~||~}~~}}}||}}yxxy|}||}}}}{yy{z{{||zwvw{{yy{}}{|~~}}~|yxy{}~|{|~~}}~}}|}~zxxx{~~}|~~~~|yz|{||}}{ywx{{yy{}~}{{}~~~}}~|zxy{|~~|z{~~}}}}||}{xxy{~~}|}~~|yy{{z{||zxwx{{xxz|}~{{}~~}}~|zxy|}|{|~~~}}}||}~zxxy{~|{|~|}|yy{{zz||zxvx{{xy{}~~|{~~}{ywy|}~|{|~~~~~}||~|ywwy|~||~~}~{yz||{|}|zxwz}~yxz|~|{{~~}~~|{yxz|}~}|{}~~~}}}|}|yxxz}}||~~}~zyz{{z{|{ywxz~}yxy{}~~|{{~~~~|zyxz}~~}|{}~~}|{}~{xwwz}}|{}}|~~{yz{{zz|zywx{~~yxz|~}||~|zyxz|~~}||}~~~~}}{|~{xwwy|~||~}~|zz{|{{||zxx{~~zxy|~}|{}~~|zyxy{~~|||}~}~~~~}||}zxwx{}|}~}}zzz{{y{{zxwy}|xxz|~~~~~}||~~~~~zyyy{}}}}}}~}~~~}|{|}zxwwz}~||}~}~|zzz{zz{zyxx{~{xz|~~~~}}~|zyxy{~}}}}~}}~~~}|{}|yxwwz~~|}~}|{{{|z{{{yxy|}zxz|~~~~}}|}~~|yyyy{~~||}~~~}|~~~~}|||yxwx{~|}~~}{{zz{zzzyxxz~|yxz|~~}}}}}}~~~~~zyyyz}}}}~~~}}~~~}|{}|zxxwy}~||}}~}{{z{zzzzxxy|~{yz|~~~~~~~~|yxyz{~~}}~~~}||}~~~|||~}zxxwx|~}~~~~||{{{{{zyxz|~{yy{}}~}}}~~~~~}zxyz{}}||~~~|||~~~}|}~zyxxx{~}~}||zzzzzyxxy|~{zy{}~~}}|}~~~~~~}zxyz{}}}}~~}||~~~||}~}yxxxy{~}}~~}|{zzzzyxxz}~{zz{~}~~|}~~}zxy{{~~}}}}|{|~~~}||}|xxxxy|~}}}{z{{zyxy{}{{{|~}~|{}~~~|yxz{|~}||}}{{}}~~}|}~{xxxyz}~}}|zyzzyxwy||{{{|~~~|}|{}~~|yxz|}}||}}|{}}}}}||~{xxxy{}~~~~~}~|{yzzywwy||{|||~}~|{}~~|yxz|}~}||~~|{|}}}}||~|xwxy{}~~}~}|zz{zxwy||{|||~~}|z|~~~}~}zxy{|}}{|}~|{{~}}}}||~~ywxy{|~~}~~|zyzzxwx{~}{{|||~~~}}}zz}~}~~|yy{|}|{|~~{{|}}}||}{xwxz|}~}}~}~}|yyzywwy|~|{|}}}~~~|z{~~~}{yy{}~~{{}}{{}~}}}||~zwwx{|~}~~~~~|zz{ywwz~~{{|}}~~~{z{~~}}}{yy{}~}z{}~|{|~~||||}~zwxy{}~}~}~~}{yzzxvx{|{{}}}}~~~}}zz|~}~~|zyz|~|{|~|{|}||||}{xwxz|~}}~~}~}|zyzywwy}}|{|~~}~~~{z{~~}~}{zz{~}{{~}{|~}||||}}ywwz|~~}~~~~~}{zzzxwy|}{{|~~~~~~|zz}}}~~}|zz{}}{{}}||}~|{{{}}zxwz|~~}}~}~~}{yyyxwx|}{{|}~}}~~~|z{}}}~~}|{z{}}{{}~||~~|{{{}}zxwz|}|}~~~}}{zyyxwx|}{{|~~~~|{{}}}~~}|{z{~}{{}~~}|}~~|{z{~~|zxxz}}|}~~~~~}|zyyxxz~|wronprty|{xtqprssstwz}}zwtsrrtvxyxwurqpnorvz{|~|yurpmkjklot||z{{zxvspmlmpsvurqrtvwutsstssuwxyxwtrponpstvutsuuutuxyyxy{|}~~zwtrqqooonnmnprw}}{{|~}yvvwxxvtsssrrtwxxyxvtsrruxz||{z{||{z}~~|||}}~~}|{|~~}}|{{||xtsruxzxusrpqrrrrsrqqsux|~~}~|{{|~}}~~~}|{ywx{}}{yvutrsuxxwvvvvvvwvsqprvzzwutrrtuttuutstux{~~~~~~~~~zxxvtsuxz||zxvsqqppqtvvttuuuvwwurqruz}{ywuuuwwvvwwutvxz~}~~~~~~}~~|xwvtrrtwy{{yvtrppppquvvuuvvvwxxvtsux|~|zxwvwyxxxyxvuwy|~}~}~~~}}~~~~zwvusqruxz{{xusrqqpqsvwwuvwwxyzywuuwz~}zxwwyyyyyzxvvx{~}|~}~~~}}~~~}xvutrqsvy{{zvtrqqqqruwxwuwxxyzzywuvy}}zxxxzzyyzzxvwz}~|}~}~~}|}~~~}zvutsqrux{{{xtsrrrrsuwyxwwyzz{{zywwy|~|yxyz{zzz{ywwz}||~}}}}|~~{wvutrsuy{||yusrrsstuxyywwy{{||{zxxz||zxxz{zyzzywvy|~||~~}~~}|~~|xvutssvy|}|yusrsstuvxyyxxy{{||{zxxz}}zxyz{zyzzywwy}~||~~}~~}|~~{xvutstwz|}|ytrsstuvwyzzxy{|}}|{zyz{~|yxyz{zzzzyww{~}||~~}~}}~{xvuttvx|}~|xtsstuvwxz{zyy{}}}|{zzz|{yxyzzzzzzywx{~|||~~~~}}~|yvutuvy|~~|xtrstvwxyz{zyy{}~}|{{z{}|yxyzzzzzzyxx{~|||~}{wuuvyz|~}}}~~yurrsvx{}|zyyyyxxwvutwz~~|zxxxy|~}{{zyxxwxz{}~~~~~{zyzyxwxyxz{}}|zz|~~|zvtstuwz|~}{wttvxz|~~zzz{{{{zz{{{zyyz{}~~|{||{|~}{z{|~|zwvvwy{~|xvwy{}~~~~~}}||~}{zzz{}~}}}|}~~~~}zxyz||||{|}}|{zz{|}~}zyy{|~}ywxz|~|{{{{{{zzyyy{~~{ywwwwyz{{|{{|~~~~~|zyz|}}~}|}~~}|{{{|~~~}~{z{}~}yxy{}}zyyyyyyxxwwxz~~{ywvvvwyz{{|{|}~~~|z{|~~}}~}|{{|}~}}}{{|~{xxz|~~~{yyxxxyxwwwwy||zxwwwwxz{||||}~~|{|~~~~~}{{{|~}|}|{|~|yxy|~~|yyxxxyxxwwxy|}zxwwwwxz{|}}}}~~|{|~~}}{{{|}~}{||{|~}zxy{~~|zyxxxxxxwwyz|}zxwwxxxy{|}~~~~~}|}~~~}{{{|}~~|{||{|~|zxy{~~~|zyxxxxxxwxy{}}zxwxxyyz{}~~~~~}}}}~~}{zz|}~|{||{|~~|yxy|~~|zyxxxyyxxy{|~|ywwxyyyz|}~}}|}~~~}{zz|~~|{{|~~|{}~{yxz}~~|zyxwxyyxxy|}{xwxxyzzz|~}|}}~~~}{zz|~~|{{|~~~|{}}{yxz|~~|{yxwxyyxxz|~{xwxyzzzz|~}|}~~~}{zz|}~|{{|~~~|{|~}{zxy|~~}|zxxxyyyxz||ywxyz{{z{}}||~~~~~~{zz{}~|zz{~~~~~}{|~~|zyy{~~}|{yxwxyyxy|~}zwwxz{{z{|~~||}~~~}{zz|~}{z{|~~}}|{|~|zyxy|~|zzz{{{{}~{yz{}~}}~}~~|{z{|~}{yyz|~||{{}~}{zz|~~}{zyyz|~|zzz{{{|~~zyz|}~~}~~}~~|zz{}|zyyz|}|{{|~~|{{{|~|zzyyz}}{zzz{{{||yyz|}~}}~}}~}{zz{~~{yyz{~~}|{{}~}|{{|~~}{zyyz{~|{zzz{{|~~zyz{}~~}~~}}~|{z{||yyy{}}|{{|~}|{{{}~}{zzyz{~}{zzz{{|}~{yy{|~~~}}}~~}{z{|~}zyyz|~}|{{|}}|{||}}{zzzz{}~~}|zyzz{|}~{yyz|}~~}}}~~~~}{z{|~}zyyz|~}|{{|}}|{||}~|zzzzz{}~~~}|zyzz{|~~{yyz|~~~~~}}~~~}{z{||yyy{}}{{{|~~}{{|}~}{zyzz{|~~~~}|zzzz{}|zyy{}~~~~~|}~~~}|{z{}{yyz|~~|{{|}~~|{|}~}zyyz{{|~~~~}{zzzz{}~|yyy{}~~~~}|}~~}}|{{{}{yyz|~~|{{|}~}|{|}~|zyyz{|}~~~~}{zyyz{~~{yyy{}~~}}}~~}}}{{{}|yyz|~~|{{|}~~}|{|}~}zyyz{|}~~~~}|zzyz{}|zyyz|~~}}~}}}|{{|~|yyz|~}{{|}}~~|{{}~}{yyyz|}~~}}}}{zyyz|}zyyz{}~~}}~~}|}||{|~~{yy{}~~|{{|}~~}|{|~~|zyyz{|}~~~}}}|{zyz{~~{yyyz|~}}}~~}|||||}~}zyz|~}|{|}}~~}}|{}~~{yxyz|}~~~~}}}|{zyz|~~{yyy{|~}}}}||||||}|zyz|~}|{|}~~}|{|}}zyxy{|}~~~}}}}{zzyz||zyyz{}~~}}~~}||||||~}{zy{}~|{{|}~~}||{|~~{yxyz{}~~~}}}|{zyz{~~{yyyz|~}}}}||||||}~|zzz|~~}|{|}~~}||{|}~|zxxy{|~~~}|}}|zzz{}{yyyz{}~}}}~|{||||}~~|{zz|~}|||}~~~~}||{|}~|zxxy{|~~~~}||||{zz{}|yyyz{}~}}}~|{{|}}}~|{zz|~~}|||}~~~~}|{{|}~|zxxy{|~~~~}||||{zz{~{yyyz{}~}}~~|{{|}}~~|{{{}~}|||}~~~~|{{{|~}{yxxy{}~~~}||||{zz|~zyyz{|}~}}~}{{{|}}~}{{{|}~~}||}~~~~}|{{|}}zyxxz|~~~~|||||{zz|}zyyz{|}~}}}{{{|}~~|{{{|~~~~~}||}~~~~~}|{{|}|zyxxz|~~~~|||||{z{}~zyyz{|}~}~}{{{|}~~|{{|}~~~~}}}}}~~~}~}|{{{}}zyxxy{~~~~}||||{{{|~{yyz{|}~}}~}|{{|}~}{{|}~~~}}}}}~~~}}~~|{z{}~~{yxxy{}~~~}|{{||{{{~|zyyz{|~~}~~|{z{}~~|{||}~~}}}}}}~~~}}}{zz|}|zxxxz|~~~~}|{{|{{{}~{yyz{|}~~~~|{z{}~}{{|}~~}}}}}}~~~}}}~|{z{|~~{yxxxz|~~~||{{|{{|}~{yyz{|}~~~}|{z|}~|{{|~~~}}}}}}}~~~}}~~|zz{}~{yxxy{}~~}|{{{|{{|~}zyyz{|}~~~~}{z{|~}{{|}~~}}}}}}~~~}}}~}{zz|~|zxxxz|~~~}|{{||||~~|zyz{|}~~~}|{z|}~|{|}~~}||}}}~~~~}}}}{zz{}}zyxxy{}~~}|{{{|||}|zzz{|}~~~~|{z{}~|{{}~}||}}~~~~~}}~~yussstvxz|{wtqonmnpty}~{ywxyyyxwxwvussrqqrtx{}|wspnnnprtwz|}ytpnnoqsssromkklmosuwxxyy{|{zxvtttuvwxwusrqonmmmnpqtw{}|zxwvtrqonoprtvxz{|~~zwusssuwxyyyyyzzzywuttvwyz{zyxxvuuttuvwy{~~}||}~}}}~}{yvtttuvvvvvuutttssrqqrsuwyz{{|}}|{zzz{|}}|{{{|}~~~~~}|{yxvttuvxyyxvtrpopqrtuuuvvvuvvvuutstvxy{||||}~}}}}~}{{zz{|}}~~|{{z{|}}}|{zzxwutsrqrsuwwvusqpopqsuvwwwxxxxxxwwvuvwy{|||||}~~~}|{zz{{|}}~~|{zyyz{|||zyyxwvusrqqrsuvwwvtrqpprtvwxyyyyyyyyyxwwwxy{|}}||}~~~|{zzz{{||}~~|{zyyyz{||{zyxxwutsrqrstvwxwvtrqqrtvxyzzzzzzzzzzyxwxy{|}~}}}}~~|{zzzz{{|}~~}|zyyyz{||{zyyxwvutsrrstuwxyxvtrqrsuxz{{{{{{{{{{zyyxyz|}~~}}}~~~~|{zzzz{{|}~~}{zyxyz{|||zyyxwvutssstuwxyyxvtsrsuwy{||||||||{{{zyyz{}~~}}}~~~~}{zzzz{{||}~~|zyyyz{|}||zyyxwvutsstuvxyzyxvtsstvy{|}}}}}||||{{zyz{|~~}}~~~~~|{zzz{{||}~}|zyyz{|}}|{zyyxwvutttuvxyzzywusstvx{|}~}}}}}|||{zzzz|}~~}}~~}~~|{zz{{{|}}~~|{zyz{}}~}|{zyyxvuutuuvxz{{zxvtttvx{|~~~~~}}}||{{zzz|}~~}}~~}~~|{{{{{||}}~~}{zz{|~~~~}{zzyxwvuuuvwxz{{zxvttuwy{}~~~~~}}}||{{zz{|}~~}}~~~}~~|{{{{||}}~~}{zz{}~~}|{zyxwvvuvwxy{{{zxvttuwz|~~~~}}||{{zz{|~~}}}~~~~|{{|}~~}|{{{|}}|{|}}~~}zxvuuvx{~~|zxwvvwxz}}|{{|}~~~}}|{zzyyyyz|~~}}}~}}|{{}}|{{z{{||{xvtstuxz|~~~~}}||{zyyxxyzz{zyxwwxy{|}~~~}|{zyxxy{}~|zyyyyyzz{|}}~~~}}}}zyyz|~~}|{{zyxxxyz{{{zyyyz|}}|{zyz{}~}|||}~}|{zzz|~}zxwvwwxyyz{|~~}~}}~}}}}~~}}}~}|{zz{|~}|{z{{|~~~~}{zzxxwwwy{}}zxwvwwxyz{|}~~~~}{zzz{}}|zzzz{|}~~}}}~~}|{zyxwwvwxy||zxwwxyz{|}~~~~~~}{zz{|~~}{zyyz{|}~~}||}~}|{zyyxwwwwxz}|zxxxyz{|}~~~~}~~|{zz{|~~|zyyyz{|}~~|||}~~}|{zyyxwwwxz|~|zxxyz{|}~~~~~}}~}{zzz|}~}{zyyyz|}~~}||}~~}|{zyxxwwxy{~}{yyyz{|}~~~~~}}}}{zzz{}~}{zyyyz{}~}||}~~}|{zzyxxxxy{~~{zyyz{|}~~~~}~}}}~}|zzz{}~}{zyyyz|}~~||}~~|{zzyxxxxz{~~|zyyz{|}~~~}}~}|}~}{zzz{}~}{zyyz{|}~~||}~~|{zzyxxxyz|~~{zyyz{|}~~~}}}~~||}~}{zzz|~~|{zyzz{}~}}}~}|{zzyyxyz{}}{zyz{|}~~}}}}~}||}~|{zz{}~~|{zyz{|}~}}}}|{zzyyyyz|~|zyyz{|}~~}}}}~~}||}~|zzz{}}|zzzz{|~}}~}|{zzyyyyz|~|{zzz{|~~}}}|}~~|||}~|zzz{}~}{zz{|}~}||{{|}~}|{|}~~||{{{|}~~|zyz{|~}}||}}~~}}}~}|zyyz|~~}|{z{|}~~}}|{{||~}|{||}~~}|{{{|}~}{zyz|~~}|||}~~~}}}~~}{zyz{}~|{{{{|~~}||{{|}}{{|}~~||{{{|}~~|zyz{|~~~}||}}~~}}}~~|{zyz|~}|{z{|}~~}}||{{|}~|{{|}~~}|{{{{|}}|zyz{}~}}||}~~}}}~}{zyy{|~~}{{{{|}~}}|{{|}~}|{||}~}|{{{|}~~}{zyz|~~}|||}~~}}}}~~|{zyz{}}|{z{{}~~}||{{|}~|{{|}~~}|{{{||~}{zyz{}~}|||}~~~}}}~~}{zyz{}~|{{{{|~~}||{{|}}{{|}~}|{{{{|}}|zyz{}~}}||}~~}}}~~}{zyz{}~|{{{{|~~}||{{|}}|{|}~~}|{{{{|~}{zyz{}~}|||}~~}}}~~}{zyz{}~|{{{{|~~}||{||~~|{{|}~~}|{{{|}~~}{yyz|~~}|||}~~}}}}~~|zzyz|~}|{z{|}~~}}|{{|}~}|{||}~}|{{{|}~~|zyz{}~~}}||}~~~}}}~}{zyy{|~~}{{z{|}~}||{{|}}|{|}~~||{{{|}~}|zyz{}~}|||}~~}}}~~}{zyz{|~~}{{{{|~~}||{{|}}|{|}~}||{{{|}~}|zyz{}~}|||}~~}}}~~}{zyy{|~~}{{{{|}~}||{{|}}|{|}~~}|{{{|}~~|zyy{|~~}}||}}~~}}}}~}|zyyz|~}|{z{|}~~}|{{|}~~||||}~~}|{{{|}~~}{yyz|~~}|||}~~}}}}~~|{zyz{}~|{{{{|~~}||{||}}|||}~~}|{{{|}~~|zyy{|~~}}||}}~~}}}}~}|zyyz{}}|{{{|}~~}||{||~~}|||}~}||{{{|}}{zyz{}~}|||}~~}}}}~}{zyyz|~~}|{{{|}~}|{{|}~||||}~}|{{{|}~~|zyyz|~~}|||}~~}}}}~~|zzyz{}~|{{{{}~~}||{||~~}|||}~~}||{{{|}~}{zyz{}~}|||}~~}|}~~|{zyz{}~~}{{{{|~~}||{||~}|||}~}||{{{|}~}{zyz{}~}|||}~~}|}}~|{zyz{|~~}{{{{|~~}||{||~}|||}~}||{{{|}}{zyz{}~}|||}~~}}}~~|{zyz{}~~}{{{{|~~}||{||~~}|||}~~}|{{{|}~~|{yyz|}~}}|||}~~}}|}~}|zyyz{}}|{{{|}~~}||||}~|||}~~}|{{{|}~}|zyy{|~~}|||}~~}|}}~~}{zyyz|~~}|{{{|}~}||||}}|||}~~}|{{{|}~}{zyy{}~}|||}~~}|}}~~|{zyy{|~~}|{{{|}~}||||}}|||}~~}|{{{|}~}{zyy{}~~}|||}~~}|}}~~}{zyyz|~~}|{{{|}~}||||}}|||}~~}|{{{|}~}|zyyz|~~}|||}~~}}|}~~}{zyyz|}}|{{{|}~~}||||}~~}||}}~}||{{|}~~|{yyz{}~}}|||}~~}}|}}~|{yyz{}~~}|{{{|~~}||||~}|||}~~}|{{{|}~}|zyyz|~~}|||}~~~}}|}~}{zyyz{}~|{{{|}~~}}|||}~}|||}~}||{{||}~~}{zyy{}~~}|||}~~}||}~~}~{vrommnquz~{wtqoooprtw{~~|ywuttttttutrqonoprtwy|~~zwusssstuwy|~}zwusrrrsstuvwwwwvutuvwyz{{zzxvutsrqpqrsuvxxwurponoprtvxz|~|{{zywurqoooprtvxy{}~~{yxxyzzzzzxwvutsrrrstvx{|~}|zxvvvwxz{}}||}~}{yxvtsrrrrrsrrqpppqsuwy{|}~~}}|||}~}{{|}~~}{{zzzz{||||zxvttstvwyyyyyxwutsrqpppqrssssrrrstvxz{}~~~}}}~~~~|{yxwvvvwxxyyxvtrqpqrtuvwxwwvutsrrqqqrstuuuutttuvxz{}~~~}|||~~~|zywvuuuuvwwxwvtrqppqrtvwxxxwwvutsrrrrstuvwvvuuuvxy{}~~}|{{|}~~}{zxwvuuuuvwwwwvtrqppqsuvwxxxxwvuutsssstuwwxwwvvvwyz|}~|{{{{}~~}|zxwvuuuuvwwxwvtsqpqrsuwxyyyxxwvuuttttuvxxyxwwwwxy{|~~|{zz{|~~~|zxwvuuuuvwxxxwusrqqrsuwyyzzyyxwvvuuuuvwxyyyxxwxxy{|~~}|{z{|~~~}{yxwvuuvvwxxxxvusrqrsuwyzzzzzyxwwvuuuvwxyzzyxxxxyz|}}|{zz{}~~~|zyxwvvvvwxxyxwvtsrrsuvxzz{{zzyxxwvvvvwxyzzzyyxxyz{}~~}|{z{|}~~~|zywwvvvwxxyyyxvtsrstuwyz{|{{zzyxwwvvwxyz{{zzyyyz{|~~}|{z{|~~~}{zxwwvvwxxyzyywvtsstuwyz{|||{zzyxwwwwxyz{{{zzyyyz|}~|{zz{}~~~|zyxwwwwxxyzzyxvutstuwyz{||||{zyxxwwwxyz{{{{zyyyz{}~~|{zz{|~~}}}~|yvuuvxz|~|zxvtsstvy|~}|||}~~}|{zyyyz{|~|{zz{}~~}}}|{ywusssuvxz|}}}}||{zyxxxyyz{{{zyxwwwxyz{|||||{zxwvvvwy{}~}|{||}}}~~~~}}|{{{}~|{zz{}~~}|{zyxxxxyz{{{zyxxxyz{}~~}|{yxxxyz|~~}}||}~~}|}}}{yxxxyz{|}~~~~~}||}~~~~}}}~~}|||}~~|{yyyz|}~}|{{{|}~}|{zyyyz|~}{yxwwxyz|}~~}}~~~~~~}{zyz{|~~}|{{{{}~~}}~~}|zyyxxyz{}}{yxxxyz{|~~~~~~~~~}~}|{zz{|~~}|{zz{|}~~}}}~~}|zyyxxyz{}~|zyxxyz|}~~~~~~}~~~}}~~|{zz{|~}|{zz{|}~~}}}~}|{zyxxyy{}}{yyyyz|}~~~~~}}~~~}}}}|zzz{}~~}{{zz{|}~}}}~~}|zzyxyyz|~~|{yyyz{}~~}}}~~}||~~|{zzz|}}|{zzz{|~~}}}~~}{zzyyyz{}~|{zzz{|}~~}}}~~|||~~|{zz{|~~}|{zz{|}~~}}}~~}{zzyyz{|~~|zzz{|}~~}}}}~~}||}~~~|{zzz{}~~|{zzz{|}~}}}~}|{zzyzz|}}{zz{{}~~~}}}~~}|||~~~}{zzz{|~~}{{zz{|}~}}~~}{{zzzz|}}|zz{{}~~~}}}}~}|{|}~~~~}{zzz{|~~}|{z{{|}~}}~~}|{zzz{|~}|{z{|}~~}}}}}~~}|{|}~~~~~~~}{zzz{|~~}|{z{{|~~}~~}|{zzz{|~}|{{{|}~~}}}}~~~|{{|}~~~~~~~|zzyz{}~~}|{{|}~~}}|}~}}}}~~}||||}}~~}{zyyz|~~}||||}~~}|||}~~|{zyyz|}}||{{|}~~}}}}~~}|}}~~}||||}~~|{zyy{|~~}}||||}~~}|||}~~|{yyyz|~~}||{{|}~~}}}}~}}}~~}||||}~~|{zyy{|~~~}||||}~~}|||}~~|{zyyz|}~||{{|}~~}}}}~~}}}}~~}||||}~~~}{zyyz|~~}||||}~~}|||}~~}{zyyz{}~~}|{{|}~~}}}}~~}}}~~}|||||}~}|zyyz{}~}|||||}~~}||||}~}|zyyz{|~~}||{||}~}}}}~}}}~~}||||}~~~}{zyyz|~~}||||}~~}|||}~~}{zyyz{}~~}|{{|}~~~}}}~~}}}~}|||||}~~|{zyy{|~~}|||||}~~}|||}~~|{zyyz|}~}|{{|}~~}}}}~}}}}~~}||||}}~~}|zyyz{}~}||||}~~}||||}}{zyyz{}~~}|{{||~~}}}~~}}}~~}|||||}~~|{yyz{}~~}||{||}~~}||||}~}|zyyy{|~~}||{||}~}}}~~}}}~}|||||}~~|{zyy{|~~}||{||}~~||||}~~|zyyyz|~~}||{{|}~~}}}~~}}}~}|||||}~~|{yyz{|~~}||{||}~~}||||}~}|zyyz{|~~}||{||}~}}}~~}}}~~}|||||}~}|zyyz{}~}|{{|}~~}||||}~~}{zyyz{}~~}|{{|}~~}}}}~~}}}~~}||||}~~}{zyyz|~~}||{||}~~}|||}~~|{zyyz|}~}|{{|}~~}}}}}}}~~}||||}~~|{zyyz|~~}||{||}~~}|||}~~|{yyyz|~~}|{{|}~~}}}~~}}}~}}||||}~~|{zyyz|~~}||{||}~~}|||}~~|{zyyz|}~}|{{|}~~}}}}}}}~~}||||}~~}{zyyz|~~}||{{|}~~}||||}~|{zyyz{}~~}||{|}~~~}}}~~}}}~~}||||}}~}|zyyz{}~}|{{||}~~}||||}~}|zyyz{|~~}||{||}~}}}~~}}}~~}||||}~~}{zyyz|}~}||{{|}~~}||||}~~}{zyyz{}~~}||{||~~}}}~~}}}~}|||||}~~|{zyy{|~~}||{{|}~~}|{||~~|{zyyz|}~}||{|}~~~}}}~~}}~~~}||||}~~~}{zyyz{}}}|{{|}~~}||||}~~}{zyyz{}~~}||{||}~}}}~~}}}~~}|||||}~}|zyyz{|~~}|{{||}~~}||{|}~}|zyyz{|~~}|{||}~~}}}~~}}}~}|||||}~~|{zyy{|~~}|{{||}~}||{|}~}|zyyy{|~~}|{||}~~}}}~~}}}~}|||||}~~|{zyz{|~~}|{{||}~~}||{|}~}|zyyz{|~~}|{||}~}}}~~}}}~~}||||}}~~}|zyyz{}~}||{{|}~~}|{{|}~~}{zyyz{}~~}||{|}~~~}}}~~}}~~}||||}~~}{zyyz|~~}|{{{|}~~}|{||}~|{zyyz|}~}||||}~~}}}~~}}~~}||||}~~|{zyyz|~~}|{{{|}~}||{||}}|{yyyz|~~}||||}~~}}}~~}}~}}||||}~~|{zyyz|~~}|{{{|}~~||{||}~|{zyyz|}~}||||}~~~}}~~}}~~}||||}~~}{zyyz|}~}|{{{|}~~}|{{|}~~|{zyyz{}~~}||||}~~}}~~~}}~~~}||||}}~~}|zzyz{}~~}||{||}~~}|||}xtponnpswz{xvsrrstvxz{|~}|||}||zxwvtsrqrrstuwyz{||~zvsponopqsux{}|xtrpoooopppooooppqsuwyy{||}{zyxwvuvvvwvuusrqqpppqrtwy{}~}|zxvtrponnopqsuwyz|}~|{zxwwvwwxyyz{{{zzyxwvvwxxyyzzyyxxxxxxyz|~~~~}}|zywutsssstttttssrrqqqqrstvwyz{|}~~~~~~|{yxwwvvvuuuutsrqpoopqrssttttttttttuvwxyz{|}}~~~~~~~~~}}|||}}~}}|{zywvuttssssssssrqppppqrstuuvvvvvvvvvwwxyz{||}}~~~~~}~~~~~~~~~}||{{{{{|||{{zyxwvutssssssssssrrqqrrstuvwwwxxxxxxxxxyz{{||}}}~~~}}}~~~~~}}}|{{{{{{|||{{zyxwvuttttttttttssrrrsstuvwxxxxxxxxxyyyzz{|||}}}~~~~~~~~~~~}}|||{{{{|||{{zyyxwvuuttttttttttsssstuuvwxxyyyyyyyyyyzz{{||}}}}~~~~~~~~~~~}}|||{{{{|||{{zzyxwvvuuuuuuuuuuutttuuvwwxyyyzzzzzzzzzz{{{||}}}~~~~~~~~~~~~}}}||||||||||{{zyyxwwvvvvvvvvvvuuuuuvvwxxyyzzzzzzzzzzz{{|||}}}~~~~~~~~~~~~}}}||||||||||{{zyyxwwvvvvvvvvvvvvvvvwwxyyzzzzz{{zzz{{{{|||}}}~~~~~~~~~~~~~}}}}||||}}|||{{zyyxxwwwwwwwwwwvvvvwwxxyyzz{{{{{{{{{{{{|||}}}~~~~~~~~~~~~~~}}}}}}}}}}}||{{zzyxxxwwwwwwwwwwwwwwxxyyzz{{{{{{{{{{{||||}}}~~~~~~~~~~~~~~~}}}}}}}}}}|||{zzyyxxxxxxxxxxwwwwxxxyzz{{{{||||||||||||}}}~~~~~~~~~~~}}}}}}}}~~~}{yxwwxy{}~}||{{{||}~}||{{{{{{|||}}}}~~}{zyxwwwwxz{}~}|{zyyxxxxxxyyyzzzzzzzzzyyyzyyzzzyyyyyyyyzz{{{||||}}}}~~~}|{zyyxxxxxxxyyz{|}~~~~}}}||{{zzzyyyyyyyyyyzzzz{{|}}~~~~~}||{zzyyyxxxxyyyyzz{{|}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}||{{{zzyyyyyyzzzzz{{{||}~~~~~}}}}}}}}}}}}}}}}}}}}}}}}||||{{{zzzzzzzz{{{{{|||}}~~~~}}}}|||||}}}}}}}}}}}}}}}}}}}|||{{{{{{{{{{{{{||||}}~~~~~~}}}}|||||}}}}}}}}}}}}}}}}}}}}||||{{{{{{{{||||||}}}~~~~~~}}}}}|||}}}}}}}}}}}}}~~~~~}}}}}|||||{{|||||||||}}}~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~}}}}|||||||||||||}}}}~~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}||||||||||}}}}}}~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}||||||}}}}}}}~~~~~~~~}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~yuqpopsvz}|zwtrqqqrsux{~|{yyxwvuuuuuutuvvvvvxzz|}|yvsrppqrtwz}}{xvtsrqpppppqqrssttuvvvwwwxwwwwvvuuttssrrrqqqqrrsstuwxy{|~~}{zxvutsrqqqrrsuvxz{~~}|{zzyxxxwwwwwvvvvvvvvvvvwwwwwxxxxyyzzz{||}}~~}|{zzyxwvuutsssrrrrrssstuvwxyz{}~~~~~}}|||{{zzyxxwwvvuutttssssssssssstttuvvwxxyz{{|}~~~~}}}}}||||{{{{zzzyyyxxxwwwvvuuuttttttsssstttttuuuvvwwxxyyzz{||}}~~~}}}||||{{{zzzzyyyyxxxxwwwvvvvvuuuuuutttttuuuuuvvvvwwxxxyyzz{{||}}~~~~~}}|||{{{{zzzzyyyyxxxxxwwwwwvvvvvuuuuuuuuuvvvvvwwwwxxyyyzz{{{||}}}~~~~~}}}|||{{{zzzzyyyyyxxxxxwwwwwwvvvvvvvvvvvvvvwwwwxxxyyyzzz{{{|||}}~~~~~~}}}|||{{{zzzzzyyyyyxxxxxxwwwwwwwwwwwwwwwwwwxxxxyyyyzzz{{{||||}}}~~~~~~}}}|||{{{{{zzzzzyyyyyyyxxxxxxxwwwwwwwwwxxxxxxxyyyyzzz{{{{|||}}}}~~~~~~}}}||||{{{{{zzzzzzyyyyyyyxxxxxxxxxxxxxxxxxyyyyyzzzz{{{{|||}}}}~~~~~~~}}}}|||||{{{{{zzzzzzzyyyyyyyyyyxxxxxxxyyyyyyyzzzzz{{{{||||}}}}~~~~~~~}}}}|||||{{{{{{{zzzzzzzzyyyyyyyyyyyyyyyyyyzzzzzz{{{{||||}}}}}~~~~~~~~}}}}}||||||{{{{{{{zzzzzzzzzyyyyyyyyyyyzzzz{{||}}}~~~~}{zyyz{}~~|{zyyxyyz{|}~}|||{{{z{{{zzz{{{{{|}}}~~}{{zzzz{|}~~}|{{zzzzzzzzzzz{{{|||||||||||||{{{{{zzzzzyyyyyzzzz{{||}~~~}}|{{{zzzzzz{{||}~~~}}}}|||||||||||||||||||||||||||}}}}}}~~~~~~~}}}|||{{{zzzzzzzzzz{{{||}}~~~~~~~~~~~}}}}}}|||||{{{{{zzzzzzzzzzzzz{{{{|||}}}~~~~~~~~~~~}}}}}}}|||||||||{{{{{{{{{zzzzzzzzzz{{{{{{||||}}}}~~~~~~~~~~~}}}}}}||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{|||||}}}}~~~~~~~~~~~~}}}}}}||||||||||||{{{{{{{{{{{{{{{{{{{{{{||||||}}}}}~~~~~~~~~~~~}}}}}}}|||||||||||||||{{{{{{{{{{{{{{||||||||}}}}}~~~~~~~~~~~~~}}}}}}}|||||||||||||||||||||||||||||||||||}}}}}}~~~~~~~~~~~~~~}}}}}}}|||||||||||||||||||||||||||||||||}}}}}}~~~~~~~~~~~~~~}}}}}}}}}||||||||||||||||||||||||||||}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}||||||||||||||||||}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~yuqppqsv{~|yvtrqppqsux{~~|zyxxvvuuuutttuvvvvwyz{}~|yvsrqqqsux{~}{xvtsrqqppqqqrssstuuvvvvwwwvvvvvutttssrrrqqqqqrrrstuvwyz|}}{zxvutsrrqrrstuwxz|~~}}|{zyyxxwwvvvvvuuvvvvuvvvvvvwwwxxxyyzz{{||}}~~~||{zyxwwvuttsssrrrrssttuvwxy{|}~~~~~}}}}|||{{zzyyyxxwwvvuuutttsssssssstttuuvvwxyyz{|}~~~~~~}}}}||||{{{zzzyyyyxxxwwwvvvvuuuutttttttttttuuuuvvvwwxxyzz{{|}}~~~~~}}}|||{{{zzzzyyyxxxxwwwwvvvvvvuuuuuuuuuuuuuuvvvvvwwwxxyyzz{{||}}~~~~~}}}|||{{{zzzyyyyxxxxxwwwwwvvvvvvvvvvvvvvvvvvvwwwwwxxxyyzzz{{||}}}~~~~~}}}|||{{{zzzzyyyyxxxxxwwwwwwwvvvvvvvvvvwwwwwwwxxxxyyyzzz{{{|||}}~~~~~~}}}|||{{{zzzzyyyyxxxxxxwwwwwwwwwwwwwwwwwxxxxxxxyyyyzzzz{{{|||}}}~~~~~~~}}}|||{{{zzzzyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxyyyyyzzzz{{{{|||}}}~~~~~~}}}}|||{{{{zzzzzyyyyyyyyxxxxxxxxxxxxxxxxyyyyyyzzzzz{{{{||||}}}~~~~~~~}}}}|||{{{{{zzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzz{{{{||||}}}}~~~~~~~}}}|||||{{{{{zzzzzzzzzyyyyyyyyyyyyyyyyzzzzzzzz{{{{{||||}}}}~~~~~~~~}}}}}|||||{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{|||||}}}}~~~~~|zyyyz{}~~}|{zyyyyz{|}~~}}|||{{{{{{{{{{{{||}}~~~}|{zyyyz{|}~}|{{zzyyyyyzzzzz{{{|{{|||||||{{{{{{zzzzzzyyyyzzzz{{{||}~~~}}|{{zzzzzzzz{||}~~~~}}}}||||||{{{{{{{{{{{{{||||||||}}}}}~~~~~~~}}}|||{{{zzzzzzzzz{{{{||}}~~~~~~~~~}}}}}|||||{{{{{{{{zzzzzzz{{{{{{|||}}}~~~~~~~~~~~~}}}}}}}}||||||||{{{{{{{{{{{{{{{{{{{{{{||||||}}}}~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{{{{{{{{{{{||||||||}}}}}~~~~~~~~~~~~}}}}}}}}}||||||||||||{{{{{{{{{{{{{||||||||||}}}}}}}~~~~~~~~~~~~}}}}}}}}}||||||||||||||||||||||||||||||||}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}||||||||||||||||||||||||||||}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}||||||||||||||||||||||}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}||||||||||||||}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~}wsqpprtx}~{xusqppprtvy|}{yxxwvuttuttttuuvvwwy{|}~~zwusqqqrtvy|}zxvtsrqqqqqqrrssttuvvvvwwwwvvvvvuttsssrrqqqqqqrrstuuwxy{|~~}{yxvutsrrrrrstvwy{}~}||{zyyxxwwvvvvvuuvuuuuvvvvvvwwwxxxyyzz{{|}}~~~}|{{zyxwvvuttsssrrrssstuvwxyz{}~~~~~}}}}|||{{{zzyyxxwwvvvuuuttttsssssstttuuvvwwxyz{||}~~~~~~}}}}||||{{{zzzzyyyxxxwwwvvvvuuuuutttttttttuuuuuvvvwwxxyyzz{||}~~~~~}}}|||{{{{zzzyyyxxxxwwwwvvvvvuuuuuuuuuuuuuuuvvvvvwwwxxyyyzz{{||}}~~~~}}}|||{{{zzzyyyyyxxxxwwwwwvvvvvvvvvvvvvvvvvvvwwwwwxxxyyyzz{{{||}}~~~~~}}}|||{{{{zzzyyyyxxxxxwwwwwwwvvvvvvvvvvvwwwwwwxxxxyyyyzzz{{|||}}}~~~~~}}}|||{{{{zzzyyyyxxxxxxwwwwwwwwwwwwwwwwwwxxxxxxyyyyzzzz{{{|||}}}~~~~~~}}}|||{{{{zzzzyyyyyxxxxxxxxxxxxxxxxxxxxxxxxyyyyyzzzzz{{{||||}}}~~~~~~}}}}|||{{{{zzzzzyyyyyyyxxxxxxxxxxxxxxxxxyyyyyyzzzzz{{{{||||}}}~~~~~~~}}}}||||{{{{zzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzz{{{{||||}}}}~~~~~~~~}}}}||||{{{{{zzzzzzzzyyyyyyyyyyyyyyyyyzzzzzzzz{{{{{|||||}}}}~~~~~~~~~}}}}|||||{{{{{{zzzzzzzzzzzzzyyzzzzzzzzzzzzz{{{{{{|||||}}}}~~~~}{yyyyz{}~}|zzyyyyz{|}~}}|||{{{{{{{{{{{{||}}~~~}{zzyyyz{|}~}|{zzzyyyyyzzzzz{{{{{{|||||||{{{{{zzzzzzyyyyyzzzz{{||}}~~~}}|{{zzzzzzz{{||}~~~~}}}}||||||{{{{{{{{{{{{|||||||||}}}}}~~~~~~~}}}|||{{{zzzzzzzzz{{{|||}}~~~~~~~}}}}}}|||||{{{{{{{{zzzzzzz{{{{{{|||}}}~~~~~~~~~~~~}}}}}}}}|||||||{{{{{{{{{{{{{{{{{{{{{{{|||||}}}}~~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{{{{{{{{{{{||||||||}}}}}~~~~~~~~~~~}}}}}}}}}||||||||||||{{{{{{{{{{{{|||||||||||}}}}}}~~~~~~~~~~~~~}}}}}}}}}||||||||||||||||||||||||||||||||}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}||||||||||||||||||||||||||||}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}||||||||||||||||||||||}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}||||||||||||||}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m28.bin b/applications/SprPoP/dist/MUS/m28.bin deleted file mode 100644 index 6b1396f..0000000 --- a/applications/SprPoP/dist/MUS/m28.bin +++ /dev/null @@ -1 +0,0 @@ -}{{zyzz{||~~~~~zxwvvwxxyyyxxwvusssssuwy|~z|~|{{{||~~|zxwvuuvvwxyzzxtolrzzursuy|}||||}~yz{{}~~}{zxwvvwxyz{{zzyyxxxwwwvvvvvwz~~|{zzyxxxwxz}}xsru}{snkjjkklmmmmmot{~|zxwxz~{z~{vqnrz}{yxxwuurprxzxvvwxy{|~~}{xvw~|{zyz{~~}{z~ztmptru{|zxurokq}~{xvutuwy{}}z~{zzz{{||||}~}|toy|xxz~}|{|}|w|}|}~}zvqw}yvsrqpoooqtx}~}}}}~||}z{{vpjhw}xsomlklmopqrssuvxxtx~{yyyzzzz{{||}~~|||~wov~wsqruz~|xusrsrnltspppqrstwy{}}wt~zyxxyz{|||{|}}x{~~~~ztwwpmmosy{ums}vrpopqsvxz|}~}|yy~zwuuuvwxyyyz|~~z}ywyrmmotzz~|}}~~}xsnw~yusqpppppponoquyy{}z}|{{|woo~~xsokihijkmnopqrtuspv|wwwxyyyz{|~~~~~}|{xu~~~yt|tniggkpv||yssyuqqqrrrstvy{}~}xrr{xwvwxy{|}}}}||}zu|~|zzz{}~{w}|urqonnpty}u}ytqppqrtvxz{|}~}~{vqlo~{xwvwwxyyyz{{zw{}z|~wrnllmqv}~zuolw|xurpoopqqqqpppppmoz}|{x~}||~z{~xsoljjjklmnooprrqnuzuuvwwxxz{}~|wrmtzv{tnifdehlqw|~}~yxwvvuuvxz|}~~{wqqzwvuvwy{|~~~~~|yyt{}zyxxy{|}~~|yzxvusqppruy~}xvuuuwxz{||}~|}}wqjiz}|{{zz{{{{zzz{|{wz}}~{|}xtrpppqtw{~zuokt|wtqpoopqrrrrrrrrnltwtvy}|x~~~|wsqpoopppppqrsrnszwuvvwxyz|~{unq|yxz|{v{upmjihijknqv|~}}}~}ysp||xvvvxz|~{}|vx~{yyyz{||}}}~~z|{zxwvvvvwxz|~~~}ztnkv}ywuuwy{}~~~~z{~~~}|}zxvvvvvvwwy{|wqlr~yusqqqrsssssstuuqouxsrsuwz~z~~~}|{zxvz~{yxwvvutttuutpt{xvvvwxy{|~}wqr|wttux{zv}yurpnmlkjjjlosww{ztq}|xvvvwy{}}~x{~{yxxyz{{{||}~}z}||{{zzzzzzyyyyyxvt~~zuomy}wspoopsvy|~|~~~{|}{zyyyyyyxxwwxz}}~ysov}xurqpqqrsstttuuuqpwxsrrsstvy}}~}~~{wtpljp~}|{zyyyxurw{wvvvwwxy{}~{vqv{vsrqrtwz~}xw~zvtrqponmllklllkoy|||vv|xvvvwxz{}~|~{{yxwxxyz{{{|||yx}|||||{{|||||{ywtplju}ytop}}xtpnllmoqtwy|~}~y}}{zzzzz{{{zyyxxwuu}}wt|zvtrqqqrsstttuutqqxxtsststtuwz}}ytojfjz~~~~~}zw{zxxyyyyz{}~~{vrw|xtsrrstvx{}|xw~zwutsrrrqponnmkhjrrrtx||{{yxxxy{|~}|z~|zyyzz{{{||||zx~}||}}}}}~~~|zvrliq{{xwwy|~yupq}{wspnmmnopqsuwz|}{~z|~|{{{|}~~~}}|{{zwty~||~~z{xvuttuuuuuvvwvssy{wvvvvvvwxz{}~|||xsmhiu~|yvuuvxz}~~}}}}~}xsv{xvuuuvxyz{|~}xv|ywvvuuutsrrqqokkqrooqsvy}}}}}~|zwty~}}}}}|||}~~{y~}}}}~~~{wqkp{|wsrrtw{|wrp{~yvsqpppppppqsuwxvz{{}||}~~{w{}{{|}}{yxxxxwwwwxxxutz}zxxxxxyz{|}}~~~~}zwu{vpjjv}ytqonoqtwz~zuw~zxwwwxz{{||}}}xv~}zxwwwwvvuttttrnmsspnnopruy}~|xuqns~~~~~~{y~}}}}~~}xrnr}}xspooprvz~}wsr}{wtsqqqqqqqqqrsrpt|}|}z{}|||}~|y}~{zz{{}~}{{{zzzyyyyxut{}yxxxxyyz{}~~~}|yvqp{~{vpkn{~ytpljijkmpsvy|~yuz}zxwwwxyz{||}}}~}{{}ww|ywuuuuuuutttsqmoutpnnnnoprux{{wrmkr~|{zz{{|}~~~~~~}zy~||||}}~|wqov~xsommmnprux{~}xsu{wtrqqqqqqqqqqpnlqxvuvxz}z~|{{{|}|z~|{{zzz{|}~~|z{~~~}}|||{yvw}~zyyyyyz{|}~~{xtooy|yxyz|~{vqmq~{vqmkihhijlnpsvxzz{{x}{yxxxyz|}}~~~~~}zz~zz}zxwvvvvvvvvvusoqwurppppppqsuwy{|}~}{y}|xsnkr~zxwvvwyz|}~|{~~~~~~}ysqx{vrponopqsuwxz}|wx~{xvutttttttsssqnqwvttuwxz}}}}}~~|~}}||||}}}||zywuss}~{z~}||||}~~{wrpx|ywvwx{~}ytoq}~yuqnlkkkkklmnprttu{|~|||}~}||{~|zzyyyyxxxxxwssyzvtssssstuvwxyyyyxvsu~{vrnq}{xvuuuwxz{}~~|wsxzwtsrsstuvvwxy{}~}}{}{zyyyxxwwwwwursyyvuuvwxz}~~~~~~}{zxuspmmt~zurx~zxvvwy{}zvrr}|xurponnmmmmmnoppot{}}~}|{}}||{{zzzzzxuuz{ywvuuuvvwwxxxxwvtqnoy|xtps{xutssstvwxz{}~}}xuy|xvuttuuvvvwwwxyywy}|{{{zzyyyyxvsuzzwvvvvwxz|~}~~~}{ywtqmjip}~~~~zvu|~zwvuuvwy{}{vsv}yvsqponnnmmmmmmkkouutvx{}~|ywy~}||||{{{zywuv{{xwvvvvvvwxxxxwvtqmjmv}|zxxy{}}xtrx|xusrqqqrstuvxyyxy|xv}|xvuttuuvvwwwwwvusv|}||~~}|||{{{zzxvtw|zwvvvvwwxy{|}~~}|yx}~}{yvsokhhp{|zxvvvwxz}{ww{xvuuvwxyz|}~{wtx~zwtrqpoonnnmmmliimrqoprtvy|~|zwtru~}}}}}}|||{yvx}}yxxwwwxxyyzzzywurmjlu{zvtsstvy|{wuz~zwusrrrrrsstuuutu|~~yx~~{ywwvwwxxyyyyxxvtu{|zyz{}~~~}}|ywy~}zyyyyyyz{|}}~~}{yvtx~{xuqlignz~|yvtrrrsuwy{}zy|zyxyyz{||}~~~yvx~{xvttsrrqqpppoljmrrpopqsuwz}|zwsppy}zz~|{{{{{||}}}}|{xuqllt|{xtrrrsux{~zwz|ywvvuuutttuuussx}}}}~}z~|{zz{{||||||||zww|~|{z{{|~}~{|}||||}~~|zvrt~~{xtokhmy}zvsqpppqrtvwy|~~|~||||}~~~}{xy~{yxwvuttssrrqolnssqpppqstwy{~~}zvroov~~~||~}}}}~~|zwrnmu||yusqqqrtvy{}}z}|zyxxwwvvuvuurqv{{zz{|~~|}|||}}~~~~~~}{xx}}{zzzz{|}~~}|zxwz~~~~~|yuqt}}|||}|xupljp{}zvsponmnnoqrtuwyyy{~}}~~}{~{y||zywwvvuuttsqomptspooopqrsuwxz{||{zxx~}yvropy}{yyyyz{|}~~}{}~~~~~~~|zvropx~|xtqpoopqrtvwy{}~}}{zyxxwwvvvusqqvyxvvwxy{}}}}}}}}~~~~~~~|zxz~|zyyyyyyyzzzyxwusqqv~{xtrv~|zxxxyz|~|yuqnnu~~zvspnmlllmmnopqrrru|}{~}{z~~|{zyxxwwvutsposvtrppppqqrstuvwwvusrry~{wsqrz|yxwvvwxyz{|}~~}{~~{xtqsz}yvsqpppqrstuvwxzzzz~}|{{zzyxxvtswzxwvvwxy{|~}}}{{~|{zzzyyyyyxwutrommq{~{wtw~{yxwxy{|~|xtqpv}yvsqonnmmmnnoopooqw{{{|~~|zy|~}||{zzyxwusuyxvttsssttuvwwwwvtroos}{wttz~{yxwwwxxyz{|}}}|}}yuu{}zwutssttuuvvwxxyxw{~~}|{xwz}|zyyyyz{|~~|{~~~}}|||{{zyxvtqomklt~{xy~|zyzz{|~|xtsv}zwusrqpppooopponnswxxyz|~~|zxvy~~}}|{xvx{|yxwvvvvwwxxxxwvtqnlpx~}|{|}~zwv{}{yxwwwwxxyyz{zy{{ww}}zxvvuvvvwwwwxxxvux}~}{y|~|{zzz{{|}~}{yx}~~}}|{zxvtqnkijr|}{zzzz{}~}z{}{zzz{{|}~}yvuz{xvtsrqppoooonmklqttssuvxz}~|{xvtsx~~}{yxz~}zyxwwwwwxxxxwvtrolkox|zxvuuvxz|{xy~{yxwvvvvvvvwvuux}~~~{xz~{xwvvvvvwwwwwwvtsw{{zzz{|}~~{{}{{{{{{||}}}}|{ywuu|~~}|{zxvsplihkt|}{ywutttuvwyz|~~|}{zzzz{{|}}~~~}}~}yww~}zwutsrqqpponmljkprqppprsuwy{~}{yvsqqv~~~~~~~~~~}|zy|~|zyyyyyyyyyyxwurolkpx{yvtrqrsuwy{~}{|}{yxwvvvvuuutsru{|{{{|}|z|}{yxxxxxyyyyxxwutw{{yxxyyz{|}~~|{z{~~~~}}}}~~~~}{yvstz~}~}|zwtqnjilt|}|yvtsrqrrsuvwyz{|||~}}}}~~~~~||~{yy~{yxwvutssrqpnlmqsrqppqrsuwxz|}~~}|{}}zwtqpt}~~~~}|~~~}}}}}}~}}|zxvrnmpx|{xusrrstuwy{|~~~~}|{zzyyxxwvtuz||{{|}~|}~}}}}}}}}||{zxvx|}{zyyyzz{{||||{zywvv|~{xutx~~~~{yurnkls{~}{xvtrrrrrstuvwyyyy|~}}zz~~|{zyxwwvutrporuusrrrrstvwxz{{{{zxwx}zvsqs{~}}}}~~~}~|yuroqx}|zwusssstuwxy{|~~}||{zyxvvz||{{{|}~}~~~|zxz}~|{zyyyyyyyyyxwvtsrrw}yvuy~}||}~}zwtpnou}~{yvtrqqqqqqrsstuttx}~~}{zz~}||{zyxwvtrruwvtsrrrsstuvwwwwvusrt{~{wusv~}{zzzz{{||}~~}||~|zvsru{~}zwtsrqrrstuvwxyyyy{~}}|{ywvw{|{yyyyz{|}~~|{~~}{z|~|zyyxxwwwwvutsqpnmou~}zww||{zyyz{}~~{xurqsz|yvtrpoooooooopoopsxzzz{}~}|zyxz~~}||{zywuttwxwusssrssstttttsrqonqx~~~~|ywvz~|{yyxxyyyzz{{{z{~~{xuux~~{xvtsrrrssttuuvvuuw{~~}|zxz}}|zyyyyz{|}}~~~}{zy|}|~~|{zyyxxwvvusrpnlklrz~~~~~|zz~~|zzzz{|}~~{xutv|~{yvtsrqpppoooonmnquwvwwyz|~~}|zxvvw}~~}|{ywwz|zxwvvuuuvvvvvutrpnmnt{~}|{z{|}}zx{}{zyyyyyyzzzzyy||ywz|zxvvuuuvvvvwwvvuuy}}}}~~||~}||||}~~~}{yxy~~}||{zywvtqoljjnv}~}|{||}~~|~}}}}}~~{xvw|}{ywvutssrrqqpomosvvuvwxz{}}|zxvtty}{z}~}{zzyyyyyyyxwvtrommrx||{yyyyz|~}{|~}|{{zzzzzzyxxz{z|~|zyyyxxxxxxxxvuux{||{{|}~~}~|zxvw}~}|{ywuroljimu{~}|zyxxxyz{|}~~~~~~|yxy~{zxwvutssrqpnmorttsstuvwy{}~~}{yvtrsx}{|~}|{{{{{{{zzxwtrolmry{zxvuuuvwyz|~}~}|{zzzyyxwvvy}~}}}}~~|{~}{zzzzyyyyyxwvttx{zyxxxxyyz{{|||{zyxy|~}|zwutw}~~}|{ywuroljkow|}{ywvtttttuvwxyzz{{}{vy|{}}zwuuuvwy{}~~|wqoqnjhgfdddfhknqtvxyzyvu~}zvxzx~~|~}~{ywvtpnlllnpsuwyzzwpfj{}yz|}~|y~{}~xzskdgnkknoonlmnpsuwyz{}~~zy}w{}vtrnkmrwxvtttuvwxxwsu~}wohes~{{{|~}xw~{y{||{zz{|}}}|zxwusomvzx{~~}zwtrqpqu{~wz{{~||sommov~{vrpmkgnyyz||{yxwwxy{{|||}}|wu}|xzzzyyxxxyxwtqmifeehz}~zwtssuz~~~~}|zwuqlhdaiy{zzxvsponnnooooopqqnkssoqsw{{{tmifk{wtrpnlkjijjhcgmjiklmnoprtwxyzzzyvrko~||||}}~~xs}~xtyvqpqtx~~yridnwrnkjklmoqstuvwwy{||y|{vqw~~}|||}}{w|~}~}{zyxy{~~}{zzyzz{{zywtqmicao~|ywvvvwyz|}~~~{{{{|~~wu}}~~xqkfcl|wtqomkjhgggiigelpnnpstvy{~~y|wv~|xqptnjijmrw}~{vogguytqopqsvy|~ysnkw~|zzzzzzyyz{|}zyz{wusqppprux}}ytor~|{{{||}}|{{{{zxsvytrstvy}{uz~ytojeabt}{yxwwvuuuuvvrt|zxyz{|}~|uu}yxwwwxyz|~~xt{}|{{{{{{zywvusnjooiijmqv|yt}~zyyyz|~{wrnjn}|||||{{{{{{yvz|{|}}~~{uy{wusrrrsvy}|xsmkw|xvtrrrstuuuuuuutppwuqrsux{}wu~|zz{|~~yupljt~|{ywvuutpntwsrstuuvwxz|}~}ytpy~zxwwwwxz{~~zupq}{zz{|}~~~}||zwrtytqrtwz}uv}xutstuvxz{|}~~~|zvpkjv|zwvvuuuuuuuuurrz{wxz{|}|}{yxxxy{~~{wrmhm~}ywtsrrstuvvvvvvtqnsvqopqsux{|vpv~zyyy{}~ytnjk|~|{yxwutssrplnvtqrstuvwxz|~}xrrzvtsssstvxz|zv~~}{ytpuvpnoqsw{yt{|xuttuwxz|~~~|xtojfhy}{yxxxyyyzz{||{x||}zwusrssux{|xsmjs|xurqpppqrrrqqqqpljqrmmnorux}|x|xsnjfk~~}|zyxwwvtosyvssttuvwxz|}~~~}|ysnq{vsrqqrtvxz}{x}||||{zyxwvusnlrrmmnqtx}|w~ywuuvxy{|}}}~~{~{wsnjp~{|uzzusrqrsuxz~~zuoigt~yurpoopqrsssstuutpqyxutvxz~~||wsojgp~|{ywvvuuqntxtrrrstuwy{}~{vqz{yxxxz|~{uu}{zzzzzzyxwvvurmntqnnpruy~xx|wutuvxz{}~~~zuqny~~}||}}}zy~~~yu}xtrqpqsuwy|~zvqlo}zwvvvwxxyyyyyyyvrw{xuvwx{~y~}}~~zwrnjfgv~}|{zyyyxwst|zwwxxyz|~|vw{ywwwxz|}}xt{~{yxxwwwvutsrqokhnplkkmptx|~z|zz{|~}ytolo}}||}|||||||zw|}}}~~}wru{uqnmlmnprux{}xrq||ywuttuuvvvvuuusopvurrstvx{{{}}}~}zwtplhfo~~|yw}{zzzz{{|}{urz}yvtsstuwy{~}xst|yvtssssssrqqpolhkqnlmoqtx|~}||}~|yuqmmw~}}|||||{{{{yvv}~zyz{{{|}~}ysr{yurpooprtvz}~ztqt}ywusssstttttttspnswtrrsuwy|}y~|{z{{}~~|yurnkm{~}{wx~}zyyyyzz{}~|xst{xutttuvxz|~~zury|zxvvvvvvvuttsqnlrtqpqrux{}|||}~~{wsoln|}|{{{{{{{zzzzxvy}{{||}~zuwzvsrqqrtvy{~~ytry}ywutttuuuuutttsootvrqrsuwy||z~}}~~{xtqmko~|y|~|{{{{|}~|wsv~zvtssstvxz{}}xv~|zyxxxwvvutssqmlrspoprux{|yvrnkp}z}xsw}xtrppqrtwy{~~yts||ywuuttuuttttttropvvsstuwz}~{xuroljx~{yyxy~{vrw~xuuutrrrsuwxz{zyxx||tr{}xtplijpuutssttuurnorngfjligghkotyw}|x}}vwrifdcdgjmqtvvtmp~wolt|{vronoooz|obgzurstuvwxvpuxqqpmkpwvuuuvwxx|}zvvwxy|~~{ywvu~vsoklx}{yy~ulheszpkiijkmnnjrsostsrx~}{~}|zwu}}{zyxxxxwsnidccsxrppruxz}~{z||tkdcw~vpkhfddd`^gdcffecjrttttroiq|ywwwxyzyvsy~x{|}~}xtw~|xtt{r{|{{}xqhjysommlllmhkrmoqqnmr{|||}ztmz|xwwvvuusqpx}wqjp~wsrrsuvxy{x|}~}zut}{u~umdl{snjfcb`^Y^a\_``^]bkmmoomhew}vwx{}||vq~|{{}~{|}zvrr}xs|{{}~~yrkds{smjhgggggdmnkprsrqwyy}{xvz}{{{{zxsmgcvyrpoprtuwxxw}~{wu}wy}vnfau}wspmkihebkhgjjjhgkuwvvtpgmzttvxz|~~}zwy~xrr||}~~{wsr{~sx~xvwxz||wpq~yusrqpplmuqqsttrqu}ypz|{|||}}|zwtv}||||{xsmfj~}~yspprstuvxux|}~|yusyzq|~vnq{vrnlihbdkefhhhggjsxvurmfswttvy{~}zy|wqx}{z{||}~~z~}|{yvssyz~yuow~yvtrqqqlqvqstuuvvxys~}}~}zvv|~|{{{zwsnicnzvxwrpqstvxz|z}ztmxzvromkfkmhiijkllmt{zwsliz}vttvx{~}ywx~}}~zvr}{~}~|}}yuq}zwuttrnvwrttuvwxx~|st|zyz|}~|yvuv~zvqn}~|zzzz{|~}|~ztq~{zuromifmjggghikllqxxtphn~zuuvy}}}xx~|{}}}~~|yups{{xusrsopxttuvwy|}~x}yx~|yupjo}wx{yxxxxyz|y}|}ytov|y{yvtnrvpqqqrsuvvyyrm{ywvxz}yx{ws~~|{|}{xxy{}zy}|{zz{|||{xvt}{zxtz|wyyzz{}~~ww}|}~|xrw}|{zwtpkizzuvyxvvvwwxwu~|}||{z}{{}}xw||xtolsomnnnnoqqqtyxqio}ysrsuwz~ztz~}||}}{yuqs}~~~~~~~{|~|{zyz|||~|~|{{{{{zxurx||ywuqryuuwxxxy{||~ys}|z{{}}zupy~{ywvtqniek~zuqrvuttuvvvtx|}yvr{{y~{wslormnoooopqrrswumhv{sqpqsux{~~{uqy~~{yxyyxwtqo{~~}zzz{|~{y~}~{yxwwwxwvsqp}z}|yvspvwsvwwwxy{{{|}tszwvwxz|~~}{wqqy~~|{zwtpmk{{vw{|zyyzyxv~}|{|}}{wtsyvz{vplrokmmmmmopqppsrjlzxrqqrux|z{~}{wtu~}|~{xwwy{|~~~|wy{w}|yvuuuvvusqor{vx|xvqpxutwxyy{}y~~~wz|~}zwsnjm{wsrx{zxxxxuw}}}zyz|~~~|zvrw~xtw|uuzusuuuuwy{|zxxxqy|wuuwy}{~}|}|yvrw~}|{~ywvwxz|||||zv{xtz{yvvwxyyxvur{}}y{||~~|}~}x|yy|xtojr~{wtrw|{yyxxty|}|}|yxz|}y~yzy{~xwwvvwy{}~|xvtrzvtuwz~~|z~{xtp}~|}}z{}{{|~}}|x}~||}}}|{xvt}{~x~{}}~~|}~|z}vv}yupkix{xvsqu{{zyywu~}~}}~}zy|xx~y~|wwvvwxz|}}{vrowwtstvz}~xw}{xtpr{y}|zz{|~~~~~|x}wux|{zyzzzywtrv{zzy|z{|}~yw|{|~z~{su~{vqlq~{ywvx~~}|x{{wz{yyz}~}yv~~yvu|zz}vutttuvxz{{xrkl}ztrrtwz~z{|xuq}~~{tqy|ywwxyz{{zyuu|wrruyxvvvwvtqnm}{xv||w|}xyz|}~}v|~~zvx}xrljz}ywusrtz|zxus{|y{}|to{|wvuvx{}~{~}zy|}}}}{yvrt|xuu||}xxxxxy{|~~}yrlvzvuuvx{~{xy|yuqnq~}|~}}|unl}}xussstuuutrotvpnnquusrssrpnls|{~}}{}~zu~}}~|}utu}}xrmio}zwusrqsxzxuqu{vwy{|}}vnl~xusstvxz{||zy{{z|~}zwu|yy~|zyyxxyz|}|zunk{}vsssuwy|~~ywv{}yvqmkw}|{z|}y~~~yrjl~zwvuvvwwwvsszvsrqswxwvvvtqoo}|{~}{{|}}~}vv|||}}}zussy{vpjhu}zwusrrqrwzxtqxzvyz|~xpq|ywwxz|}~~~{}}|z|}{xuv|yx||{zzyz{|}~}ztmo{vsssuwz|~|}ywux}yuplm~~~~}~~}umq|zxxxxxxwvrvzusrqquxxwvuspnq~|}|}}~~}vz|{|~}zwutwysnp~|zyyyxx{~yx}{}~ypu|yxwxz{|}}|y~}|{zy~}{xvz~|z{~}|{{|}~~zunt|xuuvx{~|yzzvrv}ums|zyyyxxxwurwyttrqptyyxwusqnu~}z}zwx|vqv~|{{{{zxz~~z{}|}~wqx|zxxyz{{||zy||{zy|}{xu~~|{yu~}{{|~}{zyuq{{rmw|~~|zyyxwwvurrxvssrqpsxzywusqp}|yww}zuq}}{zz{{zxx||y~~{{|}~|tr|||~~{yxxxyzz{{x{~|}}|{~|yz~|vy}}|zz{}}zy}|wqr~woq{yz}|{zzyyxvswzvuvutsv{}{yvsqu}}zwttz}vqt|zxxxyywvvyyz{yz{|~yszzz~}{zyyzz{{yx||}|{{}|yw}|~|vt}{yzywwxz|}~~~|zvuz~wrnw~|tqz{vx~}{zyxwvtqqxurssrqprwywtqnn{}~{|~}{yvssyztq}~{yxwxyyxvuwy~yyz{|}{tu~{uv{}zxvvvvvvusw}yxyzyxwz~zwtv}zy{~}~~~{vqv}vuxxvvwy{|}~}|~{xw{}wqs~~}~|vot{vqtz~|{ywvutrpmruppqqpnnpuvtpljp}{{}z~~{xuuyytw|ywvvwwwvtssyywxxyz{}|vry~vprxzyvuttsssqqxyvwxxwvvx|~|wsrz}zxy~{vu}wvyzyyz|}~~~}~~{xvy{tpx}||}~~|yz~yroxzqlow{{zxvusrolmsqnoooonmnsusokkw}|~~~}zwtsv~~xt}~zxvvvvwvuspq{~xwwwxy{}}yss}}smntyywvttssrpszxvxyyyyxy}~yvv~{z{vx|vuxzzz{|}~~~|~{xvw}{tr~}|||}~~|yyxqr|ypklt{~}|zxwusorxtrssttsssvywsop{~}}~|yvtu{xw}zxwwwxxxwuqq}zyyyz|~{uyvont{~}|zyxxvty{{|}}~~}|xz|x||v|}wtw|}}~~}~|yx{|vv~~{|{tz{rllr{~|{yurxzvuuvvwwvuvyxtpt{~}}~|ywvyz}~|{|||||{ytv~}}}~zwvpnrz~}{zyxuu|}z{{|}~~}}{x|x}zw}xuw||z{~y}xu~yqkjoy~|zwssyxtttuuvwvttvvrpz{~{ywx~}~|||||||{xty~~~}||}|wx{snlow}~|zxwuru{yxyz{|}~}{{||yy|yy}}ywy~~|yy|~|{~~~|zux}tmihmv}~|zwtptwtrrsstuutrrssps~~~|ywtuz}|}{zzzzzzyxttyy{{zzz{|}}zvs{}uokimuz{zxvtqpvxuvwxyz{|{yyzzx{}{y~zwuw|~~~}{|}{xuuwzy~{zz{|}~~~|y|~}yts}wpkhglu|}|zxuqqvtqrrsssttsqpqrr|~}}|~~|zxvsqquy|}zxwwxxxwvtrvwuyzyxxyz{|{yvrvysnkjnuz{ywurorwutuwxxyzzywvwy{}{{w|zwutuz}}|{{yx|}}~~~}|zxurqtw{|zyz{|}~~}z{~}{wty}upkihmu|~|zwtqtvqqqrrrrssqonoqu}{~|{|~}|zxurposx}zyxxxyyxvtvyvvz{zyyz{|{zwts{~wrnkjmtz{ywtpotvsstvvwxyyxvttw|}~}yx}ywutvz}~}|{yy}~~~~|zwsqsx}{{|}~~}{~}}~zvu}{tokhhksz}|zwsrvuqqqrrrsssrommpz}~~}~~}{xuqory~|{zz{{zywvy{vvy|{zz{||{yvst~{vqnkjlry{zwtqqvustuvwxyzzyvssw~~{}|zyxx|}{}|xtpqw~}}}~~|}}}yuwztokihjpx}}{xttxusrssstuuutqmmr}zvqnnv~}|||||{xw||wvx|}||}}}|zvsw|wsomklqx||zvsuywuvwxz{|}}|yutz}~|{{z}|~ztonv~~~}~~}}yuzztpmjijow}~zwy|xvvvvwxyyxvsonu~zvpkkv~}}}|||zwy~{wvx|~~~~~~|yus{|wtqonnqx}~|xvz{xxxz{|~}zvt~~}||{{|~}}ysnmy~~}}}xwytqnlkkow~{y~}yxwxxyyzyxvrnmy}ytojk{~~}}}|zx}{xwx|}{wsu~zvsqonnpv|}{ww{yvwwxz{|}}|zwsu~~}}|{{{z{|~|wqmo~~~}~~}~}xuy|vroljijmu|~|y{}yvuuuuvvvvtrnko|~{wrlip}||{{zxvx}zwutvz|}|{zxuqpy~yurpnlkkntyzwuxxtsstuwxyzyxvrqy~}~~|}{zyyyyz}|{~~~|xsnku}{zz{{|{zy~~|{z|~{wst}|vrnkihggksz{yx|ytssrrrsssrpmjju|~~{xsnijv|{yyyxwutyzvtsstxzzyxvspmq|~ytqomljiilrwxuvyurrrstuvwxwvsqs~}~}|zyyyyxy|~}z}}||}}}~~}|yuplm{{yyyyyyywy{zyyz~}{wsqv~xsomkihggkqxzy|}xuttsssttsrpmko}~|~~~~}{yuqlim{}{zyywvtv{yutssswzzywurnnu~|wtqpnmlkklqvxwzzussstuvwxxwvssy~~~~{zyyyyyy{}|{|{|||}}}}{xtpmr}{zzzzzyx|~|{{z{~~{wts|~xtqomkjiikpwz{~ywvuuuuuutspnnu~~}{wrnkr~~}|{zxvz}zxwvuvx|}|zwspqz|xusrqponmmpvxz}|wvuuvwxyzzywuu~}|||||{{{}}~~~~~{wsqx~~~~}|{~}}~yuv~zvsqponlkloux}{yxxwxxxxwusqqz~zupow}{z~}{zyxxz}|xtpt}|xvtsssrponpsw|}yxxxxz{{{{ywvw}}~|xtt}}}~~}xuy}yvsrqpomllmrw~|zzyyyyyxwusqu}yuqs}}z{|{zzyxy|~{wrpw~zwutttssqonnqv}|zxxxyz{{{zyww|~||~{wty~}{~~~zvu|{wtsrqponlklpv~|{zyyyyyxwussz~{wsqx}{y}|zzyyxxx{}|xtprz~{wutssssrqonmpw{yxxxyyzzzywvx~~~~~~}{zz~~~~~~}zwtu~~~}|z{~}}}}||}{wtx{wtrqqponmlkkoy|{zyxxxxwvtrqu~}~~}zwtqu~}~~|zxz~|zyxxwwvwyzytpov|{wtrqqqqqpomkkoy~{ywvvuvvvvvutu|~}||||||zxwy~|{{{{{|{{ywutz~~~}|{yx|}|||||{z{}~|xtt||wtrpponnmkjhioz~|{zxwvvuutsqpqz~~~~}{~~}|{xursz~{}}{yx|}zxxwwvuuuwxvqnqy{wtqpooppponljipy{yxvuttttuttsrv}||||||{zwv{~||{{{{{{zxvtv||~|{yxx}~{{{{{{zzz{|zvsw~~yurpoonnmljhghpy||{zxwvvutsqpot~~~}|~~|zwtrv|zz~}{xy}|yxwwwvuutuvtpnt{zurpoooppponliiqxzzyxwvuuuuutssy~}}~~}}{xw}~}|||||{zwutz{{}}{yxz~}{{{|||{zzz{yutz}xurqpppoonlighpx{}}|{zyyxwusqqx~}}{xuty|yy||z|}{zyyyyxwvvvtqqx}zvsrqqrsssrqnkkrxz||{zyyyxxwvuv~}zz~~~~~~}{yvv}|z|~|z~~}~~~|{{zww~{wutssssrqolijqw{~~}|{ywust}|yvv}}yx{~}}|||||{zxvvtrt|{wutstuvvvuspmnswz}~~}||{zyxvux~{|}{ywy|z|}|}{zyw{{wvuuuutsrpmjlquz~~}{ywtsv~{xvy}yxz}}}|}}|zxutsrw~zvtsstuvvvuspmpsuy}~~}}|{zywuu{}|~|zww||y{~|~~~~~~{xwvw~|xutssttsrqnkjnprw}~}|zxuss{~|yvw~{xwy~}|{{{{{zywurpps{~zvsqqqrsstsrpmnqqrw{}|{zyxwutsv~|z|}|~~}}|{zxvux}yxy}~{z~|{{{{||||zwtsty{wtrqqqqqqpnkiknnpv|~~}|{ywurrv~~~~~|zxuuy|xvuw|}}~{yyxxxxwvtqnmow}zvrpnnoppqqqpnmopopvz{{zxwvtsrsz}|~~~~~}}{zz}}zy|}}|{zyxwusu|~zxvw||z{~|yyyzzzzzywtqqu}}xtrppoppponljknnmpu{}}|{zxvtrt|~}~~}~~~}~}}~}|zxvtu}{xutv{}}zyyyxxxwuspmlqz}ytqonnnopqqpomnqpnpuy{{zxwusrru~|z}~~~}}|zy{~{xy{||{zyxwuttw~{xwx{||~~{zzzzz{{zywsqqw|wtrqppppppnlkmomloty|}|{zxutsw~|}~~~~}}}}|zxvvy|yvuw{}{{zzyyyxvsplmt||xtqoooopqrrqonqronotx{{zywvtssx|z}~|{~}zz{}~~|{zywuv{|zxy{~~}{{{|||||zxtqqx|xusrrrrrrqpnmppnlnsy}~~}{ywuuz|~|zxx|}{xwwz~}||||{zxuqmnu}}yurqqqrstttsrqturppsx|}}|zxwuv|~{}~~}|}~|zxww}~|zz{}}}~~~~}|yuqrz|yvtttttttsrpprrpnosx}~{ywx}}zxy~|zxxz~~~~}}|{yuqnow}|yvtsrsttuuutstwvsqqsx|}|ywvx}z}~}||~~|zxwy}{z{~~~~~~~}{xtrt||ywuuuuuuutrprtspnorw}~|yx{|~|yx{~{yxwy|~~~}}|zxtpnry|{xusrrsttuuussvwurqqsw|~~}{yvv{{z~~~|{{}~|zxvv|~|zyz}~}}}}~~}|yvsrw~}zwutttttttrqqstqonnqv||zxx~}}~~~{ywx~|zxwvx{~}|||{zwtqnouzzxurqppqqrrrrqsvurpoorvz}|{xvuw|y|~}}{zyz{}}{ywutx}{yxwxz~|{{zzzzzywurpsz|{ywtrqqqqqqponpspmlllouz}}|zwwz~|~}}}|||}}{xvv{|zxvuuvy}~}|{zyyxvtqnmrwwvtrpnnnoopppopsurpnnnptx{zxvtuz}zz}||}{zxwxy{{ywtst{~|zxwvvy|~}{zyyyyyxwurpqv{zxwusqpppppponnrrolkkloty||{ywx}}~}||~}}|{{|~~|zwvw~}{ywutstw{~|{zyyxwuromouwvtsqonnnnoooooqutqpnnnpsxzzxvuw|z|}|~}{zywwxzzyvtsv~}|zxwuuwz~}{zzyyyyxvtqpsy{ywvusrqqqqqponqtronmllosx|}|zy{}~}}~}}|{|~~|zwvy~{yxvuttvz~}|{{zxvspnrxxussrqppppqqqpptwvsrqpopsx{{zwwz}{~~~}|{yyy{{zxuuy~}{yxwwz~}||||{zxusrv||ywwvuuttttsrqqtvtrponnorw|}}{{~}}~|yy}}{zxvvvy~~|zxtqquzyvtstssssssssrswyxvtsrqqtw{|{yy~~}~}|{{||{yww|~|zxwy~~~}{yvsty~|ywwwwwvvvvutsswxusrqpoprv{}}|}~~|zz~|{ywvux~}zwtqsx{yussttttttttssuyyxvutsrrsvz{{z{~}|{{|{ywy~|zxwy~~}|zxusv|~{wuuvvvvvuutsrtwwusrqqppruy|||~}}~}{z|}|zywutx~}{yvsqu{{wsqrssssssssrrvyxvuttsrrsuxzzz~~}|{zyyyyxx|~}{yvvz~}||{ywussx}|xussuuuuttsrqruvusrqpoooqtxz|}~~~}|{zz{zyz~}{zxwustz~}|zxurqsxzxtqopqqqqqqppprvvttssrqppqtwyz|}~~}|{{zxwvwwwwy~~}|zyvuv{}|{zyxvtrquz|yurqqssssrrqpprutrqpoonnnosvy|~~}}~}}||{zyxxyyy}}|{zywvtsv{~}|zxvsqquzyuronnpppppoonosutsrrrqppopsvxz~|}}||{zyxwvvvwx|~~}|{zxvuw|~~}|zyxvtrqrw{zvsqpprssrqqpopstrqpponnmnoruy}}}~}~~}}||{{zxwwxy{}|{zywutsw{~~}{zwurqswzxtqnmnoqqppoonptusrrrrqppopqux|}||~~}|{{yxwuuvwz~~}|zxvvx{}~|{zxvtrqty|zvsqpprstsrqppruusrqqpoonnnqty}}~~~~~}}|{zxwxy}~}|zxvttwz|}{yvsruz{yuronnprsrrqppsvvuuttttsrqpqtx~~|{}~}}|zywuuw{~}zxxz{|~~|zxussv|}{xusqrsuvvutsruwwuttssrqpoopsx~}~~}{yxwy~}|zxuvxz{~}zwttw|}zwtqppqsuutsrrvxxwwwwwvutrqqsw~|{}~}|zwuuw}~{yy{{|~}{xvsty~~{xvtrrtvwwvuttwyxvuuttsrqoooqw}~}{ywwz~}{xvwyyz}}zwtuz~}zvtrppqtuutsrtwyxwwwwwvutrqprx~|{|~}{ywttx}zy{|zz}|zwtsv{~}zwusrrsuvvutsuxxvuuttsrqpnmnqy~}}~~}}|{ywvw}~|zxxyyxy|}zwuux{}{xvsqpoprssrqqsvvutttttsrponos|~|{{~}|{zyxwuttw}~}|||{zz|}zxvtsux{|zxvtrqpqsssrqqstsrqpponnmlklou~}~}~~}}|zyy}}wx{xuslcgmhdhmrvy|~xsojjt|{|{yvtrqqt|}xss|~|~}ytrrv{~y}}~}}|xu|{tqolhfntsssrohgv}|||}~~~{u}wstz}ywrpn`Xcw{medhkswzvqwqkx~|}{ywwyzzz{{yxvtrsy|~}w}{wy|zqhnlm{tpmicig^fa`bbdglsy~~z}yxwvust|~}vy~}z~}~zzuswnoy}~{yxxy{{zww{~ytnmrrkj||wrldajifijlnpns}{|tksvkgkzy{|y||}~~}~{}}{xtmfltu}||wszuonp{~vl\c|rhc^]\_bfjloqsqztvteZV`tysdbi`[ZZZ\`ekqvyxsnxrltvsvyzyvtx~}w~|wtw}yuuuwxxxwwvwtwznd`t~tkgfgifholjlmiruty}}vngdfr|}}{z~|}zyxtov{y}{~|zwsrvzofs|nki`]^`cegjmqqoxyx}vvui`YW\jzmhkgbbdfikoqqkp}vttust}~{{|}}vx{~zpn}}x|{{||{yyzzuz}z~tjw{xywqljklmlhltmjrppruy~xurpnmq|~|wy}zuoslv}{wtwvsunf^cebbbcehljnvstux{||ypiea]\bq{rpupmllljfbs|vsrsux{|xy~~}{~z{zoft}{{|~|~|{{{wu|yxyz|~yokouvspnnoqofrvmmnprv{~|zywtpou{vqkz~~|vkev{srw~|tmghnzqlhfbbifcdedbllkoqsvz~}yx}}}zqmid`^`etz}|tnh_esnjjlpswz|}}|y~}}~}|z}~wnl||xwonoty}xr~{ytzytuuwxz~|tlimv{xurqmotklsqmorux||~|yvrmk||phm~xuttvz}{}|vmy{nfjnmt||rg[atzyrmjjifafic]cgbdfhkosx|}x~|{}|{wplgaY^bi|}|ogtriedehmrvy{}~zx~}|{~vly}vlglljnsw{yw~{}}y}vvutttvxzzzxvtt|xtppu|~xrwtqrplqvstvxyxupw~{stunr~voq}xsnkkmpuz~||{rmjefqsu{xmiwxleemuwurpnmjf_emcaccdeimrvz|{xs~~z|{}~ujgka]]cqr|ynigghknrvy|~}{~|~}vpopx|upliegppnrvzyysry{~yxwvvvvvusokfm}~yuvx~ywwwwut|zxuoky}{|yvsrwuwslhhiknrvz~~{ss{tpooompyyz}xnd^]er|~zwtqjlkcjieefilpruwvqo~}|~}u{y~xnjgcacl}~{zspprstux{~}}ukan~upnnnmilutrp{~{xwz~|{zyxvsojd`o}ywwyx|x}~{{nn~xvw{|zxy}sssmklmnqtyzzwv~~zxwwwtpv~x}{smiebdmy|uzwqnhhpomnopqpnhozxy|~{~}~{spmljjlu{}{yyy{|y}uml|vrtyzwtrqqqomotzww{}~zvqke]_tytpmlnsx{{~um~{ursvzzusv}yrouupnoqspv~xwqs{~zyyysn}|{vsokgejtz~zvuspmrwrpnjdaqytrqtw{w|zxxz|{{{wtroliily~~yz}wwpxxpifjryzwtssrmqquwtwz~}~}|rh`Zhtnieddfimquy{|xz~{{trtvy|zqh_a|~{yvqsxtqmlutsvy}~x~yurpqw|xsxyqs|}}{wtpleerz}{yywsquqf_hsmhghkoty|{~~}vuuwxy|~~~{zvqjaZety}}~~xoq~vqmifefmuzyuorwqomqxvw{zvomcftpib^[[]`dhkosutz|utz{xy{|}~~uldkvrrx~}|zvsvtuupqrtuy}~}{xxxwutu{|z}xvvtrx~}~}wqtpilt~~ztkekuqieddfimrvy|{ut{zvvwxxww|pefpkcahu|vtyzxpnmmkihkquw|xssssqqx~|}~~|x}|vkdusic^[Z[\_behkknyzyz~|z}~~~~{up|ulfdju}wty|xuttuwz}{|y}}}|zwuvx|yxxwuu{}xvtrx|wspor|~vnrwtqjffgjloruxz{{yt||{ywy~|yxux|}{tsxzqjdaafq{w|{trwwtsrqomho{}}{yxxyzyxzur~uw~rpme`_^^_`begflsrux|~~s{~voiedgp{~yx~}z{|~{z~yu{|}}|ytpspit}zxy~zzqknqnnopqsuxwz~x~|y|y}~{{}|x~vnkjihjoz~yv}|zvv|{xwtptspt{~xttv~}~~ulfiida`_`baajmmorvz~ysoligfku{~|xsz~{xw|{wrlbd{}zyz}|{~|xuqpvwsrrssqx~||||}xw|{{|~}}uvxsqpnlkmt~{tx~~~~}zwzww|wtrpry{tnu{vwy~{top|vqlfeigca_[`fdehkosx|}}xzztqnkhgfiyxqp}}{z|~}{xvyxpidnyutupnosw|wnq}|zwsuzvrmpwttvy|{su|wtuz~{xy~{|}z|ytu{zurpnkfm{xu}{zz|}~~~|utxwvusrsxyqv}totwuvz~vlix}xtpmgcfg^\b_]^_bejoswz|{ws{~zv|~zvw{wrnkddiip|}zysxzurrsvz~~|zxustwwrjlz{tmfejkjlpsx|}v}yx~|wsuuwtpqqrtvy{}}|yuqyzxwutsv}~|zx}~|xss~}}}}zx{~ysmkpmjpzxw~zwuvwz|~}zxz~zyyywvuxzyy|vspnqz{z{~{t{~uprx~~zwsojc^ij`^]^^`cgkoqqpmq}{zts}zwz{smqohfflv}ss|tqqqsvz}~|xqkedi{}unjfcaemmlnqruxssyyx||yussstuvwvtqnozw|}zyzywvuv}~{vpv}{{|~~z}zzysomlmt~}{xwwxy{}~~|{unjv|wvvvvuz~}|~unjjpz|wqnpheigcbcegijkjhfq~}zxxx|~vx|v{ztqnlknu}zz{wvwy{|~~yrjact}~|vqljjigejqplqz|~}zz}{z}zxwvurplip|zxvx~~~|wx|u}|yz{~{{wusqqrx}}}}~vqy|}~|{{vrnmov~}~~xsnijonlkjjhfbcqywutvy}~~}{xwvttu{{y~~xpjq{wqopttspnmlljgjuwtvy~}~ytplgfs~{yxyzzyx{~|}|}ux{xxz|~}|yvsolknt{~~~~{}|vtv}y|}yvroorv}|ywtqnqsokfa]cppmmnpsw{~~~~~}zwsomp}}|xsruzsmhfinstrpnlijmmqttvy~||zrjejx{xussstvwxyzxwz~}}~|~{xxy{}~|~|wrle_]i}~{{}~zwttv{~~{}~zvrmipw{}{xupmni`_jlfccdgkoswz}|}}}}}~~{uohjuz}}~~~~ysqz|uokgdcdinqpljonjiimtuvy|}uponw{vrnkkkmoqsuvwuuz{~~~|ww|}{z{{||{z~{yzwng`bmqoqv}}|~{zyxwwz}{z~}|ywv}yssuomov}}ztlccnrjd`__adgkoruwxz~}}|z~~towzrmlqy||~}~~~~zvs{}xxvpljhfccehkrtomllkkmtyxwwvux~untzrmjhhhjlnprrrx|yww||zz~|zy|}|{xzzsonjowslfcdjt|}z~~|zyy||ytpq}xtqoot|}vnmsqnkea`acegjmoqqw~{~}|{xyrmkkow~|zwtu}xrquuqnljfbbkqrtsqqqrrqrwwqnu~zywqmkkklmopoqy{yz|||}zuz|{tmky|tmhdbbgoy|~xuvss~{ywvw{y}~tlgjlihhhjkmmnv|{|~|zvtrrsw~{wtpw}{yvsswwspjknjilsxzzyxxvsmjnz~||~wsvvronnnnlryxyz|~|z~|wry{ywrlifdcdjs{~xqkv~|{|~}yvuvx~xtokkopmkkkhksuuwz|}~||zxvuvzzton{~}||{zxusuvrutokiikry~~|xtoikuz{xuvy}zw}zttwurokkrsrtvy{~|z{|z|}zwsuyrorrnjgecbbiy|ysu~~zwvwy{}{tlit|ywtqmoroiglnlmnqtwy{||zwrny~|yvqqy}zzuou{xwvwxyyyywsmrzxspnlkjkouyxsmkuysmkpssux{|w~zurtsnpqnmnoqtw{}}yuv}{zzz{}{xuqnoz}yvsolnpniea^cfgox~~|{|}}zyysrvvtstuwxx{xqszxstx}}zwsnklqqkhhiiklnpqqpnlo|}|{{{}~{vxytsw~{tnt{wtrrrstvwwvtuyvqswurponljijkmv}ysnkiimtvvxz|}}wqs{xrooopqsvxyywtt}|z~}}}}}}~}wrnkq~|ywutsrpmlnnhhjebbekt~{y}xspr~|xtqpswvttsrx~~}vpmot{~ztprrrpljiiijjjihfgq~~zwx|~}|}}}|}{xutuzzw}{vsrrsstuvuuz}zxwutx{yvtplfaes{zvronnnnotyyxw{~|}xwzyvtuuvvvusqu~{zy||~||yrox}ywvuvvvvutqljsunjhgfhls{zsqz}zywtsvyvtz|{|{wtrrv{{uqqtromkjhfcadq||{yxwvuy}}}{xwx{~z||zxwvwwwvw~}}}}}|{z}{tnhkrrpqtvvvuuutssvy}|}}{zxurpw~}~~{urx~zxwwwxxyyxwttwsqttqonnmnpty}|}~}{yurx~{{|~{yxx{~zvssurmhda_dqwwuttuvwwww{~}yurqrv}zy}}{yxwuy~}}~{xxurwzvplkmrwzzyxxvsrx}~~~zupqz~|{{|~{w||xvvuvvwxxwuy|yvtqswwtrpmjggp}zy{}ywzyx|}}}~|yvu{|xronle__iqqonmmnprsuvvutzyrlgjx}zwy|zuuzzxyz{}zsovysnkjjlpvz{zwtxyvsv{|||}|~}|xsx~|xutuwy|~~}}{|{xutssssrquzywwwvussvwsnhbajruy}|yvuw{~{}{xuuy}}}}~~}{z}~xwzyx|~ztlecdkqplhfddegikmoppptxy}woms{zxy}~|yusv|{wuuvwxz|~~~{xtw~~yywspnlkjknruvy|yutrqrvyxvutrrvztz{vrqqrtvy{}}}}~{ywxz}xuvyxtrqomouvtuuvwxxwurrqmgjpojghnv}|ywutux}|zyxwx}}zwtt||wuvy~{tmggpwvohdb``acdfhiintussuz}}{xx{|vqnotz{wwyy{zxvwwyz{{zxuu~|wrsvwtrpnljimw|{zxvutrpqttplks~yusttvwxzzz|}yupoy|zwuwzwspsvustuwxz{|{yuojlw{tmhedfks||yurw~~|zz|zuu~zxvwz~|wsx|tonlhdcbbcdeefmrrstvwxz|y{}yytpnnqw~{x{~}}|{zwtx~|zxwy}}zwsonsstx|}|{ywtpkjkmv}~|{{{{{z~zut||yvwy||ywwxy{}~}zvqsyzyxsolkjkmrz}||zts{~|yuttvw{zsmjlligeedcelopqsuxz|}}~~{{wtqpqty}~}zvt{~|{}|xyzvrqrvz}~|xtojfhv~{z{|~{z{yzy|~}~yx|}{z{|}~~~}zvu|{urtutqonmnosz~|}~zx}~}~ztnjkv}ytokkmliebfjjjlnqtwy{|{yuu{}yurpmnw{vw~}~~zx|{wurqpruwwupkjqyyvwxwvvwy{~}~~~~~~{wst{yyz~|yz~~}|{{zywtrw~{xurqrvvsqnkinuy}~}}zy{}}}}yrmnw}~}zwsokhhhhiheefgilorsttsppw~~~}}~|wrnorrsx~|wvy|yxxyz|}~|{}}|}|xurpmkjkllmr{|xtpmmqrrqrtuvx~|{zzz{{{zxvtt{{ywvvvy|}{yz}~{xurorz|ywvuutsrrturmmonlmqw~zvw~|{{|}zwwy}~}}}||~zx|~yvtvy|}|zwsojgkolhfeefhiklmlkkqz}zwx{~~~~}||{xywrppqu{{xz~zxwxyz{|}~}}|zyz|yurnjfbbhu~}yuqnljjmqqpoory||}|{zyxvtsrw~{{}~{zxwutty~}||~}xsqv}}ywvuuvwwwvtrprwvqnlklnrx}~|z}|ww~}|z{~}|{yzurqrvz}}|xtstpkkmmjiiijiihgiqyyxvuuuy~{}|xutssvz|}~|{{{|}}}}~}zz{{wqkfdhqvwxxvtqpnmkknppswwxz}~|ywtrry~|{z{}{xvx||{xxy|ywwxyz{||{zvuwxxwtqonmmnquxz|~~}~{wtrrtx{}}|xtpoprrpmkifdhqwwvuuvxyy{}zxwvwx{~~~~}|xuusonrwwtrstwxwvtsqnklsyyxxz||xtrt|}|~~~}}{xz|{{{||}}|{z|}{xttvurnkhfefkt}}}zvtsu{}zvrpppmiffjostttuvwxyzzyy|{wsrsx}|zxxxz|zustrnjecdjs}{vqmighmqsux{~{yvsqpr{}xuvvusrrtvz~~{xxxz}{yxwwvvvvwwxy~{wspnnqtwxxz~zxwwxz~||~{yx{{{~vojfeehlqtxz{zvplkmmllkkidbgu}ztrrsttuw|~~}~~~~~|{{}~~}yvsrsux|~}~ywx{~~}{{zyz~thdr~wkfefghggfdb`_eqsonoqtx}xmlytqonnnnoquuppwtoprv{zpxxrqqrtvxz{{{xu|~vtvyywvuvutrokqzuqoqvyzyyyvy{wwy}~x{|xvtqmlkiiikkq|wvvspmklqvvsng]htkgfhjlpsvxyz~}|z{}|~}|{zz||||||zwso~}{z{~{x}{tmr}rjb^[]bgjkjig`cgcjnnmorw}zou{upnllnprrrqqowzrtssrsx~vyxxxuuuwyz{{{xw{z|zxwy}~{wrjc\j}zvsrrrswz{ysz}y~}{yz|~~|v{urruwurolidmppx{{yxwwwvvvrgcrofdcefimqtvxxytx}}~wml~}y|~}|skzxohb^]^`dhkmjfmieggjmqsw{~~x}|x{upmlmorstuurnvtuzyxvwy|yvvy|}}}||||w|{~~}}}}~}wmc^pwpnmpqtvxyxxwwvx}{~}vv|zyxxwxzzwpmslknrv{xpgp}skfeegjnqsuwyyv}z~t{ysqsx~}}tu}uqoljhgeddeigltnmmlkkmsy}~~|xr}~xsqpqstuvwxsv|vvvw{~}z}||~~z~zsohl|wngbadjpvz{|||vy}{{s{|{z|}~~}{z{w||tsqpptywp{zqolkjkmopqsuwuu~|{}~~|w}|}wrqru{zsxrmknppnkiheajmlpqqqrstuvz{yrr|z{~}ywuuuuuvwvs{zwyyyyz}uw{tkeuxqic_^`diotx{zx}yz{|wv~zwvwy{}~~~~yw~{{{yvuuwz}x~}tnklopppppqqqovyvy{}~|tv~}zwvvwy{ysw}xtqpnnoqromfgkefimprtvwxwuqljywtstvz~|xyzyxvuutpuytwxyz{|~zu{xx{}~}xqjsxqmjfb`__abfkopz}y{{{z|~vo}yurqrtvxz{|zx}xz{}}{zzzzzyxuy~yuronorsrponjmsoruwxy{}~}{yur~yxx{~{|}|{zyxxy{{yrp|xtrppqqponomlpkghggilquyyxtnfkxtrpooqsw{~~~{yz{{xvpqwssuvwxz|~|xuxuttvz~}xrknyrkfccdcb``_`^epqvz|||}~z~vy|yuqooqstvvwty{|}}{{~{voky~}|{ywvusstutolrpmqrstvxz{zxsniszurpqty~~}~~{yuu~~yry~ytqpoprsssqniotppnljijlptxxtmjwxojiloprtwy|~y}||{w{ztvvwwxz}|us|ywvvwz}|vpkwysnjfdceghfc`[cdcjpvz}{~{ytpqsssstuuts|}z~|rq~{tsuy|~}zwuqvzsrrsssuwyzxuqkhx|zxurppqsuy}{}z{~|trxtqpqrtuvvvsqxtprtutsrssssrpkr{solkjmrwyz{|}{}z~}{zz|~}v|{wx{}~~~xpoytpmllkjighjfejebceiov~v~}x|{vsrsvxxxwsu~{{}~xwrpqu{~x|{xzyxwwxyyxuqmgk{upnortuuuvwyx~~x{{wtsstuvwxxtx}xwvutvz}}|yupidt~xurqpqqrsuy|}zy}}{}zv}zxxz~~yqy~xspooopoonlicjojigffhlry}{}|}u|zyxvtvyxt{|xy{}{y}wustvy}|zz{||{zxuqkfbr{vqnkjjmruvvvtqz}|}{zvtuvuuuuuvts|{xyyzyzz{~zrhkxtnlnpstvvvuttvu||}|{{}{t|}zyzz|}~}xqu{uqnmnoopppojjoihklkjijlnqtvut|zy{~|{{|}}|zyvtpu}yxxyy{}~z}{{zyxxxxy|{z{|{wqkd_gxwrnjhgghijlorsotxsux{|xurr}wsqruwvutrouxtvxyyz{}}}{ywqo{zpjggimqtwwwusopxwy{{||~{y~}}|}~}vw|ywwxy{}~~~~vr{uqmllmnnnnkjqokkjjkmnnnmkifcl~}|zzzz{~zwuvy|~~|zusyvtxzzyy{}|w~}zyz|~~}{zxwu}}zxvrkccrwqlheccdegijjjinxusuuvx{~wpjn}zxvtttuwvrsyutvwxyz|~}yrmx{tnkihilosvxwurwwrsvy{{{|}|{xury|{}~|y}{{xwwxy{~|{~y~|wvtrqpppponkotooponmmnqsroib_lspprvy{}{xwwy{}~{~zxxxy|~|wzylk|rida_^]`gr{|wpoyyqqsux|}rnyw}|vm`_ruhb`abdddflqmhabmkffhkotzgo{cjs{~yuqmnz{njoga_`bglrx|{m_xxrsw|{z{ps|wuvx|}w`Ruug`_afjnruwtq|}yxurz~~sx{zz{}|ysz}qt{vuuwy}~wrxvynu{wvx{wndbypjhghhjlnjjvututqyv|{{zxtpwz~ywuvwy{{yvsqojj~{yz{|~~z}yrkbfzsmhdaa`aZ\fccdc`gpopqssqleu|xuvy|~~{txy}{u{zw|uytphrzvsqonnnnhmsoqqqns}||}}vr~}}}|zuos~~zyxxz}~}{wtomhu{xwxyz{}|zrkdtzsniecbba[cebddebfqpopqqngh|~yvvxz}{tu~~~zwu|xw{{wsnlzvsqonnoljsqoqqqos}}|}~|yqx~}~~}zuor}{xxxz|}|yuplhj~xvvwxyy|~||~wphkysniecbb^_gcbcddaeppnoomibq~xutuwz}~}ytt|}}~|xs|{w|u|ytpkw}xurpnmmniptoppqpnr}}{||zsq~}}}}}|ysop|zwuuvxyxurmheuzuttuvwxz}}{}}|unizytnjfdca^ffbdddcadopmnmkcfxxsrrtwz|~}ysr}|||}~~yts{wwz}}~~~}|yvrop}xvsqpopmmvsqrrrrpr}{|zwow~||}~~~}ytop}zwutuvwusojei~ytstuvvxy|||yztnq{upkhfe`bjeddeeeceornnlhan{tqprtx{~~zur||||||}{wrz|y~v}|}~}||||zxuroy~ywtrqqqmrxrsssttst~}|zup}|}~~{vqpz{xvuvwwurmidqz~zttuvwwxz}{~zzuo|~xrmjhfahkefffghhhpuqolfdvzsqqtwz~}xtx~||}zvr}~z}|}}|||}|zyvsq~zwututqyzvvvwxz{y~{tu~~zurt}zxxyywuqmheyy|}xwwxxxz}~}}~|wt}wronkgonjjijlopmqywrnfj{ytstwz~}xu~~~{vu}}}~|{{{{{{zxwtw~zxxyvu~{xxxy{~}{s{}ytq|{{zywtqmhjz|}zzzz{|~}|w{zvsrlmtomllmorsqqyxrmfr}xtsux|yt~|yt{}}z}{zyzzzzyywvt{zzzuz{zzz{}~xt~{sq}{zyxvsolgsyy~{zzz{}|~~{w~|xurlstoonoprtusryyrjjz|ussuy}vv~~}zwt|z{}{z}zxwwwxxxwwvtv}||yw~{|{|}}uz}}~zqu~|zxwuspmij|ww}}zyyz{}{|~|y}|}yvppvqppopprtusrxwngr|vrpqsw{|uy~|{||}|{yws{}xwwy|}~~~{vz{|yutsstttttssr~|zuz~z{{||}~~xt}{|}~vrx}zxwuspmjgt~zut{|yyyz{zx~|}~~~~~~}{yx{{uputpqpppqsuvtqutjkyzsqpqswz~ywz}~{zzzzzxvsszvuvwz{||||{vwxuywsqqrrsssrrqt|xw~}z|||}~~wz|{|~}uvwz~{ywtqokhj}|ytsz|yxyzzxy{z{}~~}}~~~~}|zx}{}|utytrsssstvxxvrttkr}xsqqrux|z|zz|{{{zywurx}~xutuwyz{{{zwtyuqwvrqqrssssrrq{~y{}}~y|}~}wzvw}zwtqnkhp~|yvtz~{yzzzw||yyz}|~}~~~|{y}}wy{vuuuuvxy{{ytsspz~xtrstx{~zx}|{zyvtr~{}~xvuvxyzzzyyvu{upuwtrstuuttsss{|wu~zvsokiw~|{yvy~{{|{x}}zz{~|~||~z~yxxxyz{}~~}wsrvyvtvx||x}{ywtt{x~~ywwxyyyyyyxsw{upsxvuuvvvuutsv~~}xu~{wrnjl}|{zwx||}zz~x||yz{~|~~|~~{zzz{}~~yqp}~yvuwz}|x~|zxvryysy|wvuvwwwwwwtqxxsnpwwuuvvuutsr|{|wt{zuplhs|{{zxw}}|{w}{ryywwy{}~~~z{}{z~|{z{{|~}vmq|wuuwz}|w|}zwtqp}tlyyussttuuuutorxsplnuvtttutrqorw{vsz}xrlgi|{ywwwut{~{yvv}{}ul}zussuwz{}~~}y}{wy~|zw~|zyyyz|}~yqjxwtstux{~~zv{~{wsoluxni|wsqppqqqrqnltsnmjkstrrsrqomly|wyurx{uoieq{xvuuussy}yvsy}yz{}{pm~wsqqsuwxzzzwx|yxuv}}}~}|zww}~{zzz{|~|wnm~}vsstvx{}~yux}yuqmm}~ujlzurppoooponknsomlijqtrqrqpnloz|zvsv{tniiz~zxvvvvusx}{uu||yz{}xnq}vrqqrtuwxyxuz~zyxvu|~}}{yv{~~|||}|vot~wttvxz|{ww~ytplq}sjp{wtrqppppomkqrnnmkjpvusrrpnlt}|xuu|vojo~{yxxyyxuw~}vx~{z{}~xpv~xusstuvwyyxv}}zzzxw|~|zx}xs|zxxy{}~zy{vqnw~tmu|~zwutsrqqqnmsroponmqxxvtsrpo{{wv}~wqmv|{{{||{xx~y}}|}~xs{|~{wuuuvvwyyvw~|z{{{y|~|zz~xx~{{|~}z~{vrp~|sqyxz~{xvusrrrqmotqppqpoqxzwusrpr}yw|~wqp~}{{||}|zy}~|||}~vx~yy{xwvuuvwxxuz}zzz{{z|}{y}}x~||~~{}{vquyqvztv}~{xvtrqqqnlrsoopqqpqwzwtrpow|ywz}vpv}{zz{{|{yxz{~|{{|~yu}}uu{}zwutsstuvst{{xyz{{z{~|yy~yy}{}}{{}~|y{xrp{}tszwoqy}{yvtrponlilqnmmnoonouxuronp~{wuxysr{yxxxyyyywtuz{xxyz{}ztxxppwzwtrqppqqpouyvvvwxxxy~~zxv|zv~zw{|zz{}~~{xy{tos~~~wrx{rkmw|zwurpnmkignnkllmmmmmsvsollu~zwuwwrx|ywvvvwxxwuqr{xvwwxz|~{ut}~tmltywtrpoooomowvtuvwxxxx}}yvv~}wxxvz|{z{}~}~{xxzsp{~~~{|{tt|yoijs{{xvspnljfiolkklmnnnmrvsnkm||xvv~wuzxvuvwwwxwupq}~xwwxyz|~~ytx}skjqyxusqpoonlrwuuvwxyzzy|~yvz}|w}yvz~}|}~}~zx|{tt~{}ztxxogfpz|zxurpnkhmpmmmnopqqoqvtolr|xvzyz|zyyyyyyzzxrs~{yxy{|~yu|}umioy{yvsrqqonuywwxy{}~~||{x~|y|xz~zz}vz}zu}ypifmy~{xusqmkrrpopqrtuurquvqnwzvw{~|{{{{z{{{xsv}}{z{}~}wv}unjmx~|yvtsspqyzxyz|~~|}z~||~z{zx~}yxxxpifjw}yvtqlntrppqrtuwvsqtuqq~{vt{~~|{{{{{{{{xuy{~~|{{}~~zuy{tmikv||yvtsqnsywwxy{}~{}|}{~{{yvz~|}{v|}unheit}~|yvsolqspooprsuvuroqsqv~ztrw~~|zzzzzzzzxtvyw{|zyz{}~~~|ytt~}wpkhjs{{xurpmmtutuvxz|}~~{xy|{|}~{yy}|wsv{|~||}}{uwxpjecgr{|zwspklqnlmmnpqrsromnpr~|wrpt{|ywwwxxxwwtsxutxzxwxyz{zywspw~xrmifhqyyvrpljosqqrtvxz{|{xuvz~}z~{yxx}~|{~ytpsy~{zz{}~|~~}{vt{{tniecfpy{xuqmjnokkkmmopqrqnklpx~zvqnqz{xwwwwwwwusvxsswyxwwxyyxvspq{{uplhfgowxurnjkpqoprsuwyzzywttz|}}|zyy}~}{}~ytopy}{zz{}~}||||xtu~ysnifdfnx{yvrmmqnlllnopqrrqnkkq{vqmny|zyxxxyxwutyytsvzyxxyyyxuros}~yuqmjhhnvywtplnrqpqsuwyz{|zxtt{}~}|{~~|{upox}|}}~~~||xtyytokhfflv{{xtoqspoooprstutsqmks}xrmlx}|{{{{zyvw|{wuw{}|{{{{yvrpw{vspmkimuzzwrorusstvxz|}~~}zvt}~~}wpnx~}xu|zuqmkhgjs{}zvruvrqqrstuvvvurnlv~yrlkx~}}|||zwz~{xuv{~~}||zxuqq{~zwtrpmklsz{xsquvttuwy{}~~~}zwu|vpm{~~zvw~ytqnlihiqz}{vuxvsrsstvwwwvtrnn{}wqkk|~}||{zxw|}zwuuz~}|{zyvros~|xusqomkkqy{xstwustuwy{}~~}|yvx~ztnp~~~}}~{wtz|wspmkhfhpy}zvxyurrrstuvvutqolq{tnjp}|{zzywtx{ywussx||zywuqmmw}{wtrpomkiipwytrutqqqsuwy{{{zxvu}~~~~}|}~{vpmv}{{{{|||{z}|z{}zurt~{vromjhfegnwzvwyuqpoopqrssrpnlmx|wqllw|{yxxwussxxutrqqvzywuspljpzzwsqonmljhhnuvstuqoopqsuwyyywutx}}}~}|{}|~~}xsnq||zyyyzzyx{}|{yz~}zvrpx}xtpnkjhfdfluxwzztqpoopqrrqpnlkq~{uokp||zyxwutquyvtsrpptyywtqmjjtzxtqonnmljhglsvuxvqppprtvxyyxvtu}~~~~~~}{}~}xspv}{zzzzzyx}}||zz~}zuqr||wspnmkigeejswz}zusqqqqrssrqnmnw{vqnv}{zywutxzwvutsruz{xurmjmwzwtrqpppomjikqux{wsrrrtvxyzyxwvx~~~yut|~}||}|{{~~}|~~zvrv|yusrpomkhgipv||xvuuuuvvutspor~|wrrz}|zww||zyxwvuvz}{wsnkqz{xvtssssrpmkkou|~zwvuvwy{{|{zxw|~zvx~~{}zusy}yvusrqomjhimu~~{yxwxxxxwvtrqw|wsv~}zxz}|zyyyxwvy|{wrmmt{zwutttttsqolkmu~{xwwwyz{{{{yxx~}yw}~}{~}xtt|~zwutsrqomkihku}zyxwxxxwvusqr|zvu|~~|yx|}{yxxxwvvxzytolowzwusrssssrpnkiku~}zwvvvwxyzyyww{||}zxz~}|{y{~~~~~~}|~~ytrw~}zvtsrqponljgfjv~~{yxwvvvvutrqpv{wuy||}{xvx{ywvvvvutsuwupkkrwvrpoooppppnlifkw|zwutttuvvwvvuw|y|~~~~|zww~}|~|{yww{~|{{{{|{zy{|zuqrz|yusqpooonljhedkw|{zxwvutttsqpor||yvw}zz~|yvuyyvuutttsrqrtrmimuvrommmnooonmjgflvzxvtsssttuuttty}zy}~}}}}~}}|zxvz{z~}{zxvw||zzzzz{zyxxzxspu|{wsqpooonmljgddlv{{zxwvuttsrpopv}zwvz|yx}~|xvwzyvuuuuttrqqrpljqvuqommmnopponkggmuyyxvutttuuttsu}~{z~~~~~~}{yww~{z}}zxvz}|{{{{|||zxxywsry}zvsqqppppomkheemtz||{yxwwutsqpqz~{xx~}yx{~zxz|zxwwwwwvurqqpmmtwurpoopqrrrrpmiioty|{zyxxwwwvuuw}~~}zxy}z|}zy}~}}}~}{xxwuv}|yvtttttsrpnjghmry~~|{zxwusqt~|yzzxz|z~~|zyyyyyywtqppnqwyvsqpqrsttutroklpsx}~}|{zzyxwvvz}zy|{|~z{~~~~~{xvvuy|yvuuuuutsrokhjmpw~~}{ywussx~{y}{xy|}~|{zzzzzywtpnmntyxurqqqstuuuusomopqw}~}|{zyxwvv~}{xy~zz}z|~}}}~}zvssv}~zwuttuuutsqnjjlmov}}{yvtrs}|yz}yvw}~|~|zyyxyyxwuqmjkpxyvrpoopqrsttspmnpopv|~~}{zxwuttx~~~}{ywvz{xy}}yy}}{zz{{|}}|zvqory~zvsrqrrrrrqokiklkmt|~|zxvsqqw}{xx}zwuv|{|~{xwvvvvvutqmigktxuqnlllmnopqqommoolmtz|{zxvusrqs|}~|{~}|{zxvuv}yww{zxz{yxwxxyzzzxvrnms|zvrpoooppppnlijlkilsz~~}{yvtqps|~{xwy}yvtuz~|~}zwvuuuuttrokffmwxsolkjklmnoponlnpmkmry|{zxvtrqqv~~}{{}~}{zxvuty|yvvz~zy|{xwwwxyyyywtplmu~}ytqpoooppponljlmkikry~~}{ywtrrw}{xx|}zwuuy~~}zxwvvvvutrojffoxwsolkklmnopppnnqqnlmqx||{ywusrsz}~~}{}~|{ywuv|~{yxz||~|zyyyzz{{zxuqmnv~}yusqqqqrrrqpmmooljkpx~}{xvtu{~|zz|zwvx~|zyyyyxxvtqlhiqxxuqonnopqrssrpqttqonqx}~|ywutv~~~~}{xwx~{yz~~|{{{||}|{yvrnow~~{wutssttttsqoprqnmmpw~}{xvx|z|~{ywx|~}|{{zzywuqmikrxxurppppqrsttsrtvusqprw}}{ywvy}zxwz|zz}}|||}}}|{yvrory}}zwuttttuuutrprtrommpv}~{xw{~|{~~|yww{~}|{{zyxvtpljntvvtrpoopqrsssrruwusqpqv|}{xvv|~~~{yww}|zy{}|{{{{|{zyvsppuy{{yvtssssttsrpqssqommou||zxx~|z{}zxvvy~|{zyyxwvspljkqsttronnnnopqqpprvusqonpu{~}{yvux}|{}}zxuux~|ywwy}}zyxxxyyxwuronrwxxwusqppqqqqponqsqnlkknt{}zxw{~{yx}~|zxuttw||zyxwvvtrpliinrqqqomkkllmnnnnostrponmoty||yvtu{~}{zy{}}{xutu|~|zxvuwz~{ywwvwwwvtromotwvuutqpooppponmorromlkkmry~~|ywy}zxz~|ywussuz~|zxwvvusqnjikqrpoonlkkklmmmmmpttrponmnrx{{yvuw~}|zyz|}zwttw~|zxvtuy~{yxwwwwvusqnmqwwutttrqppppppnnqtrpnlkkmqx}~|zy||yx|~|zxvtssx}{zyxwusqnjjnsronoonmlmmnnnnosvusrqonorw||zww{~|{{|}{xuuz~|zwutw~~|zyyyyxwtroouyxutuuutsssssrpqtvtrponmnqw|~|{~{z}zxusrv~}|{zxvsollqutpooqqppppqqqprvxwvutsrqrw{}|zz}||}}zww}}zwtv~~}||{{zxurpqw{yvtuvwvuuuuusrswxvtsrpoopu{~~}~|||zwtru~~}|{yvsomotwtqooqrrqqrrrqqtxyxwwvusrruz}|{|~|{||ywy}zwtv~~}|{zywtqptz{yustvwwvuuutsruxxvusrqpopty}~~~|~|yvsqu~~}{ywtqmmqvvspmnpqqqqqqqpquxxwwvutsrrtx{||~|zzzzxx|~|yusw~~|{zyxvtqopvzyvsqrtuuttssrqrvwvtsrqpooorw{}}}~}|}~|zwtqqw~}|zyvtqmlnsvsplklnooonnnmmptuttttsrqppswz{~~}{yxwxxwz}|yvstx}}{yxwvtrpnnrwxuronoqsrrqppoorttrqqpoonmnqvz}~|{z{{|~}|zxusprx|}|zxvtqnklputqmjijmnnmmllkmqssrrrrrqpooquy|~~}{zxvuvwx}~~}|zxusux{~~|zywvtrpnmotxvtqnmnprrqponnostrqqpponmmmpuz~|zyz{}~}{zwurqtwz~}|zxuspmlnsuspmjijlnnmmlllnrsrrrssrrqooqty~~}|zxvuuwz~}{xutvxz}}{zxvtrpnmqvxvsqnmnprsrqpooqttsrrrqqponnptz}zyy{~}{yvssuwy}}{ywtqnmpuvtqnljkmopponmnqtttttuuutrqppsy~|zwuux|}zwwxxy|~|zxvsqoosxywusqooqtuutrqqtvvuuuttsrqonory~|yy{}{xuuvvx{~{yurooswwuspnmlnqrrqpoptvvvwwwwwvusqprx~{xvuw~~{yyyxx{~|zwtqopuzzxvtrqqruvwvtrswxxwwwvvutrpnnpx~{yx|}{xvvwvwz~{yurpqvyxvtrpnnoqrsrqpruvvvvwwwvutrppry}zxvvz}zyzzyy{~~{yvsqqswyyxvtrqpqsttsqqrtutssrrqqponmnrz~}{yxwx}}{ywwxxxxz}}zxuttvyzyxvtrqpqrrrqpqrttttssssrqpppqu}~|{yxxz}{zzzzzz{|}}}{ywtsrsuwxwvtrqpoopppooopqqpoonnnmmmmorw~}|{zyxwvwz~~|zyxwxxxxyz|}}}|zxwvvwyzzywvtrqqqqqqppqrrrrqppppooopruz~}|{zyz{~~}{{zzzzzzz{{{zywvtttuvwwvtsqponnnnnnnoooonmmmmmmmnprv|~}|zyxxwwxy|}|{zyxyyyyyyz{{{{zyxxwxyzzzywvtsrrqqqqqrrrrrqqppppppqsuy}~}|{{{|~~}||{{{zzzzzzyyxwvuuvwwwvutsrpoonnnnooopooonnnnnooqsuy~~}|{zyyxyz{}~}{{zzzzzzyyzzzzzzyyxyyzzzzyxwvutssrrrsssttssrrrrrrstux{~}}}}~~}}||{{zzzzyyyxwwwwwxxxwvutsrqpppppppqqqqqppppqqrsux{~~}|{{zzz{|~~}||{{{{zzzzzzzzzyyyyyzz{{zzyxwvuutttttuuuuuuuttttuuvxy|~~~~~}}}||{zzzyyyxxwwwxxxxxwvuttsrrqqqrrrsssssssssttuwy{~~}}||{{|}~~}||{{{{zzzyyyyyyyyyyzzzzzzyyxwvvuuuuuvvvvvvvvvvvwwxy{}~~}}||{zzzyyxxxxxxxxxxxwwvuttssrrrssstttttttuuuvwyz|~~}}|||}~~~}}|||{{zzzyyyyyyyyyyzzzzzzyyxwwvvvvvvvwwwwwwwwwxxyz{|~~~}}||{zzyyyxxxxxxxxxxxxwvvuttsssstttuuuuuuvvvwxyz|~~~}}}}}~~~~}}|||{{{zzzyyyyyyyyzzzzzzyyxxwwvvvvvwwwwxxxxxxxyyz{|~~~}}||{{zzyyyyyyyyyyyyxxwwvvuutttttuuuuvvvvvwwxxy{|~~~~}}}~~~~~}}}|||{{{zzzyyyyyzzzzzzzzzyyxxwwwwwwwwxxxxxxxxyyzz{|~~~~}}||{{zzzyyyyyyyyyyyxxwwvvuuuuuuuvvvvvvwwwwxyz{|}~~~~~~~~~~}}}|||{{{zzzzzzzzzzz{{{zzzyyxxxwwwwwxxxxxyyyyyyzz{|}~~~~}}||{{zzzyyyyyzzzzyyyxxwwvvvvvvvvvwwwwwwxxxyz{|}~~~~~~~~~}}}|||{{{zzzzzzzzzz{{{{{zzzyyyxxxxxxxyyyyyyzzzz{{|}~~~~}}||{{zzzzzzzzzzzzzyyyxxxwwwvvwwwwwxxxxxyyyz{{|}~~~~}}}|||{{zzzzzzzzzzz{{{{{zzzyyyyyyyyyyyzzzzz{{{||}}~~~}}|||{{zzzzzzzzzzzzzzyyyxxxxwwwxxxxxyyyyzzz{{|}}~~~~}}}|||{{{zzzzzzzzz{{{{{{{zzzzyyyyyyzzzzz{{{{{||}}~~~~~}}}||{{{zzzzzzzzzzzzzzyyyyxxxxxxxyyyyzzzz{{{||}~~~~~}}}|||{{{zzzzzzzz{{{{{{{{{zzzzzzzzzzzz{{{{{|||}}~~~~~}}|||{{{zzzzzzzz{{zzzzzyyyyyyyyyyyyzzzz{{{||}}~~~~~~}}}|||{{{{{zzz{{{{{{{{{{{{zzzzzzzzz{{{{{{||||}}~~~~~}}}|||{{{{{{{{{{{{{{zzzzzyyyyyyyyzzzzz{{{{||}}~~~~~~}}}}||||{{{{{{{{{{{{{{{{{{{{zzzzzz{{{{{{||||}}}~~~~~}}}||||{{{{{{{{{{{{{{zzzzzzzzzzzzzzz{{{{|||}}~~~~~~~}}}}}||||{{{{{{{{{||||{{{{{{{{{{{{{{{{|||||}}}~~~~~~}}}|||||{{{{{{{{{{{{{{{zzzzzzzzz{{{{{{|||}}}~~~~~~~~}}}}|||||{{{{{{{||||||{{{{{{{{{{{{||||||}}}}~~~~~~~}}}}|||||{{{{{{{{{{{{{{{{{{{{{{{{{{|||||}}}~~~~~~~~~}}}}}||||||{{{{||||||||||||{{{||||||||}}}}}~~~~~~~~}}}}|||||||||||||||{{{{{{{{{{{{||||||}}}}~~~~~~~~~}}}}}|||||||||||||||||||||||||||||||}}}}}~~~~~~~~~}}}}||||||||||||||||||||{{{||||||||}}}}}~~~~~~~~~~}}}}}||||||||||||||||||||||||||||}}}}}}~~~~~~~~~~}}}}}|||||||||||||||||||||||||||||}}}}}~~~~~~~~~}}}}}}||||||||||||||||||||||||||}}}}}}}~~~~~~~~~~~}}}}}}||||||||||||||||||||||||||}}}}}}~~~~~~~~~~~}}}}}}|||||||||||}||||||||||}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}||||||||||||||||||}}}}}}}~~~~~~~~~~~~}}}}}}}}}|||}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}||||}}}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m29.bin b/applications/SprPoP/dist/MUS/m29.bin deleted file mode 100644 index ff23415..0000000 --- a/applications/SprPoP/dist/MUS/m29.bin +++ /dev/null @@ -1 +0,0 @@ -s}ux}zw}ywr|q|tss~~ttp|~ry|w}v}|~|y}|nvvzzyzow~ps}~~t|vw}zx|zwq|q|urstuo}ry|w}v~~||y}}pxs}{yy{pv}rq}u{xu|}zy}ws|rxyst}|q|n}|zq}~~w|w{~z|~{tx|q{|xzz~vqzuq}{|{~t~vyuzvt|r|xxyyv}nzwsxy|{x}~|y{x~tqu~x~|z||o~|~}p{}|}wu}}v{{}u|p|vup{}rwrv{zq{z~v~|w|}v~wp||xy{~uww~uv~|||~x|wzv}uu}ur}zxv|p{{uqyz}{x|~}|zzwuqt~y}}z|}r}z|{}r}||~~{x~~v}}}v~qz{wo}}syswy}p~||w}{zx}xz|ox}wz{}zvuyyw~||}xu}~|||rz|wuxzsww}r|wr{|}u~{z~vz|x~}mqzv~p}mzzxwo{mqw}znj{|uz{u}|x~t{wgw}}}ftvs{sv{vy{{}{q}wtwt{zr{puriny~qrt}~v{t{x{w~tznzswmu}{lyykuwwz|yw}nz{yqqrys|~vvk~ow~{xu~zszv|iyh}sz|vslu~n}v|vt}szuyxx{zwurprmt{}owkxue}v}zzw|{}u~xyp{umz~xov{q{{i||v|ntu{{zrqymk}t~n~{{xqv|rxvxz|{wzwx{qy~xsyvyryuu}}{|yx{xyzxxzz}|tzypx}k}v~tl~x~vv{ouywx}}~vo{su~o|s{}~|~zyq|stw|pws|lyylsvusm~v}|~xvtwytzw}v{xytrxt{y||}~~vuxz{}}wky~{uuxyqv|h{|~yrtru}yw~qtw|pfv|{tu{x}m}yv~yzy~~}p{x~~zqxsy|yryrytztm|ujsuzkzo|zlowvw{wlss|}jq|nryy}wqx{xwzZvyuk{xsr|x|zupzunqwqwzrly{yxlrm|tyoxxn}uvz~u{uzs}~zz~l}wvnpuu{fy|px{uym{{uzxy|oittkn}txz||xqswqzul}{wtjx|yx}syzyr{lucks{wyzrvytiurxowlj~xsu{jr}~vt|tt~{nnzv~{xjvs|xvstl}xkljnyuxyz}vquysysuw~zrmt{ws~svstzyy~rsuwzrzu~tvsqr|}sxzyxv|yz{}|wzwt|svt{wr{szrs|u{||{}sv~syszw}q~o}yrvx{y~s|yqsy}vukuz}|y}~tz~srv|x{~t}}n{utq~szwyyuz|{u~{txyos~ow}xzqw{tz~qqwz}vpsz~}zozwu|{~w|~~|tx~~mt{ypxhhgae]LiodZXawohswyvvr|xs{rxwkqsldntm`qtz|p~pztuz{muTbzektv{u|~{vsryokoirkustwllmhnrZ[c}zw{v}t}x{w|q{~m|zWfhib`pvr{~roz_gdnojs[[bct~||nz~ygYVefgbv~}}~xqsowrjsp_muu}~nk~s`|zrdpmnxw{nkfciqWPS[VORYkj\]x{Wpz|z~twqstmo~|s}xzhu}o^gnmbgWU_bZntkx~|joqi]bnfY]aujb{}wwwjsdbeoxoxyiokrvxmrwlwqvrnoxcem|uhsm{ltsop~dydfnrlpudYbydlqwpixrjvxmjoz}uqu~xdjzrdabjjT^ndp}xuzqjx~~wx{~uymontiwpdl~w~zudenaTjoj{}xdh}novv]lw|urnr}|~sxtujon_WecZabmhdcptanromc_mrnxzyosumzwj}ouzpdotgfg^ohUpnhow{|}{uhaifZT\ipT\s{wvqp~}wpsdXYnjkm{|qtv^Yk_`cpzx~y|rxv~z}sxw_jcjlkj^almiopx}posxt}~ynlxzx}xptehlbc_[hj]mjfqzzxlpppllq~mcx||vs~{{~sslx{rxm{qmutvlsx~zos{wrmzu|~z|u{sfgohjyw}zr~tuuzwllcio^vqkokqptozzztniqpgzs{zryymjip}f^gmnnnillddr~jqxw~wz~y}v~xuu|{m}{szsttjsvprrmnmd]djX`]X^[edY]bagihspit}ul}y|{wts}{qot{yrty}ztourv{t~~z}qx~z}{s|}les~joqmsmqohrssvw|zur}xhac[WUWasgdviqz~|qywshoomlgec_ecdmuvvpk}zp|{}}{|zu~{qpod`\]esfjwgt}{yt|{{~xywus~hwrsspzysrinv}t{wn~xw~z}suofba^ik_qmdsqz}z{}~upp}}{zymkrw}~{wsl_kwfgpcfcfpijmcelsnmafsnclstrknkp{xuqz~|pyzwyz~~{|}w}~}uqporwymywqzquzvx}y}|wt{{zut}xigootwx~|skz~ks|oqhelmpkhjjoknmu~ujqutsonifhihajmh_qjgsmtsmiqxqtsz}vwqs|{urmca`jgg^iteqtqzwsx}{{sggfjmigcind_cdmxopqsgjzol~x|y}z|{~~wwt}{{x~ozxz}||{}{}z}}{~yzzx}~rp~tjt||wtihfhknjizllzt~yzww{ylkrw{xspibtteqpiqjirx{qsy|zsuw|w}x~~{~z}kkpokja]a`ecd_opgwst{{~~~{{}{||{}wzx}zvpxu{~yw{}zso{vp}vr|usz}~x|{}ztvv}tnu{~}xuqyxwvyy~||}zx}|mnvzwtjhjii`__hb[nmlwwtxz{ytz}|xxunosyyuyrmemufgqeimegntsprprhdgmvskkrvwtpmouptwy}}~{x~xxzzyslomsqlirvhrypy~xtux|~}xqru{xzwuuw|szqytmptuoponmfhksxupswzzupiljgijvuhlxjirjmvvqsqrxw~}{tms~}pkpvwvpkkfje]^kncpunw|wutvzwyrsz|}vontysjhkrwstpljmxpm|ws}zuz~~||zsr~u||zz}}{~}}~~vqv}qlqwyytprpsnffsxnyy~xuz{rmnruqrmijmukdtqirohlsvtxxxxrsy{}}~y|tuww}||tpuzte^`egfa`eglhcguvly}t||}~}{ysy|~xvx}|yysrty{os~tu}wsv{{{~{{vpsz~urx}|xtuwuy|{xuy}{}}xpkovwodchlmkgiggg^^gofjwrr}{xx}{{||{z{usuxuusorv{umxymswomrwwz{wummry|wrsx~|yzyy~~~z{x|~|z||~zy~videikjhllmnggqxnp~xt|~zxz~~{xx{y{{y~~ywqrutwvtsnnsz|ww|}xxutwwztlrznlvqnu||{xx|}xqov|yohimpqooommgcjsojy}u{{yzzxzzzuw|{tppssrsnou|zpv~su|vw|{x||y~{rqtxzyyyvvrlp{{s}}vrv}xplnruuturqmgjuwox}}~|~vsssqqomrxwllwrjrvpmptvyxwvrsz||zu}}pszsov|~~|}}tnlqtnc[Z]_``bcceaalunn{yrz}}~}|~y{}zx{~~|vqw}w|zzywxvv}twxr{}vrtvxzxwuqsx~zuuy}~{yyxz~}vvtyztx~}}}{zunjlruridcfhiiihec^_infftupx~}~~||{y}}wxxxvxz|}zywuy~{wy~~~~|zvx{~~|xty|}}}}{zy|{}}|yz{|vtvyvmd`_`bdgigfdckurlv|ts{}{{}~}|}~}{zzzxutrtz}}~vs{|qq{worx|}{|~}}zuqrw{zuomnoqqsromjjrxro{xy|}{xwutuuw}|swzszzutvxzyyywz|{~~~}xtty|zsljknppqqomjks{vs~}vstx|{unklnprssqonow~yu}yvtrrrsy|wor{vnu{wqpruwwvwwy~|}xqpxwlmwuoouz{{||zwrkeehjf_WUVY]`ceeeehrxrp{~vv~}y|yz{y|}wuuwyz{{ywuy}~}{yxxy}zu|wt{~ysrsuutsssv{~}xtsw{}}}}}}~~ztqw}wpv|uoquxxwxvsqlgfinokea`acdefeddeltsmt}x}~}|{|~}|||~~|~~{xy~|vrsuwy{{yxwz}|}~}x~~x~{|~~}vsx~yqw~ztuz~{wqkhillha[YZ]`cfffgjqxvot}ztw}|}{zzz}~xx~ts{{sptxzz{{zxurqsx{{vqnmnpqrqooot|}ww~~|zz{|yv~xu}~xuuvvuuvx|{xwz~}xx{v~zuvz|}}}{xtpmnquuqkgfghjkkjijowzus}~~~}~{wttw{|ysommopqqonnrz~zv}~|zyz{}~wrv}yqu||xtstuuvwy}}z{~}{volpvriltsljnruvwvsplgefiif`YUUVY\^_`bfnutor||uu{~z}wwz}~~|zwvx|~zvtuvwwwvuv{}{|x~|vyzussssstux{}|xtrsvz|}}}||zvqpv{vnqxwqmnqrssqoligfimomida`abcccbcgnsqlp{|ww~~}|ywy|~yusrstuuttuz~z}y}yz~{{||}~}xrorxvnmuytporuwxxurnjhhjkje_[YYZ\^`acgnvwrpx}ysu|}xw~~xrrtvwxxvtrppsw{{xtpoopqrrqru{~yy{y~wy~zwvvuvwy{|xuux{}~~|xtty~{ssz|vqpqstttqoljikoqqnjgeeffgggimsxxst}}~}{|}~~}{xvtsvy{{xtpnmnnoooqtz~yy~}wu{{uv}{yxxxy{}|yxy{|||zxvrmihmroghpsojilnppolieb`abdb_ZWVWZ\_bdgmtywrr{zvxyz|vuvxyyywvtsux|~~|xuttuuvvwy~z{{wz}yvuttuvxz}}{wsqrtvxyzyxvsomqwwqloutnjijkkkjhfdcehkmkhebaabcdeglsz}zy{{ytpoooqrsqjhnsqjhmrrolkklmpsx}yusrolhd`][\_``]ZVSRRSTUW\doz}sg~jv}|{vqnkjijkmopolihilptwz{|||z{~zuojebaa_[VQNNNOQSVY_fmpoox|rkm~|wtqppqrsrpmkknrvy|~~~||~~~vomqzzuoiehoqlhkqsokhfeddefggfc^ZXXY[^_```_^]_elmihowyvtvx|~~vsuvsokihhiklnppnjfddegijjjihfddiopkhmtwtqqsvxz||}~|rnqvurv}}wqmifeefffd`\YWXXYYYXUSOKJLRTPKMUYXUUWZ]_````acfjmnmkkkmptx||vqmkklnoqqpnkheeilh`]aec^ZXYZ\]]^_`chmrvwwwwxz}~}xz{vrppsx|{vplifdb`][XTQPSXYTNPUXUQMMMNNNNNNORUY\^]\\\]_adgkqx}}}~||}wpljlnjc_bggc^\[\\\[ZYYZ\`cddc`___abdgkpvzzwx~{wusrrsttsqooqoh_Z]`^YTRQQQQONLJJKMNMLIGFFHJMPUZafihegnutqptz~}~|skea^[YXVTTUX[]^]\ZYXXYZ\^bfjkhfhpvurrv{}xusrrrstuuuvy|{uoou{|zwusrnkhc_\[[\\\ZWTRPPPQRTX\_`]Z]diifeinswz|zww{~~zxxz|}|zyxvsoid`][YYYZ\_bb_[\bgfb`bfjmopqrrrqpoprvz~ytppqpjddjqsrpppponlkjkmpsutrmiea_^]]^`cec^ZZ_b_ZX[_dhknqtvwwvvvx{~{tnllmoprstuxz~~zsnnstpjhjmoqqqqqpnljiiknqsuuuvwxyzywuuz|~~xtqmigdcceimruvvtrqqrtw{~z{~|uolnopqpppomligfgiloqrrsstuvvurprx}}yx|{xvuvvvusrrstuwxz~~{yvsokgdccegiiiihhiijigdcfmpnjkqvyzzz{|~~{yxwuqmhdbaacfintz}|xw{|~}{zyywusqpopqqpnlnuzyuruz|{ywvuuwy{~~|ywwwy{zvtw}zwusqnjecfjje`afijhgfffgilnopomlkloqtwxyzzy{~{trvyvqorw|}vrsvvqkjmpqomlkkkmoprrqomllnqsuwxxxxx{~xvvwwwvwwxxxwwx{~~{spqstqnljiiijklkifc`___`aaa`_^^_djljgjrwxwvvxz{||{{|}~|zz{}|yvuttsqoljijlnprsssrppswzwrpsxyvrqqrtuvwxz}|{z{{{{{{|~~}|zwsniecbccccca`^]^bfgc_afjifddeghikkmorvz}~~~}}}~}{zyywustz~|yxwutsqoljhimpnhfinomigghijjkkmnrux{{{zyyz{|~|wqmlptrnnsz~|yuqmiecdghd_^bggda``````_^^^`ceffedefhjmquy}z|~|zzz{}xuvwtniedcbbbaaacfilmmmlklmnpruy}}~{|}{xspmlklnpsuuuuuwy}}}vqmkjhggeddegikkkigfeefgiknrvvtqsz~{{}~{yxwvvtspnmnpswz}~{ywtrpppqrrpmkigfffghknqqnklrvurpswz}|wqkfcaaabbcdeegjpx||{~}yvsrrqomjgeeeeghjmprqmijnqnjhkoswz}}{zzzyxwusqpquz{yvx~{yxwvwxz|zurty{xtsux{}~~|{z{}}||{ywuqmjgfikidadknnlmorux{}{vsuyytportvxxxyxwvtsstwz}|zz|{volnpokgffhikmoqty~yuuvxxwwvutrpnllmpruvwxxyz{||{yx|}zyy|~yttxxuojgeca_]\[\_bfjmoqswz~~|zzz||||zyyyz{{{zy{{wtrolifdccdfhjjjjjloruy~~}|||{zwsruzzvstx|}|{zz{|~~~}|zxvsrqqqqpnkhecbccehlqvyxx{}zvsuz{wsswz{zyxxxy{}~|{|~~}~~~|ytpmjhfedegijgdejqrqqtz|yy}{vuw{|{zxwwwxyz{zyvsqppqrstttsrrtx}}z|}{{z{|||ywtsqqpppqrsspjffijeaadimrv{}|{z{{{zxvttuwz}~|wvxzwqnmoqrstuvvwwxy{}|||||{yvsqppqrstttsrqrvz{xuv{~{yyz{||||}~{|{usstttsqpomkihgilpuy|}zxwwwxxxwvussuy|zvtw|~{ywwxyzzzz{|~~zyz{{zywurolheccdfhjkmnprvy{}}~{ywvvtsqomllnrusootxywtssssssrqqqsuwyxxwwwyz|~||}~|ywwwvvtrpnlllmnnmns{|{~~zvtsrrrrqqrsvy{||{zzzz{|~~~|zxvtsrolhfhmonklqw{}|{}~zwuttttttstuwz|}|{zyxxyyz|~~}{zxvqmiilmjedgkmnnnpruy}~|zyxxwvuuvwy{{zywuttttuvx{~~{xx}|z{~~}~}zvrruvrmjlnomkjihikmorsttttvx|~{xvtttuvvvtssrstvwy|{xx|~{wvx{~|{|~}yuqnljiiijjjiijlpty~~|{zzzz|~~|~~{{}~}{xurqoonnmkifcaabdfhjlnopsw~|z}}zz|~~}}}}~}{yxxxwwvsplhfedeeeeedcceinrrqsy|{zwxz|}}}}}}|zxwwy{~~~~}zwsnkhgfedcb`^\[]`decaekppopqtvx{|~}{}~{ywwxyyyywvsqpqssoigilkgdcdefhjlnquz~~~|{{|||{zxvsrrturlikmmiebaaaaaabdfjnsvyz{}|z|{{~~|||}~~~~|{ywxz|zuqruuqmigfedb`_^_`befggffgiknquy~~~}zxvvwwxxxwvutux|}zvw{}|xtrpomkgeb```aa`_^\\]^`cfjnswxwv{}{{{}}zxwvtrpnmlmnnmlifdba```bdgjkigjpvwvw{}|{{|~~~}|{|||~{zzzzyxwvvwxyyxvsqnljihgghjjhdachjhefimqux{}}{{{|}}~}}{zz{|z||zyyzyyxwwwyz{{zxvsqonmlkklmlhcacfe`]]_bdeghjklmnprv{|zyyyyyyxxvvvy}~zz~}{{zzyxvuttuwxyxwuttttuuvwyzxsnlnokeaabcdefghijjjjlnrvy|~}|||{zyxxy{|~~}|zxwwwwwxy{}|xuuxzwspprssrqponljgffgilnprsuwy|~~}|}}}}|||}}||{{||~|yx|~{wttuvvvusrpnkheddefghhhhhiklmmlmpw||{}~}||{{{zzz{|~}|zyxxxxyz|~}zvvy|zvssuvwwvutsqnkihhhjjkjiihhiiigeegmqqopu{~|zyxxy{}}}|{{{{|}~zz}~{wuvxz|}~~~}|zxvvvvvtrpnlkjihfcbejlkhinrtuuuvwy|}||||}~~~}{z{|}}}|zyxwvtrojhjllhddfiiihghikmpsvxzz{}~}}~|zyyz|}}}|{zyxwvsplkmomheehigecbaabceghhhhhikorvy|}||z{}~~}{zyz|~~{xvwzzvrprsspnkigeedddb`^\ZZ\^`cfhjlnqu|}|~~~~}|ywx|{y{}~}zwurponmljheb`__`acefghhikpuyywy~}~zz}~}zxwvvuuuusqmjhgfggffedbaabfihfeioqqqrtwz}|{|}yy{~~}{yxwwwwxwvtqnlkjjjjigfdb``cec_]_bdca`acegikmosw{~}||||{zxuqomllllkkjhfeehjkgddgigda`___^^^^_beimprtvx{~|yvuuuvwwwwvuttuwyvqoprqmifdcba`^]]^`bdefffghjlorvz~|{{|||||{ywvvxzyuqqtusoljiihgedcccefggfedccdefikoswxww|~|{zz{{{{{yxvuvyzytrsvvspmlkkjhgeddeeffeca_]\\\\]_befecdiopoorw|~{zyyzz{{zyyxxz~}zy|~~{wusrqomkihgghiihfeccbbbcdeghgda`dgecbdhlpsw{~}{zyxwusrqqqrrqpmkhgeddccdfgfb_`dfebacfikmoqrtuvwy{~|||{{zyxxyz{{{zxusqponmmnoonjffhjgcaaceffggghggggikoswz}}{zzzyxxwwwxyzzywurqponmmnopokgfijhdaabdddddccba`_`behknprtwy|}|{yxwxyz{||zyyxxxxxyyzzxtonppmhffgijjjjkjihfedefghhggghijlmnnpu|}{zxxwwwxy{|zwtuxywsqrstutssqpnlihgghijjjiiiijkjihhlqsrqty}{z{~}zvuvwxxwvutrpmkihiijjihgffeedc`^^beebadhlmnoprux{~~~}yy|~|xvvwyyyzyxwvtrpoopqrrrqponnmlifdehifbbdghgfffghjloqssttvx{~}zyz|}~~|zyxyyyxwusqponlkhefikjgegjkkihgffffhiiiihhjlosvz}~}||}~}||{zyxtqoqsrnkjlmmkigffffgghhgeddefikmoqrsuwz~}}~~}||{zyvspqttplkmonljhgfeeeeedb`^]\]^_`aabbbcfjnpoosx|}}~}{{~|xvwxxwtrpnmlkjjhfc`^\\\]]^^^^]^_cgihfhmqrqqqsuwxz{}}{z}}{|}{yxwvutsqnkhedddeeedcbbbcgihedgjkjhhijkmnoqsuy}}||||||{yvsqpoooonmljhffhjifbcegfcbabbcdefgilosvxzz{}~}|{{{|{zxusqppppoonljhggijhdabeeb_]\\\\\\\\]_bdfghhhikmoqtx|~|zwussssssssrpoopstqmlnonkgecb`_]\[Z[\^_aaaabcdfhkmqtxyxwy}|{zzzzzzyywvvx{{xutvxvspnlkjigedcbccdcb`^]\\\]^_begihgjosttux}~}|}|xwy{yvsqpponmljjjjkkkjhfdcbaaabcefgecdhkkjjlpsvy{}~}|}|xwy{zvsrqponmlkjjkklkjhecb`__^__aba_\\`cb`_acgikmoqstuvxz}|yxvusrponnopqqpomlkkjjiijjjhd`_ab`\[\^`bdfhjklmnnprvy{}~|zyxxxxxwuspnmkjjiijkkifcdggeb`acdeeffffeeddehjnprtvxz|}{ywvuttttuurnlmonjgfghhhggfedba_^_`bdegghijlnopporw{}||}{yxwvuuuvwvronprpljijkkkkkjigfdbbbdefgghhhijkkjiilpsrqrvz}~~~yvvxvsonopqqqqqqpnmkiiiiiihfecbbba`_^_cghggkostuvwxz|~sqtpifc`_a_\\[YWVSQOONOQQQRRQRTUVWXWY_dhijnuz~~|~~}|}}}||{zz{{||{yxxy|rwgv{wqlkjga[WVUSPLIGEDEEGHJKLMPTX^djouzzxz{zvttwz|~zh\avvcWQOQX]^]_dhkkkjjhgghgffec`_^^____`___^`cfhfefjnnnmnprtvy{~{qdXOJKS`ny~xoe\UOKIHHJNTY[[^chlmlmoqstuvvwxy{|||zyxvvuuuvwyzzyww{~~||~|zxvqh`[XWUUYamz|zyxxxy{}~}{y{~~|ywxyyyyxvutromlkklmmmmmmmmnnnlklortrqswz{|||}~|xvuvw{~yvutttttsrqqppnligfhifb_`aa_][YXWVVWWWVUSSSTUWY[]_`abdhlppoptxyyxxz{|~{wvx}{vqmifdba`_^[XUSQPPPOONNLKJJLNOMJJMPPOMMMNPQQSTVX\_cfhjkmpsvz}zsmifefinu}xqlie^XUUUSOLJJIIIHHGHHJLMNNMMLMNOPRTWZ^__`bhlnnpsx|~zria]YTNJJLQXaiqyzslf`[WTRQQRSSQPQUYYXX[^adfhjlmnnooprux{}~~zvrnid_[WTQOLLPZdls||zzyyxwvuuuuutsrqswyxuvx{{{zxwwvvvvvusqpnnopqstuvwwxz~|yvtrpnmmptz}{xusqoooonnmljhghijifdeghfdbbbbbbbccdfhjkllllllmnpqsvxzzxy|~|yvuttvz}zxtojfdb`_][ZYYYZZZXWUSRQPPPPQRTSQOOSUUSRTWZ\^`cefhijlnquy}{tmgcaacgkqy}xtpmic]XWWUQNLMNOOPPQQQPPOOPQSTUVVVWXY[\]^_bgmoprw}~ytokhc^XSRTWY[_gpy~zvrolifc`^]\[ZXVVX[\[Y[^acddefhilnprttttuwy{~{vrnkhd`[WSPNNOQTW]gqz~|zwuromlmmnnoooonnprttrpruvusrrrssssttuvxz{|{{zz{{|}{xtqnkhfc`]ZXWX\aiqy}{xwvvwwvvtsrqqpppppqqoliikkifefhiklmnoppppqrtvwyyyyyyzz{zyxy|}ywusrqruy~xtrqponmkjhgeca``abbcbbaaaaaa_^]^aca_`beggghhikmortuvwwxz|}wutrmjikoty~zvrlhdddb^ZYZ[ZXVUTTTTTUUUTSRRSTUWYZ[\]^`bfjlklpuxyz{}zvrnjgda^]_cimot{}wrmjgecba`^^][[\_`_\\^aa`_^_``abcegilosvxy{|~{xtqmhc]XSPMMNORVZ_ekr{|zxwvvvutrqonnnnnoqsttrqruwwuuvxz|}}~{vtsrojea^\ZWVUUVY]biou{~|zxwwwwxxxwvvuuutrpoprtrpoqstuttttuuwxyyyyxxxxz{|}}}}}}}~}|{{zwsomnnlihilotx}{xwwvtqolkjiiiihgedcbccdefggggghjlmljjlnnmkkkllmnopqtwz}x|z|{{|~ytulca_ZVWUSSSRQQQQTWWVUWZ^___abegjlnqtw{|ywutrqqponlkjiijkot{{bhy_fqsvx{{yvvwxxwxxy|n]mV_lv~}yvtsrplhedc`ZURQQRSTVX[^`ceimrx~~{xuronnonmjijmmlkklpsvxxvrj`XWby~yy|{xuqonnnnonoooqqrpnje`[TLKSalmg_XTTUY]bhmpswz~}tmhfjt~yutsoic_[XVUX\bgihea]YVRMIFDCCDEGIJLORVYYXY\`aa``abcdfgijmosuxy{|~woidaaelv{tnhaYTQOMJGECCCCCCDEFHJKMMNLOLRIkueWSRTXYbklqz~}}|}~yurqqsuxz{zwsmf_XSONPSY`is|~vcg{hZcjqw|yywslihimry}tja[YX\aglswyyyxy|tkyywxyz{{zyxwvtsssstuvxzzxtnieehmu}wv|vuw||vmaVPUcsz{xxyywrmjlr}}|}{zyxwwxxyz{}yusqomkihgjr~vpkgecbbbba`^[XTPMLLNRVXXXXUSSW^ejlmpsx~vlc\Y]fwzurpoppqqqpnkheccefggffefhlptx|~{wsrtx}{slfa__dlw~xtqooprstsoh^QF@DP^inqrsroligfeeefhknqux|~~}||||{{||}~ztqqoib_bju}{yxx{~yncZSMGBAFP`p~xqkfgjic_]]^adhknpponmlmoqsuxz|}zz}{tmggp{vqonpuz|xtpnkjihiijjjjjihhiknqtvvvuvvxyzxwyyqha\YZ_hs}vnheddefghjkjigffinqnha][Z[\]]^^__`aabcdefhikmoruy}~{}|zwtrrsw}{urqsuxyz{{|}~~{}~~}{yuohcbdb\RLLXiv~}|}~}{yxwwwvvutsrqooooonjebhy~zvqmhc_ZVSRSUZ`flqtutrpmkihhikorturpqvxtqrw}~}}}}}ztnmvsha\ZY[`hr~|wtqonmmnnonkg`YUW[YURQQRSTWZ_dinqrru|{upjeb`achnuz}}|{|{smq~}}}~~}{yy{~{xvsolhd`]ZZ[]acb^WU[kwytpoqsvyz{{{{||}~zy|zwvwxyzzyxwvtrpnligdb```behjmr||{{}}zvsokfa\XUSUYalxzuqmkjjkmpqqokgfghigefjoruvwwxyz|}~~|zwurponnnnmjd\X]isrmjkqx~}zxvtsstuvvvusrrrsuwz}|xtplihkqzxu}~}{xuqnljihhhhhiijjklnoqssroljlppmjijmptxzzxvvz~|ywxz{||{yxvutuwz~~{xuqnljijlortvvrkcbo}{zxvusqpnljhfda_[XTQNLMPW`ku}~}}|{zywvutsstuvxyzxsonw}yvtrqonljils{~~}xwxvpkiijif`XRT^fgedju~|ywtqnkhedcbba`^\ZXWVVVWWYZ]`einppnkkptvvtsux|~|zwtqnljiiikmpsvy{||||{{zywrkfit|z{~~{yxxxwusqppsy}{zxwusrrrrrplhhq~|{zyxwutrqpnmlkigc_\]cimosy~|{ywvttssuwyzzxtqt~~}|}~~}{xtpkfb]ZWX[`ir{}|zyxwvutrqpnnnopqpnkknpmjhikoruy||ywtrponnnnnkheeo~|||zz{}~|ywtqnlklnt{~}|wst}~{ywusrqonnmllkkkjjjjjjjjjjjiiiiiiiiijlmopomkkoqngb`dkprsvz}{xusrponmljihhhjmptvvtqpw~{vqonnnnnmlkjigfdcbbcflt~}|zvssstuvxz}{}~}}~}|{zzzzzzyxvvyzvx}~|zwurolifc`]YVTQONMMNOQTVWY]gz|zxwusqomkihggffffggghijklnpsuwwussw{{xvvxz|~~yutuxxurruy}~{z}zy{}|wspopqrstvwyyxuqprutpmmqw|zwtqnlkklmoqqqqsuuqmq|}zwurpmkhfda_^\[[[]^adhloswz}}|}|}~}{ywusqnljhfeddefhjkjgb][bmvwrmjjlnpqqqppprv|~~~zuokjkjf`]]_ciou{~}||{{{zzzzyzz{}~|{{|}{wsqrsvwxyxxwvusrpomkigdcbbejrz}{zzyzz{|~~|xrmnv~}|{zyxwuttsrqponmlkkjihhhhhiihfc`afjkklotz~{{{|||zyxvutttuvx{~{ww}zxusqomkihfeddefilqw~}{ywusqomlkklmpswz|{vneclzxqmnqvz~|zxvusrqppqqqqqrstuvvtqptxzxwwz}}|~}yvtsttssrpomkigeca_]ZXVSPNLKKMQYbmw}~~{xwxyz{|}}}~~~~~~~|{yxwvvvwxz|~|yy|}|zyz||y{}{ywusqomkihfedccbbcdfhjkljfbafp{}zvtqnljigggghjloqssqmiglw}}zurponmllkklmnpruxz}}}{yz{}~~}}}~~~{wuvxxtpkhfeddccbbbcdfhjkkhd`ahs{|xsqqsv|}{ywtroljgdb`^]]]^`bfjpv|}|{zyxwvutsrqponmllkkkklllkhebbdfeb`_`begjlmmmkheehnsx{~}{zzyz|~}xw||xvwxywsolkkklllmmoqsvy|~~|zxvtrqponnnoooomlmpv{|zvqmlpz}|}~|{yxvusrqqrtx~}|zxvusqomjigecba``abcefhhfc_[]eowywuvy~~}}|{yxwvuuuuvwxzzzyvttwyyvsqqrrssrrqonlkihfedccccccdddb_\YXZ[[ZZ\air{~|{}yutvyzxuttuvwxyz{|~~{xvsqonmmnprtvxyyvsqrz~}zwurpnljhfedcbbbaaa``___`adhnu|~}|{zxwutrqoonnnoqruwzzzwspqv}||~~}{zyxwxyz|~}|{zxwusqomkifdb_\ZXWUUUUWY[]_`abceeb_^cluz}~|zyxxwvutrqomkigfdcccccdegikmorux{}z{|xvutttssrqonlkjiggffgghhgecabehhecbbcdeefhkou{~|zwusqonllkklmoquy}~}{yxvtsqonljigedcbaaaacdfghhfb^Z[akrusqonopqrv{~{yxwxy{~~{xwxyxuqmjhgffedba`__^_`acegiigdbdkv~{xuuvxyxusrsuwxyyvtstwwurnmmmnnnnnmlkkjjiihhgfedca_^]\]^bflqvz|~}|{yxxwwvvvvtqnkkmpponnqtwz{|}~}{xuspnmkjihhhiklnqrsrokgglt|~|xtrrtvxz{|}}}|||{{zywtqmkkmoomjgeeeeeefgjmsy~|{yxxwwvutrpmkknruvvvwxxwurpry|z{|~{xy||xuttuwxyzzyxwvtsqomkigeca_][YXVUSRRQQQRRTVWZ\_bdfgghioy~||~~{xuttuuttsqpomlkjjjjkkllkjhgilpssrpppqsuwy{}|xx~}zxvtrpomlkjjjihgfc`^^`dgiihhhhhghlpqpooqv|~|{{{{|~}|zyxxxxyy{|}{yxyzyuqnkjiihgfedb`^\ZXWVUTTTUWY[^`aa_[XVYakrsqnkklortwy{~~|zxvtrpnligec`^][ZYYYZ[\]]^_cipx}~}zxx|~|zywutrqonmlkkklmnnmljhhjjhc__adfgfeefgijklllkjiiijlosx}~}{ywutsrqqrrssrqonpsxzzyy{}~}{zxurnjhhhihea_^^_behknprrolgdejrxzvrnlklnpsuvwxxyyyxxxwwvvutsqponmmorv{~|zxvtrpnmkjjiiijkmoqssspmihkr{|wssv|~~~~}|zwtrrtuspmkjjjklllmmlkigc`]\]^^\YVTSSSTTUUVVUUUUUVWZ^dks{~||~~}yvsrqqqqqppoonnnnnnnopqqrsttuuuuuutttttuvwyz|}}}||||zwttw~~||}}{ywusqpnmljihhggfffggghhijjjihhjow~~}}~~{yz~}}~}|{{{{zyxvutrpomkihfdcb`__^]\ZXUQNLLMPQPPOPRUY\_aa`]\^dlsvuqnmnqv|}{zwusqomkigeca`_]]\\\]^`bdgkosx|~~~|zxvtrqonlkjjiiiiihfda`aceeca``acdeffffgghilorvy{~~~~~{wtrrstttrqqrtwy{|}}~|zxvtrpnkigeca_^\[ZZZZ[\^adfijjhc^ZY]emrqmhedehkoswz}~~~}|{ywusqonljihgfffghikmnppnligglt|~||{zzyyxxvtqnllmmlifdbbaaaa``_^]\[ZYYXXWWWWWVVVVVWWWXYZ\^afmu~~zvqpqtvusomkklnqsuwyz|}~~}|{zxwutsrqppppqrsuvwxwurnkkntz~~|ywvwxzz{zzz|~~{xvuttttsssssrrrrrrrqqqppppqruy}|ywvvvwxy{}|xwy~~|{{|~}|}}zwuuttssrponljigeca_][YXVTRPOMLKJIHHHIJKMPSW[_cgijjhedfks{~}|}}~~}{xurpoppomjfdcbbbccdefggijkmoqsvy|}zvttttspmigeddddeefghijjihgfgjlmljgffghilnrvz~~|{yxwwvvvvwxy{}~}|{yxvtromkigeca_][ZYWWVVVVWXZ\]_``^\XUTW]ejllihhilpuy}~}|{zywvtqmjfdccddb`^\\]_bfilorssrpnot|~}|{zyxvsqonoqrqnjebacddb`___``abbbbbbbbbaa``_^\[ZYXXY[]aejnruxz|}~}||{{zxvspooqssqonopruxz}~{xvtsrrqpponnmmmmnopqsstsqniedejqvwuqmjiijlnoqstuvvvwwwxxyzz{|||{yvsrrstttsstw|~|zywvttssstuvxy{{{{zyxwvspoqv{~}zxvvxz}}zxxy{{yuplhghjklkieb`^]]\[[ZYXWVUTSRQQQPPQQRSTUWY[]_aceghiiikqy|xvuttssrqonmljihgfffffghijkmmnmlkkmoswyzz{|~|{|~~~}{ywutrpnlkihgffffghijlmnoonlkjjkllmljiikou{}|{zzzyyyxwvutrplhea`_``^[XTRPNNNOOPQRSUWY[^`abb`^[Z\bjqvwusqooprux{}{xvtromkigeca_]\[ZYYYZ[]_bfjnsw{}~}zwuw}}{ywusqonlkiihgggfeb`]\\_acba```````abdgiigeb`__`adhlptx|~~~}}|||||}}}}|{yxxz}}}}}}|zxvtsqonmkkjihhgeca_^^adgijjihfda_]]agnrsqnkihijloqsvxz{|~~}|{zzz|~~|zxvtrqonmllllmmopqsttsqnkhgjpw}~}~~}|zwspmmmmljfc`_^]]]\\[ZZYYXXXXXWVUSQPPRTVWWVTSSTUWZ^bgmt{|zxyz||zwtrqqqqqqqqqqpppppppppppqqqqqqqqpqqqqqrstvxz|~}yvssvz}yvtssuvy{|~~}|{yxwvusrqppoonnnnnnnnoopppppponmoty~}~|zxwvutsrpnljhfdb`^\ZXVTRQONMMLLMMNOPQQRRTVZ_cffda^\]ahotwwutuw{~|{zyxwvutrqomljhgedcba`````acdfhknqtw{}}{z{~}|zyxvtsqpnmllkkkkjjhfdbbceggfdcbbbbcccccccdfhkosw{~~|{{|~~}|zz{}}|}~|zxvtqomkigeca`^][ZZYYYYZ[]^_aaa`_\ZZ\afjkjgedehkmoprtvx{}}zvsqonmmlllkkkkklmnoqrsttrpnlklpw}|zyxxxyxwuspmkkllkheb`^]]]]]\\[[ZZYYYXXXWWWVVUTTSSSTUWZ_djpv|}zxwwyzzxtpmllmpsuuutsssstuvwxxyyyyxxwvvutssrrrrrsstuvwxxwurnkhhjotwwuqnlkklnpruwxyzyxwuuvwyzzywvttttuvvwwwwwwvvwxz|}||}}zyz~~}}~~|yuqnlkkkkkjjigfdb`_][YWUSRPONMLKKJJKKLNOQTVY\_befggfdcbdir{|ywuuttttsssrqpnligdccdefgfdcbbcegilnqtw{~~}|{zzywuspmjhghikkkjihhijjkjjjkmoqqoljiijknptw{~~~~~~|{z{||{yvsoljhfedcba_^][ZYXWVVUVVVWYZ\]^___]ZXVWZ`gloonlkklmpsvy|~~|zwusqonlkjihhggffdcbaacfjnrtuuutsqpprw}{ywutsssssssrpmid`]^`ceda]ZWVVXY[]_`abccccba`_^^]]]^_behlptwz}~|ywtstuwwvtrqpqrsuvwxyz{|}}}}}{ywtrpopqqpoljhgfghikmoqstttrokgddfjpstsokhfffgikmoqrtuvwxyzz{{|||}}}}}}}||{zzz|}{zxwvuuttuvwxz|~}ytqoquz{ywxy|~{wtrrssrpmjhggfedb_][YYZZZXVSPNMLLLMNOPQRTUVXY[\^`bdfhiklmnnoqv}~zwusrrqqponmlkjihgffeeeeeefghikmoqstvwxxy{}}xutw|~}}}~~}{zywvtsqponmlkkjjkklmoprstutspnllllmmnqtx{~|zxwvvutsqpnmkjhgfecb`^\YURPOOPQRRQPOOPRTWZ]`abba`^_afmswxwtrpooprvy}~|zwusqomjigedba`_____`acegiloruxzzzzyy{}{ywusqponmllkjhfda_^^_aba`^^^^`bcefffeca_]]^acfgijlnpruwy{|~~}}}~~}||}~}||}}~~~~}|{zxwutrpnmkjhgedccbbbcdefghiihhfedcba_^^aehjkihfefgiknqtwz|~~}{zxwutsrrrrrrstuvwwwvtrolklotz~}||}}zvsomlllkhea^\ZYYXXWVVUTSSRRRRRSSTUVWXXXWVUUUWZ]`dfilpuz~}}}|{xtqnmmmnopqqrrrrrrrrrqqqqqqpppppppqqqrsstvwxz{|||{ywtrqrv{|xspnopsvxxxxwvvvvwwxxxyxxxxxwwwwvvvvvvvvvvvvvvvuttsuw{~{xusrqpnmkjhfdb`^\ZXVTRPOMLKJJIIJJKMOQTW[_begiihecaachnuy{{ywwx{|zyxxyyxvtqnkigffeeeeedeeeffghikmoqsvy{~~|{|}zwtqponmmmmmmmnnnnmljhgfgikkjhfedccccccddeghjmoruwyz||}~~~|}~|{{{||}}}}||zyxvtrpnljigeca`_]]\[[[[\\]^_```_]ZXVUW[`fknonmlkkkkmorvz~|yvsrqqrqpnljhgghikmprtvwxxvurppqu{~}}}~~}{wtokhfeefggfca_]]\\]]]]]]\\\[[[ZYYXWWVUTTTTUVX[^bglrw}|ywuvwxyxvutsssssrqqqqstvwwvtsqpoooppqqrrrrssttuvwxxyzzzxvsokihimqtvurokiggghikmoqsuwxz{|}}|{zyxyz{}~~|zxvutttuwz}zvtux}~}|zyxvtrpnligdba`_^]\[ZYXWVUTRQQPOOOOOPPQSTVY[]`bdfgggeca__agnv~~{xvtsrqpponmllkkjjjjihgfdcbbcegjlnopprsvy|~~~|{zxwvutssrrrqqppomlllmoqssrpmkiijkkjhgffhjlpswz~~~}}}~~~|{{{||{{ywtqnljjiihfd`]ZWUTSSSSTTUVWYZ\^_abccba^\ZZ\`flqttsqommmnprtvy{~~|ywuspnljigfedcccdefhjlnprtuuuvvvwwvvwy}~|zxvtsrpponnmljhfdccdeec`\YWWY[^`aa`_^]]\\\\\\]]^^`bdgjnqux|}zyxyz{{zwusrrrsstuuuuuuuuttttssssrrpomkigggikmnopoonmmlkigdbabejosttqnkihghikmoqsuwyz{}~~~~}||{{{|~~}|{zyxxxxxyyz{|}}|{ywtrqsv|}zyz}}yvspooonkhda_]\\[\\\\[ZYWUSPNNNOPRSSRRQPQQSUWZ\_bdgjloqrtuuvwz~{xvtsrqqppoonmlkkjihhhgggggghhijlmoqtvy|{wutvz~zwutuvwy{|~~}|{zyxwvutssrqqqppqqqrrsssrqpnlklnqtvwxxxyz{}}zxvtsrqpnmkigeca_^\ZYXWVUUUTTSRQPOOPRUX\_accb`_][[\_djpuxxwvtrrsuw{~}|zywusrpnlkihgfeddddefghjlnprtvwxwwutstw{~|zxwutsrrqpomkifdbbbcdddb`_]]]^^_``aaabbbcccddegilptx{|}}|||}~|zxxz|~~}|{|||}}}}}|{zyxvusrpomkjhgfdccbaaabbcefgijkkjheb^[ZZ]bhlnonligfefhknqtwz}~|{zyxxwwvvvvvvvwxxyz{{{zxvtqonorw{}|||~~{xurpoonmkhea^\[ZYYXXWWVUUTTSSSRRSSSSTUVWXYZ[\^_acehmrx}|{zyyxwutsrpommlllmnnooppqqqqqqqqqqqqrrsstuvwxyz{|||{zwuronnpsw{}}|zwutsrrqqqqqrsuxy{{zyxwvvvvvwxxyyzzzzzzzzzzzzzzyyxxxz}~~}|zxusqonmllkjhgeca_][YWVTRQONMMLLLLMMOPRTWY\_bdfhiihggfgjmruxyz{}~}||{zywtromlkkkkkjhfdcbaabcefhkmoruxz}}||~|zwutttttsrpnlkkkllmmlkjjijlmnnmkigedcccdefgikmoqsvwyz{{{{{{|||}|zyyz{||{zxvtrponmmlkjigfedba`_^^]]]]^__`abbba`^[XVVW[`ejmonnmmmnpruwy{|}}~~~}|{zywvusqnlkiijlnprsttuuuuttsrqpqtx}}zxuromkihhhgfda^[XVVVWXY[\]]^^^^^^]]\[ZYYXXXXYZ\_bfjotx}|zxwwwxwvtrpooopqrtuvvvutsqpoooqrssrqonmlllmoprtvwyz{{{ywtqmkijlpsvwvspmjhgfggijlmoqstvwy{|~~|zyxwxy{|~{wuvx}}{zz{}}|}}}|zvqmheb```aaa`_\YWTRQOOOOOOPPQRSTUVXZ[]_aceghiihgeca`aejrz}zwusrqponmlkjihgffffffgghijjkkkkklnquy}}zwuuw{~}{zywvutrqqppppqrstvwxxxvtqmjhhjlnpomjgeddfhlptx|~{ywuutttttttsrqomjgda^\ZZZZZZYXVUTSSTVXZ\_abccba_^]^`ejotvwvtrpnmmnoqsux{~~|zwusqomkjhgfeedeefgikmpruwy{{{zxurqqty~|zywvusrqpnlifdbaabceeedba_^^]\\[[\]_```_^\ZYXXYZ]`dhlpuy}}}}~~}{xvuttttttttttssrqpoonmmllllllmmmnnnmmllllmopqpnkgb_^^aejmopomljijjkmoqtvxz|}~~}||||}~}||||||}}~~~~~|zxurpopsx}{xtrqponliea^[YXWVUUUUUUUUUUUUUUTSRQQQQSUX[]^_``abcehjmprtvwy|~}|zxvtrqpppppqpppoonmmllkkjjjjjjkklmnoqrtvxz|~}{yxy{}~}{zywvuttttuvwxyz{{|||{{{zzyyxxwwwvvvvvwwwwvutromkjkmortvxy{}~{xusrponlkigfdb`^[YXVTSRPPOOOPQRSUWY[\^_``aabbbcccdehkortuutssstwz~}{xvtrqponmllkjjiiiiiiijklmoprtvxyz{{zxwuuuw{}|{yyxwwvvuuuttsrpnligfffghhgeca_^]]\\\\]]]]^_acehkmpsuwyyzz{|}}{ywvuvvwxyyzzzyyxwutsqpomljihgfeddcccdddeefffedca_^]]`cgknoomjhgghjmprtuvwxyz{|}~~}{xvtsrrrstuvxyz{|||{ywusrrsv{~{xurpnmmmmmkifc`^]\[ZZZYYYXXWWVVUUTTSSSSSSTTUWY[^aejnsw{~~|xuqnlllmopqrrqpnmlkkkkllmnoppqrstuvwxyz|}}~~}|zwtqommoruxzzywtrpnnnopqstvwwwwwvvvwyz|}~~}|zyxwwvvwwxyyz{{||||{||~~||~}xtpligfeeddcbba_^][ZXWVUSSRQQQQQRSTVWY[]_abdddcba___aekpv{~|zxwvvvuuutsrqomkhfedddfghijjjjjklnqsvy{~~}||~~}|zxvtrpoopqstuutrpmjhffghjkkjigedccddfgikmoqsuwyz{||}}}||||}}~~~|zxwutsrrssttsqomjgdba_^^^]]^^^__`abcddeeedcb_][YY[^chloqqpnmlllmoqsvx{}}{zxvutrqqppppppqqqqqqqqrtvxz{{zwurpqsw|~~}}||{zxvsokhecbabbba`_][ZYXWWWWXYZ[[\\\\\\[[[[\]^`behlptx}|zyyyzyxvsqommlmmnopqrrstttttsrpomlkklmoqrsttsssssttttsqomjihjlptwyxwtqnljiiijkmnprsuvwyz{||}~~~}}|}~}{zz|~|ywvuuutsqnkhfecb`^\ZXVUTTTUUUUTRQONNNOPRTVX[^`cehiklmlljigfghlry~{xvtsqpponmlkjhhgfeedddeefgikmoqtvxz|}}~~~}}||{{z{|~}|||}}~~}{zyxwutssrrqqrrsttuvvvvtsqonnmmlkigffgiloruwy{||}~|ywusqpponnmlkjihggedcb`^\ZXVTTTUVX[]_``aa``_^]\[[[]`ekpuwxxvtrqpopqruwz}}{ywusrpnmljiiihhijjlmoprsuvvvvutttux|~}{zyxvuspmjgecbbcddcba_^]]^^_````_^\ZXWWWY[]_bdegiknptw{~}{{{{}~~}{yxvvvuvvvvuuttsrqponmlkkjjiiiijklmnoqrsssrpnjgc`_^`cgkoqrqoljhfffhilnqtvy{}~~~}{xusqpqtw{~{xvutsrpmjfb_][YXWVUUTTSSSSSSSTTUVVWXXYYYXXYY[]`cgjmopqqrrrsvy|wtpnlkkkklmmnnnnnnnnnnnmmnnnnoppqstuwxy{||}}|{ywutsuw{}yvsrqrsuwxzzzzyxxwwwwwxxyyzzzzzzz{{{{{{zzyxwuspommnprtvwxyz{|}~|zwusqponmkjigeca`^\ZXVUSRQPPPPPQRSUWY\^acegghgfdb`_`cglqvy{{zywwwx{~}zxwvvvvvutsqnljhgfeeffghjkmnprtvxz{}}~~}|zyxxy{~~|zxutrqqqqqqqponlkiiijjkkjhfdb`_^]]]]]]^^_`bcehjmpsux{}}|{|}~~~~}{zxvutsrrrrrrrrrqqponmlkjihhgffffffffgggfedb`^\ZYZ\_dhmprssrqonnmmnoprux{~}zxvuttuvwxxxxxwwwwxxxxwvutsstvz~}{xtqmkihgfdb`^\[ZZZZZ[[[[[[[ZZYYXWWVVUUUUUVWXZ]`cgkpty}~|{{|||{zxvtsrqpoonmlllmmopqqqponmlkllmnpqsuwy{|}~~}{xvspoopruxz{zxvspnlkkklmnprtvwy{|}}}}|{zyyyyz{|}}}|{yxxwwxxyyz{|}~|yvspnmlkjhgeb`^\ZYXWVVUUUUUTUUUVVWXY[\^_abdeeedca_]\[\_chnsx|~{yvusrrqppoonnmmmllkkjihgfefgiknrtwyz{||{{zywvvvwy}~}||{zzyyyyyyyxxwvutsqponnmmmmmmljgda_]\]^_bdgjmpsuxz|}~~}}~~|{yxwwwwwwvusqomkihhhgggfdca_]\[[[\]^`acdffffecb_^]^`cgkorttsqomlkkklmopsuwz}~|zxvtrqponmmmmnoqrtvwyz{{{ywurpnnorv{~}|{zywusqnljiiijjigea^ZXWVWXZ\]^^]\[YXVVUUUVWXY[^`cgknsw{~~~~~}{yvtqonmmmmmmnnnnoooopppqqqqrqqpponnnoqsuwyyyxurnjgdbbdgkosvwwutqpnmmmnopqstvwxzz{|||||||{{{zzz{{|~|zxwwy|}}~{xvutsrpnkhdb_^]\\\\\[[ZYXVTSQPOPPRTUWXYYZZZ[\^_acegijjjjjjjmpu{~|ywutsrqqpponnmlkjiihgggggghijkmoqtvy{~}zwuuvy~|ywvvwxyz{}~~}|{zyxxwvvuuuuuuuuutsqomkjjjkmnopponnmmnoqsvz}~~~~|yvtrponlkjihfecba_^]\[[[[[[[[[[[[[[[\]^`bdeedba_^^_bfkortuutssrrsuwy|}|{zyyxwwvutsrqpoonnmmmnnopqrstuvvvvusrqpqsv{~~}|{ywuroljhfeeffedb`^\[ZYYYZZ[\\]]]^^^^^^^_acfinrw{}{{{{zzywvuttttuuvvvvvvuutsrqponmlkjjiiiijjkklmnoooonmkhfdbbcdgjlnpqqpponmlllmnoqrtvxz|}~}|{{{{|}~~|ywuttux{~~}}}}zxwvuutsqolieb`^\[ZYYXWWVUUTSSSRRSSSTUVWXZ[]^`abdefghjlnpruwz~~{yvusrrqqponmlkjjjjjjkklmmnoopqrstuvwxz{||}}}|zywtsqqrsvz}~|{yxwvuuttttuvwy{|}}}|{zyxwwvvwwxxyzz{{{{{zxwusrqqrtvxy{{||||}}~}zvspnmlkkjjiihgfdca`^][ZXWVUUTTTTUVWXZ[]^`abcccbbaabdfimprtuuttuvy{~|{zxvtrpnmlllllmllkjiihiijlnprtwy{|}}}}|{zzz|}{zyxxxyyyyxwusqoljhgffghijjihgeca`__^^__``acdegikmoqsuwy{}~|ywutsstuvvvvusrpomlkjjiihhhhhhhhhiiiiijjiihfec`^]\\]_bfjlnpppoooopqrstvwxxyyz{|}~}}|{zzxwvuttstuvwy{|||{ywusqpprtx||xtqnljjjkkjhfc_\ZWVUUUVVWXXYYZZZZZZZYYZZZ[[\^`bdgjmqtw{~~|{zzyywvtrqonnnnoppqqqqpponmmmmnopqrstttttuuvvwxyzzzyxwusrqrstwy{||{ywusqonmmmnooprstuvxyz{||}}}}}|{zyxxxxyz{}~~~~~~||}~|zwspmigdca````__^][YXVTSRRRRSTUVWYZ\^_abdeefeedcbaaacfinsw{~|ywutrqponmlkjjiiiiiiijkklmnoopqrstuwyz|}}}}|zyyz{~~}|{zyxwwvvvvvvvwwwvutrpnkihggghhhhgfedcccdefhjmoruwy{}~~|{ywvuttttttssrqponljhgecbbbbbbcccccbbbaaaa``__^^^^`behknqrrrqpoonnnopqstvx{}~}{yxvusrqpoonnnooppqrstuvvvvuuttttvxz}}|zwurpnljihhhggfdca`^]\[ZYXXXXXXYYZZZZZZZZ[\^`cfimptx|~~}}}}}|zywvussrrqqqqqqqqppppppppppqqrrsttuuvvvuuttsrqponmmllmnopqrrrqpoonnnnopqrstuvwxyyzzzz{zzzzzzzz{{|}~|{yxwwwy{}}|zywusqnkifdb`^]\[[ZYYXXXWWWVVUUUTTTUUWXZ\]_abcddeeffggijmpty~|ywusqpoonnnnnmmmllllllllllmmnnopqrtuvwyz{|}}}}}}}}}~~~}}||{{{{{{{{zzzzyyyxxwwwvvvuuuttsrqpomlkkkklmnnoppqrsuwxz|}~~|{ywvtsrqonmkjhgfdca`_^]\\[[[[[\\]^^__````__^^^^_`behkmpqrssssstuwy{~~}|zxwusrponmmllllllmmnnopqrstuvvwwwwwwwwxz{~~}{zzyxwvtsrpnmkjhhgffedcb`_^]\\[[[[\\\]^_`abdfhjlnpruwy{}~}}}}~~~}|{yxwvuttttttuuuuutttsssrrqqppoooonnnnnnmmmlkjihhgffghikmnoppoonmmmmnopqstuvwwxxyzz{|}~~}}||||||{{{{zzyyxxxyz|~~{ywusrqpomkjhfecba`_^]\\[[ZZYYYYXXXXYYZZ[\]^`acegikmoqrtuvxy{}~|zxvusrqqqqqqqqqqqpppooopppqrsstuvwxyzz{{{{{zyyxwwvwwxz{||}||{zyxxwwwwwwwxxxxxwwwwwwwwxxxxxxxxwwvvvvvvvvvvvvvvvwxyz|}~~~}|{ywvtrpomlkjihgfeddcbaa`__^^^^^^^^__``aabbbccccccddfgjloqtvwyz{|}~~}|{{zyxwvutsrrqqqqqrrsttuuvvvwwxxxxyyyyyz{|~~}|{zyxwvvuttssrqqponmlkkjjjihhggfffffgghijklmnpqrtuwxy{|} \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m30.bin b/applications/SprPoP/dist/MUS/m30.bin deleted file mode 100644 index 4f8e3b7..0000000 --- a/applications/SprPoP/dist/MUS/m30.bin +++ /dev/null @@ -1 +0,0 @@ -~|{|}}~}~}}~}}{~|~|~|}}}{~|}|~}~||{|}}~~~}~}}||~}|~|~|}{}|}}||}~~|{|}}}}~~|}}}||~|~{~|}}}|~}}}~}}||~}~~~~}}||}||~}|}z}{~z|{}|}||}~}|}~~~~~}}}}|{~{|{}|~{{}|}{z{y~{y|}y~~{~z}|}}}}~}~}~~~}{z}z{||~~z~~y{~~z|}|~xzxy}xx|}zy~z{}y~}|}~~}||z{~z{z}~{{y~{x{}{y|z|{wyvz{wx|{xz|x|{z~~}}~|~~~~~}|~{y|}zzy}||}w{}yxz}yy{y|xwvv|xvy|zx{zy}x{~{~}}~}~}{~}zx}|zxy~|}zw}|wx{}xzy{|vwuy|vvz}yx{y||w}}{{~~}{|~y}xv||xxx||{{|uww|w|wzxyytzrv{~y{vz~ty{||}}~~y~~xuwyxw|z|z|ztxxzx{x{wzvxusx~|yzv}zs}}{|z~~}~}z~{{ztz~wxw~z{z}vuwzwzxyzwzt}~ruzz{yxvw|||~~~~y|yxw~|xx{}}|}}vxyx|y}yzxvysv}~xzvz|sy~yzy|}|~|~}~z}|x}tv}wvu{}yzwzwrvv~xxxvzvysxtsw|{{w{w}~~}~|{||}}y|{{z~|zz{}{~y|}{{u~vvyzzvv|uv|yy{{z~{{}|z~|}}|w}}vwy}ywuw|y|wuwsutzyxzrwwxts}vuuz~|}x}~z~~|~~~~}}}{|~|~~z}|}}}{{~w{}v|xzx{||wtyuw|{~|yyzzyv}y}}{~|{x}{x{xwyxzzt|{wvuuq{zusqv}wn|st|wz{w{u~}~{~zz}z{~|~{||y|{}|{~}{{}xt~x{uz}|xyq}ws|z{|wz}wux~~}~|~{}~|~y|wy|~wuyv~xx~tvvwus{s~}nsyyryynxzw~wx{|~}}}~x|y|}{{{z~|~~}{~{{y{z{vxywvv|x|~|puzr}|w|}{{~x|yx{|~{{~~|~zzu||}zwwz}{xqwyvwysztrywny{qutw|yt~y|}|~~}|}}~|||}|}}}|z||zy}~xwxwxyvy|rtusw{{yyvzx{zw|}~yz~~~{yz|zytw}~yt|zzwvyvwxwyr}{xrsvwuv}zz~v~w|}~}|~~||~~{{u|{{z{|x~{~~w|{~|xyzx}yv|stwtzw~xyv{yz{z}z|w}|u{yrv|{xtvxxyvutqrwzyuvvywtqs|}st|}}~~}~~||~~x|}}|v}zx{}|~}ywx}~|xz~~|yx|}sz~}ywx|t}}yxz|}|{y}}}}z~vz~|qt}zytuxyyuxtrqvzzwtwzwurszts}}{~}}~{}~}zy|v}{wz|~}|zxw|~|yx}~|zx{ux~~zxw{vy~z~{x~|}z}z}~}|}~ww~|vrz|ywtyxyxuvssuz{wwvxyvsqx}xsx~}~}~}~|}{|{|z|yw}{~z|yyz~}x}y}~{yx|xz}vxyw{{{{z}}|~~{|~z{z~||u}~uxuy|xvu|v}ypusvv}xu~rwytqsuxuxz~~z{|x~}|}{}x~}y{~zxx}y}u}z|zwzt}~zw~xy{~zs{uzv||}}xyy||~~x{xy~tz}}|xy{}|y}ytvwu}yu|wutuup|tqyw{|}|{{~~}~|{{|~zw~}~zwz}}|||z~}w||y}z{~|~xv{vvxy}{{v|w}{r|x~~}}z}~||t|{v}zx~{w}}yxw~zv{~rw|wpuxvuuv}yx~z|~|y}|}~{}y~~}}|{~~z|y}xxyy{x{y|syzy~{}}vwxu|y~}}~||wu}~{z~~}{|zxztuy{wtzx{}w}vwx{}}{~{vz{t{yt{x{~~}~{z}}xzw{}}}|~~~~z{~|}||}|}}{z}yyz{{xv}y}y}wwwwutz}}{w~zy|x~}~|t|{vwt{|tux{}zvuyxvvz~{r{{vvt{wv}~}~~zw}~~~~}{~~~|~~}t||||}~z{|~|~}{~|x|}{zx{~|zvsvtvsz~xz|{||y{~}~}|zy|~tw~|x|uy}wxx|zs|uptq{xqu~{}|w{v~~{||}v~{xw|yrxz}y~wy~~}{z}~|zz}wytux|~u{|z|x{zy{z{~~{~~{y|~yuvv}}~z|x}uv}~z{yvuowus|vw}~{||}{|~{|z~|~~~}z{~~|~{wxwy{y{|}z~}~|y{z{~z|uv|tux~}tz{~~vy}}||}{~~{~xzzxyz{|~z|xw|y{~|}utwusrv||sw}}~|{xx{~}}|}|y}{|||wzxt||~|z||~}y~z}~~|}wzyxvsy{yvv}y~z{{z}~}}|xxz}ywz~yy}yzyzx|z}}vxttswztzv|~~|~~y}}|~}zy|}~}||{zxy}ywxyzx}{zz~}~zxwvyvuvxyu~{}uv~~}|zz~~x}zv{}|w{{{{ywvz{{{rwyupxxw{v~~}w|}~}{{}~~yz|{}xy}{xwx}}}{~~}{w}}|u{wswvywvx|y~t{~}{z~~}~{u~xz|xzz|x}~zxyy{z~xwzrqyyvz}{}}||yx{~~~}|z}}}~}~}yy}~zz~~{|vy}z~~}{|}~|{}}{vutxztvxy|{z~vv{}|y~~~~|x~{t|zzzx|~~~xty{~}{twwqxxx{~~|~~}xz|z|~|w~~~{~{~~v}}~}y{y{{~~~~~w~{|}wwwxvtxy{vyz~|w~|{}~{zz}}~z{zwzyy{z{z|}~ywx|~{sxwtuv~~||}uw}{}~{|y~~~x{zz}||}{yzz~|z}|{{~{zyx{stv{}ux}~}{~|z~||w~}~{wy|{wu{x{z~|{y~z{|wxvry||~}}}ywy|{z~~~{{~|~|~~~}{{}z{{{y{yy}}}}}}}x~}}z}wwxuxxzyx~|~|~yzy{z{~{w{y|zuy||w{}}~}|{z|~~xxuw|{|}}~|~wyx{}|~~{|}~~~~~|~{}{{|~~{zzw}}}z~}{~|}zyzzvrw|zx{~{~|yz||||{{}~|}~zzyw|xv{{zyy|~}y}yx|w{{~~|}z~xt{zz{}~~{|~}}~x|{zwwx{~}~{|}z{~{~||ytvuyyx}~x~|x{|~~yz{|~}xw|xywx|wwy}|~}}{{z{x{{}}}}~}}yuzwy~{|yw|}~|{~z|}~}}|xz{z~~yy}}x~xzwvwuz{{}~zx~xz||}{~~zz{w}wwyt{zwxx|{{~}z{~z{|uw~}|}|yvv|ux}{|{x|}~}zy~}{|}}~~yz{|}~{z|{x|}{yxvx{xz{}|{{}xxz~|~zyyy|~~wtyxv{yz{xz}{z}yxvv{~}|xzwxww}z|{|x~~~y|{}}|x}|{z|z|}wy|z~~zzxzzzzy~|||z|zxy}{|{x~z|~~wwyvw{}zxy{~~~|y~|wxyy}~~{}}||wuyyz|||~}{yy}}|~{xz}||{y~}zzxz~|x|{z~{z}yzz|}zz}z~~{x{z||~}|{{{|~~~|wtxy{|x|{|~z~~{zy{xz~}zz|~}yww|xw~~~~{zy{}~}~~{z{||~}z{{|yvz}}|{~x|}}}zy{z{{|~~}~~|wv~~}||z|~{~~~xuxy|{x|~yy{~{{vy}}|{y}|}{wyxy|~~y{zzz}}~||{{{||~}~}~{||xxy{}~}z{|~}zxvz}~}zywzy{~}zyzz{||xwyyy~{}yy{{zxxy}{|z{~~}vv{|}|{{{{vz~}~{~~{|y{~|~||~}{|yzy{}~{{~}~}~yuwz~|~}|~~}zz|zz{~~|{vvz|}{wvw|z{|y{z~y{xz}{z|~{yy}}xy|~~zyyx}~~~||}|y{|}}|{|~{xwy{~~}}y|}zzwz{{}}~|~~vw{{z{ywwz||~~yvx}}{{z|{y}~}~xxy{~|{|}|~}}zzyw||}~~|yyw}}{~}}{}}||z}||}{|wwx{{z|z|~|{|z{{z}~~~{}~}yvxz{{~~~y{yx|}~~{xyzy{}||||~}|xyy|~zz~|zxzxx}}~~{zyv{z~}||zyz}~|z||{{xwyyz~~x{|{~~|{z{zy~xz~yxwz}|}~}z{wz|{}wvyx{~~}}~zyz{|z{~|zxzyz}~~~}~yvuy}{|}~~~yvy|}|z||}||yyvw{}zz~}~}z{zz{}~~y{{xyz~}}|~zyxw|}~~}~wtyy{}}}z{xx~}|}~}{{x||z~~zwvv{z{~{~{yxx}{|~~|}~~}yvwz~~z}~}}zx{{}}~}zz{yy}~~}|{{yxwz|~~||}~yuwz}|{{~}xvx||y|~|y|~|{}zywww{}~|z||{xy}|~~~~}|}~yxwy|{~}~|yzz~~~|y~|}|yz~yz|~zwwy{}{{~}}|xvy~|{|~yxxyzyz}~|}||||~~~ywxvz}~}~{z{~|y||~~|{~|{{}{xww{~||zz{|}{|}}~}yz}{z{~yxwz{z~~}{}~|wy|~}}}}}{xwz~|zz{|}~~|yz}~~}zwxxy|{}||{{}~}||}{{{~|{|}|xux~}|}~}|yy{~}{}~~}}yuy||}|}{yxwyy|}{|}~~|x{}~}||~~xwx~{{y}}}zyz~}~{yyywx|~}|z~~}|||}{|{~~}|{|~xyy|}}z||~|yxz}zy~~xuw}{||~{xwuy{}~{{}|||y{}|~~}}~|xy{}||{}z|||}~~~}~~}|wvw||~}}~~}xx|~{|{~~|z|~}{xxz}{z~~yxz{||~~|||xyz}}z}{}zvvv{{|~~}|~~|~}vwz}~}}}~}zxx}}|}}|}|||}~|~|xwyz|zz~~~~|xw{~{|{|z|{xux||}~|zyzzy{{||zzy}|{}|{wwwwz|}z|~}}}}uu|~}~~~~}xwx}}~~~}}{||~}|{}}}{zywyxz~|~|{z{{|}}|~}}zwwx|~}}~~}|zwz{~zx~~~}yxy|}|}||ywuw|||}~~~{wx|~~~~~yy{}~z|}}}{|~~|z|}~~}xwuxzz}{z}~|zy}~|{x||z}|zwwz~{|~~}zxy|~}~|x{}{~zxxz~||~~{vvw{}|~~~}~~|xy|~~~~~{zy||zz}}||}~}||}|{wvvxz{}{z{~|zx{{xz{~{{~|xxz}~|~~{|z{{{~|{y}~||~}zwy}~~}||~xwwyy{{{|xvy~~|{}wvz}{{~~~~}|~}{||{xwxwx|}~|{{~|{zz||yzy{~}}}|zx{|}|xx{}z{~~~zxy~{{{~zwuw{~zy|~~|ww{}~||}yww{~~~|~~~}}}}{}}~}|yxwxz|}~~~}{}}|{z{zyyz~~|}{yz~~}~}zxz}}}{}|{yy|}{{}|{vux|~}}|z||||{wy}~}}{}}xxx~|{~~|{{~}}~|}~{xyzy|}}|~~|{|}|zz~|yyz|z{}~{yx{~~}{{z|{|}|}|}~{xyz}||||~yxyzz{|~|z}~}}|{wy~}|{}wy{}||}z||~~{{|}}{zzz|{y|}|||~|{{}~|z{zz}}z|~~yvx}}}~~~|yyz|}{~}}|~yvx{~{||}|ywvy{}~}}}~}~~{w{~}}}~}yz{}|}}~|||{~{{~~~{{zxyy{|}}}~}zy{~{xx|~}{~zxx{z{~~{xwz{}}xz}}}ywxz|||~~~xvux{|~}|~yy{|~~~~|zz|}}|}~~{{|~}}~||~}zxvy{{~}}}{xx{}yyy}~|~}zyz|||~}}yxxz|~|y{~}~zxwx|~~~{}|vvvx|~~}~~}wuz}}{}~~|{y{}}~~|{|~||z|~zwwx{|{}~~~~{yxy{~|z|{|}}}~}zwz}|{}~{zxx{}~}{}~zvw{~{|}zwvw{}}{|~~~yvx|}||}~~xwy~~}~~}~}|}|{z{|~}zxyyyzz{~}||}{yxy~}|yx|~~}ywy}}{~}{yy{||z~}||yxz}|{~ywxz}~{{|~}xvy|~~~~~yxy}}{{}}{{~|z||}~}zzywx{|}|z{~|zyy|~wy~||~zy|yyyw|{~}|x{}yy~}}ww|ywsx~~~x|}}||}}x|}z}{}z{|~~~~|z~{}}w{|}xvz|zyzvy|yz}}|}wy~}yyxu{|x~}y{ww{~yy|z~zy}|xsx}~z}{~|}}|yyvu{}}zwxw||~~}~|{|z{{|}wvz|yx~}}{xxy{~~||w{~{}}xyuu|}}~~~y~z|~y{~{{vv}{yv|~w{z{z{{|xzvy~}~~|xuy{x}{|z{y~z~{|~yyz|xv{y|{xxx{~~~yxqy{|yzty~|~}~~wxuz~{}{{tuz{y|~||zw|{~|}|}vxy~|xr|zy|z{~}|zw{{}z|~|zxz|y{}||||y{z}||yryxw}~vy|v}}~}{|~zyw{~~xx{~{{||{}yx|yz~y~{su}y|ws}xy{y}}}xtt~~{{{xz}wyyy|zz|}}{}xrywx~x}x~yzz|}yw{~|}~vx}{}}|z|}yy{{~{~uwzu{~xu}w{|{~}~ytu~}z}}~{xy~{x|||}~|y~z|}vt{x{}w}{w}y|{{}zvu|~|y}xzwyyz{yz||zzsxyw}~xzy~{z{~~|wsv~}~vx}z{}}{}{|{{}x|~ww{u{yu}{}~|z{~|wxr|txs~{{zwu}|x||{ux~ys~|}~wy{yy}{~~zur|zz~~~{y~|xuy~||{}}}yu{{{~{uw}uz|u|~~~}z|}~wy}{}~wzxv|x~~}|z~z}wtsy}z}ytv|~{|}y}|{z~yy~x|w{}~vtv}v}{z}{{z~{z|{sov|z}}~}z~|~|{|vw{y~~zsx}wtyxsv}yy~~~}vuyu~vuws}||~}~yxtu}~zss|}zzxww}~y{}}{x}~}xzvuzvw{}zyuxw~z||vzvvst~{}yx{z~}}y{z}}{{~|}yuz}|{~|z|z}|w}~}zz~zstzv~~{~x}xszwt}xz{zy~{|{}}}~||z~~z||}}{}{|{~zx|yx{|~uxrv|}wz{z~~zz{}{zz||}y}|||~z|wy}}w~|wz|}z{xz~{yz{|utz}~{{yqw{zwzysz}zx}~|y}zzy{|~|v{yx~}yrty}{~}xuwsvz}z{~tux~{z{x~}{||ypx|~{vxww}~{|}|z{{{{{|}~|y}|xrsz{}}wx|}||}}rz|}{x|x{{~zyx}~~~wtv}vwvx{v}}}~}tyvvz~~w|}y|~~{{tu}~~}}|zz}ytyx{~|{us}v{wx{y}{zw|~~yv~}x}z~{~}wz}v~wz~{y}{|{z{x}~~z}{yy}{syuyz~{z}ys|}xyw{x}|qt}{~}yz{vz}z{{yxxrw}}~|{|z|~su}wt|}{|}|suzt}|{x}|ywr|wtz|yusu~{x|zuuzx~~yuw~~{ywuvz}{y~|~vyzz~y~wz{}z{t}~}|~wt~ytyz|xts}}}{zyyz||}z|~uwwt~}yzro{|uyx|}yz~~|x}|}|v}x||}y{xys}{~z||}{}}|y~x|zy}x|}y~{}yxv~{vxzx|~~{zwpy}|~~~ttwz}}y||qx|xx}~uwwzz}~zsyw~~{|wsu~vw~~|{||yyy{~wux~uov||}yzy{|{yy}{y{~vpyuw}z{vymt~}}z~w{w}|~}}}z|s|~y}~|~{u~ux{|}~|~{{~zwzw~~||{|}{plzv|}~}y|}vtzz}}wyv}~y~t{z{xv}wyvy|y{~|vy~~yz}z|vxw}~wvu{{{~|}|}z|||yyz~{~~zxvr{}zwz}z|yuuy{|~usv|~{{{xzyv|yxx{su}y~v{zvy}z~u}y}vw{|}vyx{}~|{|zv|z{}}yxz|y{vp{vz~}~z}wvwwy}~wtp}~}x|xr|w|vx~yrz~yv|~~|wy{y~uz}vx{}}vyw}}{{{|w}yz~{wzz}x}vp{uy~}|}{wwxww|~ztr||~y}wr~x|wx}yry|~}y|||~zyyw~}u~zvx{wxv~|~|~~x~}}xx}~}x{z~}vwtxu}y~{z{yxxxy~~vt|x||wr}x|xz|}zsx{}{}z|}zww{x||xw|}xxv~~{~~~|v|~wxz~z|{{wwwvu|}z}zz|zwz}yx~vw{vz~vs{y{x|y}yuw{||~x}~{vw~yy{}yw}}yww|~{}{v{~}xyy~z|{yyvxtv}|z}z}z|zw{|yyvzzwy~~uvyzzy{y~wvw||}}|x~}zvz~xy{~~yx}~yvz||||zw{{xy{}z|{x}vwtw~|z}~{{|{yw||x{|u|yzz~}szxzzzz{tww|{{y~zzv~wy|{yy}zxu}}||~~}z{|zw{}{|z{|y|svvzzz}~~z}wyx~zy~~yu{z}|~{u|~xz{x||{swyyx|{}zy{yz}xyx}|{y~}~~{xw}y~}}z~{|ysuyy{yuzytwyy~vv{z}}{{xw{{zyu~~yzy~~|yy~tvw{}|u~~}~}|yv|yy~~x~x}z~||z||vpxz{|v~zvzz|y}zwwz~x}|~|}r{~|~vywx|}}{t~{vu}~{wz|~|}zz||~~z~}w|~{~||zz}}{y~{sxw{|~|z{yvyy}~~~~xvt|~~ut}|yy{}}rwv~|{yy{~yu{}~zv~u{}~{z}}}{zz{}{}~zz}|~{~|xxs{z{{x~~~qtwy|{|ww|x~~}zyvy}vx~{tt|~||}|{uzx~yuz{z{v{~|}}|v{w{~{{z{y}}wsuz{{}y~{}}}|z{sx{{||z}~~{{yws}~|z{wuy}{{z|zyt{}~z~{|z~yv}|}~|{}|z~|y}~z}z}wwz}u|yxy|{z}wx{}~y{{|{z}yt{|y|}|w~yvty}yzy}v|y}~}z}}~|~uw}|~{{x~{~}y{~~x{}~xyx}wuy}{{ww{||z{vy~~x{y}{|yx~tvw}zzy{}}yy|z}~y}|~|{{z~}|yyy}~|}~|}|}|x}{}yvwut|z}~{|~}uz{z|wy~x}}~}wwywz{sv{}x{{}t~|||~~{~}}}{~}{{{~||w~yxy~~|}~}}|~~zy~{{yuwtwz{}~z}~|u{z|zw|{y|~~}~|ux{v|~xtw}yy{{{v{~|}|z{~~z~y{{|yz{wy{|{~}~~~|~|{{|xxtxt{z|~}{}yu{{~}xxw|~|~}~zs{{|w~xxuz~v{z|zz~}y~~~~{z~z}yx}}~~z~~wx{}x}~|~}}{}}}~~zwvuyw}z~~z}zwu}z{vw}{|~uv}|yz~{wzv~zw|z{||}zx~}|~yz}~u|}}{{~u{{~x~|}~|y|}wvuu{x~z||zyuv}zyy}y{}}||sy}}y}}wzyyyx|y}|~}w{~~}|}y}{}{v}}~~{||{v|||z|~~~}}|{|~~vvuw|x~||~yyty{zx{|z|{~}zt{}}~y~}v{x{}xy|y~{~|v~~|}|z|}~}yw}~~~|{}yw|}z{}~}|{||}}vwtw{y}|~}~yytz{{x{{{~{}~zs{|~}y~~w{w{}wz{y}{~}w|}{{|~}~zx}}~}}}~yw|}z{~~}}{|~|~}xxtwyz||~~{xuz||x{z|{~~zs{|~|y}yyvz}w{{zz{}z~||{}|~~|x{|~z~zwz|z|~~}}{{|~~{yuvu||{}~~yxz{~yzy}}}~{ty{{y|}wvy}~w{y{y{~}~|{|{~|~zy|~y{xx}{|}~~}~{x|~~~|zuvt}~{{|yz{z~z{~y~~|~{vy{~zy{vuz|~x{y{x{~~z||{}}~~{x}~y{yx}~{}~}}~~~zw}}~~}yuut~}{{|y{zz}z|~y}~~|ywy|~yz{~uvz}|y{y|x}}}z|{|}~}~zx~~zzxy~}{}}|~~yx}}|~xvtv~{|z~~x{y}}z}}z}~}|xwz}}y{{{uw{~yz{y{x}|}}|z~~|yy}}|}yxz}z~}|~|xz~}||vvtz~||z{xzz{z{}~}|~~ux{~{z{~~xvy}~w{z{y{~|}~~}{z~~}zz||zxz}{|~~}~{{{||{yvuw{~}{|}xxz}y}}~~~|~~yvz}}{|}zyw|~yx{{y{}}y~|z{}}~}zz{~|xy{{}~~}~|z}|~{yxuvy{|||zww~~{z~~}~}~|}~~xv{}|||{zyx}}yx{{z}}zz~~{zz}}{z{|~~zxz|~{}~~~}}zz~|~zyxtxy}|||yvy~{{~}~|}~|ww{}|||z{yz}{yy|{z|~y{~~{z{}}zzz}~~yxz}}|}~~}}}y{}|}zywtxz}~}|}xvzz{~~}}{~~|wx{}{||{{xz}{xz|{z|~y|~~zy|~~{{z|~yxz|||}~}~}z{{||{zwuvz}~}{~yxz~z|}}{}}|wx{~z||}zwz}|w{z{y||}~~{y}|{yz}zyy|{|~}~|~{{yz|||wvt{{|}z~{y{z{|{~|||}wyz}zz||zvy|~~w|zzy{~~|~~|x~}~}|zy~|zx{{|||~|{wz~~}}}wvtz{{}z|{{y|}{~|||}xyz|zz|{zvy{~~x{zyy{}|}~{y~}~~|yy~}zx|{{|}~}zwz~}}}xvt{{{|z||{z}~~{|||~~xy{|yz||ywy|~}x|yzy}~|~~{z}~~|yz~|yy|~{|~}~~~|yw{~|~|wuu{z||{{|z{|~|~{||~|xy{|x|{~~xwz|zy|yzy~~}~y{~~~~||x}~zyy~~z~|}~|{yx}{~zwtx|z}{|z|z~~||~}z||yyy|zy}{|xy|}~w{zzz{~~~~~~}x|~~||{y{yy{{}~}|~~yzzz{||xvuz||}z}{y{}{~~}{{~}|yxz}y}}~~||x{|yx{{|z}{~~}zy}~~z|y~xzy~~|~}~}|zx|z~~{|zxvw{||}zyy}~|~}~~z|}zzx{|{}~{}zy|}}xyz||{}}|}|zy}~{z|{|xzz~}~}~~|}~xy}{}{{yxvw||}|{~xz~~~}~}~~z}}~zyx|{{}~{}zz{~|xyz||{~|}}|yz}~z{{{{yz{~|~}~~|}~wz|{|{{ywvx}|}{|~x{~~}~~}~}y}}}zzx|{{~~{~yz{~{xzz{{{|~}|yz}{{{{{yz{|~~~|}~wz{z~|{|ywvx|{}{{~y{}~~~|}y}|~zzx|z{~}|}yz|}|wzz{{{~~~~}y{}|{|y}yzz|}~}}}~zyzy~}{}zxux{{}|{{{|~{}z{}zzy{zy}|}zy||~xzzzy{~~}y{}~}||y~{zz}}{~|~~|~{yxy|}{}{xuyzz|||~}|{~~|}zz}|zz{yy||~|zy{}~yzzzy|}~}z||~~|{z}{z{}}{~}~|~|yxy{}|}{xuyyz|||~~|{}||zz}|z{{yz||~|zy{}}yzzzy}}~}z||~~|{z~{z{}|{~~}}|}|xxy|||}{wvzy{|||~~{}}|{z{}{zzzx{|}~|zy|}{xzzzy~~~{z{~~~||{{}zz|}{}~~|~}zxyz~~{}|zvwyz||||~|{~~|}~zy|~}{zzzy|}~}|y{|~yyz{z{~~~zz|~~}{|z~z{z}|}~}~|}~~zwzy}~~|{|{xvwz{|}{|{~~}~}x|}~{{yzz{}~|~z{{}zxy{{|{~~~|yz|~{{{|{zz{}|~~{}~}}wzz{~~|{{|zxwx}{}{}z}~~~~~zy}}||zyz{|~~}|}z|||yxz{||{~~|{x}~|}z|{z{z}|}~~}z~}}zw{z}~}|{{{yyvz}|}{}z~~~}}yz}~||zy{{|~|}|z||{yxz{|||~{{x~}||z||z{{}|~~~|z}}yw{z~~|{{{{yyv{||}{|{~~}}y{}~~||zyz{|~|}|{||{yxz{||||{y~}}|{||z{{}|}~~}{~}}ywzz}~|{{{{yxwz||}{~{}}~}yz}~~||zyz{|~~}}|z||{yyz{{|}}{z|}}{{{~{{{||}~~~||~}{wyz{~}z{{|zwxy{{||}|}~|~zy|~}|{zyz|}~~|}{{|~~yzy{z{~~{{z~}|{{}~{||||}}{}}{xwzz}~z{|{{wyxz|{}|~|{~~{y{~~}|{{xy{|~}|}|{|}zzyzy{}{|z}|{{|~{||||}}{}}{ywzz}~z{|{{wzxy{{}||{~{y{~~}|{{xy{|~}|}|{}}~zzyzy|}{|z}|{{}~{||||}~}{~|{xwzz}}z|{{zwzxz{{}|~}~|}zy|~~}|{zxz{}~~~|}{{}~|zzyzy}~~~|{{~}|{|~|{||{}~}~||}{zwyz{~{z|{|yyyy{{||~||}|yz}~}}{{yy||~}}}}{|~~zzyzz{~~|{z~}|{{~~{|}{}~~~}{}|zxxz{}}y{{{{xyx{{{|~}~}}}zy|~}}|{zy||~~~}}}|{}~zzyy{{}}|{z}~|||{}|{|{|~~}{}|{yxy{|~~zz{{{yyxz|||}~}|}{yz}~}}|{x{|}~~~}}|||~~zyxz{}}~~{{{}{|{|~~{{|{}~~{|}{zxyz{}|y{z{{zyx{|||~~}}zz{~}}}|zx||}~~~~}}||}~|zyy{{}}}{z|}{|{|~{||{}~~~{||{yxyz|}~{y{z|zzxy||{|~}}|zz{~}}}|zx||}~~~}~}}|}~|zyy{{}}~{z|}{|{|~{||{}~~|||{yxxz||~|x{z{{zyy{|{|~~}}zzz~}}}|{x{|}~}~~}}}||~~}yzx{{|}~|{{~{|{{|{}z}~}~{||zxxy{{}xz{z|yzyz|{|~~|~{yy}~|~|{yy|{~}~~}}}|{~}z{yy{{~}}|z}}||{~}|}{|~~}|||zxxxz{}}yy{z|zzyy{{|}~}}|yy|~|~}{yx{{~}~~}}}}{}}{{yyz{}}~|z}~||{}~|}|{~~}|||{xxyz{}}zx{z{zzyy{{|}~}}|yz|~|~}{yx{{~}~~}}}}|}}{{yyz{}}~|z}~||{~~|}{|~~}|||zxxyz{}}yy{z{zzyy{||}}~{yz|}|~|{yy{|~}~}}}}}|~}z{yzz|}~}|{||{{}|}z}~~~~~||{yxxy{{~{xzz{{zzyz{||~}}zyz~|}~|zx{{}~}~~}}}~|}~|zzyz{}~~|{}~{|z~~|}{{~~}~|{{yxyx{{}}xx{z{{zyz||{~}~zzy}}|~|{yz||~}}}~}~||~~}zzyz{|~|{|~{|{||||{~~~~|{{zxyxz{|~}zxzz{{zzz{|{}~~{zzz}|~}|zy||}~}}}~~}~|~}{zyy{|}}{|~|{|{~|}{|~~~}|{{xxyx{{~~|yxzz{{zzz{|{}zzy|}|~|{zz||}~|~|}}}|~~{zyz{|}}z}~|{||~|}{}~~}|{{xxyx{{~~}{xyzz{{zzz||{~~zzy|}|~|{zz||}}|~|}}}}~~~{zyy{|}}{}~|||||}{|~~~}|{{xxyx{{~~}{xxzz{{zzz{|{~zzy{}|~}|zz||}}|~|~}}~|~}{zzy{|}~{|~}{|{~}}{{~~~~~}{{ywyxz{}~|yxzz{{{zz{|{~~~|zzz}|}~|zy{||}}}}}~}~}}~|{zyz|}~}{}||{}~}}{}~~~}|{zwxxy{{~~}yxyzz{|zzz{||~~zzz||}~|zy{||}}|~~|~}~}}~~}{{zy|}}~{}}|||~}}{}~~~}}{zxwyy{{}~}yxyzz{|zzz{||~zzz||}~}zzz||}}|~~|~}~}}~~}|zzy|}}~{}|||}~}}{}~}~}}zzwxyy{{~~}yxyzz{|z{z{||~zzz||}~|zz{||}}|}|~}~}}~}{zzz}|~}{~~|||~~}||~~}~~}|zywyxz{|~{xxyzz|{z{{||~{zz{||~~{zz{||}|}~|}~~~}}~~|zzy{}}~|}|||}~}}{}~~~}}zywxyy{{}~|yxxzz{|z{z|{}|zzz||}~|zz{||}|||~~}~}}~~|zzz{}}{}~}|||}}|}~~~}}zzwxyxz{}~}zxxyz{|z{{{||~zzz{|}~}{{z|||}{~~}|}~~}~}{zzz||~}|~|{|}~}||}~~~}|zywyyy{|}~{yxxzz|{{|z|{~|zzz{|}~|{{{||}|{~~|}~}~}}~~}{zzz}|||~|||~~~}||~~~~}{zxwyxy{|}zxxxzz|{{|{|{|zzz{|}~|{{{}|}||~~~|~~}~~}~~}zzz{}||}~|{|~~}||~~~~}{zxwyyy{|}{xxxzz|{{|{||~|zzz{|}~}{{{||||{~~~}}~}~~}~~}{zzz}|}|~|||~~||}~~~}}zywxyyz||~|xxxyz{|{|{{|}~{zzz{}~~{{{{||}{}~}|~~~~~}}}z{z{}}|}}||}~~}|}~~}}}{yxwyyz{|}~yxxxzz}{{|{|||zzz{|}~{z{{||}||~~|}~~~~}~}}{{z{}|}}~|{}~}|}~~}~~|yxwyyy{|}~zxxxzz||z|{||~|zzz{|}~|z{{||}||~}|}~~~~}~}}{{z{}|}}~||}~~}|}~~}~~{yxwyyz{|}~yxxxzz}{{|{||~|zzz{|}~{{{{|{}{|~}}|}~~~~}}}{{z|}}}~~||~~|}}~}}}}zxwxyyz||~~|xxxyz{}{|{{|}}{z{z|}~}z|{||||{}~}}}~~~}~}}|z{z}}~~}~}{}~}}}}~}~}|yxxyyy{|}~}yxxxzz||{|{|}}{z{z{}}~{|{{|{|{|~}}}~~~~~}~}}z{z|}~~}~}{}~~~|}}~}}|yxxxyy{||~zxxwyz||||{{}~|zzzz|}~|||{|||{{}}}}}~~~}}~{{{{}}}~~||~~|}}~~~~}zxxxyyy||}~|xxxxz{||||{|~}{z{z{}}}||{{|{{{|}}|}}~~~~}~}}{{{|}~~}~~||~~|}}~~~|zxxxyyz||~~|xxwxz{||||{|~}{z{z{}}}||{{|{{{|}}}}}~~~~~}}}{{{|}~~}~~|}~~}}}}~~|zxxxyyz||~~}|yxxyz{||||{|~}|z{z{|}}||||||{{{|}}}}~~~~~~}}|{{|}~~~~~}}~~~}}}~~~}{yxxyyz{|}~~|zyyyz{||||||~~|{{{{||}}||||||{{|}}}}~~~~~~~~~}|{|}~~~~~}~~~~~}}~~~|zyyyyz{|}~~}{zyyz{|}||||}~~}|{{{||}}|||||||{|}}}}~~~~~~~~}|||}}~~~}~~~~~}}}~~}{zyyyz{|}~~}{zyyz{|}||||}~}||{{||}}|||||||{|}}}}~~~~~~}}||}}~~~~~~~~}}}~~}{zyyzz{|}~~}{zyyz{|}|||}}~~}||{{|}}}}||}|||||}}}}~~~~~~}}||}~~~~~~~~~}}~~~|{zyzz{|}~~~|{zzz{|}}|||}~~}||{||}}}}|}}||||}}}}~~~~~~~}}|}~~~~~~~~}~~~}{zzzz{|}}~~}|{zz{{|}}}|}~~}|||||}}}}}}}|||||}}~~~~~~~}}}}}~~~~~~~~~~~}|{zzz{{|}~~}|{zzz{|}}}}}~}|||||}}~~}}}}||||}}~~~~~~~~}}}}~~~~~~~~|{{{z{{|}~~~}|{z{{|}}}}}}~~}|||||}}~~}}}}||||}}~~~~~~~~}}}}~~~~~~~~|{{{{{||}~~~}|{{{{|}}}}}}~}||||}}~~~}}}}||||}~~~~~~~~~}}}~~~~~~~~~}|{{{{||}~~~}|{{{{|}}}}}~~}}|||}}~~~}}}}||||}~~~~~~~~~}}}~~~~~~~~}||{{|||}~~~}|{{{{|}~~}}~~~}}||}}~~~~}}}}}||}~~~~~~~~~~}}~~~~~~~~}|{{|||}~~~~}||{{|}}~}}~~~}}}|}}~~~~}}}}}|}}}~~~~~~~~~~~~~~~~~}|{{|||}~~~~}||{{|}~~~}~~~}}}|}}~~~~}}}}}}}}}~~~~~~~~~~~~~~~~}|||||}}~~~~}||{||}~~~}~~~~}}}}}~~~~}}}}}}}}~~~~~~~~~~~~~~}|||||}}~~~}}||||}}~~~~~~~}}}}}~~~~~}}}}}}}}~~~~~~~~~~~~~~}}||||}}~~~~}|||||}~~~~~~}}}}}}~~~~~}}}}}}}~~~~~~~~~~~~}}||}}}~~~~~}||||}~~~~~~~~}}}}}~~~~~~}}}}}}}~~~~~~~~~~~~~}}}||}}~~~~~}}|||}}~~~~~~~}}}}~~~~~~~}}}}}}~~~~~~~~~~~}}}|}}}~~~~~}}||}}}~~~~~~~~}}}}~~~~~~~}}}}}}~~~~~~~~~}}}}}}}~~~~~}}|}}}~~~~~~~~}}}}~~~~~~~~}}}}}~~~~~~~~~}}}}}}~~~~~}}}}}}~~~~~~~~~~}}~~~~~~~~~}}}}~~~~~~~~~}}}}}~~~~~~}}}}}~~~~~~~~~~}}~~~~~~~~~~}}}~~~~~~~~}}}}}~~~~~~}}}}}~~~~~~~~~~}~~~~~~~~~~~~}~~~~~~~}}}}~~~~~~~~}}}}~~~~~~~~~}~~~~~~~~~~~~}~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m32.bin b/applications/SprPoP/dist/MUS/m32.bin deleted file mode 100644 index 4dbae7b..0000000 --- a/applications/SprPoP/dist/MUS/m32.bin +++ /dev/null @@ -1 +0,0 @@ -}xuj[NSXYfhsx}~y~hkfr}z}zufjcUSlvtj_f`}{uqw|iuePnse^iceemxqwtnqr{svlqwqkwu{gtt~|{ntpzlpmorn{z}|stszyo^hijm{z|xwqm|{lr}vwotszxx{rxqjjkpks~jpahy}xd_\~pmomi|~z|r}joolqtrxw~v~ncelp|vzxx~~whtjwtwyy~{rmY[cvxagj^`ceblmreiimrktyzoukyur|yrwdgjirlwwvuvwtu{|oZ`Zpyq~tqqgq^b]foqhhmfzt~vvqkhfckv{{|wvzw~zxu}s|qz~vodZb`b`w}|mlgeajondrlxvpwtsxv~suj{s{wsvuxxxz{~xzr|~||}}yed`]hcqy}wnkixyyinerts}o~{sxttoy|u|ynofuiqjeulsy|||zz~un_gedSWZlxz~q|y}wy~x~x~twtxuwv|{~ty{}y}xr}q||trquxxv|{{xw~|jkibj\\m}zrtqitsp{lxvszwzu|~yu~ojdoakc]f\dhmrvy~}}~qcmmoZSLYbi{~|ryzv{xutq|w|{}w~vv}n}x~~r}}unvwp}nvuqytvqu~wkvknp`bkhoy}{vo{ouukvouwuxt~wy~}p{zqruepb`b[d_cdiy}t~rjoryg\R[TVeawsv{}y{xvpy|pz|}}ozrryq{xqxwn{nqqqysvrqzvmzqqxjlqhikn{vtuyjqmtwtxrxwwy~nsyyxowncg`hcbb_ksmz|wlqs~vf\`]P]SYcpxtzzz|{|ts{rpwzy}nxzujs{vu}pxwkqmxwuwpuznu{p}yqwrlnhnv{z~r~tmmmwsuvqy{rztmu{yv}tpkghmfgd`iig|{pntzue_fZV^OSUet|qv|}ruz}}wuzpuvzqwwkcnzux|ozwmnnxuvysvynx|s}w{soqjnst|x}uyqnpwrtwrwzr|qnwwt}|suokmpijibhhdwx{oox}xgaf^U`RPO[fl|~puqq{uw~~w|vuy~vtui`kxwyst|ponwutyxu|vwv|}yrqnjssvpzx~tsszutwuu}w~{sq|ys|~r~{rorvomoihlftxt~pu~~yjdjcV`WPOX_amz}yp{qpyrpyz|y~{z|~xssgbox{~|tzuv}qnqzvuxyu|y~x}zvtpjrsrir~x|xuyyxzwt{x}rytr|{syspxzttslmolyus|~vq~}wihma]dWQQ\Z\dnq}|qs{pvxlryqr{|{{yvqelw{xyqo{|s{rnvzwxzvuzy|xzxrnurlgtoktv|yy~}~uww|wwzovzpy~uqw}xyzwnqny|q{}}xnyqioh_ibWRZZY[aagssp|tuynoxplq}}~~ximv}{ysjsuo|wsyzvy{yrwv}y|zxszumfrlbfdey~{ww{||tvvlu{uqx{xz|}tuq|}t}{wnw}{rshblg_\aYZZ^XY`kwuz{{xlsypkrusz}ruuworjhx{wwyyw|xstw~}~}||xxxmktibcY\eq|}zz}}|rjuwrr{uuw{|uyy|vzy}ztww{xifnibdi^]Y_WUW`nkv{{msxsntrnqt|x~y}xwuidprrvz|y|xswz|y~zv{srwlcdZZ\`oz}}~~}zlrvroywtuv{rwzy{{z}x}vsiongelcd__\UY\gccstszvquynrnvqs{~w}|~}ugkqmlxzy}{tuxyw{|urzyjc_Z_Ydju|{yxuxxrxvpryqwuywqv}~xxy|}}qpvpgikagaf\VZbj`box|rw~yrwvquovnou|yt}z~vlpsnktqv||yyz~{||uqyylf]W_]jjpspwx|yuoqz~ws|{srwowu{}tw}x~{zz}~}{rpxwommbgahf]]ajecmrxtss}}xywnwrzuot}|}{rz}xmqxuosqmww~~~xrvynfaY_^injpjnom{~~vokxz}rw|zvxnquwz|~z|y{~{pmvyuttdhdeiadflfblrunzvt{|y||qvpzzsu||zws~}powxtvplup~}wyznc^[gekjemiifdp~zuoozz}w{|yy|osqvy}~|}xwu}pmuyvxukndkjcgmqgbjpqlxwzy~}y~wvzq{wwzz~ztqt{txyww{orrs}x~}~zkc\bjktfeihf^_luztomyy{x{~y{ttwnywxztpvxsvxwxzntplpggkrnhjkmists|w|~xr|u|ysyzx~~sop||zu{}x{suvpzvwvjadhhwokidc\[cimsyy}tmvvy~|zwuz~rsyrztsz}upsz}vu|~yxwowqsvkknrlinplerprvmtx}wuzt|vw~yy{~zypqvt}vs{{pzvv{sv{|tjlfiuoupg^W[cedifjyzzvvnzvnkvswus~vsw}}z~{svttxu|}unyrvysqrslhmqokrkrukmnr|yy{s~|{}~|wz}}w}}~xusm~sqx~uzuxywz{yzuuihvpuuofYX`edd]^kgx}|~qxxnhlu{~vzzq{yyz{yuz{yvo~z|~qwrs}wwzypklnonwrlsmmmlq{}{~v|z}zuz}{{~zy|ou~yqry}}y|s}~}~}xy~zxwlvvtwpkc]^`becZbbcrv{}zvwkhnstyxs}xx}xyytw}}vu{|zx~qv{y{}~vooqnozruthkmosxvssx|~|z|||{}ywxs{|urvyz|{s|y}}zz~|xzwq~uuoh`^beb``[ea`npu}}xx~yvolnqsspp}xy}|~zxz|y}{}}zy{yuzy}y||xvsppxqszojhlsyuomnvz|}{{{~}yys~|ywuqt{~|ztw~xvtxv{xpjc\^dgfaW_gakorz}{}tmmqtsrqjsyw|~~|z~~|}|ysu~xz{}{z{wrztowromjmtuoihqu~~~zy|{z{}~{upmry}~tvw|xyvzxvnd_`cege\_b`lqqv}}~ypjovwuojtrr{{~}~|vrsx~z{}|}|{|~rtwnmnoppnkhinp~}~{|}zz}yz{wnkox|}{v}zw}{}{zzuqhacfed`_h^^kpsuxy{}zwrqqtwumotmu|~|}|{~~}uquz||~{{}~}vztnlnsvqgdfkkp|z}~~xtx~||ywtpnrvyys|u}|~z|zunkkihf_]hdaggmruqpuz}wtvywttosvkryy~}}~{}||~yvvy{{xy|{}~vponqvtmdafdmyrtxx{~|}|vtttv~wqquvutsr{yx~y~zvqorrmhaaf`fkgfkoljkpt~zuw}~zrlwvotuvx{~~~|{~{|xx{xy||zytt~|~wqrtusokgc\huppppnnxzttql~}sosyzvolxwt~~~}}|~vqsuvuqddj_ckkkigghjklu~z~zy{~{rsupxyuvyxuz~~|wrytz|uq{ww}|wvxvokjja^moqqlhfiq}|wskt~}zustwzxnpupz~~~||~~uprxyvqjlgbhhjmnibejmnvvv|}~|yw}rpz{{yxvttz}~xx{yx}wt{tw}~~{uoifhbipinpmfafmsy~{zzvmxz|~wuxywvurwtmw}|{~{usuwxrltmdkkjkomiefkpyur~~}~wuyvyy}~xtvxy~|yz}x~zxz~~{|yyuw~|piffajwojhigddhosuvvtxu}yyyvuuuz}zuot|suz{~{{~~xwzyvusostlpomnnlklljiszt{|z}|v{{x{}{xxz{vsw{~}{vtz}vu}z~|~~wnfhfhtplkeaadilnqps{wvu~xvupmsy{|{ssxs{{}xz~}{uv|~xslswotwspqoiimomqsq|}wx|x|w~~}|z{~zuvwxtux|zv}{~}~uonlgoujjkhb[`hmnljrzx|z|}x}|xrnlov||yu{wr{{|}~yy{}~uovrszxyxsnkijmqwonywvvw~z~{z~}|~wvzzwysuz{||y{vzuprqowqjigea^_ejlggrpuzz~}|}}xpkmqtxztz}uy|~{|}}~|~}}{zwzspy{{{{ulkljmwsptoqtvx{}zx~}z}{}{uvzz~}ywwxzzwu~y~~{vuvpu~snmhb`accbecflfnwz~|z~|ztonpsrqq{yv~}{}|y{vzsq}wtvrrss|vkjjrz}zwv|v}}zwqjkwzik}zx~srz|vppv|v{uulizjhuytwyyxzsjo{~|ri`Y\dlpohf_Tcyvny}xuw}{oga[gkktvurpmpx~vzv||yutxy|okovz|~|{rihhhlu|xgn}y{vrx}{{}ysqqmiyvljq~zxlfrz}~{pefttvytlgefhgcXPUU^nv|zppx~ukgozy|}wv{~wsrpiw{unijqvxz{yrmkjjjliejfbba`cinsvyurzxz}zy}tuxlgjpt{~}|{wposvtv{uywx~xvqkbWUWWWWTY^Zhuz}~|wrsy}}woifkqutpwzv{ry||}x}z~{umkosx|zyvuyrinideiosrqsvyut|x{|zy}~zz{wzvxz{}|z}~}ytkeglrvv|}zrlheeec_enilsx}~xvvwz|uolllkkmjmpjpvw}~|zwuzvxzx|{z}}zsrqty|xqnsxwyywwxx{~|{~}yzrqtxzwplknswzxrmjklmortv~xuwuqnjiqorxvw||xwxy}|xrklpqniejpikopruyxvwxuw{v}ww{~ywxyzwsooruxwwurv|~}}x}xspsxvngfkkp~{vwtnhhnsrqos~|}{{|}{tlnrkrxxxwyyyyxxyrnnpsrnejmjppnprqruwwx}zu~{yy}zz{vqopsoszxwtrpry}zzxvturnihjjtxuwvtokkosvrmtxx|z~}vpsnmstwywrrvxxz}|srssrrmnnfnsrqqqnnrwx~y}}z}||~~|}yrmlpprytuxvplnsw}}zz{|yqmlllhrvptvupnopqrsptsq|}~xxzoosrsvwsonruz}w|~~zxxuqmtsknprtsollmpv~}{}xx{}zyz}||tpnmktztsttokkortvx~|}xolomktsqsqqonpsrplmwrpwy~{{~vuussstrqpnox|vz|yz}}yuottpurpsusnlmnoy{z{xx|zuuyxz|}~}uqoioywxtomklmnoomv~{~yrnlkvwstrnjkorssojqrqwvw{{{zz}xuttpoqsqqwuy}wvwz|tuwpuywtstrqpnnswt}{{{z|~}{utts{y{{ytmkvvvyvngglonmjjssy{xuskmyyxwqlhgmrsroormqyyxy|}~{|z~~|xuqoprswwpx|xvux|zzyswyzzytqrtsqvvry|{{{}~|xvwwsr{wz}~~{ztpywuvtpjfgloojiqor}|vtvrtzwyyumggilpsopvppvx|~|z{y}}uqqqqv|uuwvwwwy|zxywz|z{}|wrsvvzzswyux{}ztqtxw~|z{}~}{~}{{{y}utqnkiiijlmkppnw|xtuuw~vuurlhiklmnovttyxy}{|}~ytsrtyvx{trtxzzwtsu~~~~~|zwuu{xyzutuz~{vrrvx~~}{}~}|y~|x{~}zuqmhfilljhiqrnuy||wyxy{wromlmmllikvtu||{|~~~~}{wsvxrwzurpsxyupoty}~{{|zw{|y~zttsuz|xttwwx|{yz}~zx}{}}~zslhfilmlfgonrwwz~~}z}}{unknqqnkhmpqy|}~~|z}~~|yzutxttsqqttqnnsu}~{{}}}v}~yvstvvvusvyw~{vuz~~x}|z~|tkhijkkhhkgltuvx{{|~{uomprrnhlmjsy{}}yy{}}wvysqrstrommnonw~~~}~{}{yywvvvrpruuw}}wtv{}~{{xyzsnllkifjleinqsutsux~|xusqqrolokjprv{|{zyyz~~|{}|~~}{trpruvqjhkmksyx}}xxyzyvrnmqrt}zywwxy{yy|z}}ywuqmkehlgkkjnrsppruyzwxyvronspkopqsxzxyzzz~|z{y{~{urqrtsojgihnwuwzzyzzwy||wqmmmmy~|vtx{zxwu|}z}~}zzzwqjgkghnnllopppqsw{xy{zwontpqsqqstuwy{|~|~|zw}}|{ustsomkjhdowuwwussx|z{|xsonmlw|~}wtuy|{wtz{{}{y{{zwpjlhglmoonlmprsu|{~~{{{{zustorwvuuussx|}~~z~|{~{}}~|xusoighkhmsrvwtporw}{wtrrqow{y}~zxwxy{{xz{x~~yxz{yuprmgklloqpllptv{{z~}{xv{vqvyzzyvtuw{}|}~}}}~~}{{wpieeijpvporsqnosw{zrpswuz}xxz{zxy{{zyx}~x}~}~{zyywrvsmnmmmopnnmos{{y~~yw|yxzxy|}zvvwx|zxz|z{~}}{}~}}|ztkffihqupommmmortwxy|vrswy~xywvuwz|}|xv}}|{|~~~{xtuysstpnnnllnonpwvz}z{}z|}y~|{|}{yxxx}{x{~|ywz}~zx~}~~zsnjjhlropplhgjoqrrqv~~zxwwu}{yvrquz||{w{zy}{}~~}wtyuuxwspoljkmnpsotzxwwy}{~~{yz{zy}}z}|{|{}ywxz|}{x|z||wsqojmrnopnieeimonkpuu~}z|zx~}wrpruy{yx}yx~~}|}z{zzuxzyxvqljjjlqrlpsrstuxzy{~yy{||{z}~|zz}{|}yxyyywy}wy~~yvutorsppomifeegjjhlnmtz~}|||urrstvvw|yy||~}~|z{}z}|~wx{{{zxsmkkjnqnqplnqsuutsty|z{}{~|z{~{yzz|~}{yxuu|yy||~~~|xwwsv{vsrniffggeedhmimruz{~zvttssrqxzx}~}~|xyz{~{|}{{||yvspmmqnnsplkmqtspoptv|}{yyz~~{zzw~~|xtvyw{}}ywwu{~{zuoieeghgdagjinprv{}xvwwvtqouwx~~}{zywz}~~}~}}}{yxvsprnmqomlklpqnlmpqy}~{yyz|}|{{}z|}~wwzvy~~}{yv|}}ztlfeefggcegfmqrtxz|{vuxyxtpsus{~{xx{|{}{|}}zzzywxsmqollmmmlkkloru~~|zzzyvwy}~}{}}|}xx}}}|}|~~}|xslgfgffdfiehoruwyy{~zxwxxxsuvrw{zwvy}~~{{}|z{{z|yssokjjlmkgehoru~~|}}|{wtru|}}~}|{~}~}}~}|ytpnkigebfifjlnsvwvx{}||{ywvuyvtxy{{vvz{}|}~~~}}{{|yyxpljijkjfdflow|y|~|{|}{vrruy}}~~}}~~{xtrrplhcdgdhlklosstvx{~}~~|wsxyvyyyz}|yyy{}z{}~~}{{}}||}xrmkkjhfeeggoxvxyyyz~|}}yustuv}{}|z}|}zvvvusngfgcfjjkkklorsuz~~{wywv{zyyzyz}~}}y{~yx|~~|xz}{}{xtpnlhedffdkprvvtrsv|}zxvusu}~~||~|{~|~|{{}~yxxxvsmlidghhjkjghloqvww~~|~xvz{{zzxvx{~~|}|zy{||yy|x|}|yxuqkgddedjmlqsspnptw|{xwxvt{{|}~~}}|}|{|{{|~}|}|{zywsrrkijhhjjiggilqurvzz}}z{{{|}zwvwy{}}~~~{z{{zyyy}zy}~}|{{zumheecfnmmnoommnqsuw}}xvxvz~|}~}||}~~|y|}~~}|~~{y{}|ywtvuppmjjjihiiiiosrwwvx{}~||}|zyxxz}{}}}zxz||zvw}{{~~}|||xrlhgdgnnooljikmopqqu~{yxv{~{xwy}~z{~|~~~|yy{{}ytvwsttqnlkhhjkknpouwuvwz~}~||||{z}~}~}}~{xxz{|zwzzz}|{yupnmjhnnmnnkgfjnpqpqxz~~{{|{{}yvuw{~}}}~~}|{zz}|ywytvxvusokiijkoqmpttuuvy||~|{|}}~{~{|zyzzzzy|{x~|yurprqorolmljhggjmoopuvx|{~~~zvuvxz||}~~~~~zwx{~|}{vxyyxwtokjjkopnqporsuwwvw{}|}~|}|~}z{~|{zywx}zz}~ytrsutvxqnmjgfghhikkpsrw|~~|ywvvwwwy~~}}}~}yvv{}}}|{{zzzyvsomkloloqmlmpstsqsw}}~~{{{~~|{}~}zwvzyz~}}zutvvy}xuqmieegihgejonrvx|}yxxxwvutz||~}}}|yxwxy~~}||{yvurnnokmolkjknponnquy~~{zyz}~}{|}|}xwzwy}}}}}~{yxv|~|{xrkgfgggfcfhhnqrux{~{xyzywsswuy~}{xxxyw{}}~~|zyxwutqlnmkkjjjkkjkmnqz~}{zzzzyz{}|~|z{vx{{}~}{{}~~}y}~~zslihgfecfecilnprtux~|zzzyvsvtty|}ywwyxz}|}~}{zzyzyrqpljjkkkhfgilkry{~}||{zwuvz{|}|xzz{|~}{z{~~~~{uqmjhfcdgddgiloqqruy}~|zyvwxsuxy|~yvwxw~}|}}}~~}|{{}|wwrmkijkkgdcgiksvx|~||}|zvtsvx|~~}~|{|}}||||~~|wusplhddgefihimopqrux~}yvxzvwxxy|~}yxxx}}|}~}}|{~|~~z{ysnkjjigedegiqvvy{{||{||xusuvx~}~~}|}~~~|xvuuroiegfeijkkmnprtvy~zwyxx{zz{||~}||z}}zz}~}{|{||xtpljifddfhhnttwxxxx|}{zxvuvww~~~||}~~zwvvusokkgfijklmllosux|}|{|wx|||||zz}~}~}{z{}~~{}~|}~}ywtqnjecbfiiopptvvuux|}zvuvxxy~~}}}}}{|}zyxwuqpoiijijlmkklorwzy}}}y{{|}}{zz{}~|~}{{{{{|{||}|zxvtqkfcdfglpnoprrqsvy|zvuwyz~|~~~~~}~}}~}{z{}{ywtttonmjjjkjjjklquuxzz|}~}{|}|zyzz|||}zz{|{zxz~{|}{zywrlhffeknmonmlmoqstvw~zxxwz~~}{z|~}}~~~||~}zyz|}}yvwwstrnlkihhijjmoouutux{~}|{{zzyz~}~}~~~zzz{{zvxzy|~~~~||zvrnlifkmmoolihknopppvy}~||yy~{xx{}}}}}~~}||||zyyzz~yzyvwwurolihhijlljoqpqrtwz||z{{{{}{{~~~}~{z{zzxxzwy}}~~~}zvtrplmnmnonkhghklmlorsy~|~zwwxz||{~|~}}}}}{yxyzy{}}yxzyxvsokiiiknkkmlnoprtuux}~|{||~~z|}|}}|}~|{zywzzwy{}~}~zwuusqtropomjhhghijjnopuy~{xwxxxyz~}}}||}}|xvwzz}|{{zzyxtpmkjkmkmnkklnqrqqrv|~}|~|}}{z{}}{|~}{ywyzx{|{}~}xuuvuyyuspmigghhggglonsvy}~{yyyxwvv{}}}|{{{ywvxz}}|{{yvtrnlnlknmkjjlopoorw{~~~|zyy{}}||}}yy{y{~}}~|yxxw{}yxtokgghiihfimmruwz}{yyzzxwuxz{~|{zwwwzz|~|zxwvtqqmkmlkjjjkllmpux}}~}{yxyyyy{}|{{y{}~~}~~||||z~~{zxsmihhhhgfiijpsuxz|}zyzzywvxwy~~|yvvx{{~~~{zyxwvuonmjiiiiihhkosu}}|{zyxwuvy~{z||}~}}}~~}|yupljihgegigjmpsvwxz~~|{zyxwywvy{~}xvvyz}~}~}{zzzzusqlihhiigeeinpv{||{zzzxutuy|}}||}~}}||}}yuspmjgeghfhijmprrtwz~~}ussssspnmrvq{xronnortsyur}tikorttusneXUewoctxm~|~}vsux}yik}wvsx~{sqt|slttrstxz|}{uszv~}qvxspsuttlaZ_lz~wlgp|wtqokikimv~vor}}xusstuvzxnsznpzzw{vqqt|~{xuqi`XV^m{xkgo}{xwy|sjp}trw~}zwsru||uqnry{|}}}{xtsw|z~|wpoonsvwtne^\`hr}ywxwvsrnkllnry}wtwwxxxwvtrswxtty~{vux~~{wrlf_XYcmz{snnpsvw{~}|xvtsqpnpt{zwvx{}vpmnrw{yvu}ytpmieda^acglqtuvwwuurorstx|}yvqmjkotz}xtsqu}|yvuttwvy~~|wtrrqpnmnqsrrphedefhlrx}|unjiklt|~||}|zxwwwxwvsspkheb`_]\\]`djnouwxz}{wtqpprttuuuxwvwwwxzzzzz{{~~}}yvspmje`[Z\_dnruy{|||}~~~~yusqpqsuxxxwx{yzzyxyyxvvtstwuwxvtrrrrpnnqw|~~~z{zxwwvuuvvuxzz~{{yvwvsrqqppppquwx|}{zxwwwuttx|~zvsqmklhfedbacgjmopsy{~zwtqpqtv||zyzzyurrqnnlifda``acdgghmnnnoqsvx||xtstuuvttvtvyxyyyyz}}zutuuv{{tpjiijjovv~yut|{kkwrgfny{qp}lzozzpkhgjou~uq|{yz}ji|~hcxthejsy}|wuxvrnnwuenqgo}us}yyy}yxxvsonpwvdZ[cmqpib^\[^aahp|}ysnnxxqqv~}|{xwwwx~|tsy}xvuqsy}wgZXcpuna[Y\bhmoonkilpw|{uv{yvvy}}~|}skluztrrstrlgccdfpw|}umgb_^_afghkmry~vqnnqv{~vqpvunjiihfdcdiny}yvwwtuuuywmfbbinrux|}uonqv~ysollmmllnrv}wrrrqtwy}|z{zunid_]]_cfhhinoprrqpoopt{}yuuwx{{{}}}~}|xyzwvtsrruz~~ytplhghjlqttvuqmjgffffgkpv}wstsssuwz|}}}~|{}|{{ywuuwxyyvuusrsqomkjkosx~~~~}}ywtrpmmoosx{|{yvrnmnoonlnnmprrrrqqtx}|zz|~|wrkeda__`aabbceghjprv{}|{{}~}|}}yyzy{}~}{zyyvvvsrpmida_^]]\]][]_`bdfimt|}}}{{{{zxusrrsruutuvusrstvxyy|{xyxwwwustuwy||yyvrpnnmljilptx}yyvtstttsqpquwx||{|{{zyyyzzyxyvssqqqrstvy|~vzz|zwrsjce`aefilox~|otuhjsy~zupr~xwrqx}xrtrr~ynrrecluyvojfeiqyymt}{{ty{nhhiiijklmwxv|~tpt|tt|zrxvfhv|ug\YWWZZ[_gp{~vtx~~|qpu~yuu{xvzyuqr|smv~{}vnkmw|~ztmgc__bgnwwpr{sonpsuusqpnnsy~tt|wqopxwtx}qf^]]\`aa```bhox|tty|xwux~xtsw~xutsvyz{{{{}xpic_\YWWZ]`hpx|uqrx}z}~~~zwwx|~zxzyyyyxz}}zxvuuwz~|smjijkmonpqqrqoljjihijkqw}{wwvvvwyz~}{{|~wronpquz{~{ywuqqqpooonkhfdedcdcadgkou}}yxy|}{wuttwyy|{wtrqqqoppprtvwxwwxz||~~||}~{uspnnoooprvz}xtstrssstutrpooortsuvuvwxzzywvwwvwwuuvwxz~{vrppqrwxxyywtrpnmmnqwz}}{yxwuqnmmlnppqtvx|~xsolkigfdbaa`__^^^abcghiloty|}}zwspmnpprtuwyz{|}||||yzzvtrqpnkgda_]`aadefgjosw{}~}|zxutvvwz{{{|{zzxvuusrtsrqqrssrrrssw|~}zwtrpppqqsutxzz{|~~|yzzywvussrqqrrquwwupjb^`kfni`]RMV`g{yzvy{zimrzrpz}}}}n|yzsghv{tc|v}ympmnz~|biy}sqywy~zw[P_tz||zx|~onktvu|tyyze_swgflz{mfehq~z~}~xz|w|}~ttv]F\is|kaa\feips}xtwolow`[WWg~~{q^jrtt{uuuvy}xkkwvdTOXZejpxwuqtz~ha^XQ\gu{y~swfct{xx}ysp~}x{|thmtuw\WSPVlw|vzo__XIWkuu~pfozmlw{w{|zrgmlotw|~umljfuvkqsnmuruv{tuwtox|~xqsnjcfzqiicWV\_jwommhhvzmirogpz}{~{}|xyztrp}~ywvyy~yvy~zlkwn\Z_des{}zkcef`cklnqvz{{~tw{yw{~~~ypw{poqpxwlY_mdYZ[YWbhms|~pl|ykjkkdhjiiot}{wzytpm~{vqmj`cb`]dfqv{~pfdc\_deflpswomqvrqemullmkm{|xuuvuw~upnhlqtuy{zytqttsy|wwoogj~sevz}nq~pea_ZY[[X^dky|wsqnkoonpzz}|}yp}~{oijjr{{xogc_]_`^dmqzsstxssf]kre_][X[^ck}||vmyz~~xlf_YTPHIRXmtwqmgfd`\feokihmiieYYjldgryutsuw|{{vusstsswtskjgilrs~zyurqik}~}wypnx}~{|vr}}mca]XSQJX]pz~ttsrpqjsw}tnq}umoprv|}zztdca`]_Ycgmuquvxuwkedrn^\_cgnps}{|{xytwojmrif`XUQSch{~}pnkfaaZedlmlmnllj__i|~yxuorsrvxzsttqopjuu~vrt}qt||ztrmqgdj~~~z}vrkmwsc\^\Y^bhxz~rrqmjlfrpwxxyxwz{sytu|yiafhhnuy~xjikfac\egjomolkkl\X`llgkw{x{xw|}~zzx}sjs}~y}qvzrdY]^]aggtr{}vrrlfhafgeidfbdirin{s|zsginlnz~zv{ysvvx~}|z~ofy|~w{{xutpptrcr|v||}~uanrhg_cnpovsx{y|ypvvqstrzuywz{jtzlja_jpvzv{uqeiiddjhrnonnnkjpi`z~yx|xtzw{|xzwzustucrz~|hw{osnnxvpsprxv}~ymrqihjgojlmsy}wxz}|{eryklges|~}}{nruqrxw~zyy{|{||}}~wwsrlhkl]qz~zuzyw|{}|{ytquk^txrzvv~zuyy~~z}{vlrpjmqryv|{zn\lk_b_dw}|uncjjegjhmhhilnouxx~~tw}xw||}|ywroupcx}sxss}|}}{on}~stuljpqwvw~tnrlgklquu|r{~{wr|~{{y{ieupknlz||zqqxsptu{|xz~y{uy{}vspplhkqccxzy|vv}qqvpqwz|wy{|ws{zo}yrxsorsvyw|}|}uksqlnt{|{v{q`frnpnuy~umfmfadgknjow|{pvupsvxzsprtsnmsjaixvuozyw~yx|~}~|rxqjhilqosz~~}qvskmr{zv}rtxnmptyzwy||ytzwlgt|{yyuinlhjlpttzy{xvvtsojmoniahg`[ftur{}sjrpkklorontwzyuxxnnptwvsx|w|}okqlnvx~zvx{yx}~|srxqmk|{ouxu{}|kgmebbejos~zwpyvqonqropvvsklsld^nz{z}xw}urtx|zvzzlojefillhgqw~}tw|{v{}miqlmosutqu{{ysz}}~s{zxz}wwkmohimv{uzpkilnnjiljjcgjd_az~r{zmkngijopqpyuksommnomjlv{}~|vzr{}|rowssqtwxuuzy{w}{~tmutqu}wvjhkfkqw~vlpighlrrliopqjjqmjr~vzoksnoou{zqinkfdbfgghsx{y{{sx}qlgojgglturpy||ttwt{yq~}pkfppywkllgjkpmhbhifebnpq{}wokekjgkp}}~{uqosjihostruzx}{xzw~wuooojoryzyu|uvyojgrs{~}trnw~{wnihmjnmpsrnnqptr}~zsrmorqzv{wnjcd\^`gopmluy~|vsyx~}zz}wy~|~nq{{z}ze\bry^Xlvdanutikktus}nlx{h]l{y{ykinqtcZ[ntkljsw{}yi[\fq~~xz{la\o~rr~}ps}z|xyjeloktxscSbvwf`m~}tqyz~hbbwxzqpz~qojf_TSS`kvy||xwxl\[Ydkx|txxhefrzzyot|~|zyy{|tb[RYcp{xnhqrwwxypp}ssv~vztmmsjhovqhh^YSYfnrwuuxvsqvlacofc_fpv}{{sriaaokedfppurvxvv~~{z|vi]^ZY_hzx{xrp|xxz|yuzuleu{{}}tw|ng^]QKKPWX[bs}~}rkftrifku}}{qytnmtrwutqnns||ytrz~zound\XVQX]n}|wlxz}yuzriuuma[XUXTYZ]`o~{w}y~~sp{wsrxmusmfkonnipsspw{xxv}~zyzy|v|~wfbpkaWZY^fu||y{|}qppk``meWMROMJJSV]hx~t{ys|upuojjnurnmx{{utrwuzztlqtswswpg^dlbXT[\ipy}z~yvorqndeka\XZSSTYdmsvw{}{suv~vwljwsnfeafklqruw|{{}z|wtpsxy{qpomcfmea`msz~~y~}}yvpqilngYVb\ULMKMR[kpsu||~{}st{pefkjmjsy|yvuxsuomrqnpxtqjmqh_aneehrtvzx~|v{|uyvttrtg\aof\VW]]jqyz{xpusv|~vwojnwm`abheinx}{sssxyzwysrvsujfozwz{|~vy||{~~|vpqqqrid\Zaf\SSR]ckmuyvtx~z}}~ztvwrmmhkjqw~~~ytpkhlkuxvllqstllffr|wvuvpv{|z~~xsnv~}w~zty{wrinieimgbdjtrwwy|}|}~tsqyyww|}~{|xyuwmbhsnfbajlw}qsqpuyzts||ywuxqzzw{}z|ur~uvz~wsswutrtmikji\Zdibfghlijirzyru{w~}~y~xmpz}njimu~~okgjionoosssuqvsw{y|vqmvsoot~|{}tuspspry|w{{wzw{zuvx|tqmgjyyvsvnos{}xvyzrqz|yootx|{|}|{zysx}{wlpngenmgflyyxsywvqs~|w}~}{usz~{{{{yyxmlzss{|~rsnnouxrjmvsqiklfgs~vpffdilhlnru|}xw|xt{{w~~~x}~}~||{|qgq{wzy~~}wuzupqjejntpmnvx{r}ojmmroutlhlnnuqslihs|trs||{wy|zyuyy}xxxv}~y|qrwsoksyyw}|vxuyzzrw|zxtuotv}||qmnmruuqqpvtpnie`r}ztwwsv{|vtuzwwxzz|v}zyywtsrpx{~wqvwrjtzws}uourqijrsnkopsr}{uszvqmmcbejpoplmosrv~vm{y}~{zvvu|}tvwxzvsz|zw{ytxknqmkhpru|yw}zupqmkcdgeejqljhorlcduz}tsv}}xty~{{~qqnsstyumlsqwz{{rprwrvx~~qmmhnopmjmqunomoswtuvnsxyuv~|uyz|y{pmmnquvttpxxvtoln~v}~wnkipopoppr~wouujciihiihefelrxxrvzyx{sxxzvry~}y{{}|{{ux}}sz{umonruwr{ytjda``akmiagnpohmmjs}t{||zx|vuutqrjmpnmtzwus||vs~yx~tmknngfjtvqlqtu~}yw}{xxoqpz~~|ynlnioswuprw{vvuxw{wzsoprrlpsy{~{spusokfja^`dgknoqnx{}{u~~~}{wsq{|~~~|}vqy|~}~yx~xxvy}zv{|neb]abgdbhmlkllrpw~|||}~xxvojfigmuvqluzzxyxyrpu|uoedlmokquuv{sr}umnqvtzxw~}~volosnllx|yru{zttysmrsuxunrxyqh\Z]Y^dkkfhrxwyy~{xuw|yvx}~||wvysqryz~}}wyjipmegcbZ^filhikpw{y~}okeffkmjkptuwu{|~yyvssuqqmjggmmy~~}vllz|spmidijpz{ztu~wlhmprnquvw}~||vz{wz}~~~zsmhlg`Z``]Y[gjiemrw}|wzuzw}||ylmjjmtyvqv|~~yrskhirrhcega`_finty}xy{~xz~tpwvusplchkpnjkqu}}}~{}yrnqtxtsoprz|}{xwsvvrpwsmigegnnontw}}}|||zxxz~xvoprs}zz}x{y}~|||zwxw{|~xrkgdckmmc__cdaceghv}}~~~~|wvvuosrrnktwvrvy|~~xvtrmqokdiqqngiginvzzzxyv||xutx|}{{{|}ywurrwzvqotqokmrrw}~~{z~|usrvsqnux{~~yyw{vssohgljmmojgkpvtrt|~}}|vtx|~zzz|{}z}|z{xy~}~y{|}z|y}}||rnigfiihhekjjeehiqxzz}{vrrosuwtpnrvtvvxx}xtptrslfimomoorqx|}|yyzuusxxzyyvxxx}~|~zyuqvvtnswxvsttxz~~|}~wryxytquw{}zyzxtrgfggfknkiiqqtrv|}~zvv{||y||{||vuxuxz}zxy}}|xoomhkhifeglnkklppz}}~yupoorssrnsuxuwy||{wxwvsrmlpnonqru{|}vtxvwssqoppsuyyyx~~yxxwxqprxwxwwz{~|~{|}~~xz||yzwyz{|~zurmhicfikgfknqprsy{y{}~}y|{|tqrsqtxvvtz{}}~vvsokomjgekmnjoqrw}~{~~wxtqrqspopvvuuw{|{|wyxyxrpruqqprv{z|{zvwsolillpptqqv{}|}{zxrvvwxxxz~~~{zxzy{|{xx}|}{z~||wplkjilhffklmnmqsy~~|~~|vrsnqsuqqsuxxyz}wxusvtpjlkomkmpqs||~~~~|{}}}xxyzw|}~||{vvxttnptvsrvuy}|zy{{yvsyxvrttuz||}}|xrnnjkjmnmjnsrtuz{}xv{yxxw{}|{uwwxwyyuvy|z|{}~{wstpomnigjjllllqrx|}~~xuvrttqppsrvstwz~{zxwuxusqpppplpprx}}~~~~||z{ustruwyvwwz}~~}|}{xttrvustwwx}}y{{zxvvx{wxyxw|}~zvtqmknkmihmppnrsvzz|}zz{z~yyuuttwwtrxxxxz{~|wvstqrsnjknlmkmpswz~}~||}z{wutuppqrtuttyzz{yvwvuqssuusqsvv}~~~}~{}xsqsoqsrssvv{zz}~}{}|yyvwwyuwxy~z{xyy{xxvuxyyx|{~~}{zusrnponkkikonpruu{~~|{{}~|yvsstsuqruyxx{{~{zusrrrqrloqqopqqvw}}|y{zz}~{y~|zxyvwxtprusvuwz}zzyxuxurrtuvxuy{{~~{|{{xysqspopropqtxzz{~}zz{zwyx{|{y|~~zwyuvxwwvww|zz{~||{trppmponnlmoqortv{~~~|~{{}~yyvtvusrspuwwy{}~~|ywstsqqpoptrtuuux|}~~||ywvxvyyxz}~|yxwwvxttwwvwxx|}~xyxvxxvrvvvwxx{~|~|xwtrprnprpnorrvvy||z}zxwyz|~{zyywuvwtuuxz|z|~}xstpoopnnnnrrqquux~}{}|}||{~~~{xwtvvtusrswuwyz~{{wvvvstroqrtsvvx|~~~||yyvuvvuuwswz{}z{yxwvuuywyyyz}|zyyvxwuuxwwywy{~}zytrppopqmopqqtsuz{}}~~zy{y{{||~|xyxuxwusutwxxz}~{ywvrsspnmopspruuv{~~~}{{}zzzz}~~|~}{wvvvturtvvuwyx~}|wxwvvurrusssuvz~~~}ywuttusutsrvwvzz}~}{y{vuvxwyzy}z{zywxvswwxxyx{~~}zvstqppomoporrstxy~~|~|{z|z|}{}~|yxwuwwsttuuxvx{|}xyvuuuqpqopqqquvw}~|||y{{zxyxy||}}yxyvusstwvuxyz}{jfThqtqkqx}ton^nkkmophac~tswqg|nhop|tnbVN]RVfgqo}srptdZty}wr|x}}ysq_ckmerwq~~~yoehv~q{pt^[RW]Zcfecyzuema\Raew|k]lcilp{n}wy{}u}ueqfssx{w}kfhfx`UYww[MJJFSkeut|~~~z~ndbb_ihrdkjmk^a_ljqy}s}{|ztgQQVk~mztoptuu}|{ishoe_]elksvlZcZOTL]S`ackopusy{{cXYWmrohtag`djbk`fejbmfftnz}xrrlqtxhopqvikp|xz|{kqzlyoxtqxr~{y~t{vvvlbegq|xtnstk^j_i_XYcabhtvqvsskxnwlqrvtw{w~~}xw|}|}t|t}orhfmfs}}jqaljljennuoyp~{kviqlacnnmmpnbtkiusqo}yyqs|wxxzz~~kmeg_how{}qszqjlhijl}ztmljoqjro|svz}y~smynflojbnuzv[ecVbWOfn|z~t}}q|wzxqajijukn}}~r}}nuu}w{}zuw{~zpy{wm~tvjrkp]eYbVZ\b|~~{{v~zrikUhut~jvptojt~|xwwwhitgndkgav{~{t}qbca^fRXc^hozyz~{hmhlzzy|xrwx~n{vwmuxxwovwtxw|xztr~{yffvj|luywlprlwjpv|vx}|{~vjjphiggaXofv}z|}{zhpysp{}u}yrqwuw~u}}xtzvx~vmmehieeqd{zt|vzuv}~~tuys{qmibmfr}ru}ly~~|~ykxf`hqqynymo}vx|wvttzmvbpz{{y{xzyrrnyw{~zxuqwwwpxtvwzswgwlw}zkmqxwor{}zs}kbhn_YPXPQfq|{rztpr_jqniihojtwy{ov~|prs|x}s~~ss{xqegek[lc\_bw~vjhns|zwg`ddtux{w~slr{eQSY_swzxwvfWZ[]_Va[d_epto|p||xmpzvqv~uwqqlptzz{hx|xlo~z~t{|~z{txpopoxmcmqwluuw|y{mly{}|mW\bcglu{~xz|widmrqmuvwyrtq{~y~~}|~ot|mxy|n]aX]_r}~y|z~{}urj^hjlx~sbmtnqluy}iuxsvvsj[nhhnuw|~tu{tpfdhe}|sqx}yy{~~r~}}{pl~|tz{vxwyzsjtvzqnvv}z|{~utdZPVWMT]c\_qo|{pkzqyysffnkndgs{{yz~}t}xy}w~vmy|{qkv{yxulqyqwufhY`aburu~~}lvmvrkwupkiwx~{x~snggomuvsoet~wypw}~zs}|upg]f_^RWV`Wbdsu~uutzyotwyywy}{puplts}|ms}}z~tsxtgnet}}zojsyuvwxwtdpy~|~zgu}t}wuxqxvu~yznwpn]pvrxrtvhl||vm\q}{}|w}ytq}mgmp`g[lpmslqqo{rswtl_]chv{tkio~t}zrdio|lbbggtw~uy}~xqpkxvsdcemuz{zwu|x|okmzz~~yv}~|{vms}{yvjmq|{}nx~wqrj[YOBTkll[bcchjv{~uyojpupo^fmolq}v~w|ywzxxvw~wxvsnvoksyzhx|~umokqshZhtxwgtsvsryxw{sq~|uzxo}~wpsghf^armstu|{y~~xtw{u|jna``^TUYjrugjqu|wxxzquolqvv{|zmyy}pt~y||l{wuyusmltsr{|w|qwztlqkxzyzrsssuwx|z|ytrwpkmwwhpqmhnuxry}|vvy{~|rzzs~~|pppxrqsngru}skjkqghrt{ytzqgnouwv}u}ssx{uvs}|wqmjjegfkmz{nuuw{vxy}zyyrijswzchloe`figss|~tvxkx}}~|~qyvzyx{ml}w~q~}~rxyx~|naqcXY^eenibY_njkkk{nwlzwynevwgt{t|stj{sy}zusugxqrkitzwyz|tspj}|qmorqqvefmskbkkryuxz}z}~qqjtomppxtsskn~||~~z~{spjkumqj`ppkhdfign]`ewz}ynyrxztxxwvsq}{yx{x~{v{~}|uzfcjrzzxrqnytwx~}~xkrjnccdhy}{|x{~rqknsogpt|wytv}|toq|{yw~yxxv|pltnozel{y~twsdhmylmocnkmgg{o|ukuhizy}vxxwwvtmqcaqqrrxsv{ugoox|uogkcnun`ccc`ae^m}ywx~}uuxplvzxtruu|vzs{rx{~yz|rwxz|lqjai[TV`mlmphpjrnkw{zwvl|ttrvkt~~~yo}|~vxsmphfqwyuv~{|poz|}uryrpifcnuwrlfgghmi|~~~zlo~}zxlsprnnzywzzy~qtxfdfrrzcio_iflvd_qmkrvw}|~~y||ww|svwt{z{~xuy}{|}yv~xpq}ioqxwtynt}|ym{vz}n{}pcepmjgulino|ws~}y~qlll|xdxprtwtu~~z{zr{~onsnltpqrttu{~joomqtpmprqbgpypi}{v}qn~}s~}~}xz|ww~rqzyrcrxnijvwsquwy}{|x}{dnreY]a`Y\gpjfgmwspy~upusvo~{||w|zwu|rs~ox~x{}}}}n{yrljpl^h^dj_jyxlcmmlo~|~|zx{rx~zsqlombuwopotn{~yyzr|{xkpupgj~x_bc^acntysmr{|vy~}uxxw}xonypn}x}v{|ttnt}xoijm]nwtmefdfmnxzu~}n{zzwy{y~}|vryzqwzutxvvsv}}rvskxw{yomyxxso}uxw}|ohqswxxqlm`gpvkbkmfoy|n~zyzuqgsvz|srwwv{|{wxyurfplokvyxsoyv}wr}{|otz}ytonphiidehqtwz~~|zv}v~~~u}~snvsjdcktv~~z}ogWc^\dkflhce`imijpx{|up{{}y}twr|xxuytv|||wwvpvonkvnhhjefkkwynzuvu{x}jqmhoovx}}~|r}{x|~x{qvpu{y}pnnWb_eoq~~yv~o{zlrpzx}}yuvwwx|w|xtv|vz|{r}jonlskmrsj|v{w|wuu{{vmquuywz~wzsuv||t|{|{z{y|~y|vyzwltxzstrmmtxwrurusmjigehmvz{}~uxzrvr|||sunrotw|}|vwt~~{{{x}yrsuxoyvzmiodkstt{v~||}}z~~{{rzuowty|||}}teoggiimhgljhqfcorut{|w{~}zx}|~yyxz}||svw|t|zzzuqmqllisouytys~x|~z~~}ywwv}zttwyw{|}z~wvzw}{ytnjp`jqpvottvuyurumvvyx}{{{}|}~z|}y{vyzz}nvukkllss|yvzux}{zvwyxt|v}}tunnprx{}~~wwvx~{|z|ysx|syv|uz||v|wvpywotlslzmnnhmduttrwrwzy~{zqz{}}wq|opsnsxv{xz|rvy|~{ysswwylinlonotlnqkjtsr~|~~wutrtx}tyvxy~||~s}q|xqypmkrdofhedeell}pwv|y|z{wxyz}||x{y}x}us|w{{}wyxtlsqlzrnmltoq{yw|vsvutpvw||}~}|~yzzx~}}w~trsrxzzzx~|vunomoguktrrsuxosinjltvx~ss{ru}~u{zz~~}~y}~y~yzx||rrqqtuu|vx|yt}}{zynxnnvuv{wwtornhnppu{w|q|tv|vx~|~z|zy|zzyy{|xwv{qxswv~v}tt{npglfrppys{qpqofvu{{}}~z{t{sxssvyzw|x{zz}{xxz}}qzoglkutqrpirmjoqwx}~{|~y}{{z~}v||~xxrsmy{z}utrqqjbdbksqwtx{xvzz|~|~}{|}y}||}~u}x}tqtzuw~tovpmlq|v~tszrmqzyxwx~~|xy~xtxy}}||uznfjqq~pzs{tttjoswo|}wyzutys~z}~}~}~}yx}tw}w|~u}pss~~}~|ptvvtuvwwsulosq}xpooxz||{{z{osz~|xvupt}||yw{trnuntknyr}uqoou{wv{s|}}|y}{}|z~|xzqrv|~vtxzzvuovt{tolikvjgqrzw{xwvx|z|xvyxx{|xvvuszvsyvxskinwkjrnswznu||~yszws|~qyst{~mrw|}tzvwwyvvzwqqtu{w}npokrpqny|{uy|ssnrs||zz}|{{x}}xwz~}||wqlxvksswwvuntuys}wonrokss~zv|spmtu~~~x}vpw|zs{nvw}|{v|uy{{u{yu|uyuyyqzpikfkjttvx|wiukdhsz}}}||}w{zqxyxtz|wy}r{~u{}uuquvituxw}uwxjmfklkr|wx~nxooqvx~xytzz~}|yxs}t{{~{x~|z{russpz|v~wrjff``lqpvw{vyppoptr~{|~{vt|y}}~zwy}sy}~v}ytpoloomrwzwvquidjivvy}xxrqvnzz}}|zuzzy}}yzx}}v{y{viqllpyy}xzxjvhfjonqvxxt{vlotou|{}vt|||~||wvwvuw{{xzszrphjpty{}~woplhktw}wzvvvqoz{stw}y|~{|{~{}~wtvwxv~xyukpu}y}}stvkofnmouuuuzklorvtyz}|~~~~}xsytsw~~{sinps|||xztooootqouxtrttwx~~u{vz~~~~}}{|~{~|zyuzyz}srryyzutxuhhrrllrsswvnsvw|~vv~{x}~}ory~|zzsunouyvxzvyrtquxtpqwq{{vuv~~z}}tyxy|~{~|~}zwz}{~~vstuwsw}zvurnsryysqkpxuoty}zv}zwyxz|yz|suu|~zz~xrwywtts{z}|xxsuz{rvrsurvr{}}y~zqurrv}|{~xsx}}}}|uv}~|zzts}xnuyzwuvnmopovtrzzyw{zrty~v{||u}zy{{{{}ztv{sxyx}xssy{yx~vioomp{~||ys|wpu{ssyw{{~~x{~~}wrrqututuyyurxppijnu{ywy{srw{~{~}t~yy|~}ywvz|w}uotuqqx~{|{tutnkpuww|~zy|wtspty|~}vy~x~x|~z}||z|y~y~zy|yywqtmpqt{y{~|ounjjnyy{{v}xpxvyu|}~}~~z{~y~|{sw{rt|~}x}xsuotyuw|{tzrkmosxwz~wvuotutz{|{~~{}{}}~}~}}~y}yzyvw{}}~xqrnorzy}{{wsrolmsmtyzyxyvq{vy||}~|{zz{{}|xuyuwz|{zztopqvy|}{yootmpuxxxtvytwtzww}{z~}{}{}z}~tyuxy|}p{rlpuwuy{}t{ymrqopvw|x|sy{y|~~xz~y~~z}~}x|~{}~{|ssv{{}xuqpsu}}|yzwsqzzxtuywxwzyz|w~zxuy~{x~{}}}}}}xrtx~}zywwqtuv{}zzwrrw}txzxw}vtzz~xv{yw}yz{}otw}|syxx|~{}~|~w{{y~|turszzxv{z~xzvvz~~~zz}}{xz~}xt|}{w{{{uvxxz~~xpv{xy|~}~|{wz}yv}~{{wy}~z{}xxzw~z}z~|}}yuwwtw{}{}~wwz~{~|~~{xxyy}uu~|xw}x}zswvv{xwwy}}}yy|y|~||~}~z}zx{}z{y}{~uvtsw|}v|}xv||{~zy}xz{~~xswyw{}~|y|ssuzzz|yz{|}}z}|}z|~~uxxv{}zs{xoqv}}|~wzzx{}{z}}}zwy~|{~~}y{ssuz}}zyzrstz{~|vy~||~{~~|{~}x{}{||z{yvrsx{{z{wtpsux}~}xzv~}}~}~~~~|}{~~~}}~{z}wwxz~y|vpst{x|xryuzxy~||||yz~|z|z|y}|z}}}~~}z}{~~|wzuw|}vyrstw{|~|~wrwyux|z}vz{vy{yz~y}~}~}{||}{|~xxtvy||wywusyyz~~|~wt{vvxy{x{x{~}z{wzxxzz||~{x|z{{|{uww{|~xw{vxx~}yz{vwtx}zvywy{w}{|~y}{x}|{{~}x{~~|}z{~vxtwx}~wzyvvyz~y~wyzx~~||vx{ux{~x|wy{}}}~{~~|zy|~|yx{}~~{vv~{||x|y}{z}{|y}ywuxz{yz||uvz|}wz}~}x{{{~~~yw||{~}zzx{{|ywzyx||y~|uz|~}|~~xz}x||}~~|}zzwx||}~}~zzxvw}zxz{}}}~~}{~~~|{|{w{}}~||zywv|{{yy~~}~{||~}~{}~x}|z||~|}~|xv{}~|~{}~~}{}~{~}}vzzz{xwxy|~y~||~~{}~|{yx{v|}xxy~{~~}{~w{}|~~yw{|~~{~y|~}~yy~~}~|}||}{~w~|uxy{||zz{}~}}}|~~~~}~}~z|~zzx|~|~ywz~~~}uy~|~|}||~~~{{|}z{~~~}}}~}{vyz|~~ywxx|~~}{yw}{}{|~{{|~~y~~}~~||}~{}}~~z|{}|}~{wwz}~{v|xy}||}}w|~{zyyy|{~}x}vxz~~z||}~~}~y}x{|zvxyz~~||u|{zz~~}z}}xy{x{}w{y~~{}{|yz{{xy{~}~|||yy}y}{xwy}~zw{y{z|z{|~{}{u{yvx{|~{xx}|}z~y~|}}yy|}~~~{~x{|}zyv}~~||zz{}~}z|xw{x{y}||uvy}~~{}~~~|~~z|vy{~~~z{|z|}}~|}{}xzwz{~~|y{wx{~}v~wzz~{~~}{~}}{~|z{|}xy~|~}}|~|zu{|z}|}}{wux}~|~|z|}z}~zz~~||~y}{}~~x}{{~zy~|wv{zzz}~}~}}|z~}zz|~{zz{~{yy}~x~}~|~~~||~||z{|{{z{|~||~~~~}{}~||z||}|}~||~|~}~y~}}{}z}~z~~}}~}~~}~~z}z{|~~|}}|}~}~z}~~{~|}|~{x}z~{~}|yz}|}~~}~}~}{|~}~~~z|~~|}~{z||{~xz~~~~}~{x}~~|{y|~}}~~~~y{}}y}|}z{~}|}||}|}z|~|~~||~x||~z}}~~~}}}~{{{|z}||}~~z}}|{z|}}~~~x}zx{{~|}{{}~{~|~~{z}}{z}~~y~|~{{{{|~}{||}{|{}x{|~{ywz{z}~}{}}|~z{||||||}|}}|}~x|~xz|~~{yz|}}{~{{|}~}z}~z~}|}{}}|}|{x~}}}~~~{x~z{|}|}|ywy|~~~z|}~~|~~z~|}}~yz~{~}~}}}~|||{}~}~{~y{||xxx|}x~||~~~|}|}~}}}}~y|~~~~|x|{}}yzy~~|{{|~}}{{~~}{~{{}~~|~{|}z}zy{|~}{|~{}~~{~}~}}~|||~|~{}{}y~~}~~}~}}~|~}|}|{~~{}}}~~~~~}~~~~}~~~}~~}}|~}|~{}~}|}~~{~|}~}}{~|}~}|~{~~~~}~|z~y~~~~}}}|}|{~~}~|~~|~~}y}{|}{}~~z}}}y}~~~}{|{|}~}|~{{{}|}|||}~}~~{~~~~|}yy|~~z||||}~}~~~{}}~|~|}~|~}}z}}}~{}}{{}}{y{|}}|~}~~~~||{||{|}{|{}~|}}{|~{~~}}zzy}~}{~{xz{~y~~~{~}}}|~}}~|}~~}{z~}{~~~}|~~~{}|{}}y{||{~|z||~z|~}}|}~~{~~{~|||~~~}~{y~}~}|y~~{}}}}zzy}}~~}~}}}~||}z}~{~}{~}}}zz}}}|~}|~~~~|{{~|y{y{~y~||}||~}|~}}}}}~~}~|z}}z}~}~z|}~}|}z~~~~y|~}~~}~|~{{|~}}~~|~}{}|}~}~}~|~~~|~~~{|~~}}~}z~~}{~~}{~}}~|~~~}~}{}~}~}}~}~~~~~~|}~}~~|~}}~|{~~~{~~~~~z~~~~~}|{}~~~~|~}~~}}~z}~}~~}~}~|~~{}~}~~~|}}~|~|~}|~|~}}}~~~|~}~}{|~}|}~~}{~~~~{~}}}~~}~|}|}~~|}~}~|}{{}|}|}~~~||~~|}~~||}|}}|~|}~}||~|}~|}{|~{{|{|~~{}~~}}~}{~z~~~}~||~~}||{}|~~~|~~~|~}~~~{{~|{~{|}z|}}~}|}~|{~||}|}~}}}|{~~}~}}{~}|}z||}~|~|~~~}|{~{~~}~|}}~}|{{{~~~}}}||~}~|~}z||{~{~~}~~}||}z}}~}~}~|{}}~|~~~z~||~}{|}~z||}~}{~}{|~~~||~~|~|}~~}~}}|~}|{}|~~~~~}~{|~}|}~{~}~~z}~~}~|}|}~}}~}~}}~~}~}~~}|~~z~~z~}}}}|~~~}~}}~}~~}{~~~~}~~{~|}~}{}{}~}~~}~}|~~~~|~}}~~~~~}|~}~~~{|}}~~~~~~}|}}~~|}}|}~~~}}~}}{~~}}~}}|}}}~}}~}|~~|}~|~}}}}}}||}}}|~~~~}}|{~~~~}|}~}{}||~~|~}}~~~|}~~|}~|~}|~~{|~~}~}~|}{}}~~~~}~~|~~~}{}}~~~~{~~~|||~~}~}}~~~|||}~~}}}}~}}|{|~~~}}}}|}~~~}~}}}|}}~}}~~~~~~~}~{~~~~~~~~~|{}}~}~~~~~|~~}~~|~}{}~~~}}{~~}}~}}|~~~|}}}~}~~~}~}}}|}~~~}~}~}~~~}}~}|~}~~|}~~}~~}~|~~~}~}}}~}~|~{~|~}{~}~~|~}~}}}}~}~}~}{~}~~~{~|~~~~|~~~}}}~|~~~}}~}}}~~}~|~}}~~}~~}}}}}~}}}}~~~~}}{}|~~~|}}~|}~}~}}~~|}~|~~}~~~~{}}|~~~}~~}~}|~~|~}}|~||~||}{~~|}}}~|~~~~}}}}~~}}~~|}~}}~~~|~~{~}~}~}}~~|}}~~}|~|~~~~|~|}|}}~~}~}~|{~}~~}~}|~}~|~~||~}~~~~}}{~}|~~}}~~~|}}}~~}~~~}~}~~|~~~~~~{{~|}|~~}~}~~}~}~}|~~~~|}~~}~{~~~~~}}~}}}~~~|}~}~~}~~}}~~}}}~~~}~}}}~}~}{}~~~~|~~}~|~~~~|~~~}~~~~}~~~{~~|~~|~~|~~}~}}~~~~}}~~~~}{~~~~~|~}~}}|~~~~}~~}|}~~}~~}~}~~~~~}}~~~~~~~~}~~~}~~}~|}}~~|~~~~}~~}~|~~||}~~~}}|~~}~~~}~}}}}~~~|~~}~}}}}~~}~~~|}~~~~}~~}~~~}~|~~}~~}~|}}~~~|~}}~~~~}~~}~~}}}~~~}||~}~}~~}~~~}~~~}~~}~~|~~|~}~~~~{}~}~~~}~}}~~}||}}}}~}~~}~~~|}~}~~~~~~|~~~|}~}~~~~~}~~~~~}~}}~~~~}~~}~}}~~~||~~~~~}{~~~~~|~~}~{~~~~~|~~~~~}~~~~}~~~{~~|~~~{~{~~~}~~~~~}~~~}|~~~}{~~}|~~~}~||~~~~}~}|~}~~~~~}}~~~}~~}}~~~~~}~}}~~~}~}~~~~|~~}~~~}}}~~~~|}~~~~~}~|~~}~~~~~|}~~~~~}~~~~}}~|~~}~~~~|}~~}~}}~~~}}~~}~|}~~~~~|~~}}~}}~}~~~~~~~~}~~~}}~}~}~~}~}}~~~~~|~~~}}~|~~~~~~~|}}~~~}}}}~~~~~|}~~~~~~}}~}~~~|}~~~~~~}~~~||~}~~~~~~~~~~}~~~}~~~}~}}~~~~~}}~~~~~~~}}~~~~}|~~~|~~~}~~~~}~~}}~~~}~|~{|~~}~~~~~}~~}~~~|~~~}|~~}|~~~~}}~~~~~~~~~}~~~~}~~}~}}~~~~}~~~~~|~~~}~}}}~}~~~~~~|~~~}~~~}~~}~~~~~|}~~~~~~~}~~}~~}~~}~~|}~~~}}}~}~~}~~~~~~~|}~~}}~~}~}}~}~~}}~~~~|~~}}~}~~~~~~~}|~~~}~~~}}~}~~~~~~|~~~~}~~}}|~}~~}~}|}~~~}~~~~~}~~}~~~|}~~~~~~~}~~||~}~~~~}~}~~~}}~~~~~~~}~}}}~~~~~~}}~~~}~}}~~~}|~~~|~~~~|~~~~|~~}}~~}~~}|~|~|~~|~~~}~~~}~~~}}~~~}|~}|~~~~~}}~~~~~~}}~~~~~~~}~}~~~~~~~~~}~~~~~}~~~~~}~~}~~}~~}~~~|~~~~~~}~}~~~~~|}~~~~~~~~}~~~~~~~~}~~~~~~~~~~}~~~}~~|~~~~~~}~~~~~~~}~~~}~~~}}~~~~~~~}~~~}~~~}~}~~}|~~}~~~~~}~~~|}~~~~~~~~~~~~}}~~~~~~~~~~~~}}~~~~~~}}}~~~~~~}~~~~~~~}}~~~}|~~}|~~~~}|~~~}~~~~}~}~~}|~~|~|~|~~~~}~~~~~~}}~~}|~~~}|~~~~~|}~~~~~~~}}~~~~~~~~}~}~~~~~~~~~~}~~}~~~}~}~~}|~~}~~~~}~~~~|}~~~~~}~~~~~~~|~~~}~~~~~~}~~~~~}}|~~~~~}~~~~~~}~~|~~~~~~}~~~~~~}}~~}~~}}~~~~}~~}}~}}~~~~}~}~~}|~~}~~~~~~~~~~|}~~~~~}~~~~~~~|~~~~~~~~~~}~~|}~~~~~~~~}}~~~~~~~}~}~~~~~}}~~~~~}}~~}}~~~}|~~~~}~~~}}}~~~}~}~~|~|~~~}~~~}}}~~}}~~}}~~~}}~~~~~~}}~~~~~~~~~}~~~~~~~}}~}~~~~~~~~}~~}~~~~~~~~~}~~~~~~}~~~~~~}}~~~~~~~~~~~~~~}|~~~~~~~~~~~~~~}~~}~~~~~~~~~~~~}~}}~~~~~~~~~~~}}~}~~~~~~~}~~~~}|~~~~~~~~~~~~}}~~~~~~~~~~~~}|~~~~~~~~~~~~}}~~~~~~~~~~}~~~~~~~}}~~~~}}~~~~}}~~}}~}|~~}~~~}}}~}~}~~}}~}|~|~~}~~~}}}~~}}~~}}~~~}}~~~~~~}}~~~~~~~~}~~~~}}~}~~~~~~~~~}~~~~~~~~~~~~~}}~~~~~~~~~~~~}|~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~}~~~~~~~~~~}~}}~~~~~~~~~~~}}~}~~~~~~~~~~}}~~~~~~~~~~~~~}}~~~~~~~~~~~}~~~~~~~~~~~}~~~~~~~~~~}~~~~~}}~~~~}~~~~~~}~~~}}~}}~}}~~~}~~~~~}~~}}~~~}~}~~}~~~}~~~~}}~~}}~}}~~~~}}~~~~~~~}~~~~~}~}~~~~~~~~~}~~}~~~~~~~~~}}~~}~~~~~~~~}|~~~~~~~~~~~~~~}}~~~~~~~~~~~~~~}}~~~~~~~~~~}~~}}~~~~~~~~~~~~}}~~}~~~~~~~~~~~~}}~}}~~~~~~~~~~}}~~~~~~~~~~~}}~~~~~~~~~~~~~}~~~~~~~~~~}~~~~~}~}~~~~~}~~}~~~~}~~~}}~}}~~}}~~~}~~~~~~~~}}~~~}~~}~~}~~~}~~~~}~~~}}~~}~~~~~~}~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m33.bin b/applications/SprPoP/dist/MUS/m33.bin deleted file mode 100644 index 117beed..0000000 --- a/applications/SprPoP/dist/MUS/m33.bin +++ /dev/null @@ -1 +0,0 @@ -}|{~|}|~~}~||~{{}|~~}}~}}||}}|~~|}{|{}|~}~||}~~|{|}~~}~|~}~||~||~|}}|{|}}~~}~~|~|~{{}|~}~~}}|}}}}}~|}|~}~~}}~}~|{~||~~}|~}{}|}{{||}~{~|}~}|}~~~}~~~~}|{|{{{}}}}z~{~}y|y}|x{~z}{~{{|~}|}~}~~~~}~{z~~z{{~|{}yz~{{~|~xyyx|yw|~{y~|z}y}~{{}}~~}~}{yz{zz|{}}y~{w{~|y||}|vywxzvx}|yz~z{zy}}~{|~{|~~~|~yy|}yzz~~y~zy}xx|}zy}|}xvxuyxuz|{y|}yzyz}|}y~~||~}}|{yz}{yz{z{~w{{wz}|z{}|{vxvvzvx{|zy~{x{xz|}{{~|~|}~~~{}~yzz|{zz}}z~zw|yx{|}y}|{ywwtxxwy{}z{~xz{w||}||~~|~|~~z~x~~x{wzy~}{|zvy}z|v{u}}xvst|w{|}yxxz}xyyy~{z}~}{}~~{|xz{xy{z{}zyu|||xxwz|}vvrx{x|}}w|~v}{wyy{}|z}z}||}~y{z{w|y}y~ywv}{|u}~u}|}{tur{yy{~{w~yx~wwyx}{||~}~}~}~z||w}|zxw{x~{z{yuz}}{x{x|xwtv}z|~z{x}~xzyz{z}}~{~~~~}}{||y|{{{~zxv|{|vzuz|{{stpwywy|{u{{u|xuwwy~{{~z~}}~{~xz{vy|wxtyv{|y~}zvx~{}xx{xwv|~~{}|{z{{~||}~|||}~{}}|~}|z}{~~~{u{{yzv~xw}{zssptxuzyzuw}vxzvuvvy{xy}~}}~zw|ww{uxwuvwzz}z|yu~z||}{|yyu~|}|~~~z{|}~~|~}z}{~~{y}}|z}z{}{yy~xxx|yw}|stqqxrx}xvvzyzwyyuvxwy|~y|~~~~~}}w{{u|vuywtwzw~|x~w~}w{yv|xx{z}|}zy}z}z~}}xt|xx}}~}|z{{t}~~|{{zztz{y}sqxoxrt~{zuwyz{x}tr{wv|yvz~|}}~~zzy{tzyp{|ttxwzxxxy~|z|}}uzx{}z~|v|}|~~w~x}|{~~{uz|x~}xyx{w{v}{yzwzv{ynsquxsy|xzyyzxyxtvwu|uy~y}z}xz}x{|su|vt{tvz}wx{z~|}|wtwt{~~|{z~x}|}z{}~wv}{|}~{|w}{|{{zxz{{yy|{pqltvw|wyvx}zwt|xsxstx~w~}}}~~{|~~|w~tyvzwwxy~w|}~~yy|~vvwy|~x}{y|yzw|}}z}{}}z|~~|~~|z~{y}}}|zz|xz{}zutuqyx{{zr~|uztursvqrz}{z|w~}{}}~|}||xwvozyxytts{}y}y}~{|z}}z}||vw{z{yzvx}{{~vz~}~z||~uy}}~x|rz{zxzz{|{~|{zqzvw}~|wz~{~|trqu{s|}w}}~~}z~~z{~~vyxxzxwxzy{vy{|~|wuw~~~y}}zut}}|{~}|~zvx~~}{|{yy}}|}{{z{|{w|}yvqrt{xy{~}yzxu{zxtoqxuy~xz~||}||~{zwyyx}|wyvzzzwx}}z~~|yuw||~{z~}wuz|{{~z~|xw{~~~|}|xz}~}~|z}|}}|~~~~{{{wtpuz{w}}}~yzxx|ywpruuwzzv}}{~~}{}}~y|z}yz|w~}wwzw{}uy{|~{}zws{}|~|{}{t{yz~~~||{||yyxy}||}vx|}}~}~yz~zz}z|}xtrpzzwx{}~x{yx|zuqxquxvwy~z~z}zy}z~}|x~y|xvxzvyu}z|{}|xtv|{~|||wy}v}~|}z}}~~|xzv|}zwtz|{~~yy|}||~}{}}~uspw}xy{|z}y{|xrwuqyuvx||{|{{~~v|{{z{}|zzww{w{}t{|y{|~xvt}~{~{~}yw~wz{}~|~|}}~yzxy~}x}t~{z~{yv|z}}v|yyy~}~yvp}}|zwxxovzywtw~|z~xy|~~~z{}twzx|xyz}zu{{}~yz{ur~{z|zwz|z}||}~|{vv{v}}}yywx{|}}{z{||~~}}}}{~}~~w~{qvtw}{}{zzyvt{y|{v}||~v{~||y~~tyyxzsv|xxutyyw{x|~ux}yz|y|yy~|w}}|z~|}~{xtyx{{tzy|~}}}{|~|{~|x~}zwqu}}}}{}|wvtxxx{}yv||yzwz{z{~}~{}|zyz{{uywuxsx~{zu~|zyzy||y~}xy}{~~}ywsx~z~}u}{{z}~zyx|y}z}}|~|wz|{~|w}zutou|wyyvzx{~v}~}yzyv|zvy~{}z}{{ux~y}|z|~zrx}}|}}}z}{}}}}}vtoy~~~|zz|{}z~yzzx|z|~~|w|{~|}}~{sz|xtvtvyx~xt{||wz}x}{w|z~{}{wx{}~~~ztwz}z{~~~wxzwv}yzxy{{{}}}vyrty||{xwxytw~||vyy|y}{y~|}|~~{}xw~~wwxtxu}|swx{uz{~||z|w~|y}~}}zyy|~{xx{}v{z~y~usv~y{w{~}}}}~~~{trr{|y}}}wyxyzz~}ww}yx|{y}|~}z}uvt}~}xuut{xxvw}yz}x}}z~}z|{}~wvz~~|~}~y}z}}{zy{~x|||vvx}{~|x{{}~~~}xuxuv{}|wywwy~~}|{{xuw~z~~~}||xt|y|wtyyswy|zuy{z{|}~~zy~xxw~~x|}}}z{x{|{zzzz{{~y}tv~}~|}z|}|}}}}~{qtw}|}}z~zw}{}}txv}~|}~{|}zxy{|x{wtuyzvxz}|{y|{z~~{|zz{}w|{{z}~xx|~{~xx~y}z}tuw}|~{||z~~}|{tsu}{|~~~~{|~zwxz~~{u{}w|~~}~}~~}wzy{~{tvv||x{x||y|y|x{~~||y|{xv{~~~}zy{x~|~}x{z}||~{~xxy~}~}|w{}~z~z}}~|ywxxwy}~{~ytwy|~}}}yzxw}{}|}}}}{y{~}zvwx{xx~}vv{{~~wwz|~{}}||wx}}}}zzy{|}{{z{}x|}z~~{xv~~yxz|}|~~~}{}|zrszz}|txx{{}~{||ztx~{}}y~~~}~y}|{yyxu}~uww|}ywzz}|}z}xw}{|y~~~}}}y|}~~{{xx{|||ttz}}x{z~|~}{~~~}}{|rsuzz|{xvz}xv~{|y{uz{}}~{~~~~yz~}}{uv{z{zwyx}~y{zw}{}}z{{|v{{||y{}~yyzwz{}}~yxtyz}y}~z|{{}~|{|}yvttw{}}z{|wxx}}}z|z~}|{wy}}}|z}~xyz}|z{}}xv|~~~~}w{xw{{|z}|ywx|{~}||{}yxyy|{y|ywtz}{{yzz}}{{}zvrw|}~}{{{wzw||x{}vxwy|}~yy}~|}}yyyz}|||z|~~~~}wwwy|{}|~~}}zywv~z{~}}|~||z{|wx{~~wzw~{|{x~}z~~{}}||~yxuz|}z}xu{z~~}y|{}zuxv}{|||yzzyxxy|~}{{}ywv{yv{}z}|{yyz{}~z|~{}||xxzy{||yyz}{z{}~y|}|{~}}xvx|}|{yxy{{|~~~|y{}|ux{|}}}}}vxyzvyw{}~}z~~~{yyzvwx}~}~xz}z}{z|~}~{}~zyz{y{}}yy~~z||z~~zz~~}~{|}}xxzz{{y}yxzz~}}yx{~zx{{~|~zwz|~~|yxx|~zwy~~{|wvxy}{|}{z{{|~}{{~|}~~~}{~}|||~xx~||yy~}|{||}}{}}zz}}{zuu{|}{z{yzx}}{z|~}{{y|~|}~z{y}~~|xy|}xwx~~}xvyyzx{|z{{}}~z}|~}~|{z~~|{xz~|~uw{}|}y|~}{z~}yy{}zstx~}|yxywy~~}|w{}y{{}{~}{z|~z}{|~{z|||~{{wyvw}~~xz~}}zy}~}}~{{|}x{}|{{~xvx{zy{}w|}}{zz~}wuv|}|zwxxyw~}zvw{zz{z}~~~~}z|{~|||~|~|x{{~~~|xtxz~}{}}|~~{vx}}}}{{}}|}~{w||~{yzz}{zy{|}}|~~|zz{yxu{}|wyyyyy}ywxz|xuy|~|}~|{y|}zz~{yz~|{xw{|zxy}~}}|zzy}}|}~|z}|zyx}z~}xxz}zzzz{z}}}}~}~}xuv}|}zz{wwx{{~ywx|}{ux||}}~~wz}yyy}}zy~~z}{||{yxwx{|~}z}|~|zz}~{~|}}}~}y{zy~}yxx|~{zwy|y}}}~~}}yvvx~~}}|zvw{}}~}|yz{{}|xz|}~}zz}}~yy||}{{{}||}~}yxvwwz~~{w|~~zx{}|}~|{~|{z}z}~|xv|yyy{~{z|}}y{}|vuy|~~~yzyz{y{zzz{}yyz}~z}~y{{~}|{|}}|z{~~}~|{wvxwz{}{z{}|{x{~zz}}{}~~}{}}{}~{yx{~z{}z|{z}yx}zxvx}}z||wxz~}}}{z||{~|vz{}|{|{xy|~|{{|~|z}}~|{}zyxwxz{|~|{}|~}}x{}~}||}~|~~~}~}}|~}|xz}~||wz||}~zzyzwx{}~~~|ywz{|}|}{{wz}|yyz~}|{{yz|}zz}~~yy{||}{{zuzzz|}{z}~|{y|~~|}}}|~~~~||{{zz}}yy{{~~|{{}~~zxw{}~}xy|}{w|}}}ywx||xx{~}z|~~{{{|}{|{~|xx~}~~}{zyzzz|||}yz~||{z}~}|~zz}~~~yz}}|xy|z{||}}|}{yyvvy~|~|}{|zvw}|~|xxy~~|yy{~~~{{}y|~|z|}}zxz~~||~}zz{zyx{{{||}zz}~~~~~~{{{}~~{yz~~|xw|}|}yw|~{|~|z~|vvx|~|}}xwxwz}~{{{z}|yz{}}}{~~|}~{{}~|yx|~}{}~~|z{wwx{~{||}|wy~~|}~|}z}{z{||~}~zxx{~zywx}{y}}~{|{vvw{}~~{zxwxx||zzz}~zxx{|z~~z}~{|}{~zxy}||~~|wwwy{}~~}{|{yxz~}|{~~}~|~}|}~}{}}zwv|}wywy}||}{|{uuy~~~}z{xxzz~}zzzz~zuxz}~}|z~}}{{|~|zy|~}{{|~}}~}{xxzyz{|~}}~~|{zwz~~}~~~}~|z||zww|~zyzy~~}~}|zvvz}}|~~{|zy{z|}}~|y{{}{vyxz~}~~}~|xy}|xyz}~}yy}~{zzyzyx{|~y{~}|xx{}}~||}z}}~{yz{}~z{zz~~{~}~~{xxw{}~}~|}{z{|}~||{{{|~|wwy|{|~yy}}zy{z|}xx|}}}yyxxzz~{z{}~zzy{~||~}|~~|||{|~~|yv{~{|{|~{|~{}{vty~}|{{{w|}~{||~}wwz|}yz||}}~}zyy{}{z|~zwyyyz|}{z|~{{{y{}}}|~~{{}~~}||z~~~ywz}||{}}~{|}z{}wvx}|}~~{{ywx~}|z}{xyy{}|{y|~{{}|yy{|}~}{y{~}{xyxy{}~{z|}{zw{}~~~~{{|~{||{}}|~|{z{~}||yz~~{|}|{}zyyz{{}~{xxxy~{zy|~yxwz}|{z}z{}}{zzz|~xwz}~}}yxyy||}|z}~zyy{~~}}~~~}|~{z||~~{xy|{yyy~}|}{}~zvuz|}~~zyy{yz~~}z{{~{vxz~}||~|{{}~~{yx{}ywx|}|yzzyzz}~}|}}{{|{z|}~|}}||yy||}{wvz~}yz{{~~}~~~}|}{vuw{|~|||zzxz~|{|z|}yzzz}~~~~|z{}|xy{}~~|{zxz~~}z{ywx{~{{~}{xx|}~|}|yzyz|z~~xww|}{|{z~|z}~~}xvvy}~~}{yzy||z{|}xxwz~~~{z}~~{{{z{|}|{zy}~~}ywvw{{~~|{|zww{~~|}~}{yz{wz|~zxx{||zy|~|z}|{{wwy}~}zyyy{|{}}~}wvwy~|||{}}~}zyxy|}}}{z|zxxxxy|}~~zyxx|~~~}}}|{zzyx|~}{yy|{{zy}~}|{|~~|~|yxz}~}}zyzy|}}}{}zvwx{}z}|zz}zxxy{}~~}{{~}{yxxvx{}{||zzx{~~~~||zyzz{}{}|z{~}{{z{~}z{~~{zy||}}{zzzz~}}{{~|xwx{~}|}{xz|~{xxyy~~|{z|}}{xxwx{|~|z|~{z{z}~}}}~||z{{yz|}}{|~~|{{{}~~|{}~|{zy{||}{z{zy}}~~|}~|xyz}~~}~}zzz|}}{yxy|~{xy~~}{xyyyz{~|{}~|||{~|{|~~||zzyw{~~}zy|~|{|||}~~~}{|}{{|ywy}~~|{zxw|~~~~~~~{z{|~||~{yz{~~}yz{{~}zyz~~}|zyywx|~||~|}{{}}}{{|}~}}{xyzz~||}yx{|}{y{}~~~|y|~}|~}xwx}~}}zxxwy}~~~|{~~{zz{}~~|~}zy{}~~}{{{|~|||{{{~~~}zxwx{~~~~}|xz}~||{||~}{yz|{y{z|zyz}|{yx|~~|{|~||yyy|}}|yxxxy}~~|{|~{xxy~~~}~}{z{{~~{|zy{|}}{zz~~{xyyz{|}~{zy|~~|z|}~{||zzxw{{~|{z|}yzyz|}~~~~~}~~~|zyy|}~|{yxxw{||}}|xxy|~}~~zxz|}zyxz~~~|zz|}|{zzxxy|~{{{z}}}{|~}}}|yxxwz|}}{y{~~zzzx{~~}{{}~zxy{}~~||ywxy{~}~~}~~}yzz{~~~}yx{~|yxy{~}|{zz|}~}zyvwz}}}~}}{z|}~}|||~|zwxyx{|~|zy{~~zyyy|~~~~{z|~}yxy|}~}{yyzyy}~~zzy{~~|zz|}~zy{z|||}{zz}~zxwwy|~}||z{~~}z{}{zxxyyz}~|zz}|y{z{||~}{z{~~{yy{~~|{zyxw}{yyz~~|{~|{zz}~}{zyz||||{z|}{xyxx|~~|{{z|~|{|~~|zzxy{z}~~{z{~{{{yz}~~~}|z}}~zyy|~}}~~}zxxxz~~zyy{~}|{}~zxy{~zyyz}|||{zz}}}{yyyz}||{z}}}}}}|}~|{{z{{z|~|~|{|}~|zzx{~~}~~||~|zzz|{}|yxzzz|}~~{zy{~}}|{|yxy{~~zyzz|}|}{yx{~~}z{{z}~~~{|{|~~|{{|~{|{{|zz{|~~|z{|}yz{{}}}~}~~}~{yxz||~||{{zwy|~~{zz|~}~}}{}~~{yyz}}zyzz}}|{xwy}~~~||{{|}~|}{|~}}|z{|}}}|{zyy|}}zxz}}z|{y{|}~~~~~~ywx{||}~~{yxwy}~{zz{|}}}}|}|zy{~}zzzz||zyxxy}}}{y{~}}zz|}~||{||~}}zyyyz}}~zxx{}~{{yx{}~}|~zxxy{|}~|xxxx{{zxy|}~~}~|}|z{}|zzzzzyyyxx{~{zyz~~{yx{}}~}}||}~}|zxz{z||}{yyy|zyyx{}~~||~|yxxz|}}{yxyy{zxwy|||~}{|~~~|{|}}{zxxyzyyxx{}{{z{~{zyy{}~}~}}~|{{z{{y{|}{yxz}}yzzy|~~}}|xxy|}|~~||zyzz}|xxxy|}~~|||~~|z{|{xwwyzyyyyy{~|||{|~~}{{yy|~~~~~~~}{|{zzyz||}zyy|}{z{{{~~~}}~zyyz{{~~|{{{{{~zxxx|~}~}~~~{yz|{xxwxyyzzyxy}~||zz}~~{zzy|~~~~}|~~}{yyzy{}}}{yy{}{{||}~}|}~zywy|}~~||}{{~}yxxz|}|~~|||zz|}~}{xxwxzzzyxx{|{z{}~~{z{z|}}}||}}{{yzzy{~~}{xx|}|}|}~}~}}~~zxwz}}~~}||zy}~zyyy{||~}{{}|{||~}~|yxwxzzxyyy{}|||z|~~}{{zy|~~}|}}~~{|zyzzz}|}~zyy|~}~}{|}~}}~~{yyz|||~|zzz|~zywx|~~}|||}~{z{|}}{xxxxyxyzzyy|||{{~~~}{zy{~}}}~}~~{{zy{zz{z|~{{z{|~}|{z}~}}}{yxz{|~~{z{{|zxxz}}|~}|}}~~{z{|}}|yxwwyzzyxwy}}|||~|{{{|}}~~}}}}}{{zz{xxz{}~{zxz|~{|{|~~~~~|yxx{|}~}|||{}|yyyz|{|~}}~~|zz{}~~zwwwyzyxwwx{~}}|{|~~~||{z{|}}}}}~~||{yxxwy{{{yxz|{|}|}~~}}}}yxx{}~}||{|~{zxxz{|~~~}zz|}~}zxxxxyxwxxy|~|zz|~}|zy{}~~}|}}|{zwwxwyz{~zyyz}}}}{|~~~|{|~|yyy|~|{{z|~zxwy{|}|{{|}~||zyxwwxxxyyz~~{zy{}~{zyz}~~~}}}~}zyxwxwx{{}}{zz}~||{{}~}|{|}~~~{zz|}||{{zxxx{}~~}}|{{{}}~|ywwwxxxzzz|~{{zz|~~{zzz|~}}}}~}zyywxxwz{{}|{{|~}|}{|~~}}}|{|~~|{{}~~}||{|}zyxz}~~~}~}zyy{}~~{wwwwyyz{{{|~{{zz}~|{{{~~~}}|||zyywxxwyz{~~}||~}|}{|~~|}~}|{|~}|||~}}||||~~{zy{}~~~~|yxyz|~~}{xwwwzzz{{zz}~|{{{|~}|}|}~|{z{|{yyxxxwwz{}~}{|}|}}|}}}}}}}}|||}~}{z|~~}}|z{~~|{{|~~}~~}zyxyz}~||{xxxy{{zzyy{~||{z{~~~~}~~~}{{{{|{yzxwwwx{||}{z|~|}}{|||}}|}~}|{|~~{zz}}|{yz~}|{{}}}~~~~}{yxy{}~|}{yyyy{zyyyz|}|zz|~~||~}|{{z{|{yywwxxyzz|~|{{}}}{y{||}}}~~|{|~}{{|~~~{zzz|~}{zz}}~~~~~~|zxyz{}~~}~{zzyyyyyzz{}~{{{|~}|{~}|{zz{||yxxxyywyz{}||}~|{zy{||}}}~}||}~~~|||}~~}{z{z{~|zy{}~~~~~~~~~|{yyyz}~~~}{zxxyzzzzz}}|}||}~}}}~}|{{{{|{yyyxxwvyz|~|{|~~z{{yz||~~|}}}}|}}~}{{}~~}{zzz||{{|~}~~~~~~~~}{xxy{~~|yxxyzzzzz{}~}|{{~}~}}~||{{{z{{zzxwwwx{|~}{{}~|z{zz||}}|}~~}{{}~}|{|~~}|z{{{}~}|{{|}}~~~~~~~~}zxxz{}~~~|yyyyzyyzzz|}|{{}~}|~}}|{zz|}{yxwwxwz|}~|{|}~{{{z{|||}}~~|{{|~}|{}~}|{{z{}~|{{|}~~~~~~~}{zyzz{|~}|zzzzzyyyz{}}|{|}~}}~~|{{{||{zyxwxxy{}~~}|}~~|{{{{||~~}}||{|}~}|}~~|{zz{~~|{{|}~~~}}~~~}}|{zzyz|~~}|{zzzzzzzz|~}||}~~}}}~|||||{{zyyxyzz{|}~}}}}|{z{|}~~}|||||~~}}~~~~}|{{{}~~|||}~~~}|}}~~~~~}|{yyy{}~}|zzzzzzzzz|~~~~~~~~~~}}}|{{{zzzz{{{{|}~~}}}~}{{{|}}}}}||}}~~}}}}}~~}}|{{|~~}|}}~~}}}|}}~~~~~}|zyyz|~~}{zz{{{{zyy{~~~~~}|{{{{||{{{{{{{}~}||}~~}|||||}}}}}~~}|||}~~~}|{{}~~}}}}|}}}}}}~~~}|{zzz|~}||{{{{{zyyz|~~}~~~}||||}}}||{{{{|}~|{|}~~}||{||}}~~}|||}~}|{{}~}|||||}}}}~~~~~~}||{{|}~~~}||{{zzyyyz|~~}}}}~}}||}}~}}|{{z{||}~}{{|}~~}|{{|}~~~~~|||}}~~}|{|~~|{{{|}}}}}}~~~~~}}}}||}~~}|{{zzyyyy{}~}}|}~~}}}~~~~}|{{{{||}}}|{|}~}||||}~~~~}||}}~~~}}|}~~|{{||}}~}}}~~~~~~~~}}}}}~~}|{zzzzzzz{|~~}}}}~~~~~~~~}}|||||||}~~}|||}~~}||}~~~~~}||}}~~~}}~~}||||}}}}}}~~~~~~}}}}}~~}|{zzzzzzz{|~~}}}}~~~~}}|||||||}~~~}||}~~}}}~~~~}|}}~~~~}|||}}}}~~~~~}}|}}~}|{zzzzzzzz|~~~}}~~~~~}}}}|||||}~~}|||~~~~~~~}}}}~~~~}|||}}~~~~~~~~}}}}}~~}|{{{{{{zz{{}~~}}~~~~~~~}|||||}}~~}|}}~~~~~~~}}}~~~~~~}}|}}~~~~~~~~~}}}}~~~~}||{{{{{{{{|}~}~~~~~~~}}|||||}}}~~}}}}~~~~~~~~}}~~~~~~}}}}}~~~~~~~~~}}}~~~~}|||{{{{{{{|~~~~~~~~}}}}|||||}}~~}}}~~~~~~~}}~~~~~}}}}}}~~~~~~}}}~~~}}|{{{{|{{{|}~~~~~~~~}}}|||||}}~}}}~~~~~}}~~~~}}}}~~~~~}}}}~~~~}|||||||{{|}~~~~~~~~~}}|||||}}~~}}}~~~~~~}}~~}}}}~~~~~}}}~~~}}}|||||{||}~~~~~~~~~}|||||}}~~~}}~~~~~~~~~~~~~}}}~~~~~}}}~~~~~~}}}||||||}}~~~~~~}}|||||}}~~~~~~~~~~~~~~~~}~~~~~}}}~~~~~}||||||}}}~~~~~}}}||||}}}~~~~~~~~~~~~~~~~~~}}}}~~~~}}|||}}}}}~~~~~~}}}|||}}}~~~~~~~~~~~~~~}}}}~~~~}}}}}}}}}}~~~~~~}}}}}}}}}~~~~~~~~~~~~~~~~}}}}~~~~~~}}}}}}}}}~~~~~~}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}~~~~~~~~}}}}}}~~~~~~~~}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}~~~~~~}}}}~~~~~~~~~}}}}}}}~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m35.bin b/applications/SprPoP/dist/MUS/m35.bin deleted file mode 100644 index cdcd17f..0000000 --- a/applications/SprPoP/dist/MUS/m35.bin +++ /dev/null @@ -1 +0,0 @@ -~}|zxwvtrsqpqqqqqrstuwyz{}}~{ywusrpoooopqqstvxz|}~|zxvtrpnmkjhhggffeeeefffggijlnpsw|ypsu{~~~}}~~|{yxvusrqppoonnnnopqqrtuvxyz{}~~~}||{{{{{{{||}~{upor{~ywvvvwxyz{|}~~~~~}}}}}}}~~}xtrruz{xusqqpppoooooppppoooooooooooooopppqrsttvwxyz|}~{wsppqv|{wusqqppppppppppppppooooooooooopppqqrstuvwxyz{}~}yuplighjou|}yuqmkihgfeeeeeeeddddddddddddddddeeffghhijklnopqstvwxz{|}|xtqooquz}zxvvuutttttuuuuuvvvvvvwwwwxxyyzz{|}}~~|z{}}|{zyyyxxxxxxxxxxxxwwwwxxxxxyyzz{||}~}yvsqpquy}zwtsrpponnnmmmmmmmllllllllllmmmnnnoppqrstuvwxyz{}~}zwtpmigffimrx}{xtqnlkjihggfffffffffffffggggghhiijjklmnopqrstvwxy{|}~}zvsqooqtx~}zxxvuutttsssstttttttuuuuvvwwxxyzz{|}~|zyy|}|{zzyyyxxxxxxxxxxxxxxyyyzz{{||}~~{wtrppqtx}}zwvtsrqpoonnnmmmmmmmmmmmmmmnnnooppqrrstuvwxyz{|}~}{xuroljhghjmqw||yvsqpnmlkjjiiiiiiiiiiiiijjjkkkllmnnopqrstuvwxyz|}~}zxurpooqsw|}|zywvvuutttttttttttuuuuvvwwxxyzz{|}~~|yxwxz}}|{zzyyyxxxxxxxxxxxyyyyzz{{||}~~~{yvsqppqtw{}{ywvtsrqqppoooonnnnnnnoooooppqqrrsttuvwxxyz{|}~}{ywtqoljihiknqvz}zwvtsqponmmlllkkkkkklllllmmnnnoppqrrstuvwxyz{|}~}{xvsqpoopsvz}{zyxwvvuuuuuuuuuuuvvvwwwxxyzz{|}}~~{yxvvwx{~~~}|{{zyxxwvvuutttssssrssstttuvwwxyz{|}}zxvuttvxz~~}||{{{{{zzzzzzzyyyyyyyyyyyyyzzz{{|}~~~}{zxvtrpnmkkklnptx}~}}||{{{{{{zzzz{{{{{{{{||||}}~~}{yxwwxy{~~}}|{{zzzyyyxxxxxwwwwwwwwwwwxxxyyzz{{|}}~~~|{ywvtrqpppqsux{}|{zzyyyyyyyyyyyyyyyzzz{{{|||}}~~|zywwvvwxz|~~|{yxwvvvuuutttttttttuuuuvvvwwxxyyz{{|}}~~|{yxwvuuuvxy|~~}{zyxwwvvuuuuuuuuuuuuuvvvwwwxxyyzz{||}~~~|{ywvussssstuwy{~~|zywvutssrrrqqqqqqqqqqqqrrrrssttuuvvwxxyzz{||}~~~}{zywvuutttuwxz|~}|{{zyyyyyxxxxyyyyyyzzz{{||}}~~~||{{{{|}~}}||{{{{zzzzzzz{{{{{{|||}}~~~~}|{yyxwwwxyz{}}|{zyyxxwwwvvvvvvvvvvwwwwwxxxyyyzz{{||}}~~~}|{zyxvuttsssstuvxy{}~~|{zyxwvvuutttttttttttttuuuuvvvwwxxyyzz{{||}}~~~}|{zyxwvvvvwxyz|}~}}|{{{zzzzzzzzzzz{{{{||||}}~~~}}|{{{{|}~~~}}}}|||||||||}}}}}~~~~~}|{zyyxxxyyz{|~~}|{{zzyyyxxxxxxxxxxxxxyyyyyzzz{{{|||}}~~~~}||{zyxwvvutttuuvwxyz|}}|{{zyyxxwwwwwvvvvvwwwwwwxxxxyyyzz{{{||}}~~~~}||{zyyxxwwwxxyz{|~~~}}||||{{{{{{{||||||}}}}~~~~}}||{{||}}~~~~~~~}}}}}}~~~~~~~~~}|{{zzyyyyzz{|}~~}}||{{zzzzzzzyyzzzzzzzzz{{{{||||}}}~~~~~~}}|{{zyyxwwvvvvvvwwxyz{|}~}}|{{zzzyyyyyxxxxxyyyyyyyzzzz{{{||||}}}~~~~~}}||{zzyyxxxxxyyz{|}~~~}}}}}}}}}}}}}}}}}~~~~~~}}|||||||}~~~~~~~~~~~~~}}||{{zzzzzzz{||}~~~}}||||{{{{{{{{{{{{{{{||||||}}}}~~~~~~~}}||{{zzyyxxwwwwwwwxxyz{|}~~~}}||{{{{zzzzzzzzzzzz{{{{{{|||||}}}}~~~~~~}}}||{{zzyyyyyyyzz{||}~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~}}||{{zzzzzzzzz{{|}~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~}}|||{{zzzzzzzz{{||}~~~~~}}}}}}}}}}}}}}}~~~~~~~~}}}||{{{zzzzzzz{{{|}~~~~~~~}}}}}}}}}}}}~~~~~~~~~~~}}|||{{{zzzzzzz{{||}~~~~~~~}}}}}}}}~~~~~~~~~~~}}}||{{{{zzzzzz{{{|}}~~~~~~~~~~~~~~~~~~~~~~~~~~}}|||{{{{zzzzz{{{||}~~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{zzzzz{{{{||}~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{{{zzzz{{{||}}~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{{{zz{{{{{||}~~~~~~~~~~~~~~~~~~~~~}}}}|||{{{{{{{{{{{||}}~~~~~~~~~~~~~~~~~}}}|||{{{{{{{{{{{{||}}~~~~~~~~~~~~~~}}}}|||{{{{{{{{{{{||}}~~~~~~~}}}}||||{{{{{{{{{{{||}}~~~~~~}}}}||||{{{{{{{{{{|||}}~~~~~~}}}}|||||{{{{{{{{{|||}}~~~~~~~}}}}||||{{{{{{{{{{|||}}~~~~~~}}}}}|||||{{{{{{{{|||}}~~~~~~~~}}}}||||||{{{{{{{||||}}~~~~~~~~}}}}}|||||{{{{{{{||||}}}~~~~~~~}}}}}|||||||{{{{{|||||}}~~~~~~~~~}}}}}||||||||{{||||||}}~~~~~~~~~}}}}}}|||||||||||||||}}}~~~~~~~~~~}}}}}}||||||||||||||}}}~~~~~~~~~~}}}}}}}|||||||||||||}}}}~~~~~~~~~~}}}}}}}|||||||||||||}}}~~~~~~~~~~}}}}}}}}|||||||||||}}}}~~~~~~~~~~~~}}}}}}}}||||||||||}}}}}~~~~~~~~~~~~}}}}}}}}|||||||||}}}}}~~~~~~~~~~~~}}}}}}}}}}|||||||}}}}}~~~~~~~~~~~~~}}}}}}}}}}}||||}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~}||zzzyxxxvvwwvwwwxyz{||~~}|ywtrnljihggffghikmnprtvxz{}~~~}|zyxvusrqponnnmmmmmmnnnopprstvy|wz{~}}}}}}~~~{z{}~}{ywvtrqonlkjihggggggghijlmoprsuvxy{|}~|wux|zzz{|}~~~}}|||{{{{{|||}~}xtqqtz{wtrqppoonoooooooooonnnnnnnnnnnnnooppqrstuvwyz|}~|xtqqrv|{wusqqpoooooooooooonnnnnnmmmmmnnnnoopqqrstuvxyz{|~}ytpliggjnu{}xtpmjhgfeedddddddddccccccccccccdddeefghhijlmnoqrtuwxz{|~|xusrtx}}{yxwwvvvvvvvwwwwwwwwwwxxxxxyyzz{{|}~|z{}~|{zyyxxwwwwwwwvvvvvvvvvvvvvvwwwxyyz{|}~}yvrppqty|yvsrqonmmllllkkkkkkkjjjjjjjjjkkkllmmnoppqrsuvwxyz{|}~~{xuqmjhfgimrx~|xtqolkjihgggfffffffffffffgggghhhiijkllmnoprstuwxy{|}~|yvsrrsv{~|zyxwvuuuutttuuuuuuuuuvvvvwwxxyyz{|}~}{zz|~|{zyyxxxwwwwwwwwwwwwwwwwwxxyyzz{||}~~{wtrppqsw||yvusrponnmmllllkkkkkkkkkkkkklllmmnoopqrstuvwxyz{|}~~|yvspmkihhjnrw||yvtrpomlkkjjiiiiiiiiiiiijjjjkkllmmnopqqrsuvwxyz{}~}zwtrqqruy~~}{zxwwvuuuuttttttuuuuuvvvwwxxyyz{|}}~}zyxyz}~}|{zyyxxxwwwwwwwwwwwwwxxxyyzz{||}~~{yvsqppqsvz|zxvusrqpoonnnmmmmmmmmmmmmmnnnooppqrrstuvwwxyz{|}~~|zxurpmkjijknrv{}zxvusqponmmllllkkkkkkllllmmmnnoopqqrstuvvwyz{|}~}zxusrqqrtx|~|{zyxwwvvvuuuuuuuvvvvwwwxxyyz{||}~}zxvtstuwz~~}|{zzyxwwvuuttssrrrrrqqqrrssttuvwwxy{|}~|{yyyz{~~}|{zzzzzzzywuxyuvxyyyyzzzz{{{||}}~~~}{ywusqonnopruy|~}|{zzyyzz{{zxvw{ywwxyz{||}~~}{zzyz|~~~}|zywvxyzyxvuuuvvwwxxxxyyzz{{{||}}}}~~~~}{yxvtsrqqrsux{~~}|zyxxxyz||||{zzyyyzz{{|}}~~}{zxwwwwy{}~|{zxwvuttuvwxxxxwwvvuuuuvvvwwxxyyz{{||}~~}|zywvvvvwy{~~|{yxwvuuuvwxxyyyxxwvvvvvvwwwxxyyzz{{||}}~~~}{zxwuttsstuwy|~}{yxwutsrrrrsstuuuttssrrrrrrsssttuuvvwwxxyyz{{||}}~~~~}|zyxwvuuuvwxz}~|{zyyyyzz{|}}}|||{{{{{{{{||}}~~~~}||{||}~}|{{||}~~~~~~}}|||{{{||||}}}~~~}|{zyyxxxyz|~~}|zyxxwwxxyzzzzzyyxxxwwwwwwxxxxyyyzzz{{|||}}}~~~~}|{zyxwvuttttuvwy{}}|{yxwvuuuuuvwwxxxxwwvvvuuuuvvvvwwwxxxyyzz{{{||}}}~~~}}|{zyxxwwwxxz{}~}|{{{{{||}~~~~~}}}||||||||}}}}~~~}}|||}~~~~~~~~~}}}}}~~~~~~}|{{zzyzzz{}~~|{{zzzzz{{|||||{{zzzyyyyyyyyyyyzzzz{{{{|||}}}}~~~~~~~~}||{zzyxwwvvuuvvwxy{}~~}|{zyxxxxxyyzzz{{zzzyyxxxxxxxxxxyyyyzzz{{{|||}}}}~~~~~}}|{{zzyyxxyyz{|}~}}||}}~~~~~}}}}}}}}}~~~~~~}}}}}}~~~~~~~~}||{{{z{{{|}~~}|||{|||}}}~~~}}}|||{{{{zzzzzz{{{{{{{|||||}}}}}}~~~~~~~~~~~~~~~~~}}}||{{zzyyxxwwwwwxxyz{}~~}||{zzzzzz{{||||||||{{{zzzzzzzzzzzzz{{{{|||||}}}}~~~~~~~~}}||{{{zzzyyzz{{|}~~~~~~~~~~~~~~~~~~~~~}}}}}~~~~}}|||{{{{||}}~~~}}}}}}~~~~~}}}|||||||{{{|||||||||||}}}}}}}}~~~~~~~~~~~}}}}}|||{{{zzyyyxxxxyyyz{|}~~~}|||{{|||}}}~~~~~}}}}||||{{{{{{{{{{{|||||||}}}}}}~~~~~~~~~~~~~~~~~~~}}}}|||{{{zzzz{{{|}}~~~~~~~~~~~}}}}}}}~~~~~~~~~~~~~}}}|||{{{{{{||}}~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~}}}||||{{{{{||}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}||||{{{{|||}~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}|||||||||||}}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}||||||||||}}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}|||||||||}}}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}||||||||||}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}|||||||||}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}||||||||}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}|||||||}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}||||||}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}|||}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|{{{zyyywxyxxxxyz{|}}}|zwuqomkjihhghijkmoqsuwxz|}~~}|{yxvusrpomlkkkjjjjjkkkllmnoqrtvy}y|~y~~~~~~|}{{}}{zxwusqpnmlkjihhhhhiijklnoqrtuwxz{|}~~~~~yuv|~|{{|}~~~}}}}}}}}~~zurprw}}yvtrqqpppppppppppppppoooooooooopppqqrstuvwxy{|}~yurpqty}zwusrrqqqppppppppppppooooooooooooppqrrstuvwxyz|}~}yuqmigfhlrx~{vroljhggffeeeeeeeeeeeeeeddddeeeeeffghhijklnoprstvwxz{|~}yusrruz|zyyxxwwwwxxxxxxxxyyyyyyyyzzz{{||}~|zz{~}|{zyyyyxxxxxxxxwwwwwwwwwwwxxxyzz{|}}~~zvspoprv|~{xusrqponnmmmmmllllllllkkkkkllllmmnnoopqrstuvwxyz||}~~{yurnkhffgkpu{~zwsqnlljiihhhhggggggggghhhhhhiiijjkllmnopqrstvwxz{|}~}zvtrqrtx}~|zyxwwvvvvvvvvvvvvvvwwwwwxxyyzz{|}}~}{yyz}~}|{zzyyyyxxxxxxxxxxxxxxxyyzzz{||}~~{xurpopruz~{xvusrqponnnmmmmlllllllllllmmmmnnoopqrrstuvwxyz{|}~~~|ywtqnkihgilpuz~{xvsrponmlkkkjjjjjjjjjjjkkkkllmmnnoppqrstuvwxyz|}~}zwusqqqsw{~}{zyxwwvvvvvuuvvvvvvvwwwxxyyzz{||}~}{yxxy{~}|{zzyyyxxxxxxxxxxxxxyyyzz{{||}~~~|yvsqpopqtx}~{zxvusrqqppoonnnnnnnnnnnnnoooppqqrrsttuvwxyzz{|}~~~|zxuspmkjiijlpty}|zxvtsrpoonnmmmmlllllmmmmmnnooopqqrsstuvwxyz{|}~}zxvsrqpqsvy~~|{zyxxwwwvvvvvvvwwwwwxxyyzz{{|}~~}{yxwvvwxz~~{ywvtsrqqpooonnnnnooooppqqrsttuvxyz{|}~|zyxxxxyy{~}|{zyxxwvvuttssrrrqqqqqqqqrsstuvvwxyz{|}~~|{yxwuuuuuvvw}~}||{{{zzzzzyyyzzzz{{||}~~}|zywvvy}}|{zyxwvuuttssrrrrrqqqqrrrrsstuuvwxxyz{|}}~~|{ywutrrsuy}~}}|{{{zzzzzyyyyzzzzz{{||}}~}zxvtrqpqruy|}|{zyxwwvvuuuttttttttuuuuvvwwxxyzz{|}}~~|zxvtsqqqrtwz~~|{zyyxwwvvvuuuuuuuuuuuuvvvwwxxyyz{{|}}~~}{ywusqpnmmnoqtx{~}|zyxwvuttssrrrqqqqqqqqqrrrsssttuvvwxxyzz{|}}~}|zxwutsrrtux{~}}||{{{{zzzzzzz{{{{|||}}~~}{zxwvvvxz}~}}||{{{zzzzzzzzzzz{{{|||}}~~~}{zxvusrrrsuwz}~}|{zyyxxwwvvvvvvuvvvvvvvvwwwxxyyzz{{||}}~~~}|zyxvusrpoooopruwz}~}{{zyxwwvvvuuuuuuuuuuuuuvvvwwxxxyzz{{||}}~~}|{yxwuutttuvx{~~~}}|||||{{{{{{|||||}}}~~~|{zyxwwxyz|~~}}}|||||||||||}}}}~~~~}|{yxwutttttvwz|~}||{zzyyyxxxxxwwwwwxxxxxxyyyyzz{{{||}}}~~~~}}|{zywvutsrqqqqrtvxz}~}}|{zzyyyxxxxwwwwwwxxxxxxyyyzzz{{|||}}~~~}|{zyxwvuuuuvwy{}~~~}}}}}}}}}}}}}}}~~~~~}|{zyxxxxy{|~~~~~}}}}}}}}~~~~~~~}|{zyxwvuuuuvwxz|~~~}}||{{zzzzzyyyyyyyyyzzzzz{{{{|||}}}}~~~~~}}|{zyyxwvutssrsstuvxz|~~}}||{{{zzzzyyyyyyyyzzzzzz{{{|||}}}~~~~}||{zyxxwvvvvwxy{}~~~~~~~~~~~~~~~~~~}|{{zyyyyyz{|~~~~~~~~~~~~}}|{zyxxwvvvvwxyz|}~~}}}|||{{{{{{{{{{{{{{{{|||||}}}}}~~~~~~~~}}||{zyyxwvvutttttuvwyz|~~~}}}||||{{{{{{{{{{{{{{||||||}}}}~~~~~~}}|{{zyxxxwwwwxyz{|~~~~~}}}}}}}}}}}}}}~~~~~~~~}||{zzyxxwwwwwxyz{}~~~~~~}}}}}}}}}}}~~~~~~~~~}}|{{zyyxxwwwwxyz{|}~~~~~~}}}}}}}~~~~~~~~~~}}||{zzyyxxxwxxxyz{}~~~~~~~~~~~~~~~~~~~~~~~~}||{{zyyxxxxxxxyz{|}~~~~~~~~~~~~~~~~~~~~}}||{zzyyxxxxxxyyz{}~~~~~~~~~~~~~~~~~~~}}||{{zzyyxxxxxyyz{|}~~~~~~~~~~~~~~~~~}}}||{zzyyyxxxxyyz{|}~~~~~~~~~~~~~~~~}}||{{zzyyyyxxyyzz{|}~~~~~~~~~~~~~}}||{{zzzyyyyyyyyz{|}~~~~~}}}||{{zzzyyyyyyyzz{|}~~~~}}}||{{{zzyyyyyyyzz{|}~~~~~}}|||{{zzzyyyyyyzz{{|}~~~~~}}}||{{{zzzyyyyyzzz{|}~~~~}}}|||{{{zzzyyyyzzz{||}~~~~~}}}|||{{zzzzzyzzzz{{|}~~~~~~}}}|||{{{zzzzzzzzz{{||}~~~~~}}}||||{{{zzzzzzzzz{{|}~~~~~~~}}}|||{{{{zzzzzzzz{{||}~~~~~~}}}}|||{{{{zzzzzzz{{||}}~~~~~~}}}}||||{{{{zzzzz{{{{|}}~~~~~~~}}}}|||{{{{{{zzz{{{{||}~~~~~~~~}}}}||||{{{{{{{{{{{{||}}~~~~~~~}}}}}||||{{{{{{{{{{{||}}~~~~~~~~~}}}}}||||{{{{{{{{{{|||}}~~~~~~~~}}}}}||||||{{{{{{{{|||}}~~~~~~~~~}}}}}||||||{{{{{{||||}}}~~~~~~~~~}}}}}}|||||||{{{|||||}}}~~~~~~~~~~}}}}}}|||||||||||||||}}~~~~~~~~~~~}}}}}}||||||||||||||}}}~~~~~~~~~~}}}}}}}|||||||||||||}}}~~~~~~~~~~~~}}}}}}}||||||||||||}}}~~~~~~~~~~~~}}}}}}}|||||||||||}}}}~~~~~~~~~~~~~}}}}}}}}||||||||}}}}}~~~~~~~~~~~~}}}}}}}}}}||||||}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~}{xwwtssrpqqqqqrstuwyyz}~|zxvtrpoooopqqstvxz|}~}{ywusqomkjhgfeedcccccccddeghjlnquz{p~uv|~}|zyxvtsrqpponnnoopqqrtuvxyz{|}~~}||{zzyzyyyyyzz{|}{upnrz{yxxxyz{|}~~~~~~~~~}xtrqtz~zwussrrrqqqqqqqqqqqpqpppppoppppppqrrstuvwxyz|}~~zvspoqu{}zwutsrrrqqqqqqqqqqqqqppppppppppqqqrrstuvwxyz{|~~{xtpliggjou|{wsomkjihggggfffffffffeeeeeeeeeeeffgghiijklnopqstuwxy{|}~~{wtqonptz|zxxwvvvvvvvvwwwwwwwwxxxxxxyyzz{{|}~~{zz}~}|{{zzzzzzyyyyyyyyxxxxxyyyyzz{{|}}~}yurppqty{yvutrqqppoooonnnnnmmmmmmmmmmmnnnoopqqrstuvwxyz{|}~~|yvspligefhlqw}}zvspnmljjihhhhhgggggggggghhhhhiijjkklmnopqrstuwxyz|}~|yvspoopsx~|zyxwvvuuuuuuuuuuuuuvvvvvwwxxyyz{{|}~~{yxy{}||{{zzzzyyyyyyyyyyyyyzzz{{{|}}~}zwtrppqsx}~{yxvtsrqqppooonnnnnnnnnnnnnnnooppqqrsstuvwxyzz{|}~~|zwtroligggilqv|~zxusrpommlkkkjjjjjjjjjjjjkkklllmmnoopqrstuvwxyz{|}~|zwtrpoopsw{}|zyxwvvvuuuuuuuuuuuvvvvwwxxyyz{{|}~~{ywwwy|}}|{{zzzyyyyyyyyyyyyzzz{{{|}}~~~{xusqppqsv{~|{ywvutsrqqppppoooooooooopppqqqrssttuvwwxyz{|}}~~|zxvsqnljhhijmquz~{ywvtsqpoonmmmmlllllllmmmmnnnoopqqrsstuvwxyz{||}~~|zxusqoooprvz~~}{zyxxwwvvvvvvvvvvvwwwxxxyyz{{|}~~}{ywvvvxz}~}|{{zyxwwvvuuttsssssssssttuuvwxxyz{}~~}{yxxxyz}~}||{{{{{|{xvxzvwyz{z{{{{||||}}~~}|zxvtrpnmmnoqtx|~}|{{{{{{||{zxx|{xxyz{|}}~~|zyxxyz}~}|zxxyz{{ywwvvwwxxyyyzzz{{|||}}}~~~~~|{yxvusrqppqrtwz~~}|{yyyz{}~~}|{{zz{{{||}~~}{zxwvvvvxz}~|{yxwvuuvwxyyyyyxxwwvvvwwwxxyyzz{{||}~~~}|zywvuuuuvxz}}|{yxwwvwwxyzzzzyyxxwwwwwxxxyyzz{{|||}~~~~|{zxwutsrrrstvx{~~|{yxvutssssttuvvvvuuttssssstttuuuvvwwxxyyzz{{||}}}~~~}}{zyxwvutttuvwy|}|{zzzz{{|}~~~~}}||||||||}}}~~~}|{{z{{}~~}|||}~~~~}}}|||}}}}}~~~~}|{zyxwwwxxz{}~}{zyyxxyyz{{{{{zzyyyxxxxxxxyyyyzzz{{{|||}}}~~~~~~}||{zyxwvutsssstuvxz|~}|zyxwvvvvvwxxyyyyxxwwwvvvvvwwwwxxxyyzzz{{{||}}}}~~~~}}|{zyyxwvvvvwxyz|~~}|||||}}~~~~}}}}}}}}}~~~~~}}||{{||}~~~~~~~~~~}}|{zzyyyyyz{|~~}|{{zz{{||}}}}}||{{zzzzzzzzzzzzzz{{{{|||||}}}}}~~~~~~~~~~~~~~~}}}||{{zyxxwvvuuttuuvwyz|~~}{zzyyxyyyz{{{{{{{zzzyyyyyyyyyyyzzzz{{{{||||}}}}~~~~~~~~}}||{zzyyxxxwxxyz{}~~}}}}~~~~~~~~~~~~~~~}}|||||}}~~~}||{{zzzzzz{{|~~}}|||}}}~~~~~~}}}|||{{{{{{{{{{{{{|||||||}}}}}}}}~~~~~~~~~~}}}}}|||{{zzyyxxwwvvvvvwxxz{|~~}||{{{{{{||}}}}}}}|||{{{{{zzzzz{{{{{{{|||||}}}}}}}~~~~~~~~~~~~~~~~~}}}}||{{zzzyyyyyyyz{|}~~~~~}}}||||}}~~~~~}}||{{{{{{{{{|}~~~~~~~~~~~~}}}}||||||||||||||||}}}}}}}}}}}}}}}}}}}}}}||||{{{zzzyyxxxxxwxxxyyz{|}~~}}|||||}}~~~~~~~}}}|||||||||||||||||||}}}}}}}}}~~~~~~~~~~~~}}}}}}|||{{{zzzzzzzzz{{|}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{{zzz{{{||}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}||||{{{{{{{{{||}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}|||{{{{{{{{{{||}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}||||{{{{{{{{{||}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}||||{{{{{{{{||}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}|||||{{{{{{{|||}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}|||||{{{{{{|||}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}||||||{{{||||}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}|||||||||||||}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}|||||||||||||}}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}||||||||||||}}}~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}|||||||||||}}}~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}||||||||||}}}~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}||||||||||}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}|||||||||}}}}~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}|||||||}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}||||}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}|||}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|zxwusssqqqqqqrrsuvxyy|~~}{ywusqpooopqqrtuwy{|}~{zxvtrpnljiggfeeddddddeeffhikmoruyrwwty~}~~}{zxwutsrqpoonooopqqrstvwxy{|}~~}}|{{zzzzzzzz{{|}~~xropw{xvvvwyy{|}~~~~~~~~zvsrsx}zvtrqqqqppppqqqqqqqqqqppppppppppqqrrsttuvxyz{}~}xtqppty}yvtsrqqqppqqqqqqqqqqqqpppppppppqqqrrstuvwxyz{|}~~zvrnjhgimsz{wsoljhhgfffffffffffffeeeeeeeeeefffgghiijklmnpqrsuvwyz{|~}yvrpopsx~|zxwvvuuuuuvvvvwwwwwwxxxxxyyyzz{{|}~~|zz|~}|{{zzzyyyyyyyyyyyyyyyyyyyyyzz{{|}}~~{wtqpqsw}{xvtsrqpooonnnnnnnnnmmmmmmmmmnnnnoopqqrstuvwxyz{|}~}{xuqnjhffgkpv{}zvspmlkjihhggggggggggggghhhhhiiijjkklmnopqrstuvwyz{|~~{wtrpoprv|~|zyxvvuutttttuuuuuuuvvvvwwwxxyyz{{|}~}zyy{~~}|{{zzzyyyyyyyyyyyyyyyzzz{{{|}}~|xusqpqsv{~{ywvtsrqppooonnnnnnnnnnnnnnnnoopppqrrstuvvwxyz{|}~}{yvspmjhggikotz~zwurqpnmlkkjjjjjjjjjjjjjjkkklllmmnoopqrstuvwxyz{|}~~{xusqoopruz}{zxwwvuuuuuuuuuuuuuvvvvwwxxyyz{{|}~|zxwwy{~}|{{zzzyyyyyyyyyyyyyzzz{{{||}~~|ywtrppqruy~~|{ywvtsrrqqpppooooooooooppppqqqrrsttuvvwxyz{{|}~}|zwuromkihijlpty}~{ywvtrqponnmmmllllllllmmmmnnnooppqrrstuvwwxyz{|}~}{yvtrpooprux}~|{zyxwwvvvvvvvvvvvvwwwwxxyyzz{|}}~|zxwvvwz|}|zywvuttsrqqppooooooooppqrrstuvwyz{|}~|zyxwxyyy|~}|{{zyyxxwwvvuuuutttttuuuvvwxyyz{|}~~~}|zywvtsrqqrrrst{~}}||{{{zzzzzzzzzzzz{{||}}~}{zywvwy~~}|{zyxxwwvvuuuttttttttuuuvvwwxxyz{{|}}~~|{ywvtrpooprv{~}||{{{zzzzzzzzzzzz{{{||}}~~}{xvtrqqqsvy}}|{zyxwwvvuuuuutttuuuuuuvvwwxxyyz{{|}~~~|zxvtrqpprtwz~~|{zyxxwwvvvvuuuuuuuvvvvwwwxxyyzz{{|}}~~}{yxvtrpnmmnorux|~}|zyxwvuttssrrrqqqqqqqqqrrrsssttuvvwwxyyz{||}~~}|zxwusrqqqrtwz~~}||{{{zzzzzzzzzzz{{{{||}}~~}{yxwvvwxz}~}}|||{{{{{{{{{{{{{{|||}}~~~~}{zxvutsrrsuwz}~}|{zzyyxxwwwwvvvvvvvwwwwwxxxyyyzz{{||}}~~~}{zywvtsqponnoprtwz}~}|{zyyxwwvvuuuuuuutuuuuuuvvvwwwxxyyzz{{|}}~~}|{zxwutssrstuwz}~~}}|||{{{{{{{{{{||||}}}~~}|{yxxwwxyz}~~~}}}}}}}}}}}}}}}~~~~}|{yxwvutttuvxz|~~}|{{zzzyyyxxxxxxxxxxxyyyyzzz{{{|||}}}~~~}||{zxwvutrqqppqrsuxz}~}||{zzyyxxxxwwwwwwwwxxxxxyyyzzz{{{||}}~~~~}}|zyxwvuttttuvxz|~~~}}}}|||||||}}}}}}~~~~}{zyyxxxxy{|~~~~~~~~~~~~~~~~~}{zyxwvvuuuvwxz|~~}}|||{{{zzzzzzzzzzzzzz{{{{{||||}}}~~~~~~}||{zyxwvuttsrrrstuvxz|~~~}||{{{zzzzyyyyyyyyyyzzzzz{{{{|||}}}~~~~}}|{zyxwwvuuuuvwxz|~~~~~~~~}}}~~~~~~~~}|{zzyyxyyz{|~~~}|{zyyxwwvvvwxyz|~~~}}}|||||{{{{{{{{{{||||||}}}}}~~~~~~~~}}|{{zyyxwvuutttttuvwyz|~~~}}||||{{{{{{{{{{{{{{{{|||||}}}}~~~~~~}}|{{zyyxwwvvvwwxyz|~~~~~~}}}}}}}}}}}~~~~~~~~~}||{zyyxxwwwwwxyz{}~~~~~~~}}}}}}}}}~~~~~~~~~}||{zzyyxxwwwwxxy{|}~~~~~~~~~~~~~~~~~~~~~~}}|{{zzyxxxwwwxxyz{}~~~~~~~~~~~~~~~~~~~~~~}}||{zzyyxxxwwxxyz{|}~~~~~~~~~~~~~~~~~~~~}||{{zzyyxxxxxxyyz{}~~~~~~~~~~~~~~~~~~~}}||{{zyyyxxxxxyyz{|}~~~~~~~~~~~~~~~~}}||{{zzyyyxxxxyyz{{}~~~~~~~~~~~~}}}||{{zzyyxxxxxyyz{|}~~~~~~}}||{{zzyyyyxxyyyz{|}~~~~}}|||{{zzyyyyyyyyzz{|}~~~~}}}||{{zzzyyyyyyyzz{|}~~~~}}}|||{{zzzyyyyyyyz{{|}~~~~~}}}||{{{zzzyyyyyyzz{|}~~~~~}}}|||{{zzzzyyyyzzz{{|}~~~~~}}}|||{{{zzzzyyyzzz{{|}}~~~~~}}}|||{{{zzzzyyzzzz{||}~~~~~}}}|||{{{zzzzzzzzzz{{|}}~~~~~}}}}|||{{{zzzzzzzzz{{||}~~~~~}}}}|||{{{{zzzzzzzz{{||}}~~~~~~}}}}|||{{{{zzzzzzz{{{||}~~~~~~}}}}||||{{{{{zzzz{{{{||}~~~~~~~~}}}}||||{{{{{{{{{{{{||}}~~~~~~~~}}}}}||||{{{{{{{{{{{|||}~~~~~~~~~}}}}|||||{{{{{{{{{{|||}}~~~~~~~~}}}}}|||||{{{{{{{{{|||}}~~~~~~~~~}}}}}||||||{{{{{{{|||}}}~~~~~~~~~}}}}}}|||||||{{{{|||||}}~~~~~~~~~~}}}}}}|||||||||||||||}}}~~~~~~~~~}}}}}}|||||||||||||||}}}~~~~~~~~~~}}}}}}}|||||||||||||}}}~~~~~~~~~~~~}}}}}}}||||||||||||}}}}~~~~~~~~~~~}}}}}}}|||||||||||}}}}~~~~~~~~~~~~}}}}}}}}||||||||||}}}}~~~~~~~~~~~~}}}}}}}}}|||||||}}}}}}~~~~~~~~~~~~~}}}}}}}}}}||||}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~|zyxvuutsstssttuvwy{{|}{}{ywvtrqqqrrssuvxz|~}{ywusqomkjihggfeeeeeefffgijkmorvzyn~rsy}~|{{}}|{zywvutsrqqqpppqqrsstvwxz{|}~~}}|{{|{{{{{||}~{tomqy|xusstuvwxyz{|}~~~~~~~~~~}xtqqsy~zvsqonnnmmmmnnnnnnooooooooopppqqqrrstuvvxyz{|~zvrooptz}yusqoonnnnnnnnnnooooooooppppppqqrrsstuvwxyz{|}~|xtplhffintz|wsoligfedcccccccccccdddddddeeeeffgghiijklmnoqrstvwxz{|}{wtpnnosy{yvutssrrrsssstttuuvvvwwwxxyyzz{|}}~~{yy|}{zyxxwwwwwwwwwwwwwxxxxxxyyyzz{{|}}~}yurpopsx~{xusqponmmllllllllllllllmmmmmnnnoopqqrstuvwxyz{|}~}zwsplifeegkpv|~zvsomjihgffeeeeeeeeeffffgggghhiijjklmmnopqrtuvwxz{|}~|yvspnnpsw}~{ywvutssrrrrrrsssstttuuvvwwxxyyz{|}~~{yxxz~~}{zyyxxxxwwwwxxxxxxxyyyzzz{{|}}~~zwtqpopsw|~{xvtsqponnmmmmmlllmmmmmmmnnnoooppqrrstuvvwxyz{|}~}zxuroligfghlpuz~{wurpomlkjiihhhhhhhhiiiijjjkkllmnnopqqrstuvwxz{|}~}zwtrpnnprv{~|zywvuttssssssssttttuuvvvwxxyyz{||}~~{ywwwy|}|{zyyxxxxxxxxxxxxyyyyzz{{||}~~~{xusqpopsvz~~|zxvusrqppooonnnnnnnnoooopppqqrrsttuvvwxyzz{|}~}{yvtqnljhhhjlpuy~~{yvusqponmllkkkkkkkkklllmmmnnoopqqrsttuvwxyz{|}~}zxusqonopruy~~|zyxwvuuuuttttuuuuvvvwwxxyyz{{|}~~{ywvuvwz}~|zywvutssrqqpppppppppqqqrrstuuvwxyz|}}}{xvuttuwz}~||{zzzyyyyxxxxxxxxxxxwwwwwwxwxxxyyzz{{|}~}|zxvtrponmmnprvz~~}}||||{{{{{{{|||||||}}}}~~~}{yxwvwxz}~}|{zzyyxxwwwwvvvvvvuuuuuuuvvvvvwwxxyyzz{|}}~~|{ywvtsrrrsuwz}~}|{{zzzzzzyzzzzzzzz{{{{|||}}~~}{yxvuuuuwxz}}{zxwvvuutttttttttttuuuuvvwwwxxyzz{{|}~~~|{yxvuutuvwy{}~|zyxwwvvuuuuuuuuuuuuuvvvwwwxxyyzz{{|}}~~}{zxwutrrqqrstvxz|~}{yxvutssrrqqqqqqqqqqqqrrrrssttuuvvwwxyyz{{|}~~~|{zxwvuuuuvwy{}}||{zzzyyyyyyyzzzzz{{{|||}}~~~}|{zzzz{|~~}}||{{{zzzzzzzzz{{{{{|||}}}~~~|{zyxwvvvwxyz|~~|{zyxxwwvvvvvvvvvvvvvwwwwxxxxyyzz{{|||}}~~}|{zxwvutsrrrsstuwyz|~~|{yxwwvvuuttttttttttuuuuvvvwwwxxyyzz{{||}}~~}|{zyxwwvvvwxyz|}~}}||{{{{{{{{{{{{{{|||}}}~~~~}||{{{{||}~~}}}}|||||||||}}}}}}~~~~}|{zyyxxwwxxyz{}~~}|{{zyyyxxxxxxxxxxxxxxxyyyyzzzz{{|||}}}~~~~}|{zyyxwvuuttttuuvwxz{|~~}|{zzyyxxwwwwwwwwwwwwwxxxxyyyzzz{{{||}}~~~~}}|{zyyxxwwwxxyz{}~~~}}}||||||||||||}}}}~~~~~}||{{{{||}~~~~~~}}}}}~~~~~~~~}|{{zyyyxxyyyz{|}~~}}|{{{zzzzzyyyyyyzzzzzzz{{{{{|||}}}}~~~~~}}|{zzyxxwvvvuuvvvwxxy{|}~~}}|{{zzzyyyyyyyyyyyyyzzzzz{{{{|||}}}~~~~~}||{{zyyyxxxyyyz{|}~~~~~}}}}}}}}}}~~~~~~~~}||||{|||}}~~~~~~~~~~~~}}||{zzzyyyyyzz{||}~~~}}|||{{{{{{{{{{{{{{{{||||||}}}}}~~~~~~~}}||{{zzyxxwwwwwwwwxxyyz{|}~~~}}||{{{{{{zzzz{{{{{{{{{|||||}}}}~~~~~~}}||{{zzzyyyyyyzz{||}~~~~}}}}}}}}}}}}}}}}~~~~~~~~}}||{{zzzyyyzzz{{|}}~~~~~}}}}}}}}}}}}}}~~~~~~~~~}}||{{{zzzzzzzzz{{|}~~~~~~}}}}}}}}}}~~~~~~~~~~}}}||{{zzzzzzzzz{{||}~~~~~~~~}}}}}}~~~~~~~~~~~~}}||{{{zzzzzzzzz{{|}}~~~~~~~~~~~~~~~~~~~~~~~}}}||{{{zzzzzzzz{{||}~~~~~~~~~~~~~~~~~~~~~~}}|||{{{zzzzzzz{{{|}}~~~~~~~~~~~~~~~~~~~~}}}||{{{{zzzzzz{{{||}}~~~~~~~~~~~~~~~~~~}}}|||{{{{zzzzzz{{{||}~~~~~~~~~~~~~~~~~~}}}|||{{{{zzzzz{{{||}}~~~~~~~~~~~~}}}||||{{{{{zzz{{{{||}~~~~~}}}}|||{{{{{zz{{{{{||}}~~~~~~}}}||||{{{{{{{{{{{{||}}~~~~~}}}}||||{{{{{{{{{{{|||}~~~~~~~}}}}||||{{{{{{{{{{{||}}~~~~~~}}}}||||{{{{{{{{{{{|||}}~~~~~~}}}}||||{{{{{{{{{{{||}}~~~~~~~}}}}|||||{{{{{{{{{{|||}}~~~~~~~~}}}}|||||{{{{{{{{{|||}}}~~~~~~~}}}}}|||||{{{{{{{{||||}}~~~~~~~}}}}}||||||{{{{{{{||||}}}~~~~~~~}}}}}|||||||{{{{{||||}}}~~~~~~~~}}}}}}||||||||||||||||}}~~~~~~~~~~}}}}}||||||||||||||||}}}~~~~~~~~~}}}}}}|||||||||||||||}}}~~~~~~~~~~}}}}}}||||||||||||||}}}}~~~~~~~~~}}}}}}}|||||||||||||}}}}~~~~~~~~~~~}}}}}}}||||||||||||}}}}~~~~~~~~~~~~}}}}}}}}||||||||||}}}}}~~~~~~~~~~~~}}}}}}}}|||||||||}}}}}~~~~~~~~~~~~}}}}}}}}}}||||||}}}}}}~~~~~~~~~~~~~}}}}}}}}}}|||||}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~}|ywwussrqpqqqqqrstvwyy{~}{xvtsqooooopprsuwy{|}~|zxvtrpnljhgffeddcccccddeeghjlnqtx~uu|uy~~~~|{yxwusrqpoonnnnoopqqstuwxyz|}~~}||{zzyyyyyyyzz{|}ytppv~zxxxyzz{}~~~~~}}}}~~{wtrtx~|xvtsrrrqqqqqqqqqqppppppooooooppppqrrstuvwxy{|}}yurpqty{xvtssrqqqqqqqqqqqqppppppppooppppqqrsstuvwxyz|}~~{wsokhhimsy}yuqnljihggffffffffeeeeeeeeedeeeeeffgghijklmnoqrstvwxz{|}~zvspopsx~~{yxwwvvvvvvvvvvwwwwwwwwxxxxyyzz{{|}~}{{|~}|{{zzzzyyyyyyyxxxxxxxxxxyyyzz{{|}~{xtrqqsw}}zwutsrqpooonnnnnnmmmmmmmmmmmmmnnnoopqrrstuvwxyz{}}~}{yurokhgfhkou{{xtqomlkjihhhggggggggggggggghhhiijjkllmnopqrsuvwxz{|}~~{xurpoprv{}{zywvvuuuuuuuuuuuuuuuvvvvwwxxyyz{|}~~}{yy{}~}|{{zzzyyyyyyyyyyyyyyyyzz{{||}}~|yvsqpqsv{}zxwutrqqppoonnnnnnmmmmmmmnnnnooppqqrsttuvwxyz{|}}~}{yvtqnkihgikotz|yvtrqonmlkkjjjjjjjjjjjjjjjkkkllmnnoppqrstuvwxy{|}~~{yvsqpopruz~|{yxwvvuuuuuuuuuuuuuuvvvwwxxyyz{||}~}{yxxy{~}|{{zzyyyyyyyyyyyyyyzzz{{{|}}~}zwurqpqruy~}{zxvutsrqqpppoooooooooooopppqqrrsstuuvwxyyz{|}~~~|zxuspmkjiijlotx}}zxwusrpoonmmmllllllllllmmmnnnoopqqrsttuvwxyz{|}~~{ywtrqooprtx|}|zyxxwwvvvvvuuvvvvvvwwxxxyyz{{|}~}zxwvvwz}~}}|{zzyxxwvvutttssssssssttuuvwwxyz{|}~|ywvuuuwy|~~}|||{{{{zzzzzzzyyyyyyyyyyyzzzz{{|}~~~}|zxwusqomlkkkmorvz~~}}|||{{{{{{{{{{{{{{|||||}}}~~~|zyxwwxz|~}||{{{zzzyyyxxxxxwwwwwwwwxxxyyyzz{{|}}~~~|{yxvtsrqppprtvz}}||{zzzyyyyyyyyyzzzzzz{{{|||}}~~~}{yxwvvvwy{}}{zyxwwvvuuuuuuuuuuuuuuvvvvwwxxyyz{{|}}~~}{zxwvuuuvwy{}}{zyxxwwvvvuuuuuuuuuvvvvwwwxxxyyzz{||}}~~}{zxwutsssstuwy{}~|{yxvuutssrrrrqqqqqqqqrrrrrsssttuuvvwxxyzz{||}~~~}|zyxwvutttuvxy|~}||{zzzyyyyyyyyyyyzzzz{{{||}}~~~}|{{{{|}~~}}||{{{{{{{{{{{{{{{|||}}}~~}|{zyxwwwxxy{}~}|{zzyxxxwwwwvvvvvvvwwwwwxxxxyyzzz{{||}}~~~}|{zyxwvutsssstuvwy{|~~}{zyxwwvvuuuttttttttttuuuuuvvvwwxxyyzz{{||}}~~~~}|{zyxwwvvvvwxz{}~~}||{{{{zzzzzzzz{{{{{|||}}}~~~~}||{{{|}~~~}}}}}}}|||}}}}}}}~~~~~}|{zzyyxxyyz{|}~}||{{zzyyyxxxxxxxxxxxyyyyyzzzz{{{|||}}}~~~~}||{zyxwvvuuttuuuvwyz{}~~}|{zyyxxxwwwwwwwwwwwwwwwxxxxyyyzz{{{||}}}~~~~}||{zyyxxwwwxxyz{|}~}}}|||||||||||||||}}}}~~~~}}|||{|||}~~~~~~~~~~~~~~~~~~~~}||{zzyyyyzz{|}~~~}||{{{zzzzzzzzzzzzzzzzz{{{{||||}}}}~~~~~~}}|{{zyyxwwvvvvvvvwxyz{|}~~~}||{{zzzyyyyyyyyyyyyyyyyzzzz{{{{|||}}}~~~~~~}}||{zzyyyxxxxyyz{|}~~~~}}}}}}}}}}}}}}}~~~~~~~}}|||||||}}~~~~~~~~~}}||{{zzzzzzz{{|}~~~}}}||||{{{{{{{{{{{{{|||||||}}}}}~~~~~~~~}}||{{zzyyxxwwwwwwwxxyz{{|~~~}}|||{{{{zzzzzzzzzzz{{{{{{|||||}}}}~~~~~~~~}}}||{{zzyyyyyyyzz{{|}~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~}}||{{zzzzzzzzz{{|}~~~~~}}}}}}}}}}}}}}}}~~~~~~~~}}|||{{zzzzzzzz{{||}~~~~~~}}}}}}}}}}}}}~~~~~~~~~~}}}||{{{zzzzzzzz{{|}}~~~~~~}}}}}}}}}}}~~~~~~~~~~~}}|||{{{zzzzzzz{{||}~~~~~~~~~~}}}}}~~~~~~~~~~~~~}}}|||{{{zzzzzz{{{||}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{{zzzzzz{{||}}~~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{{zzzz{{{{||}~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{{{zzzz{{{||}}~~~~~~~~~~~~~~~~~~~~~}}}|||{{{{{{z{{{{{||}}~~~~~~~~~~~~~~~~~~~}}}}|||{{{{{{{{{{{|||}~~~~~~~~~~~~~~~~~~}}}}||||{{{{{{{{{{{||}}~~~~~~~~~~~~~~}}}||||{{{{{{{{{{{|||}}~~~~~~~}}}}||||{{{{{{{{{{{||}}~~~~~~~~}}}}||||{{{{{{{{{{|||}}~~~~~~~~}}}}|||||{{{{{{{{{{|||}}~~~~~~}}}}}|||||{{{{{{{{{|||}}~~~~~~~~}}}}}|||||{{{{{{{{||||}}~~~~~~~~}}}}}||||||{{{{{{{{|||}}~~~~~~~~}}}}}}|||||{{{{{{{{||||}}~~~~~~~~~}}}}}|||||||{{{{{|||||}}~~~~~~~~~}}}}}}||||||||{{||||||}}}~~~~~~~~~~}}}}}}|||||||||||||||}}}~~~~~~~~~~}}}}}}}|||||||||||||||}}}~~~~~~~~~~~}}}}}}}||||||||||||||}}}~~~~~~~~~~}}}}}}}}|||||||||||||}}}~~~~~~~~~~~~}}}}}}}}||||||||||||}}}}~~~~~~~~~~~~~}}}}}}}}|||||||||||}}}}~~~~~~~~~~~~~}}}}}}}}}||||||||||}}}}~~~~~~~~~~~~~}}}}}}}}}||||||||}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}||||}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~}|zwvvsrrqpppppqqrstvxxy|~~{ywusqoonnoopprsuwy{|}~|zxvtrpnlkihgffeedddeeffgghjlmoruy}|rvw}~~~~~~~|{yxwusrqpoonmmnnnoppqstuwxyz{}}~~}||{zzyzzzzzz{{|}~xsqt|{ywwxyzz{|}~~~~}}}}}}}}~~zwttw|}yvtsrqqqpppppppppppooooooonoooooppqqrstuvwxz{|}}yurrsw}|yvtsrqqqppppppppppppoooooooooooppqqrrstuvwxyz|}~~zvrnkiilqw~{vroljihgfffeeeeeeeeedddddddddddeeeffghijklmnoqrstvwxz{|}~}zvsqqrv||zxwvvuuuuuuuvvvvvvvvwwwwwxxxyyz{{|}~}||~}|{zzzyyyyyxxxxxxxxxwwwxxxxxyyz{{|}~{wtrrsv{~{xvtsqppoonnnnmmmmmmlllllllllmmmnnoopqrrstuvwxyz{|}~~{yurnkighjnsy}yvrpmlkjihhgggggfffffffgggggghhhiijkklmnopqrsuvwxz{|}~~{xurqqruz~|zywvuuutttttttttttuuuuuvvvwwxyyz{|}}}{z{}~}|{zzyyyyyxxxxxxxxxxxyyyyzz{{|}}~|yvsrrsuy~~{xwutrqppoonnnnmmmmmmmmmmmmmnnnooppqrsstuvwxyz{||}~~|yvspmkihiknsx}}zwtrqonmlkkjjiiiiiiiiiiijjjjkkllmmnoppqrstuvwxz{|}~~{yvsrpprtx}~}{zxwvvuuttttttttttuuuuvvvwwxyyz{||}~}{yyy{~~}|{zzyyyyxxxxxxxxxxyyyzzz{{||}~|zwtsqqrux|~|zxwutsrqqppooooonnnnnnoooopppqqrrstuuvwxyyz{|}~~~|zwurpmkjjjlorw{~{ywusrqoonmmlllllllllllllmmmnnoopqqrsttuvwxyz{|}~~{ywtrqpprtw{~|{yxxwvvvuuuuuuuuuvvvvwwxxyyz{{|}~|zywwxy|~}}|{zyyxwwvuuttssssrrsssstttuvwxyz{{}~~{ywvuuvy{~~}||{{{{{zzzzzzyyyyyyyyxxyyyyzzz{{|}}~~}|zxwusqomlllmnquy}~}}||{{{{{{{{{{{{{{{{{{||||}}~~~|zyxxxz|~}||{{zzzyyyxxxxwwwwwwwwwwwwxxxyyzz{{|}}~~~|{yxvusrqqqrtvy|~}|{zzzyyyyyyyyyyyyyzzzz{{{||}}~~~|{yxwwwwy{}}{zyxwvvuuuutttttttttuuuuvvvwwxxyyz{{|}}~~}{zxwvvvvwxz|}|zyxxwvvvuuuuuuuuuuuuvvvvwwxxxyyzz{||}~~~|{yxvuttsstuvxz|}{yxvuutssrrqqqqqqqqqqqqqrrrsssttuuvvwxxyzz{||}~~~}|zyxwvuuuuvwy{}~}|{zzyyyyyyyyyyyyyyzzz{{{||}}~~~}||{{|}~~}}||{{{{zzzzzzz{{{{{||||}}~~~}|{zyxxxxxy{|~~|{zzyxxwwwwvvvvvvvvvvwwwwwxxxyyyzz{{||}}~~~}|{zyxwvuttsstuvwxz|}}|zyxwwvvuutttttttttttttuuuuvvvwwxxyyzz{{||}}~~~}|{zyxwwwvwwxy{|~~~}||{{{zzzzzzzzzzz{{{{|||}}}~~~}|||||}}~~}}}}|||||||||}}}}}~~~~~}|{{zyyyyyz{|}~~}}|{zzyyyyxxxxxxxxxxxxxyyyyyzzz{{{|||}}}~~~~}||{zyxwwvuuuuuvvwxy{|~~}|{zyyxxxwwwwwvvvvwwwwwwwxxxxyyyzz{{{||}}}~~~}||{zyyxxxxxxyz{|}~~}}}||||{{{{{{{||||||}}}~~~~~}}||||||}~~~~~~}}}}}}}~~~~~~~~~}||{zzzzzzz{||}~~}||{{{zzzzzzyyyyzzzzzzzz{{{{{|||}}}}~~~~~~}}|{{zyyxxwwvvvvwwxxyz|}~~}||{{zzyyyyyxxxxxxyyyyyyyzzzz{{{{|||}}}~~~~~}}||{zzyyyyxyyyz{{|}~~~}}}}}}}}}}}}}}}}}~~~~~~}}||||||}}~~~~~~~~~~~~~~}}||{{{zzzzz{{|}~~~}}||||{{{{{{{{{{{{{{{{||||||}}}}}~~~~~~~~}}||{{zzyyxxxwwwwwxxyyz{|}~~~}}||{{{{zzzzzzzzzzzzzz{{{{{{||||}}}}~~~~~~~}}}||{{zzzyyyyyzz{{|}~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~}}||{{{zzzzzzz{{|}}~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~}}|||{{{zzzzzz{{||}~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~}}}||{{{zzzzzz{{{||}~~~~~}}}}}}}}}}}}}}~~~~~~~~~~~}}|||{{{zzzzzz{{{|}}~~~~~~~}}}}}}}}}}~~~~~~~~~~~~}}}|||{{{{zzzz{{{||}~~~~~~~~~~}}}}~~~~~~~~~~~~~~~}}}||{{{{{zzz{{{||}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{{{zz{{{{||}}~~~~~~~~~~~~~~~~~~~~~~~~~}}}||||{{{{{{{{{{{||}~~~~~~~~~~~~~~~~~~~~~~~~~}}}||||{{{{{{{{{{||}}~~~~~~~~~~~~~~~~~~~~~~}}}}|||{{{{{{{{{{|||}}~~~~~~~~~~~~~~~~~~~~}}}}||||{{{{{{{{{{||}}~~~~~~~~~~~~~~~~~~~~}}}}||||{{{{{{{{{|||}}~~~~~~~~~~~~~~~~~}}}}|||||{{{{{{{{{|||}~~~~~~~~~~~~~~~}}}}|||||{{{{{{{{|||}}~~~~~~~}}}}|||||{{{{{{{{||||}}~~~~~~}}}}}|||||{{{{{{{{|||}}~~~~~~~~}}}}}|||||{{{{{{{||||}}~~~~~~~~}}}}}||||||{{{{{{||||}}}~~~~~~~~}}}}}||||||{{{{{|||||}}~~~~~~~~~}}}}}||||||||{{||||||}}}~~~~~~~~~}}}}}}|||||||||||||||}}}~~~~~~~~~~}}}}}}||||||||||||||}}}~~~~~~~~~~~}}}}}}}||||||||||||||}}}~~~~~~~~~~~}}}}}}}|||||||||||||}}}~~~~~~~~~~~~}}}}}}}}||||||||||||}}}}~~~~~~~~~~~~}}}}}}}}|||||||||||}}}}~~~~~~~~~~~~~}}}}}}}}|||||||||}}}}}~~~~~~~~~~~~~}}}}}}}}}||||||||}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}||||||}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~}|{xvvtrrqppppppqrstuwxy|}~|ywusroonnnopprsuwy{{}~|zywusqomkihggfeeddddeeffghjkmoqtx|s}wv{~~~~~~~|{zxwusrqpoonmmnnnoppqstuvxyz{|}~~}}|{{zzzzzzzz{{|}~ysqsz|ywwwxyz{|}~~~~}}}}}}}}~~{wttv{~zwusrqqqpppppppppppooooooonoooooppqqrstuvwxy{|}~~zvsrsv|}zwusrrqqppppppppppppooooooooooopppqrrstuvwxyz{}~{wsoliikpv||wspmjihgfffeeeeeeeeeddddddddddddeeffghiijkmnoprstvwxy{|}~~zwtqqru{|zxwvvuuuuuuuuvvvvvvvvwwwwxxxyyzz{|}~~||~~~|{{zzyyyyyxxxxxxxxxwwwxxxxxyyzz{|}}~|xusrsuz|yvtsrqpoonnnnmmmmmmlllllllllmmmnnoopqqrstuvwxyz{|}~~|yvrolihhjmrx~~zvspnlkjihhggggfffffffffggggghhhiijkklmnopqrsuvwxy{|}~|xusqqrty~|zyxvvuuttttttttttttuuuuvvvwwxxyz{{|}~~|z{|~}|{zzyyyyyxxxxxxxxxxxyyyyzz{{|}}~}yvtrrrux}|ywvtsqqpoonnnnmmmmmmmmmmmmmnnnooppqrrstuvwxyz{||}~~|zwtqnkiiijmrw|~{xusqpnmlkkjjjiiiiiiiiiijjjjkkllmmnoopqrstuvwxyz|}~|yvtrqpqtw|}|zxwvvuuutttttttttuuuuvvvwwxyyz{{|}~}{yyyz}~}|{{zyyyyxxxxxxxxxxyyyyzz{{||}~~}zwusrqrtw{~|{ywutsrqqppooooonnnnnnoooopppqqrrsttuvwxxyz{|}}~~|zxuspnljjjknqvz~|ywvtrqponmmlllllllllllllmmmnnooppqrsstuvwxyz{|}~~~|zwusqppqsvz~|{zyxwvvvuuuuuuuuuvvvvwwxxyyzz{|}~~}{yxwxy{~}{zxwvutsrqqpooonnnnnnooopqrsstvwxz{|}~}{yyyyzzz~}}|{zzyxxwwvvuttttttsstttuuvvwxyz{{|}~~}|{zxvutsrrssstv}~}}|{{zzzzyyyyyyyyyzzz{||}~~~|{yxwx{~|{zzyxwwvvuuuttttsstttttuuvvwwxyyz{{|}~~~}{zxvtrqppqtx}~~}||{{zzzyyyyyyyyzzzz{{||}}~}{yvtsrqrtw{}|{zyxwvvuuuttttttttttuuuvvwwxxyyz{||}~~|zxvtsrqqsux|~|{zyxxwvvvuuuuuuuuuuuuvvvwwxxyyzz{||}~~}|zxvtrponnoqsvz}~}|zyxwvutssrrrqqqqqqqqqqqqrrssstuuvvwxxyzz{|}}~~|{ywutsrqrsux|~}||{{zzzzyyyyyzzzzz{{{||}}~~}|zywwwxy{~~}}||{{{zzzzzzzzzz{{{{||}}~~}|zxwutssstvx{~~}|{zzyxxwwwvvvvvvvvvvvvwwwwxxyyyzz{{||}}~~~}|{yxvusrqpoopqsvx{~}|{zyxxwvvuuuutttttttttuuuuvvwwwxxyyz{{||}}~~~}{zywvutsstuvx{~~~}|||{{{{{{{{{{{{{||||}}~~~|{zyxxxxz{~~~}}}}|||||||||}}}}~~~~~|{zxwvuutuvwy{~~}}|{{zzyyyxxxxxxxxxxxxxxyyyyzzz{{|||}}~~~~}}|{zywvutsrqqqrstvy{~~}|{{zyyyxxxwwwwwwwwwwwwxxxxyyzzz{{{||}}~~~~~}|{zywvvuuuuvwy{}~~}}}}|||||||||||}}}}~~~~}|{zyyxyyz{}~~~~~}}}}}~~~~~~~~}|{zyxwvvvvwxy{}~}}||{{{zzzzzzyyyyzzzzzzz{{{{||||}}}~~~~~~}}|{zyyxwvutssssstvwy{}~~}||{{zzzzyyyyyyyyyyyyyzzzz{{{{|||}}}~~~~}}|{zyyxwvvvvvwxy{}~~~~}}}}}}}}}}~~~~~~}|{{zyyyyzz|}~~~~~~~~~~~}|{zzyxwwwwwxxz{}~~~}}}||||{{{{{{{{{{{{{{||||||}}}}}~~~~~~~}}|{{zyyxwvvuutttuvwxy{}~~~}}|||{{{{{{{zzzz{{{{{{{{||||}}}}~~~~~~}}||{zyyxxwwwwwxyz{}~~~~~}}}}}}}}}}}}}}}}~~~~~~~}||{zzyxxwwwwxxy{|}~~~~}}}}}}}}}}}}}~~~~~~~~~}}|{{zyyxxxwwxxyz{}~~~~~~}}}}}}}}}}}~~~~~~~~~}}||{zzyyxxxxxxyz{|}~~~~~~~}}}}}~~~~~~~~~~~}}||{{zyyxxxxxxyyz{}~~~~~~~~~~~~~~~~~~~~~~~~}}|{{zzyyxxxxxyyz{|}~~~~~~~~~~~~~~~~~~~~~~}}||{{zzyyxxxxyyzz{}~~~~~~~~~~~~~~~~~~~~}}||{{zzzyyyxxyyyz{|}~~~~~~~~~~~~~~~~~}}}||{{zzyyyyxyyyz{{}~~~~~~~~~~~~~~~}}||{{zzzyyyyyyyzz{|}~~~~~~~~~~~~}}|||{{zzzyyyyyyzz{|}~~~~}}}||{{{zzzyyyyyzz{{|}~~~~}}}|||{{zzzyyyyyyzz{|}~~~~~}}}||{{{zzzyyyyzzz{{|}~~~~~}}}|||{{{zzzzyyzzz{{|}}~~~~~}}}|||{{{zzzzzzzzzz{||}~~~~~~}}}|||{{{zzzzzzzzz{{|}}~~~~~~}}}|||{{{{zzzzzzzz{{||}~~~~~}}}}|||{{{{zzzzzzz{{{|}}~~~~~}}}}||||{{{{zzzzzz{{{||}~~~~~~}}}}|||{{{{{zzzzz{{{||}}~~~~~~}}}}||||{{{{{{z{{{{{||}}~~~~~~~~}}}}||||{{{{{{{{{{{|||}~~~~~~~~}}}}}||||{{{{{{{{{{|||}}~~~~~~~~~}}}}|||||{{{{{{{{{|||}}~~~~~~~~~}}}}}||||||{{{{{{{||||}}~~~~~~~~~~}}}}}||||||{{{{{{||||}}~~~~~~~~~~}}}}}}|||||||||||||||}}}~~~~~~~~~~}}}}}}||||||||||||||}}}~~~~~~~~~~}}}}}}}|||||||||||||}}}~~~~~~~~~~~~}}}}}}}||||||||||||}}}}~~~~~~~~~~~}}}}}}}|||||||||||}}}}~~~~~~~~~~~~}}}}}}}}||||||||||}}}}~~~~~~~~~~~~}}}}}}}}}||||||||}}}}}~~~~~~~~~~~~~}}}}}}}}}}|||||}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~}|zwwusrrqpqqpqqrrtuwxy{}}{ywtsqoonooppqstvxz{|~~|zxvtrpmljhgffeddccccddeffgikmoquy~xsux~~~~|{yxwusrqpoonnnnnoppqrsuvwyz{|}~~~}|{{zyyyyyyyyz{{}~|vqqv{xwxxyz{|}~~~~}}}}}}~~}yustx~|yvtsrrqqqqqpqqqpppppppooooooooopppqrsttuwxyz|}~{wtrqty|yvtsrrqqqqqqqqqpppppppooooooopppqqrrstuvwxyz{|~|yuqmjijmry~zvrnljihggfffffeeeeeeeeedddddddeeeffghhijklmopqstuvxyz|}~|xurpqsw~~{yxwvvvuuuvvvvvvvvwwwwwwwxxxyyzz{|}~~|{|~}|{{zzzyyyyyyxxxxxxxxxxxxxyyyzz{||}~}yvsrrsw|~zxutsrqpooonnnnmmmmmmmmllllmmmmnnooppqrstuvwxyz{|}~}zwtpmjhghkou{|yuromlkjihhggggggggggggggggghhhiijjklmnopqrstuwxyz{}~}yvtqpprv{~{zywvvuuttttttttuuuuuuuvvvwwxxyzz{|}~|zz{}~}|{zzzyyyyyyyxxxxxxyyyyyzz{{||}~~{wurqqsvz}zxwutrqqpooonnnnmmmmmmmmmmmnnnooppqrrstuvwxxyz{|}~~|zxuroljhhikoty~}ywtrqonmlkkjjjjiiiiiijjjjjjkkllmmnoopqrstuvwxyz{|}~}zwurqppruy~~}{yxwvvuuuutttttuuuuuuvvvwwxxyzz{|}~~|zxxy{~}|{{zzyyyyyxxxxxxyyyyzzz{{||}~~~{xvsrqqsuy}}|zxvutsrqqpppooooooooooooopppqqrrsttuvwwxyz{||}~~}{yvtqolkiijlosx|}{xwusrqoonmmmlllllllllllmmmnnooppqrrstuvwxyyz{|}~|zxusqppprtx|~|{yxxwvvvvuuuuuuvvvvvwwxxyyzz{|}}~~{yxwwxz}~}}|{zzyxxwvuutttsssssssssttuvvwxyz{|}~|zxvuuuwy|~~}|||{{{{zzzzzzzyyyyyyyyyyyyzzz{{||}~~~}|{ywvtrpnmlkkmoruz~~~}}||{{{{{{{{{{{{{{{{||||}}}~~}{yxxwxz|~}||{{zzzzyyyxxxxxwwwwwwwwwxxxyyyzz{||}}~~}{zxwutrqppqrtvy}~||{zzzyyyyyyyyyyzzzzzz{{{|||}}~}|zyxwvwwy{}}{zyxwwvvuuuuutttttuuuuuvvvwwxxyyzz{||}~~|{yxwvuuvwy{}}|zyxxwwvvvuuuuuuuuuuvvvvwwwxxyyzz{{|}}~~}|zywvutssstuvx{}}{yxvuutssrrrqqqqqqqqqqrrrrsssttuuvvwwxyyz{{|}}~~~}|{zxwvuutuuvxy|~~}|{zzzyyyyyyyyyyyyzzzz{{|||}}~}||{{{|}~~}}||{{{{{{{{{{{{{{{{|||}}}~~~}|zyyxxwxyz{}~~|{zzyxxwwwwvvvvvvvvvwwwwwxxxyyyzz{{|||}}~~~}|{zxwvuttssstuvwyz|~~}{zyxwwvvuuutttttttttttuuuuvvvwwxxxyyzz{{||}~~~}|{zyxxwvvvwwxz{}~~~}||{{{zzzzzzzzzz{{{{||||}}~~~~}|||{||}~~~}}}}}||||||}}}}}}}~~~~}|{zyyyyyyz{|}~}}|{{zzyyyxxxxxxxxxxxxyyyyyzzzz{{{||}}}~~~~}}|{zyyxwvuuuuuuvvwyz{}~~}|{zyyxxxwwwwwwwwwwwwwwwxxxxyyyzzz{{|||}}~~~~}}|{zzyxxxwxxxyz{|}~}}}||||||{{||||||||}}}}~~~~~}|||||||}~~~~~~~~}}~~~~~~~~~~}}|{{zzyyzzz{|}~~~}||{{{zzzzzzzzzzzzzzzzz{{{{{||||}}}}~~~~~}}||{zzyxxwwvvvvvwwxyz{|}~~~}||{{zzyyyyyyyyyyyyyyyyyzzzzz{{{|||}}}}~~~~~}}||{{zzyyyxxyyyz{|}~~~~}}}}}}}}}}}}}}}}~~~~~~}}}||||||}}~~~~~~~~~~~~~}}|{{{zzzzzz{||}~~~}}}|||{{{{{{{{{{{{{{{|||||||}}}}}~~~~~~~~}}}||{{zyyxxxwwwwwwxxyz{{|}~~}}|||{{{{zzzzzzzzzzzz{{{{{{|||||}}}}~~~~~~~}}||{{zzzyyyyyyzz{{|}~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~}}|||{{zzzzzzzz{{|}~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~}}||{{{zzzzzzz{{||}~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~}}|||{{{zzzzzz{{{|}}~~~~~~}}}}}}}}}}}}~~~~~~~~~~~}}}||{{{{zzzzzz{{||}~~~~~~~~~}}}}}}}}~~~~~~~~~~~~}}|||{{{{zzzzz{{{||}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{{zzzz{{{||}}~~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{{{zzz{{{{||}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{{{{{{{{{||}}~~~~~~~~~~~~~~~~~~~~~~~}}}||||{{{{{{{{{{{||}}~~~~~~~~~~~~~~~~~~~~~~}}}}||||{{{{{{{{{{|||}~~~~~~~~~~~~~~~~~~~~~}}}||||{{{{{{{{{{{||}}~~~~~~~~~~~~~~~~~~}}}}||||{{{{{{{{{{|||}}~~~~~~~~~~~~~}}}}||||{{{{{{{{{{||}}~~~~~~~}}}}|||||{{{{{{{{{|||}}~~~~~~~}}}}}||||{{{{{{{{{|||}}}~~~~~~~}}}}|||||{{{{{{{{{|||}}~~~~~~~~}}}}}|||||{{{{{{{{||||}}~~~~~~~~~}}}}}||||||{{{{{{||||}}}~~~~~~~~}}}}}||||||{{{{{{|||||}}~~~~~~~~~}}}}}}|||||||{{{{|||||}}~~~~~~~~~~}}}}}}|||||||||||||||}}}~~~~~~~~~~}}}}}}}||||||||||||||}}}~~~~~~~~~~~~}}}}}}|||||||||||||||}}}~~~~~~~~~~}}}}}}}||||||||||||||}}}~~~~~~~~~~~~}}}}}}}|||||||||||||}}}~~~~~~~~~~~~}}}}}}}}||||||||||||}}}}~~~~~~~~~~~~~}}}}}}}}||||||||||}}}}}~~~~~~~~~~~~~~}}}}}}}}}||||||||}}}}}~~~~~~~~~~~~~~}}}}}}}}}}|||||||}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}||}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~}}}|{|{z{{{{{{{}~z}~{yvsqomlkkjjkkmnpqsuwy{}~~|{yxvtsqpnmlllkjjjjjkkklmmnpqsux{v|zw|}zyxxz|~|{zxwutsqponmlkkkjkkklmnoqrsuvxyz|}~~|vst{}zyxxyz{|}~|wspnqu|zvsqoonnnmnnnnoooopppppppppppqqqrrssttuvwxy{|}~{wspnorx~zvtqpoonnnnnnoooooopppppppppppqqqrrsstuuvwxy{{}~~{wsokgeefjpw}|xsoligeedccccccddddddeeeeeeefffggghhijklmnopqrtuvxyz|}~{wsqpqty|zxwvvuuuuvvvwwwxxxyyyyzzz{{||}}~~{yxz}}{zyxxwwwwwwwwwwwwxxxxxxxxyyyzzz{||}~|xuqonnqu{{xurqpnmmllllllllllllllllllmmmmnnnoopqrrstuvwxyz{|}~|zwtplifddfjotz{xtqnljihggfffffffggggghhhiiijjjkklmmnopqrstuwxyz{}~{xurpopsw|~{yxwvuuutttuuuuuvvvwwwwxxyyzz{{|}~~{yxxy|~}|{zyyxxxxxxxxxxxxxxyyyyzzz{{||}~~}zvspnnnpty~~{xvtsqponmmmlllllllllllmmmmmnnooopqqrsstuvwxyz{|}}~}{xuroligffhkoty~|yvsqpnmlkjjjiiiiijjjjjkkklllmmnnoppqrstuvwxyz{|}~|yvsqpoprvz~|{yxwvvuuuuuuuuuvvvvwwwxxyyzz{||}~~|ywvvxz~~|{zzyyxxxxxxxxxxxxyyyyzz{{||}}~}zxurpnnopsw|~|yxvtsrqppoonnnnnnnnnnnoooopppqqrsstuuvwxyyz{|}~~}{ywtqoljhhhilosx|}zxvtsqponmmmllllllllmmmnnnooppqqrsstuvwxxyz{|}~~|ywtrpoopruy}~|{yyxwwvvvvvvvvvwwwxxxyyzz{{|}~~~|ywvuuwy|}|{yxwuttsrrqppppooooopppqrrstuvwxz{|}~}{yxwwwxxx{~}|{{zzyxxwwwvvuuuuuuuuuuvvwwxyyz{|}~~~}|zywutrqqqqrrrtz~}}||{{zzzzzzzzzzzzz{{||}}~~|{yxwvvy}~}|{zyxxwwvvvuuutttttuuuuuvvwwxyyz{{|}}~~|{ywusqponorvz~~}|{{{zzzzzzzzzzzzz{{{||}}~~|zxusqppqrux|~|{zyxwvvuuuuttttttuuuuuvvwwxxyyz{{|}~~~|zwusrpppqsvz~~}{zyxxwwvvvuuuuuuuuvvvvwwwxxyyzz{||}~~~}{ywusqonmmmoqtx{}|{yxwvuttsrrrqqqqqqqqqrrrrsssttuvvwwxyyz{||}~~}|zxvtsqppprtvz}~}||{{zzzzzzzzzzzz{{{{||}}~~~}{yxvvuvwz|~}}||{{{{{{zz{{{{{{{|||}}~~~}{yxvtsrrrstwy}}|{zzyxxxwwwvvvvvvvvwwwwwxxxyyzz{{||}}~~~}{zywvtrqponnoprtwz}~~|{zyyxwwvvuuuutttttuuuuuvvvwwwxxyyzz{||}}~~}|{yxwutsrrrsuwz|~~}}|||{{{{{{{{{{||||}}}~~}|zyxwwwwxz|~~~}}}}}}}}}}}}}}~~~~~}|{yxvuttsttvwz|~}|{{zzyyyyxxxxxxxxxxyyyyyzzz{{{||}}}~~~~}||{yxwvtsrqpppqrsuwz|~~}||{zzyyxxxxwwwwwwwwwxxxxyyyzzz{{|||}}~~~}}{zyxwvuttttuvxz|~~~~}}}}|||||||}}}}}~~~~~|{zyxxwxxyz|~~~~~~~~~~~~~~~}|{zyxwvuuuuvwxz|~~~}|||{{{zzzzzzzzzzzzzz{{{{||||}}}~~~~~~}}|{zyxwvutssrrrrstvxz|~~~}||{{{zzzzyyyyyyyyyzzzzzz{{{|||}}}~~~~}}|{zyxwvvuuuuvwxz|~~~~~~~}}}}~~~~~~~~}|{zyyxxxyy{|~~~}|{zyxxwvvvvwxyz|}~~}}}|||||{{{{{{{{|||||||}}}}}~~~~~~~}}|{{zyxxwvuutttttuvwxz|}~~}}||||{{{{{{{{{{{{{{{|||||}}}}~~~~~~}}|{{zyxxwwvvvvwxyz|}~~~~~}}}}}}}}}}~~~~~~~~~}||{zyyxwwwwwwxxz{|~~~~~~~}}}}}}}~~~~~~~~~~}}|{zzyxxwwwwwxxyz|}~~~~~~~~~~~~~~~~~~~~~}}|{{zyyxxwwwwxxyz{|~~~~~~~~~~~~~~~~~~~~}}||{zzyyxxwwwwxyyz|}~~~~~~~~~~~~~~~~~~~}}|{{zzyxxxxwxxxyz{|~~~~~~~~~~~~~~~~}}||{{zyyxxxxxxxyz{|}~~~~~~~~~~~~~}}||{{zzyyxxxxxxyyz{|~~~~~~~~~}}||{{zzyyxxxxxyyz{|}~~~~}}||{{zzyyyxxxxyyzz{|}~~~}}|||{{zzyyyyxyyyzz{|}~~~~}}}||{{zzzyyyyyyyyz{||}~~~~}}|||{{zzzyyyyyyyzz{|}~~~~}}}||{{{zzyyyyyyyzz{||}~~~~}}}|||{{zzzyyyyyyzz{{|}~~~~~}}|||{{{zzzzyyyyzzz{|}}~~~~~}}}|||{{{zzzzyyyzzz{{|}~~~~~}}}|||{{{zzzzzzzzzz{{|}}~~~~~}}}|||{{{zzzzzzzzz{{||}~~~~~}}}|||{{{{zzzzzzzz{{{|}}~~~~~}}}}|||{{{{zzzzzzz{{{||}~~~~~~~}}}}|||{{{{{zzzzz{{{||}}~~~~~~}}}}||||{{{{{{zz{{{{||}}~~~~~~~~}}}}||||{{{{{{{{{{{|||}}~~~~~~~}}}}}||||{{{{{{{{{{|||}}~~~~~~~~}}}}|||||{{{{{{{{{|||}}~~~~~~~~}}}}}||||||{{{{{{{||||}}~~~~~~~~~}}}}}||||||{{{{{|||||}}~~~~~~~~}}}}}}||||||||{||||||}}}~~~~~~~~~}}}}}}||||||||||||||}}}~~~~~~~~~~}}}}}}|||||||||||||}}}~~~~~~~~~~}}}}}}}||||||||||||}}}}~~~~~~~~~~}}}}}}}|||||||||||}}}}~~~~~~~~~~~}}}}}}}||||||||||}}}}~~~~~~~~~~~~}}}}}}}}||||||||}}}}}~~~~~~~~~~~}}}}}}}}}}|||||}}}}}}~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~|zyxwvvuvvvvvwwxyyz{|}~vzy}|zwurpnligedbaaaaabdegjloruwz}~}|{{{zzyyyyyyzzz{|}~}~~}}}~~xt{xropqsuvxwwxxxwwwvutsqpnlkjigfedccbaaaabbcdfgikmoqsuxz|~|}}|||}}}~~~~||{zzyyxxwwwwwxxxyz{|}~|xspopu{{wsqponmmmmmmmmmmmmmmmmmmmmmmmnnoopqrstuvwyz|}}xtqopsx~{wtqoonmmlllllllllkkkkkkkkkkkkkllmmnoppqstuvwyz{|~~{wsokgedfjov|}xtpligeedccccccccccccccdddddeeefgghijklmnpqrtvwyz|~}zwvvy~~|zzyxxxxxxxxxxxxxxxxxxyyyzzz{||}~}zxxy}|zyxwvuutttttssssssssssssssttuuvwwxyz{|}{wtpnlmosy~zvsqonlkjiiihhhhhgggggggggghhhiijjkllmnopqrtuvwyz{|}~~{yurnkhffgkpu{}zvrpmlkihhgggggfggggggghhhhiijjkllmnopqrtuvwyz|}~|yvtstw{|{zyxwvvvvvvvvvvvvvvvwwwxxyyz{{|}~|yxxy|}|{yxwvvuuutttttttttttttuuuvvwxxyz{|}~|xuromlmorw|}ywtrqomlkkjjiiihhhhhhhhhiiiijjkklmmnopqrstuvwxz{|}~|zwtqnkihhilpuz~{xurqpnmlkkjjjjiiijjjjjkkkllmmnnopqrstuvwxy{|}~|zwusrsux}~}{zyxwwvvvuuuuuuuvvvvwwxxyyz{{|}~|zxwwxz~}{zyxwvvvuuuuuuttuuuuuvvvwwxyyz{{|}~|ywtqommmoruz}zxvtrqponmllkkkkkjjkkkkkklllmmnoopqrrstuvwxyz{|}~}{xvspnljiijmpty}~|ywvtrqponmmmllllllllmmmnnnoopqqrstuvwxyz{|}~|zwusrrstw{~|{zyxwwwvvvvvvvvvwwwxxyyzz{||}~~{yxvvuuvwy}~|zxusrponnmmllllllmmnnoopqrstuvwxy{|}~~|ywvvvwy||yxzwtuvvvvvuuttttutttuuuvvvvwwwxxxyyzzz{{||}}~~|zxvtrqqqrtw{~~|~~{yyyzzz{{{{{||||}}}~~~}{yxwvwxz|}{z{{{xvtsrrrrrrrrssssssssttttuuuvvwwxxyyzz{{|}}~}|zxwvuuuvxz}~}{zyyxxxxxyyyyzzz{{{||}}~~~}{zxvuutuvxz}}|{{{{{zzyxwvuttsssssstttuuuvvwwxyyzz{||}~~}|zywvuuuvwy|~}|||||{zyxwvuutttttttttuuuvvvwwxxyyzz{||}}~~}|zywvtsrqqrstvy|~~|{yxxxxxwwvutsrrqqpppppqqqqrrssttuuvvwwxyyz{{|}}~~~}|zyxwwwwxy{}~}|{{zzzzzzzzzz{{|||}}~~~}|{{z{|}~}|{{zzyyyyyyyzzzz{{{|||}}~~~}|zyxwwwwwxz|~~~}}}}}|{{zyxwvvuuuuuuuuuuvvvvwwwxxyyyzz{{||}}}~~~}|{zyxwvutsssttvwy{}~}||{{{{{{zzyxwvvuuttttttuuuuuvvvwwxxyyzz{{{||}}~~~}||{zyxxxxyz{}~}|||{{{{{{{{||||}}}~~~}|||||}~~}}|||||{{|||||||}}}~~~~~}||{zyxxxxyyz|}~~}}|{zzyxxxwwwwwwwwwwxxxxyyyyzzz{{{|||}}}~~~~~}}|{zyxxwvvuuuvvwxz{}~~~~~~~}}||{zzyyxxxwwwwwwwxxxxxyyyzzz{{|||}}}~~~}||{zzyyyzz{|}~~~}}}}||||}}}}}}~~~~~}}}||}}~~~~}}}}}}}}}}}~~~~~~}||{zzzyyyzz{|}~~~}||{{zzzyyyyyyyyyyyyzzzzz{{{{||||}}}}~~~~~~~}}||{zzyxxwwwwwwxxyz{}~~~}}|{{zzzzyyyyyyyyzzzzz{{{{||||}}}~~~~~~}}||{{{zzzz{{|}~~~~~~~~~~~~~~~~~}}}}}}~~~~~~~~~~~~~~~~~~}||{{{zzzz{{||}~~}}|||{{{{{{zzzz{{{{{{{{||||||}}}}}~~~~~~~~~~~}}|||{{zzyyyxxxxxyyz{||}~~~}}|||{{{{{{{{{{{{{{|||||}}}}}~~~~~~~~}}|||{{{{{{{||}~~~~}}}}}}}}}}}}}}}~~~~~~~~~}}|||{{{{{{{||}~~~~~}}}}}}}}}}}}}~~~~~~~~~~}}}|||{{{{{{||}}~~~~~}}}}}}}}}}}~~~~~~~~~~}}}||||{{{{|||}}~~~~~~~~}}}}}~~~~~~~~~~~~~}}}|||||{{||||}}~~~~~~~~~~~~~~~~~~~~~~~~}}}|||||||||||}}~~~~~~~~~~~~~~~~~~~~~~~~~}}}||||||||||}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}||||||||||}}~~~~~~~~~~~~~~~~~~~~~}}}}}|||||||||}}}~~~~~~~~~~~~~~~~~~~}}}}|||||||||}}}~~~~~~~~~~~~~~~~~~~}}}}|||||||||}}}~~~~~~~~~~~~~~}}}}}}|||||||}}}}~~~~~~~~}}}}}|||||||}}}}~~~~~~~~~}}}}}}||||||}}}}}~~~~~~~~~}}}}}}}||||}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m36.bin b/applications/SprPoP/dist/MUS/m36.bin deleted file mode 100644 index ef5ec94..0000000 --- a/applications/SprPoP/dist/MUS/m36.bin +++ /dev/null @@ -1 +0,0 @@ -}{xwvtrpomllkjjiiiiiiijkllmnoqrttuwxz|~}~}{zyyxxvuuttttsrrrrrssrrrrrrrqqqppponnnmmllkkkjjjjjjjjjkkkllmmmnopqqrsuvxy{}~|{~}}{zzyxxwvuutttsrrrrqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrssttuuvvwxxyzz{||||||{zzyyz|~~~~~~~~~~~~~~~~~~~~~}||{||~~}||{{{zzzzzzzzzzzzyyyyyyyyyyyxxxxxxxxxxxyyyyyyyzzzzzz{{{{{{|||||||||||||||{{{{{{{zzzzzzzzyyyyyyxxxwwwvutsrqponnnnoprstuuuttsqponnmmlllkkkkkkkkkkkkkkkkkkkkkkklllllllmmmmnnnoooppqqqrrsstttuuvvvwwwxxyyyyzzzz{{{{{||||}}}}~~~~~~~~}||{zzzz{|}~~}||||}~~}}|{zzyyyxxxxxwwwwwwwvvvvvvvvvvuuuuuuuuuuuvvvvvvvvwwwwwwxxxxxxxxxxyyyyyyyyxxxxxxxxxxxxxxxxwwwwwvvvuuttsrqpoonnnnoprstuvvvvvutssrqpppoooonnnnooooooooooooooooppppppqqqqrrrssstttuuuvvwwwxxyyyzzz{{{{||||}}}}}~~~~~~~~~}||{{zzz{|}~~}}||||}}~~~}|{{zzyyyyxxxxwwwwwwwvvvvvvvvvvvvvvvvvvvvvwwwwwwwxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxwwwvuutssrqqpppqqrstuvwxyyyxxwwvuuttssrrrrqqqqqqqqqrrrrrrrrrrrssssstttttuuuvvvvwwwxxxyyyzzz{{{||||}}}}~~~~~~~~}}||{{zzz{{|}~}}|||||}~~~}||{{{zzzyyyyyxxxxxxxxxwwwwwwwwwwxxxxxxxxxxxyyyyyyyyzzzzzzzzz{{{{{{{{{{{{{{{{{{zz{||}~~}|{zzyxxwwwwwwvvvvwwwwwwxxxyyyzzz{{||}}~~~~~}}||{{zzzyyxxxwwwwwvvvvvvvvwwwwwxxxxyyyzzzz{{{{|||||}}}}}}~~~~~~~~~~~~}||{{{zzyyyxxxxwwwwwwwvvvvwwwwwwwxxxxxyyyzzzz{{{||||}}}}~~~~~~~~~~~}}}}}}~~~~~~}}}}}}}}}}||}}}}}}}}}}}~~~~~~~~}||{{{{{|}}~~~~~}}}||||{{{{{{{{{{{{{{{{{{{{{{{{{||||||}}}}}}}~~~~~~~~~~~~~~}}}||{{zzyxxwwvvvwwxxyyz{{{|||}}}}}}|||{{{zzzyyyyxxxxxxxxxxxxxxxxxxxxxxyyyyyzzzz{{{{|||}}}}~~~~~~}}}||}}}~~}}||{{{{{{||}}~~~~~}}}||||{{{{{{zzzzzzzzzzzzzzzzzz{{{{{{{{|||||||}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}}}}|||{{zzyyxxxwwwwxxxyzz{{||}}}~~~~~~~~}}}}||||{{{{zzzzzzzzzzzzzzzzzzzzzz{{{{{{|||||}}}}}~~~~~~~}}}||||}}}~~~}}|||{{{{{|||}~~~~~~}}}}||||||{{{{{{{{{{{{{{{{{{{{{||||||||||}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~}}}}}||||{{zzzyyyxxxxyyyzz{{||}}~~~~~~~~~~~}}}}}|||||||{{{{{{{{{{{{{{{{||||||||}}}}}}~~~~~~~~}}}}||||}}}~~~~~}}|||||{||||}}~~~~~~}}}}}}}||||||||||||||||||||||||}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}||||{{{zzzzyyyyyyzzz{{||}}~~~~~~~~}}}}}}}|||||||||||||||||||}}}}}}}}}~~~~~~~~~~~~}}}}}||}}}}~~~~~~}}}}||||||}}}~~~~~~}}}}||||||}}}~~~~~~}}}}}|||||}}}~~~~~~~}}}}}|||||}}}}~~~~~~}}}}}}|||}}}}}~~~~~~}}}}}}|||}}}}}~~~~~~~~}}}}}}}}}}}}}~~~~~~~~}}}}}}}}}}}}}~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}{yxxy{|~~~~~~~}}||{{{{zyyyyyyxwwwxxxwwwxxyzyyz{||}|}~}z}~}||{{zyxxwvvusrqpponlkjihgfedcccbbbbbccdefghiklmopqstuvwyzz{||}}~~~~~|yxz}yvsrqqpqqqpppoooonmmmllkjiihhggfeeddddccccccdddeffghijklmnopqstuvwxyz{|}~~}}}~~~}}}}}}}}}}}}}}|||||||||||{{{{{{{|||||}}}~~~}|zxwvuuvwy|~}|zywvuutttsssssrrrrrrqqqqppppooooooonnnnnnnnooooopppppqqqqrrrrsssssssssssssssssrrrrrqqqqqqqqppppppooonnmlkjhgedcbbcdfiknopqponmkjihggfffffffffgggghhhhhhiiiiijjjkkkllmmnnoopqqrsttuvwwxyzz{|}}~~}|{ywvtsrrqrsuvyz|}~~}|{yxvutsrqqpooonnnnnmmmmmllllllkkkkkkkkkkkkklllllmmmmnnnooopppqqqqrrrrssssssssssssssssssssssssssrrrrrqqponmlkjihgggghjlnqstuvvvutsrqponnmmmllllllmmmmmmmnnnnnooooppppqqqrrssttuuvwwxxyzz{||}}~~~}|{zxwvussrrrstuwy{|}~~~}|{zywvutssrqqpppooooonnnnnnnmmmmmmmmmmmnnnnnnnooooppppqqqrrrssssttttuuuuuuuvvvvvvvvvvvvvvvvvvuuuutttsrrqponmlkkjjklmnprtvxyyzyyxwvvutsrrqqqpppppppppppqqqqqqrrrrsssstttuuuvvwwxxyyzz{{||}}~~~~~~~}}|{zyxvuttssstuvxy{|}~~~}|{zyxwvuuttssrrrrqqqqqqppppppppppppppqqqqqqqrrrrssssttttuuuvvvvvwwwwwwxxxxxxxxxxxxxxxxxxwwwwvvuuttsrqqponmmmmmnopqsuwxz{{|||{{zyxxwvvuutttssssssssssssssttttttuuuuvvvwwwxxyyyzz{{||}}}~~~}}}}}~~~~~~~~~}}}}}}}}}}}}}}}~~~~~|{zxwvutsssstuvwyz|}~~~}}||||{|{{{{{||||||}}}~}~~~~}|{zyyyyyz{{}~~~}}}||{zzzyyxwwvvuutttssssssssssstttuuvvwxxyzz{||}~~~~}}}}||{{zzyxxwvvutttsssstuvwxz{}~}||{zzyyxxxxxxxxxxxxyyyyyzzzz{{{||}~}~~}}~~~~~}}}|||{{zzyxxwwvvuuuuuuuvwxyz{}~~}}||{{{{{{{|}}~~~~~~}}}}}}}}}}}}}}}||{{zzyzz{|}~~~}}|||||||||||||||||||||{{{{{zzzzyyyxxwwwvvuuttttttuuvwxyz{}~~~}}}}|||||||||||||||||||||{{{zzzyyyz{|}~~~~~}}}}~~~~~~~~~~~~~~~~~~~~}}}}|||{{{zzzyyyxxxxxyyz{{|~~~~~}}|||||}~~~~}}}|||{{{{{z{{{{|}~~~~}}}|||||||}}~~~}}||||||}~~~~~~~}}}}}}}}}}}}}}||||||{{{zzzyyyxxxwwwwwvwwwwxyyz{|}~~~~~~}}}}}}}}}}}||||{{{zzzyyyzz{|}~~~~}}}}}|||||||||||||||||||{{{{{zzzzyyyyxxxxwwwwxxxyyz{|}~~~~}}}|||}}~~~~~}}}|||||{{{||||}}~~~~~}}}||||||||||}}~~~~~}}}|||}}~~~~~~~}}}}}}}}}}||||||{{{{zzzzyyyyxxxxxxxxxyyzz{||}~~~~~~~~}}}}|||{{{zzzzzzz{||}~~~~}}}}||||||||||||{{{{{{{{{zzzzzyyyyyxxxxxxxxyyyzz{|}}~~~~~}}}}}}}~~~~~~~~~}}}}}||||||||||||}}~~~~~~~}}}}}||||||||||}}}~~~}}}}}}}}~~~~~~}}}}}}}}||||||{{{{{zzzzzyyyyyyyyyyyyzz{{||}~~~~~~~}}}|||{{{{{{{{{|}}~~~~~}}}}}||||||||||{{{{{{{zzzzzzyyyyyyyyyyyyzzz{{|}}~~~~~}}}}}}}~~~~~~~~~~~}}}}}}}|||||||||||}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~~}}}}}}}||||||{{{{{{{{{{{{{{|||}}~~~~~}}}}}}}}~~~~~~~~~}}}}}}}}|||||||{{{{{{{{{{{{{|||}}~~~~~}}}}}}}}~~~~~~~~}}}}}}}||||||||{{{{{{{{{{{||||}}~~~~~~}}}}}}}~~~~~~~~}}}}}}}||||||||{{{{{{{{{{||||}}}~~~~~}}}}}}}~~~~~~~~~}}}}}}}}||||||||{{{{{{{{{||||}}}~~~~~~}}}}}}~~~~~~~~~}}}}}}}}|||||||||{{{{{{|||||}}}~~~~~~}}}}}}}~~~~~~~~}}}}}}}||||||||||{{{|||||||}}}~~~~~~~}}}}}}}~~~~~~~~~}}}}}}}|||||||||||||||||||}}}~~~~~~~~}}}}}}~~~~~~~~~}}}}}}}}||||||||||||||||||}}}}~~~~~~}}}}}}~~~~~~~~~~}}}}}}}}|||||||||||||||||}}}}~~~~~~~~}}}}}}~~~~~~~~~}}}}}}}}}|||||||||||||||}}}}~~~~~~~~}}}}}}~~~~~~~~~}}}}}}}}}|||||||||||||}}}}}~~~~~~~~}}}}}~~~~~~~~~~}}}}}}}}}||||||||||||}}}}}~~~~~~~~~}}}}}~~~~~~~~~~}}}}}}}}}}|||||||||}}}}}}~~~~~~~~~}}}}}~~~~~~~~~~}}}}}}}}}}||||||||}}}}}}~~~~~~~~~~}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~}}}}~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~}}}~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~}}}}~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~}}~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|{yxwvutsrqpppprtwz{}~~}}}|||zyyyyxywwwwxxyxxyyzz|}}~~}{zxwusrqpponmmlllljiiihhhgffgikorvwrdivedlostvvvvvvwwwwxxxxyyyyzzzzzyyxxxwwvvuuuttttttuuvxyz{}~}{{zyxxwwwwvwwwwwxxyzzyxvsojfccgmswvrnifcbbbcdefghhiijkklmmmnnnnnnoooopppqqqrrstuvwxyz{|~}{yxxy|~|zyxwwvvvvvvvvvvvwwwwvvvvvvvvvvvvvvwwwwxxxyyzz{{||}~~~|zwuromllmoruy|~~|zwurpnlkiihgggggffffffffeeeeeeedddddddddddeeefffgghhiijjkllmnnooppqqrrrssssttttttttttuuuuuuuuvvvvvvvvvvvutsrpnljhfeeegjnrvxz{{yxvtrpnmllkkkkkklllmmnnnoopppqqrrrssttuuvvwxxyz{|}}~~}||{{{zzzzzyyyyyyyxxxxxxxxxxxxxxxxxyyyzzz{{|||}}~~~}}|{zywusqomkihhhjloruxz{{zywurpnlkjhggffeeeedddddddddddddddddeeeeeefffgghhiijkkllmnnoppqqrssttuuuvvwwwxxxxxyyyyyyzzzzzzz{{{zzzzyyxwvtsqomljiijknqtwz}~|zywutsrqpppooopppppqqqqrrrssstttuuuvvwwxxyzz{||}~~}||{{{zzzzzyyyyyyyyyyxxxxxxyyyyyyzzzz{{{|||}}}~~~~}|{zyxvusqomljjijkloqtwy{|}|{zxwusqonmlkjiiiihhhhhggggggggghhhhhhiiiijjjkkllmmnnooppqrrsstuuvvwwxxxyyyzzz{{{{||||||||}}}}}}||||{zzyxwutrqonmlllmoqtwz}}|{yxwvuttssssrrsssssssttttuuuvvvwwwxxyyzz{||}~~~~~~~}||{{{zzzzzyyyyyyyyyyyyyyyyyzzzzz{{{|||}}}~~~~}}|{zywvusrpomllklmnprtwy{}~~~|{zxwutrqponnmmlllkkkkkkkkkkkkkkklllllmmmnnnooppqqrrssttuuvvwwxxyyyzzz{{{|||||}}}}}}}}}}}}}}}||{{zyxwvutrqponnmnoprtvy|~~}{zyxwwvvuuuuuuuuuuuuuuvvvvwwwxxxyyzz{{||}}~~~~~}|{{zzz{|~~}}|{zzzyyyxxxxxxxxxxxyyyyyyzzzz{{{|||}}}~~}|{zywvvuuuvwy{}~}|{{{zzzyyyyyxxxxxyyyyyyyyzzzz{{{|||}}}~~~|ywusrponnnooopqqpqqoonnlkkjiiihgghhijjjklnoqrrtuxy|}}~{ywuspnkigfdcb`_^^]]]\\\]]]^^_``abcddefghijkklmmnopqqrsstuvwxyz{|}~}zxvutsrsstuvvxyz{||{{{zywvtrpnmkihfedcbba``________``aabccdefgghijklmnoopqrstuvwxyz{|}~~|{yxxxxxyz{}~}}||{{{{{{z{{{{{|||}}}~~~}|{{{||}~~}}{yxvtsrqqpqqrsuvxyz{}}~~~~}}|{zyxwvuttsrrqqppppooooooopppppqqqqrrrrrssssssttttttttuuuuuuuuuuuutttttttttssrrqponlkjihgffgghiiiihgedba`__^__`abdeghjklmmnnnnmmllkkjjiihhhhhhhhhiiijjkkllmmnoopqqrstuuvwwxyzz{||}}~}|{zyxwwxxyyzzyyxwutsqppoooopqqrstuvwwxxxxxwwvvutssrqqpooonnnmmmmmmmmmmmmnnnnnooooppppqqqqqrrrrrsssssssssttttttttttssssrrqqponmlkjihhhiijklllllkjihggfffffghijkmnopqrrsssssssrrrqqppppooooooppppqqqrrsssttuuvwwxxyyz{{||}}~~~~}|{zyxwwvvwwxxyyyyxwvutssrrrrrrssttuvvwwxxxxxxwwvvuuttssrrrqqqpppppppppppppqqqqqqrrrrrsssssttttttuuuuuuuuuuvvvvvuuuuuutttssrqqponmllkkkkllmnoopppoonnmllllllmmnopqrrstuuuvvvvvvvuuuuttttttttttttttuuuuvvvwwxxxyyzz{{|||}}~~~~~}}}}}~~~}|{zyxxwwwwwwxxyyzzzyyxxwwvvuuuuuuvvwwxxyyyyyyyyyyxxxwwwvvuuutttttsssssssssssttttttuuuuuuuvvvvvvvvwwwwwwwwwwwwwwwwwwvvvvuuttsrrqpponnmmmmnnoopqqrrrrrrqqqppppqqqrrsttuuvvwwwxxxxxxxxwwwwwwwwvvvwwwwwwwxxxxyyyzzzz{{|||}}}~~~~~}|||||||}~~}}|{{zzyyyyzz{{|}}~~~~~~~~}}}}}}}}~~~~}}|{{zzyyyyyzz{||}}~~~~~~~~~}}}}}}~~~~~}}|{{zzyyyyyzz{{|}}~~~~~~~~~~~~~~~~~}||{{zzyyyyyzz{{||}~~~~~~~~~~~~~~}||{{zzyyyyyyzz{||}}~~~~~~~~~}||{{zzyyyyyyzz{{|}}~~~~}}||{{zzyyyyyyyzz{||}}~~~~}}||{{zzzyyyyyyzz{{|}}~~~~}}||{{zzzyyyyyyzz{{||}~~~~}}||{{zzzyyyyyyzzz{||}}~~~~}}||{{{zzyyyyyyyzz{{|}}~~~~~}}||{{{zzyyyyyyyzz{{||}~~~~}}}||{{{zzzyyyyyyzzz{||}}~~~~}}}||{{{zzzyyyyyyzzz{{|}}~~~~~}}}||{{{zzzyyyyyyyzz{{||}~~~~~}}}||{{{zzzyyyyyyyzz{{||}}~~~~~}}}||{{{zzzzyyyyyyzzz{{||}~~~~~}}}|||{{zzzzyyyyyyzzz{{||}}~~~~~}}}|||{{{zzzyyyyyyzzz{{||}}~~~~~~}}}|||{{{zzzzyyyyyzzz{{{|}}~~~~~~}}}}|||{{{zzzzyyyyyzzzz{{||}}~~~~~}}}}|||{{{zzzzzyyyyzzzz{{||}}~~~~~~}}}}|||{{{zzzzzyyyyyzzz{{||}}~~~~~~}}}}|||{{{zzzzzyyyyzzzz{{{||}}~~~~~~}}}}|||{{{{zzzzzyyyzzzzz{{||}}~~~~~~~}}}}|||{{{{zzzzzzzzzzzzz{{||}}~~~~~~~}}}}||||{{{{zzzzzzzzzzzz{{|||}}~~~~~~~}}}}}||||{{{{zzzzzzzzzz{{{|||}}~~~~~~~}}}}}||||{{{{{zzzzzzzzz{{{|||}}~~~~~~~}}}}}|||||{{{{{zzzzzzz{{{{{||}}}~~~~~~~~}}}}|||||{{{{{{zzzzzz{{{{{||}}}~~~~~~~~}}}}}|||||{{{{{{{zzz{{{{{{|||}}~~~~~~~~~}}}}}||||||{{{{{{{{{{{{{{{|||}}}~~~~~~~~}}}}}}|||||{{{{{{{{{{{{{{||||}}}~~~~~~~~~~}}}}}||||||{{{{{{{{{{{{{||||}}}~~~~~~~~~~}}}}}}||||||{{{{{{{{{{{{||||}}}~~~~~~~~~~}}}}}|||||||{{{{{{{{{{{||||}}}}~~~~~~~~~~}}}}}}||||||||{{{{{{{{|||||}}}}~~~~~~~~~~}}}}}}}||||||||{{{{{{||||||}}}}~~~~~~~~~~~~}}}}}}}|||||||||||||||||||}}}}~~~~~~~~~~~~}}}}}}}|||||||||||||||||||}}}}}~~~~~~~~~~~}}}}}}}||||||||||||||||||}}}}}~~~~~~~~~~~~}}}}}}}}|||||||||||||||||}}}}}~~~~~~~~~~~~~}}}}}}}}||||||||||||||||}}}}}~~~~~~~~~~~~}}}}}}}}}||||||||||||||}}}}}}~~~~~~~~~~~~~~}}}}}}}}}|||||||||||||}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}||||||||||||}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}|||||||||}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}||||}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~}|zyxwvutsrrqqpppppppppqqrrrstuuvvwwxyz{z{|}~~~}{zxvsrpnmljhfedccba````___```aaabcdeffghjklmmoprstvvwyz|}~~~zxxusssqpqpoppoppppqrrstuvxy{}~~|{yyxwwvvvvvvvuuuuuttsssstw|syuu~}|{zzyyyyz{|}}{wyy|~}{zywvtrqpommlkjjiiiijkklmoprtvwy{}|}~}||}~~}}||{{{zzzz{{||}~{uqnnpu||xtqnlkjjiiiijjiiiiiiiiiiiihhhhhhhhhhhiijjkllmnopqrstuwxyz{|}~}zwsolhecdfjpv|~zvrnkigeddcccccccddddddddeeeeeeeefffgghhijjklmnopqstuwxy{|}~}yuqommnrw}{xusqponnmmmmnnnnnnooooooppppqqqrrsstuuvwxyz{|}~|{|~}|{{{zzzzzzzzzzzzzz{{{{{{{|||}}~~|xusrrtw|}zwurqpnnmmllllkkkkkkkkkkkkkkkkklllmmmnoopqrrstuvwxyz{|}~~}zwtqnkhfeefimrw|{xuqoljhgfeedddccccccddddddddeeeeffgghhiijklmnopqrstvwxyz|}~~|yvspnmmnpty~~{ywusrqppooooooooopppppqqqqrrssttuuvwxxyz{|}~}{yyz|~|{{zyyyyxxxxxxxxxxxyyyyyzzz{{||}}~}zwtsrrsvy}|ywusrqpoonnmmmmmmmmmmmmmmmmmnnnoooppqrrstuvvwxyz{|}~~~|ywtqnligffgimquz~}zwurpnlkjihgggfffffffffffgggghhiiijjkllmnnopqrstuvwxyz{|}~|zwurpnmmnorvz}{ywvtssrqqqqqqqqqqqqrrrrsssttuuvwwxyyz{|}~}{yxwwy{~|{{zyyxxxxxxxxxxxxxxyyyzzz{{||}~~~{xvtrqqrtw{~~|zxvutsrqqppoooonnnnnoooooopppqqqrssttuvvwxyyz{|}}~|ywuspnljihhikmptx|~{yvtsqpnmllkkjjjiiiiiijjjjjkkkllmmnnoppqrrstuvwxyzz{|}~~|zxusqpnnmnorux|~|zyxvvuttsssrrrrrssssstttuuvvwwxxyz{{|}~~|zywvuuvxz}~}|zzyxxwwvvvvvvvvvwwwwxxxyyzz{{|}}~~|zxvuuuvwy|}{zxwvvuttsssssssssssstttuuuvvwwxyyz{||}~}{ywtsqpoopqsvy}~|zyxwvuuttssssrrrrssssstttuuvvwwxxyyz{||}~~~|zywusqpoopqrux{~~|{yxwvvuuuttttssssstttttuuuvvwwxxyyz{{|}}~~|{ywusrppooprtvy}}|zyxxwvvuutttttttttttuuuvvvwwxxyyz{{||}~~~|{ywutrqpoopqsux{}|{zyxwwvvuuuutttttuuuuuvvvwwxxyyzz{||}}~~}{ywusrqpoopqsuwz}}|{zyxwwvvvuuuuuuuuuuvvvvwwwxxyyzz{||}}~}|zxwusrqpoopqrtvy|}|{zyyxwwvvvvvuuuuvvvvvwwwxxxyyzz{{|}}~~}{zxwutrqppoppqsux{~~|{zzyxxwwwvvvvvvvvvvwwwwxxxyyzz{{||}~~~}|zxwutsqppoopqrtwy|~}|{zyyxxwwwwvvvvvwwwwwxxxyyyzz{{||}}~~}{zxwvtsrqpoopqrtvx{~~}|{zyxwvuttsrrrrrrsstuuvwwyyzzyz{zz{zwwxvvwvsssssssppqqrsuuvy|}{}~}{zyxwvtrqomkjheca_\ZXVTSQQPQQSTWY\`cgjmqsvxz|}~}{xx|~zwusrqppooonnmllkjjigfecb`_]\ZYWVTSRRQRRSUVY[^adgknqtvxz|}~~~~}}|{{zyxwvutsrqponmmmmmmnoqsuwy|~~}{zyxwxyz}~}{zxwwvuutssrqqponmlkjigfedcbaa````aabdegilnprtvxyz{||||||{zzyxwwvuttsrrqpppoonnnnmmmmmmmmmmmnnnnnnnnmmlkjihgfeeefgjloqstttsrponlkjjihhhgggfffeeddcbba``__^^^^^^_`acegiknpsvx{}~}|{zyxwwvuuuuuvvwyz|~~~}}|{zywvutsrrstvxz|~~}|zywvtsrqponmllkjiihgfedcbaa`__^^^^^__`acdfhjlnpqstvwxyyzzzzzyyyxxwwvvuutttsssrrrrrrqqqqqrrrrrrrrrrqqqpponnmlkjiiijkmoqtvwxyyxwvutrqpoonmmllkkjjjiihhggffeedddddeefghikmoqsuwy{}~~}}}}~~~~~}|{zzyxxwwwwwwwxyz{|}~}}|{zyxwvutsrrstuvxz{}~~~~}|{yxvutsqqponmlkkjihhgfeedccbbbaabbbcdefghjkmoprsuvwxyzz{{{||{{{{zzzyyyxxxxwwwwvvvvvvvvuuvuuuuuuuutttsrrqponnmlllmnoqsuvxzz{{{zzywvutsrqqpoonmmllkkkjjiiihhhhhhhiijklmoprtuwy{}~~~~}}||||||}}~~~}|{{zyyxxxwwwwwxxyyz{|~~}}|{zyxwvuttssttuvxy{|~~~~}|zyxwutsrqponnmlkkjiihhggfffeefffgghijklmoprstuwxyzz{||}}}}}}}}}}||||{{{{zzzzyyyyyyyxxxxxxxwwwvvuuttsrqqpoonnnooprsuvxz{||}||{{zyxwvutsrrqppoonnmmlllkkkkkkkkllmmnoprstvwyz|}~~~}}}||||{{||||}}~~}|{zyxwwvuutttssssssttuuvwxyz{|}~}||{zzyyyyz{|}~}|{{zyxxwvvuuttssssssssssttuvvwxyz|}~~}}|{zzyyyyzz{}~~}|{zzyxwwvvuutttssssssssttuvvwxyz{|~~}}|{{zyyyyzz{|}~}|{zyyxwwvvuuuttttssstttuuvvwxyz{|}~~}||{zzyyyyyz{|}~~}}|{zyyxwwvvuuutttttttttuuvvwxyz{|}~~}}|{zzyyyyyzz{|}~}|{{zyyxwwvvvuuutttttuuuuvwwxyzz{|}~~}}|{{zzyyyyzz{|}~~}|{{zyyxwwwvvuuuuuuuuuuvvwwxyyz{|}~~}}|{{zzyyyyyz{{|~~~}|{{zyyxwwwvvvuuuuuuuvvvwwxyyz{|}~~}}|{{zzyyyyyzz{|}~~}}|{zzyyxxwwvvvvvvvvvvvwwxxyyz{|}}~~}}||{zzyyyyyzz{|}~~}||{{zyyxxwwwwvvvvvvvwwwxxyyz{||}~~}}||{zzzyyyyyzz{|}~~}}|{{zzyyxxwwwwwwwwwwwwxxyyz{{|}~~~}}||{{zzyyyyyzz{|}~~}}|{{zzyyxxxwwwwwwwwwxxxyyz{{|}}~~}}||{zzzyyyyyzz{{|}~~}}|{{zzyyxxxxwwwwwwxxxyyyz{{||}~~~}}||{{zzyyyyyyzz{|}~~}}|{{zzyyyxxxxxxxxxxxyyzz{{||}~~~~}}||{{zzyyyyyyzz{||}~~~}||{{zzyyyyxxxxxxxxyyyzzz{||}}~~~}}||{{zzyyyyyyzz{{|}~~~}||{{zzzyyyyyxxxyyyyyzz{{||}}~~}}||{{zzzyyyyyyzz{|}}~~~}||{{{zzzyyyyyyyyyyzzz{{||}}~~~~}}||{{zzzyyyyyyzz{{|}~~~}}||{{zzzzyyyyyyzzzzz{{||}}~~~~~}||{{{zzyyyyyyzzz{|}~~~~}}||{{{zzzzzzzzzzzz{{{||}}~~~~~}}||{{zzzyyyyyyzz{||}~~~}}|||{{{zzzzzzzzzz{{{||}}}~~~~}}||{{zzzyyyyyyzz{{|}~~~}}|||{{{{{zzzzz{{{{{||}}}~~~~}}|||{{zzzyyyyyyzzz{|}}~~~}}}|||{{{{{{{{{{{{|||}}}~~~~}}|||{{zzzyyyyyyyzz{{|}~~~}}}|||{{{{{{{{{{{|||}}}~~~~~}}}||{{zzzyyyyyyyzz{{|}}~~~}}}|||||{{{{{{|||||}}}~~~~~}}}||{{zzzzyyyyyyzz{{||}~~~~}}}|||||||||||||}}}}~~~~~}}}||{{{zzzyyyyyyzzz{{|}~~~~~}}}}||||||||||}}}}~~~~~~}}||{{{zzzyyyyyyyzz{{||}~~~~}}}}}|||||||}}}}}~~~~~~}}||{{{zzzyyyyyyyzzz{||}~~~~~}}}}}}}}}}}}}}}~~~~~~~}}|||{{zzzzyyyyyyzzz{{|}}~~~~~}}}}}}}}}}}}}~~~~~~}}}||{{{zzzzyyyyyyzzz{{||}~~~~~}}}}}}}}}}}~~~~~~}}}||{{{zzzzyyyyyyzzz{{||}}~~~~~~~}}}}}}~~~~~~~~}}}|||{{zzzzyyyyyyyzzz{{||}~~~~~~~~~~~~~~~~~~~~}}}|||{{{zzzzyyyyyyzzz{{||}~~~~~~~~~~~~~~~~~~~}}}|||{{{zzzzyyyyyyzzz{{{|}}~~~~~~~~~~~~~~~}}}|||{{{zzzzyyyyyyyzzz{{||}~~~~~~~~~~~~~}}}|||{{{zzzzyyyyyyyzzz{{||}}~~~~}}}|||{{{zzzzzyyyyyyzzz{{{|}}~~~~~}}}|||{{{zzzzzyyyyyyzzzz{{||}~~~~~}}}|||{{{{zzzzyyyyyyzzzz{{||}}~~~~~}}}|||{{{{zzzzyyyyyyyzzz{{{||}~~~~~~}}}|||{{{{zzzzzyyyyyyzzzz{{||}}~~~~~}}}|||{{{{zzzzzyyyyyyzzzz{{||}}~~~~~~}}}||||{{{zzzzzyyyyyyzzzz{{{||}~~~~~~}}}||||{{{zzzzzzzyyyyzzzz{{{||}}~~~~~~}}}}||||{{{{zzzzzzzzzzzzz{{{||}}~~~~~~}}}||||{{{{zzzzzzzzzzzzz{{{|||}}~~~~~}}}}||||{{{{{zzzzzzzzzzz{{{|||}}~~~~~~}}}}|||||{{{{{zzzzzzzzz{{{{|||}}~~~~~~~}}}}||||{{{{{{{zzzzzz{{{{{|||}}~~~~~~~~}}}}}||||{{{{{{{{{z{{{{{{{|||}}}~~~~~~~}}}}|||||{{{{{{{{{{{{{{{{|||}}}~~~~~~~}}}}}|||||{{{{{{{{{{{{{{{|||}}}~~~~~~~~~}}}}}|||||{{{{{{{{{{{{{||||}}}~~~~~~~~~}}}}}||||||{{{{{{{{{{{|||||}}}}~~~~~~~}}}}}}|||||||{{{{{{{{{|||||}}}}~~~~~~~~~}}}}}}||||||||{{{{{{|||||||}}}~~~~~~~~~}}}}}}|||||||||||||||||||}}}}~~~~~~~~~~}}}}}}}||||||||||||||||||}}}}}~~~~~~~~~~}}}}}}||||||||||||||||||}}}}}~~~~~~~~~~}}}}}}}||||||||||||||||}}}}}~~~~~~~~~~~}}}}}}}}|||||||||||||||}}}}}~~~~~~~~~~~}}}}}}}}|||||||||||||}}}}}}~~~~~~~~~~~}}}}}}}}}||||||||||||}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}|||||||}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}|||}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~|xursx~zurpppqsssttttutuuwwxyz{{|}~~}{yxwuuuttux|z~}{wtqqwyvvuvvxz|~}}}~|zwupouqponnnooooonnnmllllmpuwxy{}}|x||{}}~~}}}}}}}}{vxslgeiryzuokiikmortwy{}xrprvz}|yvrppmnomnilnmortuvwyz{|}~~|~zxurplonnopqstuwxy{x{~xrrtz~{zz{~}{xusqomigkjlnquy}~xrqqtx{}}zxurpnmlkkjkklpuz~~|uttx|}ywuuvy|~~{zz|xsrtwxtka^WUVZ`ejlllkjiiijkmpsvy|vogbdir}xpjfcbbaefhjlnopoojilijknquy|{wsuy~}{zywwx||xzpkibcdjotwxxwuttuwz}ywz~zpfd^]_djptvwvtrpomlkkkkmpty|vkd``eltz}}zwtrrstvvwwwxy{}|zzzyx{}xuomot{|yxwwwvtqqpoqv}~~~}ywzxtqmoqv{}{|xtuxvpc\XPRUX^cgijihfedddefhkmpswz}uokkmsz|zxxwwusqonnprwz~|{zxxy{}~~||zx{zqpllnrw{~}|{{|}~~}|sohccdhntz|zy{xsruzytida^`bhmswyzyxvtqrsrtuwy{~|ujd_\_fjquwuspnmlllllkklnprtuvrtsqrrux|{{wxz~~{xwvy}~{vrnnqwzxqfd`]_bglptvxyyz{|{{}}vkfb]^_beiloqsuwwwvutsrrrstvxzy}~|}xoliikpw}~|{xyywwwxy{}||{z{}~xvqqrvx{|}|{yxwvvvwwwwvusssvz~{{~xqnihjmqvz~|zy{|~xrnmpqng][VUVY^chknooonmlllmnoqtvy|{rmiggimsw{|||}~~~}{yvuttuvwwtotpnnnpsvy{|}||||~{xy}wttqtw|}wroqwzrohfegjmpsutuxwxy{}||~|sifabbfjnsvxyyyxwvuuvvxy{}~yunfb^^^afmty||{yxvutssstuxz~}}}|}}|y{}~|{|~||~|xtollouwvnhgbcehmrw{~}wsruwvqkkgefgiknoppomkikiiijkmoqsvx{|~|{tojkjkpv}~}|||}ywttux|~xtrx||yqqomoqux{~~}|{{||}~~}}}}xttx}}tqnijkorvz}~{xvvwy{zwsqqstqhba[\]`dimqsuvwwwwwwxyz|~}}wtrpmllmqw|}xvuqpnnopruwxyuvwssrsuwz|~}~|{}}vojilooofc_[\]adimqsvxy{}~}~|snkeefilpsvxyyxvusqonnoqty~||zwojfcbbdhnv~~}}~}}{ttqrv|}|{z}{{zywvuvw}}xsomoty{ypolijknrvy|}~zxxyvqe`]XWX[^aehjklllkkkkklmopsuwz|~~xtomkjknt{~yurqqtx}|{{||}}{}}zzz{|}}|yuqpsx{|vrsopruy}|vrpquxwqhf_\\^cipw}{xvvvwxwwy{~}xnlgcdeilpsvwxxwzxwwwxxyz{}~xsidb`]\]`elt{~zwtrppqruxz||y}{yzy{}z||}z{}~|zyy{}~{|}{zxuqlgcbdimmhaa\Z[^aejnrux{~}}|}}~zole`_^`bfjotwz||{zxvtsrrqrqrvvwvw}|~|~~yrpmkjjknsz~}~}|{|}~}|xwqrsuv{~~}}|||}}}}|{zywwv~}xspnpsxzsnmhghjmquy|yusonmmnorvwvoge^\\]`bfilnopqqrrrrstuwyz|~{tpkhecceipy|zyzz{zzzx{wvuuvxz|}~~}|{{{{|||zxustw{~~xyxvy{}zvrnjhjouyzsomfdcdegjkqtvyz|~~{yxxy{~yttkjijloqtwyz{{{{{{{{||}}~~~z}zwuvz~wwplifecccehnu|~}}}||~|}~|xu{~}||{ywtplgddfknojjieffilptx|zuqnlmpu|}yxqnlkklmnoonprqqpqqqrrstuwxyz{x}yvvrqooopsx~}{yz{~}zvqrprvxwrtsqsux{}~zupmlosvvqrokllnpsuxz|}~}yuqmkjknsx{{troihghjmpruwyz{||||}}}}~~}~yww{}zxvtrponmnrv{~~{zyyz|~~{~~{zyz{|~}zyz}}}|}}}xsmgc`cijideb__`behlosvy{~~zvsrtw}}zwpomnoqsuwwxxwvtssrsuwz}{wttv{~{xxqolkkjklmpu{{xuuwyzwtxvx|}{ytwz{~}|zxuqmjhimqssnpljjkloqt{|ytomnqvz{ytpledbcdfhjlnopqqqrrrrsstuwxyz{zz}~ywtsrqqqrtw|~}}~~~~}~}{zxxwwvusqonptwyusvrstw{~|{wsnjghkmnjega`adhmrw{~{vqmjjmrz|{upomnoqsuuw{z{|||||||}}~}|}xvrokhgffefgjmrx~~|{|}~}z{yx{{}~~{ywurokfb^]_cgigdf```acfjnqux{~ytpnpt|{ypjgeegjnrvy|}}|{yxvusrqrusuturuzx{{}~~{xvw{~{vusrrrrstux{}{zzz{}}}{z{|{zytsrxy{~|x||}}zuqlhfjoqtqpqllklnqtwz~}xrmgcabhpw|{wwpligghikmoqrtuuvwwxyz{|}~~|}{urnlkjjkmpuz~}|{zzz{}~~|{zzyyxwurqqty|~{~{}~}zvspmjgfgjosuqqoigeddekmoruwz|~ysnklpy|yutsstuwxz{||}}}}}}}|||{zzyxu{~~}yuqopsx{zxztpnlkkkkllmpsx}}{yy|~~~{||xyz~|zxurokfb_^`ejmjmmiiiklpsvy}~wpidbdkv~ytqnmllkoooppqqqrrrsstuuvwwwu{||~~~{z{~zywvvvvwxy{}~~}}zxutsonnqtwwuxvttuxz}|xtokhhjortqsrnmlmnoqrtvxz|~ztnidbchqx~}xvrqqqrtuwxz{||}}}}}}|~~yuqprw|~|ywvtsrrqpptwy~}~~|}}~}}|{yxvvw{~~|~{{z}{vpje^^_`dgheiebbbcehjmpsvx{~}wqmklrz~{wvtttttttttssttuwy{~~{y~{{zy}||}}~~~}{xurpprvy|yyztrpppqrstuvx{~~|ywsqopsuxwx}{}~~y{|xyy||zwtpmifeeimqqnrnlkkkostxz}|unhddgoxzz~{vtrqqqrpqrtuwwxyzyyyxvuusqqpmmtsuvy}~xqnlmmnoqrtwz}~|{zyy}}||{ywtrpnlkihgfhjotxwz~zvols|xtw||~~zxwz|}|z~zyyxwsoow|xrppqrttwzz||}~~|~xspqv|}|xvuqt{|wspmkjjow~zwrsvssstuvy{~|ywtrnje`]^clsuusrrqruvwxww}{|}}~|zz{{}{yzzzz~|zxvusrpoljhggjnrvuvxsqonmmmnnpqrssttsrrrtx~~}~~}~|yuqnoposvz|}xxwqporv{}{{|}{wvvrrsux{~~}|}}}|{zyxxxyz{|}~}|||~zuonlknqtrtwrqooopqrsuvxyz|}~}yupkgefjpw{~~|zzyzz{|}~~~|yvtqommmmnpsvy}~~zwvrswtutttttsstux{~~|xusqrtx}|yutuy|}xuoklhfdcccdefgikmortwy|~~zyx~|yvusrrqrrstuvxy{}}{zz}|yyz|}}|yuqmjiiknsw{}~}}|{{|z~|zwtsuy}zxvvvwy{}zwuuwy|{~{ywusrstuwxyzz{~~~~~{wspoquz}{xyvrpnlkjiiiijjklmnopqrqy|}}|}zuqnnqv}}yvrvywyyzzzz{{|~~zzxsplkknswyy~{xvtsrqrtvy}~~~}ywtqonnoquy~~~~~~}{ywspljjlqw}}zwtrpnmlklmpsvx~}|z~~yrmifefkpw}}xtotvw{}~}{zzz|}~~|zvronnqv|}}|||}}~~~}zyy{~}ytrqnorvz|{}yxvvuuvwwxyz{|}~|vqmlmqv{}}yvsqomkkjjklnprsuvvvutrqoovvx}{ywvwz}zyyz}z}yxy{~}~}ysomnrx~|zxwwwxyy~~}}|{zzyxxxxxwvswwssqqrty}xtqpqtw{||{xuvwuvvwxyz|~~wrmmqrw{|~zvrokigeddddefghiklnoqrttvuw~}~~|{yxvtrqpopsw||xuronnoqsttsqpprvz|~|{{|~~||}|yvtrstx{~~~}{yxwvuutssrrqqptvuwxz|~|wspprw}}|{zyxwvvuttttttuuuuvtv{yz{{||}~|ywwy}~|zwtromkjijknrw{~|z{{}{~}}}{xuuu|~ytokhfgiloorvqpnnoqsuwy{|}}}}}~~}{{{}~~}|{{{{zzxwutrtutuuvvwvvsw{xyyyyz{{|}~}}}|zxuvxvvwxyz{}~}||{{||}~|yvsponprvz}{{xvvw{~|xurponnnnnnoopqsux{~}yvsrtw{}~|zywvutsrrrsuwy{}~}}}~|{{{{||||{yvtrpoopqvz{}}~~|~}|{zzzyyyxxxwvutstvy~~||{}}|}}|}~~vsromnnopqrrrrqqpppqqrstuvwxxx{wxwxz}~~~}}}||{{zzyyyxxv|~}~}~}{yxwvutsrqppopqtw{}|y{|z{{}~}zxxy{~~}|yxxxz{~~zwspoorv{|wtrsuz~zywxyz{|{zywvywxy{~|zwusrqqppqqrsstuvwyyyzy}{ywuttuwy{}}{x|~|}~~|zxvstsonkjijlqv{~}|zyxxxxxxyzz{}~|upkjkotxz}zvspooprux{}~~}zwtpmjiiknqrz}|zyww{yyzz{{|||||~~}|{z~}{zxwutsssuwz}}}}{{zzzz{{{{||||{zyxvtsssvxzz~~}zyxyy{}~|y||}}~xurqsuxzy~{vrmiebaabdhkortuvvwxz|}|||||z~{{zywvtqonmlmoqux|~{y~}}}}}~~~~|zxvtrqoooprv~}{yvtqommorw|~}{zxvuwwuutttuvwy{}}|zzz{~~}}|zwvuuvwxyyyyxvtsqpqrtw{}zywvvtz{z|}~{yvspnlkjiiijklmoqrtuy~}{ywutsssux{~~||~~~}{ywutstwy}}}||||||}~~~zuqopruvy~~~~}}}}||{zywzzwwvvvvwxz{}}~{}|zyxy{}~{zyyy{|}~~~~}|yxtuxttsrrstux{~}|{{zyxwvutttuwz}}}|{zzyz~~~}}|||~~~}}}}~~{|yvuuvwy{~~|zxvuvxz}}}zxutsrrrrstuwxz|~~zvsqpoprtvvv}|}|zy|~}~}}{zxwutru{z}~~~}|{zyxwvwx|}|{zz{}~~~}zwspnnoqty~~~}}}}~~~|xutuvxx||{zyyyyyzzzzyyxwwvvuvwxz|~~|yvsrqqqqoosnmlmpswz|~~}|zyxxy{}~{}|}}}}|zxxxy{}}{}~~~~~~~~~~}|{zxvuttvy|~|zywwvw{z|}}}}~{zz{{y|~|xsqnljihgggghikmoqtwy|~~}~~}}~}zvrnjhfffhknpsuvvww~}{z{~|zxvtrpnmlkkklorw|}{ywvvwz}|{zz{|}~~}{zxy|zzzzy{|{z|}~~{ywvutssrrsvuuvvvwwxxyyzz{{|}|||~}{wurponmmnnoooonljgekkknqtx|~~}}|{zxwuuttuvwwxw|}~~|zwvvwz|}}|{zzzz{{|}~~~|}~{zyyz|{yvusssstvwxz{{|||||{{z{}{zz~~~~~~~~~~~}{zz{|}|~}|}~~}~~~}}|{zxwuu{{z||}~~~}{ywusrqqrtvxy{|~~~~~~}|zxwuuvxz}~|zywvvuuuttttsstuw{~~~~~~~}{yurpopruwx}}{zyxxwwwxxxyyz{{|}~}zwuttuvxw}|}}~~}}||||||||||{{zyy|~}}|{zzzzzzzzyxwusrqqrtty~~|yurolkkmptvz~~}}~~~~}~~{yxyyyxzyvtsrrrstvwy{|~{xvuuuutyxtsrrstuvwxxxxxxyyzz{|||}}}||z|}{{|~~{xtqnkjijkmpsuwy{|}~{wtrrsuy|~~|zywwvvvwwyz|~|zxyy{|}zxvtrpoqpnnnnnooqrtvwy{}}~~~~}{zxwvuuuuuuvuvux~|~~~~|zxvuttssssrqpomljjjlnqtx~}}~~~~~~}|~~~}{zyxwwwwwwwwxxxyyyyyy|}{{y~}}~~|{yxwvuutsrqorwtutuuvwxy{|~~|zxvtsstvwyz}}{wtrpooqsvy|}{xvvv{~|zxwvvwxz~}|{yxwwvuuuuvwxz{}~~}}|{zywvuttuwy|}~~}||{}}~}|{zxwuuvxz}}{yzzxwvwxy{|~|zxwvvvvvvutsqnkgecceinsw~~}~~}~~}zxvvvvw~}|{zzzzz{{{|||||||}}}~~~~~zxvvvvwvx}yxvvwx{}~}||||}}~~~}|zxvtttuwy{|}{zyyyyzz{||}}}||{zzyzz{}~|zwtqnlkklnprsyyusomkkkloruy|~{xtrpnmmnrrsuwy{}~}}}}}{|zzwsrqrrsttttsrrqqqrrstvwxyzzz~|{zxwutsqponmmnorux|}|zxvusrrtuwxx~}yxusqpppqrrsttttssrrrrtux{}{ywwvwxyyzzzzyxwutsrqrvuvwwz~}}}~~~|zyyz|}~~~~|zywvuttssrrqqxyyz{}~~}|{yxwutrqponnnpqsu|}{ywvvz}~}{zyxxwwwwwwwwwuy}zzyyyyz{|}}~}}}~}|zyxxxxy{|~}{zz{|~~}|zxvvxusyxvvuuttttuuuvwwxyzz{{{{{|~~}}||}~}{yxwutrqpoooqsvz~}z}}|{{{{zzyyxvusrqppqrstvwy|~{xutssuwy{~}}{|~|{ywvttuwy|~}|{ywuspnllkmoqsx~~}}}}}~~~{xvtrrqqrrrrsstuwwx~|ywutsssttwyxxwwwxy{}~}|yvuuvx{~~{{{xwwwyz{|zxwvuttttuuvwxxyyzz{{|}~}{yxxyz|}}{ywutssssstuwy{~~}|||}}}}}|{zyyyxy~{ywusrqpponmlkjiiijlnqtz~~~~}{ywvuttttuvvwx|~}}~~}|{zyxwvusrqpoooopqsw}{{}~~~}{{zyyyyyyyyyyyyyzzzzzzz{~{xvsqnmkjiiijklnpqstttsrpuwvwxz~|}}zxz}zzzz{{}~}{{|yxvtrqonnmnnryyz{||||{{zzzz{{{{{{{{zzz{|~~~}{zyxwwvvwwxy{}~}{zyxxwwwwwvvvuuttsrrqqppvyxzz{|}}}|{zyxwwwx|}}~~~~~~}}|}|zyxwvuttstuwy}~}{zyxwwwy}|zywvtrpomlkkmoqsv}}|zywvuuuuuvvwxy{|~~}|||~~~~~~~~}|{zyxxyyzz{{zywutrqppor{ywwvvvvwxyz|~~~~~}}}~~}}}}~~|zxusqomkkklmopwxvvttssstuvwxz|}~~}}}}}~~~}}zxvttsttuvwxyzz{{||~~|yvtrqqrstx}zywtrpommlmotvxz|~~|zyxwwvvvvvvvwxxyz|}~{ywxyxyz{}}{zyxwvvuuuuuuuuvvwwxy{{}~~|zxwvuttsssssttuuvwxyyzz}}xtqnlkkmortwz|}}|{yvtqpqpnnovyy{|}~~~~~|zxwuuu{~}}}~~|{zzyyxxxwwwwwwwwwvvuy~|||}}~~}}||{{zyyxxwwvutv}||~}{zzzz{|~~~}|zywvutsrrqrxxwwvuttvutuvwy{}~}||}}{zy|}~}|zyyxwwvvvwxz|~}{zxvtrqonmmmnoqtx|~~~~~~~~~~~}}|~}{zyxxyz|~}zxvvzxvuttttuvwxyyzzyxvsrrolkjkmptx|zwussstvw{~~}}}}~~~|zxvtrpommlmmopsuwy|~|{yxxxxyyz{{|}~~}zxvutuvxz{}{ywvuuvy~~}|~~}|{zxwutrponnnprtwx~~|ywvuuvxy{}~~}||{{{||}~}zxyxwxy{}~{yxvutttttuvxy{}~|zyyyyz|}{xurpnlkjjijjklnpruwz|~~}}}~}zxvtsstuvxz{|}}}}}||~~~}}{ywvvvvw{wsnkhedcccemopsvy|~~{wtrqpqqrttx{wussuuvxz||wt{{}zyxwvuuttuvxz~{xurqonnmmmnoprux{|tsy{~~~~}|{z{~zursx}~zuqonnoqsvy|~~~}~~~}||}{}|xtpkhhky}yvtsrqpoooquvwxz|}~~~}||}}{{zzzz{~~}|{{z{|}~~zxvuvy~~ytpnnu}~zwurqomlklmjihhhikmpsz|{|{{zzzyyyzzzzzzzzzzzyyyxxwww{||||}~}{yvtrqqrsu{~||||||}~~{wtpnmmt||yvtttuvvwywurpmlkkltxy|}~}}||||||}}}}~}zxvwvuuvx{|zx{}zzyyxxxwwwwwwxxyyzz{{||}~~{wspmpuw{|xuromkkkjiijlnpvyxxxxwxxyyz{{||||}}~~{yyy{|~}{ywvvvvwxy{|~~zwssussv{~|||}~{wsomkpsrtvxyzzzzyxwvutsssssttuwy{~}{{{xwwxz|~{yvy|z{|}~}|{zyyxxxxyz|~}zz~{{|~|xtpmkihhkoqsuw~~}|zyxxwwwwwwxxxyyyyzzz{{{||}zwusrrrsuwy|~}|}~~||~yusruy~}}}}|{yvurmjlomoqtx{~~|zxvtrqpppqrtwz|}|zxwvwxwwxz}~}|zyxvtsrqqpqrux{~{yy|zwwwxz}}zwtrqppquuz}zywvtsqqpqqstvx{}~~}}~||{yyyz}~|{zyxxxxyzz{}~{yxwyz||~~ytolklpu{~|{~~yxvvwxyy{|||{zxvsqnlkjklnrvz~~|{ywutrstttuwz|~~~}|{zyxwvvvwy{}~}}zxvutuvx{}zwusrrrsty}|{zyxxxwxxxxyyyzzzzzzyyxwvuv{}|~~{xxwuuvy|}{yxvuuuuuvwxy{}~|zxxy|ztpmmnrv|~}|}}~~~}~{xvvvvwy{|~~}|zxvtrqqrsux{~}||{{{||}}}||{|}}|}}~}|{{zyxwvusrrrrtvx{~}{z~}zyxxxyz{}}{xvutttw|zyzywvuttttttuvvwxyz{{|}}~~~~~|yvtrqsvx{}{xwvutttuuvwyz|}~}}}}xsnigfgkqw~{xvuuuvxyz{{zywvsrpooprtw~~|zxvuuvvuvx~|ywusqppqrtv}~|{zyyz{}~}|{{{|~|zxwvvuuuuvvvwxxyyyzzzz~|zyy||zxvvvvwxz{}~~}}~~zvspoprv{~}~}|{xvtrqqpqrx|{|||||}}~~~}|{zzz}~{yvsqponoqtw{~}|{zzz{{}}zwuuusrrrstvxz|}~~~}|{zyxwwwxz~}||{{zzzzzzzzz}~}~~~}|zxuromllnrv{}zxwvuttsrrrrrsuwy~~~~~~~~~~}}|{zyxvutu{~|zyxxxxz{~~}|||||}~|{yxvuttttuvxy{}~}|{|}||{{zzzzzzzyyxwwvy}|||}~~}{zxxwwwxy~}||{{zzz{{||}~~|zyxwwy}~}{yvspmkjkmpuy~~|zzyyzzz{{||}}}zxvuttsttuwxz|~}{y|~||~}{yxwvvvwxz}~|{zxwwvwwxz|~~|zyxwwxzzyyzzz{{{|||||}|{ywvutsstuwz}~|{zyxxwwwwwwxz~}}|}}~{xvuttssrqponmnprv{}{xvtsstuwy{}~|{zyxxyyz{|}}~~~~}|zywuxzwwwxz~~|zyyyyz|~}{zzyyyz{|}~}{zw|usqnmommmlrpmmmkkqrruw|}|}}~{zyz|}~}}|{zywuronotw{|u~}}{xzwxz}zwsokgefkonqnmnoqrtz{~{qgcnxplmnpruvuz{}|ywy~{zzzzzz{~~|~}z{~}{ywsnlnutqolmmpptxz}~wogbbhv}xtprsu{}}xuy|vqliijlmmmmpw~{ywrvtvyz}|yusy}zvutrnjjqtomhggfgjjlnpruvy~|zvvx~wuyvqmihhhhfda`cjpoonllhilnpruwz}}xplihkjklnprsuuy|}~}}wutwz{vsoljlqy|ywqlc`c`bcfimqtx{}~}zvw}yvyy|}wnggmnqtux{ywusrqrsttvzz{x|}}~~xst}~{zxy{}yqpsy|vnhfgeefffghijlnoomjkswvupstvy{}~wuqommot|}vnmtx||vvtuvxz}}tkcafq}~xusrsuwz{{wrnqtolecfgjmopuwy{}}}|||}}~~~||zxutvy{~vokortvomjfffghjlnpsvy{~}vmkkkpw~vqrxurmfggggeghhjknpsuxyw{|zyy{~~{|}~~~{|wqnigfffghikmortwww}~|wrnllou}{wspqssvwyzzzyxvqqronmns{~{wqmnsspmjkmpsvy}ypjluz}vrnlkjjkllnoppmqpstuxy{}}~~zvyytrnow~~~||ywyvspmmqwywvqnjfbakljieghknqtvy||vqljmry~|utvuvwxyz{{|}zy~|{yyuplihinv|y{|wnecegb`[Z\_cfjmpruwyz|{~}{xuspnljihkntu~}yvw~{ytttuuvutpmnov}{|~|x{}smhinkhc^^]^_`acegilorsvyw~~z{ywwx{xuttttspnnjjov}~|z~}|tqrwx||rnkjkmoqsuvwwwvstpruuxx{zy{xwwy{z{xxxz|~}yvw~zwvrnkkovzywqsqqrstuwxz|}~~|yx}skgjllnmsx~~}}}|}z{yx{yxwyxyyz{|~}|xsqvtsssxxzxvvvxy|}~ywvxzzxvssrsstuvxyz{yvtx~~~}yvz~vqlhda`adlvxqqpkigloqsqppqrrrqtuuwtuz{~~{xsnjjoy~|{{zzzyvrnksvutoonossvwy|~~~}}}|||{{{zyxwx|yxytqlknnnlhfeeedhjlnpppwx{}}wvvy}xuyyzxrpnmpqrtuwy|||~}}}|yvuw{}wyz{{xwutpqpnmlkjglnqux|}}|~|zxvtspqu|}zxzxxxxyz{|~~~}}xy{}xv{}|}~~xtxy{||}wvussrsrrnpsqssuvwxz{|{~|ywx~~y{{|~}wwzyytppsw~~zywvz~xux}~{xxxxxyyz{|~}rmmlihfekt|}}~|vsvwvusrsvy{}}{xwy}~~~|{yzxwvuvy}}{{~{snmrrrokkklmoprsuwz|}~{vtonllkkloqt~|y{}z}urqqqrsssssrppqu{|}~~~~~~~~~~wsuttrlkijklmoprtvxy~{urqrsuwyzzuwurpnnosz}|}|wsppsvtzywyz{}~~~}{xtruzz|yxyz|}{wwy~~~{xusrrqqqqqpptuuwtt{{|~~}~{zvrr}}zwwx|z|~xwutstrruuutspoqwx{zwxy{{~|utussonquz~}z|{zzzyuz|{}~}xux}{}zvvuwy||}|snkjlpv~~{wqlkoqstqstwyz~~ywtsqomlklru{~{|~}|tlknruvxutstvwz|~~}~}||zyvtrqrx~|xspsvwxrqppqpruuwxz{}}|zzyyywvyxxxxyxxtuz~}~~womjnsuxxuuuwxz|}~{~}||}~~|{{y|zvqllqv}||}~|{y{{yzz{|~xvuvvwxx|}~xtsuxxzvsrqqqsstrsxwyz|}}{zyxwvtronq{}~{z{~{x{{{wvvvvvyz{}zyzzxuux}|ywvvvvtrwvwyz{}~~~|zyyz{}}}zvstuuskihjov||ysuuyxttstsvwwwxyy{|~~|upopssvuux{}zxvv{}}zyyyz|}~{||zyxxvuxuusvz|{yyyxqmjhjkklmoqsux{}~}xvw|}zxusrqppnsx{zyx{~{zyz{}~~|x~}}|ywvuutwzy}|wywwx|{xuwxwxwvutssstvxxy{vuv{}}~~|{zwy|}~}}}}}}}}|{yusov}}~}|||~~{yy~~zwvw{|~{xwvvuuuvvwxyzzx}}|}}yvwy~|wqqtx~|zvrqs{}}}~~~zwux{~~wutttuvuy{ywqohbbciow~~|xy~}zwwx{}{yyyyyw|}}~|zyxxz}|}|{{{zxuqoquwzurrrrssswz{}}}vqnmlkjjiijmpu{~|zuqnmptwz|ywwvxxyw{~}~~|zwttrpox}{xtpquvzwssrsstxwy}}vpljijkmoqstuuusrolqqsx}||}~yvtttutu|z~{yxy~}||~~{xutsvtutwxxz{}}~{{ywwwxxyyyz{|}}~}{~}{yy||zywwtrvuxwxyz{|~~|x}|{~}{ywtpljnsz~~|{}|{zyy|{|}~}{yx}~|tqnnoprtwy{}|~}|{}wsttxwstvy}}|x~}ywv|x}~}zy{}{uqrqrux|}zuuvx}}xtqppqrsuvxy{}|xtpnmoruz|}|y|~||||||{|~{y||yzwwvvvvutwvwxy|zxwuw}~xusttuuvwxyzz{{|{y~}~~}zvsqoprwz~{wsmkjgmot{}}|xvwx|~}{yz{|}~{wutsssttuuwxyyzx}}|~{yxz}|y{wuyxwxwwwwwxz||y|z}}zwxy|~}|}}~~{ysonpv~~{yxwvvvvwxxzz{y||}~~|yxvuvx|zwrttrttvxz|~~zupllqy{||yvtsv|}zx{~~ytrrtw{~|}}zxvuuw{}}}z|~~{yz{}zwttvtuwz~}yuqvw{|wvtuuvwyz{{zxz~|xtrqponnmmmoqtwzz~~|ywvvxyz{yvtrpuuvxz|~|wwvy~wvrtwwz{||{zywusroovx|xy{~~}|{zzxz~~yvv||xxwwwxyyzz{{{zytz|z|}~{xvsrsuz}}zywurwxxzz|}~}{|~~~~~{wzwvxz|~~~}{{{{{|||{{xw}}{zyyyyzzzz{{zyx{~}~~~|~~}}|{zz|{xtrqwy{}|zyuy|{}}}zwtrut||xzxz{}|ytvusuy~{wpniddhmw{|~zvuy~||{zzyyyxxwyzx~|{|||||{|}}}~~~~~~~}{xvtvwy~zyyz{|}~~}|zurprsxxrpmmlmorvz}u{~}|~{ywuutuvwxyz{z|{|xvspkkihkory|}~~zz}{zzzzzzzzzz{{{{|{zvx~|zvrnlpxyvvrrqqqpmlomorv||tqpoooruvy{~}}yuuwz}zxusqqrtx|}yyxyyxwusstx||~~}z}~|vvuutsvuuuvy|{uw{xsrrtuvwyx{~yrommllkkkjkmosw{}vqrsuw{xusqppprtvxyw|}wt{|yxxwvtqnkhhjox|{yz{}}yy~|vuttsswuuuuy~|xz{sonmnnopqrtvxz}~wpnlnqswwtsrrtx{}|{{z}~}}||zz{~|w{~ztsutttttttuwy}~|ww~|zusssstttuuuuuyzzysnoxytqoljkgfffhlqxyurqruy~~~~~~}}|{{zz{|{xy}{utqsuxyy{|}ztvz|xvpnmmmmnnopqsvzyx}|zwvuuuvvwxy{{}|wspljkilmqutvxz}{{{zzzzyyyxxxwwvvvxy}~}{zxtpmpxzxwssrrprrooopsv{xtrqptuvxz}~yy{|~z||yxutrrqrsuy}~yxwvtsrrsvy~}}}}y{}|~~{x{|wvusuwuuuuvxz}zzz}{uuuvwwx|}~}z~zwpnmmllllkklmort{}}{wvwzwxtqpnnmnnpqsuvuz}~}xuvy~~zwwvurpliggjou~}|}}~~zwx|zutsruvuuuyy{}~{xrpooppqrsstvwyz|~~{}{~{wqnpquxutqpooprt|}|z{~|{zywwx{|{yz~zyvtsstttsstuvx|{yz}|vutttttssrsuttw{{~}}}zwutrw}{ysonkmljigghjnsx~wutwy}~}||{|{{zyxvutux}{||uwuvzyzz{|}~~~{xusyyxvponnooppqqrsx}|}~zxwwwwwxxyyzz}xrnjllmostrrqsux{~~}|zzyyxxwvusrrsux{~~~~}|{zywtqnpwyzzutsvutsrqqqsvz|xuvxwxy{}|wty{xvuttssstux{~zxusrrsvy~~}{}}|}|y|}}~~}}|~xvuxwvvvvvwwxz||||{zyz~~zwz|zztqonnnnnmnnopty|~z|~yywtrqpooopqsuwxzx}||wux~}ywusoljhjkpwz~{ww|~~xutwvuxzyz{}|}}}vtrrrrstuvwyz|}{vwzxzvtxywusqponoosy{|}}{zxwvxz}|xx}~xwuuuuttttuvx{}~zz~ywuttssqtututy{|~}{zzz{|wux{zysonpmlkjiiikoty{z{~}||}~}}|{zyxwtsqqrx||zwyzyzz{{|}~|{}{}}}wtswyyxrpoopppppquwy}|ywvvvwwwwxxy}~~|~}zsllnnqusqonmnprvz}~}}{yywvusqonmnqty{}~{}~~}}|{zyxvsonpwy{yvxuutsrqpoppv||xyxwwxyz||}}xwwzvsrqqqqqqrtvy|{urpqsuz}{zwvxwwxxxvz{{}~|z|}vwwuuttsrqppqtx~}{{{xz|{}}zvty{zyronmmlllkkkkouy}}yy~~xwsponmllllmnprtvwv|}~~~yutx}}ztqnlkkllqrqstw{}xuv{}}{tutswwwwxyz{~{x{}||vsrqrrstuvwxxyz~~ztqtz{zwtrpomllllptvz~~~yxvuuwy||~yx{ywuuutttssstuvwz{}|}zy~ywttrrutssruxxz{}~}|{z{||xspsvwxqrpnmlkjiijlpty~~|{{{|}|~}{xwtsponprz|{}{{{{{||}}~~~~~|{wy}}~{wtrvxz{utrrqqpruuvxz}|yxxxxyyyy|~}yxywysssuwutqpnnnoqtw{~}zxvtrpnmmprx}~~}y{|}}~}}||{{zyxvsppvx{zxwvusrqpqvw{}|{{{||~}~zwtsrrrssstvx{~|wuuw|}|{ywxyxyyzzzx}}}~{z~~~|zwwvutrqpoprw|~{wtwy{|vtrqppoonmnvvx{~~{vtrppoooopqrtvwyyx~~|zvtuyz}ytqoooqutrqqqtw{~|~~zwuyy{|yxywwwxxxyz|~|z|}{ywvvvwwxxyyz~}~zvssv}~yurqponmmnrrux|{ywwyz~|~zz~~zyvvuuttsrrrrsux{|{{xtvvutssrvwwxy{|}{|~~|{{{|}~|xsonqttxvsqponmlllmnqtz~|{z{{|}~~{|~yvsrpooqqx~|~~}|{{{{{{{{{zyxxwxv{~}||}{yusrvwz{usqqptttuuvxz}|zyyyyyyy}}}~~~|wssuu{{{xutqpnmmmnoqtwz}~~}yvtqonmnoqwyxxxwvrw|z{{|{{{zyyxwuspnpyz~zywvutrqoqsruwz}~}|{{|yzzz}wtrqqqqrrstuwy|||}}}z{~}{ywuuwvvvwwxxxu{{{}}~{yx|{ywtsqpommmosw~~~yz~|xutwxz|vsqpoonmmsttvx||wsqponnnnooprsuvxwx~}|yvsqruuzvsrruwusqonmoqtx|~|yy{z{~}yvtww}|ywwwwwwxxy{|{y{||zxwwwwwwwz}|~~|~~|{yxwz|~{wsqpnnmloqqsux|}{yz{|~{z}~}{xwvutrqpooorvz}{}~|}ywvutstwvwxyz||}{{||{{|}|yvroloqx}wvsrponnmmmnty|}|{{||~|{}~~xtpoooqv{zzz{|~~~}}||{{zyxwwxz|~}|~~|zwtruw|~yvuwvuuvvwwy{}~}|{zzz|~~ytpqt~~zvsqponnnopsuy|}ytqonnosxxyxxxwwvv|}|}}}}}||{{zywuutsxz~}yxvtrpsqpqrtvz}{wwx{zwtsrrrrrstuwy{|xwz}~|ywuvxvwwwxyzzzx~~~|xwy~{xtqomllmpu{|~~|{{}yvtvx|ywtrpoprtttvwz|}{}|wtqpoonoopqrtuww{}{~}{yuqnnqszyx{zxvspnmllnptw{~}{xyyy|~~yvsw~}zxwwvvwwwxyz|~~zx{}~{yxwwwwz{{||}}}}}~~~}|{{||~{yyz{yuqonmlmpooqrux{}~|~~}}}}}~~}{|zz}~|zvusqonlllnqv}~{z||~{xvtsruvuuuvwwxwwtvzxz{~~|ywtrnljntw|wurqonmlklpqtw{~||{{{{|}~|yxyy}ytqppsvwyvvuuvxz}~}}|zzyxvutttvx|}}}||{z{~}}|{zxusruvz|yyuutttuuuvwy{|~}|{zyy}}||~|zvuszyurponnnnnoqsvx{~~xtqppquxvvutssssswtx{z{{{{{zzyxwvxvtsswy|zwusqqronmmnquz}zy~|xvssvy{|~~}~~}|~|sp}z|~}ysux~||wxwyyyxxwwwwwvuvt~~|{{|~{{~tt}vux{~|{|wrptyttoliku~|{{}xtuleddeefghijkmquuwtwz{~}zwrory~vmn{{y{~{v|}{~~~|||~|z{|uru~}wyyyz{{zywx~{usw{vslgechotz|}}yoop}zphhjlkhfehmsy|~yyzxyz~zwttv}xrrwurqlllnstuvwyz|}~~}zwrpz|z~|}~ztps~xprvqqmkkklnrx~{vw}yrnkihiijlnnpnptuy{~yuu{zw{{}yuqnt|urtw|}{zz{}~z|}~~}~{vvuw}}{yz}z||xupljihghjouy}{phgsyrqrrplklmqvw{~||{{|~}{yxx{|x|{vslkllnprsuwxz{}~|wxttv|}~}|zz|~{vrru~wpiddfjiklosyyqps{wromlklklijnnqtz|}{vty~yuw~zxy|}{vrqwztnqtw|~}||}~~|}|{yz{|}~~~|zwvxz{uqt~|tmkjjihgggimrw~ymdcir~xvwxwtqolovy}}}}~}}~|{{}|wpmlkjnnoqrstuz}}~}{~|{yxx{~zsqsz~|xvvy{zurprx}vu{umgddeghikmpuz|xtsrtzywrommijmmpsvx{}{vppyyyzy{~|z}{yy{|{wrqt~~{xsqux}}|{yx}}~{}xvvwxyz{{{{{z}|zxvywxtuxwzzussnlkjihgghkoty{~|}yxtoigjr}|}}|uqqnqx}~~}{xwy|zyxwx|wqqqooomoqqtuwy{}~~|~}}|~ztrqsuy}}yutuxzztlps{|wy~{zyxytqlgfeffgiotx~~{xvuuvyzvonpoqruvyz|}~~}~|vrqwvstoquz~}z~~zz|}|wqllu||~zvrtw|~z}|~~wsuvuvwxyzz{|}~~~~|wx|~~zsmotwvvqmkjihhhjmptuzwuuwwupljms|}zvvw{|wst{~~|w{}|xqmmu~|zxvutssssrvwy{}~~{|yvtrrrtwz|~~yvtuwvyxsvz~yssrtwwtqliiikmptw|}~|{yyyyz||zuwwwxyz{|}~~~{wsqvxwvoklorw{|{{zumhksz~~{xx}|xvvvwwy|xwvvuutsqrttz~|zxyz~soica_`adhmrw|~{{~wrqsy~~{wtstvzy~~}~{yvuuvx}||yyroqrstuvwxyzz||~~}{}wv|zzwvtqmjijmqrrpqx{xz{sieddegjmqw~~{xsttvy~~ywvvwxxyuvywxxxxxxxy}|{yuuv~~}|}~wxxzyvttyzuqnruwz{}}}~~zz||vuuuvvvvuutttuv{{sry}xskgdbaabdhlrw}~vu{yrlkou{}ywtw}~~~}yy~zwussv{~}ztssssruuvwxyz{{|||zz}}vrnnprtrokhjlptwwvu{~zy||}vqnihghikmpty~{vqqrstvx{}zyuyzwxxxxxxyyyyxvsonq|}{}}~zyzzzwuuy~}{vtqsxz|}{vtuuvwwwvvuuttttuvx}}{|}tqpppolgdbbfginrx~~yy{yywtspnosy}yzxz}~|zwssw~~~{xvuuvwwwy~zxwvvuutuxyxz{|}~~}z~}~~xplkloqttsqnllotx{zxrx}|{vsuvtsokjijlosw{{usqqqrsv{|~|~{{zzzzyyzyyxvsnlntvsqsuy~}|y}}{xtqqu~|z|~}yvuu{xuttuuuuxxwwwwwwwwwuz||sljjnppplihhikosx|xsnpruusqrv|~}~~ztqryzwwruz|}zwtqnmpy}||{{{{zzzzyy}}~||tplkklortutrpopsv{{|zxz}xvtssuuwvpnmnoswzzwtsrrrsux{~|{z|}{{zxupnnturqmnnsy|~}xqkjr|~{xxz|~|||~{xwwwwwwxxxyyzzzzvy{{~xupmjikopsronnoqsxz}~y|~tpmnpsuxvtvzztqwzusnnnqsy}~xpkjpvvywxy{}}~~}}}}}}}|~xsnkkmnqtwxywvtsrx||}zz{{{z{zxvuvwwxurqrsvuz|zwvtssrstvy}~}}|zvqnnsurpjiijlnr|~~|vqmqtoppty||zyy|}{yxxxxxxxxxy{vyzy{{~}zxusoomkmquwzwvuuruz{~xtv|yurpopsvxxwvx|~{y}}wskhghjlprw}~{uprtppllorux{||}ytpnmnqux{|{xqqrrvxzyx{}}|{|||zwvvyyxxuvpqvw||xvutsuutuuwy}|xrmnuysojgfghjlortz~{xtux}vmgadiqw{{zy{~|{~yz}}{zyyz{|||zx{yzzz{|~~}|{yxvtrpoqu{}zuvuvx{}zx}yvsvwxwurpopsvyyyx||yz~voifdddhkmquy{wtu|ypkfgilptx{}~|z~ywxz~|wsqqruy~zzusrtwz{{{z}~~{zz|~~~wxvvy{zvuwvvvvvvwwy{}ztrv{vplhhghijklnqtwzzqmrz{wqida`clsy}|vu{|{zz{~~}~~~|y|{zzyy|}}~~}}|{zzxvuv{}~{yyz{~}|wroppty{yvtqqsw{~~}{yx||xrpmkihhhjlpuz~wqrz{xwoljijmosvz|}yy}yyyxy{|}}z{{}}|~|xusrtwz|{zyxw{}{~{yy|~zywwuxwwxxyyz{~~xy}zvqljiikmlmmoqswz~{vvroolqnlifehmu||wv|||yz|}~}~}~~}}||||}~}{||w{~~}|z{|~ztrqrtuw|{{ywuvx|~{yttssqomlkklnrv|}z}ztqmmnqppnoqsw{~~|z}|wwwy{}~~}}~z~~~~|{yustvy||zuqqz~}}zxvxzyxwx{z|}~~yrqoonnnnnopqsux|~wneceioqnliikqy~z}~zxsrsu{~~~}}||{zxvvu{|xxz~~~~}ywtssstuww{|zzzzyzy~|sporuuutsrqqrsuy{~~ypljlnpttrrrtvz~|z~|{usstuxz|~~}}~~}{xussttsqorvuxyz~}}||}||zyyyyyyyyzz~|~}yvsrqqppoooooquwwzy{}~|rkdbcejotsqoorx|urqrtvy{~~}{xtposxwyuuvx~}{z{~~{yvv{~zxwywvtttuwyz{}zxsvy{}wsqrsuvwwwwvvvvwuw||xtpnkjknorusrtxz|}|{~|zsqoqttvwz}~~~~~|yupmljkqwtrmlouz~yutw~|xxxxxxxxxxyz{~~||ywutssrrrssttutuz{zxuqlfbbdkpwywuuw{||{wussssuvy{}~|ytpnpurrnklquvx~{zz|}~}xtv}}~}zzz{{zyxvuuuwy{~|{xyuwz~yusstvxz}~}|{wvyx{}}zxzvuo~~{z|{zyuqpppqqx~|xy}ywuvyyxxuvwyy{}||}~zrqtz~~}|{||zxqij{|wrsvy}zidxthb__bgmsvuspopu}yt{}|~|}}vxsrstvwxyzz{}~~zy||zyxtohejx~zvolklnoqsuxz}vij~th`\]cjprojfglu~~}zx||xux~{}~zxwvuuuuvwyz}}~zxxttw}~|xwutqnjio|}{tsrrsuvxz|~rhl~rgaaflppmifglt{|}zz}~zvty|y}}zxvutttuvxz}~zzyuvy}ywutqnkls~zsrqrstvwxy{rjp~riddglppmiginv}~{{xz~zvuz}~|{~{ywvvvvvwxz{||yzxutx~}zxvtqnklu~ytrqrstuwy{}|pks~skffjnqplhgiov{{yyvy||xtu|~}{{|zxvvvvvwxz{|zwxvtuy|xwuspmjmw~yutssstuwx{}xnku}sjfgkoqplhgiov|~zxxvy}{wtv~~{y{}zywwvvvxyz{|zvxtsuz{xvtroljny}|wtrrrrstvxz|ully|rkhimqrplihjpv{}xxwvx|{wtv}zx{~|zxwwvwxxzz{wvwssty~ywusqnkjoz}|utrrrrstvxz|~skm{{rljlorsplhhkqw{|xyvuw|~zvtx~|yx{|zyxwwwxxzzzvvwssuz~ywusqnklr|~}{vusssstuvxz}|rln}{slkmqttqmiilrx|{xyuvx|}yvuy}{yy}}{yxxwxyy{{{vwvssuz~}xvtsqnlms}~~{vuttsttuwyz}{qlp|tnmosvurmjjnty}{xyuvx}|yuuz}{yy~~|{yyxyyz{|{vxvssuz~}xvtrpmkms||}yutssstuvxz|yplq{snnptvvrnkkouz}zzzwwy}~zvvz}|zz~|{zyyzz{||zwzvtsuz~}xvtrpmlmt||~yvuttttuwx{}ypls{tpprvxvsnlmqw|{|zwxy}}zvv{~}|{|~|{{z{{}}}zy{wutvz~~|xvtrpnlnv||~yvutttuvwy{~ypmt|uqqtwywsomnsx}{}zwxz~}yvw|~}|{|~}|{{||}}~zz{wutvz~~|xvtrpmlow|}~ywuuuuuvxz|~xpnv|ursvyzwspnptz~|}zxy{}yvw~~}}|{}~}|||}~}}y{zvutwz~~|xvtrpmlpx{~}xwuutuuvxy|~~unmx{ustwyzwspnpty~~|~zyx{~}yvw~~}|{z}~}}}}~~~}y{zvutvz~~~{wusqollpxz}{wvtttuuwxz}|tnnz{usuxzzwspoquz~}}~yyx{~}yvx~~}|{zz}~}}}~~~}y|yvttw{~~{wurpnklqy{}zvuttttuvxz|~{rmp}zttuy{zwsooqvz~|||xxx{~|xwz~}}|{zyz~~~~~~~~|y{xvttw|~~zvtqolkmsz{}yvutttuuvxz|~xpmr~yttvz{{wspoqvz}{}{xxx{~~zww{~~}||{zyxy~~~~~~~zy{wutux}~~yvtqolkmuz{}wvtttttuvwy{~~volsyuuwz|zwroorw{}z}zwwx{~yww|}~~}|{{zywy~~~~zz{wutux}~~yvspnkjmuy{|vuttttuvwy{}}tnmu~xuvx{|{wsppsw{|{~{yxy{~zwx~}~}||{{zyxzz|{wvtux}~yvsqnkjnvy|{vuststuvwy{}|tnmw~xvwy|}{wspqtx}}|zyxy|zwx~}~~}||{{{zyx{~{~{xvtvy}~~~yvsqnkkovy}zvutttuuvxz|~{snnxywx{}~{wsrrvy~}~{zyz}~zwx~~~}|||{{{zyy{{{yvuvx}~~zwtrnlkpvy~zwvuuuuvwyz}{snoxzxz|~~|xtstx{}{zy{}~zxy~~~}}|||{{|{{zz|}|ywuvx|}~ywtrnlkpvy~ywvuuvvwyz|zsnpzzy{~|xutux|~|{z{}{xz~~}}||{{{||{zz}~|zwuvx|}}ywtrnllrvz~ywvuuvwxy{|~yrnr{{z|}yutvy}~|{z{}~zxz~~~}||{{{{|{{zz~}{yvuvx||}ywtqnlmsv{}yxvvvwwxy{}~}~xrns|{{}}yvuwz~~|zz{}}yx|~~~}}||{{{{{{{zyz}{yvuvy|}|xvspmlntw|}xwvvvwwxy{}}}}}vpou}{|~}yvuwz~~}{zy{}|yx}}~}}||{{{zzzzzzyy{|}zxvuvz|}|xurpmlouw}{xwvvvwxyz|~~}|}~ztopw~{|}yvuwz~}}|zyz||zy~}}}|||{{zzzzzzyyy{||zxvvwz||~{wtrollpux}zwvvvvwxyz|}}|||~~ysoqy}|}|xvuxz~|}{yyz|{yz~~}||{|{{zzzzzzyyxy{~|{zwvuwz|}~zwtqnmmqvy}ywvvvwwxyz|}~|{{|}~xrory~|~|ywwy|~}|{yyz|{yz}}~{|{{zzzyyyyyyxxy|}}{zwvvwz|}~ywsqnmnsvz}yxvvvwwxyz|}~|{{|}}wros{~}|ywwy|~}}{yy{}~zy{~}~~{|{{zzyyyyyyyyxy}}~{zwvvw{|}~zwtqnmnsv{}xxvvvwxyz{|~}|z{|~|vqot{~~|ywxz}~~~{zz{}|z|~}}||{{zzyyyyzzyyxy}~|{xvvw{|~zxtrnmnsv|}yxwwwxyz{|}||{|}|vqpu{|yxyz~~~|zz{}|z}~~~}||{zzzyyyzzzzyyy~}{xvvw{|{yuromnsv}|yxwwwxyz{}~~|{{|}|vqpv||yxy{~|{{|~|z}~~}|{{zzyyyyzz{{zyz~{yvvx{|{yuromotw~|zyxxxyyz|}~|{{|~{uqqw}|zyz}~|{{}~{z~~~~}|{{zzyyyyzzzzyyz~{xvvx{|{yuromptx~{zxxxxyy{|}}|{{|~ztprw~|zy{}~|{{}~{z~}}~||{zzyyyyyzzzzyxz~{xvvx{}|yuqnmqtz{zxxxxyz{|~~|{{|}~xsprx|yyz}~~|{{|~{|}|}~|{zzyyyyyyzzzyxxz~zxvwy|}{xtpnmru{~zywwwxyz{|}~}|{{|}}wrpsy~{yyz}~}|{{|}{}~||}~}{zzyyyxxxyyyyxwx{~}zxvwy|}~zwsomnsv|}yxwwwxxyz|}~}}|zzz{}{uqpuz~{yyz}}|{z{|~|{~}||~}{zyyyxxxxxxxxxwww|~|ywvwz|}}zvromosv}|yxwwwxxyz{}~||{zzz{}~ytppv{}zxyz}||zzz|~~|{~}{|~}{zyyxxxxxxxxxxwwx}~|ywvwz|~}yvronptw~|yxwxxyz{{}~~{}{zyyz|~}xspqw}}{yyz}||{zz{~||~|{{}~~~zzyyxxxxxxxxxxwwx~~{ywvwz|~|yuronqtx{yxxxxyz{|}~~{|zzyyz|~|xsqsx~~{yz{}}|{z{|~||~|{{}}~{zyyxxxxxxxyxxwwy~{ywwwz|~|yvronruy{zxxxyz{||}~}{{zyyyz|~|wsqtx~{z{|}~|{z{|~~||~|{|}}~{zyyxxxxxxyyyxwxz|zwwwz{|zvrport{|{yyyzz{|}~|}{zyyyz|~|wsqty~|{|~~}{{|}~|}}||~~~|{yyxxxxxyyyyyxx{|zwwwz{}zwsppsu}|{zzzz{|}~{}~zzyyz{}{wsrvz|||~~~|{|}~}~~|{{}}|zyyxxxxxyyzzyxy{|zwwxz{}{wsqqtv~||{{{||}~z~}zzxyy{}~{vtswz}|}~}|{|}~}~~|{|}}|zyxxxxxxyyzzyxx||zxwxz{}zvspqsw~}|{{{||}~z~|zyxyy{}~zvssw{}|}~}{{{}~~}~~|{{}|{zyxxxxxxyyyyxxy}~|ywwxz{|yusqrtx~}|{{{||}~~}y~{yxxxy{}}yustx}~}|}~~}{{|}~~}}{{{}~|{yyxxwxxxxyyyxxy}}{ywwxz||yurqsuz}|{zz{{|}~~}|y}yyxxxy{}{wtrty~}||}}~~|zz{|}~~}{zzz}~}~{zxxwwwwwwxxxwwwy~}{xwwy{|{xtrqtv|}|zzz{||}~~~~{}yx|xxwxyz|~}zvsrvz}{{||~|zz{|}}~~|{yyz|}}}zyxwwwwwwwxxwwvwy~|zxwwy{}~{wsqqtv~||zzz{{|}}~~~~~}z|xy{wxwxy{|}yurrvz|{{||}{zz{|}}~|zyyz|}}|zyxwwwvwwwwwwvvwz~|zxwwy{}~{wsqruw||zzz{{|}}~~~~~|z|wzzwwwxy{||xussw{|{{}|}{zz{}}~}~|zyyz|}}{zxxwwwwwwwwwwvvw{~|zxwxz{}~{wtrrux|{zzz{||}}~~~~|z|w{zwwwxy{}|xtrsw{|||}|}{z{|}~~}}|zyyz}}{{yxwwwwwwwxwwvvw{~}zxxxz|~~{wtrsuz~|{{{||}~~~~{zzw{yxxxy{}~~{xtrtx}}||}}~|{{|}~~~}{yxxy{||{yxwwwwwwxxxwvvw{}{yxxz{|xursu{~|||||}}~~~~{{{x}yyxyz{}|xusux~}|}}~~|||}~~}{yxxy{}}|yyxwwwwxxyxxwvw{~{yxxz{|xurtu|~}|||}}~~~{{zy}yyxyz|}|xusvy~}~~~|||}~~~}|zxxz{}}{yyxwwwwxxxxxwwx|~{yxxz{|xusuv~}}|}}}~~{{yz|xyxyz|~~{wtsvy~}~~~|||}~~~~}{yxxy{}}|zyxxwwxxxxxwwwx|}{yxxz|~|xutuw~}}}~~~}yzxz{xyxz{}~}yvttwz~}~}~|||}}~}|zxwwyz}~|zyxwwwwwxxxxwvvy|}zyxy{|~{wutvx~}}}}}~~~}|yywzzwxxyz|~~|yuttx|~}|}|}|{{|}~~}{yxwwyz|~}|zxwwwwwwwxwwvvvz}~|zxxy{|}zvttvz~}||}}~~~~~~}|zxxvzxwwwyz|~}{wusux}~}|}|~|{{{||~~|{yxwwyz|}|{xxwvvvvvwwwvuvw{}~{yxxy{||yvtuv|}}||}}~~~~~}}{ywvvywvvwxz|}~|yvtsuy~~|||}~|zzz||~~~}|zxwwwyy|}|{xxvvvvvvvwvvuvw{~}{yxxyz|~|xvtvw}}}|}}}~~~~~}|{xvvvyvvvwxz|~}{xussvz~|||}~|{zz{{~~}|{yxvvwxy{|~{zxwvvvvvvvvvvuuw{}{yxxy{}|yvuwx}}}}}~~~~~}}{zwuuvxuvvwyz|~~}zxuttwz~|}|~|zzz{|~~}|{yxwvwxy{}~{zxwvvvvvvwwvvuvx{}{yxxz{}~|yvuwx~~}~~~~~~~~}{zwuuwxvvvxy{}~}zxuttx{~}}|}|{z{||~~}{zxwwwxyz~}|zxwwvvvvwwwwvvvy{}{yxxyz}~|ywvwy~~~~~}|zwvvyxvwwxy{}~~}zxutux|}~}~|{{{|}~~}|zxwwwxy{~|zyxwwwwwwwwwvvvy|}{yxxzz~~|ywww{~~|zxvvzxwwwyz|}}zxutuy}~~}~|{{||}~}{zxwvvwxz}|zyxwwwwwwxwwvvwy|~|zyyz{}zxxx}~}|zwuwzxwxxz{}~~|zwvuvy~~~~~}|||}~~}{yxwvvwx{~|zyxwwwwwwxwwvvwz|~|zyyz{}zwxx~~}{yvtxzwxxyz{}~|zwuuwy~~}~}|||}~~|{ywvvvwx|~|zyxwwwwwwwwwvvwz}}{yyyz{|ywxx~}|{xvtxzwwxyz|}~}{yvuuwz~~}}|{{||~~}|{ywvvvww|}{zxwwvvvwwwwvuuwz~}{yyyz{~|ywxy~~}}{zwusyywwwyz|}~~}zxuttxz}}|}{{{||~~~}|{yxvuuvww}~}{yxwvvvvvvvvuuuwz|{yyy{|~{yxy{~~~~~~}}|{zxvsrxwvwwyz|}~~}{ywutuy|}}{~}{{z{{}~~~~}{zxvuttuvw}}|zywvvuuvvvuuttuwz~|zyyy{|}{xxy|~~~~~~}}|{zywursywvwwyz|}~~~}{xvttuy}~||{~|{zz{{}~~~~}|{yxvttttvw}|zxwvuuuuuuuutttx{~|zyyy{|}zxxy~~~~~~}}}|{zxvtqsyvvwxy{|}~~~|zxvttvz~~|||}|zzzzz}~~~~~}|{yxvttttuw}|yxwvuuuuuuutttuy{}|zyxyz||zxyy~~~~~~}}|{zxvtqtyvvvxy{|}~~}|zxuttvz~}|}}|{zz{{~~~~}|{yxvtttuuw}|zywvvvvvvvuuttuy|}|zyyz{}|{yzz~~~~}}|{yxvtpuyvwwyz|}~~~}|ywutuwz~}|~~}{{{{{~~~~~}|{ywvttsttx}|zywwvvvvvvuuttvy}~|zyyz{}}{yz{~~}|{zxvtqxzwxxz{|}~~}|zxvuux{~~|~}|{{||~~}{zxvutstty~~|zyxwvvvvwvvuuuvy~~|zyyz{~}{zz|~}}{zxvtqyywxxz{|~~~}|zxvuvy|~}~}|{{||~~}|zxwutsttz~~|zyxwwvvvwvvuuuwy~|zyyz{~}{zz}~}|zxvsrzywxxz{|}~~~}{ywvvvy}~}~}|{{||~~}{zxwuttts|~~|{yxwwwwwwwvvvvxz~|zyyz{~~}{{z~~}{zxvrs{xwxxz{|}~~~|{ywvuwy~~}~}|{{{|~~~|{ywvttsss}}}{zxwwvvvvvvvuuuxz~|zzyz{~}{{{~~}|zyvuqs{wxxyz{}}~~}|zxvuuwz}~~|{{{{|~~~~}|zywutssss~}|zyxwvvvvvvvuuuvx{}{zyyz{~}{{|~}|{yxutptzvwwyz{|}}}|{ywvuux{~|~}{{zzz{~~~~}{zxwutssst~~|{yxwvvuuuuuuutuvy|~}{zyyz{~}|{||~~}|{ywusotyuwwxyz||||{zxvuuvy{~|~|{zyzz{~}}}~}|{yxvttssru}|{yxwvuuuuuuuttuwy~~|zyyyz{}|{|}~}|{zxvtrovyuwwxyz{|||{ywvttuy|~}|~|zzyyy{~}}}~}|zywutsrrqv}|zyxvvuuuuuutttuwy~|{zyyz{}}|}~~}}|zywvsqowxvwwyz{|||{zxwutuvz}~}|~|{zyyy{~}}}~}{zxwussrrqw||zyxvvuuuuutttttwy~|{zyzz|}||}~}}{zywusqoxxvwxyz{|||{zywuuuwz~~}|~|{zzzy|~}}~~}|zywussrrqx||zyxwvuuuuuuuttuwz~|{zyzz|}||}~}}|zywusqqyxwxxz{||}|{zywvuuwz}}~|{zzzz}~}}~}|{ywutsrrrz}}{zxwvvvvuuuuttuxz~|{zyzz|~}}}~}|{yxvtqszxxxyz{|}}}|zywvuvxz~~}{{z{{}}~~~|{ywutsrrr{}}{zyxwwvvvvvuuuux{}{zzz{}~}}~~}|zywutqt{yyyz{|}}}}|zywvvvy{~}|{{{{~}}~~|{ywusrqqr|~}}{zyxwwvvvvvuttux|}{zzz{}~}}~~~}{zywusqu{yyy{{|}}}}|zywvvvy|}~}|{{{{~~}}~~|{ywusrqqs}~}}{zyxwwvvvvuuttvx}~}{zyz{}~}}~}|{zxwusqw{yyz{||}}}|{zxwvvwy|}~|{{zzz}~}}}|zxvtsqqpt}~}|{zxwwvvvvuutttvx~|{zyzz}~~}~~}|{yxvtrpwzxyyz{|}}||zywvuuwz}~}}|{zzzz}}}}~|{ywvtrqqpv~}}|{yxwvvvuuuttttwy}{zyyzz}~}~~~}|{yxvusqpwyxyyz{|||{zyxvuuuwz}}}{zzyyy|||}}|zywusrpppv}||{zxwvuuuutttsstwy}{zyyzz~~~~~}|{zywvtrpqwxxxyz{{|{{zywvuuux{~}}~|{zyyxy|||}~}{zxvtrqpppx}||zyxwvuuttttssstwz~|{zyyzz~~~~~}|{zxwusqoqxxwxyz{{{{{yxwuuuvy{~|~~|{zyxxy|||}~}{zxvtsqppqx||{zyxwvuuttttsstux{~|{zyyzz~~~~}|{zxwusqorxxwxyzz{{{zyxvuuuvy|}}|zyyxxy|||~~}{zxvtsqppry||{zyxwvuuuttttttvx}~|{zyyzz~~~~}|{zxwusqotxxxxyz{{{{zyxvuuuvy|}~|{zyyxy|||~~}{zxvtsqpory||{zyxwvuuuutttttvx~~}{zzyz{~~~~}|{zxwusqpvyyyyz{{{{{zyxwvuvwz}~~}{zyyyz}||~}|zxwusqposz||{zyxwwvvuuuutttvy}{zzzz{~~}{zywutrqwyyyzz{|||{zyxwvvvxz~~~}{{zzy{}|}~~|{ywusqpotz||{zyxwwvvvvuuuuuwy}|{zzz{~~~}|zywutrrwyzyzz{{{{{zyxwvvwx{~}{zzyy{}|}~~|{ywusqppuz{{{zyxwwvvvvuuuuuwz~}{zzzz{~~}|zywutqsxyzyzz{{{{{zywwvvwy{~|{zzyy{}|}}|zxvtrqopvz{{{zyxwwvvvuuuttuw{~}{zzzz|~~}|{yxvtsqtxyzzz{{{{{zyxwvvvwy|~|{zyyy{||}~}{zxvtrqopwy{{zzyxwvvvuuutttuw|~|{zyzz|~|{zywvtrquxyzyzz{{{zzyxvvuvwz|}zxvvwy{~|uojiijkprrtutplqy|{}{zz{{}~|wuzxy{|~}}~}|{yxz~vqnptsmkda``abcegjmopoootwpjdccefhoux}~zsttvzxrx|vpljjnuyuqonjouy|xwz}}{~}|||}}}{xupnrzytsprtx{~~vlkvtic^\ZYZ[\]_behknoqrwz}~zzxvtqnkkmrz|yyxyxy|~{vtr{zphfjwxojhjmrvxxwtqnljigjv}{xwyxwvttz{xx{~zxvy~uontvpolmorux|~|}yttogb\XWWWXYZ\^acechlnsw{}wtuxzy||usqpoopqsuwyzzy{vmfcfpxngccfjosssspmkkloqu|z}~}}}zwsporz~{{zspt|}z|{vromkigea][^dlnkkefjkorux{~|z{|vtw{}yxttuwxxvuyzyxxxxyz{ukgm|xld__aeiklljfdbbeimpz~vqmlnu~}yxwwwxy{}~}}xx|~}yust{xsmmjihhhhhijlnqsvuuzyyxx{|~}{{|}}~}}{ywuu{~~zx~yvtsrrrtz~|wvz{qieflu{xungc`_adhiovz~}}zy|zwvuuuvwxxyxvrps{~~}{|}vssw~rmhecba``abdfhikoqrryz}}{xwwvuspnkjkpy}{zzzx|}{wuu{~xrpqw}vrqsvz}~}{wsokhfjny~|yzzyzzyvvy~||}yx{{toovzxxtuvx{}xrkeb_^]]]^_`bdfhjioruy|~ytstwutvy{ywssrrrstwy|~xqkijpyvollosvwzzwuqonoquw{~|{~~yx~}~~~~}{ywtssx}{~{{z~~}|xuspnjfbachmlljgheimortwy{{|ywwy~zxvz}|{yvvwy{}~~||~ywuuuwx|~tllr}~rkggjmqstspmiffgjoru||wspnory}}|z{z{|}~||~{ywwy}~vvrrppoonnnooqrtturrtrty|z|~xwwwwwvusqnnsx}uqooprrw}}{wvy{pklrz|}{uqlieddfilmuz}~}|xvwzzyzyzz{{|||{yvvy}}{wvw{~{rolkjjiiiiijiknortttz{}~~yussrrpomkiilrxx{zyyy}~zurqty~{ux{}~xspqu}zwwz}~ysmhfjpxx~~{{{yyyyyxvwz}|~~{|~{xtrsy||}xxy{}}}snjhgfeedddefgikmnmtvx{}~{wtrrturqorw|||{tuusttuwy{~~}{{|}}|{~ysnklpyvssux{}zvtrruw|~~~|xuv|~}|||{zxwuuw|{wy~}}z|~zwtplhginpoplkklmkorsvwzz|~~}|zxvuux}wx~~~~zxxy{}~~xvuvwy~vons{|rnmosvxyxvspnnoqux{|yvurrsx~~|zyy{|zwvutsssssttvwxwvqquw{xx}~ywuuttsqnopsy|{spnoosy|~|yzxy~vruy|~{xtpljhhjmqtv~}xutz||||||}}~~{z|~zwx{}uronnnnnnnmqqsuwxzzz~wrponlkigfgkqx}~}y}{~xqoqv{}ywutrx||xtrtx||}yrlikqwz{~~}|{{{|{zxx{}}}~}{yvttz~~{zz{{wpmkjiihhhhhijlnprsryz|}xtoopsrpnjlouxy{xvqtutvwz|~{yyz~|xx{|z~~zuplkmt~}vvv|}zwuuvz}}zxutu||zyyxwvutuy~~xuv{xwvz{|}~zupmklorrspljjjklmlqttwxx}~~}zyxvtrqrvz}}{{xzz|}{z{|}{wvzzzxrrrsw~~|uqptx|yrpqtwz{{yvspoopqw|~{wuz}ywusqqqtz}~}}{~}~||{yxwx{}zxxwwwvvvvvutsqopov~}~{}||~~|xvv{|}|vsqpomkmlnsy~ytvwuuommmswz~}{xwvvz{{xx{~}|}~}|{zwspmkjkmquyz}zvssy{~~}~}~~~~|{}}~|xvvy}|~~wtrrrqrprtsuuwxz{}{}}xyzyzsomkjhfeeglqwz}}}yx~|{xxsrvv~~~}{xurppprs{~}yurru~xroptvz|{}{~}||}}}|zy{~~~~}{xvv{~~}z}~~xspnnmmmmmmnoqsuvxxw|~yrqqsqrmjikptvzzxxvswyy|~}{{z}yvvxx{{w{~~~{xtpmmpx}~{zy{}}|zyvuuy}~}{zyyxxy}~}}yz{z||}~~ytqqsuwxtrpoooprrsqwxxy{~}|}zwvsrppqtvyx{|~}~~zvsvw{|wuw~|z{|}wsrtu|}||~yz{~}zwvtuz}|wuv}~zwvuttux|}|{~~~~}|x|}{|{{zyz{|{{zzzzyyxvustuzy~|{}|}}~~|yvtxy|}wtqnoomnquzytpqrsvrpqvx{}~{yvtruxz}}yxy}~~|yusqpqsvz}}zwtuy}~}}}{|ywwxz~|~~{ywvutwwwxxyz{|}~}~~{vsuvvxrnlihgfgjntwz~}}{~}|xsqrt{zw|{z{{{zxuromlmoruv~~|yurptz}ywwwwzyyz|}|~}|||}|{yyx~~~|}|yww{~}{}}}~~}{z|}|||xurqppppppqrstuvwxwy~~}yxxvyvtsrpkhgilqsuywwvvvtz|}|yx{{~y|~|xtrsstwuuvuy{xwspmlnt|}~|{z|}|{zywvtrswz}zywvvvwz}{xyxxxyzz{{{||}}~}|y{wwxxzyusppoopqrsssqvwv{|}|{{~~yvrqnnmoqstw|~}xz}||xsposuxzx{}}zxvvwy{~|}zxuutuxz~yyz|~~{xtvxy|}{yyzz{}xusrrsuy~{{y|}{zyyyyyxuzzyyxxxy{}|{{zyxvtrqruz{{{{x|z{|}}~~~~}{yvtsvw{|tqqnmmnrv{~{wqmjmosxsvwxz{|zyvtqostw{~{xxz~~~|yvsqqqsvz}~{vssw||}{z{}~~}yxvvx||z|~~|xxzxyyzz{|}~~}~|wspqrtxrnjhffgkoux|}~}vokmtx~|zzyxvtqnlkkmqtx{z{ytruw}}{|{||yyy{}}z||~~~{yx{}~~}~zxy}~yyz|{xusrrrrstuvwxzz|~~}wqoqu}{ztniffhlqsxzyzz{{|{}yvvz}}yurqrruvuvwxzw{{vsonmpv~{~}}}}|{zywusrvy~~|yxwwyzz{~}zyxyyyyz{{|}~~~}~~{{{{xusrqqqrstuvwvuy|~}zy|}zvrolllnqt|~}z~~|yvrooqv}zwvuuwy|}}yy{wxvxww{zuwy|z{yz||{|~~~zzz||xusrsvy~~~}~}{yxxwwxxxxwuyzxyxyz~}|{ywusrswz}~|wv{zz{||}}}~~}}{yvsrtux|vronnptx|~~|yurnkjlpt||zzzzywurooqqtw{{ywwz~{xutsstwz}}||~xutxz~{}{ywwx|~z{{wvuuvy}~{z|~}~|{zz{{{||}}~~~{y|z{||yvrpnoprvolhggimqux}{~~zuqnnos{z{zwwtrpmkiijmptwyywzwrrssx~ywuvwy|~}~}}{z|yxy}||}~|zyx{{}|~}~~||{yvuw{}zwyz||yvuttttuuvvww{{{|{y|xsporvyyqlhghkorvzyyyzz||}|}{xuuuz{~zvsrrqqsqpprtvwwsttnmlnry~|~|{zzz{}}|{zyxwvtrrvx|{ywvx~}ywxxz~{ywwwwxxxyyyzyz}|{z~~~}yy|{xutssssttuuvvvus|}|~~}zxwyy}|uqmlkmptz~~}}{}{zxvspnrvw}{xusrrstvxyxxtvywxttv{}ywy}|||}~|{|~{wtuvx~zwutux|zxwy~|zxwwvvvwwwwwwtyzxyz{~}{xvttvy{~}zxt{|z|{||}}}}}}|zyvsqrvy|wtrrsvz|}~}zxvsqommor{}{xuroqppsuy}|~zwvwz~}|yxwwxz}|yxxx}}{{{~~|zxvvxz}~|wxzvwvxz~~|~}~~~~~|xz|z|~|zxvspnnoqwrommnqvx}~zwutttwxvx}~{wtpmkjijmptwz||zuwwtuw|~{yxxz|~}~~{zy|}~}{yy|~zy|wvvy~{yyz}}{yyxxyyyy{~}~~~{xvtsrvxv{vpmklortz||}}~~}|zxwwzzvsrqsspppqsvwxwvpqqmnpv|}|zzz{~}|{zzyxwutsuw||z|}{xutx||zxxwxxxyyyx{}}}}|z~~~{z|~zwvuuuuuvvvwwxvyx}}~}ywvwvzyrollnsy~~~~~|~|{zxwvtsuttz|xusqpqrstuvwwxuwzssrw|{xx|~}}}~}~zvsqrtv}ywvvy|{vts{|ywvuutttuuuuuutrwxwxz}{xuttvx{~|zxwtvtw{y{z{{|||||{zxvrrssty|wutvy|~~}|}}}zxvtsrponmmrx||xupopnnprvy|~}xuwtsuy~{y{~{xwvvwx{}~{xspoqt}~}~~~|~|}{xvtttwy|}{xvtprusuw{~|z~~~~~~~}}|zxuvtu}}}|{yxwuspmllmpuqooqtvy~zwtttvx{yvsuzz~yupmjhhhjlpsvxyxusqsssw}~{{zwuuuwy{}~~~|zyx{|~~~|ywv{{~}}}}~}}}}|{xvrputx|~{ywxz}~{yxxxww{{{|}}}|~~|yxxxxxzusrqvsooorsx||}|}}~}~}}|{zxwux{zzxutvspnmnprtvvurojlnmqw~}zwvvw{~||{zzyxwusrtuy~~{xtsww{}{yxwxwxwx|{||}}|{y~~}yxvvuuvvvwwxxx{zx~|yussqvvppptx|~}|{zyxxvyzyxw{~|xtrpppqrstvxyy{usuqtw~|{|}{xurppqt|{zz{~|yvw{~zxvuuuuuuvvvwwvvsxzy|zxwyz~~|{zxwzyvy}{||}}}}}}}}|y{xusssx~{||~}zxwvuuuuvutsvxy}{utsppqtvy||ytrvtvy~|{|~|zyz{||xuqoorv|xutuwx~}|zywutqtwvy|~~|zwwyxx{}|{{zyxwurpmmmqxwwwyz}zwutvw{~|ywxwwy}xsoljjkmpsvyzzxywspuwy|{|}zxxxy{}~}{ywyy}}yyz}}~~}}}}||{zywtrpqyz~{yz{~}|z{~}~~~~~}~}{yyyz{~~zuqonnuvtuvx}~~}|||||{ywxvvv|~zzxspnmmoqsuvvtromhlqsy{wsrqruwy}{{zwpkkmmorux{}~~zvsrruz|~~~|{x~~zzwux{~{zvuutsrpqu}|}xy{{wq{rfc``acdfgijkmnoqqqnqtuwy{{{~}~{z{{{||||||||}|{vrropu|zumnoquy~|nb`m{oc\VTTW\bhnsuvvsngihhknsx{~}zutxy|}~~}xrlhkw|uqmkjjjklmoprtuwwwsqpmnrx|zxwtuvwwvttx|{w{}wonxwohea^\[ZYYYZ[]^`a_efilnqssx{~~|~{zxwvvuuvvwxxwrqqoqw~xrttw{zpgcgwzphb_^_cgkorssqnhcedehlqvy{{ywqsvwz|||~{xsojin{|xuqpnmllmmnpqsuvwvrsqnosy}{zxvwxyywuu{{y}~wop{ypkgda_]\\\\]^_`b`bggkmprsuz|~}|zywvvvvvwxxxwrtqprxwtuuy}wngci{zqid``bfjnrtutqngffegjotx|{{~zvrvvy{}}}~}zvrnkkq|{vsqonnnnopqstwxyvstpootz~||zyyzzywuv}z|}~voq~yrmheb`_^]]^_`bbdaehilnqstv{|{||zxvttsstuvwxyvtvssu|{sttw{}umgelyqiebbdgkpsvwvtoijggikotw}}~|usuuxyzzz{~{wsolmv~{vtrqoonooqrtuwxyuuupppu|~~{zz{{zwuxz}~{soswqmifdba`__``bcdcbhhkmpqttx{}}z}|zxvuttttuvwxxtvustw~~wrtsw{zrjdfqxpjeddgjnruwwvsmijghjmqux}}zsttuwxyyz|~{xtpljo{~~yvtsqpppppqstuwxwrtroorw}~~{zzzywtuz{z|}~wpoxvqljgecbaaaabccdbdiilnqruuy|~|yy~|zxwutttttuvvtrusrsy~|rqrsw{~umgdix~vnifefimqtwxxvrkkjgiknrvz~~}wrtsvwyyxvy|xtpmms}~ywutsrrrrrstuwwxusurpptz~|{{zyvuw~y{|}|upq}}urnlihfedddddeeecgijmoqstuz{}}}}ywx~{zxvutsssttvvwuuxuvx}}|{vprquy~{smgfm}vpkihilptwz{{yunolkkmoswz~vttsuuvvww{}zwsomow~yxvuttsssttuvxxxtuurqquz|{{zxuux}z|{~zsps|wspmkjhggfffgghffkkmoqsuvw|}~~|yxz|{ywvutttuvwxxtxxwwy|||{uqrrvy~yrlhhpvqlkjmpsw{}~}{urrnnnprvy~~vwtuvwxxxx|}zwtpoqz}zywwuuttuuvwxyyyuxvtrrv{}}|{yvuy|{}|{uru|xsqnmkjjiijjkklikonqruvxxz~}zy|}{yxwvuuvvxxyxv{yy|}}yz|yyrrsty}}}xrmjktwrnmmorvz~~wwuqqprtwy}xxuvvwwwvwz}zwsru~~{zyxwwwwxyyz{{xx{wvtux}~}|ywv||~|~~ytrx}zvsqonmmlllmmmmjopqstvwxx|~|zww{|{yxwvuuuvwyy{xz}{||zwzyyvqsrux}}|zupkjmy~wronoruy}|wyusrrtvy{{ywuvvwvvvx{}{xtrsx}}zzyxwwwxxyyz{{wyyvutuy~}|zwvv}~~z|z}~|wst}|wtrponmmmmmmmnllpprsuwxyy}~}zwvw~~~{zxxvvuuvvwxyzw{{z{|{xwzxwropptw|~y|~xrnjjq{uqonpsvz~zxxssrstwz}yyuvvvvvvvx|~{xurqs{|{zyxxxxwxxyyzzxvzwutsvz~}{zwttw}|~yzyy{|~ytrv{wtrqoonnmmmnmnknqpssvvxyy~}}~~~~}zxurru~~|{yxwvuuuuvwxy{yy~|}}{vsuvutoporuy~|zw}{upkiktzuqoopswz~z{yuustuxz}|zvvuvutstvz|yvssw~}|{{zzyzzz{|{|yx{xwuux|~|yvttz}~~y{xz{}~}xssx~zvtrqoonnnnooonlqrrtvxxzz|~}~~~|zwurrw~|zyxwvvuvvwxyz{y|}~~ytrutusnporuz}}{xyzupkjmwytpoprux||~ywvvvxz|~zxxwwvuttw{|yvssx}}|{zzzz{{|}||y||yxuvx||yvttz{|zzxz{}~~xtsyzwusqpppoppqpqoputvwxz{}|~~~|zwtrsx}{zyxwwwwxyz{|}{~xsqtsvrpppsv{~}{x{zuplkozztrpqtw{~zzxxy{}zzxxxwvusuz~{xvv}~~}}}}~~~|}{zxxz~~zwuw{}}|{z{|}|wst{{xutrqqqqqqrrrosvuwxz{|}}~~~}~~}{zwuqpqx~}{zyxwwwwxyz|}~ztopqrupqpruy||}zxw~}xsollr|yurqrux|}{zzz{}|{yywvuttv{}zwvw~~~}}}~~|}|{yxxz|xutxz}{{yyz{|~zvsv|{xvtsrrrrrsssrpvvwxy{|}~}~}}~~}{zxuspps{~|zyxxwwwxxz{|}~|}vqnpprroporux|{yywtv|}zuplkmv}xtrrsvz}~|{yyz|}~{yxwvutstw|}zxuvz~~}}}}}~~~~~z}}zzxwx{|yustxy~|zywxyz{}~|wtty}}zwutssrrrrsssprwuwxz{|}}~~~~||~~}|zxvtqopu|~|zyxwwwwwxxy{||}{}wqmmoospppptvz||zvvvtrw|}|wsollpz}wtrstwz~~|{zz|}~|zywvtsrsv{~{xwx~~|~|{yyz}|yvtvzzzzxwyyz{}~~{wtv{}zxvuutttttuttquwvxyz{|}}~~}~~}{{}}~}}}}|{zyxvtqonovz}|{xxwxwxxyy{|}~{upmnnoroqortwz}~{yuuurqx{~zvqnlms|}xuttvy}||{|}~~|zywvtsrtv{}{xxz}~~}{zz{~|xvtwy|~{{xxyzz|}~~zwuw{{yxvvuuuvvvvusyyyz{|~~~~}{|}~~}}|{zyxvtrooqwz~~|zzyyyyyz{|}{uplnmqrqrpsuy{}}{xuvuru{|zvronou||xvuvx{~}}~}{zywvttuw|~{yx{}~|{z{~}yvuxx}}|{xyyz{|}~~zwuxz|zxwvvvvwwwwuv{y{{}}}}|zz|~}|||{zzyxvtrommpv{|~{zzyyzz{|}~xrmklmrssssuwz|~~}{yvtutrx|~}yuqnnpw}}ywvwy|}{ywutstw{~~{yy}}|{}|xuvxz}}zyyyz{|}~|xuux{|zxwvvvvwwwwty{z||}~~}~}{zz|~~}||{{zyxwuspnlmrv}|}zzyyyzz{|}|uokklnsqsqsuwz|}~}|{ywtrurrz||wspmmry{wuuvy|~~~|zxvtsrtw|~|yxy}|{{}}yvtvw{~{{wwwxyy{{|}~}zwuwz~}zxvvuuuuvvvusyyz{|}~~}~}{zyz}~}}||{{zyxwuspnlkmtx|{{yyxxxyzz{}~~xqlikkpqpqortvxz{|{zxvtqpsosz{~yuqmlnt{}yvttvy|~~~~}{ywusrrsvz}~~|yxx|}|{|~|xuuwx}}|ywwwxxyz{|}~~~~{wutxz}zxvvuuuuvuvsuzxz{|}~~~||~~|{ywwx{~}|{zzyyxwvutrpmkijntyz{zyyyyyz{|~zsnihklqrrqrsuwyzzzywvsroprov{}|wsommow}}yvuuvy|~|zxusrrtw{~~|yxx}~}||{wtuwy~||xwwwxxyz{|}~~~zwtux{}{ywwvvvvwvvty{z||~~~}}~}{yxxy{~}}{{zyyxwvtspnkjjpt{z}{zzzz{{|}zsniijmsssrsuwyz{{zyxvtroqrqz{{wspnnqw~~zwvwy|}{yvtsstx{~|zyz}}}|xuvw{~}yyxyyzz{|}~~zwuvx|}{yyxxxyyyxv}||~~~~~|zyyz|~~|{zzyxwvusqnlklqt}|~|{{{{|}~}xrmjkkquvvuwxyz{{{yxvusqosqt|}~zvspooty{yxy{}~|zwusstwz~~~|zz{~~|yvwx~|zyyyyzz{|}~}zwuwx}{zyyyyzyywy~|~~|~~|{ywxx{|~~}{zzyxwwvtsqomkjmqv||~||||}~|vpljjlsuvuuvxyz{zzxwusqpnrpw{}|yurpopv{~{yxy{~~{yvtsstxy}{zz}~~~{wwwx~~zyxxxyyz{{|}}~~}~~{xvvxz~}{zzyyyzyyv{~|~~~}|~~|{yxwwx{|~~||zyxxwvutsrpnkkkoryz|}{{{||}~}ysnjjkmstuttvwxyyyxwusqommpowy|}yvrpnosx~}zxxz|~|ywusrstwy~}}|zz{~}~~|yvwwz~}|xxwwwxxyzz{|||{}~~~|ywvwy}}|zyyyyyyxwv}||}}~}{|~~~}||{ywvuvwzz~|~zzxxvvuutsqonljjkps|}z|}{||}~~~zvplikkquvvtvvxxxxwvtrpnnkmoqyz~{xtqoopuy|yxxz|~}zwusrstwy}|~~~~~}|zyy{~~|xvww|~{xxwwwxxyyz{|||{}}}{xvvxy~~|{zyzyzyywx~|}}~~}{}~~}}|zywvuvxz{~}}{zxxwvutsrqomkjjmqu}}{}}|}}~~~~~yuokjklsvwuuvwxxxxwutrpnmloou{|~{wtqonpuz}zyy{}}zxusssuwz}~}|zyy||xwwx{yxxxxxyzz{||}}|~~~{yvvwy}{{zzzzzzw|}}|~~~}|zxwvvxz|}{zyxwvvtsrpnkjjmpx~}~}~~~~~~zupljknuwxvwwxyyyxwvtrqoompry|~~{wtqporv|}{z{}}{xvtssuwz}~}{yyy||yxw{{zyyyyyzz{|}}~}}~{xvvw{}|{{{{{{zy~~||}~}|{ywvuuwx|~~}{zyxwvusrpomkjjnr{}|||||xsokklryzzxxxyyyxwutrqponnqu|}}yvspoosw~{{{}~{xvtssvw{~~}{zxxy}{xww}}zyxxxxyyzz{||}||~|ywuwx~}|{{zzzzxz~}||}~}|{zxvuuuwy|~~|zyxwvutrqomkiikot~~~~|{{{|~zvqmkkmtxyxwwwxxwvutrponmlmpw{}~{wtqonpuy~|zzz||zwtsrsuwz~~~~~~~~}|{zxxx{|ywwx~{yxwwwwwxxyyzzzzz||zwuvwz}|{zzzzyyw|}~~}|{|}~}|{zxwuttuwx{~|zyxwvutrqpnljijmpw~|~~~~}{yyxz{wsollmpwzzxxwwwwvtsqpnmlkklpxz}~{xuronoqv|~|zz{||ywtsssuvy~}|||||||{{zyxwwx||yxxz}zywwwwwwxxyyzzzyy}}{xwuvx{~||{{zzzyx~~~~}{{|~~}|{zywvtttuvwz|{yxwvutsrqomljjkor{}}{yxyxzzvrolmmryzyxxwwwvutrqonmllkmsz{~~{wtrooptx~|{{|~|ywusstuvz~~}}|||||||{zxwwy||yxx}}{yxxwxxxxyyzzzzyz}{ywvwx~~}|||{{y{}{||~}|{zxwuttuvw|~|{yxwvutsrpnlkklot~~{yxxx{zvsommnu{|zzyyyxwvusrponmmlow{|}zwtrppqux~}}}zxvtttuv|~~~~}}}}|{zyxxz||zyy~|zyyyyyyzzz{{{{z{}{ywvwx~~~}}{}|||~}|{zxwuttuvv}~~|{yxwvutsrqomllnqw|zxwwwzyvspooqy}}{{zzyxwutrponnmmmr{|~|yvtrpprvz~~~~{yvuttuv~~}}||||{{zywwwz|{zy{}{zyyyyyyyzzz{zzy}~|zxwww{~~}}|{~|{|}~}}|zyxvuttuuw~~|zyxwvutsqpnmllor{~{ywvvwzxtrooot|}|{zyyxwvtrqonmmlmlt||~{xusqppsw}~}~|zwutttuw~}}||{{{{zyxwvvx{~}zzx}~|zyxwwwxxxxyyyyyw}~|zxwvwx}~}}}||z{~|{{|~~}|{zywvusssuux~|{yxvutsrqonlkkmpt~~|ywvvuvyvspnopw||{yyxwvutrqonllkkklv{{~~|yvspooptx~||}~}{xvtsstty~}|{zzyyxxwvutttwz|zyy|zxwwvvvwwwwxxxxwv~|{ywvvwy~}||{{y}}{z{|~}|{zyxwutssstt{~~|zxwvutsrqonlllorx|zwutttvxurpoprz~}{yxwvutrqonlkkkklny{}}zwurpooqvz~||}~~|ywutstt|~~}|{zzyyxxwvuttux{|zy{}{yxwwwwwwxxxyxxwx}|zxvuvwz~}}||{z|{z{}~}|{zxwutssstt~}{yxwvtsrpomlklor{|ywutttwxusqpqv}~|{yxwvtsqonmlllmlr}|}zwurpoprv|~~|ywutttu~}|{{zzyxwuttux||zy~}{zyxxxxxxyyyzyyw|~}{ywuvw}~}|}~|{|~~}|{yxvutsstv~|{yxwutsqpnmlmpt~}zwutttwxvtrssz}|zxwutrponnmmmonw~}zwurqpqtw~{xwuuuw~}|{zzyyxwvttsuw}~|zz~|zyxxxxxxyyyzyyw~|zxvvvx~}}{{|~}|{zywvttsssx}|zyxwutsrponmorx}zxvtsstwwusrst}~}{yxvusqpnmmmmmnoz~~~{xvsqppquy|zwvttsy~}|{zzyxxwvutttvy}{z{~|zyxxwwwxxxxxxxww}|zywvvwz~}}|}|{{|~}|{zywvussrssz}|{yxwutsrponnnqs{~{yvusssswvtsrsv~~|{yxvusrpomlkkllloz|}~{yvtrpppsw|~~~}{xvusts|~}|{zyxxwwvutsstwz~~~|{z}|zyxwwvvvwwwwwvvuw~~~~}}{zywvvwx|~}|}~|{{{}~}|{zyxvutsrrrs}~~|{zxwvutsrqooopsw~{ywtsrrrtwuttttz~|zywusrpnmlkkkkllr||~~|zwurqopqtx~}}~}{ywuttu~~}|{zyxwvvutssrsuw{~~~~|{z|zyxwwvvvvwwwwvvtz~~~~~~~~~}|{zywvvwy~~|~|{{{~}|{zyxwutsrrru~~}{zywvutsrqpooqty~{ywusrrrtwutttv}~|zywusrpnmlkkllmmv}}~|zwurqppruy~~~{ywutsw~}|{zyxwwvutsssuw~~~}|z{}{zyxwwwwwxxxxwwv}~}|{yxvvwz~|{{|~}|{zywvutssrx~}{zywvutrqpooqt{~|ywusrrruwuutux}{yxvtrqonmmmmnnpz~~~|ywusqpqru{~~|ywutt{~}{zyxwvutsrqrtw~~~}|{~}{zyxxxxxxxxxxxvx~}|zxwvvw{}|{~~}|{zyxvutsst{~|{zxwvusrqpqsv}{xvtsrrsvxvvvv|}{ywusqonmmmmmnot}}{yvtrqpqsw~}~|zwvuu}~|{zyxwvtsrqqruy~~~~~|{{~|zyxxwwwwwwwwwwuz~}|{yxvvwx~}|{|~}|{zxwutsrru}~}{zxwvusrqppqty}{ywusrqrrvwvvvx~{ywusqpnmllllmmnu|~}{yvtrqopqtx~~}|~}zxvutw~~}{zxwvutsrqpqsv|~}}}}}|{|}{yxwvvvvvvvvvuuu{~~~~~~~~}}|{zywvuuwz~}}}~|{{|~}|{zyxvutsrrrw~}|zxwvtsrpoopru|~|zwusrqqqsvwvww{|ywusqomlkkkkklmpw|~}|ywusqpoprv{}}~||}}zxvuty~|zywvusrqpoopsv~~}||||~||{~~{yxwvvuuuuuuuuusu{}~}}}}}}}|{zyxvvuvx|~~}~~|{{}~}|{zyxwutsrrry~}|zyxvutsrqqrtx~|zxvtsrqqrsvwvwx}|ywusqomlkkkklmmry|~~}{yvtrqopqsw}}}~||~|zxvuu{~}|zywvutsqpppqtw~}||||~||{|zyxwwvvvvvvvuutx|~~~~~~~}}|{{yxwvuwx~~~|||~}|{zyxvutsrt{~}{zywvutrrqruy}{ywutrrrrtwwwwy}zxusrpnmmllmmnov{~}{ywusqppqtx~}}~}{xwuw}~}{zywvutrqqqrty~}||}}|{|}{zyxxwwwwwwwvvv{}~~~~}|{zywvvwy|||~}|{zywvttsw}~}{zyxvutssuw}~}{ywutssssuxxxy{|ywusqonmmmmnopry|~|zxvtsqqqruy~~|}~{ywvz~~}{zxwvtsrqpprt{~}|{{|~||{~~|{yxxwwwwwwwvvuv{|~~~~~}}}|{zyxwvvw{~~||}~}|{zyxvutssy}}|{zxwvutstux}|zxvussrrsvxwxy}~{xvtrpommllmmnosy{~}{ywusrqqqsv|~}~}|}|zxvv{}~}{zxwvtsrqppqsv}|{{{|}||{|zyxwwvvvuvuuutsw{{}|}|||||{{zyxwvvwx}~}~}||~}|{zyxwutsstz|~}|{yxwvutsstvz}|zxwutrrqrsvxwxz~|yvtrpomlkkkllmnty{~~~~}{ywutrqpprtx}|}||}}{ywuv|}~|zywutsqpoooqsx}|{zzy|}{{|~{yxwvvuuuuutttssxz{|{|{{{{{zyyxwvuvwy~}}~||}~}|{zywvutsrv{}~|{zxwvutttvx~~}|zywvtsrrrrswyyz|}{xusqomllkkllmnovy{~~~~}|zxvtsqppqruz||}||}}{ywvy}}~|{ywvtsqpoooqtz~}|{zyy||{{}}{yxwvvuuuuuuttstyz{}|||||{{zzyxwvuvw{}}~||~~}|{zyxwutssx|}~}|zyxvutttvy~}{zxvutsrrrtwxyz~~{xvsqonmlllmmnoryz~~|{ywusrqpqsv|}}}|}~|yxw{}~}{zxvusrqoopqu}}|{yyy|{{{|{yxwwwvvvvvuutw{{}~}}}}|||{zyxwvuvx}~~~}}~}|{zywvuttz|~~|{zxwuttuvz}|zywutsrrruxyz{~{xusqonnmmnnopqv{|~|zxwusrqqqtw}}}|}|zxy~~~|zxvusqponoqu~}{zyxy{{{|~|zyxwwvvvvvvuuuy|{~~~}}}}||{zyxvuuvx~~~}}~}|{zyxvutv|}~}{zywvuuvx}~|{yxwutssssuxyz}}zwtrponmmmmnopqx{|~}{yxvtrqpqruy~|}}|}~{yxz~}~}{ywvtrqpooprx}|zyxwy{{{}}{yxwvvvuuuuttstyzz}||||{{{zyxwvvuvwz~|~}}~~}|{zyxvutsw||~}{zywvuuuwz~}|{yxvutsrrrsvyy{~~{xusqonmllllmnorxy{~~~}|{ywutrqppqsv{}{|||~~|zxw{}}~}{yxvtrqpoooqt|}|zyxwwy{{{~{yxwvuutttttssrtyxy{{{{{zzzyxwvvuuvx}~}|~}}~~}|{zywvutuz|}~}{zyxwvvwy|~|~}|{zywvutsssstwzz||yvsqonmlllllmnotyy{~}}|{yxvusqqpprtx~|{|||~|zxx}~}~}{ywvtrqpoopru~~}{zyxwwyzz{|zywvvuuttttssrrvyxy{{{{zzzyyxwwvuvwy~}|~~~~~}|{zywvutv{|}~}{zyxwvwxz~~}}|{zywvuttssstwzz}|yvtrpnmlllmmnoqvzy|~~}|{yxvtsqqpqruy}|}}}~|zxz~}~~}{zxvusrpppprx}|zywwwyzz|}{zxwwvvuuuuttstxzy{||{{{{{zyyxwvvvwz}~~~~~}|{zxwvux||~~|{zyxwwxz~~}|{zywvuttttuxzz~|ywtrponnmnnoopsyz{~~~}{zxwusrqqrsvz}}}}~|zy|~}~|{ywusrqppqsy}{zxwvvxyz~}{zxxwvvvuuutttuzzy{}|||{{{zzyxwvvvx|~~~}|{zywvv{}}~}|zyxxxy|~~}|{zyxwvutttuxz{{xvsrponnnnoopquz{{~~}|zywvtsrqqrtw}||}}~|zz~~}}|zxvtsqpppqt}~|zywvuvxy{}{yxwvvuuuuttssvzyy{|{{{{zzyyxwvvvwx~~}}~~~}|zyxwvw{|}}|{yxwwxz~~}~}|{zyxwvuttstvy{|}zwtrponmmmmnooqvzy{}~}|{yxvtsrqpqruy}{|}}~|{yz~}}~|zxwusqpooprw~|{yxvuuvyz|}{yxvvutttsssrrrvxwxz{zzzyyxxwvvuuvwz~}|}~~}|{zyxwvux||}~|{yxwwwx{~|}~}{{yyxwvutssstvz{~~{xurpommlllmnnoqwyyz||{zywvtsqpppqsv{~|{|}~~|zy|~}~~|zxvtrqpooqsy~|{yxvuttvyz~~|zxwvuttsssrrrqswwvxzzyyyyxxwvvuuuvx|~||}~}|{zyxwvvy||~~|{yxwwxy|~}|~}||{zyxwvuttsstwz|~zwtrpommlmmnnopsxyy{}|{zywvtsqpppqsw}~|{|}}|zz}~}~~|zxvtrqpopqt|~}{yxvuttvyz|zywvuutttsssrruxxxy{{zzzyyxxwvvuuwy~~}}~~}|{zywww{}}~}|zyxxxz}~}~}}|{zyxwvuttttwz|~{xusqponnnnooprvzz{|~}|{yxvusrqqqrtx~||}~~|z{~~}~~|{ywusrppprv}{zxvuttvx{}{zxwwvuuuttssswyxxz{{{{zzyyxwwvuvwy~~~}|{zxxy}}}~}{zyyy{~}~~}||{zyxwvuttuxz}}zwusqpoonoopqrsy{{{}~}|zywvtsrqqrsuz}|}~~}{}~~~}{ywusrqpqsx~|{ywvtstvy|~|zyxwvuuuttssstxyxxz{{zzzyyxxwvvvvx{}~~}|{yxx{}}~}|{yyyz|~}~}||{zyxwwvuttux{|yvtrponnnnoopqtyzzz|}|{yxvutsrqqrtw|~||~~}{{}~~}{zxvtsqqqrt{~|{yxvutstwy~}{yxwvutttssrrrtwwwwyzyyyxxxwwvvuvwy~~}~~}|{zyxww{}|}~|{zyyy{~}||~~~}|{zzyxwvvutttvy|}zwtrponmmmnnooquyyyy{{zyxvutrqqqqrux~|{|~}|z{~~}}~~}{ywutrqqqsv~~~~~|{zxvutsstwz}{yxvuutsssrrqqqtwvvvxyyxxxwwwvvvvvxz}}~~}|{zyxwy}}}~~|{zzyz|~|{|~}||{{zyyxwvuuuuwz}|xvsqpnnmmnnnoprvyxyy{{zxwvtsrqqqrsvz~|{}~~}|{|~~~~|{ywutrrqrtx~}|{yxvusssux{}{zxwvuttssrrrqrvwvvwxyyxxxwwwvvuvvx{~}}~~}|zyxxz}}}~~|{zyyz|~}|~~}||{zzyxwvuttuwz~|yvtrpoonnooppqtxzyzz{{zywvusrrqqrtw|~}|}~|{~~~~~}{ywvtrrqrtz~}{zxvtsrrtx}~|zyxwvuuttsssrtxxwwxzzzyyyxxwvvvvwx}~~~}|{zyy|~}}~}{zzz{}~}~~}}|{{zywvutttwz~{yvtrqpoopppqrsvzzzz{|{zywvutsrqrsux~}|~~}}~~}|zxvtsrrsv}~|{ywutrrrtx~~|zyxwvuuttssrsuxxwwxzzyyyxxwwvvuvwy~~~}{zyz}~}~~|{zzz{~}}~~}}|{{zxwvuttuw{~{xvsrqpoooppqrswzzzzz{{yxwutsrqqrsuy~||~~}|}~~~}{ywusrrrsx~}{yxvtsqqruz}{yxwvuttssrrrrvwwvvxyyyxxwwvvuuuvx{~}~~}|{zyxz}}}~~}{zyyz|~}|}~}}||{zyxwvutttux}|yvtrpoonnnoopqswyxxxyzyxvutsrqqqrtw{~}{|~}|{}~}}~}{zxvtsrrsu{~~~}{zxvusrqqsv|}{yxvuttssrrqqqrvvuuuwxxwwwvvuuuuuvy}~}~}|{zyy{}}}~~|{zzz{}~||{}~}}||{{zyxwvutttvy~{xusqponnnnoopqtxxxxxyyxwutsrqqpqrtx~~}|}~}|{{~~}~~}{zxvtssstw}~~~~~~}|zywvtsqqqsw}}{ywvutssrrqqpqsvvuuuwxxwwwvvuuuuvwy~~~~}|{zyy|}}}~}|{zzz{~}||~~}}||{zyxwvutttvz}zwusqpoooooppqrvyyxxxyyxwvusrrqqrsvy~}|~~}|}~~~}|zxvutsstx~|{zxvtsrqqsx}{yxwvuttssrrrruwwvvwxyyxxxwwvvvvvxz~~}{zz{~~}~~~}|{z{|~}~~~}}|{zyxwutttv{}zxvtrqpppppqqrtxzyyyyzzywvutsrrrstv{}}~}|~~~~~|{ywvtttvz~}|{yxvtrqqqsy~}{yxwvuuttssrrsvwwvvwxyyyxxwwvvvvwx|~}|{{}~~~~}|{z{}~}}~~}}|{zxwutttw|~|ywusrqqppqqqrsuyzzyyyzzxwvutsrrrsux}}}~}}~~~}|zxwuttuw|~}|zywutrqqru{}|zyxvuuttssrrrtwwvvvwxyxxxwwwvvvvwy~~}|{{{}~~~~}|{{{|~~}}}~~}}||{zywvuttux~}{xvtrqppppppqqruyyxxxxyxwvussrrrrsvy~}~~}|||~}}~~|{ywvuttuy~~~~~}|zywvtrqqqrv}~}{yxvuttsrrqqqqtvuuuuvwwwwvvvuuuuvx{~~~}|{zz{}}}}}~}{{{{}~}|||~~~}}|{{zywvutttvz|ywusqppooopppqsvxxwwwwxwvusrrqqqrtv{}}~}||{}~~}}~~|{ywvuuuw{~~~~~}}~~~}|{yxvusrqpqsx}|zxwvutsrrqqppqtuuttuvwwwvvvuuuuvwy|~}|{zz|~}}}~}|{zz{}~}||}~~~}||{zywvutttv{~{ywtsrqppppppqrtwxxwwwwwwutsrrqqrstx|}}~}|||~~~}}~}|zywvuuux}~~~~~}|zyxvtsqppqsy~|zywvuttsrrqqqsuvuuuuvwwwwvvvuuuvwy~~}|{{}~~}}~}|{{z{~~}}~}}|zyxvutstv|~|zwutrrqqqqqrrsvyyyxxxxxwvutsrrqrsux~~~~}|}~~}}~~|{ywvuuvy~~}{zxvtsqppqtz~|{yxwvuttssrrrtwwvvvvwxxxwwwvvvvvwz}|||~~~~}|{zz{~~~~}|{yxvusstw}}{ywutsrrrrrrrstwzyyxxxxxwvutsrrrstvz~~}|}~~}~~|{yxwvvw{~}|zywutrppoqu|}|zywvvutssrrrruwvvuvvwxxwwwvvvuvvx{~}|{}~~}}~~}|{{{}~~~}}{zywutsstx~|zxvtsrrqqqqrrstxyyxxwwwwvutsrrrrstw|~~~~}||~~}}~~}|zxwvvvx}~~~}|{yxvtsqppprw~~|{yxvuttsrrqqqruvuutuuvwwvvvvuuuvwy}~}|{{}~~}}}~}|{{{|~~}}}~~}|{zyxvutstvz}{ywusrqqpppqqqrtwxxwwvvvvutsrrrrrtvy~~~~~}}|{|~~}}}}~~}{zyxwwxz~~~~~~}}~}}|zyxvusrqppqsy~|{yxvutssrqqpppruuttttuvvvvvuuuuvvxz~}|{{{}~}}}}~~}|{{{|~~}}}~~}|{zywvutstv{~|zxvtsrqqqqqqqrsuxxwwvvvvvutsrrrrstwz~~~}||{|~~}}}~~|{zyxwwx{~~~~}~~~}|{yxvusrqppqtz~|{yxvuttsrrqqpqsuuttttuvwvvvvuuuvwy{~}||}~~~}}}~~}|{{{}~~}~~}|{yxvutstw|~|zxvusrrrqqrrrstwyxxwwvvwvutssrrssux|~~}||~~~}}~~}{zyxwxy}~}|{zxwutrqppqu{~|{zxwvuttsrrrrsuvvuuuuvwwwwwvvvvvwy|~}||~~}}~~~}|{{|}~~}|{yxvtsstx}}|zxwutsssssssstvxyyxwwvvwvutsssrstux}~~}|}~~}}}~~}|zyxxxz~}|{yxvusqpopqv|}|{yxwvuttsrrrrsvvvuuuuvwwwwwvvvvvxz~~}}}~~~}}~~}|{{|~~}|zywutssty~~}{yxvutssrrrssstvyyxxwvvvvvutssrsstvz~~}||}~~}}}~~}|{zyxxy|~~}|{zywutrqpopsx}~}|zyxvuttsrrqqqtuuuuttuvvvvvvuuuvwx{~}|||~~}}}}}~~}|{{{|~~~}|zywvtsssv{~|zxwutsrrrqrrrrtvxxwvvuuuutssrrrrsuw{~~}}||{{|~~}}||}~~~}{zyxxxz}~~~~~~~}|{zywvtsqpopqtz~~|{yxwutssrqqppqsutttsstuvvuuuuuuvwy}~}|{{|~}}|||}~~}|{{{{}~~~~~~}|{zxwutsstw|}{yxvussrrqqrrrrtvwwvvutttttsrrrrrtux}~~}}||{{{|~}}|||}~~~}|zyyyy{~~~~~~~}||zyxwusrpoooqu{~~}|zywvutsrrqppprtttttsstuvvuuuuuuvxz~~}|||}~}}|||}~~}|{{{|~~~~~~}|{zxvussstx|~}{yxvutssrrrrrrsuwxwwvuututtsrrrrstvz~~}}|{{|}~~}|||}~~~}|zzyyz|~}|{zxwutrqpoprv{~~}|{yxwvutsrrqqqsuuuutttuvvvvvvuuvwx{~}|}~~}}|}}~~}|{{{|~}|zxwussstx|~~~|{yxwuttsssssstuwyyxwwvuuuuttsssstuw{~~}}||}~}}}}}~~}|{zzz{~~}|{zywutrqpoprw|~~~}|{zyxwvutssrrqrtvvuuuuuvvwwwvvvvvwy|~}}~~}}|}}~~~}|{{{}~}|zxvtsrsuy|~~}|zywvuutttsssttuxyxxwvvuuuuttsssstvx|~~}}|||}~~~}}|}}~~~}|{zzz|~}|{yxvusrppoqty|~~~}|{zxwvuttsrrqqsuuuutttuuvvvvvvvvwxz}~}}}~~~}|||}~~}||{{|~~}|{ywvtsstvz}~}|{yxwuutssssssstuwxxwvuutttttsssssuvy~~~}}||{{{}~~}||||}~~~}|{{zz{}~~~~~~~}|{zxwvtsqppprvz}~~}|{zxwvutssrqqpqrtttttsttuvvvvvvvvwy{~}|||~~~}||||}~~~}||{{|~~~~}|{zxwutssux{}}}{zxwvutsssrrrrssuwwwvuttsttsssrrstux{~}}||{{zz{}}}|||||}~~~~}||{{{|~~~~~~~~}|{zywvusrqpppsw{}~}}|zyxwvutsrqqppqstttssssttuuuuuuvvwy|~}||}~~}|||||}~~~~}||{{{}~~~}|{yxvtsssux{}}|{zxwvutsssssssstvwwwvutttttsssrsstvx|~~}}|{{{{|}}}|||||}~~~~}|{{{{}~}|{zywvtsrpppqsx{}}}}|zyxwvutsrrqqqrtutttttttuvvvvvvvwxz}~~}}~~}}||||}~~~~}||{{|}~}{yxvtsrsuy{|}|{zywvvuttttttttuwxxwvvutttttsssstuvy}~~}}||{|}~~}|||||}~~~}}|{{{{}~}|zyxvusrpppqtx{}}}}|{zyxwvutssrrrsuuuuttttuvvvvvvvvwxz~~}}~~}||{{|}}~~}||{{{|~~|{ywutrrsvy{|||{zyxwvuuutttttuvxxxwvvutttttsssstuwz~~}||{{|}~}}|||||}~~~}}|{{{|~}|{zywvtrqpopquy{|}}|{zyxwvuttsrrrrtuuuuttttuvvvvvvvvwy{~}}}~~~}|{{{||}}}}||{{{|~}|zxvtsrrtwz{||{zyxwvvuuttttttuwxxwvvutttttssssstvx|~}}|{{{{|}}}||{{{|}~~~}||{{|}~}|{zywvtsrpoopsvz|}}}|{zywvutssrqqqrsttttsssstuuuuuuvvxz}~}||}~~}|{{{{{|}}}||{{{|}~}|zywutsrsuxz{||{zywvuuttsssssstvwwvuutssssssrrstuwy}~}||{{zzz{}}||{{{{|}}~}}}|||}~~~~~}|{zyxwutrqpppqtwz||||{zyxvutssrqqppqsttsssssstuuuuuvvwy{~~}}||}~}||{{{{{|}}}||{{{|~~~}{zxwutsrsvyz{{{zyxwvuuttssssstuwwvvuttssssssssstuw{~~}||{zzzz|}||{{{{{|}~~}}}|||}~~}|{zyxvutrqpppruxz{||{{zywvuttsrqqqqrttttssssttuuuvvvwxy|~}}}~~}}|{{{{{|}}||{{{{|~}{zxwutsrtvyz{{{zyxwvvuuttttttuvwwwvuuttssttssstuvx{~~}||{{{|}}}||{{{||}~~}}}|||~~}|{zxwvtsqpppruxz{||{{zyxwvutssrrrrtuuuttttttuvvvvvvwxz}~~}~~}||{{{||}}||{{{{}}|zxwutsstwyzzzzyyxwwvvuuuuttuvwxxwwvuttttttttttuwy}~}}||{{{|}}}|||{{||}~~~}}||}~~}|{yxwutrqppqsvyz{{{{zyxwwvutssrrrstuuutttttuuvvvvvwwy{~~}}~~}||{{{{{||||{{{{|}~}{yxvtsrsuwyzzzzyyxwwvvuuutttuvwxwwvuuttsttttttuvxz~~~}||{{{{|}}||{{{{||}}}}}}|}~~}|{zywvtsrqppqtwyz{{{{zyxwvuttsrrqrstuttttttttuuvvvvwxy{~}}}~~}}|{{zzz{||||{{{{|~~}|zxwutsrsvxyzzzzyxwwvuutttttttuwwvvuttsssssssstuvx{~~}|{{zzz{||||{{{{{{|}}}}}}}}~~~~}|{zyxvutrqppqruxzz{{{zyxwvutssrqqqqrtttssssssttuuvvvwxz}~}|||}}||{zzzzz{{{{{{{{|}}|{ywvtsrrtvxyzzzyyxwvvuuttttsttvwvvuttsrrrrsssstuwy}~}|{{zzyyz|||{{zzz{{|}}}}}}}~~}|{zxwvtsrqppqsvxzz{{{zyxwvutssrqqqqsttsssssssttuuuvvwxz~~}||}}}|{{zzyyzz{{{{{{{|}~}|zywutsrstwxyzzzyyxwwvvuutttttuvwwvuttssrrssssttvwz~~}}|{{zzz{|||{{zzz{{|}}}}}}}~~}|{zywvusrqppqsvxyz{{{zyxwvvutsrrrrstutttttstttuuvvvwwy{~~}}~~}}|{zzyzzz{{{{{{{|~~|zywutsrsuwxyyzzyyxxwwvvvuuuuuvwxwwvuttssssssttuvx{~}||{{{{|}}|{{{zz{{|}}}}}}}~|{zywvusrqpprtwxyzz{zzyxwvvutssrrrsuuutttttttuuvvvvwxy|~~}}~~}|{{zzyyzz{{{zzz{|}|zxvusrrsuwxyyyyyyxxwwvvvuuuuvwxxwvvuttssssttttuwy|~}}|{{zz{|}||{{{zz{{|}}}}}}~~}|{zxwvtsrqpqruwxyzzzzyxxwvuttsrrrrtuuttttttttuuvvvwwyz}~}}}~~}|{zzyyyzz{{{{{{|}~|{yxvtsrstvxxyyyyyxxwwvvuuutttuvwwvvutssrrsssttuvxz}~}||{zzzz{||{{{zzzz{{|}}}}}~~}|{zywvutrqqqrsvxyyzzzyxxwvutssrqqqrstttssssssttuvvvwxy{~~}||}}}|{{zyyyyzz{{{{{{|~~}{zxwutsssuwxyyyyyxxwwvvuutttttuvvvuutssrrrsssttuwy{~}|{zzyyz{|{{{zzzzz{{|}}}}~~~}|{yxwvtsrqqqrtwxyyyzyyxwvuutsrrqqqrsttsssssssttuvvwwxz|~}}||}}}|{zzyyyyzz{{{{{{}~}{zxvutsssuwxxxyyxxxwwvvuuutttuvvwvuutssrrrssttuvwy|~}|{{zzzz{||{{zzzzz{{|}}}}~~}|{yxwvtsrqqqsuwxyyyyyyxwvvutssrrqrstttttssssttuuvvwxyz}~}}}~~}|{zzyyyyzzzzzz{|}~}{zxvutsstvwxxxyyyxxwwwvvvuuuuvwwwwvuutssssstttuvxz}~}||{{zz{|||{{{zzzz{{|}}}}~~~}|{zywvtsrqqqsuwxxyyyyyxwwvuttssrrstuuutttttttuuvvwwxy{~~}}~~~}|{zyyyyyyzzzzz{{}~}{yxvtsrstvwxxxxyyxxxwwwvvvuuuvwwwwvuutsssssttuvwx{~~}||{zz{{|||{{zzzzz{{||}}}~~}|{yxwutsrqqrtvwxxyyyyxxwvvuttsrrrstuuttttttttuuvvwwxz|~}}}}~}|{zzyyxxyyyzzzz{|~}|zxwutsrsuvwxxxyyyxxxwwvvvuuuuvwwwvuutssrrssttuvwy|~}||{zzzz{|{{zzzyyzzz{||}}~~~}|{zxwvtsrqqqrtvxxyyyyyxwwvutssrrqrsttttssssssttuvvwwyz}~}|||}}|{zzyxxxxxyzzzz{|} \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m37.bin b/applications/SprPoP/dist/MUS/m37.bin deleted file mode 100644 index 6f0fb38..0000000 --- a/applications/SprPoP/dist/MUS/m37.bin +++ /dev/null @@ -1 +0,0 @@ -|ys|}v{~z{}n}{~v|}}x~|}yxywymzxz}~~z{{w~zvv~s}vv~~v|{u|ynyyql{uznmt|xjy}v~|x~s}}so}z~~yzyzx}z|jr}w{yw|{vx~~xpzzuz}t}rx}}yqotqy}~~w~z{}so~~}}toggywwy~{wuiiy|~~~}|{~~uzzypqqwst{~w{rs}~tmmt{yutpusv{{|}~xzovtvzz|zr{}~uwtuv{{zqpp~}t~{ru~{vw~z{v}}}yrqzsyxwnhortxxqsvpw{|z}~~wv~}{{~}wy{monv|u{}}z{tmpvwu{krpvxlkt~z{{~|~zpqz|{|rrzgluzz{vxztvsqsypy}svx}}t|{nlklnq|~rq{}{~}||{trnw|}sw|uxu|z|}xt}yu{zsjk}rgpstt{{yuy||~y}{qw~{xq~||||ytsust~}~u||zysswxpuzutuypyqmt|}~{z~z||||vptz~wxp|pltyxvtpwwruyyn~}{kryynx|xqokllr|wtz~y}yx~xxut~~z|zv{}}}vz|z~y~z{~txwozvoqtmuyzx}}{x|}|y|~}}rqvvs~y}wyxwvsuszx{~z~{zx~zpws{yrr|xlqw{|~y}~z|zy}~}~~ysx}}vqx~wz{zyvuztw{xvwv}wiu}}wyyoy{wpooy}}~}}|~pty|wzvv{xrxrzz~{|yy|wz}{zxrsnnyrjry|~~~xtz}}}|yyworyz|utrvv{{~{wvzy~~rpytyyvp}}ttuww}}|yv~y|~{ovs{|v|{w{o{{r}}zy}{zrxvytvssvwwyx}}~yz~xzuupsrrz|zsyxyyxytrv}xmtzpxvqxwwvyuy}xy|}~|w~}{vy}zx{v~|||~y~r}{|z}~zz|~{u{{nzmzzqsy||{y|xy{xtwx{{|~x}|{zxsy|~y~r||utu{~st{x|xzrvyutyyx|w{}|}z{rwwx{y}}puv}|~s}~{||y}z~uyx~xsvqnt{~||}v{}}~~w~{}x|trsz~vy}yx{vw~|||vxq~~wpquru~vz{yxz||{{yvxw{z|{zx{{|owys{{yy{{~{{w{vzrqxurx|}}{~{y}{z}urvtvyzvzzz~{zz~{uvvx~}~|qx{{zt}sq{~|yxzytz|{|}}~{{x|y{vyu{~||q}x{~v~|xy{}{tw}xzs{yqrz~}~x{{}||uvuvx||v}zv|~~xvvu{~pq{xyr~mzxtz}||x}{xx}~~z~}u|ytvzz~yx}}}w~{x}z}|sy}|xtu~tsurt{}~~|~}{wz}}yusv{}wzvt~}zx{vxvzy}}~xqvx|orv}~y{||{{t}~w{zzz||y|y~}zvzx{z{vt~|}~{~zwz|u~~orxusz~~}zw}}~tuuvvutw~{~y|u|rwzx~yy~vzsx{os{|{}|xt}}~~~|}|{~{y|}u}v|}z~}yz{|~wuz{}~wqwy{~zyzvt{|z~x{z}xvvvz~|||u~|{|~xu{t|xx~zwuu{hy}pt}~z|{y}}y|zy~zuzz{|}}}|~|}y~~wut|}urz|~~}zw{zw}tuwrsy~}x}|xwxxwty{sz{{w|v{}wy~}wu|zzvwxoz~}{z{}~wvzz|||{|y}z|{{~xy|~yw~uw~~z{~}}|yux{yzvsvywtw{t{}}~}|{xxvstz~}}~{zy{w|xw~zv~|~x{zn~zlr{~~~vyy|{}}~~}~~|~}yz}y}}{}~zyx{}tv|{|zwtw}v||z{yx}~uy}~~zz{||z|vx~~~}y~uwur~y{}zwzyzrn{mrw}|w{}y~xzy{{|zx{zz|}~|w~ws|zvtx}|~~}~~}}~{wwwvtz~{}}|}~xzutruoew{~~~vx}|~{swzpw|zsq{s|yztwvsovsvgooruy}o|w}z{}xv{ptztnv}zuknrsiupquyz{~ysxv}~txzo|~vsvoqzoiv{u~yxqxp}tzzq}yuy}svnr|s|xmrzu|zvxu~zl}swssgpxpr|vt}~~}|~stu}tswrursxxrzzmwlu}yxytvptxvr{}tr~|t{{qy{~~pw|lz{yyvrmu|{xxzvl|r{}~~}~}ny~lzphswzuqyxvtow{y}}|{~}uz~tquymspwvsrtut~|w|y}}{|{y{xtxz{|ywyyzzxyyu~v}uouz}qlzqjtsnszkv}wz}}uuninnjly{{~~{{~qqz||y}{vxz{wt|zwytpvzsy}{y|{rxxmrzwvtx}mwuu{pu{|}y{{morvuuns{qqzqzio~zz}utxpquwux|svwr}~w~|u{rz}xyzqn}jpyrlt{ry~}zy|x}qouppw}yy|ynuyuz{}z{zvyvxvzuxss|~qs|mt||w}|ptwsqpojq}yx|rpz~vq|}|}|{qw{||~uzxv|vyrjzx}~wx~x|zzwu|}y|}}~yxv}xo{yx|yx}uopij{}xsllhywlt}s~~~}v{zz|qwxysuzxvzxwuqpju|||u{|~}}~wz}}n~pxljqrv{xsy}}hy|~|u{|~ozzw{tz}|xwpzzu}sorumwz}||{v{v}}vip|xv~lx{y~xtxmz~|svtu{w{rtger{|zwnx{zrs}z{}}|v}z~~wryyt}zrsvls}}~~~nlwu|yxzvzvqy{z}rm~x~~z}y~{{}~xwr~{{rkru~}wrjmx{|t~vw|z{odv{y{~yv{iiruqq{{trw}{~~}w~~vy|u~uu{ytstrwuxti~y{zyzyqvqoyvtv}y{~zuvz}~}{}|{v|zp{}{mx~lfjkmmmm{rlr|zsu|{u{~}tpomflz{}zzyyw~|u{||voszutvtuts~xty~zzw|uv|t}s~}z|zouuvv{zzvw~|z}{w~~}xkzqzyvpigffjuyppy}~rt||}~sxtrp~mlrrlp~}{z|{}}~}krwoqutorrpxs|mw~~xyytzy{yx~zwrrry}zx|x}~y{vlw{}vnyuliijhozx|~~vr|}wz~}vxnmprpnu|}xy}|w}yyzwswxvqkjtu{uvz}}ytvryzy~x~sy~}|xuuwxy||}}r~|wr{ql{zrnkijkhx}utt~zy|yyv}w{xnmopuv}z}|zv{}~zzvoy{wwvqmuz|yx}x|}v}}}~|}s~t|xxxz~{|~{}~}}|u||wny|vv}|skggpr|~{y{}|~rs|w~wzwzuvwu}~|z}y~yv{{||uwsuyzxtqs}zvsw|}||}}y{yx~|~zx{~}~~~}~zw{|uxwu}ssytpmmy~vy~}}~tw~zsu}}y|ztxy~}|{y{v||~}}}st{|zxtvx|xxx{|wyz|xzzr~w~~~~{}}~|~~yr{|{~~{~zywz}{vtoq{~z|ut}~}y~|z{{|x~yz{wz~~vz|x}~|yzxz{x{zz|}yxz|yy|zyz}}|~|}}}}|~}|~|xzyr||y~rx~{wwxv|}}{x}|zwy}|~|{yz||{||}y{|{zx~wz}z~|ysw}}v|~|wyx~~~z~~}zw{v|{}~~~}~}~|}vv}y}~~{{{{}|zv}~||||}}}|xz~}{~}z|z}{yz|||}xw}xyx{xy{{~{z}|zu}~x}{y~{||y}}{|{~~z|{|~~{}}z|y~v}z{uyzu{{}{yu|~}{}|}~y{z}x}}~|}o~}yv}~y{tutrxivrlx~{yr}y{vwwo~~qoz~}{ts}w~xux~snr||wq}|v~}|qusv|sxzv}uytq~wu}}{x|}|wy~o}vryqwvz~ty{~zt~}~~yuzzyxnvwt{||}w~v{~tqrqq}~{|vy~zw{wkp{}~~nkt~{xsju~}|}~}yz|{~xwz{yryt}vvvvyq~~mw|}xy|zuw{|y|{}wwz{yyx{ts~zzvvx|w~{}sxz{xtxs}y{uvypnyy{zt}qpvw~wqprppy~}xyvyxr{ynr}wy{|z|~w{qsp{ztvqx{q~szuwxkz{q{uxzxpjmw|}xtx|pzvy|uow~~|{zz{zznw}tu|so}zy}~~y}qrqrvvpu~}v~|~w~ttnjspnwwzznu|}}y}tr~rxvlluxt{||z~y{jo}wzqyusx{}y~rztxvrzpz}zyv|x~~vr{ruuw{ywoiwz~{}}yxjzxs}nozwu~}xtzssvhlzs}z{yss}yt}xvs}xws{vyuszptyuzxoqz~}y|}|yvv|qvxvvr{}z}}yw|qr|swyu|ut}uyzpvzt|zqw{~v|v}ymsynkv{t~~ry|~s{y~yztvus{vrz~|~y{z||y{lqyyvsx{{~}wwvyxrwuvu{}y|zs{xsv}~~}xrt}|z|wxtzz|x|sy~q|vx||}}uqokru|zxzy}{xs}{ts{vy}zwy{t{~y~wx|qs{|}|~rv}~v~xwytw{syyyv{{z{{uxv{~xv}|}vyv{~vs|}vxn{rs~|}wwpzjmxt~}zu~wq}v|~~}s{z~}z{xv}w|{|}~~xyysuy~{ow|~~}}}yuzv{txo~yw~~~y}|}y{~}stz{|{{vvw}zz}t|{z~tr{zxz{z~z{zozxotp{zv|x|}w{yw~|}xv}}{}p}}uw~yw|zow}|z~r}xt||||{|zwtx|rz~~{}~}||~y~xx|tt}~~{yz~ws}|wpxty}~x~|~z{zu}rmxv}{|~~v~uv~~~zy||s~~{}}~v|{|~~}}~uz}wyyy{}n}|||~}zxxzyxzzs~~x}{yx}}|v~}uux|~z~z|s}}xvzv{|rn}zv|~~||{|syytxtz}~w}{~|}vy|{z|zv{|r|{x~}~yzysx}xw|}s~|wz|z}wu{uvymz}}~~~~y~x~{y{|rv~x|x{sxyz}ruuwyy~|~|x{{v|~uuz}y{{u|}vty}zv}{o{|~xx}}z~zsvwy{vp}zqv|vuw|vyv}{{x{{z~~z~w|||yx|y~~|}vw|}|qxzrzzxmxr{~~|z{|z{ywy{{|y}~tz}{{tz|xwy~z|yt~ywxy{zqz{}u~zwy~qt}s{z~o~}~}}|~z}~vvw|~v|~}{zx~{~~s{yxswxxu{wx}~~xt}}y|||x~}~|zuy}}t{|~yuzzq{|tz~~|rpw~}ro|wuzwvz{|yv}y|q{z{vz~xx~|u{|{x{z}{yxx|{ss}vyz|{zo|wn||~v|ztyqyy{uz|||u~~}|y}wr}}t{~||yxv}{rvztz{{}yw~zyu}yzzy{xxz}}y{~z|zwqxz~{{|yyyy~vx|z~{ww~lr|wx|~}}|~x{~s|{||}x~~w}|t|y{~~~yyw~~|yv}}{tnv~|prvvxv~w|t~xy~t}zw|{z~vvy{|qw}vxz~x~|}{}{zzrwx{wyx|xvvou{{|sxt~stx{|xwyx}wz~|{|||}xxvxwv}}ytvxxqxwysz|}||x~|z{~}}|y~~txx}wz|~w~{qt}|yy~wxy~~~wvy|}}zuqjz}yx}{~yxy{{~~tz}|~{~}xxzv|tyzxy~|x{xyq|{y}uwrpw}vots}vzy|~}u~{w}~{|}s|~|tuw{ypw}x|yz~z|~w}}{z{trw|vuw|zzpqwz~||}sz|w~qv~|}xv{{v~y}~~x|}|z}{~|xz{wzv~{xpsx~xsxvxt{~|~|y{}|}vv|{z}vwz~|~|w}ts{v{}wx~~|}~~~wvv}}}{xqmx}x~~z|z||t{}~s}}|}|{|~uwyzttzywxyy}yz~q~}zvouttxyqur{v|~~||x~{}y}~{wv~{urty~y}sty}{yzz|}}|xz}{{~|xsu}ytw{{oozz~~{|}ww|~p{ztsx~~vx{}y|y|{y~}|yuu||~~{{~xuotz~wyv|}sx}~|r~|}~x{zx}}yuwz~{}{x{~||wxzyzzv~z|xvuz~{{tqr|z{~yx~|uwvz}y{}||xu}|}oszxvvzy|vz~yukrzwvttvr|ww~|y}{~~y{|~~yxyusrux~yysux~xx~z~{~sz||~}~zwuz|xtyw|js{z{|}}}|{ytz{xypsywuw|}t}{y{{yzrx{z}xqsrw{}z}xz}rvz~{~~{x{yyyvyx~zxwy{~{{||z{|xx}}|xx}~|z{}{{zvux}~}|wswsy~}{xz|wxz~{~wz~~|}yxu|mtxwux|{y|y|~~vmo{|wqwuu{pw~}z}}{}x|}~{}zqqrtvz}~}wvvuz~|x}}yu{~y~~|zyyzz{wxutpv}z|}}~|{~~{ux~v|~tpszuw|x~uw~{{}}}ytz}}|~vmruw{zzx~ypy|}{x{~wxwz}x{wxxyz{y|{~x}}~zu}|{w{{|}ywz}~xvtx{}~|zszxu{|yzzx{{{~|zw{~}}z|twtrvywx~~{}xzzytmp{}urxtzup{||{y{x~~~~yoortvxyuxywy{}{~|{}y|zz||xxxz{yzyyuyvu}}~~|{~|~rz}v{{|uqry~xvy|}tw}~~~|wy~~~~ylowwyxy{xsy~~~z~}vwx|}~{}~xtwyyzzw}{x~~v{y~v{z|yv{}}|uttx{|}~yw||wz}|z|}{{~z}}||yx{~~|{}sz}wvwzy|zyw{x}{usor{}tuuyzpv~{~xy}y{~~rmptuwu|xx|zy}zz~~||~{v}|{ztux{~{y{{u}{|vz~}{~}r|{xzzxtssx~|wt{sx||z}{{y~wmpyzyv{|sv{~~yyvy|}z|xrrw{{{vz|~wz|wz|xz}{v~}~{}}urtx{|{~{w{~|x|z{}~~}~{x}|xxy{~{{}xzuyzwxz~~~zx||xxtutsy|xvt}tpv}~{x|}{}~~}|zrnrvwuu|wz|{zxy|}~|z}v}}|tpsz|y}x|{w~wy~|}~~wz|y{x||ttuvztytux{}~ww~v~{vos{|vwx~uux{}w|}xx|}|z~}sprz}}xwy|xw{}{z|z~~x~{~wqqw{}{{~~xz|}zzx}|{}~u~|~{uw{}z|{~xoy|yx|}z}|z}x{~wsvyvvy|wv}rqvz}}~|}}xvuqrvxuty|z{{}|~~w~z}~y{x{~upov~z|{~{t|x{{|{~}|~x|yz~xsuxx||~w{wvwy{~{~vv{t}zyxts{|vxvvxyz}~x|{xy|{|}~|soqw}~xxx|wx{{|x{}}xrpu{}{y~~~x{|}~y|z|zvz~}~|utx~||{~pr{~z{{|~}{z||~~w~~ztsx{xuw}w|xqux{y~}~ztqtttvytvw}|}}}z}~{~}y~xx|toou~|}{{t||{{z{~~~xx{yz}xtvzz{z|{tyzzz~{~xu}yu|wwyxty{xxv|xz{{}~{|zyz|{~}|{zsoqv|~xzx}wz|z}zy|~~wqquz}zz||z}~|~~{{{v}z}}~xsty~{}z}qt|}~~{}}xy~~w}~|}|vtu{}yrz|}|suwz|x~~~~{|ynpvwvyvvwz}}}|~~}z{v|{toot{~||u{}{z~|~~ztz{y{}~yvw|}{yyqz|{z~}{~zw|t{~~ytw{|wwy{wz|wz{|{~|yz|~{~}vusqtw|zzyzxy|}{|||~{x}}xsruy}z{|}}z}~~~{~|~|}zy{}~xttx~}|}{|utx~}z|ywy{|~~zuuw|ysw{xswxy{z~z}zvmoxzywuxv~|{~{|}{{y}}uqprw}~}wx|~{{~|s{~|y|}zyy{~~x|~qs{}{{~|}}z}wz~z}uty~xsz{z|sw||{|~{z}}yqsuuvxx{{y~vv{|}|z}~~z{wzwutuwzzy|z~z|}~~|x}{~|x~z{{~yutvz~{}{xyvv|{z|{x~{}{}|yuvy|zvtywtwyy{~yz~{~yqmt{|wvyw}yxz{{{~}zwrqsvz}}yz{~}~}|zw}~z{}}zyz|}{z||st|~|}~}}}|w{{~{utzyrzz~yqw|}|~~|{~{opvxwwuz{{~tv{}~~{~|zw{ysstvxyxy}{}y}~xy||t~{zw|~xutvy||}|~yz{xx~}{||y{y}|z{{wux{}~zys}~vwy{{~~|xzz~}}xrot{}wxyz~vx|{{z~{|}wqqtwz{|y}}|}~{~zz{{{{}~zxy{~||{}wv{~~~|zw{|{~zvuz|tx{yqv{~~}|~}~|pnuzzwsy{~~tv{~~~{}~|zzxqptwyzvy|}zy~zy~}}s}yv{{tstx{}}|~|}x}~{y}}}{|t~|{x|{xvwz|~{}xx}xyz|}~zx{y|~}|xvrrw}yxyzvv}||}~{z||}~}}~xrrvxzyzz~|z~}|~}{zy}|}~|wux||~||{xz||~~}}w}{|y~}xvw|zwvutv|~{|~~{~xppx|{tux}utx}{{{~{}{qnrxz{vw{}yw}~z~}v}~zwyzsqty|}z{~}zw|~z}|~}}yt~}zx|}wvwy|}{{~z|vuz{|~{z{{v}|~xvwvuxzyzyzsx}z~z{~{~|}zxuruy{yv|z|zzy}}{yyz~~wstz{~~{}}zz|y{|~~z|}}y{ywy{~|wxxvvw{~|}}x|z||vqsz~xtuxxtvy}~z}{}}~~vpntz{xuxz|vy}~~}|~{}zzz~wqpu{}|x|}vx|}|~}~~ww}|~yy|{uux{}}y{~~~rsz}}~~~||~zv}{}wuwzywvx{zztw{~{{}y~{~ztttvy{zvz||}~z|z~~~{~zvzztrv}~}|{{z{x{~}~|y~~|y|}xx{~~y{{wxxxz}{}~xzx~{xuty}ywux~vuwy|z~~|}~~}xrorx{xuxx}wy|~y}||{|~~yrprx}}xz|yw{}~~v|z|{xz}~xtuz}~{x}~|qt{~~~|~~}~vz~{|{uux||xsx{|xvy|~}{|}~y}{~zrqtwz|zwz{}}}{}~~y|ux~ytrv}~|||}y{}~}~wy}~{y|}yy}~~|}xw{{zz~}{}xy~w~}zyxwx{yzwy|vvxz|}}}}~|zwtqsw{wvxy{xz}~~}yz{}}||{vqqty}|x{|xy|}}~}y|{x{xy{~zutw|~}x{|vsw}~}z~}wx}{|{~|vtw||ttz}ywy|~~|}{{|}~}y}{}}rosx{|zx{{}~|~y}uw}~xtsw}~{}~~{|{~~|s|}~z{}{y{|}tx|}{{|{~~yzy{yy{{yxx{zy{uvxz{}~}~~wuutuvxwwxx~yx{}{~|xz~{xtqqux|zy{{}xz|~z||{{yxx{|~~|xuux}{x{|wuv|}|z~|w}{||{}xtuy~zsux|vy{}|~zz|~y{}xpou{}{xzz{~z}}}y{xyyutvz~}}|~~}|}vw~~{|~{z{}~vsy~~||~~|{||||{~{xz~~zuw||~trvyz{~~~{ssvxxwuuxw}yuy|~~y}|vzztqqswyzx{zyyz}~xz~w}|zux{~~|yvuvy}~zyz}}zxwy~~z|yw~{{zz~~xtuy}}wvv|vy|~~~|xz~z|}|tpqx}{w{z~y|~}{~~{}{||z{{vtuy|~}~}~}}}}}w{{~}z}{zz}}vty}}|{{~~}z~~}{y{|uw{~urvz{|~~~uquz{zutyx}zux|~~{~yx}}vqrtwyzw{|}zz{}~}z|yx}yvz}~zvuvy{}|z|{{|{z{{}|v{~{|y||xvwz~|{xxzxz}}yz}|z~}~|wttx|{y}|y|}~}~}{|{~~~}}~~{wvx{|}{~}|~}~z}z}|{~}zz{~zxy~~}}}y}}{z{}xy{xvx{}~~}}}~wsv{}{wwzzzwz}~~~~~{{~}vstwyzyy}|z|}~~|~w}~~yy|}xvwy|}}{~||}}}{}~}~wz~{}{{}zxxz}~}~|z~{y{}~{{}{{~~|ywwx{z{~|~z|||{}|~~}zwwz||||~~}{}~~|}z}|}{yz|~~{z{~~}~~z~}}~|{|~{|{||yxz|~}|}|{wvz||yz{z{xy|~~}~~~~~yuvxzzy{}|~{|}~~~{}||~|xwx{}}{}||}~~}~|y|}}|}{yz{|}|~|~}yz{}~}~|z}{zzzzz{~|{{|}}|}|~}{yxz|||{~}~~|}~~}|}}~}zy{~~}|||~~}~~~{}}}||~~}~}{~{zz{}||~}}}zyy{||||z~~zz{}|~~~}ywxz{zz}|~}||~~}|~|yxy{}}|~}~|}~~|||}~}~|zz||}|~}~zz{}~~~}{~}{{||{z{~|~}{|~~~|}~}~|zyz{|||~~~~~}}~~~}~~|zz|~~~~~~}}}~}~}{}}|}~~~~~|~||||}}|~}~~|{z{||~~z~{{{|~}~~~~~~{zyz{{{~}}}|}~~}}~{yy{|}}~}}}}~|~{~~~|{{|}}}~~{{|}~}~||~~}|||||{||}|}~~~}}}}~}zzz||}}~~~~~}~~}~~~|{{|}~~~~~~~}~~}{}}|}~~~~~~}}}}~}}~|~}|||||}~||||}~~~~}{{{{{|}|~}}~}~~}}{zz|}}~~}}~~||~}|||}}}~|||~~}}|}}~}||}}}|~|~}}~}~}}~~~~}{z{||}~}~~~}~~~~}~~|{|}}~~~~}~~~|}~}}~~~~}}}~~}~}}~}||||}|}|}}~~~}~~~}|{||||~|~~~~~~~~~~~~|{{{|}~}~}~~}~~}~|||}}~}~}|}~~}~}}~}~||}}}}~|~~}}~~~}}~~~~~|{{||}~}~~~~}~~~}~~}|||}~~~~~~~}}~~}}}~~~~}}~~~}~~}}}}}}}~}}}~~~~~~~~~|||||}~}~~~~}~~~~~~||{||}~}~~~~}~~~}|}}~~}~}}~~~~}}~}}|}}~~}~~}~~~}~~}~}||||}~}~~~~~~}~~~~}||}}~~~}~~~~~}}~}}}~~~~}~~~~~}~~~}}}}~}}}~~~~~~~~~~}||}}}~~}~~~~~~~~~~~~||||}~}~~~~}}}}}}}~~~~~~~~~}~~}}}}}~}~~~~~~}~~~~~}||}}~~~~~~~}~~~~}}}}~~~~~~~~~}~}}~~~~~~~~~~}~~~}}~~}~~~~~}~~~~~}}}}}~}~~~~~~~~~~~~}}}}}~~~~~~~~}~~}}}~~~~~~~}~}~}}}~}~~~~~~~~}~~}}}}~}~~~~~~~~~}}}}}~~~~~~~~~}~~}}~~~~~~~~~}~~~~~~~}~~~~~~~~~}}}}~}~~~~~~~~~~}}}}~}~~~~~~~~}}}~~~~~~~}~~}~~~~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~}~~~~~~~~~~~}}~~~~~~~~~~~~~~~~}}~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m39.bin b/applications/SprPoP/dist/MUS/m39.bin deleted file mode 100644 index e18ccb9..0000000 --- a/applications/SprPoP/dist/MUS/m39.bin +++ /dev/null @@ -1 +0,0 @@ -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}||||||||||||||||||||||||||}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}||||||||{{{{{{{{{{{{{{zzzzzzzzzzzzzzzz{{{{{{{{{{{{{{||||||||}}}}}}}~~~~~~~~~~~~~}}}}}}}|||||||||{{{{{{{{{{{{{{zzzzzzzzzzzzzzzz{{{{{{{{{{{{{{||||||||}}}}}}}~~~~~~~~~~~~~}}}}}}}|||||||||{{{{{{{{{{{{{{zzzzzzzzzzzzzzzz{{{{{{{{{{{{{{||||||||}}}}}}}~~~~~~~~~~~~~}}}}}}}}||||||||{{{{{{{{{{{{{{{{{zzzzzzzzz{{{{{{{{{{{{{{{{|||||||||}}}}}}}}~~~~~~~~~~~~~}}}}}}}|||||||||{{{{{{{{{{{{{{{{zzzzzzzzz{{{{{{{{{{{{{{{{{||||||||}}}}}}}}~~~~~~~~~~~~}}}}}}}}|||||||||{{{{{{{{{{{{{{{{{zzzzzzzz{{{{{{{{{{{{{{{{{|||||||||}}}}}}}~~~~~~~~~~~~~}}}}}}}||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{|||||||||}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}|||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||}}}}}}}}~~~~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}|||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||}}}}}}}}~~~~~~~~~~~~~}}}}}}}}||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{|||||||||||}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{|||||||||||||}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}}|||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{|||||||||||||}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}|||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}}}||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{|||||||||||||||||}}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}|||||||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}|||||||||||||||||||||{{{{{{{{{{{{{{{{{{{||||||||||||||||||||}}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}||||||||||||||||||||{{{{{{{{{{{{{{{{{{||||||||||||||||||||}}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}||||||||||||||||||||||{{{{{{{{{{{{{{{|||||||||||||||||||||||||||}}}}}}}}~~~~~~~~~}}}}}}}}|||||||||||||||||||||||||||{{{{{{{{{{{{{|||||||||||||||||||||||||||}}}}}}}}}~~~~~~~~}}}}}}}}||||||||||||||||||||||||||||{{{{{{{{{{{{|||||||||||||||||||||||||||}}}}}}}}}~~~~~~~~}}}}}}|||||||||||||||}}}}}}}}}}||||||||{{{{{{{{{{||||||||}}}}}}}}}}}||||||||||||||}}}}}}~~~~~~~}}}}}||||||||||||||}}}}}}}}}}}}||||||||{{{{{{{{{|||||||}}}}}}}}}}}}}}||||||||||||}}}}}}~~~~~~~}}}}}}|||||||||||||}}}}}}}}}}}}||||||||{{{{{{{{{||||||||}}}}}}}}}}}}}||||||||||||||||}}}}~~~~~}}}}||||||||||||||||}}}}}}}}}}}}}}}}|||||||{{{{{||||||}}}}}}}}}}}}}}}}}|||||||||||||||}}}}~~~~~~}}}}||||||||||||||||}}}}}}}}}}}}}}}}||||||{{{{{{||||||}}}}}}}}}}}}}}}}}|||||||||||||||}}}}~~~~~}}}||||||||||||||||||}}}}}}~~~~~~~~}}}}}||||{{{{{||||}}}}}~~~~~~~~~}}}}}|||||||||||||||||}}}}~~~~~}}}|||||||||||||||||}}}}}}~~~~~~~~}}}}}|||||{{{|||||}}}}~~~~~~~~~~}}}}}|||||||||||||||||}}}~~~~~}}}|||||||||||||||||}}}}}}~~~~~~~~~}}}}||||||{{{|||||}}}}~~~~~~~~~}}}}}}||||||||||||||||||}}}~~~}}|||||||||||||||||||}}}}~~~~~~~~~}}}||||||||||}}}~~~~~~~~~~}}}}||||||||||||||||||}}}~~~~}}}||||||||||||||||||}}}}~~~~~~~~~}}}||||||||||}}}~~~~~~~~~~}}}}||||||||||||||||||}}}~~~}}||||||||||||||||||||}}}~~~~~~}}|||||||||}}~~~~~}}}|||||||||||||||||||}}~~~~}}|||||||||||||||||||}}}~~~~~}}}||||||||}}~~~~~~}}}|||||||||||||||||||}}~~}}}|||||||||||||||||||}}}~~~~~}}|||||||||}}~~~~}}}}||||||||||||||||||}}}~~~}}||||||||||||||||||||}}~~~~}}|||||||}}~~~}}}|||||||||||||||||||}}~~~}}||||||||||||||||||||}}~~~}}|||||||}}~~~~}}}|||||||||||||||||||}}~~~}}||||||||||||||||||||}}~~~}}|||||}}~~}}|||||||||||||||||||||}~~~~~~~~~~~~~~~~~~~~}}||||||||||||||||||||}}~~~~}}|||||}}~~~}}||||||||||||||||||||}}~~~~~~~~~~~~~~~~~~~}|||||||||||||||||||||}}~~}}|||||}}~~}}|||||||||||||||||||||}~~~~~~~~~~~}}}}}}}}~~~~}|||||||}}}}}}||||||||}}~~~}|||||}~~}}||||||||}}}}}}|||||||}~~~}}}}}}}}~~~~}}}}}}}}~~~~}|||||||}}}}}}||||||||}}~~}|||||}~~~}||||||||}}}}}}|||||||}~~~~~}}}}}}}~~~~}}}}}}}}~~~}|||||||}}}}}}}|||||||}}~~}}||||}}~~}}||||||}}}~~~}}}}|||||}~~~}}}||||}}}~~~}}}|||||}}}~~~}}|||||}}}~~~~}}}||||||}~~~}}||||}~~}||||||}}}}~~~}}}}|||||}~~}}}}||||}}}~~}}}|||||}}}~~~}||||||}}}~~~}}}}||||||}~~~}}||||}~~}}|||||}}}}~~~}}}}|||||}~~~}}}||||}}}~~~}}}|||||}}}}~~}|||||}}~~~~~~~}}|||||}}~~}||||}~~}}|||||}}~~~~~~~~}}||||}~~~}}||||||||}}~~}}|||||||||}}~~}|||||}}~~~~~~~}}|||||}~~~}||||}~~}}||||}}}~~~~~~~~}}||||}~~}}}||||||||}}~~}}|||||||||}}~~}||||}}~~~~~~~~}}|||||}~~}}|||}~~}|||||}~~~}}||||}~~}}|||||||||||}~~}|||||||||||}}~~}||||}}~~~}}||||}~~}|||}~~}|||||}~~~~}||||}~~~}|||||||||||}~~}}||||||||||}}~~}||||}}~~~}}||||}~~}|||}~~}|||||}~~~}}||||}~~}}|||||||||||}~~}}||||||||||}}~~}|||}}~~~}||||}~~}|||}~~}||||}}~~~}|||}~~}}||||||||||||}~~}||||||||||||}~~}||||}~~}||||}}~~}|||}~}||||}~~~}||||}~}||||||||||||}}~}|||||||||||||}~~}|||}}~~~}||||}~~}|||}~~}||||}}~~~}||||}~~~~~}}|||||||||||||}~~}|||||||||||||}~~~~~~}|||}~~}||||}~}|||}~~}||||}~~~}}|||}~~~~~}}|||||||||||||}~~}|||||||||||||}~~~~~~}|||}~~}}|||}~~}||}~}}|||}}~~~}|||}~~~~~~}}|||||||||||||}~~}|||||||||||||}~~~~~~}|||}~~}}|||}~~}||}~}||||}~~}|||}~~}}~~~~}||||||||||||||}~~}||||||||||||||}}~~~}~~~~|||}}~}}|||}~}|||}~}|||}}~~}}||}~~~~}~~~~}}||||||||||||||}~}||||||||||||||}~~~~~~~~~}|||}~~}|||}~~|||}~~}||||}~~}|||}~~~}~~~~}||||||}}}}||||}~}|||||}}}}}|||||}~~~}}}}}}~~}||}~~}|||}~~|||}~}|||}~~}|||~~~}}}}}~~~}|||||}}}}}}||||}~}|||||}}}}}|||||}~~~}}}}}}~~}||}~~}|||}~~}||}}|||}}~}|||}~~}}}}}}~}|||||}}}}}}||||}~~}||||}}}}}}||||}~~}}}}}}~~}|||}~}}|||}~}||}~~}|||}~~}||}~~}}}}|}}}~~}||||}}}}}}}||||}~~}||||}}}}}}}||||}~~}}}}}}}~~~}||}~~}|||}~~}||}~|||}~~}||}~~}}}}}}}}~~}||||}}}}}}}||||}~~}||||}}}}}}}||||}~~}}}}}}}~~~}||}~~}|||}~~}||}~~|||}}~}||}~~}}}}|}}}~~}||||}}}}}}}||||}~~}||||}}}~~~}}||||}~~}}||||}}~~}||}~~}|||}}||}~~}|||~~}|||~~}}|||||}}~~}|||}}}~~~~}}|||}~~}|||}}~~~~~}}||||}~~}}||||}}~~}||}~~}|||~}||}~~}||}~~}||}~~}}||||}}~~}||||}}~~~~~}}|||}~~||||}}~~~~}}||||}~~}}|||||}}~~|||}~}|||}~~|||~}|||}~}||}~}|||||||}~~}|||}}~~~~~~}}|||}~}|||}~~~~~~}||||}~}}||||||}}~~}||}~}||}~}||}}|||}~~}||}~}|||||||}}~~}|||}}~~~~~~}}|||}~}|||}~~~~~~}}|||}~~}}|||||||}~~}||}~}|||~}||~}|||}~}||}~~}||{{{|||}~~}}}}~~~~~~}||{z{|}~}||}~~}}}~~}|{zzzzz{{|}~~~~}{zzz{}~}~~~}}~~~}}~}{z{|~}|{zzz{{||}}~}}}~~~~}}|{zyyyz{}~}}}~~}~~~}{zyyyyyzz{|}~~|zyyyz|~~}~~}}|}}~~~~}{zz{|~~}{{{{{||}}~~}}}}}}}}}|{zzyyyz|~~~~~}}}~}|zyyyyyzz{|}~~}{zyyy{}}}}~~}}}}}~~~~|zyy{}~}|{{{||}}~~}||||}}}}||{{zyyz{}~~~}|||~~}{zzyyzz{{||}~}{zyyz|~~|||~~~}||}}~}{zyyz}~}||||}}~~~~}|{{|||}}||{{zzyz{}~||{|}~~}|{zzzz{{||}~~}|zzyz|~~|{{}~~}}}}~}{zyyz|~}}}}}~~~~|{{{{|||||||{{zzz|~~}|{{{|}~}|{zzz{{||}~~~~~}|{zz{}}{z{}~~~}}}}~}{yyy{}~~}}}~~~|{zzz{|||}}}||{{{{}}|{zz{|}~}|{{{{||}}~~~}~~}|{{{|~~{zz{}~~}}~~~|zyyz|~~~~~|{zzz{{||}}}}}||{|}~}|{zzz{}~~}|||||}}~~~~}}~~}|{{|~~{zyz|~~~~~~|{zyz|~~~~|{zyz{{|}}~~~}}|||}~}|zzyz{}~~}||||}}~~~}||}~~}||}}{yyz|~~~~~~}{zz{}~~}{zyyz{||}~~~~~~}}}~~~~|{zyyz{}~}}}}~~~~}{{|~~}}}~}{yyz|~~~}|{z{}~~~~~~|zyyz{|}~~~~~~~~~~~}|{zyyy{}~~~~~~~}{{{}~~}~~|zyz|~~}~}|{{}~~~~}}~~|{yyyz|}~~~~}~~~~}|{zyyz|~~~~~~}{zz|}~~~~}{yy{}~}}}~}|{|~~~~~~}}}}}~~|zyyz{|}~~~}}}~~~~~~}|{zyz{|~~~~~~~}{zz|}~~~~~~}{zz{}~|||}~~}|}~~~~~}}||||}~~|{zzz{|}~~}||}}~~~~}|{{zz{|~~~~~~~~~}{zz{}~~}}}}~~|zz{}}|{{}~~}}~~~~}}|{{{{|~~}{zzz{}~~|{{|}~~~~}}|{zz{|~~~~~~~~~}{zz{}~~}||}}~~|{z{}~|zz{}~~~~~~~}}|{zz{{}~~}{zz{|~~|{z{|}~~~~~~}}|{zz|~~~~~~~~~}}}~~|zyz|~~~}|||}}~~{z{}|zz{|~~~~~~}}|{zzzz{|}~|zz{}~~|zz{|}~~~~~}{{{|~~~~~~~~}|||}|zyz|~~~~}||||||}~|{{}~|zzz|}~~~~~}||{zzzzz{{}~~|{{|~~~~~|zz{|}}~~}|{|}~~~~~~~}|{z{}~|zz{|~~}~~}||||||~~||}~|{zz{}~~~}|{zzzzzzzz{}}|{|~~~~}{zz{||}~}}}}}~~~}~~~~}{zzz{}}{z{|~~~~~}}||{|}~~}~~|{zz{}~}|zzzzz{zzzz{}~}}}~~~~~|{zz{|~~~~~~~~~~~~~~}{zzzz{}~~|zz{}~~~}|{||}~~~|zy{}~~}{zzz{{{zzyz|}~~~~~}}~~|zzz|~~~~~~}}{{zzzz{|}~}{zz|~~~~}|||}}}~~~~~}zz{|~}}~}{zzz{{{zzz{{|}~~~~~~|{|~~{zz{}~~~~}~~}|{zzzz{{{{}|zz{~~}||}~}}}}}}}~~}~~}}~~|{{{|}~~}}|{zz{{{zzz{{{{|~~}}~|zz{}}{z{||}}~}}~}{zzzzz{{{z{}}{{}~{z{|~~~~}||}~~}~}||}~}|{{{|~~~~~}{{{{zzz{{{{z{|~}}~|zzz{~}||{{{|}~~}}}{zzzz{{{zzz|~}}}~~{zz{}~}||}~}~}||}~~|zz{|~~~~}||{zzz{{{zzz{~~}~~~|{zz{}}{zz{}~~~~~~}{zzz{{|{zz{}~~~~{zzz}~}||}~~~~|||}~|zzz|~~~~|zzz||{zzzz|~}}~}|{zz|}~}zzz{}~}|~|{zz{{{{zz{|||}~~~}|zz{}~}}|}~|}~}||}~~|zz{~}{|~}{z{||{zzzz{~}|}~}}|{{|||}|zzz|~}|}~}{{{|{zzz||{{{}}}}~}|{{||}~~}~||~~}}~~~}|{{~}zz|~~||||{zzzzz|~}|}~~~}}}}|{z{~~|z{}}|}~~}|||{zz{||{zz{}~||~}}}|{{}~~}}~~~~}~}}~~~{zz{~~}}}{zzzz{}~~}}~~~~~~~}|zz|~||}~~}}~~~~}{zz|||{zzz{~}|}}{z{~~~~}~~}{zz{~~|zzzzz|~~~~~~}}}}~~}{z{}~~~~~~~~}{z{||{zyzz|~~~}~}zz|~~}}~~}~~|{z{}|{zzz|~~~~~}}~~}{|}}~}{{|}||~~~}}~~||}}{||}|zzzyz|}|}~{{|}}}~~||~}~~||~~}||}}}~|{zz|~~~~}||}~{z{||~~|}~}{z{}}||}~{z{}}}~}|zyzyz}~{{}}}}}{{|~~{z{}~~~~|{}~~~}|{}~|{{}~~~~}{{}~zzz{|~}}~}{zz{}}{{}~}zzz{}}{zzzz{~~~||}}{z{|~~{zz|~~}~~||}~}{z|}|~~~~}|{|~~~}zzzz|~~}}zzzz}}||~~}~|zzzz}}{zzz{}~~~~~~~~~|zzz{~}{zz{~~}}~~}~~}|}|z{}~~}|{|~}}~|{zz{}~~}}~{zzy{~}~~||}|{zzz|~|{zz{~~~~~}|}~~~~{zzz}~|zz}~~~~|{|~~||}}}~}{{|~~|{}~}{z{}~}|}~|{{z{~}{{|||{zz|}~~~}|{z|~~~~~|{}~~||~~}{zz}}||}~~~~~}|{{~~~~|{{~|{{}~|{{}~||}}}{{~}||{{}~~}{{}~}}|{{}}}|}~||~~~~}{{}}zz|}~}{|~~~~~}||~}||~|zz|}}{|~~|{{}~~~~|zz}~}}}||~~~~|{}~}}}|||}}|z|~~~~~|{{|}~zzz{}}~~~~{z|~}~~~{zz{|~||~~|{{|~~{z|~~~||}|}~~~~}~}}~~~}|}}|}~~}zz}}{{|~~||}~{zzz}~~~}}~zz}~~~}~~|{zz}~~}{z{}~~}~{{}~}|{|||~~|{}~~~}{|}|}~|z{}~~~~|{{}~}{{}}{zz}}||}~||}~~~~~|{|~~}|{z}}{{|~~~~}|~|}~}|{{|||~{z|~~{z{||}|{|}}}|}|{{}~|{{}~{{}~~}~~{z|~~~}|{}~zz{|}~||~~~}|{}~~~~~{{}}~}{zz{{}}{z|}zz{{|~~||~~}{z|}~||}~}{z{}~~~}|{~}{{|~}zz|}}zzz{}}~~~~}}~~~{z{||}zzzz{|{{}~}}|zzzz|||~~{zz{|~}}~{z{}|z{}{|~}|}|zz{||{zz{~~~~}~~~~~~{z{{{}{zzzz~}||~~{{|~{{zz|~~|}~|zzzz||{{}~~~}~~z{~~}~}{{}~|{zz~~}{z{}}{|~{z{zz}}|{z{~~~||}}{z|~|zz|~~}{}~|zzzz}|{|~~~~}|}~{|~~}}~|z{}}|z{~~|{|}~}||z{~|zz{z{~~~~}|z}~~~}|}|zz|~{|}~}{{~~{{{zz}~||}~~~{{|}~~~}{{}~|{{}~||~~~}~~}{{~}{{}|z{zz{}~~}}}|~~}~~||}}{{|~~~~~~|z{~}||{z{~~~~}~}zz{|}~{z{|}~||}~~~~~~~{z|||}~}}~~|{{z{}~~||~~~}{{|}|{}~~~}zz}~~}{||{{~~}~~~{z{{{~}zzz{}~}~~~~~~~~~}z{}~~~~~}{{~~}|{{}~~|z|~|{{}~||}~~~~}|~}z{}~}{{||{}~~~~|}~|zz{z{~~{{zz}~~~||~}||~~{|}}}|}~}zz}~}||}~}{z}~~{z{~}||~~~~{z|~~|}~}|z{{{{~~~~}{{}|z{{z{~~~~|zz}~{z|}~~{z|}~~}{z{}}}{{}~}~~~}z{}~~}}~~|{|~~|{|~}zz{|~~~}{zz{{}~|{{}|{{zz|~~}|~~||~~}zz{|}~{{}}zz{{}}||~~~{z|~~}{|}~~||~~|{|}~{zzz{~|zzzz{}{{|~~~~||{{|~~}{|~~~{zzz{~~|}~}}~~{zzz{~~~~~|{}~~}{{|}~~}~|z{}~{zzzz|~{zzz{~~|{|~~||~~}|{}~~}{|~~~}}~|{zzz}|{|}{zzz~~~~|}}|~~~|z{{|~|z{}~{zzzz|~|{zz|~}||}~}{{~~}}~~~{z|~~~}{{}}|{z{}~~~|z{~}|{z{~~|z|}}~}{z{{{~~|{|~~~|{{{z{|}}|~~|{{}~~~}|}~~|{{}~~}{{}~~}}~}{{}~}|{|}}||~}{|~~|{}~~{yz{}}{{{{{}|}~~~~{{{{z{}}|{|}|~~~~}||}~|{{}~{|}~~|{|}~|{|~~~}}}~}{z|}}~~~}~~~~~}zzzz}|{{{{~~~~~~{z{{{|}}|zz}~~~~}||}}{{}~~||~~~|{{||}}~~||~~~~|zz}~~~~~~}|}|{zzz}~}|{{}~~|zzzz{}}}{y{}~|{|~~|{|~~~~}}~}{{{{|}{{}|z{}~~}~~~||~}|{}}|{zz|~~~}||~~~~{zzzz{}~}{z{}}}}~~|{|~}||}~~~~~||}~~}{{{{|}{{}}{|}}|{|~}{z|}~{{|~~|{|}}}|~~}}~}~~{zzzz|}~|z{|}|{{|~~|{|~~|||~~~}z{|}}{{{{}~||}}}~}|{z{}~}zz{|~~||}~}~~}}}}|{{~~}}~}{zzz{}}}{z|}}{zz{}~||~~|{|}~}{zz{|~||{{|~}~~~~~~~}{zz{|{zzz{~}}}~}|}~~~}{z|~}|}|{zz{|}|{{|}}{zzz{}~~~|{|}}zyzz{}}}|{}~~~|}~~{zzz{~}{zzz}~|{|~~{z|}~~}}~~}{{{||{z{}}}{zzzz}~|{|~|zzzz{}~~}}~~}{{|}~~|zzy{~~}{z{}~~~|{|~}{{|}||}~}}~~~||}}|zz|}}|zzzyz}~||}~~~{zzzz{}~~}~~~~}zz{|}~|{{z{~~}||}|||}||~~~}}}}{z{}~~~~}}~~~}|zz|}}|zzzz{}}}}~~~~}{zzzz{|}|{}~}}~{yzz|~~}|{|~~~~~|{z|}}~~~~}~~~~~|zzz|~~}}}{z{}}{zzzz{}~~~~~}{zzzz{|||z{~}}~}{zzz|~~~}|}~~~~~~|zz}~~~}||}~~|zzz{~~}~}{{|}|zzzzz|~~~~~~~~~|zzzz{|}|zz{~~}~~|{zz|}~}}~~~~~~}}|z{|}}}~~~|{{|}~|{zzz}~}~~|}}}{zzzz{}~~~~~}}~{zzzz{|}|zz{}~~}~~~~~~|{{|}|||~}||~~||}||{|~}{zz{|~}|{z{}~~~~~~}{zyzz{~~~~}||~~{zzzz{||{zz|}|||}~~~~~}}}|{z|}||~~~~}|zz{}|zzzz|~~~}|{{}~~~}}~}{zzz{}~~~}||}}zzzz{|||zz{||{{{|~~}~~}{zz}~~~~~||}~~~}{zz{|~{zzz{}~~~~~}}|~~~}||}~~|{zz{}~~~~}|||~~~~|{zz{||{zz|||{zz{|~~|zz|}}}}~~}}}~}}}|zzz{~|{zzz|}}|}~~}~~~~}}|||}}|{z{}~~~~}||}~~}}}|{{{||{z{|||zzzz{~~}{{|||||}~~~~|||}{zz{~}|{{{|||{}~~~~||~~||||}~}{{}~~~~~||}~~}|}~}||||{zz{||{zzzz|~}~~}}}|{{{|}~~|{|~~|{z{}~~}|||{zy{}~~}}}}~}}~~{{zz{}~~~~}|}~}|z{|~~~~~}{|}~~}|{{{|~~|yxxyy|~~~~}||}~}|{{{{zyy{}~||}~}}~~|||}~~}|zyyz{|{z{|~~{z{{{yxwwwxz||~}}}}~~}~}|zxxyz}~}}}}}}}{{|}~~}}~~~~~~|zz{|||}~}~~~}}~~~~}|}~|zxxyzyyyyz{}~|zyzz|~~}}}~|{{{||}}||~~|{zzz}|zzzzyxxyz{~~}|{||{zz{}~|{|||||{zz{}~}~~}}}|}~}||~}~~|zyyz||}~~}~~~~~~~~~~~~|zyz{{zz{|~}{{|~~~}}~~~{zz{{zyz{|}~~}}}}{yxxxy{{{{{{{|~~~~~~}~~}{zz{}~~{yxxy{}~~~}~}||}~~}}~}|zz{}~~~}}}}~}}~~}~~||}~}}}{yxwxxz}~~|{zz{||||}~~~~~}|zxxyz|~}{zz|||}~|yxwwx{}~~{z{|||{|}~~}~~~}}{ywwxy{}~}}}~~}|}~~}}~~~~~~}|{zz{}}}|}}~~~~}~~}{zyyy{~~~}}||{{|}~~~}{zzz{|||{zyz{}~|{z{||||{zyz{}}}~~}}~~~~~~~}}}}{yxxyz{{z|~}|}~~~~~}}~}||}}}}}||}~}}|}}}~~}|||}~~~~~}{zyz{}}~~~}~~}}}|{z{|~~~~}zyxxyz{||}|zyz|~}{zyz{|||}}}~~}}~~~~}|{zz{}~|zxxwwxyzzz|~~~~|{{}~}{z{||{|}~~}~~~~}}~}||{{|~~}zxxxy{}~}}|||zyxxxy{|}}}~}|{zzz{{{z{|~}|||}~}~~}|{{zyxxz{|}}~~~~}||}}~~~~~~~}{yxyz{~~~||||}}~}|{|~~~~~~}}||}~~}||}}||||}~~~}|{zzz{{zyyz}~~}}||||{{|~~}|{zz{}~~~~}||}~~}|{|}}{zyyyyyyxwy{|}}~}}}}}}}}|{|}~~|zzz|}~~}}~~}|}~~~}~~~~}~~~~~~}~~{zyxyyzzz|~|yxwxxyzzzz{}~|||||zzz{||}}}|}~}||~~}~~|zyzzyxxyz||yyyz{}~~~~}}~~|{zyyyz{||}~}|||}~~}|{|}}||{|}~}}}~}}~~}}~}|{{|}}{{{{{|||{zzz{{z{|}~~{yxxyz}}|{||{{{|~~~~~~~}||~~}||{{zyxxyyyyyyz|~~~~}{yxyyz|~~}}|||{zzz{}~~~|{{|}~|||}~~~~~~~}|{zyxxyz|}~~~|zyyyzzyxy{|}|||}}|{{{||||zyxy{}}|}~~}}{zyxxxy|~~|zyyz{{{|~~~~~~~~}|{z{{||{yyz|~~~~~~~~}||}}}||{{{{}~||}~}||||}~}}~~|zyyz{||{z{|||zyyz{}}{zzyyzzz|}{z{|}}}}||}~}||}}}}}}~~|zxyzzyxwwy{}~~{yxxxz{{z{}~{yxxyyz|}}~~~}||}~~~}||}~~~~}zyyzzyxxz|~~~~}|zzyyzzyz{||}~~~~}|zyyxxxy{|}}~~}|{{}~~}~~{zyyz|||}~~~}{yxxz|||}~}{yyz{{{|||{zz{{{{|}~}}}~~|{{{|~~~~~|{{{|}}||}~~~}||{zzzzz{||||{{zz{~~}}}|yxxz|~~}|||{yyy{}~~~}|{{|}~~}{zxxxyyz|}~~}}~||{zyyyyyyz{|}~~~~|zyyzzyxy{~~}~~~}|}}||}~~|{|}}}~~}|{zyyyxy|}||||zyxxz|}{yxxyzzyxxz}~~~}|{|}}~~~}||}~~~|{{|~}{zyyyzzz}~|yxxy{||zz|}}{zyy{}~}}~|||||}~||}~||||||||||{|}~~~~}}~{xxyz{|{z{|~~~~~~}}}|{yxy{}~{yyyxyyz|}~~~~}||~}zyxy|}}||}}|}~}||}}}{ywxy{{zyz|}~|{zyyyyz{}~~~}|~~}{z{}~}{z{}}||}~{zzz{}}|}~~~~}||{zz|}~~~||{zzz{zxxyz{}}}~~~|{|}}{{|~~~}}}||}}}|}~{yxxz{|~~}|{yyyyz{{{|||{zz|~~~}~~}}~}~~}}|}~~}|}~~}{z|}}{{|}~~}zyxxyz{|~~}|~~~}{yxy}~}zz{{zyxxz~~}}}}}~~}}}}}}}~}{{}~~|||~}|{yyyyyzyy{~}||}{yxxy|~}}}~}~~~~}}~~~|{{|{{}~~~~|{|}~~|{|~~~~~}~~}}~~~~~~~{yz{{zyxxz}~~}~|{||{|~}}~}{{}~}}}}~~|{{|~~}|~}zxxyz{{zz{}}~~~~~}}~~~~}}{{|~~~~}|{||{z{~~~zxxy{~~~~~~~~~~}}~}}}}~~~}||{zyyyy{}}~~}||~~}}~~}|}~~||}}||}}~~~|yxxx{}}}~~~}}}{yyz|~~}{zz{}~}~~~~~~}{{|}{zz|~~~~~~~}|~~}|}~~}|~~}~~~}{{|{yxxz}}}~||~~~~~}~~}}~~{yyy{~~~|{}~|yyxxz{z{~}}}~}}~}~~~~||||}}||~~~}{z|~}}~~}~~||||~~}|}}}}|z{|||}~}~~~||}}{xxxy}}|}}~~}~~~~{|~~|{z{~~{yz{|~}|}~~~}|||~{xxxy|~}|~~~~}~|{|{z{~~}}~~{|~~}|}}~~}~~~}{{}~~{{}|||||~~~~}|~}}}~~{{{zxxyz}~~}{}~~}zyxy|~~}~~}~~~}~|~~~}~{zyxz|||~}}}~}}~~~~}||~~|}~}~~~|zy{}|{{}~~~~~~~~~}~}zxyz|~}~||{{}~~~~|zyz{~~||{{|}|{}~}~~}~}{z{~~~{zzz{|}|}~|}}}{y{}{z{}~~~}|~}~|{{}}~}~~}~~~~|{}~}{zzy|~~~~~|||}~~|}~|}~~|zzy{~}|}~}~}yxyyz{{yy|~~}}}~~}~~}{|~~{{{|~|{}~}}|}|}~|{}~}}~~|||~~|}~~||}|z{}~~~}{|}~}z|}|zzyy{}}~~~}{{{}~~}}~}~}{|{|~~~}~~~~{zz{~~|~}{{|~~~}|~{yxxz|{yy|}}}~}}}~}~~}|}~|zz}~~|~~}{{{|~}~~~}}~{z|~~{{|}|{||~|~~}|}~}~}|~}|~}zxxxy{|||||~||}~}}~{{zz|}||~~~||~||}~}}|~~~~{{zz{|{z|}}{|}~}~}~~}|~~~~|||~~{y{|zxxz{|~~}||~}|~~}~}{{z|~~}{{z{|}||}}}}}}}zyyz}}}}{z|}~}}~}}}}}}}}~~~|yxwwxyxw{~~|{||~~~}}|}~~~|}~~|}~|zzy{~~}}~|{}|zz{||||z|~~~}~~}|~{|||~}z{||ywvuxz{~~~~}~~}{{{|~~}~}|||~~|~~}|}}zy{}~~||}~~zyyyz{zwy|}}}~|||}~~~}}~~|{||}~~}{yyzxvw{}~~}~~~||~~|{}}||}}~}~}}~zyxy{|zy{}~}~~}~}{yyxx{{{|{|~}{||~~~|||{|~}}|xvvuxz{z{~~}~zzyy|~}{}~||~~~~}|}}|~~{xxxx{||}|||z{}zxxz{|~}|~~}|~}~}~}~~{{{z{}|yz{zwvvwy}}~}}}~}zxyy|~~~}}~~||{|}zz|{yxy{|}~}~|yywwyzyx|~~}}~|{|||~~||}}}~|{}~|zzyz}}}|yxx{{yz|~~~}~}z{|{yz|~~~}~}~}|~~}zxxwwyyxy}||}|zwwvx{}~~}~|}~~|{{z|}}~|{|}zz{||{zxwxxz||zz}|yxxxz{zy{}z{{|~~|z||{xwvvy{|~~~|yy{zxy|~~~|}~}|}|{|~}|}~~zxyxz{zxxz{zyyyz}~~~~}}}|ywwwy||~}~~|z{{{~~~~|zyzzwwy|~~~~zwwwx{||z}~~}~|zzy{||zz}~~}||}|yxxxy{{|{zz|~|{{}~~}{z{zxwz}~|{|}{{}~}}zwwvwy{zz}~|{|{yxxy{~~}}|}~}zyyy{}}~~}~|zz{zxxz||}|zzzz{|{z}~zwvvwy{z{}zzzz||{yy{zwvwwz}~~~~|zz}}{|~~}~~|{|}{yz|}~~~~|xwwvxzyxz}}{zzz|~}|}|ywvvx|~~{|}}{zzy{}||{yxz|zz{}~}}~~}zzz{|~}{|~~~|yyxxz{yz}|{|{xvvvx{}~~}|}}{|~}zyz{yy{~~~~}||}|zz{|{{ywxxy{|{z|~~~~|{}~}{{{|~~||}|yxwwy|}|yxzzxy{}~|{zz|}}|}~{xxxx{||{~{yyxyzzwwy{zyyyz}~~~~~~}~~}~}}~~}{y{{yy{~~}zwvvwz{{z|~~|zz{}}{{|{yyyy|~~||}}{yxwwz{{|{z{~~||}~~}~~~}}||}}|{~~}ywwwy{|{|{zzzxwwxz}}|}~}~~}}~~~~~||}~|{|~~~}~|z{{ywy{|}}{z{{|}|zz}~}}}~~}|{|}zz{zxwxy|}{zz|}{{}~{{{|~~~~||}~~}~~{zz{|}|z|~{wvvvxzyy{}||{{z|~~~}}~}}}|zy{}}{|}~~~}zyyy{}}{{|}|{|}~}~}}~}|}~}|}~|{|{|~~~~}{|{ywvvw{}}~}}~|{}~~~|||~~~~~~~}}|zyzz{}}}~~}{|}}{yz{}~~~}}}{{z{~~}|||~}{|~~~}zxyzzyz|~~~|{zz|}||~~}||~~~{yz||{zz{~}~}}}|}~~~}~~~{|}}zyz{~~~~}{{||{{~|zwvwxz|{z|~|zzz|}}~}|}}~~~~}~~~~}}~~~~~|||{||{z{~~~~~|{|~~~}{{|~~~{z{}}yyzyxxyy{}}|}}}}|}~~~}||{}~~}{{|~~|{~~}}}~~}{z{}~~}~}zxy{|~}{|}}||}}|}~~~}|zz{~}{{~|zz{|~~||}}~~~|}~}}~~~}~~}}}}}}}}|{|~~~||}}{zzzzz{{}~~}|~}|||}}~}}~}~}}}{zzyz}~|{|||{{}~}}}}~}~~~~|zz{~~}}}~|}|{z{|}}~}{{|~|{{|}~}||}~~}}~~||}~}|}|zz|~}zyz}~~}|}}{{zz|~}}~}|}~|zy{}~}{|}}|~}}~|{{|~}}|||}}{zzzzzzyz}~}{|}|}~}{|}~~}~~}}~~~|}~~|z|}|yxy|}{|}}~~~|{|}}}~~|zz{|{yxz||{zywx{|}~~|||}|{zz~~}}}{}}}|{zz{~~~~~~~|z{}~~}}}~|z{||~}z|~~~~~}}~~}|}~}}}{{{yy{}~}|||zyy{}~~~}}}}}}~~||{|~~~~~}{z|~~}}~~{xxz|}~}~~}~~}zz|}}|||~~~~~~}||~~|}~~~~||~~{z{~~~{xvvwxz{|~~~zxx{~~~~}{{|~||}~~}||||zyyyz{{|~~~~~~}{|~~~}~~~}~~}~}}{ywxz}|z{|ywvvvz~~|z{|zyxz|~~~~|z{|||}~|zz|~|{z|~~}}}~~}~|{{zz{zy{}}}{{}~}}~|yz}|xwxz}~~~|yxzzyz}~}~}zy{~~~||~}{{{}~}}}|{{||zzz{{zzz|}|||}~}|{xxz|}~||~~|zz|~~~~|yz}~}|~~|zxvvvy|}{{~~~}{zzz{{}~|z{|}}~}{yz{|zxwz||zzyxz}}~||~}||{z|~}~~}}}~}|~|zwxzz{|{xvxxwvxz|~~~}{xy{}|{{||~~}||~~~}|zyzzxy{|~|{{|zxxz{~~~~zyyz{}~|z{}~~~~}}~}{z{{wuvwxxxxz{yyyxwz~~|{~|~}~~{z{}{xwy||{zxustuvvx{~~}~}}~~~|{zz|~}|}~~~}~|yxz|zxwxyzzzywwyyxxxyzyz}~~~~~{{}~}~~|{zyvtuxyxvxxurrrsw{}~||~}~}~~~~~~}~~}|zxwxyzwvvvuuuttwzzyxy}~{{|}~zxy{yvtux{{{{yuuvvuwz|}|}~}}}~{{}~~~|||}~|zz{ywuuvxwwvtsuwxy{|{{|~~~~~~~~}~}}~|ywy{{zz{{xvtrrtwyywx}}|}|{|}|}~||~~}|~~~}||ywwy{{zxyxwvvvw{}}zwy|}}{z{}~{{{z}~|}~~zwuwxxxywttutssvxzz{~~}}|~}}~~{yy{|zzytrsstvwxyyy{||{{}}}|{z|}|||ywwwxyxwzzxxwvux{{yx{|z{|}~}~~~~~{z|||}}yxxz{ywwwtssrsw{}|z{~|{|~~~}||~}{zz{||{zwwxxvuvwwvwz{{{z{|{|~~}}~}{}~{{{{~~|}~zyy{||||yvvvwwxz|{{}}|zxy{{z||{}|{{||ywxxvuuttwxxwuw{~~|{|}~~}~||}}}~}~}||~~|||zwwtrsvxzxvy|}{xxyz|}}}}}~~}~~|yxy{{yyxuuxxxyz{{zz{|}|{||z{~~~~~{{}}|{{}~}{|}{{|yutvutuwxyy|}{{}|{}}||~}}}{|}|{|zxwwwx{|{xvxz{zwvxzz|~~~|}}}~}}~}{|~}~|z|}~}|{|{yyxvwy{{yx|~}zxxxy|~}}~}|{~~}~~|~~}zvtuuuvwxyxwyzzyyz{{{~}{{}~}~}|~~}}~zyzyxxxxwvwz{zxxyyxy|}}|~~~~~~~~~}{|~~|z{zwuutuy|}|zz}~}zxz{{}}}~~}}}}||~~~~~~~}}zyyxvvxzywvx{}}{{{z{~~~~}||}{z|~}~~}|}~zxyyxyz||{{|{zxvwxwwz}}}~}{|~~~|~~}}~~~|||~~{yyzz{|}|zy|}||{{|{yz|{|}}zxy|}{yz|~~|zxvuvxzzwwz|{ywwxy{~~~~}}~~~~~|{||}~}||}}|z{}~}zwxzzyxwwxy{|{{|}|||~}||}~~}}}|xxywvwxyzz|~~}{z{{y{~~}zyy{~}zz|~~~~}~{zzyz{|{zyy{zyxx{{{|~~|{{|~|yxy|~~}~~}~}}{yz|}|zz}~|ywwwx{|{{||yxy{|}}~~}~~|{|~~~|{zzz}~|}~~~{yz{{~}{{{yxxyyxxxy{|}~~~~}~}{zzzyxy{{zyxyxwx{}~}{z|~}{{}}{{||{}~~~~|}~~}~~~}zxwxxxz{|||{xwx{}{yyz}~~}||zyz||zxx{}~}{zzz|~~}~~{yxyyxxz|}~~{{{|~~~}~~~{{|~~|{}~}zxxxy|~~}|zz{{yyz{zz{||{{{|~~~~~|}~~~}|}~}}{{{zxz{{||{xwxyzxvwz~}~~~}z{||}~~||}|{}}ywwxywutwyzzz{z{~~~}}~|z{{{zxwwxy{|{{|}|{{|{zzz{}~{{{{}~}~~|}~~~~~}zyxy|}|{{{yxwxxwwy{|{{}}{{|}~}}}}~~~||}|{zz{|{|~~~|{{|}{wuvx{|||{{{|{{}}~~}}~~~|{{zy{|}}{z{}zwwy{|{zywvwyz|~}{{~~~}}~{z{{|~~|{{zyxxyywvvwyz{~}}~~~~~}{{|{{}~~}}|{zywuuvxxwxywuvwx|~}~}{{|~{yyyxxz{||{zxxz}}{yy{}}|||||}}|}~~~}}}}|zz|}}}}}}}}zxxzzwuuvy{{{zxwxzz{}~~}}zyyz|~~~}|{{||zzzzyxyyywwxz|~~~}|~~~~}}~}}~}}}}||}}{yyzzzzz{yvuttvy|~}}~}|}}}~~|{|}~~}|}~~{xwwyzxvvxzzyyxwxz||~~~~|zzz{}~~~}||~~|xvvxxxxwtstttux{~~}}~|{{}~}}}~~~}|{{}}{{{zxwwxyxyz|}||}|yxyz{~~|{{||~~~|{{z{}~}{zz{{yxxxxxxyzxwvvvx{|}}~}}~~||}~~}|}~~}{yz||yxy{}}|zyvuvvwx{~~||{|~~~|zz|~~|{{|}}|zxwwwvuvwxxy|}~~}|zyyzyz~~|{yxxvuvwxyyzzxvuvwz}~~~|{}~~~~~~~}||||{yxwutsssuwwwwx|~~}|z{{|~~~|yxxz|~~}{xwwyxvtuwz|}}|zyzzyz|~~}|}~}}}}||}~~~~|{{{zxxy{{zyxuuvwwxyzzz{}~{z|}||{{}~}|{{|zxxyzz{|}|zxwwwyzzyy|}}}}}~~~}}}||zy{}~~}{ywvvxyzywvwz{|{|~~}}~|yxxxy{{||~}|{|{xvuvwxxxwvwwwwxz|}|~~||}~}~~~|{|~~|yvuuuuuvwwwxyzz{}{zz{|{{{{zzyxy{}~~}{zyxvvvwwwxyzyyzz{}}|}~~~~}}~~}~|yyz{{{||zxvvvwy{{yy{}}|z{}}|{||}~~||||}}}}}}~}}~|zxxz{{zzxwxyyyz{{{z{||}}~~}~}||~~|}~~{yz}~~|{{{zzz{zyzzzyxwxyz}~}}|{{{zz{{{zyz}~~~|yyyyxwwwutttvy{||{|~~}}}~~~~|{|~~}~}{zxxxyyxvvxzzywwxz|~}||zxvvxy{|||{|~~zwvwxxxwvuuvwyz|~~}}~}}~}}~~}||}~~~|{{{yxyyyyz{{zz{|||{{{z{}}|{zyzzz|~~}~}}}~~||{{{z{{ywvvwy{|{{{}}{z{|~{z|}}|}}~~}}|}}}}~~|||{{|||~}{{|}|zxxxxz{|}}zyyzz|}}|{|}|{zz{|}~}}|{zxvtttttuwyz{|~~~}|}}{zz|~~}~~~~~~{z{||||||{z{zyxxxxxy{}~}zxwwvvx{|||}~~}}}}~}}|zxxwvtttuwy{{{|~~~}}}}~~~~~}ywwwx{}}|{{}~|{zz{}~~~}~{yxyz{|||}~}|}|}~~}~~|ywwxyxyzzzz{}~~}}~}}}}{{|~}{yxy{}~zyyz|~~|{zyy{|}}|zyy|}}~|zz|||{{|~~|zyyz}~|xvutuvxxwuvy|}}|}~|{||||}~~}~~|{{|}~~|z{}~}}~~|yxwxz|}}}~}||~~|{yyzyxxxz|~~~}}}}}}|wuttuvxyzzz{}~~}}~~~}}~}|{|}~|zyyz{|}~~~~}||}~}|||}||}~~~}}}}{{|}}||}~~~~}}}~~}zxwwz||{z{|||{z{|~}|}}~~}}}zyyyy{}~~}}~}}~~~~~}}|||||{yyxxyz}~~||}}|{z{}~}}||}}|{{}~|zxvwxxxyyyyxxy{|~~}{{{|~~}~}{{|~~{zz|~~}~}}~}}}|||||{{{{|}}{zz~~~~~~~~}{zzz{zyz{}~~~~~}|{{yxwwwxxy|~}|zz|~~|||~~||}}{{|~~}~~~~|yz}~~~~~~}}~~|zyy{}~~|yyz{}}}~}|{}~~{zz{||}~~|{{}~~}|{z{|}~~}|zyy{~~}{z|~~}||}|{z|~~~}zxyz{zxvvxy{}~~}}}}~~}{zyz||{zyzzz{}}}}~{xwx{||zyxxx{}}}}}}~~}{{|}}}}}~~~~~~~~~~~|zxxz{{|||{{|~~}}}~~{yyyyz{~}|}~}||~|zyzz|}}|yxxz{|}}}||}~zyz}~}|||}~~~~~{{|~~|{}}|||}}{zxyy{{zzz{}~~~}}|}~|{yxwxz|}}zyyz}}|}~}}~~~~}||}~~~~|{z{~~}}~}}~~}}}~~|{{|~~~|zxwwxzzzyxwvwy{}~~}}}}||~}{zzxwwxz}~}}~|zxyz{{{|||{||}~~~}}|{{zyyz}~}}~}}}~~~}|zxvwy|~~}{ywy|~~~~|zz|}}|||~~{zz|||}~~|{z{|||}}yxy{}~~~~|{z{|}~~~|zyz|}~}}~||~|zz{|}~~|yvtux{}~|{{}~}~}ywxxyyzyy{}}}|||}~|||}}zxz|~~||}~~|zz{~~~~|{|}~}}}}~~~~}}}}{xvwxzzzywvutvz~~|zyz|~}{yxxxyyzz{|~~~{xxz}~|zxx{}{z|~~zxxyyz{}~~~}}}~~~}|zxwwy{||{zyxy{}~}~~~}|zyy{~~}|{||{yyz|~}|||}~~|yy|~}|}}}~~}zwwx{~}}}~~}zyyz}~}}~~~}|{{{{{{zxwvwy{}~~~}||}~~}}||}~{xwwvvuvwz~~}~}{yxwxz|}}~~}|zyxz~~~|}~~~}}}~}~|ywvvvvwwxxxwwz~~|{zzyxxz|}{ywvuvwyz}}||}~}|||{xvwz~~~|{|}|zz{{{|}}}~|zz|~~|~}||}}|ywuvxz{|}}|zz}}|}}{{{zyxy{~~{yxxxyz{}~}|{|~~|}~}{zz|~}{z{{zyz{~}|z{|~~}||}~~}~~~~~~}}|~|ywwxz|}|zxwx{~~}||}~~~}{zz|~{vttuvwwxy|~}~|ywwxz{|{zzz{{|}|}}}~}}~~~~}~~~}yvtstvxz{zzyyz}|zxxxxxyz|zwutuwy{||}|zyz|||{zyxxy|~~}}}~~|}~||}~~|||}~~~~~|{{|~~}}}~~{{}~~~}}}|ywuuvz|~~~~~||}{yxyyzz{|}}{yxyz{{{|}~}|~}{{}~~~~|{z{||}|{{|~}|zz}|zyyz|~~}|}}{zz}}zz}~}}{yxxz|}}}}|zxy{~}|}}|{zzz{{ywvvwxwxy{}}|}}~~}|zxwuuwxz{{|}~~~~}}}~}||~}}}~~|{zyxwvvwx{}}}{yy{}zyyyywutuxzyxwwyz|}~~}zxxy{|||zxvvx{}~~~~}||}|zyy{}~~|{{}~~{zzzzzzyxyz}}{|~}|}~~|zzyyyxwwy{~~}{yz|}~}||~~~~~{z{}}zwvx{}~~||}|z|}|~}|{{|}~}|||}~~|zyz|~|{}}}}~}zy{}~}|{zzz}~||}~}}|zxwwxz||zwwxyzyyz{}}z{|~~}{xvuttvz|}||~~~~{{}~}}}}}~~~~}~~~~}||||{ywwxz{|||||}~{ywvuuttvz||zyy{|}}|}~~~~|zxwy{{zxwvvwy|~~~~}~~}~~~~|||||~~}zyyz{{{|||||{{{|}~}|{{{}}zwvvwxyyz|}|||||||}~}|}}}~~~}{{|}|||{zyxy{}~~~~~}|}}{{}~}}~~~}|{zy{}~~}|}}yx{}~|{{||}~~|{{}}{{|~|zyyyyzzzz|}|zyxxxy{}~~{zzzyxxxxwvvwz}~~~~~}|{|~~}}|{{}~{z|}|}~}{xwxz|~~}~~~~zwvvvwwvvx{~~|zz{|~~|{|~}yvuvwxyyzzyyz}~}|||~}}~~{ywx{}}}}}~~|{{}~|}~|{{{||}}}{xwwyzzzz{~}|{}~~~}~~|{{{}~}yxxz{}~}|zz{}~~~~~~~}}~~~}|zzyyy{~~~|zz|}||}~~|zyy{~~}|||}~~}|{{{zz{{{|}}{yyz|}}}|}~~|ywwxyzzzyxwxy{}~~~||}~~~|}~}~~{zz{|}~~~~|}~~}|zyz|~~~~~~}||~~~}}~~}|zyxxwxxyyz|~~}~~~}|||}|zwutvxzzz{|||{|~}}|{}~~~~}}~~~}}~~~~~~|zy{~~~~}{{|~~}|zyxy{}}}{zyz|||||}~}{{{}~}}||{{|}}|zyz{}~~~~}{z{|}}|}~~~}{z{{{{{|~~~}}~}|||}}|zwwy|~~|{{{|~~||}}|{yy{~~|{{{zz{|~~}}~~~|zxxyz{||zxwxz}|{{}~~||}}~~~|{|}~~~~~}~~}{zzz{|}}|{z{||{{{}~~|zzyyxxxyz}}}}~~~~}zyyz{|{yxwxz|}~~}||}}{|~}~}}}~~~|zz{}~~~~}{{|~~}|{zyyy{{{yxwwwy{}}}||{{{{{{|~}|{{zyyz|~~}{yyz|~|{}~}~}|~~}|||}}~}{yyz{}~~|||~}zxwwxxwwvuuuvwxyxxyz{|}~|{zzzzz{}~{yxyyxxy{~}}}~|{{|}~~}}}}~~}||~~~~{{{}~~}zyxz}}{z{|}}~~~}|zwvuuuvwxyyxwvwyz{zyyzzz{|}}{ywvvwyzz{}~||{{{||}~|zz{|~~~}|{|}~~~~}}~}{zz|~~~}{xvwy|~~~~~~}{zxwwwxyyyxusrsuwwwwvvwz|}}|{zz{|}}}~~|zxwwxyz{|||||{{|}~~}}~~~~}{yxwwxz|}~~|z{}~}|yusstuvvusqppruxzzzzz|}}|||||zyyz|}}{xwwwxxyz|~~|{}~~}||||}~~|zz{{{zzyyy{}~~|{|}~~}||{zyxusrsuvxxwutstwz||{{{{{{||||{zyzzzyxy{~}{zz{{{{z{}~}||}~}{|~}}~||}~~}}}}}~~}||||~~}|zzz{}}}}}~~}zxwwwxxxwvusrrtwyyxxxyzz|}~~~}}||||}}{yxwwwvvwy|~~~}}~~~~|}~~}|{{{zzzzyxxz}~}~~}|zxussstssrrrqrtvy{}}}||}~}{zxxxyz{{{yxwwvvvxz|~~~}}~}}}}~}~~~zwwxyz{{{{{|~}{z{|||{zyxwvtssstuvvvvvvxz|~~}|{{{|~~|zyxxxxxy|~}{zyyxxy{}~~}||~~~~~~~|zz{}~~}}}~~|zyyz|}}}}}|{zxvutuuvwvusstwz{{zyyz{~}|{{{{z{|~~|xvttuuvxz|~~||}~~|{zz{{{{{{zzz{}|{|~~}}}}|zwtqppqrstttssuy|~~~}}}{zxwvvwy{{{yvutuvxz|~~~~~|{|~~~~~~~}}{ywxyz{|}}~~}~}zyyyzzzzyyywtrqqsuwxxxwxyz|~~~}}}~~|zyxwvvvxz}~~|zyxxyzz{|~~~}~}}}}~}||}~~}}~}|zxwxy{|}}}|zxvuttvwxxwvuuvxz{{||}}~~}{zzzzz{|}}|ywuuvwwwx{~~}|||}}~~~}{zz{|}}|zzz|~}{z{|}~}}|zwusrqrsstuvvvuvx|~|ywvvvvvwxyyxwvvwxz{}}}}}}}}}||{|~}{{|}~~|zyy{|}~~~~~~zwvwyzzyxwwvutssuvxyzzzyyy{~~}}}~~~|zxwwwwwwxy{|}|{{zzz{}~~~~}}~~}}~~|{{{}~}~~}{xwvxz{||{zzzyxwvvwxyyxwvuvx{~~}||}}|{{zzyxxy{||{ywwwxxyz|~~}{yyy{|~~}}}}|{{||}}||}}~~|{zz{|{{zzyxwvtsstuvwwwvvwz~~|zxvuttttvy{{zxxyz|}}~}{{{|}}|{yyz}}||}}~~~~}{{{|}|ywvvwwwwwxxxwvuvwxyzzz{{{|}~}||||||{{yxvutuvwy|~~}}|||||}~~~~~}}|}~~}}}}}}}}}}}}}~~~~~}}~~}}|{ywvvwyz{||||{zyxxxyyyxxxxyz{}~~}}|||}~|{yxwwwxy{|||{zyyxxyz|~~}|{zyzz{}~}}}~~}}}}}|{{|~~}|{|}~~|zxwxy{{|{zyxwvuuuuwxyyzzz|~~~}zwuttttuvxz|||{z{{|}}{zzzz{{{{|}~~}}~~}{{|~~~{wtstuwxyyyyzyyxxxyz{}~~}|{|~}{zz{{|{{ywutuuwxy{}~|||}~~}|}}~~~~}|zzz{|~~~~||{||}~~}}|zwutuwz|}}}~~~|{yyyzz{{zyxxxy{}}|{{{|~~|{zyxxxxyz|}~}|{zyz{|}~}|||||||{{{|~~~~~~~~|{{{}~~}}~~}||{{zz{}~}{yxxyz|||||{{yxvvvxyzzzzyy{|~~~~~}|{ywvuuuuvwz|}}}||}~~{yxyz{|||}~~}~~~}~~~}}}~}{yxvuuvwyz{||{{zzzz{|}~~}|{{{|}}}}|{{zyyyyyxwwwwxy{|}~~}~~}}}|{{{{{|}}{zyy{||||{{zzyxwwy{}~}|||{{{zyyxxwwwy{||||{{||}}}}|{zyzz{{||}~~}||{{{||~}}}}}}}}}}|}~~}}~~||||~~|{{{{{zyyyyz|~~~~~~~}|zyy{}~~}||{{{zyxxyyyyyyyyz|~~}|||}}|{yxwwwwwxy{}~~~~}{zz{|}~~~~~|{|}~~}}||||}}}~~~}|{zxwwwy{|||}}~~~}|{{||||{zyyz{|}|{yyxxyz{{zyyyzz{{|}~}||||}~~}}|||}~~}}}}|{zyxxxxyz{||||{zyz{}~}{zyyyxxwvuvwy{{{zz{{}~~}|{{{{{|}~|{zzzzz{}~~~~~}}~~~~~~~~|zyyyyyyyxwwxz|~}|{||}~~~~}{yxwwxxxxxyyz{|}~~~}}}~~~~}|{zyxwwxy{}~~}}}}}~~}}}~~~}~}{zz{||||{zzyz|}~}}|{yxyz|}~|{{{{{{zzzyyyyzz{zzzz{{|}}}|{{{{|}~~}|{yyyz|}~~~~~~~~}}}~~~|{{{|{zxvuuvxy{|}}~~~}|||}~}{yxwvwwwvuuuvxz{|}}~~~}||}}}}}~~~}{zyxxxyz|~~~}}}~}zxwwwwwwvvvvx{}}}~~|{yxvvvvwxyyyxxz|~}|{zyyzz{{|}~|{{{{|}~~~~~|{{|}~~}}~~|{zzzzzzyyyyz{|}~}{z{|~~|zyyyzzzyxwwwxz{|}|||}~~~~~}~~~~~~~}{zyxxxxyz{|~~|{z{}~|zyyxwwvvvwwxz|~}{ywvvvvvutttuwz|}~~}|{{{|}|{yxwwwwwxz|~}|}~}}}~~~}|zxvutuuvvwwxy{~~}|zxwvvvvvwwxxxy{}~}|zzyyz{|}~~~|{{zzzzz|~|zxyz|~~~~~~}}}~}|{yxxxxyzzzzz{}}}}}~~}|zyyxxxxxxxxxyz|~~~~}}}}~~~}|yxvuuvwy{}~~~}|zyyz|~{yxwwwwwwwwwxz|}~}{xvuttttuuvwxy{~~|{zyyyz{{{zxwuttuwy|~~~~~}}||}~}}|zwuttuvwwwxyz|}~~}}|{yxwutstvwyzz{{}~|zyyyxxy{}~~}{yxxxyz{}~|{yyyz{}~}}}|||{zyyxxyzz{{|||}~~}|}}~~~~}{ywwwwxyzzyyz{}~}|{{{|}~~}{xvuuuvwxz|~}}~~}}||{{zzyyz}~}|{|~~~}|zyxxxxxxxxxxz{~~|ywusstuvvwxyz|~}{yxxxxyzzyxwvuuvwy{}~~}}}}~~~}||}~~~}|{zzzyxvvvvwxyzz{|}~}}}~~}|zywutttvwyz{|}~}{zxwwwxyz{|||{zyyyzz|~~}|{yyyz|~~~~~}}}~~}|{zz{|||{zzz{||||||}~}}}~}|zyxxxxyz{||||}~|{{{{{{{|||{yxwvvvwy{}~}|{{{{|||||{{z{|}~}{zyyz{|}}}~~}}~~|{zyyyyyyyyz|~~|{ywvuuuvwyz{{|}~|zxwwvvwxyyyyxwwxy{}~}}}}}}}|{{|}~}}}~~}}}}}}~~}{{{{|{{zyxxxyyz{|}~}|}}}|{{zyxwwvvwy{|}~~|{zxxwvvwxz|}~}||{{{{|}~~~~~}|{zyyz|}|{zzz{{||}}}~~}{{zz{|}~~~}}}}}}~~~~~~}}}}~~}}{zyyz{|}}}}~~}|zzyyyz|}~}|zyxxxxyz|}|{zz{{|||}}}}||{||~~}|{yxwwwy{|}~~}|zzzz{{{{{{|~~|zxwvvwxxz{|}~{ywuuuvwyz{|{{zzzz{|~~||{{{{{{{|}~~}}~~~}}|{zzz{|~~}||||}}}|{zzz{|}~~~~~~|{{{|||||{zyxxyz{|~~|zxwvuvwy{}~}||}~~~~~~~~~~}|{zzz{}~~|zyxxyz{||}}~~~}}}||||||}~}||}}~~|{{|~~}|{{{|}~~|{zyyyz{|~~}{zyyy{}~}||||||||||}~~~~~}}}}~~}|zywvuvwxz|}~~}}|||{zyyz{|}~~~}{zxwwxz|~~|ywvvvwwxz|~~}|||}~~{zyyyz{||}~~~~~~~}|{zzzyzz|}}}}~~~~~~~~~}}}}}~}|{|}~~}|{zz{|~~|zyxwwwxy{}~}|{{|}~~~}|{zzz{|~~|{zyyxxyz|}~~~~~}~~~~~~~}}~~~}|{zzz|}~~}||}~~|||}~~}|{{{{{}~~}||{{|}}|{|||||}}}~~~~~}}}~~|{zyxwvvvwy{}~~}}~~}|{{zyxxxyz|~~|{zyzz|}~~}|zxxwxxz|}~|zyxxxy{|}~~}|{{{{|}}~~~}|{{{{zzzz{|~~~~~~~~}}|{{{}~~}}}}~~~}}}~~}}|{zyxxz{}}}}}~~}||||}~~|{zyyyyz|}~~}||{zyyyz{}~~~}~~~}{zyxxyz{|}~~~~~~}}}}~~~~~~~~}||}}~~|{z{{|~~}||||}~~~~~~}||||}~~~}|{zyxwwxxyz|~~~}}}~~~}{zyxwwwwxz|~~|zyz{|}~}{zzyzz{|~~~~~}|zyxxz{|~~|{yxyyzz{{{||}}}}}|||{{{||~~~~~}}}||{zzzz{}~~~~}}}~~}}|{{{|}~|{z{|~~}}}}~~}|{zxwwwxyz{|}~~}||{zyyz|~~~~~}|zywvvvxz|}~}|||||}}}}}}~~~~}{zyyz{}}||{||}~~~~}|{{z{|||||}}~~~~}}|{zyxxxy{}~}|{{||}}}}}~}|{zyxwvuuvx{~~{zyyyz{}~}|{z{|}~}|}}~}|{zzz{|}~}|zyxwwwwxxyz|}~~~~~}|||}~}|||~~~}}|{zyxxy{|}|||}~~}|}~}{zyyz|}~~~~~~}{zyxwwvvvwyz{}~~}|{z{{}~~|{ywvuuvwy|~|zyyz{|||{{{|}}|{yyyy{}}|{{|~~~}}}||{zyyyzz{||}~~}|{zzzz{|~~|{{{{|}}~~~~}{zyxwvuuvwz|}{xwwxy{|}~~~~~}}}}~~}|{{|~~}|{z{{}~~}|{ywvuuvwwxy{|~~~~~~~~~}}}}~~}|{{zzyyy{||{z{|}~~~~~~~}{ywwwy{}}|{zyyxwvuuvwyz{|}~}||{{{|}~~}}~~~~~}|{yxwvvvxz}~{yxxxyzzzz{{|}}{yxwxy|}}||}}~~~}}||||{zyyyyz{{||}~~~}||{{{{|~~}|{{|}~}|zyyyxxwwwy{~}zxwvvwyz|}~~}||}~~}{{z{}~~}}|||}~~}|{yxwvvvwwxyz{|~~~~~~}}||}}~~~~~}}~~~}}}||{zz{|~~|zyyz|}~~~~~|zxwwxy|~~}|{yxwwwwvwwxyz|}~}{zzzz{|}}~~}}|}}~}|zyxxwxy{~}ywuuwxz{{{||}}|{yyyy{}}|{{{||}}~~~}}|{{zzzzz{zz{{|}}}}~}|zzzz{|~~~~}}}}~~}|{{{zzyyyz|~}zxutuvy{}}|{{|}~~}|{|}~|{{{|}~~}{{zzyxxwwwxyzz{|}~}}||}~~|{zz{|~}||}~~}|||~~}}~~{yxyz|}~~}|zyxyz}~~}}}}~~}{zxwwwxxxxxy{|}~}{yyxxyz{|}}||{|}~~}|{yxxy{~|ywuuwxz||}}~~~}}}~~~}|{{{|~|zyyxyy{|}}}|{{{{{{||||||}}~~~~~}{zyxyz{}~~~~~}~~~~}}}}}||{{|}~~}~~|zxvuvxz}~|zzz|}~~~~~~}{zyyz{|}~~}|{{{zzzzzzzz{{|}~~~}|{{{|}}~}}||{{{{|~~|{z{}~~}~~~|{{|}~}{zyz|~~}~~~}|{zz|~|{{{{||||||{zyyxxxyzz{{|}~}zxxwwxxyz{|}}}}}~~}}~~~~~}|{zzz{}~|ywwxz|}~~~~~~}}}~~~}|||~~|{zzzzz{|}~~~~}||{{{{||{{{||}~~~~~}{zyyz{||}~~~~~}}|{zyz{|}zxwxz|}~}}}}~~~~}{{{}~}|{zzz{{|}~~}||{||||{{{{|}}}|||}~}|{z{{|}~~}|}}~~}}}||{zzz{}~~~|zxwxy{}~~~~}}}~|{{}~~}|{{{{|}~~|{{{zzzyyzz{{{{{||~~}|||}}~~}}}}~~~~~|{zz|~~}}}~}{yyy{|~~~}~~~~}{zz{|~}|{{{|}~~|{zyyyyzzz{||}~~~~~~{zyzz{|}~~~~~~~~}{zyyz{~~~~{xwwxz|}~~}}}~~}~~}}||}~|{zyyyz{}~~}}|{zz{{||}}}}}}}}}}~}{zyz{|}~~}~~~}}~~~}}|{{|}~}|}}}zxwwx{~}||}~~}|}}|{zzz{|}~~}{zzzzz{{{{{||}}}}}~|{zz{|}~~~}|||~}}}~}|{{}}||||}~{yxyz|~~~~~~~~~~}|{|~~}|zzzzz{|}~~}|{yyyyz{||}}~~}zyxyyz{|}~~~~~~~~~~}|{zz|~~}}}~}zxwwy{}~~~}||}~~}~~}{zyxxxyz{}~~~}|{{{{|}}~~~~~~~}}~}{yyyz{|}~~~~~~}}~~}||}}|{{{|~~|zxxy{~~|{{|~~}~~}|{zyyyyz{}~~}|{z{{{||}}}~~~~~~}{zyyz{|}~~~~}}~~~~~~||}~~}||}~~}|{zz{}~}{zzz|~}|}~~}}~~}{zyxxxyz{|}}}|{zz{{|}}~~{yxwxxyz|}~~}}}~~}~~|{{|~~|{{{|}~}{yxy{}~|{{|}~~|{zyxwwwxy{|}~~~}}|||}~~~~}}~~|zxxxyz{}~~~~~}|}~~}|}~}{zyyz{|~~|zzz}|{z{|}~}}|zyxxxxyz{}~~}|{{||}~~~}~|zxxxyz{|}~~}}|}}~||~}|{{|~}{zyyyz|~}{z{}}|{|}~}{zxwwvwxy{|~~~||{|}~~}}}~|ywvvwxy{}~~~~~~}|{|}~~~}{zz{}}{zyyz{}|zyz|~}{zz{}~}{zywvvvvxy{}}}}}~~}|{|}~}zxwwxy{}~~~~~~}|||~~~~}{z{}~|zyxxyz|~~|{{}~|{z{|~~~}|zyxwwwxyz|~~}||}~~}|||~|zxwvwy{|~~~~~}|{z{|~~}{yyz|~~|{zyyyz|~|{|~~}}~~}|{|}}|{zyxwwwwxz{}~}}~~}|{{}~}{xvvvxy{}}||}~~~}|zz{|}~|zyyz|~~|{zzzz|}~|||}~}}~}|{{|~~|zyyxxwwwwy{|~~~~}|{{{{|}~~|zxwwy{|~~}}}~~~~~}|{{{}~|{zz{}~}{yxxy{}}}~~}||}~}||}~}~~}|{zzyyyyyyz|~~~~~~~~~}||{{|}~~}|zxwwy{}~}}}~}}|||{{z{|}|{zyxy{}~}|{zz{}~}||~}|||}}~~}}~}}}{{{{zyxxyyzz{|~~~~~~~~|{yyz|~~}{yxxyz|}~}|{{||||||||}~~}{zyyyz}}|{{{{{}~}||}~}|}~~~~~}}}~~||~~}{zz{{zzyyz{{{|~~}}~~~~~~~~}}|{zyyy{~~{yyz|}~~~}{{{|~~~}||}~|ywvxz|~~|{{z{|}~}|}~~}||||}}}}~~~~||}~~}}|{{{||{zyz{~|{|}~~~}}~~|zyyz{}~|zyyz|}~~|zzz{{}~~}|{}~}zwuuwz~}{z{}}zz|}~~~~}}}|{{{}}}~~~~}}}|{{{{{zyyy|~}}|||~~~}|{zxwxz|~~~}|{zzz|~{yyy{}~~~}~~|yxwwwz}~}|{z{~~}|zz|}||||||}}{|~||~~}{|}~~|zzz{{}}||{zz|}~~~|zyyyyzz|~}{z{{|~}}~~|{zz|~~~~~|ywvvx{|~}{{{|~}{z{|~}zyyz|}~}~~{zz|~}~~~~|{zyz}}zyy{}~~~~~|zyyy{||}}{yxy{~~~|zz{||}~~}|}~|{zywvvx{~}||}yxy{}~~~}{{{z{~~{yz|~}{{{{{{{|~}zyy{~}{yyzzyyz{{}~}{zz}~|}|zz{}~~}~~}{z{zzyyz~~~~}~~{yy{~}{z{||}~~~{yyz{~~~|{|~~~}}~}zyzzz{|}{zyxyz|}|z{~}|}}~~~}}~~}||}}}~~}}}~~}{zxxxz|~~}}|{{{||||zyz|~~}{xxy|~}~~~~~||}}||~~{yzz{}}zy{{{z{||||}~~}||}~~~}|}~~~|{}~||~|{|}}~|ywwz}}|{{z{zyy{|}~~~~~~}zy{~}{|~~|zzz|~|{z{{{zzyyz|}zy{}~~~}~}}~~}}~~~}{|}|}}|~}~~~{zz{|}||}~~{yzzz{||~~~~~}{z|||~~~~{yy{~|||zwvxyz{{{{{|~~}}}zyz}~|~~}{{}~~~}~||~}~{z{|zy{~}~|xvvx{}~}~~~~|~~{{||~~{yyz{z{||{yxxyyy|~~|{{}~|{{}|{zyz||}{z{}~}}}|}~~~}{zyz}}}~|{}}{wwwvy|}|}~}{{|xwyzz|}~}yvwyz{||{z|}}}||||||{z{}|zz{{z{zz}~~~}}{xxz~~{{}}|||}{wwyz{~~}~~~}~}{{~}}|{zxwz~|zxvuvy|~}zz|~~|{}|{zxwxzz}}zzz{~~|}~}zzywx}~~z{~~|ywvx{~}|~~~{|~}|~~{zyyxvvy}~yxxxz|||}~~~~}|}~}|}~~}||yuux{}{{}~||~~~~~~{zyy{}|{||}|{|zyyyy{|zxz~~}}}{wvvx|~~~|zxx|~}|{{}~zy{|}~}{yxxz{}}|{{}~}}~}}}{z||zz}}yz{}~}|~~}~{|}|zz}}}||~|{}}|xvwy{~}{z{{yyz{z{~~||{|~~}|wuvyzyz{|}~~{|~~}~}}||~~|}}}}|{}|z{~~~~}}}|}~~{wv{}|~}|~~~{wuwyxwy|||}|z{|~~|z||{{{~~||}}}{zywwxz}}||~}~|}~~}z|}~}|~~~~}}}~|zy{|{{~~ywz}|{|~~}|}~~}~~~|{xxyyz||yy}~~~}{{|~~}zy||z{|{|}|~}zxyxwz|||}}~~{{~|}~{xwz}~~}}~~~}{wwyz}}|||{||}~zy}~}}~|{}}~~|yxxxwxyyxz}~yvx|}}|zz{{}|xvwvrruxz}}{z|}z{~~}|{{|}}|{|~~|{zzxxz}~|{yyzzz|~~zxyxx{~}|yyyvrruwx{|||zz~}{|~~|}~{{}|~|xurruxyz}~|~|}~|z|~~~}~~~~|ywuux{|}}~~zwxz}ywxxyz{zyxutwwvx~|{}~~{{zyz~~}}}|||vrsrty|}}zz~|{~~}~|{|~}{{||{~}|~~|{|~}|zyywuy|~}yyz|~|{xw{{vtsuvx{|{y|~|{{{||{~}{z{~{trsrpsx{||}~~}{}~||~}{}||}{}~zz||z{}~|yzz{{|}|}{xyzy}~{|{xz{{|}~{wuusqrvz{|z{}|~~zxyz{|}xtsrsx{|y{~}}|{z}~~~~|xz}}|zyxy{|yz|zy{{yxy{}}zz~yuuttwzzz{}}}}~~}}z{}|xwz|}}|xttvxzyz~~}}~~||~~~|{|zy|{|~}|zz|{z|~yy{{z||zzy}}}~}|~~}|~|~~{yvuux{zyy{}}||}~~~}~}|{{{{}}{wtvwuttx|}|~~}}~zwy{z{}~~xwy{{|{{|z}|}}y{~|}~|}~~}~~{xy~~zwuvwvuwww{~~~~}{|}{yxy{zxyy{|}zwxxwwx{|~}~}{|}{y}~}}}|}~|z|~}yz{{~|xx}}}~~~~~}~}~~z|}}~{}~||xvxyz{yy|~}{|{z||{z{|zyyz{{|||~~zxtv|}}|z{}|zy{|{}~}|}~~|{~~{zyvtx{yy}~|{|~~~|~}}~~|}}~}~~}~~|{{zww{|}|zz}}z{|}{z~}zzwuy{{}~|zyvuvy{z{~|}|zzywwy}~}{~~|{{zzywtuxx|~~~|}yxz~~}zz~~}~|zz{{wtx{|{|{|~~|~}z{||}{xxxvstvy}}yxy{}{yzyyyy{~~~~{xwxxwx|~~}|||||~{|~}}}|z}~}|}~}{yx{~~{z}|z{~{wwz}||{xutuwy{}}||zyzz{|{~}{zyz}|}~}~{vusrtz|||}{{|~~|{}~~{z}~~}~|~~~|z{{}}z{{z{~|zyz~|{}}xuutuz}|{}|wuwzz}~zyyzz{|}|~|zxwvvuswz|~~~~~}{yy{~~}~~}}{{~~{}~~z{~|yxy{|}}{yy{~}|{wssuusuy|}}}z{|{z~}}{yz|~}~~~|ywvwyyyzz}}|~~~||{}~{{~~~|~{xx|||~}|~}}|}|{~{yz{zyy{zxvtttuy||z|~|z{||~~}~~}}~|}||~|y{zxvuuwyxy}~~{{|}~}||||zyz}zvxz|}z|~}}~|y{{{|}zvwyxuvwxzz{~~}~~{yz~~~}~}|}}yz~~}~ywwxxxywuux}~zxz||}}zy{|}~|{yy{}zz{}}|yy{ywwustxxvuv{~}~~~~}~~~}}~~||~~~|z||{yz|{xvy{}~~}z{}|{{{{}~}zxvx{}~}}}~}||zwutvwwvuwxwwz||}|z{~~|~}~}}}}~~~}|{z{}||xttxzyx{~}|{yvwy{{{}~~}|ywy|~~|}~{zyxwxwvxzzywuw{}|||}~}|z{~~|{}~}}|}~~{}}zyz||zxwwwxy}~zyywwxxxz}~~|~}}}||}|}{||yvuwxwxxwvvwvwxyz}~|}~~||~}{y{|{|~~~||{}}y{|{xvwz~}~~zuvz{z|~~~}~|zz||z|~~|zxvttuwxzyuuwxzzyz}|{{{{|||~~zxz{}~}}}~}|{yvuwxy{}{ywwvux|~|{}~~~{yy|}{|~~}}|xvwwxyxy{zxxxxwy{|}~~}|~|yy{~~~}}|{{}}}~~~~}}~{{|}|zuuxy|~ywwxwwxy||}}|~|z{|~~~||||yxwvy{{yvuwxxtux{|}~~}{|}}}|zyyz||}~~|{z|}~}||zyyy{}|||ywy{|{z|~~}zz|{z{~}}~}zyzyvvxzxxxwvvvy|||}zy{}{yz{{{|{yz|~}}|}~~}~~{yxyz{||~~|{}|zyyxwy{||}~~}}~{{{z|}{|||~}|zyxy||ywz{zwvwy{}~~}|{{zy|}{z{~}||~~~}y{~~~}}}}||}{yz|}~}}}|zyzz{yvwz|~~~~}}~{y{||~}~}|~{{{{{zyyxutwywx|~~|yyxxz||{zz|~|{}}yyz|}~~|}~~}{|{{}}~~}|{zyyzzz|~}}~|{{{{~|{|}}~~{{}}zwwyyywuwxy|}}~}yxywtvy{{||||{|||}}}}~~~~}}}|zz|}~~~~~}}}zwvwyyy|~~}{|}}~{z|}}||{xx{~~}}}|z{{zxxz{zy{}~~~~}{{|xwuux{{z{~|{|~~||~~~~{}|{{|}}|~~{{|~~}}~}{zywuvz~|{yz|~~|~~}~~zzzy{}~}}zz{{yxxxxz}{yz|~}|{}~}zvuvxz{|~}zz|}{z}}z{~~~~~}}|{z{}~~~|}~||}~{{{zwwz|~}{{|}~~{xwz}~~~}|zxxxy{zy{|{|~~|{}~zuuwwuvy}~}{z{{|}}}}~~}}~{wx{~}|~~|yuuy{{~|z}~}||}|zxxxyz{}~||||{zz{|}~}{z{~}{{{|}}yvvwwx{}||}~}|zz~~~||~~|zy{}~}~~~~||||~}{ywxxz~zz{||}~~~{wxy{||}~~|z{}}|||{{{zzyy{}}{|~}||zxvvz}}{{zzzz{|~}|}~~}{~{yz|~}~~~{|~~~}~~~~{{}|z{~}~}{}~}~~|{zzy{~}}~}zz||}~{yz{|{zz|~~~~|{yxwwwxyy{|{yy|~}|}}}}{||zzz|}|~}|~~~}~~}}zy|~|{}~~~}{|~~}~~zz{{yx{~~~~~}|{~~}{{zxz|}}{z|~|}~}~~zwwy{zyyz||{|}}~||}~}{|||}}~}}~}{|~}~~}||~}||{{}}{}~}{{}}~~~}{yy|~||}~~{z{|ywwz|||}}{{}~~~{{zxxy{{zxz}~~}}~~||~~}|{~}{{zy{~~}}||}~~{z{~}|}~}|}||~{{|}}}}~}{|~~}}{ywx{}}{{|z{z{~|{||yvvxyyz|{|~~}~}~~|||zz{zwy|~~}|}~{{{{z{~}||{z}~}{}}zy{}}||~}~}yxz{|}}~|yy{zxz}~{ywxzywx}~~}|}~}}~}{zyxy{|}|}~}}}}}|z{{z{~~}}}{y{~}}}|{{||z|~yxz|{yz|~|zywvw{~~zxxxxx{|{}~~~~~{{|ywwy{||}|z{~}zxz~~}|{|~~~|z{}}|~~}zyzzz{}}zy{zwvxz}~|{{yvuy{|}~}}~~~~}zyxvxzyxx{~~}}~}zyz|~{{}}~}{{zz}}|}}|zz|~|zzxuvy{zy|{{zwwz{y|~}~~}|{{yvvxzzy{~~|}{yy{}}}}~~}~~|}~~~|{{|{{}}~~}||||{yzzxuuwxxy}~{xx{||}|}}|~}~||yxyzzywx|~}|}~~~~{{{|}~~~}{z}~}{z}|z{|{zzwuvxyxww|~}||{z|}|{}~|}}}~{wxyzyxxzzzz}~|~~}~}z|~~{{~}}|{}~{|}~~~}}zxzzwwxxxxx{||}~}z|}|}}{|}~}{{~}~}zxy{{xvx{{yxy|~~~}{|~}z|~}{}~}}}z{}}{|~~{yxvvxyxuuxz{{{{~~}}~}|}}||}~~}}~zyz{|zy{{yxy{{{|~|}~||}|z{~~~~~||}}|{~~~~zyyxvvwwxwwyyxy{~~~}}}}yy{~~~{}~|zxz|{yvuwyy{~zz{zy{|}}|}~{{}~}{}}~}{zvuxzzyvwzzywxz||~~||}|{{|}~~~~~~|{|{zy{{yxwwwwvz|zzyvvz}}||||~~~{{~~~}}{xwxxyxxyxwuuwyyy|~|z{}~~{|~}|~~}{yz}}{ywwwwy|}~|zzyxy{{{|||}~~||}}}~~{wvwyyvuxzyvuuwxy}}||||||~}}}||~~~~~}}}|{zyywtuxz|~|xvxz{zyz~~~~~}~|{}~}{{}~~zxxxxxy{{yvvxxvw{~|z{}~||~~~~||~~~~~}}|z{}}{xwwwvvxyz~}{xwxxwwy|}}|}~}~~~~}|}~}~~{{||ywx{zyvuuutwz{{~}~~|}}||}}}|}}}|{{|~}|}}|{{{zxwyz{}~~{xy{{zxy|}~}}~|yy{|}|{{ywwyywvvvvttvz|~}||zz|{yy|~}||~}{z}}~~{z{{yyzxxz}}~~~}|{zyxz{zyxz|}}~~}~~~}{zzzz||~~{ywy{{yxwwvvyzz|}}~~zyzzz|~~}|}|{}~~~|zz{zwwzzzz|{{~}z{{{zwwxyyxxz{|~}}|}~~}||||}~~}|}|zyxxxvtuwxz}~||}{|{xy{}~}{|~~~~~{z|}|z{~~|zz{{{}}{{}~}||~}|}|{zz||zxxz|{|~~}~~}{xx|~~}{z{{yxvvuuvwxwy}~~|yxwvxz{zy{~~}|}}|||}}~~}}~|z{}~}}}{yy{~~|}}~~{{||zxwwwwy}~~}|~}|~}|||zz||~}}}||}~~~}}}|zxyzywwyy{}}||}~~|{zwuwz|||}~~~~~|{~~|{{zy{}|{}~|zyz|}~~~}{z||zwuvwvvy|~}}|zy{|~}}~|}~}|z{}zyyxxzzxxz{zz{|}}~}zxyzzzz{~}zz{|}~~|{{}|{{||{zz}}|}~}{|}{|~}{|}}|{yywvy}~~}z{}~}~~~}}~}~~{yyyxvwyyz{{yy{~~~}yvwxxxxz{{{z{{{||{~}}}}|~~|{{zyz|}~~~~~}}{zzywvxy{~{|}~}}~~~}}~}|}~~zz{}}||~}{zyxy|}||}}||{}}}}}{yyyxwvy|~}|{|}}~~}}}}|z{}~~~|}}}zxy|~}}}~}|{zxvvxyyz}~{{||{yz}~}|}}{{|||}~~~}zz|}}~}{yxz{{yy{~~|{{{zyz{||{{zyy{~~}|zyzz{}~~~|||}~}}|{|~}||}~|{{||zz{{{~|||}}|~~~~~~}~}|~}{zy{|{{|}{zyz{z{}~~|ywvxzyxwxzyy|~~|}}}||~zyz{|||}}}}}{{|~}}|{|}|z{|||~||~}{{||}~~~}|z{}~}||}}{z|}~}||~~{z{}~~}||}{ywxyyzz{{z{~~}}|}~~~~|{{{{|}~}~}}~}{{{{zz{{{||{z{~|{||{{{{{{{~}~~|z|~~~~|{{yxy{{{|~~|}}}}{|}{yxyzzz{||~}}}{y{}~~~}~~}|}|yy{|}||}~~}|{{z|~}}~~||~~}||}}~~~~~~~}~~~|{|}{yxy{~~}~~}~~|{zxwwwwvx{}}~~~}}~~}{{||yxxxyz{|||~~}~~~~{yz|}|}~~}||{z{}~~}|}}|{{{{|}}||}}}}~~|zzyy|~~}{}}|ywwxyy{}~~~}~~}||{z{{zy{}~~}~~}|{z|~~}|{{{{{~~~~|zyyzz|~~~~~}{{}~~}zzz{{{|{yyzyxy{~~|{{yxy|~~~~~~~|zzyyzzzyy{}~}{{||{|~~~~}|~}}}}||}~}}}~~|{{|{{{|~}{{zyz}~~~}zxxwwyzz|~~~|yxxvuuwyyxy|}~~~}~~{yy{}~~~~}}|}~}~}{{{{{|~~~~}{{{zxwxyz{|~~}}|zywx{}~~}zxxwwyzzyy|~}}~~~}~~~~|zz{}~}}|zz{}~}~||{{~}|{{zywvuvwxxxy}~~}||}}~~}~~~~}|zxwyyyxxyzzz{{{z|~~}~~~~||}~~~~~||||}~~~~~}|{zyzyxxyzz{{}}{z{|||}}|}~~~~{ywvvwvuuwyyyyz|}~}||{{|}|{|}|{}~~}}~||}}{yxxxyywvvy{|{{|}||}}}~~~}|{xvvxyyxyzzz{{|||~~~~~}||}~}}}}}~~~}}}}}}~~}~~{yxwutuvvvvwyz{}~}|}~|{{{zyxxyzywvwxyz|~~|{||{{||||~~~}}{yy{|||~|yxwvwyyxwxz|{{{}~~||}~}|}~~}zyyxxwwwwwwwwxx{}|{yxwvvxz{{z|~~}~~}|||}~~}~~}|zyzzyxxxxwwxyzz{~~~~}}~|{}~~~}}~~}||||{yy{}}|{z{|}}zxwxwxyz{{|~~~~~~|}~~}}~~|{}~|{|}}}|}~{yxyyxwvxyywwxy{}~}|{{{{{||zyyyyz{|}~~~~~}||{zy{}}{xvvwxywvxyz{zzz{{}~~~~~}|zyyzzyyy|~~~|zzz{{{|}|{{{{{{~~}}~}zyz{{{{{||{|{zz|~~~}|{|~}{{|}{zyyzyz|~{xwusssttuwxyyz|~~~~{{|}}~~~}|{zyxy{|||}~~}{{|}|zxxxxy{}}~~~~~~~~~{zzzyyyyyy{}~}}}}}|}~~}||}}}||}}}}}}||}}zxuuvwwvvxz{|{|}}~~~~}|||}}~~}|{|||{z{}}|||~}|{zyzzyyyyxxxz}~~~|{{zxxwuuwxxxy{~~}}~~}}}}||}}}~|zxwvvuvwwvvwyzz{}~}|zxxxyxxxyzz{{}~~~}|zz{}}||{{{|~~}}|||||{yxxxxyzzzz{~~~~}}|{{{{zyz|}~~~zxvvusqrtuuuvxy|~}}}}|zyxxxxxxwx{||||}~~~~~}|||{yy{|}~~}|{zxyzzyxwxz{{||}}~~||}}{z{}}|{z{|}~}{yxwvuvwwvwxzz{}~}~~~}}||||{zz{|{zz{{{{{zz{~}{{|{zyyyyz{|}~|yxwvvwwvvvwxyy{}~}}~~~~~~}}}|}~~~}{xvwwvuttuux{}~~|{{zzzywvvxxwvvwyz{~~}|||{{|}}|}~}|zyxwvwxzzyyzz{||}~~~~{zzzz{}}~~~|ywvutrrrsttwz}}{zyxvvwxwvwxxyyzzz{}}||}}~~~}{zzzzzzzyzzywwwyz|~~||||{{zzyyy{~{yxxwvuuuuwz|~}{zyyzz{zyyz{{yyyyyz{}~|{zzzzz{}}{ywvwwwwvwwwwvwxy}~}}~}}}}~~~~}{xvvuutuwy{}~|{{{{yxwwwvvvvuux{}~~~|zzz{}}zyxwvwwwxy{|{zyyz{|~~}{zz{|~~~}|zxvtrqrtvwy|~~|zyxyyxvvwxwvvvwxy{|~~~}}~}|zyzzzzz{|{zyxvvx{~~~~}{zzzz{|}~}zxwwvvwxz|~}||{yyz{{zzzzyyyyyxy{|}}~|{zz{}~~~}|{zxwvvxyyxwwwwwx{~~~~~~~~~}}~~~~~~~|zxwwxxxxz{|}}}}}}|{z{{zxvuuvvwxyz|}~~~~}|{{|~~}|{yxxxwvvwyz{{{{zz|~~}}~~~~}}}~~}}~}|zxvuuvwyyzz|}{zzzzyyyxxwvvuuvy|}~~}}}}}}~~||||}}|zyy{|{{{||{zzz{|~~}}|{|}~~~~~~~~}||}~}|{{{{{{||}}~}{zyyzzyz{}|{zyyyyz|}~~}||~}|}}~~}}}}|zxwwwxxyyyz{|||}~}|{|}}}~~~~}zyyyz{|~~}|||zyyz{||}~~||}}}}~~}|{ywvvxyz{|{{{|}~~|{}~~~~}{zzz{zyxxxxwwxz{}~~}}|{|}~~}}~}|}~~}|{{{|~~~~}{zyyyyz{{|~}|{yyy{{zzxxxwwxy{}~~}|{{||}~|zyz|}}}}|{zzz{{|~}}}~~~~~~~~}{zyyzzz{|~~}||}~~}~}|zyyyz{||}}||{{|}~~}|{}~}}}}}}~}{xvvwxz{}~~}~~}||{{{|}~}{ywuuwxxy{~~|{{zzz{|}~}{|}~~~}}}||{z{|||||}|{{|~|{{{}~~{zzyzz{{zyxwwwy}}{{|||{|}~}||||}~~|{zyyyyyz|}{z{zzz{}~|{zyzz{{zz{||||||}~~~}|{z{{}~}}}}}|{yy{}}|{{{{}~}}~~}~|zyyxwwwyz|~~~}}~}|{zyyz{}~~}|{|~~~~~~~~}||}~~}{yxxz|~~}|{|}}|{{|~~|{{{zywvttuwxyz}|zz{{|||zz{|}~~|||}~~}{{|}|||}}|{|~~|{z{}~~}~}{yyzz{{{{zzz{|~|{{{|}}~~~~}|{{zyyyzz{{zxwxxyz{}~~|zz{{|}~}}|}~}zxxz{}~}|}~~~~}|{{|~|{yyz|||{{z{|}~~~~~|{z{|}~}{yxxwvvwxz}~~~}|{{z{|}~~|zyxz}~}{yyy{{|}}}}~~~~}|||}~~~||}~~}}~~~}|||}}}|}~|{zxwwxzzyvuuuuuvwy}}{{{{zyxvvwz|}~}|}~|{{|}~~~|yxxz}~~~~}||||}}}|zyyz{}~~~}||{|}~~|ywvvuvwxyz{{zxwwxy{}~|{{{zz{zzzz|~~{z{}~|}~|zyz|~~}|}~~}}|{{zyxwy{}}|{z{}|z{}~~|{zyxxxyz{}|{ywvuuvx{~~}|{{zzyyyyz|~~}|}~{ywuuwyz{|||}~~|{{{{||||{{|}~~|}~~}}}{{|~}{yxxwvuuwyzyvtttuvy{~~{xwwwwwvvvwy|~~||||||{ywvvxz|~~}}~~|{{|{{{||{{{}~|{}~~|{{}~|zwutuuttuwy{|{yxwy{}~}ywvwxyzzyyz|~~~~~~}}|{yxxy{~~}|{|}~|zzz{{{zyxxxy{|}~~|zwxz}~}|{yyxxwwxyz{||{ywwxyz|}~{zyyzzzyyxxxz|~~}||{yxwvutux{|{{{|~~|zyxxyz{{|}|{{{|~~~~|{{}~~}{zywvuuuuvxyywwwxyz|}{wuuvwvvvvwwxz}~{xwxxyyxwvvwy|~~~~~}||{|}~~}|{zyz{~~}{zz||{{{zyvtstuuttvy|}}|||~}zvuvwxyz{zyy{}~|{zz{|||{yxxy{|}|{{}~}|{yxxz||zxwwxz|~}}|{zxwx{~~|{zz{|{zxwxxxxxxy{~~|{{{{|}~~{yxxyz{{yxvvx{~}zyxyyyxxwvutux{|||~|zzyyxwxz|}}}|{{}~~}}~~|zz}~}}~~}{yxwvutttuwz|}|{{{{}~~{wutuvwwwwvvwz~}{xutuwxxxxxxwwy|~~~}|{{{|}~~~}||||}~|zyz|~}{yyzzzxvutttttuw{~~~~}|}~}{wttvyzzzzzz{~~}|{zyxxyz{||{yxxy{~~}}|{yyz{||{z{{|}~~}}||{zxwxz}|zyxxxyzzywvvvwxy{~}}~~~~~~}}}}~}zxxyz{||{{{|~}|{zyyz{{zywutvz}~~~|zyyywvvwy{}}}}~|zzz{{zzz{||{yy{~|{z{{}~~|zyxxyzz{}|zzyyz|}|yxwwxxxxxxxy|~}}{wustuvvvvvuuwz~~}||}}{zzzzzz{|~~|zz{}~{{}|{{|||zwutstuvwx{~~~~||{{|{yvsstvxyz|}~~|zyyyywuuvxz{|||||}~}}~~}{{{}~}||}~~~~~~}}|{ywvy}|ywvvwxyyxvvwy{}}~~~~~~~}~~~|{zz{{|~~{yxz|~~~~~~~~}|{zyyz{{{{zyxz}~{zyxwvvx{~}zxxxyyzz{{|||{z||{||{{{||~}|{{||}|||~~{yyz{|}}}~zwvwxz{||{zz}~}|{zxutstuvxz{{z{}}|{zzz{~}{{{{{|}~}{zyz}~~~|zzz{{zyxwwxxyyz|~~|zzz{{{yvttwz|~~{z{{{zyxxyzywuvxz|}||||||}~~~~~~}|}~||}~~}|||}|{z|~|zwvuvxz|||{{}~~}}}|{yxyz|}|{zzz{{}~{{|~{z{}~~~}~~|zxxz|~~}{z{~~}~}{zzz{{{}}zwwxyzz{{|~}{z{}~|{{{|~}}}}}}||~|zyyyyyyy{~~}{zz|}~~}}|||~~}~}|||{ywtstwz|~~||}|{{zyyz|}|}~|{{}~~}{zz{}}{zz{}~~|zyyz{{{{}~}}}}}|||~~|{yyyz{||zyz}}zxxzzyyyyzzzywwy}}|||||}}~~}|}~~}~}||}~~~}|~}}~~{ywwxz}}}~|zyxxwvvvxz|}|{zz{|~~|zyz|~~~~~}|{|}~|zz}~}}~~~~~~}~~}~}zwvwy{}~~~~~|{{{~~}}~~}}}}|{|}~|yvuuvwxxxz}~~~||}~~~~}}}}|{}~}||{{{zywwy{}}}}{{{zyyyz~}zxy{}~}{z{}}}~~~}{zyz{|||}~~{zz{{{zzz|~~|{zz{}~||~|xvwyzzyyz{}~}||}~}|{{|~~}||}}}}~~}|{|}~||~|{}~~~|}~~}||}~~~|ywvvvuttuxz}}}|}}~~~~~~}}~~~~|zyz|~}}~}zyz}}}}}~~~}}|{}~{yxy|}|||}~~~}|{}}}}}}{yxxyyxvtuuvwwxy|~|||}}~~~|||~}{{}}}}~~|zyz|~~}~~~|zyyyz|~}|{{|}}|yxx{}~}}~~}}~}{zz{{{{zyz|}|{zyyyyyyyz}~}}~~}~~~|xuvxz|}~~}~}|}~|zyyy{}~}}}}~~|zyyy{~~||~}}{zz{|{ywuuuttttvz}~}{yxyz||}}~}zyz}}}~}{ywxz|}}||||}~~~~~~|{|~}|}|zz{|}~~~}|{zywustvxywutvwwwwy{|{{||}|{yxyz|~~{{|~~}{{{}~}{z{~~||}}~|{{}~~|zzzzz{||zyy{}~~}}~~|{zxwxxwvwy{}|{zyxxxyyz~~~~~|{{|~}zwwy}~~zwwy|}|zxwxz|}|||{zzyxz|~~}~}}~~~}|{zxwwxyyzywvutstuw{~~~~|{{zyxwvvy{}~~~~||~~|}~~{ywutvx{|}|{{|~~~~~~}}}~~{{~|yy{}|yxwvvtrrsvyzxvvwwxyz}~zxwxyyyxxxxyz}~~}|||}~~|zxwx{~~}}}|}~~}|{zzzzz{||{|}|{||}~~~~}~~~~|zywuvwwuuvx|~~{yxxy{{|~}zyz{}~}|zxx{}~~}{{}}|{xuuvy{|{xxxy|~}{yxz{|{zyz{|}~}zz}~~}|{zyxvvvvwy{zxwuuvwy|~|zyxxyyyxvttvx{~}|{{{yxvvuuuuw{~~|{|}}|}|||}~~~||~~}}~|{{~|yxwvusqqrsvyzyyzzz{|~zvttuwxxwxyxxy|~~~|zxy{{zyyxxxz|~~}}}||~|{}}}|}|{zyyz|~}{yxy{}~{zzzzzxuuvvvuuvy}~|{|}}|}~ywwxz|||{ywxz{|~}~|{{zz{yvttvxz{zyyz}~}}|{xvwx{}}|z{}}~~}~~|{{{|~}|}}~}{yxvuvvuux|}}{yxyy{}~{ywuuwxyxvtstw{~{zxwwvuuuuuuuuy}~}}~}{{||{z{}~}~}{z}}}}|zyxwuusqqrswz|~}|}}~yvsrtvwwxzyxwy|~~}|{xvwyyxxxxyyyz|}~}{z{|{|}~}||{|~}}~~|zxvxz}~{xxxyzzxvwwwuuvw{}}~}|~~~~zwuvy{||zxxyz{~~~|zyxwxyxvuuuvyz{|}~}}|{zxvwwz}~}~}|||}~~|||{z{~~|{{{}~}{ywuvwvvx}}zzz{}|zwttuxxxxutvw||zyxvutstuuvwvtw|~~|z|}|zz{}~}|yz}~{{|zyzzywvsrrsvy|~~~~~}}}}~}zwustvwwx{{zz{}}|zz{{xvvwwwxxxzzzz|~}{z{{z|~~~}~||~~}{wvwz~|xwwwz|{xxyxwwxy|}{|~~}~~||~|ywvxz{||{{|}|}~~}}}~}zyvuwyxvvwwxz{}~}|{{xvwwz}|zyzz{}~~}~~|z{~}~}zz{|~|ywwzyxy|}{zz|~~~~}}{vstwxyzyxyy|~}zyyywutrstuwyxwz~~{{}~}|}}|z{|||~~}}~~|z{~|{|{yy{|zywuuvwz|}~~~~||}|||{zyvuvwwx}~}}}~}}}zzyyz{zwwxwvvxx{}}~}}~~~~{{||{}|z|~}||~~~~zxyz~|{~~zxxwy||{{}|{z{{}}{{}}}}|{||}~~|{}~|{xy{|~~}}~~||~|{~~}}||~~}{xuuwxwxz|{{{{}~}~}|yxyy{~}|zwwyz|~~~}~}|{|~}{{{~|{||zy{}~}{{}}{{}~~~}}~|xvwyz||zzzz}~}||}~{yyzzxwtrstvz||{}~}}zyz{~~}~}||}}|}|{{}~~}}}}zyz}~~~zxxxz|~}~~}~~|{|}{|}|||zzzyyzzy|~~~}{yzyxyxy{|{yyzxvwy{~}}~|~~|}~}~~~|zz}}||~}||}}zz}~zzxy|~}|}}|yyzyyyxxzz|~~||~~~~}~~~~~||}~zy{||{||}~|ywwyzyz}~}}|z|}{}~~}{}}~~~~}|}~}|{wwxy|||{z{|}~}}}~}~|z{}~~~~{zzzxwz||{|~}}~{{||}}|{|{|~~|zyz}|zyz{zzxuuvx|~~}~~~~||zxyz}|yyz|~}}~~|{|}~~~|~}|}~{{z{}~}}}{||{{zxy|}||}~}}}{|~~~{}~~}}{yxvvxxy{}|z|}|zz|}}|~}|~~|}~|}}|z{}||~~~{y{}~}|||~~}~|wvwwwwvuwy{~~|~~|{|~~~}~~zyz{zz||}}{{|{z{~~~{y{||{}~}~~}}|zz}~}}zwxy{~}~{z{z{}}|~~zz|}}~}{|zyyywvxzzyz|~}}}{{|{|~~}~~|ywy|{yz|~}~|yxxy|~}~~}|yyzz}~{yxx|~~}~||~~}}~~~~~}{{|~}{}~~}{{{zzywwy{{{}~~}~~~~}|}}zywtvwx|~~~~||}~|}~~|{}}}~}~||{~~|}~~}||z{}}~~~|{|~|xvwwvvutuwy}}}}{{|{{|zz{{}~|{|{yy{|~~~}z{}~~}yyyz~~~|}~~~|yy{~~~~{z{|~||{yz{{}~}|}zz|{zzwwyxyyxvuxyyxy{~~|zzzz||{yz}~~~~~zwx{{{}|{zz|~~}|{||~}~~{z|}~|}|{zxz}~}|}}~~~~||||zx{{{z{|{|{zzxvwyzz|~}}}}~}||{{|~}{|}~|{wuvwz~~}~|{{yx{~~~}{z{|~~}}~~}~~}|}}|}~||~}~~||zxxz|zxwxwvvttuwz}|}}yxxxxxxwxyz|~}|}|zz|~~zz|~~~|xvuuy|}|~~~~{y{~~~~~}}{yzz{~~zxxxwwutvwxzzxvxyyxy|~}yxxwxyywwy{{{}~}zz}}}~~}{z|}}|}~{xxx{~}~~~~||}}~}|{z{}~}~~}~~~}~}~~|zzywvxyxxyz{}}{{ywx{||~~|zzyz|{zzxxz}}{|~~}{xxz|~}~{yyxuux{|}}zz{{~~||||}~}|~~~|{}}|||{zywvwzyxyyywwvuwx{|yzyvuvwvvvtvxy|~~~}~~{z{|{zxutssvyz{~~~~~}}~~~}zz{}~ywwwvvtrsuwz{zxy{{{{}|wtttuwwuuxyyy{}~~~}~~|{||zyz{zxwvvx|~~}~~~}~||}~~~~|}~~}||{{|{{|}}|{yyywuvxwvwyz}~}|{z|~}}~zwwwwzzyyxwx{|{|~}|{|~~~}|{ywwwustwz|}{z{|~~~~}|{yz{{}~|~~~~}{y{~~~~|||{zyvuvxxxy{|{{yxyz|{xwwuttvvvuttvw{~|yxxxwvssrrsvwx|~}{}~~}~~}|{~}}~{xwwvuusrstw{}||~~}~~~{usrrtvvuvxxxxz}~~}|{zywvwyxwvuuwz|~~~}||~}}~~}||{yyyxz|}|zzyyzyvuwxwvwx{~}}~}yvuuwyzzywvx{{|~~}}~|||zzywvvwtstvxz|||}~}}}|{zxxz{}~}}||}}|yy{|}~~}|{||{zyvvwxxy|~|z{{}{xwvsstvvvvttvy}}ywwvvutrrrrtuvw{~|z|}}|}~~}{z|}}~{yxxxwvtrsuw|~~~|wtrqsuvuvxyyy{~~}}{zyyxwtuwwvvvvwyz}~|{}~}}|{||zzxwy{}~{yyxyzzxvxyxwxz}~}~~}~}zwuuvyzzzyxz|}}~}~}|{{{yywvuwwustwy{~~}}||{zxxy{~~~~|{{{{}}{z{|||~~}|||}~}|zxwy{zz}||{|~|zywtstvwxxvvxz~~~~{xwwvuusrrrsuvwx|~{{}~}|}~|}~~~~}{{}}}}|zzzzyxwuvwy|}}~~}|yvtssuvvwz||{}~~}}~~~|zyxxxxvtuwwvvvwy{|~}||~~}}~}||}}|zxxz}~|yyyy{|zyz{{z{|}~}}~}|||}}||~}zxwwy{|||{|}~}~~~~|{|~~}|{z{|zyxvuvwvuvxz|~~~~}|{yyz|~}}||{zyyz{}~}|}~}}~|{{}~|||~~|{|}||}~|||}~}}~|{zvuuxyzzyxyz|}||}{ywwwvutrrstwyyy|~}|}~~}~~~}}}}~}{{}}}}}~}~~~~~~~|{{}}||yxyy{~}||}}}|yxwuvwxxz}~}}}}{{zzzywwwwxywvvwwvwxy|~~}~~||}~~|}~~}{{}~||}|zyy{}~}}~}}}}{{|||{zz{{|}}|}~|{zz}~~~}~~}~~}|{yxy||{zzz|}|{ywwxzyyz{||}}~~~||}~}|||{{zxxy{}~~}zyyz|}~~~~~~~}}~~|{{{{zz|~~}~~~~~~}zyy{{||zzz{|~~}||{zz|{yxyyxwuttvwz{{{}~~~}}{z|}|zzz{||zz|}||}~~~}||~~}~}{z{|}}|}~}}}||~~}||}}}}{{zzz{{z{~~~~|zyyxxwvvwwyzyxxyzyy{|~}|}}}}~~}||~~~~~}~~~}{z{~}|||~~{yyyyyyxxyz|}}|}~~~~|}}|{{{zxvxyzzz{{}~~}|zz{|{{|}}}}|}~~~~~|{{||{{yxy{}~~~~|zyxxz|}}~}}~}{yyyywwy{{{|}~}|}}}|{zzz|}~~~~}~}{zyyz{{yyz{{zywwxy{}}|~~~~}}{z|}}zyxy{|{{|}}}}~||~~}|||}}|{|}~~~~|{{||{{zz|~}|}~~~~~~~}||~~~}}~~{zyxwwvuvwxz{{{|}}|||}~~~~~}}~}|||~}}~~}}}|zz|~~{xwxxxwvvxy{}~~~~~|{|}}}|zzzzzzywwxyyz{|~~}}~|||}~}}{z{}~}}|z{|}}|{z{|~}}}|{yxxyz{|}}|}}}}|zyyxxxwvwyzz{}~~}|zyyyz{{zz}~~}}|}~~|{yxy{|{|}~~|zyyz|}}|}~~~~~~|{|~{yxxz{|||~~~~|{|}}}||}~~}||||{{}~~}|{{||{{zyz|}}~}}~~}}|||~~~}}|}~|zzzyxwuuvwz|~~~~}}~~}~~~}~}||}~~~~}||~~}}}~|{|~~~}|}}|zxwxwwwvvxz}~}}}{yxxyyyyxxxyz{yxxyyyz|~}||}}}|ywwxz}~~~~}{{|~~}}~~}}||{zyyyz|}~~{zzzzyxwxxxxxwwxzzz|~}{yxwwwxwvwz{{{{|}}|zyz}~}~~|{zz{||{|}|{z{|~|zzyz|}}~}}}}}|{|}}}}}~}|{{zyxz{|||{{|}|{{zz|~~~~~}{zzz||||{{{}}{z{{zywvvxz}~}}}{{|{{|~~}||}~~}|}}|}}|||~~}|}~}{zyyxwvuuwy|~}}|yyyyyyxwxxyzzyxyyyyz{}}|}~~}|yxxxz}~~~~|z{|}~~|{|}~~}|{zxwxy{|}}~|||{{yxxyyzywvwyyyz|~}zxxxyyyxxz{{{{|}~~~|{yz{|{}~~||{|}}}|}~}|{{|~}}~|zyyz|||~~}~~~~|{|||||}~~}}|{zz||||||}}}|{zz{|}}}|{{{|}|{zzz|{zzz{{zxvvwy|~}||{zz{}~~}|z{|}~~~~~~}}~}|}~}}}~~}{{||{zzzyyxwvwy{~~||{yxxxxwvuuvwyz{z{||{{|}}|}}|{xvvvwy{{|~~}|}~}}~~}}}|{yxyz|~~~~|{{zywvvwxzzyyy{{{|}}zwvvwwwvvxxyyyz|~}|}}|}~}||||{{|{zyyy{~~~}|||}}~~~~}}}|{{{|}}}|}~~}}~~}||{yyzzzyz{|~~}||}~~~|zyyz{{{zxxyzzz{|}}|{yxyz}~}{zzzxwxz{||{z{|~~}|}}~~~}~~}|{|~~~~~~~~~}{zyz{z{{||{zyxy{}}{zywwwwvutstvxz|||~}}}~}|{{zywuutuwxxz|~~~~}~~}}~~~}|zz{}~}{{zywutuwy{|||}~~}~|xvuuvvvuuvwxxz{~~}~}||{{zyyzzyxxxy{}~~~~~~~}}|{{yxxy{|~~}~~}|||{yxxyyyz{}~{zxxxyzyyxxyz{{|~~{zz{}~~~~}|{zyywuvwy{}|||}~}{zz{|~~}}}}}|zz{{{|}~}|zyz{|||}}{z{|~}{zxvuuuvvutuvy{}~~{zzyyxvtsssuvxz}~~~~}}~}{{{}~~~}||}~}{zyxwvuvwy|~}ywutttttvwyyyz}}|{zzzyxyyxwvvwy{}}~~}{zyxwwyz|~~}|{{zyxxzzz{{}|zwvwyzzzzyz{|}~~}}}~~~}~~~}{ywwwvvwxz|}}~~}{zyyz|~~}||}}|{yxyz{|}~~~~~~~}|{{|||}}|}~~{ywuuvwxwvuvxz|~~~}{{zyxvtrrstvwxy|~|{|}}~~~~}}~~}|{|}~~~}}~|{{{{zywwy{}}yvtstuvvwxzz{|}}}~}{zzz{{ywwwwvvwxz|}~~}~|{{{zzyxxy|~{zyz{{{{||||}~~~~}{yxxy{||{{|}}}|}}}|}}}~~~~~~~~~|zywwxyxwxy{}~~~~~}||{zz{|~~}}}~~}|zzyzzzz{||||~|{{}}}}~~}~~~~|{{zxxxxyzyxxxy{}~~|zxwvuttuuvwxy{~~}}||~~~}||||||}~|||~}|}~~~~~~~}||{{{{|}}|{yxwwwwwxz|}|{|}~}|{{{{{zzzyxwvvxyyyyy|~~~~}|}}||}~}|zyz{}~~~|zz{|~~}|~~~~~}}~~}||}~~}|{{|~~||}}}~}|}|{yy{}~~~}{{||{{{zzzzz{|||||}~{{}~}}}~}{ywwxz|}}||~}zyz{|~~}~~~}}~~}|}|zz{|{zyxxy|~~~~~|zxxyyyywuuwy{{z{}~{z{|~|zyz{|}|{zz{{|~~~~~}}~~~~}{zz|||}}~~}{zyz{{zz{||}|zz{{{zywvwxxyyz{zz{|}~~~~~~~~~~}|{{}~~~}zzz|~}{{|}~~~~}}~~}||}~~}||{{|||~~~}}~~|zyzzzz{|{zyz{}}{|}~|{z{{|~~~~}~~}|zyyxxxz||}~~~}||}|{zzyyyz|~}|{{|}~}}~|{{zz{||}~~}}~~}}}{yz{{{|}|{zyyyyyyzz{}|{zz|~}|{{{zyxxy{}~~~~~~|{{|~~}{z{~~}~~~}~}}~}~~}|{zz|~~}|{zxvuuvx{||z{}~~|{|}|{|}~~}{|}}}~~~~}{{|}{yyz|~}~~~~~~}{xxyz||{|~}{yz{|||zwwxyy{}~}|{{{~~}|}~}}{yyyz{|{zyz}~}}~~}|zwuvwy{}}{{|}}}||{{{|}}}~}|{zyz{}|||}~|zzyz|~~}|}~{xxxy{~~~~~}}}}}}}}}}zwvwxyz|~}|~~|{{}}}|}|zyyz{~~}}~}|||~~}}}~}||~~~~~{xxwwvwyzzz|~}}||~~}|||||~~~{zz{}~}~~~}}~~~{{{{{|}~~~~{yyyxy{}~~}}}}|zyz{|{{{{yxwwwy|}}|}~~~~{zz||||~~}|{z{}}}{{|}}}}~~~|yxwvvy{||{~~}|}~~~~~|yyyz|~~zxxyzyxxy|~~~|{|}|}}}}|}~~}|{z|}}~~}{{}~}}~}|~}zyxxy|||z|~|~}|{|}}}}{xxzz{|~~~||}}{{}~~~~~~}||}}{{|~}{zyyz{{ywwz}~}}~~~~}||~|{{|}}}~~~}|~|{{}~|{|~~}}||~~|{~~|{{{|}}{z|~~~zxxz||{{yvwxxy{|||z|~~|{{||yvvx|~~~}{||zz{|~~~{zzzyzzzyy{~}{|~~}}{zz{{{}~}}|{zwuuwzzz{|{yz{{~~|~}}~~~}}|zzz|}{zz{~~}~~~~||}}|z|~~~~}zz{}~}|}{yzzyz}~~}|~}{z{}}~~}~~~}}~~|yyz}~~~{z{|{|}~~{z{|{zz{}}~}}}{z|~~}}}}|{}~~~}}~~~~||}|yyy{}~|}~~}~~}~~~}~~}}}{|~~~{z{|{z{{xvvvvx{}}{z}~|}~|{|}{xwwxyy{|{z|~~~||~}}|{{~~|}~}}|{}~~}yy{}~}||}~~~}zxyyz}}~{xvwxxyzxvwyyz|}~}}~|zyz|{xwx{|||}}}~~}~~|}}|}~~|{|}||}{xxyyz}~~}|~}{{}~}}}|z{|}~|{{||{|}|z{{z{}~|~|yyyy}~}~~}||~~}}}{xy{|}~}}~|z{}}~~~|yyz||{{|{z{||~~}}}~}~}}~||}~~~}|{wvwwwxyz{z{~}}}|}{xxz{zyxwuuxyz}}||~~~~~~}~~~~}||~~}}~{{|}}}~}{z{||~}zz{yy|~~~~}}}|yxxvuvvvx|}~||~~~|{||ywwyzz{|{yxy{|~}~~}~~~~}}{xxxwxz}~~|}~~~~~|yzzz|~}}}~}|yxy{{|~~}}~~}{z|}}~~}}~}{{}~~|yyyyx{~~~~{{{z{~}|~~|{{||{||ywxyz{~~||}~~~~~~|}}zxxwwz{|zwy|~}}}~~}|{||{{{xttuuvz}~~~~}~|{}~~|zy{~}{z|}|}|{}}~|{{z|~}||{|~~~|z}}xuuuuwy{{{z}|||{xxy{{zzzxwxxxz~~~}}~~}zyzyyz{{zz}~~|z{{{{~~~||~|zzyy{~}|}}~~~}}|zz{}~}||}~|zzyy{|}|{|~}|}|{|~~~~{z{}~}||zxxyxx{}~}|~|{|}|z{}~~~~}}|zz{||zy{||{zz|}}}}~~}~~}|ywwwuvxyzyy}~~}{|||~}{z||yxy{}~~|{|}}}~~}~~|{}}}~~}|~~~}{zxwxz{zxy}}|}~||~}{zz{{{z{zwvwwxz|}|{~~~}|}}}~~|{}|||{z|~}{yy|~~~}~}}~}|{{{~~}~}}~~||}||}~~}|~}~~}|zyz|{xxz}~~}{||{{}~~|z|~~~~~~~}~}{~|{{}}}}}zwxxxz|}}{{~~||~|yxy|}~~}}|}~}~{z|~~|zyyy{~~~}|}{yyyxy{zywwz}}}|~~|{||}~}ywxz{{{|{zz{{|~~|{{|~|{{|~}|}}~}}~}}~}|yx{}}|{|}}}|yyz|||{zxvxxxy{||{z}~||~~}{{}~||||}~|}~~|zz{|}~~~}}~|}~}}~~~}}~~~~|{|||~~}~~~~~}|}|ywxy{{|}}{z{{|~||}{|}}~~}}~}}zxyyxxz|}|{}}}~|zz{{{z{{zz{|}~|{|}~~}{{{z{}}~~}}|{|~}{{|{z|}}{yy{{{zz{{|}{{{{}~|{{{zz{zxwxyz|~~~|{|}|zyy|~~~~}|}~}~~~~|}~~}{z{{|}}|}|{|{xvvwwy{||zz}~|}~}~~{{}~~}}~~~~}{|}||~}~}{zzz|~~}zz{|}~}~~{yz{{}}|}|yxy{{zzzxxyzz|}}||}~~}}~}yxyyyz{{zy{~~}~~}}{{|}||{xvvxy{}|{|~~~}|}~~~}||~~}}|{z{}~}||}~~}{{{{|~~|{}~|yxyy{}~||||}~~}{zwvwwwy|~~|}~~}{yy{|||}|zyzz|~~~||||}}}~~~}{xwwwwxz{{z{}~}}}}~{{{{|~~~}zzz{|}~~~}}~~~~~}}~}}~{z{{{|~~~{{{zz|~~~}~~}{{{||{{{ywvvwy{}~}}~~~}||}~~~~|{zyz{}|{y{|}}}}}}~~~}}|zwwvvwz|}|}~~~~~{zz||~}zyy{|zyy|~~{{{|{|~~~}}~}|{zz}~~~}}~}}~|}~zxwvwy{{{z|}}~|zz{||||zxxyyz|~}}~~~~}}}~}}}|zyxxyz{zxx{}~~||}}~|{{{|}}}|||{{|~~~~~|z{|~}||~~|||{{}~~|{~|||{|}~}||}zz{}}||zwvwwwxz{|{{~~|||}|zy{}~~}}~~}{z|~~|{z{|}~~~{yyxxxyzyyy|~~~{z{{{|}|||zxwxz{||}|zz{{|~}|}~}~}}~~~~}|{{}~}||~~~~}}{zz{{zyz|~~}~~~|{{}}|||zxxxxy{}}||}~||}~~~~}}~}{{|~}{{{{{|||zyz{{{{{||~}}~}||{}~~}||}}~}|{{|{yyz|~}{|}}}~}|}~~~}~}|}~|||}|||{ywwwwx{|{yz|~~}~~}}~}{yyy{|}~~||}~~~~~}}~~}|{zz|}}~~|{zzz{|{yxy{}}||}}{z{{|~~~|yxxyzzzzxxyzz{}~~~~}~}{z{|}{yz|}~~~~~~~~}zz|}}|{|}}|{{}}}|{xwwxxz{||{|~~||}~~}}~~|{{|}}~~~|{{{|}~}{|}~}zyyz{}~~~~~{{|}}~~}}{{||}~}zxyz{{|||zz{{|~~~}}}~~~}~~~}|zxwxxxyzzzy{}~}}||}~~{zz{{{zzxxyz{}}}~~~~}}|}~~~~}~}}}~}|||||}~}|{|||{zz{{}}{{{{|}~~~|||{yyxwvvwxz|}}}}~~~~||||zyyz{|}}|zz|}~~}||||~~~~}}|zxwwwxz{|{z|~~}~~~~|zz{|~~}}~~~}|zyz{{}~~}}~~}|{zz|~~~~|}~}{zzz{}~~|{z{{}~}{z{{{{{ywwyz{}~~~~~~}~~{yxxxyz{zxx{}}}|||}~}|||||{yvuuvxz}~~~~}|}}~~}|{|}~}||}~}{{{{|~}|~~{zzz{~||||}~~}|}~|yvuuvwxz{{{|~~|zyyz{{{{yxxyz{~~}}}~~|yxxxxyzzzy{}~~}}}~~|zzz{{}~}~~|zyyz{~}}~~}}}~~}}}||}~~}}}~|zzzz{}}}}~}||{{|}~~~~|||}}|{{ywwwwxz|}}}}}~~}~~~~|{zzz{{{zy{{|{{{||}~~}{ywwvvwyzzz|~|{{{}~|{{||zz{|~~}{{{||}~~}}~~}}||}~}}~~~~}}}zxwvwxyyyy{}~~~~}~}|{{{{{{zxwwxxz{}}}~~~~}~}{zyyz{{zxxz|}}||}~}{zz{|~~~}~~~~|{{{{|}}}}~~~~~|{z{||{{|}~}|{{{{|}}}{|~~}}}~~}|}~}|}}}}|zxwwwxyzzzz|~~}~}{z{}~~~~~~~~~}{z{|}|{zzz{}~~|zxxxxyyyxwy|}~~}{{{||~~~~{yxy{||||{zz{{|~~}~~~~~}|||}~}}}~~~~~}}|{{{zyxxz|||||}}}{{|}}|{zxwwwwxz{{z{}~~~}}~|{{{||||zyz{{zzzz{}}|||||}}||}}{{{{{}}|{z|~~}~~~~~|{{{|}~~}{yyzz{||{zz|}}|||}~~~~~}||}}}}}~~}{yxwvvwxxwvwy{{{{|~~~~}}}}|zzz{|~~~~~~~~~}{{{|{{zzzz{}|zzyxxyywvuwxxxyz{}}{zzz{||{z{~~}}}||{ywvwxxzzzzz|}~}||}~}}~}}~~~~~}}}~}||}}|zxwyyyyxyz|~~~}zwvuuvwwwvvxz{{{|}}{||}~~}{zzzz{||{yy{{{zyxxz|~|{{{|}}|{z{}~~}}~}zzzz{|||{{|}}}|}}~}||}}||zyxxxxy{|{zz{}}||{{|~~~~}}}||}~|yxwwwxxwvvwyzzyyz{|}{ywwxyz{}}}}~~}||}~~~|{{|}~|zzzz{{{yxxyzyxwxxz|~||{{{|||{|}~~~~~}|ywvuuvwyyyxz|~~~}}}~~}}~~~~}{yyyz|~~~~~}|}}}{zyyz{}}zxwvvvwxwwvxyzzzzz{~|zzzz|}~~}~}zxxyz{||{z{|}}|{{{{|~|zzzz{|}}||}~~}}}~}{zzz{||{{{}~}|}~~~}|ywuuvwxyzyy{}~~}|||}~}}~~~~|zyxxyzyxwwyzzzyyz{}~|ywvvwxzzzz|~}{zz{|}~~~~~~~~~~|zyyyz{{{{zz|||{zyyz|}}|}~~}{{|~~}}~{xvuuvwxwwwy{||||}~~~~~~}{ywwwxyz||}~~~~~~~~|||}|yxwwxyxwvvxzzyyyz{}}{zzz{||||}}zyxxxy{{{z{}~~}|||}~~~{zyzz|||{{|}}}}}||||}|{z{|~~~}}~~|yvuuuvwxxwxz|}}}}}}~}||}~~|{{{{{zyxyz{zyxxy{}|zxxwwxxxwwy{}}~~~}{zyz{|}}||~~||}~}zxwxyz{{zyz|~}|{{{|}~~~}|}~~}}}~}{zyxxxxvvwyzzzyz{~~|ywvvvxyyyxy|~}}}~~~~|zzzzzzyxwxyzyyxxy{~}|{{|}}|z{|~~|zyyz{{zyyz|~~}|||~~~}}}|zyyz{|||{z{}~~}}}}~~~}~}|{|}~~}||~~{xwvvwxxwuvxz{zzz{}~}{{|}~~~~~~~~~}{zz|}|zyxxz}~{yyyz{zywvxz{zzz{}|{{{|}}|{{}}|}~~{ywwwxy{{{zz|}}||{{|}~~}~~~~~}||~~}}}}}{xwxyzyxxxy{~~{xwvwwyyywwy{||||}}||}~~~}}~}{zz{|}}{yyz{{yxwwx{~}}}}~~~}{{}~~}||}~}{{{{|}}{yy{}}||{{|}||}~~}zxxxz{}}{z{}~~|{{|}~}~~}|{|~}zxxxxyzyxvwyzyxxxxz~}zyzz|~}}~}||}~}~~|zzz{}~~~}~~|z{{{|}|zyyz{zxxxxz~~}||}~~~|{|~~}||}~}zwvvvxz{zyy{}~}{zz{}~||}~|zz|}~~}}{|}|zyxwxz~{yxwwyzzywwy{{zyyy{|zz{|~~~~|zxyy{|}}|{{}~}{yxxx{~~~~~}|{|}~|}~}|{|}}{{{|~~~|{|~~}{zz{}}}~}ywvwxy{||zz|~}|{{{}~~~~|||~|zyyy{||zwxz{{ywwwx{~}ywwxy{}}||}|{{|}}|{|~}~}}~|zyzz|~}{z|}}|yxwwz}~}}}~||~~|{||}xvvvvxzzywy{}}|{{{{~}||~~{xwxxz}~~~~{zyyz~~~{yxxyz{{ywxz{{yxxxy||zzz{}}|~{xwwxz|}}{z|~|{zz{~~}|~}}~}zzz{}}~~}|||}}|}~}|}}|{{|~zwvuvwz{{yy{~~|{{{}|||}~~~{zzz{}}{yy{|{yxxwxz~~~{yxwxz{|zy{~~}}~|zz{|~}~}}~||}{xxyy{}~}{{~}{zzyz}}}~~}}}|||}zxwwyzzxvwz||zyyz{~~~{xvvwxz||{{~~~~~~}||}|}~~~{zyyz||{xwy{|zyxxxz~}{{{}~}{|~|yxxxz{|{zz}}|{{{}~|{}}{|~~{zzzz|~}{|}}|}~~~~||~|||}}ywwvwyzzxwx{}|{{{{|~}|||}~~}|||~~{y{}~|zxxxy|}||{zyyz{|{xwz|}}{{||~}|{{|~}|~|{|{yxxxy|}}{z|~|{{{|}|}}~~}||}~~}|{{||zxwy{{zxxxxz~~~~zwwvvxz{zxx{~~~}}}}~}~}||~}{{{{}~~|yy|}|zxxxxz~}}~}}~}{|~~}}}~}{zzz|}}zxy|~~|{{{|~}~~{z{}}yxxyz}~~|z|}|}}~}}|||}}zzyxy{{zwvx{{zxxyy|~~}{{{|~}~}}~~||~~|zzyy{~~||~|{}}{{z{}~~{xy{|{zyyy{~}|}|{}~}~~}~~zwwwxy|}|zy|~~}{{{{}}|}}{{~~}~~~}}|~~}yz||{ywwwx{~~yxxwxz{zxwy{|{zz{|~|}}}~~||~|{{{{}~{{}~}{yxxxz~~||~~~~~~~|}|{||}}||{|~~{z|}}|zz{{|~{{|~~}yxxxxz}~}z{~}|{{|~||||~{zzz{|}|ywy{{zwwwwy}~|zzzz|}|~|||}~~~{{zz}~||~|{|~|{{{|~|z{}}|yxxxx|~~}|~}|||~~zxwwwx{|{yx{~~}{zzz|~}{{}~{zz{|~~~~zxxxwz~~}~~{zyxy{||ywx{|{yxxxz~|{{{}~}~{yyyz|~~|{~}{zzyy}~|{}~||~|}}}~~~|||{}~}||~~{{~|zzzz}~~|xvwvwz|}{yz~}{{{{~~~~~~}}|{{z{}~~{xz||{ywwww{~~zyyxz}~~|{}~}~|{|{}~~}||}{|~{{||yzzz{~}{}}{yyxx|~~~~}}|||}}yxwwxz{{xwz|}|zz{z{}|}~zxxxxz}~~}{{yz~~|{}~~}zzyy{}}{xxz||zxwxxy~||{{}|}zyyxxz}~}{{~~|{{{}}{{}|{|~|{{{{~}~}}}|~~~|}}{{{{}~|xwwvvy{{ywy|~}|{{{{}}}|~}}|{}~{z|~~{yxxxy|~~~||}}{{zyz|}|yz}~|}}}~||{{}|}~~}~|{{~}yxxxy|~~|z||{{{z|}{|~~}}}||}|zyy{|{xvy{|zxxyy{~~~~zxxwwz|}|z{~~||~}|~}||z{}~~{xz||{xwxww{~}}~~}|{|~~{y|~||}}{yyxwy{|zxy|~~|z{{{}~{{|~|{{~~{{{{|}~~~}{zxxy{{zwwz||zyyzz|}}~||~{zzyz|~}{}~}~~}~||||}~||{z{}~}zxz|}{xwwww{~~~~}{z{~~}}}}||}~{z}~}}~~~~~{zzyy|~~|z|~|{{{z}~{yy|~|yy{~||||}}{}}zyyyy}|{|~~|{zyz{{yvtwyywuuvvw|~~}~~{zzyy{}~|z{~~~~}~}z|~~|ywxwwz}}~~}{yz}~||}||{z{|}|ywy{{zxwxxy}~~~{xxwvwyzyvuwz{yxxxxy~}|||zz|~|zzz{}~~~}|{}~~{xx{|{ywxxx{}}}{|~~|yxxwxz||zxz}~~|{{{{~~}~~{z{}}yyyyz}~~{xz|}{ywwwwy|}}~}{yy|~}zz{}}}}}~|}~|||}}~{zzz{}~~{z|~{{{{|}{{}~{yxy|}}~~{yyzz|}{~~{zzyy}|{|}||~|||{|~~}ywxzywutttuy||}~}||}}}~{{zy{}~}zy|~~||~~}}}}~}}{zyxy}~~|zz{}~{zz}|{{z{|}zy{||zxwwwy~{yyxxz{{yvwyzzwvvwwz~}|~}{z|~~~~|yxxxy|~~|}~}~}z{}}|yxxxx{~~}|}~{wwwvxz{{yxz|}|zz{{|}{|~~~zxxxxz|~}{z|~~|zyxxy|~~~~|zyz}}{yy{}~~|{{{|~}}~|{{{}}{{z{}~~}z|~}||||~~}~zyy{||||yvvvvx{~~}{}}||{|}||~|{}~{{{{}|z|~}zwvuuuy{{||{yy{~~||~|zzzz{}}|zz|~~|{{{|~}|||}~~}||{~~|z{}~|zy{}~~{yyyy{}~~{z|~~|yyyxy}~}}{zzyz||{xxz{{ywvvvx}~||~}||~~}}{wvvvwz|}{z{~~~~}}~}zzyyz~~}}}~}ywvuvxzzywx{||zyyz{~{xwwwx{||zy|~~|{{zz}}{yy|}zxxy|||}|ywwxy{~}}~}}|}}|{{}~|z{~}|||}}{{}}||{xttttuxzzyy{~}}}}}~}~||}|zzz{|}|~~{zyxx{}}||zyxz|}{z{}{yyxxz}}|yy|}}|zzz{}}||{|~}~~~||~}{z{}}}}}zxwwwx{||zy{~~}{zzyz}~}~|{zz{}}|yxz||{yxxxy|~~||}~||}|xvuuuvxyywx{}~}|}~~~~}|||~}}~~}ywwvvwxxvuvyzzyxyyz}}yxwwwy{{zxz}~~}||||~|{}~|yxyz{{{{xuuuuvy{|zz|~~~~~~~~|z|~~||||}~~|xutsstvwwuuwz||{{||~~}~|yyxy{}~~||~}||||}zyy{|zyxz|~~~{xxxxy{{{yxz||{zzzz{~~}||}~||~|{|}~}}|xvvvvxzzywxz||{zzzz|~~~~~||{{|~~|zz|}}{yyyy{~||~}||~|ywvuuvxwvtuxz{zyz{}~~~~~}}~}~}yxxwwyyyvtuwxwvvvwx|{zyxyz{zxwz|}|{{|}~~{zz{|{{zvtsstuwxwvwz|}|||}}||~~|{{{||xvuttuvvtrtwxxwwxy{|zyyxy{||{z|~}~}~||~~{yyz{|||{ywwwwxz{zwwy{{zxxxy{~~~~~}}}~}{|~~~~~~}zwvuuuwyxwvxzzzxxxyz~}}||}}{{}~~|{zz{}~}}~|ywvvvwwwussvwwvuvwy}~~~~}~}}~|zyxxyzzxvvxxwutttux|~}|{{||{ywxz{{zyz{|~~~~}|zwtssrtvwvtsvxyyxxyz|~~}|{{|~{ywvvwwvtrsuvvtttuw{~{zyyz||{yy{}~}||}}|{|}}||zwuuuvxyzywwyzzxwwwwy}~|{{|}||~~|z{~~}|}}|xwvuvwxwuuvyyywwwwx{~~~~~~}|}~~}|{{{|~{xwwvwyywutuvvussstv{~~~~}}~~~}}||~~~|{zzz{|{ywwyyywutttwz||}}||}~~~|yy{{zyxxyz}~zvttssuvutrtvwvuuvvx{~}}~~~|||}~|{zzzzxutuvvussssux|}~}}|}~~}{z{|}|{{{|~~|xutttuwxxwvxzzywvvvwz}~~~~~~}||}~~}{z|}}|{{{|~~{zxxxyywutvxxwwvwwy}}}~~~~~~~~|{{{}|yxxxyzyxvvwxwutsssuxz{}}}}~~~~}}}}}||~~~|{|~~}|{{|}}|zxyz{yxvvvvy{|||{zyz}~~~}~~~|zyxxy|~zxvuuvvusrtvvutttuwz}~~||{|~}~~}|||~~~~}zwwwwvtssstwz||}}||}~}|}}|zz{||ywvuvwwvuuwyzyxxxxy|}~~}|z{}~~~~~}{{|}|zyyy{~~||{{||zxvwxxwvuvvx{~~}~}}~}}}~~~}~}|{z{{{ywwxyywvuuuwy{{{zyyz|~~}~~~~~~{z|}~}||}}~||{|}}{{|}|zxwwwy|}}}|{zz{}|{z{~}|{{}}|{zywtstuvutttuwz}~~~}||}}{{z{}}}{|~~~~}|||~~||}|zwutttwz{|||{{|~~~~~}}}~~|{zzzywutvxxxwwxx{~}{{{||{{|~}||}~~~~~}{yyyy|~}||}~}{zz{{zxvuuvy}~~~~}}~~|zz{{}~}zyzzzywwvvxz|}}|{yxy{{zz{~}|~~~}~~}|}~}|zzz{~|zyyz{}}}|}}{zyy{}~~}||}~~|z{|}}~~|{{|}~~{xwxwvtsstux|}~~}}}~~}|}}||||}{zyz|}}}}~}}~~}}}~}}}}~~~}{ywwy{{{{{zz|~~}}~~|||}}zxyyywvvvwy|~~}zyz{|}~~|{{||}~~||~~~~}{zz{}~~~}}}~~|zz|}}|zyyxy{}}~~~}}}ywwwxz|}}}~~~~~}{yxwxy|~~~~}{|}~}{yyz{|}~}~~~||}~}|{{{}~}}~}{zzz|}{zyyz{zyyz|~~~~~}~~}}}~}|}~~~}|zwwwxz}}zxvutux{|}~}}}~}~~~|{||}~~~|zyz{{zyyz|~{yyyz|~~~~~||||}~~}|}~~}~~}|zyz{}}||}~{zzz{|~~}}~}~~}{yxwvx{~~}}|{{ywvvwy{}~}||}~~}||}~~{zz{|~~~}~~~{zz{|{{zzz|~}||}}zxwwwxyyxy|~~}{{|}~~}}~|||}||{xvvwy|~~|{zzz}}|||}~~}}~}}~}|{{|{ywwyzzzz{|}~~~}}~~|ywvvwxyyyz|~|zy{|||{{{{}~zxwwxz}~}|{|}|zyxxxz~|zyxxyzzzz{~~~~}||||}~~|{{}~}}||}~~|{||{zz{}~|{zz{||{zz{~~}|{|}~~~~~{wtsssuxyzz{}~~}||||~}zyxyy{|}||}~~~~~~}{{||zyxwxy|~~}}~}|{zzzzxvvwyz{{|~}||}~}}~~~~|yvuttvxz{{}~}|{|~}|||}~~|{{|~~~~~}}}}~~}{yyyyyxwwxy|~}}~~}zyxxxyyywvwy{|}~~{{|~}{zz|}~}}|zyxwxxyxy{}~}|{zyy{}}{zyyzzzxwxz{||}~{yxyz{}~}|{|}~}|{{{}~{zz{|}}}{zzz{|}}|{z{|||{{|~~}~~}~|ywvuuutstwz|}||||}}zyxxyz{{zz|}~~~~~~~|zz{|~~|{yxxy{}~~}}~~}||}}~}{yxxyxwwwxz~|zyz|~~~}}~~{yxwwwwwuuvy|~~}}~|||}~~}||}~}|{{|}}|{{|}~~}||}|{yxwwxz}~}{yxxxyzzzyxxzzyyyy{~}{yy{}~~}{z{|~}|||||{ywvwyzzz{||}~}}~~}|||{yxxyzzyyyy{~~{ywwwxyzzz|}~~}|{{{}}|{zxwwxyz|~~}|}~~~|{zz|~~~~~~}||{{zwusttuuvxz|~|{zzzzyxy{|}}||}~}{yyyz{|||}~~|{{}~~~~}||}}{yyz{|~~~}||}|{yxwwx{~}|{{zz{~~~~~~~}{zyyyz{zxwvwxxwxz|~~}|}~~~~}|{}~~~}||||~~}|||||{zz|}~}|{zzz|}~~~~}~}ywvvwyzzyxyz{{zyyyz|~}}|{zz}|{{|}}}|{{|}~~}{{{zxwvwx{}}}~~}}}~|zyz{{zzyyz|~~|zzyyyxwvwy{}}}}}~~zvtttvxz{{{}~}}}}|}}||}}~~~|zyyyxvtssux}~~~~~~|zxxyz{{{||~}{zyyz{{{zyz|}~~}||}}}||~{yxwxz{|||}~~}|{{{|~~~~~{yyz||}~~|yxwxyz{{zyyz{{yxwwwy}}||}}}}}|{|}}|}~~}}}}~~}}}}~~~|zz{{{{{{|}~~}||}~~|zxwwwwwwyz{|{{{{|~~|{{|}~}{ywvwxz|~~~}~}{yxxy{}}||}~~}}}~~}}|{zyyz{~}||||||{ywwxxxxyz|~~~~|zxvuuuuuvy|}~~~~~}~|zyyz|}~~}|}}}|zywvvxy{|~~|{zz{}}|||{zyyy{}|zxxyz{{{zz{{|{{{|~~~~|{z{|}~~~}|||{{zyxwx{}~~~}}~}}~}{yyz||xvvvwyyyyz{|||{{{{|}~~~~}|{ywvvvx{}~~}|}~}{{{|~~}~~~}{zyy{}~~~}||}~~~~~~}{yvtuwyzzzz{|~|{{|}yvuuvwyzz|}~~~}||~~~}}|{yxxxz|~}{zz|~}|||}}|zz{{zyxwwx{~}{zyyz{{zxvssuwxy{|~~~~}~|zyyzzzz{}~~}|||}~~~}{zz{~~{yxxxyzz{{|~~|zzz|~|zyyyyyyz|}}|{{{|~~|||||{zxwwwy{}~}{yxxyz{|}~}}}||~~|zxwvvvwy{{{{{|}}~}ywuttuvvwwy{~~}|||||}~}|||~}||}}~}|zyyy{}~~|zyxvvvwxz}~}|}~|zyxwuuuxz}~~~}|}}~~|ywvvwxyyyy{}~}{z{|}}|{zzz{}~~~{ywvvwxyzzyyyxvutvx{~~~|zxxy{|}}}}~~}||||}}{yxxxyxwvwxz{|}~~~}{xwxz{||}}}~~}|zxvtttuvwxy{}~~|zxxy{~{xvuvwxxyz{}~}ywuuuuutstuwyz|~~||}}{{|}~~~}|zyxwwxxyzzz|~~|ywuvx|~~}}~~}}~~~~~~|zwvvvwxy{}}{z|~~{ywvvwwwwwxyz{|}~~~}|||||||~~~|zxwwwwvvuuwy{{zywvvy|~{yyz|~~~~~~}}~}|{{zyz{|}{{{|~|{zzyxwwwxxwwxy{~~}|}}{zyyyz{|}yvutttuutttvy{}~~}||}~}zyxy|}|ywuuvx{~~|zywvtsstuuuuvx{}{yyz{~~||}~|ywvvwxxxwwxyz{|}}}}||{||}~~|{{}}}|}~~}}~~~}}}}{ywvvwz~}{zzyzz{{zyxvuuwxxxxyz{~~}||}~~|{{|~|zyxxxwvuuuvvvwyz|}~}||}~~{yxxyz{|}~~~||||}~~~~{zz{~~}{yxy{}~~}{yxxyxxwwvwy|~}}}|}~{yyz}~{xvvuutssstuvvwy|~|{yxxyz{|~{yxxz|~|yxy{}~~|yvuuuutttuwy|~}|{{zyyz}~}~~~~}{yxxxwwvwxxyxxxy{~|{{}~}}|{{|~~}|{{|}~~~}|{z{|~~yvttuxz{{yxwwwwwwxy{}~~~~~}||}|{ywvvwwvuttuwz}~||~~}|zxvvx{}~}}|{{{}~{xvuvx{}~~}}|zyxxxxy{|}}~~~|{{|~~~}{ywusstuuutuuwz~}|||||{zxwx{}~}|||~~{yxwxz{|||{ywvttuwy{|}}~~~~~~~}|zyyz{|}}{zyz{|}|zxwvwxxxxxxy{}~|{{}~~~|{{||~~|||||||}~~~~|zxvuttuwy{|{ywwwy{}~|{z{|}~~~~~~~~~|zz|}|zyyxxwvvvwxy{|}~~}}~~|zyxxxxz|~||}~|{{||}}~~|{zzywvuvw{~~|zzz|}~~}}||}~~}}}}|{{}|{{{}~~}{yxwvutttuwy{|}~~~}|||||||{{{zzyz{}}{z{}~}|}~~~}zxvvx{}}|zxxxyz{||||}~}||}~~~~}}}}}{zzz||ywvwxz{|{{zzyxwxxz|~~~~|{{|}~~}}|}}~~|{{|}~~}|{{|}~~}}|zwusstvy{{{{{|}~~{yxxz{||}~~~~{zyyz{}~}{yxxxy{|||{zz|~~}~~~~~~|{yxwy|}{z{}~~~~}|{|}~|{{{{|{zxwvvwy{~~~}}~~}}|}~~|{{|}~~~}~~}|{zzz{}}}|zwvuvxz{||||}~~}|||||}||{zz{|}}|{{z|~~|zz{|~}|{}~~~~~}|{yxwxz|~}|{{{|}~~}}~~|||{{|}~~~}}}}~~|{yxwvvwz|}}|{zz{|~~{xwwxz|}}}}}~~~~}}}}}~~~}|{zywuttvy}~~|zyxwxy{}~}}||{zyxz|~}}}~~}}|||||||}~{yxy|~~~~~}}}}}}~~zxy{}~}}~~}||||{{{{{||{zxwxz~~}||||||}}~~}|{z{}}{yyy{|}}}||{{{|||||}~~|ywxy{||{{{{||||}~~}~}zxy|}||{{||}~~~~~}zyxy|}}}}}||}~~~~~~~}{zz{||}}~~~|zxwvwwyz|~~~~}yvttvxz{||~~}}}}}}~}}}~~}}}}}|zywwwz}~|{{{}~}zxwxxy{|}~}|||}}~~}|{zzzz{|~~}}~~}|{{{|}~~}{yxxz{}~~}}{zyz|~~{yyy{||||{z{||}}}||||}~}}}}|zxwwwy{~~}|||||}~}|}~~~}|zzyz|~||}}~~}~~~}zxwwxy{|}}|||}}|{{|}~~~~~}{zz|~|yvvwy{|}~~~~~~||||}}|{zyyz{}~|{zz{}~}}}~~}||{zyxwwxz~~}|{{||{ywttuwz|}~}{{{|}~~}}~|zyz{}~~~}}}~~~}|{|~~|zyyyyz{|}}~}{yxxxz}}zxxyz{{{{||}||{z{|}||||{{zyxyz|}~}{xwxz}~~~~{yy{~|zxxxyyz{{|||||}~}|zxwvuuvwxy{}}||}|zyz{}~~}|{{|~~}|}}~~~}|{z{}~{xwwxyz{|}~~}||{{z{}~~|{}~{xwvvwxy{}~~~}|{yxwwxxyzz{|~}||}~}||||||}}}}|{zyz{|~~|zyxxxz{|{ywvuvwy{}}|||{{{{}~|{zyy{}~~}|}~|{zyyyyyxxxz|~~~}|{{|~~{yxwwxy{|}}}}}}~~}||zywvuvwxz|}}}|zyyz|~~~~~~~~~|zwvvwy{|||}~}zxwvvvvuuuvwz|~~~~~~}{zyz{}~}}}|{zyyyyz{|}~}{xwwx{~~}{zz{{|}~~~~}|zwvuvxz|}~}~~{xwvvwxxxxxy{}~~|zz{||}|||}}~~~~~}{zyxxxwwxxz{|{ywvvx{~|||}~~~}|{{|}}~~}~|zyyz{|~}|}}{zzzzyxxxxyyz{|~}}~}zxwwxyz{|}~|ywvuuuuuvvwz|~}{{{~~|zzz|~~|zxwwwxz|~}{yxwvuttuuvwwy{~~|yxxz|~}{zz{{|}~|zxxxxxyyyyy{}}{z{|~~||}~~}}||{{|}~}}}}~~}|{zxwwwxz}~}}~~~}|zyxwwvvwxyyzz|}}{{{|~}|{{{|}~}{zyyyxxwvvwyz{|||{{{|}|yxxy{|}}}}}}}}}}~|{{{|}~~|{z{|~}||}~~|{zyxyyyyxxyz|}|{{|}~~}{ywwxz}}zxwvvuuttuvwyz|~~{yxwxy{}}{yxxy|~~}~}zxvuuvvvvvwx{~~|zyxxxxy{~}|{{}~}{yxxxxxxxxyz{|}~~}}~~}{{{||}~}}~~}}||||}~}|{yxyz|}zxxy{|}|{ywwwxxyyz{|~~~~~}|{{{{z{{}}||~~~~~}|{zxxwwwwwwxz|~}}~|zyxwwxz|~~~~~~}|{{{{{|}~~~|zxwy{~~|{{zzyyyz{|}~}|{zyz|~}{z{|}}{yxvuttuvvvwxz|}|{zyxwwxz}~~~~~~}}}}}~|zzz|~~|{yxwvvvwyz|~~}|{{zyxwvvxz|~~~~~}}}}}|{zyxwxxyzzzz{}~~~~}{zzz|}}|{||}}~~~~~~~~|ywvvwyz{||{zyyyyz|~~}|{zzz{{{{zzyz{}~~~}}~~~|{zyyyxxxxxz|~~|ywvvwxz|}~}~~~}|{z{{|}~}{yxwwxy{}~|{{|}~~~~~}||}}~~~}|{{zzz{|~~~~~}}~}zxwwvvvvvwy{}~~}}||{yxwwxy{}|{z{|~}}~~}|zyyz{}~}{zyyz{|}}}~~|{{{{{{zyxxxwwxy{~|zyxyz{}}}|{zzzzyzz{|~}}}~~~}||{{{|}~||~~~}}|}}~~|zxvuuvx{|}}||||~}zxwwxxyyyzz{{{{{|}}|zyyz{}~|{zzzz{|}}~~~}|{zywwwy|}{yz{|}}}}|||||}~~}||{zywvvxz}~{zzz{{||||}}}|{zz{}~|{{{|}}~~~}|{zxwwxy{|}~~~~~}}}||{{{{zyyyz|}zxwxz{}~~}~~|zyyy{|~~~}}}~~}|{zzzyyyyzyyxxxy{~|zxwwwxxy{|}~}||{{|}}{zzz{{|}~~~~~}||}}|{{|~~~~~}{ywvvvwy{}~{yxwvvvwxyz{{|||}~}|zyxxxxyz{}~}~~~~~~~~~|{|}~}|{zyz{}~|zxxxy{}~~~~~~~~}}}||{zyyxxxyz}~~~}|{zzyyz{|}}}}}}}~~}||{{zzz{}~~}||||}}}}~~~{zz{{|||||||}}|||{|}~~}{zxwwxz}}}}}}~~~~~~~}|{zz{}~~~}{zyxyyyzzzzz{|}~~~}|{zyxwvuvxz|}~}ywvvwyz{|}~~|zz{}~}}|{yxwvwz}~|zxvvvwxxyz{}~~}{zyyyyyxxwwxy{|~~|zyyz{}}~~~}}~~{yxwxy{|~~}|{{|||||{{{|||{zyz{}~~~}}|||}~~~~}{zzzz{{|}~~}||{|||||{zzzzz{|}~~~}{zyyyyz{|}}}}}~~~~~~|{yxwxyz}}{yxxyz{}}~~~}}|}~~~~~~~}}}}~}|{zyyyyz{}~~~~~~}|{{{{zyxwvvvwwy{}~|ywvuuvxz|}~~~~~~}|{|}~|{{{|}~~}|{{z{{|~~}}}}~~~|{zyxwwwxz|~}zxxwwxxxwwwxxyyz|~~~~~~~~~}{zyxyz|~~~~~|zxxxz}|zyyyzzzz{{||}}}}}~~}|zyxxyz{|}}~~}}|{{{}~|zyyyz{{{{{{{{{zz{}~{yxwxyz{|}~~~~}}}}}}|{zyyz}~{xwvwwxyz|}~}{zzzz{|}~~}{{{{}~}|{{zzzyyyyxxwwxy{~~~~~~~~|zxvuvwxz|}~~~}~~~~~}}~~|{zzzz{|}~~}}}~}|{{{{|||}~~~~}{zzz{|~~{yxwwvvwxyyyyyz{|~~}|{zz{|}~~}|zzzz{|~~}}~}|{{|~}{zxxxxyz||}}~~|zyxxxxxyy{|~~~~~}{zyyyyz{|||||||}~~~}}|||}~}|zyxyz{}~}}}~~~}}}~}|zywvvwxz|}~~|zyyyyzz{{|~~~~~~~}{zyyzzzzzyyz{{|}~~}|{{||}}}}}|zyxwxy|~~~~}}~~}|zyyz{|}~~~~}||{{{zzz{|}~~~~}{yxwwxxyyzz{|}~~|zyyyyzz{|}~~}{{{|~~}}||}}~~~~}{yxxxyz{|}~~|{zyxxwwxyz|}~~}||{|}~}{zyyzz{{|}~~|{zzz{{|}}}{zz{}~}}~~|{yxxxxyz|}~~~}{{zzyyyz{|}~~~~~~}||||}~|{{zzzz{|}}~~}}~~}||{{{{{{|}~~}|{z{|~}{zz{|}~~~~~~}|{{zz{|~~}}}~~}|{zz{{{||}~~}|||}~}|zyyxyyz|}~}|zzyyyyyy{|~~~}{zyyz{}~~}|zyyyz|~}|zyyyyyyz{|~~|{zyyz{|}~~}|{{{|}}|{{{zzz{|}~~~~~~}{{{{|}~~~}|{zyyz{}~}}}}~~~}{zzzz{{{|}~~~~~~}|{zzzz{|}~}|{{|}~~~~~~~~~}|{{zzz{|}}~~~~~}|zyyyz{|}~~}|||}~~}|{{|~~}}|||{{||}~~}||{{{{{|}~~|{{{|}~~~}|{zyyyyz{|}~~|{zyyyyz{}~~~~}|{||}~}|{|}~}||{{zzz{}~~}|{zzyyyyyz{}~~~~~~}|{{zz{{|}~~~~~}|{{zzz{|~~}}}|}}~}{zyz{|~~~}|||{|}~~~}|{{zzzzyyyyz{}~~~~~~}~~~||{{{{{||}~|{zyxxyz|}~|zyyyz|~~~}}}}~~}}|{{{{{{zzzz{|}~~~~~}|{{zzz{{|}~}}|{zyxxyz|}~}}}}~~~~~~|{zzz{|~~}}||}~~~}|{zzzzzyxxxyz{}~~~}}}}}~~}|||||||}~~}~~~~}|{zzz{{|~~}}}}~~~}{zyyyyz|~~~~}|zzyyyyyyyyyzz{|~~~~~}}}|||}~~~}}||||}~~~~~}{zzyyyz{}~}|{{|}~~|{zzyyyz{}~|{zzzzzzzzzzzz{{|~~~}}|||}~~}|||||}~~~~~~~~}|{{zzyyz{}|{zz{|}~~~~}|{zzyz{}}|{zzzzyyyyyyyzz{}~}|{zz{{|}~~}}}}~~}}}}}}}~~~~~~}}||{{{{|}}|{z{|}~}|{yyxyz|}~|{zyyyyyyyzzzz{{}~~~~~}|{{zzz{{}~~~~}|||}}~~~}}|{{zzz{}}{zzz{|}~~~~}}|{zyyyz{|~}|{{zzyzzz{{{{{{|~~~~~~}}|{{zz{{|}~~~~|{{{|}}~~}}}}}||{{{{|}}{zyyz{|~~}}~~~~}|{zzzz{}~~}|{zyyyzzzzzz{|}~~~}}|{zzzzzzz{}~~|zzzz{|}}}~~~~~~}}||}}~~|{zzz{}~~~~~~~~}|{{zyyyz|~}|{zyyyyzzzz{{|}~~~}||{zzzyyyzz{|}~|{zz{{|}}~~~~}}||}}~~}{zyyz{}~~~}}}}}}~~~~}}||{{zzz{|~~}|{zzzzz{{{||}~~~}|{{{zzzzzzz{|}~}{zzzz{||}}~~~~~}}~~~~~~}|zyyyz|}~}||{||}}~~~~~~}}||{z{|~}|{{zzzzz{{|}}}~~}|{{{zzzzyyyyz{|~~|zzyyzz{|}~~~~~~|{zzz{|~~}|{{||}}~~~~~~}}|{zz{|~~}|{zzzzz{{|}~~~~|{zzzzyyyyyyz{|}~~|{zzzz{|}~~~~~~~|{zzz{|}}|{z{{|||}}}}}}}}||{||}~~~}}|{{{{||}~~~~~~~|{zzzzzzzzzzz{{|}~|{zyyz{|}}~~~~~~}|{zzz{}~|{zzzz{{|}}~~~~~~}}}}}~~~~~~}|{{{||}}~~~~~}|{zzzzyyyyzzz{{}~~|{yyyyz{|}~~}|{{{|}~|{zzzz{{|}}~~~~~~}}}}}}~~~~~~~}|{{||}~~~~~~|{zzyyyyyyzzzz{|~~}{zzzz{|}~~~~~|{{{|~~|{zzzzz{{|}~~~~~~~~~~~~~~}}}}~~~}}}}}~~~~~~}|{zzzyzzzzzz{{|}~|{zzzz{|}~~~~}|{{|}~}{zzyyyz{|}}~~~~~~}}}}~~~~~~~~~~~~~~~~~~}{{zzyzzzzzz{{|~~|{zzzz{|}~~~~}}}}~~}|{zzyzz{|}~~~~~~~~}}}~~~~~~~~~~~~~~}{zyyyzzzzz{{|}~~~~~}{{z{{}~~~}}~~~~}}~~~}{zyyyz{||}~~~~}}}}}}~~~~~~~~~~~}|{zzzzzz{{{|}~~~~~~}{{{{|}~~~~~~~~~~~}|{zyyzz{|}~~~~}}}}}~~~~~~~~~~~~~~~}|{{{zzzz{{|}}~~~~~~~~|{{{|}~~~}}||}~~~}|{zzzz{{|}~~~~~~}}}}~~~~~~~~~~}}~~}|{{zzzz{{|}}~~~}}}~~~~}}||}~~}}|{{||~~~~~~}|{{zzzz{|~~~~~~}}}}}~~~~~~}}}|||}~~}|{{{{{|}}~~~~~~~~~~~~}}}}~~~}|{{{|}~~~}|{{zzz{|~~~~~~}}}}~~~~~~}}}}||{||}~}|{{{|}}}~~~~~~~~}}}~~~~}}~~~|{{z{{|}~~~~}|{{{{|}~~~}}}~~~~~~~~~~~~~~}}||{{||}~~||{{||}}}~~~~}}||}}~~}{zzyyzz{|~~}}}}~}|{zz{{|}~~~}}||||}}~}|{zyyyyzz{}~~~~~~~~~||||}}~~~}}}}}}~~~~}}|{zzzz{|}~|{zzz{|}~~~~}}}~~}|{zzyyyxxyy{|}~~}||{{{{{{||}~~~~~~~}}}~~}|zyxxxyyz|}~~~~~~~~~~}||{zyyyz{}~~~~~~}}}}}}}~~~}|||||||}~}||{zz{{|}~~}|{zyxxxwwxyz}~}{zz{||}~~}|{{{{{|}~~~~~~~~}||||}~}|{zyxxxwxxz{}~~}}}}~~~~~~~~~~~}|{{{||}}}}}}~~~~}}|}}}~~~}}}}}||}~~|zyxxyz{{|~~}|{yxxxxxxxyz{|~~}||||{{{{|||}~~~}}~~~|{yxxxxyzz|}~|{{{|}}}}~~~~~~}|zyyxxxxz|~~}}}}}}|}}}|||}~~~}}}}}~~~}}}||||}~~}|{{{zyxxxxxxyz|~~|zyyyyz{}~}||||||}~~~~~}}}}}}~~}}|{yxwwxxxxy{}~}|{z{{||}~~~~}||{zz{{{{{{|}~~}}|}}}}}~~}}}~~}}}~|zxwwxyz{|~~|{zyxxxxxwxyz|}~~}|||{{z{{zzz{|~~}}~~~~~~}|{yxxxyz{|~~}{zyzz||}}~~}}}|{zyyyyxwwy{~~}|}}}}||||}||}}~~}|}~~~~~~~~~}}~~~}|{zzz{zyyyyyyxxy{~~|zyxyyyyz|~}}|{{{|}}|||}~}|||||}~~~}}|zxxxxyyyyz|~~}{yyyz{{{|~}|{yxxyzzzz{|~~~~}}}}}}~~}|||}~~~~~~~}zxwwxyzz{}}}|{zxxxyxxxyz|~}||||zyyzyyyyz}}}}}}}||}~~}}~~~~|zyyy{{||~~}{yxxyz|}}~~|{z{{{zyyzyxxxxz}~}}~~~}|||}}||}~}||}~~~}|||}}|}~}}}||zyyyz{{zz{{zzyz|~~|zyyyxxxz}~}}|{z{{{zzz{}~~}||}}|{|~~~~}}{yxxyyzzz|~~~}}|zyxxyz{{{}~}|zxxyyyxxy{}~}~~~~}{{{}}}~~~~}}~~~}}~~~}zyxxyzzz|~}}|{yyzzyxxy{~~||{{yxxyyxxxy{~}|}}}||{{}~}}~~|{{{|}||}}{xwxy{|}}~}{yyyz{{zyz{{yxxy{~~}}~~}||}~}}~~|{{}}}|{{{||||~~}}}|{yxxyz{|{{|}}|{{|~~~}{zzzyxxy{~~~}{yyzzyyyy{}~~~}|}}}|}~}zyyz{}}||~}||}}|zxwxyz{{{}~}}|{ywxyyxxy{}~~~~|{|}}|~~}~~~}|{{}}|||~~~}{yyyz|||}~~~~~|{{{{zzz{}~|{{{zyxyyyxxxz}~}}~}}|zz|}}|}}|||}~}|~}zxxy{}~~~}}}|zxxxyz{{z{||zzz{}~~~~~~~~}~~~~~|{z{}}||zz|}|{|~~}}}|{xxxy{}||}~|||~~~~~|{||zyxy{~}}}{xyzzxxxy{~~~}}~~}~~}~~|{{|}~}|}~~||}}||yxxxz{|{|~~~}{yyzzyxyz}~~~~~~~||}~}}~|}~~}|zz{||{|~}{{{|}}||}}~~}|||~~}|{|{yxyzzyxyz}~}~~}{z{}}|}~}}}~~}{}~}~~|zzz|~~~~}}~}|zxwxyz|{z{}}||||}~~}~~}~~}~~}|{{}}}|{z{}}{|~~~}zxyz{~~}}~}}}~~~~}}~~|{{{}~}}}}zyyzyxxxy|~~~~~~}}~~}|}~~|}~}~~}|}}}}{yyyz|~}}~~~{z{{zyz{|~~||||~~~~~~~}~~}|~}}{zy{|{{|~~}}|}~~}|~}}}~~~}|||{yz{{zyzz}~~~~}{{~~}~}}}~~|{}}|{{|}}|||}~~}~}~~}|zxxxy{}|{}~}}|~~~}~~~~}||}~~}||~~~}|{|~~}}~~~|z{{|~~}~}}}}||~~}~}||}~}}~}|yz{{yxxy|~~~}~~}}}~~}~||~}|||}~}~~~~|zz{|~~}~~|~~}{{{|~}~~~}zyz{}}~}~}}|~~~|zz|}|{}~}}}~||~~}~~~}~~}~~}~~~}~~~|z|}}{{||~~~~~}~}~}}}}~~|y{|{yyz{~~}}~}}~}~~~{yyyz}~}|~~}}}}~~~}~~~~~~{zz{}~}~~}}}~}~~~~}|||~}~~}}|}~}|zz|~||}~~}}~~}}~}{{}|zyzz}~~}{||~~~}~~}~{|~}{zzz}}|||}~~}|||~~~||}}}|zxyy{}~}}~~~~}}}~~|{|~}|}}~}~}{}~}{{|}~~}~~|{|~}{|~}~}||}~~~~}~}}||}}{xz{zyxyy}~~~||}~~~~{{{{}~|~~}}}~~|{|~}~~~~~}zyyy{}}|}~~~~~~}}}|}}|~||{{}}||{yy||{{~~}~~}~~~~~|~~|{{{~}~~}zyzz|~~~}~}z{||zyyy{}}}}~}~~}}||}}~{{|}||{xxxy{~~|~~}}{||}~~~~~}}}~~}}|}~{z||{yyz|~}}~~}{z{}|z{~~}}}~~~~~}~|{{z{||yxz{yxxyz~~~~{{}}|{||~||||~~}~~}}|~~}|zy{}|{}~|}~}}|yyyy{}}|}~~}}}}~~~~~|}~~|{}~}zzzy|}||{yy{|{{}}}~~|{|}~~~~|||~~}~||~}}|zxyyz}~~~}}~}zz||zxyyz~~~}}~|~}|}}~}||~~}{{}}zz{|||{xxyy{~~}~}~~}}{yz{|~}~~}~}||{|}}zy{{zxxy{~}|}~}}}zy{}{z|~~}}}}|~~||~~~~|yzyz{|zxz{zxxyz}~~}zy{|{yz{}~}}}~|~|{{z|~}}{yy{|{{}||}~}}{xyyy|}}|~}~|{{{}~~~|}~|yz||zyyyz}|||zy{}{{}}}~~}}{yz}}||}}|~}|~|{{}}||ywxxz}~}~~}|}~|yz|{yyyy|~~}~|{}~|z{{|~}|}~}}|zz{}{yz|}||zxyy{~~~~|}}||{xyyz}}}}}{{zz||zxz{zxxxy}~}||~~}}{z{}|{|~}}|}{{}~}|~|{|}|}{xyxy{|zyz|{yyzz~}}~}zy{{zxyz{~~}~}{|~}{yzy{}|||yxz|{z|~|}~}}}zyyy{}~}~}}~~~|zzz{}~}~}|}|yxz{zxxxy|}}}|z{}~|}~}|}~}}|zyz}|{}~}|~}{{|}{z{||||yxxxz}~}~~}|}~|zz||zyzz|~}}~~{z{}|zzzz~}|{}}||{yz||zz|}}}|zzz{}}}|}}||zwxyz}~~}}~~}zyyyz{{yxz{zxxyz~~}|}~~~|{|~}|}}}{}~~~}{z|~||}}{{|||{yxxxz||zz|}|{{{|~}|}~|yyzzyxyz|~}}~~|yz|{yxyx{}|||zy{}{{}~}}~}{zz{}~||}~}}|yyyz}~~}~||||yxy{zxxyy|~~~}||}}~}||~~}|zyz}}{|~}|}~}}|zz{}{yz|}}}zxyyz}~~~~~}~~~{{}}|{{{}}|{|}{yz|{zyzz}~}|{}}||zyy||{z|~~|{{|}}~}|}~}}zxxyz}~||}}}|yxxxy{{yy{|zyyz{~}}~}|~~~|{{}~}}|z{}~||~|{||||zxxxy|}|{}~~|{||}}|}~}zz{{zxyz|~}|}}zxz{zxxxx{}}}|{z|~}|~~~|||}~~||}}}}zyzz{~~~~}|}}{xy{zyxyy|~}}~~}}||~~~|z{}}|}}|{|}}||zy{|{z{}}}}{yzz|~~~~~~~|}}|}}~}||}|zy{|{yzz|~}|}~}}|zz{}|{}}|||}}}~~~~}~~~|yyzz}~~||}}||ywxxy{{zy{}|{{{|~~~~~||{}~}}|{{}~}}}|}~}}{xyyz|~}|}~}|||~}}}}~~|{||{zz{|}|{||{xyzzxxxxz}}~~||~~}~~~}}}~}|}~~~|zzz{}~~~}}~}zy{{zyyz{~~}}}}}}~~}}}~}|}~}~~|{{|}}|{yy{|{z{}~~|{{{}~}~~}}}~}||}}{z{||{z{|~}}}~~|{|~~}}}|||}~}{|~~}|||~~~}{{{|~~~}||}}|{yxxxz{|{{}~}||||~~~~}|{}~~~||~~~~~}{zz{|~~}}~}|||~}}~~~}}}~|}~~}{{{}}||{||zxyzzyxxy|~~~~~~~~~~~~~~~~~}~}{{|}~~~~~|{|}|{{{|~~}~~{z{{}~~}}}}}~~~~~~|{{|}}}|zz{}|{|~~|||}~}|~~~~}}~~}{|}}|||}~~~~}}~}}||||}|zz||{z{{}}|||~~}}||}}}|zxyy{}}||~~|||}~~~~}}}~~}}}~~~|{{|~}|~~||{{}~~~}{{}~}}~~~~~|||}}||{||{yz{{yyzz}~~~~}~~~~~~~~}}~}||}~}}}~}~~}|||~~~||}}}}zyyz{}}|}~~~~~~~~~}|{{}~~~}|{}~}}~~|||}~~||}~~}}}~~~}~~~}}~~}}}~~~~~}|{{{|{yyz{zyyz{~~}}}}~}|}~~~~}}~~~|zzz|}~}|~~}|||~~}~}{{{|~~~~~~}||}~~}|}~~|{zz|}}||zz|}||~~~}|}}|||}~|{{}}{{{{}~}}~|{{|}~~~~~|{}}|{{{}~~~~~}||}}}~~|{{|||{yxyy{}}|}~~~|||}~}~}|||~~}}~}~~||||}}{z||{{{|}~}~~}|}~}~~~~~~~~~~{zzyz{zxxzzyxyz|~}}}}|{|}}|||}~}|{|}~~}|~~||{|~~~~|{|~~~~~}zzz{}~~~~~~~}|}~}{{|}{zyyy{|||{z{}}||~~}|}~}}~~~}}}}~~|}~~}|||~~~}|}}}}{yzz|~~~~}}~}{z||{zz{}~~~~~~~~~}||~}||}}{z{|||{yxyy{}}}~~}}~~~|zz{|}~}|~~}}}~~~|{{{|{zxz{zzzz|~}|}~~~}|}~~}~~~~~}~~~~|zyxxyzywxyzyyyz|}}|||zyz{{zz{|~~}||}~~||}~}{{z{|}}|{{{}~}~~}~~~~{zzz|}~~~}}}}~~}||}|{yz{zyxxxz|||{z{|}}}~~|||}}}}|{|~~}~~~~~~}|}~~}~~}{{|||{yxyz{}~~~}|}}{zz|{zzz{~~~~}|~~}||}~}||}~~}|{{||{zz{|||zyyz{}~~~}||}}|{yyyz|}}}~~~~|zzyzzzxxyzyyyz|}||}~~~}||}~~~}~~~}|}}}|zxxxxyyxxyzzyyz|}|{{{zxxzzyyyz}~}}}}}|{|||zyyy{|||{z{|}}}~}~~~~|zz{|}~~~|{{|}~~}|||{zxyzzxxxxz||||{{}~~~}|{{|}||{z{}}}}~}||~~}|}}|{{{||{yxxy{}~~~~}|}}|zz||{{{{}~~}~~|{|}}|{{|~|{{|}}|{zz||zz{|}}|zzz{}~~~~|||||{zxxyz|}}}~~~}zyxxyzyxxyzyyyz|}||}~}}~~~}}~~}}~}|||||zxwxxyzyxy{{{z{|~}|{{{{yxxyyyyy{~~}||}|zz{{zyxxy{|||{z{}}}}~~~}{{{|~~~~~|{{{|~~~~|{{{zxxyyyxxxy|}}}}|}~~|{z{|||{zz{}}}~~}||}~~}|{{||{zz{||{zyyz{}~~~}|}}|{{|}|||}~}|}}{z{||{{{|~}|{{|}||{zz||{{|}~~}|{{|}~~~~}|||||zyxyz|}~~}}}}}|zxxxxyyxxyzzyyz|~}|}~~}||}~~~}}~}|||||zyxxxz{zzz|}|{||~}{{{{zyxyzzyyz{~}||||{yxyzyxxxy{||||{|~~~~|}}~~}~~~~}{z{|}~~~|{{{zyxxyyyxyz|~~~~~~~~~}|{z{}}}|{{|}~~~~|{{|}}||zz{|{z{||}|zyz{|~~}}~~}|}~~~}}~~}||||zz{||{{|}~|{{|}}||{{|}||}~}|||}~~}}~~}||}}|{yyz{}~~~~|||||{yxwxxyyyxz{{z{{}~~~}|||~~~~}}~~}|}}||zyyyz{|{{}}}|||}}|{{{{zyyzzzzz{~}|{{{zyxyyyxxxy{}}}}|}~~~}~~~~}|{{|}~}|{|{zyyzzyyz{}~}}}~~~}}~~}|{{|}~~}}|}~~}|{z{|||{zz{|{{{|}~}|{{{|~~~~~~~~~}||||{z{|||||}}|||}~~}|||~~}}~}|||}}}|}~~}}|}~~}~~~}|zz{|}~~~}|||||{yxxxyzzzz{||{{|~~}||~~~~~~~~~}|zzz{|}|||}~}|||}~||||||{z{||{{{}~|{zzzzxxyyyxxyz|~~~~~~~~}||}~~}}}}|zz{|{{{{|~~|{{|}}}||}}}|||}~~~~~}|{zz{||||{{|}|||~}|||}~~~~}}}}}|{|}}}}}~}}}~~}~~}||||||{{|||{{{}~|{||}~~~~}||||||zyyyz{|{{|}}|||}~}}}~~|{{|}}}||}}}|{{|~~~~}}~~~~}}}}~~}|}~}|||}}{{zzzzyyyzzyyz{~~~~~~~~~~~~~~}|}}}|||}~}}~~~}{zzz{||||}~~}}}~~~~}}}}|{zz|}}}|||}~~}~}|||}}}}}~~~~~~~~}}~~~~~}|{{{{{yyz{zzzz|~}|||}~}}}~~~~~~}}}}}}{zzz{||||}~}|||}~}}}~~~~}||}}}|||}|{zz{|}}}|||}~}}~~}~~~~~}}||~}|{{{{{zzz{{{{{|~~}}}~~~}}~~}}}~~~~}}|}~~~~~}||||||zyyz{||||~~~~}||}}}}||}}|{{{|~~~~}}~~~||||||{{|}}||}~~~{zzyzzyxxyzyyyz|~}||}||{|}}|||}~~~~~|{{||}}||}}}||{|~~~~~|||}~~~}}}}}|{{||{zyyz{|||{{|}}}~}|||}~~~~}||||||{{|}}|||}~}}~~~}|{{|}~~~~~~~|{|}}|||}}}|}~~}}}}}{{{|||{yyyz{|||}~~~~~~|{{{{|||{|}}}||}~}|{{{{{zyz{{{{|}~}~~~~~~~~|zyxxyyxxxyyyyzz|~}|||{zzz{{{z{{}~}||}}}|||}|{{z{|}}}}}}~}|{|}~~~~~}}|||{zzzzyxxxy{{||{{}~~~~~}|{{|}}}}|}~~~~~~}}}~~~}}~~}}|}}~~~~}|||}|{zzz{}~~~~}}}|{{{|||||~~~~~~~~}||}~~}||{||{z{{||{zyzz|}}}~}||}}|{zzz{||||}~~~}}~~|{zyzzyxxyzzzz{}}}}~~~~~}}}}|zxxxxxxxxyzzzz{}~|{{zzyxyyzzyz{}}}}}}|{{{|{zyyz{|||||}~~~}|||}~}}}~~~~}|{{zyxyyyxxxyz{||||}~~}|{z{||||{{}}~}~~~~~~~~}}|}~~}}}}|{{|||{zyz{|~~~~}}|}|{{{||||}~~~~~}|}}}}}}~|{{|}}||{{{|{z{||||{zz{|}~~~~}||||{zyyyz{|||}~~~}{zyxyyyxxxyzzz{}}}}}~~~~}||||zyxwxxyyxyz{{{{|~|{zzzyxwxyyyyz{}}}||||zzzzzyxxyz{||{|}~~~||}~}|||}~~~}|{zzyxxxyxxxxz{|}}}}~~~~}|{zz{||{{{{}~~~~}|||}~~}|||||{{{{||{zzz{}~~}}|}||{|}}}}~~~}}}|{|}}||}~~|{{{||||{{{||{{|}}}}|{||}~~~~~}|{{{{{zyyzz|}}}~~}}}}|zyxxxxxxxyzzz{|}~}}}}~}}~}||||{zyxxxyzzyz{||||}~~|{zyyyxwxyyyyz{}}||{{zyyyyyxxxxz{||||}~~~~~}||}}~~|{zzzyxxxyyxxy{|~~~~~~}}}|{zzz{{||{{|}~~~~~||{{|}}|{{{|{{{{|||{{z{|~~}}}}}}|}~~~~}}}||{{||||}~~}{{{{|||{{{|}||}~~~}||}}~~~~~~~~~}|{||||{zzz{|}~~~}||||{yxxxxyyxxyz{{|}~~}~~}}}~~}||||{zyyyz{{{{|}}|}}~~|zzzzyyxxyzzz{|~~|{{zzyxxxyxxxxy{|}}}}~~}|}}}|{zzzyxyyyyyz{}~~~}}}}|||||{zzz{|}}||}~~~~}|{zz{|||{{{||{{||}}}|{|}~~~~~~~}}|||{{|}}}}~~}|{{|}}}|||}~~~~~}|}}}}}}}~~}}}~~~~~~}||}}}|{{{|}~~~}||||{{yxxxyyyyyz{|||}~~~}}}~~}}}}}|{zz{{||{|}}}}}}~~}{{zz{zzyz{{{{|}}{zzzyyxxxyyxxyz|}~~~~~}}~~}|{{{zzzz{{{{|}~||||||{{{||{{{{|}~~~~~}}}}|{zzz{|||{{||}||}~~~~}||}~~}}}}}||}}~~~~~~~}|||}~~~~~~}||||||{{|||{{|}~~~~~~~~~}|||}}~~~~~~}|||||{zyyyzz{z{||}}}}~~}}}~~~~||{||}|||}}}|||}~~~~~~~~}|{{{||{{{|||||}~}{zzyyyxxyyyyyz{}~~}}}}|{{|||||}~~~~~~}|{z{{{{{{{|||||}~~~~~~}||||{zzzz{|||||}~~~~~~}}}}~~~~~~~~~~~}~~~~~~}~}|{{{{zzzz{{zz{|~~}||}}}}}}~~~~}~~~~~}|||}}|{zzz{||{||}}}}}~~~~}||}}}|||||{{{{|}}}}}}~~~~~~}}}}}}}||}}}||}~~|{zzzzzyyzz{{{{|~~~~~~~~~}}}}}}}}}~~~}}}}}}|zzzz{{{{|}}}}}~~}|||}||{{|{{zz{|}~~}}~}||||}|||}~~~~~|{zzzzyyyyzzzz{|~}||||||{||}||||~~~~~}~~~}|{{{||||||}}|||}~~~~~}|||||{{{{{zzz{||}}|}}~~~~~~~~~~}}}}}||||}~~}|{{{{{{{{|||||}~~~~}}}~~~~~}}}~~~~~~~~~}}||}~~~}}}}||||||{zzz{||||}~~~~~~~~~~~||{{|||{{|||||||~~}|{{{{{{{|||}}~~~~~}|{zyyyyyxyyzzz{|}~}|{{{{zzz{{{{{|}~}||||||{{{||{{{|}~~~~~~~~}|||{{zzzzzyyyz{||}}}~~~~~~~}}}}|{{{|}~~~~~~~}|||}|||||||||}~~~~~}}~~~}}||}~~~~~~}}}}~~~~~~~}||||}~~}}}}}}||||}|{{z{{|}}}~~~}}}}}}|{{{||||||}}}}}~~}|{zzzzzzz{|||}~~~~~~}}}|zyyyyyyyyzz{{{|}~|{{zzzyyzz{{{{|}}|||||{{{{{{zzz{|}~~~~~~~}|{{{zzyyzzyyyz{|}}}}~~~~~}}}||{z{{|}}}}}~~}}}}}}|||}}||||}~~~~}~~~}}}}}}}|||}~~~~~}}}}~~~~~~~~~}}|||}}}}}|}}}|||}}}|{{{||}}}}~~~~~}|}}}|{{z{{||||}}~}}~~~~~~}|{zzzzzzyzz{||}~~~~~}}}}|zyyyyyyyyz{{{{|}~|{zzzzyyyzzz{{|}~}|{{{{zzzzzzzzz|}}}~~~~~}|{{zzyyyzyyyz{|}}}~~~~~~~}||||{{z{{|}}}}}~~~~~~}}|}}|||||}~~~}}}}}}|}}}}}|||}~~~~~}}}~~~~~}}~~~~~~}||{|}}}}}}}}}}}}}}}||{||}}}}}~~~~~~~}}||}||{{{{{|||}}~~~~~~~~~}}|{zyyzzyyzz{||}~~~~}}}|{zzyyzzzzz{{{{|}~}|{zzzzyyyzzz{{|~~|{{{{zzyzzzzyzz|}}~~~~~}|{{{zyyzzzzzz{|}~~~~~~}}}}}||||{{z{{|}}}}}~~~~~~}}||}||{{||}}}}}}}}}}}}}}}|||}~~~~~~~~~~~~~}}~~~~~~}||||}}}}}}}~~}}}~~}}|{||||||}}~~}~~~~}}}}}|{{{{||||}}~~~~~~}}}}}|zzyzzzzzz{{||}~~~}}}|{zzzzzzzz{{{{||}}|{zzzzyyyzz{{{|~}|{{{zzyyzzzzzz{|}~~~~~}||{{zzzzzzzz{}~~~~~~}}}}}|||||{{{{|}}}}}~~~~~~~}}||||{{{||}}}}}}}}}}}}~}}|||}~~~~~~~~~}~~~~}}|}}~~~}}~~~~}~~~~}|{{||||||}}}}}}~~~}}}}||{{{{||||}~~~~~~~}}}}}|{zzzzzzzz{{|||}~~~~}|{zz{{{zz{{{{{|}~~}|{{{zzyzzz{{{|}}|{{{{zzyzzzzzz{|}~~~~~}|||{zz{{{{{{|}~~~~~~}}|}}|||||||{{||}~~}}~~~}}}}}|||||{{{|}}}}}}}~~}}}~~~}||||}}~~~~~~~~}}~~~~~~~~~~~~~~~}}|{{{{{{{|||||}}~~~~~}}|{{{{||||}}}}}~~~~}}}}|{zzzzzzzz{{|||}~~~}|{{{{{{zz{{{{{|}~~}|{{{{zzzz{{{{|}~}|{{{{zzzzzzzz{{|}~~~~~}}||{{{{{{{{|}~~~~~~~~~~~}||||||||||||||}~~~~~~~~~}}}}}}||||||{{|}}~~}}~~~~~~~~~}|||||}}}~~~~~~~~~~~~~~~~}|{{{{{{{{{|||||}~~~~~}|{{{{{{{||}}}}}~~~~~~}|{{zz{{zzz{{{||}~~|{{{{{{zz{{{{{{|}~~~}||||{{zz{{{{{|}~~}||{{{{zzz{{{{{|}~~~~~~~}}||||{{{{|}~~~~~~~~~~~~~~}}||||||||}}}||}}~~~~~~~~~~~~~~}}}}}||||||||||}~~~~~~~~~~~~~~}|{{||||}}~~~~~~}|{{z{{{zz{{||||}~~~}|{{{{{{{{{|||}}~~~~~~}|{{{{{zzz{{{{||}~}|{{{{zzzz{{{{{|}~~~~~}||||{{{{{{{{{|}~~}|||{{{z{{{{{{|}}~~~~~~~}|||||{{||}~~~~~~~~~~~~~~}|||||||||}}}}}}~~~~~~~~~~~~~~}||||}|||||||||}~~~~~~~~~~~~~}}|{{{{||||}~~~~~~}}|{zzzzzzz{{{|||}~~}|{{zz{zzz{||||}}~~~}|{{{{{zzz{{{{{|}~~}|{{{{zzzzzzzz{|}~~~~~}}|||{{{{{{{{{|}~~~~}||||{{{{{{{{{|}}~~~~~~~~~~}|||||{||}}~~~~~~~~~~~~~}|||||||||}}}}}}~~~~~~~~~}~~~}}||||||||||}|||}~~~~~~~~~~}}|{{{{{{{||}~~~~~~}}|{zzzzzzzz{{|||}~~}|{zzzzzzz{{|||}}~~}|{{{{{zzzz{{{{|}~}||{{{zzzzzzzz{|}}~~~~~}}}||{{{{{{z{{|}~~~~~}|||||{{{{{{{{|}}~~~~~~~~~~~~~~~~}}}|||||}}~~~~~~~~~~~~}}|||||||||}}}}}}~~~~~~}}}}}}||||||||||}}}}}}~~~~~~~}|{{{{{{{{||}}~~~~}}|{{zzzzzzz{{|||}~~~}{{zzzzzzz{{|||}~~}||{{{{{zz{{{{{||}~~}|||{{zz{{{{{{|}}~~~~~}}}}|||{{{{{{{|}}~~~~~}}}}}|||||||{||}}~~~~~~~~~~~~~~~~~~~~}}}||}}~~~~~~~~~~~~~}}||||||||}}}}}}~~~~~~~}}}}}}||||||||}}}}}}}~~~~~~~}}|{{{{{{{||}}~~~~~~}||{{{{{{{{||||}}~~~}|{{{{{{{{{|||}}~~~}|||{{{{{{{{{{||}~~~}}|||{{{{{{{{||}~~~~~~~~}}}}||||||||||}}~~~~~~}}}}}|||||||||}}~~~~~~~~~~~~~~~~~~~~~~}}}}~~~~~~~~~~~~~~~}}||}}}}}}}}}}~~~~~~~~~}}}}}}|||}}}}}}}~~~~~~~~~~~}}||||||||||}}~~~~~~}}||||||||||||}}}~~~}|||||{{{{||||}~~~~}}|||||{{||||||}~~~~}}}}|||||||||}}~~~~~~~~~}}}}}}}}||||}}}~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~}}}}|}}}}}}}~~~~~~~~~~~~}}}||||||||}}}~~~~~~}}|||||||||}}}}}~~~~}}||||||||||}}}~~~}}}}|||||||||}}~~~~~~~}}}|}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~}}}}}}}|}}}}}}~~~~~}}}}}|||||}}}}~~~~~}}}}}}||}}}}}}}~~~~~~~}}}}}}}}}}~~~~~~~~~~~~~~~~~}~}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~}}}}}}}}}}}}}~~~~~~~}}}}}}}}}}}}~~~~~~}}}}}}}}}}}}}~~~~~~~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}~~~~~~~~~~~~~~}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}~~~~~~~~~~}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}~~~~~~~~~~~~~~~~~~~}}~~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m40.bin b/applications/SprPoP/dist/MUS/m40.bin deleted file mode 100644 index 9f0761f..0000000 --- a/applications/SprPoP/dist/MUS/m40.bin +++ /dev/null @@ -1 +0,0 @@ -|vrpqty~}xurppqtx|{ywusrqqqppppppqqqstuwy|~yurpqsw{}yusqppqstuxz|}~~}|{zxvtsrqqqqrsuwy|~zxusqqqruw{~|zxvutsrrrqqqqqqqqqqqqqqqqrrrsstuuvwyz{}~~|{ywutsrrrrsuvx{}~}|{zzyyxxxxwwxwxxxxxyyyz{{{|}}~~~}|{zywwvutsssrssstuvxy{}~~~~~~~~~~~~~~}}||{{zyyxwwvvutttsssssttuuvwxyz{}~~~~~~}}}|||{{{zzyyyxxwwvvuuuttttttttttuuvvwxyzz{}~~~~}}|||{{{zzyyyxxwwwvvvuuuuuttttttuuuvvvwwxyyz{|}}~~~}}|||{{zzzyyxxxwwwwvvvvuuuuuuuuuuvvvvwwxxyyzz{|}}~~~}}}||{{zzzyyxxxwwwwvvvvvvuuuuuvvvvvwwwxxyyzz{{|}}~~~~}}||{{{zzyyyxxxxwwwwvvvvvvvvvvvwwwwxxxyyyzz{{||}~~~~}}}||{{zzzyyyxxxxwwwwwwwvvvvwwwwwwxxxyyyzz{{{||}}~~~~}}|||{{zzzyyyxxxxxwwwwwwwwwwwwxxxxxyyyzzz{{|||}}~~~~}}|||{{zzzyyyyxxxxxxwwwwwwwxxxxxxyyyzzz{{{|||}}~~~~~}}|||{{zzzzyyyyyxxxxxxxxxxxxxxyyyyyzzz{{{|||}}}~~~~~}}|||{{{zzzzyyyyyyxxxxxxxxxxyyyyyyzzz{{{|||}}}~~~~}}}|||{{{{zzzzyyyyyyyyyyyyyyyyyyzzzzz{{{|||}}}~~~~~}}}||||{{{zzzzzzyyyyyyyyyyyyyyzzzzz{{{{|||}}}~~~~~~}}}||||{{{{zzzzzzzzyyyyyyyzzzzzzzz{{{{||||}}}~~~~~~}}}}|||{{{{{zzzzzzzzzzzzzzzzzzzz{{{{{||||}}}~~~|zyyz}~}{zyyyz|~~}|{zzyyyyyyyyyyyyz{{|}~}{zyyz{}}{zzyyyz{{|}~~~~}||{zzzyyyzz{|}~~|{zzyyz{|}~}||{{zzzzzzzyzzzzzzzyyyzzzzzzzz{{||}}~~~~}|{{zzzzzz{||}~~~~}}}}}|||||||||||}}}}}}~~~~~~~}}||{{{zzzzzzz{{||}}~~~~~}}}}||||{{{{{zzzzzz{{{{||}}~~~~~~~~~~}}}}}|||||{{{{{{{{{{{{{{{{{|||}}}~~~~~~~~}}}}}}|||||||{{{{{{{{{{{{{{{{{||||}}}~~~~~~~~~}}}}}}||||||||{{{{{{{{{{{{{{{|||||}}}}~~~~~~~~~~}}}}}}||||||||{{{{{{{{{{{{|||||||}}}}}~~~~~~~~~}}}}}}||||||||||||||||||||||||||}}}}}~~~~~~~~~~~}}}}}}}|||||||||||||||||||||||}}}}}}~~~~~~~~~~}}}}}}}|||||||||||||||||||||}}}}}}}}~~~~~~~~~~~}}}}}}}}||||||||||||||||||}}}}}}}~~~~~~~~~~~~}}}}}}}}}}|||||||||||||}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}||||||||}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~|vrpqty~}xurppqtx|{ywusrqqqppppppqqqstuwy|zurpqsv{}yusqppqstuxz|}~}|{zxvtsrqqqqrsuwy|~{xusqqqrtwz~}zxvutsrrrrqqqqqqqqqqqqqqqrrrrstuuvwyz{}~~|{ywutsrrrrsuvx{}~}|{zzyyxxxxwwxwxxxxxyyyz{{{|}}~~~}|{zyxwvutsssrssstuvxy{}~~~~~~~~~~~~~~}}||{{zyyxwwvvutttsssssttuuvwxyz{}~~~~~~}}}|||{{{zzyyyxxwwvvuuuttttttttttuuvvwxyzz{|~~~~}}|||{{{zzyyyxxwwwvvvuuuuuttttttuuuvvvwwxyyz{|}}~~~}}}||{{zzzyyxxxwwwwvvvvuuuuuuuuuuvvvvwwxxyyzz{||}~~~}}}||{{zzzyyxxxwwwwvvvvvvuuuuuvvvvvwwwxxyyzz{{||}~~~~}}||{{{zzyyyxxxxwwwwvvvvvvvvvvvwwwwxxxyyyzz{{||}~~~~~}}||{{{zzyyyxxxxwwwwwwwvvvvwwwwwwxxxyyyzz{{{||}}~~~~}}|||{{zzzyyyxxxxxwwwwwwwwwwwwxxxxxyyyzzz{{|||}}~~~~}}|||{{zzzyyyyxxxxxxwwwwwwwxxxxxxyyyzzz{{{|||}}~~~~~}}|||{{zzzzyyyyyxxxxxxxxxxxxxxyyyyyzzz{{{|||}}}~~~~~}}|||{{{zzzzyyyyyyxxxxxxxxxxyyyyyyzzz{{{|||}}}~~~~}}}|||{{{{zzzzyyyyyyyyyyyyyyyyyyzzzzz{{{|||}}}~~~~~}}}||||{{{zzzzzzyyyyyyyyyyyyyyzzzzz{{{{|||}}}~~~~~~}}}||||{{{{zzzzzzzzyyyyyyyzzzzzzzz{{{{||||}}}~~~~~~}}}}||||{{{{zzzzzzzzzzzzzzzzzzzz{{{{{||||}}}~~~}{yyy{}~|{yyyz{}~}|{zzyyyyyyyyyyyzz{{|}~}{zyyz|~~|{zyyyzz{{|}~~~}}|{{zzyyyyzz{|}~~}|{zzyzz{|~~}||{{zzzzzzzyzyzzzzzyyzzzzzzzz{{{||}}~~~}||{{zzzzzz{|}~~~~}}}}}|||||||||||}}}}}}~~~~~~}}}||{{{zzzzzzz{{||}~~~~~~}}}}|||{{{{{zzzzzz{{{{|||}}~~~~~~~~~}}}}}}|||||{{{{{{{{{{{{{{{{{|||}}}~~~~~~~~}}}}}}||||||{{{{{{{{{{{{{{{{{|||||}}}~~~~~~~~~}}}}}}|||||||{{{{{{{{{{{{{{{||||||}}}}~~~~~~~~}}}}}}}||||||||{{{{{{{{{{{{|||||||}}}}~~~~~~~~~~}}}}}}||||||||||||||||||||||||||}}}}}~~~~~~~~~~~}}}}}}||||||||||||||||||||||||}}}}}}~~~~~~~~~~}}}}}}}|||||||||||||||||||||}}}}}}}~~~~~~~~~~~~}}}}}}}}||||||||||||||||||}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}|||||||||||||}}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}|||||||}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~|vrpqty~}xurppqtx|{ywusrqqqppppppqqqstuwy|zurpqsv{}yusqppqstuxz|}~}|{zxvtsrqqqqrsuwy|~{xusqqqrtwz~}zxvutsrrrrqqqqqqqqqqqqqqqrrrrstuuvwyz{}~~|{ywutsrrrrsuvx{}~}|{zzyyxxxxwwxwxxxxxyyyz{{{|}}~~~}|{zyxwvutsssrssstuvxy{}~~~~~~~~~~~~~~}}||{{zyyxwwvvutttsssssttuuvwxyz{}~~~~~~}}}|||{{{zzyyyxxwwvvuuuttttttttttuuvvwxyzz{|~~~~}}|||{{{zzyyyxxwwwvvvuuuuuttttttuuuvvvwwxyyz{|}}~~~}}}||{{zzzyyxxxwwwwvvvvuuuuuuuuuuvvvvwwxxyyzz{||}~~~}}}||{{zzzyyxxxwwwwvvvvvvuuuuuvvvvvwwwxxyyzz{{||}~~~~}}||{{{zzyyyxxxxwwwwvvvvvvvvvvvwwwwxxxyyyzz{{||}~~~~~}}||{{{zzyyyxxxxwwwwwwwvvvvwwwwwwxxxyyyzz{{{||}}~~~~}}|||{{zzzyyyxxxxxwwwwwwwwwwwwxxxxxyyyzzz{{|||}}~~~~}}|||{{zzzyyyyxxxxxxwwwwwwwxxxxxxyyyzzz{{{|||}}~~~~~}}|||{{zzzzyyyyyxxxxxxxxxxxxxxyyyyyzzz{{{|||}}}~~~~~}}|||{{{zzzzyyyyyyxxxxxxxxxxyyyyyyzzz{{{|||}}}~~~~}}}|||{{{{zzzzyyyyyyyyyyyyyyyyyyzzzzz{{{|||}}}~~~~~}}}||||{{{zzzzzzyyyyyyyyyyyyyyzzzzz{{{{|||}}}~~~~~~}}}||||{{{{zzzzzzzzyyyyyyyzzzzzzzz{{{{||||}}}~~~~~~}}}}||||{{{{zzzzzzzzzzzzzzzzzzzz{{{{{||||}}}~~~~|zyy{}}{zyyy{|~~}|{zzyyyyyyyyyyyyz{{|}~}{zyyz|~~}{zzyyyz{{|}~~~~}|{{zzzyyyzz{|}~}|{zzyyz{|}~}||{{zzzzzzzyzzzzzzzyyyzzzzzzzz{{||}}~~~}}|{{zzzzzz{||}~~~~}}}}}|||||||||||}}}}}}~~~~~~~}}||{{{zzzzzzz{{||}~~~~~~}}}}||||{{{{zzzzzz{{{{{||}}~~~~~~~~~~}}}}}|||||{{{{{{{{{{{{{{{{{|||}}}~~~~~~~~}}}}}}|||||||{{{{{{{{{{{{{{{{{||||}}}~~~~~~~~~}}}}}}||||||||{{{{{{{{{{{{{{||||||}}}}~~~~~~~~~}}}}}}||||||||{{{{{{{{{{{{|||||||}}}}}~~~~~~~~~}}}}}}||||||||||||||||||||||||||}}}}}~~~~~~~~~~~}}}}}}}|||||||||||||||||||||||}}}}}}~~~~~~~~~~}}}}}}}|||||||||||||||||||||}}}}}}}~~~~~~~~~~~~}}}}}}}}||||||||||||||||||}}}}}}}~~~~~~~~~~~~}}}}}}}}}}|||||||||||||}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}||||||||}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~yu}usxwz{{rut}y||wvvz|q{x|~zw{~~~~x|suxgnt~|zgxzhuv{zwvp~zlh{uh~powpw~}zw|q~m|vpnys}}pgqsj{{|~}pw~zqgt|gvzi}rsx{w{{wy{zogklvyi}p}t}{}w~so|zthl~omltvqu{v}{zqfiuvjr}jo{ox|uztwyiswsjhzwh}vl~rrvvy|gio|}l~sgq|}rlyrtuqynjvuofq~lqlwvqsxtwnglww{llw}}zhruoupsvgqvsji|xj~xmtqutz~~uv{jlryns|}|nlxpsspwpgtrnhtnsowzrrvs}zq{tinwwox~}zisvptrqxiltqkj{|j||mvrsuv}|wq}qmr|tqy||qjyqrtqswgnqniptmupurut|{ru{lou}qt}|{kpxpttpwrgqpliwntpv|truu}zzpw{os|}ptzyuhvuqttpypgomjl||kznz|tsuz}|ywnzxpvypv{zriyrsssqzlipmjoxl~|nzstu~|~zwun}urx}wqx{{om{pustq{kjmkjtunyo{uvw~{{yvum}xv|{vpvxwlozpussq{jkmkkvspxp{uvw||savw{mwsvphndp}wfffwbf~yyzrz|px{||zxx}~heefl~gsve|momwvuh{kyyn|~rgtgx|rrvr}~~|`yq^on`Zqzq{vzoryuclaiiyt[tplrtoky{w}txrufhohamyztszm~nx{msdryvvny|xpawxugs{xxmpnooto|myyq}vtvs]u~rhxt|uru}{urowlyq}{s|zwoyw~~{}}}ts^k]jvxzgqutumvvxzulnlrk{{~}w}q}y}}xlocmfq~vw{wxlpuclrq~{~sbrp|wnx}{z~z~s{oivigqemv|r|qqyohinwozri~wrwrm|yx{~xut{p_pcivx~v}tjljl~rpym^spppq}wu|~}}}w|{i~xyyz|wyqsiojtrttjfofoxahxq{}}zvpuu~uq~wwy}snod{rrubfseqy]e}}qvv~x~~xwrewo]p{o{rsiqpxh_pkets`m|~tuvsvowzouodhn_n}wx{vukkqneot\n}zw}urwmsxy~{tuuaru`yu~v}}~tiupd{s\uuxxusrxsx~x{~{{vxfvpfy{uwyqy~xn~}sses}v{pkzor~w}z~{|yxzh}zqypzbkx{ylk}|xuo}qyzo{xs~|w~}{zysh|{r~|~~fsuppif|~kng}xzwu|z|n{wv}xtxuzuhnkqmwhryzxuyz|k|wwxyly{~tuvmy}v~xitdMirs}xvzZapX^[cy^lkgvpz|xt{yz}pzsvbt~xlnzv{pqkhlgshpppy|vvsmvrnovmjdfk~|^jrog|~p|iru{o~ks|qxntv}~yk{spjuwxkspqU]zlgplywn||xyz~n{}hqp}vquy|nmqb^vwnc`eeY}z|xusvygtyrpmsnzv{uutmocbtQbu`vzz{{{ozessrksjbpkotjtr|~}u~irfeuu~wymuvrnZuyzwceoytj~sknjw|vpulyy|w|u~szjrodf_ivysy|vrmy{pwnptyrlrl|py{z~sstdjoneZlpnxy|x|cqku~xzxtqxu~kx{v~|ttg`bg^pwmtymozhdi\wuqrnywx~{|yv|}|t|krn_ghszwywd]`epimgnmojq{o~zrtv{mz||vvxz{x|wjgcbmegh`ineyyr`y}qyt~xuuvtup}z~os~pokgklrfck_apv~fiwosuwxtofpnenmtvtnhsqqxnkwmloop^dpilw~uqd^mjlnv{wxjo}khkjt|psu|xiryhvsk}{yutysrwijkv~ru||umlmllartx|jxtyqq~{z{|ysvusuleqyyxe|xxh{}uy|uu|~~kiuzrfyvybSlncUYt_hl}s{|j|~vjvze{~~y~~l{grldwxpeRhd\X_pdflk~fqu|vpnWbcxlwywerpp[bwacb`vf^i`^ntul~{rv~x{z}}|qz{}{xz|urrZlsk\jlm{ltuprtgspyx}y~|z~gmhigS\`b\Z\UF^j[Sqt^w|s{~~}|~}|{zveu|jz}hiquqUdiekovg^yuji~w{~vszxr}d}seurjVgj[hk]YQ`g]Snqft~g{|yp}~x{vz~krwp{wuxltmerqmh\onoew}pw}z{x}yt{utgstfT]chagysrv{mntivo{ltwuzt}~rwl^[dwxtzq}~wnmkjitceicndYa|l`{q~{mnlipriq~v}vyqq}}}t}toj|sfo}hw~vbkcw{}~|}tu}ohusruki|m_`ryp~yvq}tsrwymkwqtp||wn~tffyy~zmuq`nthc^We\N\c_Tpqk}wt}y}zzvrswwkgd_d[\xym~|tyuuss}vkw}tnvwn|ovquft~zn{mbqjeiakrpuzzxtv|mq}uxqhc``W\jcilimbewkjztzlriysv|u{{tyxk|}u|s~zxkit{pyyu|qmsuuzw|~q{trwux~tjvqpzypr{|}q}}nyrvqjl~nx|vyytuqhrsyzxwv|xbkppbi\c_Wfmofptgwznlzxzzzpupwy}qsrzhqpelgivsj|nxtqv}z|{yw}rqtvkcicxqs}ru~esuqkunklkvulzjxvuswyzwwywonmldkji]lhb]^kuk`uzn|{v|{wy|yn{{ufwsqlgozupxwy}|v{{u{mqr~uqsusv}}|{}z|w~ojy|vhvosutwuty{wxoutp{yq|}ysus|ihmqkjpgockurdlrr}x}y~}pq}}{ty|~u~ysqusnrxvltvyzypw}iu}~|r{|ygslzxqtxrm}{n{mrxrvv}wl|~y}~s|}pwpxosyur~}zy}zhsrvgqukjhpwqf{zfq|~{y}{|~wryt{z|z~|~s~~isyuvrutki{yoq}x~q~~rxn~~{wuvu}l}{mrtknpntpd{}rzz~}~|}}u}q|ozwvztr~p}vwilpnrkqwtmw~wu}~}u}~sxpxutpr|{ykzr{lfsputx||y|z~~{~|~{w|syywtlft}u{|ym}x{}zmypnuw{}uvz|vz{xyx{v{|yytqmir}|vzy{wwv|v~qytmu|xq}|y|{yxpwq|wrlx~|mvzx{x}~y~wvtttv|uzxzxxum}{usdpelpqmgglx{}y|}{|}vsw}sx{|yvvr|yzisrn~~yussu|mzwz}sy}sov|tyxy}z}~|yqp{unjqhx}{wtr}~{}~ttxpt~~y||{~z|t~uyyuom}stfunv~wy|vzp|z~~yxqxw|uuznmqu}ku~qx{tsp{~o{|}y{yry}t|}v~tqrt{pwwx}wws}}nnsmxzzxpn||}zzy~r}py{y{|wwvvwz{~}rnys{~{ts~{y~nu~|yyy|tuxzxqvix{rpnqxyy~{x|~rkzqx~{y~w{}~vu{}{w|o~zuux~~x}zwuztlcqlszyupnxy{~wzur}{{y}vsy|{v}{{{ukvov}~}zx{z|upxy}urpxqlsvxu|rxuw|{zu}zy}zwohvsz{yvu}|xwxw|||yxy{xzu{wswvvmfrmtwvvsq{~~}v}~oox|~{xwx|{||||x~||pkusy{|{wy}~uloy|yvyw~}}x}{txtsv{}t~x~}{wzjlut|~}{x~|szy~xzy{usuz{yywz|z|ttfmoowyvqnuy}|~|s|zzy~yy|yz}}~zxktsrxywtsz{x~zt|t|s{xtyqyyutwz}y~~w|~~|uqeoopvwvuw~}|}x~xyyx~vyuvy{}yuz{z{tpk`jjmrrpor{xw|}yx{vw~{{x|yz}~w{~~xtqfnorxywuxty|ysrysuy|{tryt|{utwz}y~zrt|z{~uqpfnqrx{{{y~{yxxz~xu|w~wuuxzw|~wsrzxy|vonclpqx||{|~ux}~xvvzy~{x~y~xvwz|{{zu~wxntyx}ww|zus~q~{tyyvrxtt}{tqsvwvzy}p{~yyooww{}~~z}zwxw{zxswvswrswzzz{s|vvniqpty{yx}}~wz}{vzzxuwyxsuyuyxz}|~{vsksuuz|}|}vx|ysq|o|sptusptqw~ywy|~}v~tsiltty|}}~|y|}vrzr{}rvwxuyxv||}s|~tslgpruz}}~uw{~~xtvyuvwyywy|x}{|~~||vu~}~vrofntvz}}}}tty}yutuzw||{{z|xy|}{x{r||}~ysrhkrty}~xuy|~yuwy}~|z~{|~yxyzz}xp{~~{rqiisuw{~{vz~|y|y|}}z|yx|xwyyyzz}ry}}}}trjgquy|~wz{~}xz{{y{zw}xvxyzz{{qw~~~~|trlgpux{|}}~z}{}}|xyzywyyv~yxyyyy|{rv}}~~trlemsuy{||}|}~yzzvwyywxxu~xvwxyy|xsv}}~~|spjdkruy|}}||~yy}{uwxywyyv~zwwwxy{yrv|||}zqoidmsvy{}z|}xz{|uxyyxywu|xwxxxyyyou{{|}zrpidmtx|~zz|w{z}uwxywzxv}xwwvvxvy~nw|}}}xrqjgpvz~y{|w~wtyyxwyvxzvuuuuyt}|ny|~ysqijsy}~y|{vw{txyyxyuz~xttuuv}syr}~yurjnvz~}z}{wxxuxyxxwsz}xuutuvuvuzvqjovz}}~}|z~zxywtwwvvvs{|wvvvwxwwxyvpimuz}~}}||z}zxyttvwvwvt{~ywxwxzzwzwuoinuy{||}}|{~~yw}ytuwwvvvu}~zxyyy{zvywsmgmtx{||}|}z}~yvyttwyxxwv~|yzyyzwvw~wsngksy|}}}||y|zvyxuxzzzyw~|yyxxyvvu~wsngksx{||}z}y|{wyzvz{|{{x~{xxxwwtxr~~xsohiqx|}|}|y{~xzw{|||{x{|xwvvv|t~}p|~ytpjhpvz}}~}{|{}wz{|zzwy|xvvuuywypy~{tqjgnuz}}}~}}|}}wxz|{zww~}xvvuuw{trv~{vrlgksy|}}}{}y{|vyzzyxu{yvuttu}rxo|~~}wsnghpw{}}~}~z{uxyzyxvwzwuuttxuwnx}~}}ysoifluz~~}|z}wvyzzyvu|}xttssu|rrr||}|ztqlgjsy~}}xy{uyzzxwu{}xutsss|~o}un{|}|{vrmgirz|}xytxy{ywtx~yussqrxqyzmy|}|{xtpjiqz}|y}{uwyzxwuv~yusrqrvtu|lw}~~}zurljqy}|z||uwyzywuv}yussqruut~nw~~~{wsmkpy~|~{z~~vwxzywtu|zusrqsvxtpv~|wtnkox~||y}vvxyxwuu|zvttssvztqv~|wsmjox~~|~{y~}uvxzywtu|{wuustwyuqw~}{wsmjow}~|zz}uwxzyxuw~{xuutuxxvow~}zvrlinw}~|z{|uxz{zyvx{xvuttxuw~oy~~~|yuqkiox~}}y~{vz{|{ywzzwutsu{s{zn{}~}|xtojjqy~}}~z{w{|}{yw|~yvutsu}q}vp|}~|{wsnikrz~|}{}}y|}}{xw}}xutssu|qrr}}}|zvsmiksz~{||y|}|zxx}|wutrsu{qqs}}~|zvrlhksz~{|zy|}|zww}{wttrsuzrpt}}}{zvqlhksz~~~|}zz{}{yww}zvtsrsuyr~ot|}}{yuqkhksz}~~{|zy{|{yww|{wttrrtzq~qr|||{yuqlhkry}~~|{~{x{||ywv|{vtsrrt~{p}rq|||{yvrlhjqy}|{}}x{||ywv{{wtsrqs|}o|so{||{zvrmijqy~}{|w{||zwuy|wtrqqrypyxmy{|{zwtokjpx~|}wz{|zxux~}xtrrqrvst{mw|}|{yvqlkow~~}xy{|zxuv|~yurrpqt~xq}os|}}|zwsnknu|~z|}wz{{yvuyzvsrqqs{~p{vo{}}|{yupllrz||vy{{ywuw}}xtsrqrwtu|nw}}}|zvrmkpx~~{~yxz|zxuv{zvssrrt~zq}qr|||{zwtoknu|~z|}wz{{ywv{{wtsrrs{~o{to{|}{{xtokms{{{w{||zxvy}xusrqrxqxymx{|{zxupllqy|~~x{|}{ywy~~xusrqrvst{lv{|{zyuqlkpx~~~y{|}{ywx~~yusrpqutr{mt{{{zyvrmkpw~~z{|}{ywx}~yurrpqt~uq{msz{{zyvrmkov}}{z|}|ywx|~xurqpqs}vp{mrz{{zxvrmkov}~z{|}{yvw}~xtrqpqt~uq{mszzzyxuqmkov}~z{|}{xvw}}xtrqpqt~tqzltzzzyxuqlkow}y{|}{xvx}}wtrqpqu~rsykuzzzyxuplkpw}~~y{|}zxvy~{vsqqpqw}ovvlwzzzywtoklqy~}}y|}|zwvzzvsqpprz|nysnyz{yyvsnkmsz~}~~z|}|yww{yurqppr|yo{ppzz{zyvsnknt{}}|z|}|yww{~yurqpqs|xo{oqz{{zywsnlnu|}}|z|}{yvw{~xurqpqs}wp|or{{{zzwsnlou|~~{{|}{yww|~xtrrqqt~wq|os{{{zzwsnlnu|}}|z|}|yww{~yusrqrt}yp|pr{{{zzwsnlnu{|}}z|}|yww{zvsrqrt}zp|qq{{{zzwsnlntz|}~z|}|zxx{{vtsrrs{|o{soz{{zzwtolmsy~|}~z}}}{ywz|wtsrqsy~pxwmxz{zzxupllqx}~z}}~|ywz~}xtsrqrvstymuzzzyxuqmlpw|||}~|zxy}~yusrqru}vp{oqyzzyxvrnlou{}~{|}|zxx{{vsrqqsz{nxtmwyzyywtolmry~~{|}}{yxy~|wtrqprv}rsylsyzyywuqmlpw|}{|}{ywx|~yurqpqs{wnzooxyyyxvrnmou{~~z|||ywwzzusqppryzmxrmvxyyxvsomntz~~z{{{yxwy~{vsqpprx{nuvktxyyxwtpmnry~{{{{zxwx}|wsqppqv|prxlrxyyxwuqnnrx~|{{|zxwx||wtqppqu|qqylqwyyxwuronrx~|z{{zxwx||wtqqpqu|spympwyyyxvronqx}}z{{zxwx{|xtrqpqt{soynpwyyyxvsonqw}}z{{zxwx|}xtrqprv|rpynpwyyyxuronrx}}z{{zxwx||xtrqqrw|rrymqwyyywuroorx}}{{{zxwy}|xusrqsw|qsymrwyyywuqoorx}|{{{zxxz~|wtsrrty{ovvltxyyxwtpnosy~~{|||zyx{{wtsrru{yoxtmuxyyxvsonoty~}|||{zyy|{wtsrsv|vozrnuxyxwuromotz~~|||{zyz}zwtsrtw|upzqnuxxxwtrompuz~~}|}|zyz}zwtsrtw|tpzpovxxxwtqnmpuz}~}}}|zyz}~zwtsrtw|tpzpovxxxwtqnmotz}~}}}|zyz}~zwtsrsv|upyqnuwxwvtqnmoty}~}|||zyz}{wusstw|uoyrmtwxwvtqnmosy}}}}||zyz|{xusssv{wnxsmsvwwvtromosx|}~|||zyy{|xusrsuzynuvlqvwwwuspnnrw|}|||{yy{~|yusrrtxzpsxmpuwwwuspnnqv{~}||{zyz}}yvtrrtx{rpypmsvwwvtqonquz~}|{{zyy{~{wusrsvzwnwtlquwwwuspopty}~|{{zyyz}|xusrrtxyorxnotwwwvtqoprw||{{zyxy|}yvtrrswzsoxqmsvwwwurpoqv{}{{zyxy{~~zwtsrsvzvnwtmrvwwwusqpquz~~|{zyxyz}~{xusssvyxnuwmptwwwusqpqty}|{zyyxz}|xvtssuyyorxnotvwwvtrppsx||{{zyyz}|yvtssuxypqxonsvwwvtrppsw|}{{zyyz|}yvtsstxzqqxpnsvwwvtrppsw|}|{zyyz|}yvtsstxzqpxpnsvwvvtrppsw{}|{zyyz}}ywtssuxypqxpnruvvusqppsw{}|{zyyz}|yvtssuxypqxonsuvvusqppsw{}|{zzy{}|yvtssuyxorwnnsuvvusqopsw{}|{zzz{~|xvtstvywnuvmosuvutrpoptx|~}||zzz|~~{xvtstwzunvtmptvvutrpoqtx|}|{zz{}~{xvttuxzsowrmqtuuusqooquy|}|{{z{}~{xvttuxyrpxrmqtuutsqooquy|}||{z{}~zxvttvxyrpxqmqtuutsqooquy|~}||{z{}}zxvttvyyqpxqmqtuuusqooquy|~}||{z{}~{xvutvxyrpxrmqtuuusqppquy|~~}}|{{{}~{xvuuvyzspxsmpsuutsqpoqtx|~~~}|{{{}~{ywuuvxztowtnpsuuusrppqtx{~~}|{{{|~|ywuuuxzvouvnosuuutrppqsw{}~}|{{{|~}zwvuuwywpswpnrtuutsqppsvz}~}||{{|}}zxvuuwyxqqxrnpsuutsqppruy|~}|{{{}~~{ywuuvxytovunortutsrqpqtx{~}|{{{|}~|ywuuuwyworwpnqtuutsqpqswz}}|{zz{}~}zxvutvxxqpwsmpstutsrqqsvz}}|{{z{|~}zxvutuwxsovtnortuutrqqruy|~|{{zz|}}{yvuuuwxtntvonqstttrqqrux|~}|{zz{}~~~{ywutuwxuorwpnqstttsrqrtx{~}|{zz{|~~~|ywutuvxvoqwqnpstttsrqrtw{~}|{zz{|~~~|ywuttvxvoqwqmpstttsrqrtw{~}|{zz{|~~~|ywuttvxwppwqnpstttsrqrtw{~}|{zz{|~~~|ywvuuvxvoqwqnprtttsrqrtw{~}|{zz{}~~~|ywvuuwxvoqwqnpstttsrqrtw{}}|{{{{}~~}|ywvuuwxuorwpnpstttsrqrtw{~}|{{{|}~}{ywvuvwxtotvonqsttsrqqrux{~}||{{|~}{ywvuvxxsovuoorsttsrqqrux{~~}|||}~}{ywvvwywqpvtnorstsrqqqrux{~~~}|||}}{ywvvwywqqwsnorsssrqqqsux{}~~}||}~}{ywvvxywqqwsnorsssrqpqsux{}~~}||}~}{ywvvxywqqwsnprsssrqpqrux{}~~}}|}~}{ywvvwywqqwsnorsssrqpqrux{}~~}}|}~}{yxwwxywqpvtooqrssrqpqruxz}~~~}|}~~|yxwwwxwrpvuooqrssrqqqrtwz|~~~}||}~~|zxwvwxxsotvpnprssrrqqrtwz|~~}||}~~|zxwvwxxuosvqnprsssrqqrsvy|~~}||}}~~|zxwvvwxvpqvsnoqrssrqqqsvx{}~}|||}~~~}{ywvvwxwrouupnprssrrqqsux{}}||||}~~}{yxvvvwwtorvqnoqsssrrrrtwz|}||{|}}~}|zxwvvwwvppvsooqrsssrrrtvy|~~||{{|}}}|zxwvvvwvqouuooqrsssrrrtvy|~~}|{{|}}}|{ywvvvwwrosvqnprssssrrtvx{}~}|{{||}}|{ywvvvwwsorvropqssssrrsux{}}|{{||}}|{yxvvvwwtorvrooqssssrrsuxz}}|{{||}}}{yxvvvwwtoqvsooqssssrrsuwz}}||{||}}}{yxwvvwwupqvsooqssssrrsuwz}~}|||}}}}{zxwvvwwtoqvsooqrsssrrsuwz|~~}|||}}}}{zxwvvwwtorvrooqrssrrrsuwz|~~}|||}}}}{zxwvwwwsorvrooqrssrrrsuwz|~~}||}}~~}{yxwvwwvrotuqopqrsrrrrsuxz|~~}}}}~~~|{yxwwwwvqpuupoprrrrrrrsuxz|~~}}}~~~~|{yxwwxxuqqutpopqrrrrqrsuxz|~~~}}~~~~|{yxwwxwupqutpopqrrrqqrsvxz|~~~}}~~~~|{yxwwxwupqvtoopqrrrqqrsvxz|~~~~}}~~~~|{yxwwxwupqvsoopqrrrqqrsvxz|~~~}}~~~~|{yxwwxwuqqutpopqrrrqqrsuxz|}~~~~~~~~~}{zxxwxwuqqutpopqrrrqqrsuwz|}~~~~~~~~~}{zxxwxxuqpuupopqrrrrrrsuwy{}~~~~}~~~~}{zyxwwxvrptuqopqrrrrrrsuwy{}~~~}}}~~~}|zyxwwxwspsvrooqrrrrrrstwy{}~~~}}}~~}|zyxwwwwtpqutpopqrrrrrstvxz|~~}}}}~}|{yxwwwwuqptuqopqrrrrrstvxz|~~}}}}}}|{zxwwwwvspruspoqrrrrrstuwy|}~}}|}}}|{zxwwwwvtpqutpopqrrsrssuwy{}~}|||}}|{zywwvwwuqptuqopqrsssssuvy{}~}}||}}|{zyxwwwwuqpsuropqrrsssstvxz|~~}}|||}||zyxwvwwuroruspoqrrsssstvxz|~}}||||||zyxwvvwvrpruspoqrrsssstvxz|~}}||||||zyxwvvwvspquspoprrsssstvxz|~~}||||||zyxwvvvvspquspoprrsssstvxz|~~}|||}||{yxwwwwvspruspopqrrssstvxz|}~}}|}}||{yxwwwwurpruspopqrrssstvxz|}~}}}}}||{yxwwwwurpruspppqrrrsstvxz|}~}}}}}}|{yxwwwwuqpsurppqqrrrrstvxz|}~~}}}}}|zyxwwwwtqptuqopqrrrrrstvxz|}~~~~~~}}|zyxxwwvtqqutqopqqrrrrstvxz|}~~~~~~}}|zyxxwwvtqqutqopqqrrrrstvxz{}~~~~~~~~}}|{yxxwwvsprutqopqqrrrrstvxz{}~~~~~~~~~}|{yxxxwvsprutqopqqrrrrstvxz{}~~~~~~~~~}|{yxxwwvtqqutqppqqrrrrstvxy{|}~~~~~~~}|{zyxxwvtqqttqppqqrrrrstvwy{|}~~~~~~~}|{zyxxwvtqqttrpppqrrrrstuwy{|}~~~~~~~}|{zyxxwwurqsurpppqrrrrstuwyz|}~~~~~~}}|{zyxwwwurpsuspppqrrrrstuwxz|}~~~~~}}}|{zyxwwwusprutqppqqrrrssuvxz{}~~~~}}}|{zyxwwwvtqqturpppqrrrssuvxz{}~~}}}}|{zyxwwwvurprusqppqrrrsstvwy{}~~}}}||{zyxwwwvuspqttqppqrrssstvwy{|~~}}}||{{yxwwvvusqqttrppqrrssstuwyz|~~}}|||{{zyxwwvvtqpsusppqqrssstuwxz|}~}}|||{{zyxwwvvtqprusqpqqrssstuwxz|}~~}}|||{{zyxwvvvtrprtsqpqqrssstuvxz|}~~~}|||{{zyxwwvvtrprttqppqrssstuvxz{}~~}|||{{zyxwwvvurprttqpqqrssstuvxz{}~~}}|||{zyxwwvvtrprttqppqrrsstuvxz{}~~}}|||{zyxwwvvtrprtsqppqrrsstuvxy{}~~}}}||{zyxwwvvtrprtsqpqqrrsstuvxy{}~~~}}||{zyxwwvvtqqstsqpqqrrsstuvxy{|~~~~}}}|{zyxwwvusqqttrppqqrrsstuvxz{|}~~~~}}|{zyxxwvusqqttrppqqrrrstuvxy{|}~~~~}}|{zyxxwvusqrttrppqqrrrstuvxy{|}~~~~~}}|{zyxxwvusqrttrppqqrrrstuvxy{|}}~~~~~}|{zyxxwvusqrttqppqqrrrstuvxy{|}}~~~~~}|{zyxxwwusqrttrppqqrrrstuvxyz|}}~~~~}|{zyxxwwusqrttrppqqqrrstuvwyz|}}~~~~}|{zyxxwwusqqttrqppqqrrssuvwyz{}}~~~~}|{zyyxwwvtqqstsqppqqrrsstvwyz{|}~~~}}||{zyxwwvtrqrtsqppqqrrsstvwxz{|}~~~}}||{zyxwwvtrqrttrqpqqrrsstuwxz{|}~~~}}||{zyxwwvusqqstsqpqqrrsstuvxy{|}~~~}}||{zyxwwvutqqrtsqqqqrrsstuvxy{|}~~}}||{zyxwwvvtrqqttrqqqrrsstuvwyz|}~~}}|{{zyxwwvvusqqstsqqqrrsstuvwyz|}~~}}|{{zyxwwvvusqqstsqqqrrsstuvwyz{}~~}}|{{zyxwwvvusqqrtsrqqqrsstuvwxz{}~~}}|{{zyxwwvvusqprtsrqqqrsstuvwxz{}~~}}||{zyxxwvvutrprttrqqqrsstuvwxz{|~~}}||{zyxxwvvutrprttrqqqrsstuvwxz{|~~~}||{zyyxwvvusrqrttrqqqrsstuuwxy{|}~~}||{zyyxwvvusqqrtsrqqqrssttuwxy{|}~~}||{zyyxwvvusqqrtsrqqqrrsttuwxy{|}~~}}|{zzyxwvvusqqstsqqqqrrsttuwxy{|}~~~}}|{zzyxwvvusqqstsqqqqrrsttuwxy{|}~~~~}|{{zyxwwvtrqrstrqqqqrrsstuwxy{|}}~~~}|{{zyxwwvtrqrttrqqqqrrsstuwxy{|}}~~~}||{zyxwwvtrqrttrqqqqrrsstuwxyz||}~~}||{zyxwwvtrqrttrqqqqrrsstuvxyz{|}~~}}|{zyxwwvtrqrttrqqqqrrsstuvxyz{|}~~}}|{zyxwwvtsqrstrqqqqrrsstuvxyz{|}~~}}|{zyxwwvusqrstsqqqqrrsstuvwyz{|}~~~}}|{zyxxwvusqqstsrqqqrrsstuvwyz{|}~~~}}|{zyxxwvusrqrtsrqqqrrsstuvwxz{|}~~~}}|{zyxxwvutrqrttrqqqrrsstuvwxz{|}~~~}}|{zyxxwvutrqqstsrqqqrsstuvwxy{|}~~~}}|{zyxxwvvusqqrtsrqqqrsstuuwxyz|}~~~}}|{zyyxwvvusrqrstsqqqrrstuuvxyz|}~~}}|{zyyxwvvutrqqstsrqqrrsttuvxyz{}~~}}|{zyyxwvvutrqqstsrqqrrsttuvwyz{}~~~}}|{zzyxwvvutsqqrtsrqqrrsttuvwyz{|~~~}}|{zzyxwvvutsqqrssrrqrrsttuvwyz{|}~~}|{zzyxwwvutsqqrssrrqrrsttuvwxz{|}~~}|{zzyxwwvutsrqrssrrqrrsttuvwxz{|}~~}|{{zyxwwvutsqqrssrrqrrsstuvwxz{|}~~}|{{zyxwwvutsqqrssrrqrrsstuvwxy{|}~~}||{zyxwwvutsqqrssrqqrrsstuvwxy{|}~~}||{zyxwwvutsqqstsrqqrrsstuvwxy{|}~~~}}|{zyxxwvutrqrstsrqqrrsstuvwxy{|}}~~~}|{zyxxwvutrqrstsrqqrrsstuvwxyz{|}~~~}|{zyxxwvutrqrsssrqqrrsstuvwxyz{|}~~}|{zyxxwvutrqrssrrqqrrsstuvwxyz{|}~~}|{zyyxwvutrqrssrqqqrrsstuvwxyz{|}~~}|{zyyxwvutrqrsssrqqrrsstuvwxyz{|}~~}|{zzyxwvutrqrstsrqqqrrsttuvxyz{|}~~}|{zzyxwvutrrrstsrqqqrrsttuvxyz{|}~~}|{{zyxwvutsrrsssrqqqrrsttuvwyz{|}~~}|{{zyxwvvtsrqrssrqqqrrsttuvwxz{|}~~}|{{zyxwvvusrqrsssrqqrrsttuvwxy{|}~~}||{zyxwwvutrqrsssrrqrrsstuvwxy{|}~~~}||{zyxwwvutsqqrssrrrrrsstuvwxyz|}~~~}||{zyxwwvutsrqrsssrrrrsstuvwxyz{}~~~}||{zyxwwvutsrqrsssrrrrsstuvwxyz{}~~~}}|{zyxwwvutsrqqrssrrrrsstuvwxyz{|}~}}|{zyxxwvuutrqqrsssrrrsstuvwxyz{|}~~}|{zyxxwvuutrqqrsssrrrsstuvwxyz{|}~~}|{zyxxwvuutsqqrsssrrrsstuuvwyz{|}~~}|{zyxxwvuutsrqrsssrrrsstuuvwyz{|}~~}|{zyyxwvuutsrqrsssrrrsstuuvwxz{|}~~}|{zyyxwvvutsrqrsssrrrssttuvwxz{|}~~}|{zzyxwvvutsrqrsssrrrrsttuvwxz{|}~~}|{zzyxwvvutrqrsssrrrrrsttuvwxy{|}~~}|{{zyxwvvusrrrsssrrrrrsttuvwxyz|}}~~}||{zyxwvvusrrrsssrrrrrsttuvwxyz{|}~~}}|{zyxwvvusrrrsssrrrrrsttuvwxyz{|}~~}|{zyxwvvusrrrsssrrrrrsstuvwxyz{|}~~}|{zyxwwvusrrrsssrrrrrsstuvwxyz{|}~~}|{zyxwwvusrrrsssrrrrrsstuvwxyz{|}~~}|{zyxwwvusrrrsssrrrrrsstuvwxyz{|}~~}|{zyxwwvutrrrsssrrrrrsstuvwxyz{|}~~}|{zyxxwvutsrrsssrrrrrsstuvwxyz{|}~~}|{zyyxwvutsrrrssrrrrrsstuvvxyz{|}~~}|{zyyxwvutsrrrsssrrrrsstuuvwxz{|}~~}|{zyyxwvutsrrrsssrrrrsstuuvwxy{|}~~}|{zyyxwvuutrrrrsssrrrssttuvwxyz|}~~}|{zzyxwvvutsrqrsssrrrssttuvwxyz{}~~}|{{zyxwvvutsrqrsssrrrssttuvwxyz{}}}|{{zyxwvvutsrqrrsssrrssttuvwxyz{|}~}||{zyxwvvutsrqqrsssrrrsttuvwxyz{|}~~}|{zyxwvvutsrrqrsssrrssttuvwxyz{|}~~}|{zyxwwvutsrrqrsssrrrsstuvwxyz{|}~~}|{zyxwwvutsrrqrsssrrrsstuvwxyz{|}~~}|{zyxwwvutsrrqrsssrrrsstuvwxyz{|}~~}|{zyxwwvutsrrrrsssrrrsstuvwxyz{|}~~}|{zyxxwvutsrrrrsssrrrsstuvvwyz{|}~~}|{zyxxwvutsrrrrsssrrrsstuuvwxy{|}~~}|{zyxxwvutsrrrsssrrrrsstuuvwxy{|}~~}|{zyyxwvutsrrrsssrrrrsstuuvwxyz{|}~}|{zzyxwvutsrrrsssrrrrsstuuvwxyz{|}}|{{zyxwvutsrrrsssrrrrssttuvwxyz{|}~~}|{zyxwvutsrrrsssrrrrssttuvwxyz{|}~~}|{zyxwvutsrrrsssrrrrssttuvwxyz{|}~~}|{zyxwvutsrrrsssrrrrssttuvwxyz{|}~~}|{zyxwvutsrrrsssrrrrrsttuvwxyz{|}~~}|{zyxwvvutsrrsssrrrrrsttuvwxyz{|}~~}|{zyxwvvutsrrrsssrrrrsstuvwxyz{|}~~}|{zyxwwvutsrrrsssrrrrsstuvwxyz{|}~~}|{zyxwwvutsrrrssssrrrsstuvwwxz{|}~~}|{zyxxwvutsrrrrsssrrrsstuvvwxy{|}~~}|{zyxxwvuttsrrrssssrrsstuuvwxyz|}~~}|{zyxxwvuutsrrrrsssrssstuuvwxyz{}~}|{zyyxwvuutsrrrrssssssstuuvwxyz{|}}|{zzyxwvuutsrrrrssssssstuuvwxyz{|}~~|{zzyxwvuutsrrrrsssssssttuvwxyz{|}~~}|{zyxwvvutsrrrrsssssssttuvwxyz{|}~~}|{zyxwvvutsrrrrsssssssttuvwxyz{|}~~}|{zyxwvvutsrrrrsssssssttuvwxyz{|}~~}|{zyxwvvutsrrrrssssrssttuvwxyz{|}~~}|{zyxwvvutsrrrrsssrrssttuvwxyz{|}~~}|{zyxwwvutsrrrrsssrrssttuvwxyz{|}~~}|{zyxwwvutsrrrsssrrrssstuvwwxz{|}~~}|{zyxwwvutsrrrsssrrrrsstuvvwxyz|}~~}|{zyxxwvutsrrrsssrrrrsstuvvwxyz{|~~}|{zyxxwvutsrrrsssrrrrsstuuvwxyz{|}}|{zyyxwvutsrrrsssrrrrsstuuvwxyz{|}~}{zzyxwvutsrrrsssrrrrsstuuvwxyz{|}~}|{zyxwvutsrrrsssrrrrsstuuvwxyz{|}~~}|{zyxwvutssrrsssrrrrssttuvwxyz{|}~~}|{zyxwvutssrrrsssrrrssttuvwxyz{|}~~}|{zyxwvuutsrrrsssrrrssttuvwxyz{|}~~}|{zyxwvuutsrrrsssrrrssttuvwxyz{|}~~}|{zyxwvvutsrrrrsssrrssttuvwxyz{|}~~}|{zyxwvvutsrrrrsssssssttuvwxyz{|}~~}|{zyxwwvutssrrrsssssssttuvwwxz{|}~~}|{zyxwwvutssrrrrssssssttuvvwxyz|}~~}|{zyxxwvuttsrrrrssssssttuvvwxyz{}~}|{zyxxwvuttsrrrrssssssstuuvwxyz{|}~|{zyxxwvuttsrrrrssssssstuuvwxyz{|}~}{zyyxwvuutsrrrrssssssstuuvwxyz{|}~~}|{zyxwvuutsrrrrssssssstuuvwxyz{|}~~}|{zyxwvuutsrrrrssssssstuuvwxyz{|}~~}|{zyxwvuutsrrrrsssssssttuvwxyz{|}~~}|{zyxwvuutsrrrrsssssssttuvwxyz{|}~~}|{zyxwvvutsrrrrsssssssttuvwxyz{|}~~}|{zyxwvvutsrrrrsssrrssttuvwxyz{|}~~}|{zyxwvvutsrrrssssrrssttuvwxyz{|}~~}|{zyxwvvutsrrrsssrrrssttuvwxxyz|}~~}|{zyxwvvutsrrrsssrrrssttuvwwxyz{}~}|{zyxwwvutsrrrsssrrrssstuvvwxyz{|~}|{zyxwwvutssrrsssrrrssstuvvwxyz{|}~}{zyxxwvutssrrsssrrrssstuuvwxyz{|}~}|{yyxwvutssrrsssrrrssstuuvwxyz{|}~~}|{zyxwvutssrrrsssrrssstuuvwxyz{|}~~}|{zyxwvuttsrrrsssrrssstuuvwxyz{|}~~}|{zyxwvuttsrrrssssrsssttuvwxyz{|}~~}|{zyxwvuutsrrrrsssssssttuvwxyz{|}~~}|{zyxwvuutsrrrrsssssssttuvwxyz{|}~~}|{zyxwvvutssrrrrssssssttuvwxyz{|}~~}|{zyxwvvutssrrrrssssssttuvwxyz{|}~~}|{zyxwvvutssrrrrssssssttuvwwxyz|}~}|{zyxwwvuttsrrrrssssssttuvvwxyz{}~}|{zyxwwvuttsrrrrrsssssttuvvwxyz{|}~|{zyxxwvuttsrrrrrsssssttuvvwxyz{|}~}{zyxxwvuttsrrrrrsssssttuuvwxyz{|}~~}|{zyxwvuttsrrrrrsssssttuuvwxyz{|}~~}|{zyxwvuttsrrrrrsssssstuuvwxyz{|}~~}|{zyxwvuutsrrrrrsssssstuuvwxyz{|}~~}|{zyxwvuutsrrrrssssssstuuvwxyz{|}~~}|{zyxwvuutsrrrrsssssssttuvwxyz{|}~~}|{zyxwvuutsrrrrsssrsssttuvwxyz{|}~~}|{zyxwvuutssrrrsssrsssttuvwxyz{|}~~}|{zyxwvuutssrrrssrrsssttuvwxxyz|}~~}|{zyxwvuutssrrrssrrsssttuvwxxyz|}~~}|{zyxwvvutssrrrssrrsssttuvwwxyz{}~}|{zyxwvvutssrrrssrrsssttuvvwxyz{|~~|{zyxwwvutssrrrssrrsssttuvvwxyz{|}~}{zyxxwvutssrrrsssrsssttuvvwxyz{|}~}|zyyxwvutssrrrrssssssttuuvwxyz{|}~~}|{zyxwvuttsrrrrssssssstuuvwxyz{|}~~}|{zyxwvuttsrrrrssssssstuuvwxyz{|}~~}|{zyxwvuttsrrrrrsssssstuuvwxyz{|}~~}|{zyxwvuutssrrrrsssssstuuvwxyz{|}~~}|{zyxwvuutssrrrrssssssttuvwxyz{|}~~}|{zyxwvvutssrrrrrsssssttuvwxyz{|}~~}|{zyxwvvutssrrrrrsssssttuvwxyy{|}~~}|{zyxwvvutssrrrrrsssssttuvwwxyz{}~}|{zyxwwvuttsrrrrrsssssttuvwwxyz{|~~|{zyxwwvuttsrrrrrsssssttuvvwxyz{|}~|{zyxwwvuttsrrrrrsssssttuvvwxyz{|}~}|zyxxwvuttsrrrrrsssssttuvvwxyz{|}~~}|{zyxwvuttsrrrrrsssssttuuvwxyz{|}~~}|{zyxwvuttsrrrrrsssssttuuvwxyz{|}~~}|{zyxwvuttsrrrrrsssssstuuvwxyz{|}~~}|{zyxwvuttssrrrrsssssstuuvwxyz{|}~~}|{zyxwvuttssrrrrsssssstuuvwxyz{|}~~}|{zyxwvuutssrrrrsrssssttuvwxyz{|}~~}|{zyxwvuutssrrrrrrrsssttuvwxyz{|}~~}|{zyxwvuutssrrrrrrrsssttuvwxxyz|}~~}|{zyxwvvutssrrrrrrrsssttuvwwxyz{}~}|{zyxwvvutssrrrrrrrsssttuvvwxyz{|~~|{zyxwwvutssrrrrrrssssttuvvwxyz{|}~}{zyxxwvutssrrrrrrssssttuvvwxyz{|}~}|zyyxwvuttsrrrrrsssssttuuvwxyz{|}~~}|{zyxwvuttssrrrrsssssttuuvwxyz{|}~~}|{zyxwvuttssrrrrsssssttuuvwxyz{|}~~}|{zyxwvuutssrrrrrssssttuuvwxyz{|}~~}|{zyxwvuutssrrrrrssssttuuvwxyz{|}~~}|{zyxwvuutssrrrrrssssttuuvwxyz{|}~~}|{zyxwvuutssrrrrrssssstuuvwxyz{|}~~}|{zyxwvvutssrrrrrsssssttuvwxxy{|}~~}|{zyxwvvutssrrrrrsssssttuvwwxyz{}~}|{zyxwvvuttsrrrrrsssssttuvwwxyz{|~~|{zyxwwvuttsrrrrrsssssttuvvwxyz{|}~}{zyxwwvuttsrrrrrrssssttuvvwxyz{|}~}|zyxxwvuttssrrrrrssssttuvvwxyz{|}~~}|{zyxwvuttssrrrrrssssttuuvwxyz{|}~~}|{zyxwvuttssrrrrrssssttuuvwxyz{|}~~}|{zyxwvuttssrrrrrssssstuuvwxyz{|}~~}|{zyxwvuttssrrrrrrsssstuuvwxyz{|}~~}|{zyxwvuttssrrrrrrsssstuuvwxyz{|}~~}|{zyxwvuutssrrrrrrssssttuvwxyz{|}~~}|{zyxwvuutssrrrrrrssssttuvwxyz{|}~~}|{zyxwvuutssrrrrrrssssttuvwxxyz{}~}|{zyxwvvutsssrrrrrrsssttuvwwxyz{}~~|{zyxwvvutsssrrrrrssssttuvvwxyz{|~~}{zyyxwvuttssssssssssstuuvwxyyz{|~~}|{zyxwvuutssssssssstttuuvwxyyz{|}~}|{zyxwwvuuttsssstttttuuvvwxyzz{|}~~}|{zyxxwvvutttttttttttuuvvwxyzz{|}~~}|{zzyxwvvuutttttttttuuuvwwxyz{{|}~~}|{{zyxwwvuutttttttuuuuvvwwxyz{{|}~~}}|{zyxxwvvuutttttuuuuuvwwxxyz{||}~~}|{zyyxwwvuuuuuuuuuuvvvwwxxyz{||}~~}|{zzyxxwvvuuuuuuuuvvvvwwxyyz{||}~~}|{{zyxxwwvvuuuuuvvvvvwwwxyyz{||}~~}||{zyyxwwvvvuuuvvvvvvwwxxyyz{||}~~}||{zzyxxwwvvvvvvvvvvwwwxxyyz{||}~~}}|{zzyxxwwvvvvvvvvvvwwxxxyzz{||}~~}}|{{zyyxxwwvvvvvwwwwwwxxyyzz{||}~~~}|{{zzyxxwwwvvwwwwwwwwxxyyzz{||}~~~}||{zzyyxxwwwwwwwwwwwxxxyyzz{||}~~}||{{zyyxxwwwwwwwwwwwxxxyyz{{||}~~}}|{{zyyxxxwwwwwwwwwxxxyyyz{{||}~~}}|{{zzyyxxwwwwwwwxxxxxyyzz{{|}}~~}}||{zzyyxxxxxxxxxxxxxyyyzz{{|}}~~~}||{{zzyyxxxxxxxxxxxxyyyzz{{|}}~~~}||{{zzyyxxxxxxxxxxxyyyyzz{{|}}~~~~}}|{{zzyyyxxxxxxxxxxyyyzzz{{|}}~~~~}}||{{zzyyyxxxxxxxyyyyyzz{{{|}}~~~}}||{{zzyyyyxxyyyyyyyyzzz{{||}}~~~}}||{{zzzyyyyyyyyyyyyyzzz{{||}}~~~~}}||{{zzyyyyyyyyyyyyzzzz{{||}}~~~~}}||{{zzzyyyyyyyyyyyzzz{{{||}}~~~~}}||{{{zzzyyyyyyyyzzzzz{{{||}}~~~~}}|||{{zzzyyyyyyzzzzzzz{{{||}}~~~~}}}||{{zzzzyyyyzzzzzzz{{{|||}}~~~~}}||{{{zzzzzzzzzzzzzz{{{|||}}~~~~}}|||{{{zzzzzzzzzzzz{{{{|||}}~~~~}}|||{{{zzzzzzzzzzzz{{{{|||}}~~~~}}}||{{{{zzzzzzzzzz{{{{{|||}}~~~~}}}|||{{{zzzzzzzzz{{{{{{||}}}~~~~~}}|||{{{zzzzzzz{{{{{{{|||}}}~~~~~}}|||{{{{zzzz{{{{{{{{{|||}}}~~~~}}}|||{{{{{{{{{{{{{{{{|||}}}~~~~}}}|||{{{{{{{{{{{{{{{||||}}}~~~~~}}||||{{{{{{{{{{{{{{||||}}}~~~~~}}}|||{{{{{{{{{{{{{{||||}}}~~~~~}}}|||{{{{{{{{{{{{{||||}}}}~~~~~}}}||||{{{{{{{{{{{|||||}}}}~~~~~~}}}||||{{{{{{{{{||||||}}}}~~~~~}}}||||{{{{|||||||||||}}}}~~~~~}}}|||||{{||||||||||||}}}~~~~~~}}}}||||||||||||||||||}}}~~~~~~~}}}}|||||||||||||||||}}}}~~~~~~~~}}}|||||||||||||||||}}}}~~~~~~~}}}}|||||||||||||||}}}}}~~~~~~~}}}}|||||||||||||||}}}}}~~~~~~~~}}}}||||||||||||||}}}}}~~~~~~~~}}}}|||||||||||||}}}}}}~~~~~~~~}}}}}|||||||||||}}}}}}~~~~~~~~~~}}}}||||||||}}}}}}}}}~~~~~~~~~}}}}}||||||}}}}}}}}}}~~~~~~~~~}}}}}}||||}}}}}}}}}}}~~~~~~~~~~}}}}}||||}}}}}}}}}}}~~~~~~~~~~}}}}}}||}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}~~~~~~~~~~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m41.bin b/applications/SprPoP/dist/MUS/m41.bin deleted file mode 100644 index d71a464..0000000 --- a/applications/SprPoP/dist/MUS/m41.bin +++ /dev/null @@ -1 +0,0 @@ -~|seY]dcpp{~xybb^gtzu{}|osnb_t|ueoh{rm{|q}zktfUp}pgqpnqsx}r}}~xlsp}t{ov{qmxy}qsdkhw~ykon~lply|~yy|s]d|_cevt{xsohw|nu|~yslrr{zxyotkddejfo{}pxhq{e^Yzygeheeyyy{vpxtmllhsx~sjlsvutlqnow~zz{}|w}vozqz}}z{u]X[l}hrpeilmksqsbhaheajwovrfkdxs}}}yu|ivpvww~~{zzvx}i\XUfitovek`fibjaeejesow{vupnju|wz|ykkrxw|w~w}~}ytx{zg_XZ[i}~xsoikqsgilhrefozz|ypvxvyxv~|x}vw~}sv~~t~|y|~}rggX`VWlzt{rvjpntmqpsvx}z|w|{|ywz}z|ryp}zw{kzvt~xx|sy}}micaXW]cquuq{nmx}{|{x|~}|~z|{{lovqrptri{sxz{}{|xzx{~omj`fUQ^iqzztwr}~yu~z}yv|utozltmfqfnqty{{|wyt~zz|}osjl_[V^gf~vv~zyz|z~{}}zyx{v|~uwqurkuelnl|}}ww{y|zx}}{zrxtlsbZa^\em|{w}xr}{|~wzux}||{uzuioekjhljxz{wz~z{~x~rxpskb]`dWgdo~|}xy|}{{|t{}}{wtzrzojrdqy}w~ywxx}t}yy|tt{mvqejh`c_iu|~}{|vzv}~~zy{}zu}z||xpqonumojjuup}}}yv{wwyswkednc`gZek}}}~~x{~z{|}suu~~x}lpndpuuq~~zyzvzw~~~w}r{|r}tlphbb\ckr{{wxz~zu}y~}xw|y~y}yvqsvopkgpnl{}~{|z~uwyuzpgfnc_fX]`q{yw~y}zv}{uvvstxiosojuzz~yvyx}{rv|pzzpslfgabihrm|zx||z{vy~~yv{y}|{|yww|utskkmesxu~w~yxwzuxvljpk`k_ZZcpuy{{y|~|wy}xsuywrzlntoiqtw}{zx~|~zwxrx}uwnffb_hhler~~{y~v}u}~}{y|x{||~}z}wuumlmgttp|y~zy{zvxxnntjaj_[Zeilw|wy~z~{~w}z}y|u}{~~orvllpps|}|{yy|z}v|p{zyyonkdcjhd\lnpwyzy~zy{|{|}y}~w~~|~}xnqkmvjq}~zvzz}|xzvowwiok_[`e`dkpux~}wz{{t|z~|zw~~wtyrptrptwtz|}v{xw}{zopnkdjkg]fh`kvz}wxux|~}z{}x|}}{}utqt}oqzx}x}}zz~x|}{rxviqqe_ehccecblr||x}}|y{uwxz|~{|~tvywtvvpy|v~vxux{rtsqlrmd\gbYadfw{y|}}z{|}wzz{t||y{zt|}qxzzwqy}{z}|w~y~unxtlgmecbe^Z^ex{~}~wz{uz{x|{~y~w|~z||y|{tzwo}yzxz{rtqplvsjblf\_\\hkv{{}~}{|{sw|u{}yz~|~w}xnsqwv|}~y|~wmuuomunifge[[[hhh}~}yzzvyx}|yzwyz|}z|~}|}ylpzz{{{{w|y|vwnqunfkj^]_[dbgjv|}{|vyu}t{~|u~}y{~}~y|xwwuuts}z}z~|xu{vnqtkmeh`]_djaes~z~}zz|xvz}~uz{www{z|qwzvuz|~yz~wwlssmjspa^]^gafcox~~w~}xv~yptu|y|y||w|w{vuroyzz~~y|xx~xpsulqkoc[]ci__ksyy|{y}v}uty|~wu|zx~xyw~tw{wt}}}||}z|urslgmqfa_[gcfdgrt||z}}xxurruy}z{zy||zzz}puvru~~~}yzuvimhlla`ajf`hmqlx{zx{{ru{x{ury~y{xwz~w{xt~}}}u~y~xuxojorhcc_g`dffpouut{~yt|wtrty|~ty{}|~}}||w~svvru}~}wu}~|mrknlddeh_]fklftw~x|vzuv~z~{zyt{{xz{}vz}ux{yz}}|ywxxrmrpfcbhlae_hooqlo}x|~|uvs{zwzw}vz~{{{v{uqv}wu{~w{vsqiqhfjpk_`fjcflkyw}||wxz~vszy|{xy}xsvw||uys~{|}y~xv|wv{zmgdfoglealmnfblw||}|y}ust}~u|t}ytz|x}||v|{y}ystx|}{|}swmiiomdddfbglfpkpyy}vzyswzyx}~{z|sru||}wyw|zwzwx~~skhmpgpiimjlecjoqv{z~|{wt{{}s|~}{~~|yuyy{~{~|y|xssy}xzy~qortmhkkf]gfej`fko|}{y}wtvwtu}|zwsux||z{zy|{zx}zuorrjqjnsoh`ailkopl|}||{xzv}}}yt{}yyzzz}xzz~zvyv}y}|wtuoghllejgendbbckv~~}zxzytoztx{{zx}vxy}w{~}{~~wyzy~~ywz{mroirsriaemnmkbkoq||xy{~yqpw~}}wtx||{}~|~~}|zyxzx}~zpkkkhhpcehbecdlttu}vsx{vw|s|yv||~~yyuzywz}{xx|}}{y~~swomrpoiefhknldmknz}~}}wx}trx|}|~~zsvyyx}|z|}~}~{x~{~tkklhhsjmk_bceinnnr|zwvux~qwxqtx~z{z~|s{}z~~xvy{z~|}xyv}xmrrpigkolijckkesvzzz}||wsuy{|{x~~wssuw~y{{}}|y{~~|vsnifnhgpga`ckrqlms~|z{zuuz{}rrrt~x{xx{}}{}|~~|yyx|~z{x}}twspicflpniajoiqstz{|{vv{~|zxq{~|}yzxtr|}z}{||}z|~~zuttokskipifc`emolkoyv|z{|yy{s|}trrv{y}~x}zuu{{}}}}zx}|v|v{zsjhjmnoiagdgrrrw}|}|}}sry~~{su~v}ytvyxx~w}}}|~|z}|~yxwurupfnkefhikkkkmqpn|}|zz{~|wzvsru{{y~xyztu}}~z{~|~xx}~x}|zskjnnlkgojalppqqqps{~}{z|~~vryrt}|xvv{tv~{{||~wywuwxw}|pspheeinmfejpnhsss}~{|~}vvxyz{zwv|}}{}}|wux|{|~{|{{|}w}}wuusrohdnjejjnqrmilps}~|~|}x|~rw|z}|{{yw}{~|vwy||~}~~~~zxwyyu{pjjilqqlgfliiqiltw{}vuy}~yuuy{x|~yy}~}{zw{}z~z{||~~}yvx{wrmipkkrmijmjfegkq~~}tyvxzz{}~||}z|~vwtv|}z~~z}yyz|sljlnnmllleepkglprqy|y}{vvzsv|~|vv}{u{||z~~zy|~zz}~~|tiomenqqoljhhhffqsqz|{}yyyuty{{}z~{vtxz||}~y{z~|w}yvtpqsojknodelhlnljkpy~}}~|{{xqyyv|{}~tyzw|{{}z|~yvz}ysuokpnopog`cghiqpq|}~ww}{wsswz|}|yx|||~~~~}|~~|}~z}{xuuvsolikohklbhnolinu{~~~y{~swzsw~|z}~}}{~u}{xz{{~}{|~zrxuiopnnqoidaehpohtwu{}}}~wqqrsx~xvw}}yy~}z~}z}}|yvxxy|yoklmfgqiegkmjjnuz}~~}~yzyz|svyz|}}|uyxy~{zz}|~~~}}|{w|{svrnnlifffcbnsmttruw|}}~xsrssz}{}{x{~yy}{{}~}vtvz|yvpmohfmhjligimrtuwuz~{u}vvxyut{y}~z|}|{~}z}|}~|}~ws|xszysppkeegfeigjtpmpv|~zxwvtrw}v}|}||~}~}|}w}}|~}~~|{wy}{vvunfniekmkcaipsspr~}{v~zz|yvtuz|}vz~}}}}}y~{x||x~|zxrkfdcehokdnpmllqy}{vxxvuysu|{}}{|wy|yy||z}||}{zyzxmolfikliffjoqmirstwz}wqrwy}|}~|vuy|||}{}{|~zu~~}}ukhgdenjgkfhkmosvvxxuwy~ty{uvz}|{~{~|w~~}{~||{{|z|xuznjmkhfgjjhljlslpyz|{}|xvxzyxw{zqnprw{wrtzztvwg[[oyuh[esqe`eo|}xx|}uijwtquk{|xxurss~}trzzwwv|||tf]`pzq`]fs|k_aemv~usspopqqnkifq{rxrhjz{pv{{}{z}}reZ`hm{veYWex|rklhfuxxysuqqvokms~xupy~slmu~xuuw|yvxz~{}}plidbgpwyreWYflz~}}{x||}ywxz}|ysmuusuuvzzvuvytmx}|w|vs|~zvsnjhfhjqqt}woh_Y[bipv}}|xuuquzvxxwvqptnquvy{ytw~}wsuxvkmqmptwvruzyunc`\S^ioty}~~tvxvuvwvx~|xvwvutstz~z{}}~}vqqtuv~~u{xpkige`[]bjkjw{{}}~~}zwvtolousr~}||zv~|yz}{yxvqigka]_aeijhjou||}~}~~|zztssilpotyzxz{|~{{~{rnigihc\[adaiiirx~}yxv{yw~xqpruuqljkoqs|{|~wrjgjcefabfhebdhkr~~~}{vruyyupmtulnonorutuy||~uonmmkheba^ajedjnoqy}xy}uw|vrlkptqljmpkr{w|}|slqkfmlhdbbaabcgrrw{wuvyywnjqmospprrqtvxy~{}yropnihhjd]ffdhiijlt~~~xvzvutsqstrpnokmtsy}~{z~{rrnimmmnkd`bdcciijuy|zwyzyyuuvmrwurpolkouw}}}|zvsnjjmhehbdijfcfkqx~~xuuwxvqprtskorms{}{{}}tpsomnmg`^adglfjpnqw|}ytyzqsuwxwrmllmrz|x{|yqnnniinebfihedhloqsz|zxy}~zsqtvorqlpruwvy~}|}xxtsqpmifa_bjfejhils|yzy{{vuwvpkjklr{y}}{y|~wqrnhnlhifeddfhjllm{~~|{{}}{xuurrxpnqrqqv{~yyxtqmhfgfceifllffhls|y}{v}|ywvsqnkkqqq|}{||}}zwokoiilkfachkkifhqry~zz{zqqxsssqomnv|~|~|}wrlhfffhg_gkhggkpux|z}~~}ztooqpnrtovzzz{~}~xnqmhjkjfabgkljgppp{|yy}ytwrrvvsnnquy}{z~|}xohgedhnhfighhikprsx|}}}}vpoppstmrurvz}~~}vwqjkkigggggiigllktx~|{|yw}vqtvuqnpstvx{~~xqkhefjeijdcgknpoorw}|zvtqopwxruuqqsz~}y}vpnliddhjhfeiplmsuw{~xy}xwvttsstttspw~z}wtpkimjgnkfcbhmollouuyzxwvrquruwqpqsx~~||}xxsniggjkjd`hiippruxz}|{yz|ztpruvuspryw~}}~zxuppkglihhffillkkonpz||xwxxyzrw{uropsvx{}|~~}~|umijjkkhgjeioonnpopv|{|~~zusvyxtnqsox~}{}|||zz{qoqjhghihffilmhlrpw~|zy}}uwwuutsuvttx~~{|~}xrpomjfhledjloonkkmqv~|zxwwxxtvuqvxz~~{zzz}~~}}zuunkiilnlfdhliinknsy}~~|~}~vtuvvvsrrv{y~}~|yxuqokgmliliikmjggjlr}||~~zvstyrrvvw{}{||z{~}||rmjjklkifghgmmimqsuz~zzvtwzzurrutt~~||}~}}}yrkhlghnmjjlkihhimtu|}{}|uu{wxyvvvvvxz{|~~|~ytomnmjiijhbimjmnoopw~}{xvwyxvtutqw{{|~}|{|}|ztmolhlmlkieegijlstu~{wytu{{yxxvtwz{|~}~}~|xuromkhgilgfjhlpqoorx~}||}|zxwwxwquxu{}~}||}||}~|wrsmhllkmnkfdgjkppoxz}||{w{{{zxtqqtx}~~}{zxwwtnihhjfjlfhmpomotx|}|}~zvwzztxvtw{~~~~~}|||~}||ywztoolkjjhfddflsprwvw{{|{||z|~|vsrssz|~~~}zyyyywqljkgeljhijlmnpsuwx}}yxzwy{uvxxxz}}|{~~}}yw{xtwronliggfddklmsqqtx~|zzxusrsv{z~~~|~z|{|~|xtpnjfkihkjgfhmpqrru~~|vz}xxxxvuy|~}~{}{|{}zuplhedfghkglronnqu{|yxwusvvt{~~|z|~~~}~~{yxvmkmhhkljffjnonkout}{||y|{xustx}~~~}}~~~{|~|zunigfehkffkklmnquvy~yvwxxzyuzzy|}~~~}~~}}~|~~}|{|vrrkjkkigffhkmlmrpt{z{}}zvuwxy{{}~~||}}~~~}}{vnihgglkimhgiknpqqsx~zxwy}wwzvvy}~~}{}{{}~||}}wywnmmkhgijihhhnolrvy~~~||zxwwxxwx}}~|{{}~}~zuqlhgjghmjgfinqqoptyz~|yz~{|}wvuw|}}y|~|~||{}}|x|zutplhefikjhelpnstuw{~}yy{{zxvsy||~}~|}}{~~}zvtrolmjhlkihghloopquvw~}{|xy~{xvwy{~~~~||}~||}}}~yy}xwwsmhghijjfgjiotuvyz{~|xwz|{yuxzx}|~~~~||}{~|xwusqqlgkighijkllnqtss{}~~}z~|zxwwyyy|{|~{|}}~}|{|xyywrlihihhgkmhlqstuutvz}{{{|zvywu{~~}}~|{|~}}|~|~|wuvuuxrnnigfgjljhjosqsxw{~}~}yyz{{zywx}~~|zy{}|y{{yurpmjieejihlmptusrtvy~~}{zy{}wz|{}~~}}|{}}z}~{xvvyvsuojhgikljhhlmousty|~zxz||zwwx{z}||zy~~~~~}|zwuutqlhgkijnlklnnmnpsw|wz}y{|{{}~~~~~}~~~|y~~}}{|~~}zz}zy{vpkiijiiiijgkqoqtvwy}|}~|yxyxv}~}~{yy{{|}~}}}zzzzwskikfimmmlkjklmmqvv~|~}{~|zyxwy|}~~z~||{z}|{~~~z{~ywtpnmkhhjkfhlknqqpqtz~}|{{{yuzzz~~zyyz{|z~|z{|{yurrkkmllljfdgjknsqv|}z}~}zwvwz{~~~{}~~}{{|~}~}}{ywurnjhhkihkhhmppnosw{{{|}xx{wy|~~~yxyy}}|}~~~~~}|zuwtmnnllmlhfefinomsvv{~~}yutuvy|}~}||~~}}||}}}{zzzyunjjjghmihjlmlmpsvy{~{|}z||wxz{|}||yx{}z|{|}~~|{x{zuuqmljhfffeflonttsux~~}zwuuuz|}~|zy{~~}{{}zyzzxtqmlhgkiikjjjmprsuuw~}yz~{yzyxwz~~|{~{}~}|{}~~~|wxzvwxtpnkhffgfgjiosrsux}}{xxwvvyzy|z{|~~|y|}}~|{{{xusqlhkhgjkjggkprssu|~|y}{{|{ywwz~z{~||}~~~}|{|xzzxuqlgedegjlhmppqrtx|~~yxxxwyxv{|~~{z{|}||}}~|}}|zxwvvpmmhhjjjhhiloqprww|~}y{}|xvvxz}~~~~~{{{}~||~}~{y|{zywqjgedejhiljlortwxy|}ywwx{|xz{yz}~}|zy}~{~}||}{yxyvstnjjjhgghijlmoutv|~}||}{yxyyyyy~~~|{{|~{{~}~}|{yupliggjijmjhilprssuy}zwz|y{{xxy|}{y~~}~~~|{{|~|{|yxztqoljggijjhfjonrvx{|zyzzyyww}~|{{}}}||}{}~}zvspkjkgikiihilopopsvw}~}{}ywwx{~~|y}z{~}}}|{|~z{}yxvrlhghijifhlkpsstvwy}}z{}|zwuyyz~~}{{}~{z~~|}~{xwvsrqkkmiihhhiklmorqu{}z}~|{yyzz{|~~{{yy{||~||z{zvplkjjiggjghnprsttux}|{|}|xy{x{}}~~|z{}~{}}{~~|}|zywvwtmnliiijkjhikoontvw}}|{zzzyxx{~|~~{yyz|}}||}{xtqnljigilijlmoqqoprv{}|{yz{wy{}~}}|{||z~{|~~|}~~{yxxzyuupkihhjkighlnmrssx||{{|}{xwx{|}~}}|zzyyz~}}|{xvtrnkgfihilllnqqpqrtx|yz~{{|{{|}~}~~~|}z{}}~}}}~~}}zxxzvwvqmjijjjihijjosruxz{~}zz||zxwyyx~}{z{zy|||~~~{xvvutpkhkihlllkklmnpru|~{y{y{}|{||{|}~~z{~{}~|z{~}~|||xzyurnkjihghjjglopsvwxy~~}||{zyzxw{|~~{xxyz|||~~~zxxxvtollgikklmljjlnpsww}~}z}~}|zxvwy|~}~|{}~~|{{}~|{y{ywusqnkhghjhiljmqsssuy~~{yz{{wyzx||zyxy}}{~}}~|zzyxustnklkjkjhffhkottv{||}}~~~{xvvwz~|}}|{{{||}}}~|zxwvtpkihighliilnppqtwz}|z{|y{zxz{}~~|zyy}}}}{{}~}~|zwwxsqpmllkihgffjonruux{~}}{xvuvw}~|{{|}|{y{}~}{{{zvrnlkgikijjjjkmprtvw~}{z}{yzzzz|~~{z|z{~|{{}~}}~z{}yyxsomjgffffhkkprqrsw|}{yxvvyyz}zyz|}}zz~}~}||zwurpjjkijkjhgimoppptz|{}}{|{zxwy|~}{~~|||{|}}}}~}|~{z|zxtplhgffgighnnoprux|{yyyxyzx{~~|{||||z||z~~~{yxwsnnlijkjhfgilnnotvx~||}}{xwxz|~~~~|~|{|}~~|{|~}}}}~|zxsmigffhkhjkklnortvx|{yyyzzwy{z|}||{{z~~{~~|{}|zyzwssokkkjihhiikllqrsy}|}}|yxxxyz{}~|{{|~}{z{~~}~}|{yuplhffihikiiknqstuwz}{yxz}z{|yyy|}{yz}{}~}||}}zzzvwvpnljhfgiiiijosrvy{~}|zzzzyxxx}~|{|}}|{{|||}{zvspmjhjihlkiijmprrtv{}~|zy{yz|zyyy|~z{}{~~}}|||||}|{wyxtsqmjgghiihgknotwx{~}yxyzzyww||}|||{{|}z{~~|yvtqnlkgijiiijkmoprtww}~}~}z}}{yxxz{|~|}{z~~|{||||~|z{xwwtpkhhhhihiljnstuwxy{{zz{{yvxyx}}{z{}|{}|}}}~{wvutssnllihhijjjjmpssv{||||{{zzzzyy||~~~~~|yxy{}~~{zzyvroljihfgjhimortutux{}|{{zy{zx{}~~~|z{|{{~|{}~}~|zxwwwsqpkjiijkihhknnruuy}|{{{{zxwy|}~~~~}{zyy{~}~~}|yvtroljgikikkklnonoqtw~zyystuw}|uwyqonmmnot~rsxw{~xkius[fyjYfuulbbddipz}yrxn}ljox|uqtzr|unnqruvspomvquv~}qgemuxqb[_gqx{zzz{~z~~}zmoyxqmlmnopu|vt~st|~wqor{|z{vnmp{wf]amvvpfbcbeimsz~vryvlkorz{urty}xvvxyyzzxusnkr{ut{~th`adjsxzxuqnnrvzyz~zz~{wronnmnpqpqssy}vv{zuoouyxnjkqx{xphb_ZZ\\`bddflt~vpory~zwtwz}xuqosz{|zwyzzxxz~~xqnifcb_\\^agntz}xtsvvw|{wwuw}{zvtvwyz|~~|tmga^^afgkonoqrqonnopru{}~{zzwx{~~|}~}yvtrrty~~{wspnkijkghhggggecbaadfeinry}}}}{yvuvyzz~|zyxvsqqponnotuuyz{}}|zxvwvuxwwy||xutqprppqsuwz}{xuqonpoqsqrrrrrsuuuutvzy{||||{xvsqnoposwxz}~}{wtsrppqqrvyy}|zyxy|~{xzxxyxvutrpnmllpqt{}zvsplhecb_acbcdcbbdfhjlmsy||ywvutttvvx}~{yyyxurprpppnkhda_^^^_aaflnrv{~}{zyz{zz}~}~}zvsrsssrrttuyzzzzyxy{}~}~}wrpkkkknsx{{xslijqzthiu~rgemw~sp~{j{vht{ursuzx{|{~yedwydawtgbgmrttplloy~}~}yuppysck~lbn~{~~uv}u{||vspmhddhotzxk_WXakqoib]Z\_`eltzyxyuoqw}{~{}wspmlowvqrx|zwstw{tdWV`ori_XW]fnuy{|||||~|}|tryzursw}~|qjjsxttw{}|yvtroqtvz~}yrkfb__afijmoqu|wqlklpv{~~vont~|tonoqrqqqtuz~zz|yzyxx{}sjd_chlpsvyzzz{}~}uomot}ytqrtvwxy|~}xuvuvyz|~zz{wunhc_[Z\_cfghkpqsuutsqpqtz}zwy{|~z{|||||}~|{zzwtutqpoooqv{~{vqlhffhjoqrutqnlklnopsx{{urtrrrsuwxyyzzxwxvvvusstvxyyvvwuuvtroljknrv|zwvtrqoprrw|~}{wvurommnooompppsuttsrqsv{ztpicc`_^^^___abeegkmqvz|}|zy{}~|{xwyyz||}}{yxxxtrrnmljgc`_^^]]^`^_bcdfghlqy|{|zzzyywtqpqqpqqoprsrrrtuwyz}~{{{yxwurqrtvyzwwvsrqqrrrrtx{|~{wwurrrrrqpoqsuuxwvxxyxxxxyyyxzxuusrrrrrsuy}}sogca]\\]ekr{~}wmffoy|zxohht|ml|}pnu~uvo}}lz|xxxzy|xx|xstuwyvi[]pt`Zn|ykekt{~~xtuxwvvqlnx|lrypzyvxyppwxt|uwunkhecbbchnuvmc\\fs{|wpkjmqu{~tmmr{wqqv}{tu~tlq}ytqsvz{x||wurtwxz{yrg]\guzskfdjqx||vqnqzukio~~ytsv|}ww{wutrqpnkhd`afjry{ysmfcbceinpuz{|uqnlnsx}|wvz~xtrpnnopqrsv~zwtrqnmnmmmpsvvrld]X[biry~ztonqx}{xvwxy|}~yusrsqruwz~~xwyz~~zupkda^\\]^`chmrwz|}|{|~}~~vrqoquxz{{|}~}{zzvvwwxzzz{|~}yupkgc``ciorwxwwwvtstwz~}ysnnklptwy{}~~~}}~zyyxwwvsrqrsvxwxxutsrrrrty}~}z{{zyywtqpopsuv{~}{{xutqpoomllmorwxy{ywwwwxxy{}vplgba_\\]_`bdgjlorx|}}{yyxwwx{~}|~{{{zyyyyxvtrqqmjhdb`^\\\\\_cdgiijlpuz~}|zwvtrqqrsssrqtttvvuvwxxzz{{}}|}{xusqqpnmmnnosstuutstvwyz||wuurpppppruwxyxy{z{}|{zzxxwvuuutuvtsrqrtvwy}~{yvrponnopquxy{}}}{ywurqpppprsswxy~}vtqlikumpf\YOKUak~yxylszwnmx}~lt{~v{~{phl~nxuyzupz{iq}{wrw~uxvq~z_Tfy{ttnuxyzxv{x{qoqv{glyulnu|uukkptz~vsyz{vlizpdPRfwwhhcggchenv|x}|~}}zv|yhgdcovvzqmfs}~~wtmsw~x}qhupb^hhlljnlkhjoruqmkd\fp{yqp{xtru{zsrqvzqu~sx|y{y{hfa\arx|tnjr|}zwk\]Zj}}rmpjsuz{w}~yx}swqnkjmppou}|~wttplz|x~z}zwrsz~{}{~xolngdksw~}{rfihfjs~yyvoqzz}}~zy~xskipq}~{{xxvrqvsor~}uqoqtpotwqqrpnnfimorwuusnuupkkkqspr{||}{||w{}yz~{~}{xypiffmmlmlflqpnru|{z~|tssruvtyztqtokilowtqsvnnqy~~}zrqrkc``bjip~~zsrpkgljggkjpiehplnorw~}~~|y{}{ttxuprvz||~zzxz}~y{xsrz|zvwnkjnx~|{zuspsv{}}|zwrpkikg_\__iir|zvqsrnkqnuokmustsu|}xwsv||vrnpmhdkismlkohieeijlmkkegghhw{}z}yutqzzzt{~vqkic[PTQ]dm{}ztromdhghiaaegde`eilpqx~~{~}~|{ryy~{{}|xxqwwwzuspnott|{wsunoiny|xwpnlkx}wrokgbXU\]npz{{yxvtnwszurrvuusmuw}}z{{|{qqonlkclhoommpmmkemlmoiiknrzx~tsqrnphqy|{urmfd[^gkyx~wwsnkiblflgeeigifbmr~y~}}y||z{||zwwmuqvwuvvtttltstzvzusnnjnflv{~|yxz|~}~}~rwwtxpijifjkmyw}yywsoqiqmonjllmosozy|uyvmqsru||wtwuopfgiflgiedae[_dcotv{z}wussrvsp|~|~|y~||vzstrefklmslsqsxzxyummhei`c\^]`cooxx~yx|rqvut~||zxrzsxuurokojhsuy}ytsmolmpslx|y}z|wmsnnskny{y|tttqwx~~~w|{ttrmsknjmou{~swrntmktxz}z~|ptuomnhngihihdbiiix|z}wzwrroqnmoslw}~{zqwsv~x|{zqrqoux~}uyulkifjceemrxzvzvnrnntmo|tzztvuqtlnnrpno{|z}vrpmlfdjlhux~}}}{xwuurnntllst~}}wzuuy~~}}xuyqknknnkpv~wsukijdhkgtrqwqmnhhe`cdgfgr|||}|}wurqmiksmqvu}y|{{wsxxrz~~vwsrqlqxw{wmmljnjov~}|{}~wqvqlokltrz~|v|vwtsslmpvwy}}~}~uokjhdeniipqz{~{zx||~{srturmtxv|{xtsnpv{{vy}v{zrqsu{{|~}vpurkmjlvx|{}zqwunnnmleektz{~{{}||skjllifmjgmnryvz{}yvxxxty|xussnkpttrr|{~tqty|z{{||utuvtmnrknqt~}}utxrrsrqnlu~{vlhjjh``fbeghoww|wx~wuuvwtmlnors{{{{xqorturw~}{}~twwrw}utxwsilnmsw{|}ptvpnmmnnq}~~~~{xyyunmomjcilghimv}zy{yqsx}y}vuuutofemquszy|vu|x{|}zuorvtqjry}}}yzrvwqsu{yusuvskfgcd_bfchlv}{zy|vuxqstwwsns~~v~zyxwtngfoswss{{}|z~z~}zv|}{zzwqosrtpw~y{~~zxysotpuyxztpqrwulcffhdajkqy}}||{yqyvuuw}~~}}uwysrllifbjqrrnzyxuwqwxrrqvtoipuz|||ywxutmvyvuvoqpsog^`b`a]hpz~|~~zyrsntsnqs~}{y}trmppmilqotqx|}z|z|wwwrvuxuqmu~{zzxvr{z|{wyu~~zxvsrvqsoppmfehglkx{zwvtytwwu{{}{yuonfifjljdenqyu{}~~wuw~yx{tsrv~uvyysfej~d]oyb\fqmeggsw}{wvqnp|ngp~yxxtmqs|{ka^oxuigdcil{|w{pcgjy}rs{~xjiurs}uutzwtotzz|zpsrngfio~}e\fzp\Xhuw|vmu{vmfkuxyyvwxvxtthbYY_gnszuwsyyvptvxvqhfadjrz}~}~xvqmkny{zvtw{}woc]`gvzthlkoqqwvxx|vvv}||yvkqttmighny}|~}vxoh^Z`bcgtzz}upmjqokdhggdfpv~yzpolljjjijisu|{}{rjo~}z{~}wmkg_`cr|~twwztttx}yumqqokkpt~ty{{|vunmf[ZX\XUUbm{zyspnoojlkw}{w{||xztsqxz||{rnjpptz|y|}{w|z{vz|{vojjdcZ\`jz}}}xu}}~{}{utyy~z}ywtstrpfd_c`]]ZY]ox{}}~wvsuv|}}wtpspxwxtrwvqkppx|~{ttrrrnvvtjijid_b_go|~wyxvujmllhkjd]X_ZZUWZZ]huy~z~~~zzwyx}urqvzumiqr}|{{}|rhlnmplojhfhea_^efsz{zyqnhklnlje_b_c\\]`gmpq||~z|~z||ywrtrvqpkmrrvtsq{~wpjjppsifhlkgfchgsz|spijfekjf\]]_XWXX^bpsrqvwx~~~|}}|{wzyuolspsotzz{{}{{|xprmhfonkdckjicgglr{~|{z~}{mnlkmkoidbgfb^^ddpuz|zsry|yz~x|xz~~zzzqtstsrmgjlqkmqxwurtqplojjmjnkmlqv}{~||yqiiikldcaeba]\]]gmssxvplr|yyv~}}|uz||toorsopntwyunonmsqkbgjnkeiknp{}|w|}xu{}|wwwxv|}||xqkqpngdmmkdffhktyw|xxuuuvuu{{}yyxxz{yv{zuxvusswqljpnmhlpq~{twruvzwmgmtpojosy}~}uuyx|}{z}ztwuqmmkcfeigcacejqoussopvumiqu}z}}{z}|}|~x{{|xvrvvuqnppw}wtoqptupmlljmilklx}{x~xvv{uwpnolrvyutu}}}z{}ulmnsnkkmlnyz|z}vux|}snnyz|{vuvwtz|xvy|{}|wpsyxvimrrmiijmq~~~}w}~|ws{ytlqsux~~y~wrsrsqyyvouz{yv~~}yvvz~wlgomlfejjotywysqqsysqooosvwslstxz|z|zzyutvytpnrq{{}yxyxxnqtxtmhlqu~~|~uxw{z{~ukhkhooomjprvqsu||ywutsmpnrz~{v~~zuu{zusyqpnnsx|ywt{|}yv|}xzxxyysonotnljkkluz}|}|~yx|{zwvs|}~}xuolrtvmlrvtpqswx~~yz}zxpuxulijknp|}~|wtwwvmorvvroijjnkqyzwt}|~|}|zorzyxnqtuy~~}}}wpvyvtqvuuz}yy|z{wumnpkginhfelpnklstxxzwpruwvwu{{z|ujgmkpstuquy~zyx}~wuvrxyyslmorlljlqz~||w|}}}}y}~wqrqtutnmhorpqossz||}~wsovvutrpmw}|zyxrlptrsrphebelqtmov~~}}~~xz}~}uxzysstwwx~z|}{z~|uz||uuvy|{||zzvnkhhfopk``ikmfkpsxw}}|x}yxzyzvxnmniflsqnlu{{xywxurruvompwungmqr{||yy}wwv~|x|}{sqrlqsvrkjnrmootx~~}~}suwywpsu{~y|srtvrorheeffghijhrtxy{~}{|ytu}|}wvtx~~yw~ywuy|z{|y}z|{~{wmlifijlfeikgffhnmz~}|~~|}{xqljkhnqnffotwquz{{yutywsiostsnsrpt|{y~~zstv{{y}vxzxt}{vwsqqwtnjpxunhopu}yx~}zuuyv}~{xxzzwvskbfbbgjlc`fpppou{{vx|~xxy}~~z{}z~ysrvwwyz|u{~}~}~vqnminmlebjknhfehnx|{}~}qplkkoqlijmlnkpst|}wz}~xqpuuqomrqy~||{}wtw{wwurjnnox{ysv|~vrkdkZ_nt~~}{pyrbjhfpq}}u}qf}zgcf`qx|~ures`gvvtkkjuueelnbhv{{}y{}uryqkfeabpqz}timgpsw~~mvzssfi{xy|qww|~z~x}wr]b~slo}|~norro{yucX[bgu~uswvnnprs}tsijdgwvposi[ikkxwrhs~xzzt||~j^QMavwyhd\VU[_`l|ypnheiwx~rkmz{vsx|{}zxysvqqxjflumgy}vvqZUNPjvmkkuv~y{ugjhbaimhoy|{wtg_[a_bjo~|}vzyuxwypjtjho{zz|mY[[e~{ppmnhhxnhgjjbtusuomhfir{tzsj_]Z`hhoyu}qu{x~uxwyxvwlszv~}|tricW^gi~|xvofnh^eehZfjhicbc_em~wnikkmnmxquu|yuzsyy~xy}w{zwyslyv{{xth`VSKN[`x|rvxmqmsekmiknmj_acuts}z}rnnswuz}vtz~w|x|vy~|{~zqslxrt}vqg_SOHPcfxpwvuz||~|{tggbrswr~}xrhilpwxywwuumogejpsp{y~zysvytrojcWZV_mo}wzxqx{zpom}vzzwmllnsuy{~xlkaclmicmkjoozu~~zxxy{|z{vupjjjje\^Ufqy}xuuz||~}wwsuzvwturwz|vuimsoibf^ijrrprqqpttlsp|{xyt{tqca_a_QRRQgk~}y~}{xlmbrsw}~urzu~|pivwslijirozssmljqxnjmjysx{||xnea]`[SXLV_fz~mnijrjz~~}xlyt{zuseowttmldony{xttut|wmrhqqsyz}y}wmkglj[_VS]Zmy~xvlvlt{x}~y{xxyxy{|igrnneb]cin{{ztsox~rwts~vx~}{ysxnvyxpqmjmb_cV`_kzwzqtvqx}~{x{rzw}kqrmngc]dbntqjjijwtq{sxwy}{zvztltrled`edX`\[dapxzrskury~|}y}plqt~s|yzvngdhdpsrllehqikogqnx}uqtofegcg^S\TVY\muxrnfnisx{likq|zxyupjllkz}wrqhkoclokqlx{~wwytmljgk_U[PX\bou|wpfkiw{{~okjpwsxrtrokppmz|xvumosirtpuq{|y}{zx|xx|tjjkjpeZbY_ckvz~vlrq}|zvy|tpoxxu{srpkgkicqwssuopuflspxvz}}{{uwupslcbedmi\bY\fp~pqq|~~yuxw{{syrtwzyusmginenvroqkfkbamluy~yvxmsnc]_]cfV[XVcoy|ljlw|{yxxlwwmnqz|xxsmqvkoysopkelc]hfnt}||yuwre^^Y]cX]ZT_l~w~pllu{z}yxktumptw}zxzwprwmp{wtvsjnf_hekr~~|w}}|twrfa`]bi^d_Xanvqpy~ww~|ymyzuu{x{vttojqtjq{xxxqjofdkhqzyty{vvyqumdbb]ce^i`\fwxtv~tw}}z{wo{wuw}{tstoltqgptpsslhlbiljr|vtwxs{uqqd^]ZWa_`i^`ly~vt|}t|v}{uwknupry|zzwpryniroqurggd^gaep~xy}{ytul^\\YY_YcaX\l~~}{xrrz}|{|~wuqitomqz|zzus|zntvsyxpgg\ceahu~|y{|w{xwgca_Z`\_eYWcu}}spw{{yz{ottmnuz~zywvqytsvpw{xmldekcep|~|utxty~x|nbaa]^a`kd[^p{|vy}y{xx}{tpxpou}|xwqp|wnsooy{vnkaihciyxuuvr}vvtb_]_Z__fk_Ydu~{w{}yuz~}|vwmstnq{}zxou|urtjqvxoib`hbam}|xyvu|sxpb^\[V[Zee[Yhuz~~|wxz|v{y|~|vukrnhmyz}~{r|wxvkruwnkbbg_^ly}|w{w{}w|pe_]ZUY[hd[Zhsz~{}y|z{~x}x}xumrkgp}|~|s{~w{xntx{rndeh_]kx~}ztxu{{tzpha_]Y\^kh__ir{~~}~|~~x}v{}xxquojq~|ypw{vzzouvzrpggkdanz}}zruqu~xqvojc_^XZ\klefov||||{svpqz{{uxswqjoz}ruxtvzoppvrojgmidkw~}usqp|zproidZ[UWWfnkktv{tvz~|rtqltuyqspsrjj|~yvyutzrqpstmkfjgchsz{vvq{|sqoih\]VVU`kkkuv}suw}~turlrsxpppuukiy~{x{vv}wwrsunlgkiejrv|~wvpy|trpkna_XXW_jknzzuuw}||ssqlqqyrrsxxnky}{z{vv}wyssupomplhntv~w|zvtnwyssomnb`Y[Zdmos}uuy}x|oqmloqzqqr{zpo~}yzxqtzv{qttrpopkjsuy{s|zxrnutopikka_X\]gmow}zss{~wxmoiliptnos|wouy{upwwxynsnonqnilwt~yw{{qrtponhnfbZWZ^fim||uqr}}{tnlhkgsqnnvzrlvz~}tt{y~uppnpprkgpsr~~t{}zrvsqslmnfbVY\cfhr~}~spvz}rokpjouporzumrw}xsz{~ttorruogjsoy~tu|~vvuprpmrji]Z]bffkyzytu~{{vqlnmitsrqy{spvv~|su{~yvpqtvvmisrt|{ryy{ttomqlqnmgY\`ffgs|}xtx~y}rlhogkrrru{xptuzxquxvtlqtyulmtpy{wuxzxvrlnnlpjm`X[bfdixz~ury}|zqjknelnqptxupur}wquy~yrjptxsimqpyyuxw}zwqknmmnio`Y\cebiwy~vtxz~{sjnmflkpqvxuruq~wsv|~|qiouytlpoqxvuyu~|xpjmmqokqb[]eebjxzyvyy}{tjnmgljqtwywvwr~ystyuiow|xprpryutwqx}woiklqnjrg^^fifky||xzy~}ztjlmglgntxzyz|u~xrquzjlt{zttrs}wvvqs~yogfhnngpkb_fkhjx}}zzz{~z|vmikdjegqwzz|z~~ytqp|~}~mgnx{xwvr~zz|wto{|rgcdjpflmf`bjkju|{||zyx}}}}wpijcifcmvzy{}}zusoy}{qhjv{yxwp{zx~xunx}vidchqgkmhdcjlktz|z}~{yx~{{~|xskldjhbjv{y{yutpw||ukjt{zzyq{{y~}ytmv{xicafqilmkiflnnuy}y}{yxz{zxsnkckhcjx}{}vttqyz}tlkv|{~zs}z{~|{rmux~vga_goiolkjimoqxy~~w}{yvywwvqnhbkfcky~z~tssqxv~}sllw||xu~z~~||ootx~se`^hkinilkjmotxzyv}yz}y~wuvuppdejccmz{y|rtruwwxqknwz}ty{{}{otu{}od_`jhmlimljknut~uw|w|y}~vuwtsmbjfcer|yyxtvuyu~~upkqxzxtzx~|{~tsuv~xib^gjlohlnkhjqtuzv{xx~}zzsvvttfhjeekyzw~uvuzvyyuoouy~}txv{~y~}zqusz|qf`ciiqkjnojhmtq}vwx|~z}|tqvtwoekgefpzyyztuwzs~~xuoouzzxxw}y}wuttzxmc^eflpikppihpsrzux}y}||~|~qqttwjgheegryx|~xuu{wt~zwsoot|y{vx{y~wxtuxvlb`fdonhkqoghrqvxux|z||~~|}}ppsuviifeehqxx}|yuv|ut|xwtpns|~y|ty~zx}yytuwumcbecpmilsqghrpxxux}|{|}{}~oorvvkkfegiovz~|{uv}uu{xxwsnq|~{~ux}zw|{zuuutoececpoknuvljsryzvx|{{yyqmpvwnngfikov|~~ut}vtxvxxwpo{}wx}zv{|~{|tsrqqgdeanpmnuyrltu|~ww~{z}{vuklrxppieimos{xryxrvsvxzuov}}x|{xx}{~wsqoqmef`ipopt{yqtw{}yvz|z{~u|znimwrrnfhmppw}suysvqsw{ypq}}y|{xwz}z{urmnphhafoort{}vuwz}|wz|zy}u{}qikwstrghnrqtvvytwqrw{{sq{|{{ywvx~}z}wsmmqlkcfoptv|~zxwz}|z{~~zw|uz}}sjivtvujjotrtxwxuxq~}xwwtuxztz{yqjnspmdkvzi[[c]jvz~x~{oyvceiv~|~~^[{xqrjekmxxtv]RcHTjsxla[VX]bjq|wjnusnqq}xvrnprqox|}iX[^m|zl_itpjinjhp|qUFY]QTcokkeabfipy}vrpc^jx{rjgwx|xy~w}~~pe]eijmuzrkqnux~yqkgs~}yojpzfNIMk{h]ghhjffknqtzkdabaV`k}xghoox}sjhuvyywxtprvtimov~|srjmyyx||gOJIVwtpnfga^bhiiq{v|~|sf^]`f\[clseidhlpx~xpliu|z~{{xz||ssmip~pVJIJ^|zmgaVSX[[\jllttri_]^hi^cerslidfehqxzsuxrppr~~{y|snpiks~scLJIOfuobVSWWSXgekoqof__cnhcefvwskigdhpuumt{yvwuz~wy|}ujklinx}sl[IJJUl|vh\YZYS[ggnqusjddhrkhgiy}xoolhltywqw{}z{x|~}}uz|x|xpdijjr||skgWIKM[r~qebcaZakmuvzxnifjsmkhk{|suqmqz}x}~~z{yy{u{zuwx|}zsl^eimw}tldcWKNRavymiihafos{|unjjrnokn}xzwru~~}xwstzv~|stsw~x~zzvqk\aio{{yunh``ZNQVf{vponhirw{rkhonqqq~{}}wwzusolwyutqry~|~vy||zurrmk^\gp~||vsnid^\]TW\k}vssolsvwohklqwv~}z~upmkfs|yuqot{~{zxvyyxqmmjja[fqy|w~tojeb]Z]X]bo{wurouvypgijq{{|}sliicq}{wqnry}zxyvvvvoiighb]gqy~~{uz~smga_[Z]\chs~zxtrwvzqhihq~~{phggbp~~yrnqy}yx~xwuutlffehb`hsy}}}~ysy{}}qjc^^Z[^^hkw}yttwvypgigswmefdbr~{sory|xy~yxttrjdcdhadiu}z||}}~wsyx}{oh`\\Z]^bln{~ytxwx~xniihx~sicfafs|spuyxywz{npqhkdlpebcin}x{~|vxwvsifamh`^b_av~}vz~zzri\i|xp^T_]fs}xumqyt|{smliY_gdk`er|~||{{z{v|zsaUa_db[ijlw}z{{upnhq~t}}ppe^_bht~tuxymx~uuknkbahjbbhs}{~~vlws{}usg`bhc^bfgly|~|z|upkkw|s|}}vrj`]^`eu}{|t|y|}yx|zmlmg`dgf__dq|q~~ws{{wumb]bb^Y`cms~~~||xoifpv}x|~yplbbb^mn{{w}vy}}w{|skfe_c`ipegl{|~~|zpttuqlecd`ld\`gik~}w||unoivz~ypjb_Vbdqy|wxuuwz|w|pljh_\f`ghqw||wzy{tv~qndbW`daffnpu}z~}zyuprs}vw~xxnh_\[cyuxy~w{qikjjbc`kmeo|~y|}y{zvnwswsjcZZYhbcnxx}y}}pmnuv}|vsoxwgd__Xhrwyrw}~~quhccb][ifkis~}z|zvokmylg_^SZdchgs}{xw}xslep~{}x{riwqn`TZ^flyrx{}xrrddYX^chjtoxvyz}x|xvglqokWTTXZalpv{yxuwxqkqz}w}qpjlhlcb`gnsvrxt|tv}}uhfZ]Xafqwzw{wzs||nnkhdhi`^W]\fhrz}uw|{|vvxyu|z{z|~tqmlhb`cnly~|zuuxv{xxqeb_cb_mn~wkruzwzwxvqoihca[_Z\fi~~zzwwrszs}~{|xzrelbhb^fp{z~{{z~~xrk]dY\]hkuzytyt|~xuuwwgibedUWV`^ryy|rztx|}yrw}~|}{xrrhjafg_f^m}twoy{x{xxfj_`]Sclw{wut}wy~s{qrlkc_h\^OP`htzw~zv|pwvuvvx|z|}{tuxk_gehca]io}~sy}{ptsh`YXZcfxyzsqsv|u}rvnbidea\SUU^q{~xxqpux~|x}{r}z{xtmldallhghl~|yzsvuvv}vwqcZZYeqp~vtthx{|wrlmhaiifa[Wc`hw{{stuu~wy|vzyv{ytjmaaf]danu|~{xzv|~lk\ZYYdk{z|smqqtywtlp`^cZ\RXZhp{zursw|yxx|y}wqxbjdid`j[lnul|sysz}qtbb]_aYoq~swyt}sywoymkedcejV_PVeu{x}v~|n|sxz||vv|~wzgq_[cdlabgqxxzyy|myo`cYSanj}}}uroqw~}ynrl[a`a\UR_]o{}|y{sxypzn|zz{pruurreha`kegkhryzx~vtpin_W^^ewu{sxnu|~upiceahg]WWTnpr}y}mu{uy|ywxwxuxxujjagk]mg`ktyzxxvwn\_\_\dn|zzxrqwx|xtjdbeW_dYV[be}}}vwquswxtxrr{rswkk_^Zdneplu~y}zyxnyia_`_Yns~{rom|p|khic_Tb_`[^fl~yrsrytyy~svtumpltqbcXfegsjm~uy{twzckca]Ybq}~qknsryxlucdXWf`a]U[r|vsxrrolq|vs{loorj_QZd^ryq}vxzvvujmm]e_gouzxqtmostsorm\P_XZidais}~uqvmuyqr|~|sfhhitpd\^Vhrk|y~vxus{}uotloX\kdnu~}nrjcnvmqsj[d][`Wdcet|~}~szsdmk{zrpkhsaccYVXikv~vtvp{rmjjf]mltpvouukmokxsjf_[TXXalmyo~}xsuzlomzzu|mcengefccZ`kk{{wuutvxzynoeffgju|{xknqgfmomgmncWXXbisrvxy}zphkqryyv}|tqkfb^a^b`Zcwv|w|txwpsidqkdiru{}~zytqurqjqtfned]OXbdpqpqx}||kpnmltswppqaaa\]ie^dem}y}twztv}lmoffbiyx}|~uxppjdgnrgqaV]W_pgpsy~{|sqjrliqpvu~pce\Zeach^[ity~}xssostjle`hmz~~{y~yqznkjmkmg]h\Ubbcjp~}|~molhhhr~{lr_V\dchkd]`o{|ztyrvusldnjmwu{~z}mipdlmtfblab^agiplr~{|vmuhejv}qrfc_gfjl^aa_jx|{}q|s|ttqceeint~|}ysqsnoetijqah_RYekixwu|{lifees}yacg__meZg_ajrwx{tsykuritkikhmx}wsunlmm`nl]ob[`^cgkls{yuy|hjh`jm}{ynjhia[gbab\bY`n}utzpvqttijjqpz~vkgikieurjkadZW_joluyy{{zzhegjjquqfjeZ[]_\acbkpzppstt{yuofmnx}yrpmlmdgjlfdg__]hut|~z{|||qkcjlm}z{yoggd`^]gddibfoz{potouztvppnqxxuqlghjikiomfid[`ampy}u|zw}tnlikir|yz}qji[YV[_bgino{wvornoorrnvxw}}~~xrwrikebgdhdfdeacqsy~yysronilqlqy{{utngdc`X]_\aetvx}~}{slsqnqt~vqtxw~|uqilgfehfgh__^gibgwz{zttpvnfhpmw~yzobZ[cZW]dakpz~}z~zlionkru}xs||}}vsj_][hfcag_afinip|qqulroplcjz{ywvpqh\`^bZTalnp{||xzyurfguzwz|v{u|uqysjhg[caig[`hgbghq}qjw|xqqfmktv~lhjc[\\[ccopps~u}zxkkilwxx~~~}~ux}|}g^ed`^cajbiidbjoxsqozwqonmwwxy}z}uynlf\VYYbnmpovxyurlurmpo~{ww}{zvxpshfh_Y_`gnfebdrrz~uzwongoplkv~wwsswi`YV`[]aqkszwxoqqjgnup}~p~|ssnlrfa\Xeba^jaeim}wz~}tnrukunwqfv~zv}tofiYZ[[eYasxyzx{u}rxnaouywyx}zvrxnh`i\_`cm[_mnmgls~ufsvulkitw|}y|zx}gejfaYY[hfu|x|q|~smjrss}uzx{~xvwxrxb_jgf_aajbmtllvy{ysvxtyklloxytnseflb\c`mtlqoy|qnlk}xvys|z{w~sungkcdnfakfnrfgcfv~~xuo{tsgmpsr|y|usua]YUbdjixt{y{}oloooswyq}}suuohpc]^Yfileoomwv|tvrtq~ruoq|n|wzptvsheg_hapoeux~~~}~w}xtl{mp|vuz|~qtmknje`j`liqvbkqsus{~q{uk{{|smmz}w}~~vi`fcg`aiyu}zs|~}~|{oz|}o{vs||uuuxpvuw{zyx}z{rlsu`oohmbdlsq~y~yoj[jfimcu{|xtlz|yz|won|u}vr{zs|pjqghnvs~}x}qwfabi_nsvroiirr|q|v|tnttwnkdblv|}z|ri}orwiywtz}uy~kpvpy|ytqo{|jlmuvfgk[hsrspewyv}}~sxxokvv}|xmhenm~uvuunfnoxrr~xv{ypiklfkr|~yiz}uuyqrhafjhzzwpwy||qpr`idqu{{rgsr}~~}uvktqp}pu|sw}}vfofbhoy~|sz{}w|tusvxkemcqs~vovy}zsxlpiiim~u}risozw}~wtprmqwo~wzy|phcfe_uytmwyv}~xtqobkasx{}}xyy~xsrcicmzry}wkjyyt~vt{ylzqt{u}{~}lnkeeji~}u}zr~{|s{u~sopimvw|zz{uvz{sjoidk{tuyqenu~xyz~t}smowus|~zqlgbdddv}}}txqt|w{yzyrmrfj|uw}}|{}yusdkff|mv|knsw~wy}~tzrvvw{oyz||}nwkej]lx{wyur}{zt~zrxrppyvt{~ryyzvrgggcxpyw~zfi{{~}w|tmqvzp}z{}nooe`_hm|}yppw}yp|smwjst{w}}}w}~pgl_opmyxsovx|{{|{zxuruxv|y}vo{hfdZiyz}rt{}}zt{ytvzyu}zx{w~v}~~~|}~k_hbintw{jlt}}tuuvuvwry}xntjdZ\ot~|xulxx~tx|utuyts~||z{wzz|jkdaratx|~~vux~~y|yt}yn{s}y|zsuukoYcswzwttz}~~xs|my~xu{x|t|t{yzed_dlfx|{pyw{~vvzyrp|qzszrfl][n{twskt}y|vu|swu~stw~y|{~zwpnahliuw|xy{zzz{wsq}~|{v{lko\ctu~}ywyus}w|}s||~uvz~{|v}y~}||ywlmh[klnzpzz~~u~~rvvzx|{uxfhg]ksz}uqosvm}y}my|u~|uy}up}x|wytw|zupkalgpzzwz~}ps~xr}v|vu}ilkgus}qquovqs~uxusuw{zztyy||uyvrqku`dgry}}z}}}|xv~vxst|v~vmrictxy~x|rsjrvhm|y{vz{ssv|w~}{|x}xrotrrwkjgwzw}w~|~xzyuw|y{ywpqelvu}}zuqns|nn}vvr~|sy{~wux|{{oxtwpgmfp||y~wz{zxsxqv~{}z}zkphg{wz~zxupouktv}}}w|xyx|xxu}w|qz|suyqs|qsphgmwsy~x{zp~uvsy}y~ynqrft{{{qwsfvpi{yz||z{tw|}x~xy{yx~{zqrswrs}qrvdlfjqy{{xwmqox~z{mzmptt~y|ysklomqqu~z}ysxtzwvx|{|wszpozqrur{milpnu}~y{z|n|~tr}~tprlmsy~zy|svslsos{p||~wwzxwx}{y}{h~~urxzyvr}zjcgqns}yww{xvuz~|wsyjkon~|ywxsstgsutv~|u}}|x||u~|{~}{moyvvsryvmsbgkmp{|zztv}|tw{yx|urqmqnu|}|qptgnunu|{}xxtvw}vvywzmu{ywnssvrv{kdbrpkzyzmzyx|vsw|y}qruuw|{~}z{vmmikvqny{~|{}yrwzus{wwpz{~vnomq|ls|pgisvryzts}z{uvxr~wnttr~}|zvzplqipusuy~|ww{uxz|p|~~}qkzssspop|uuzwnonqs~wut~{z|yuuuszxvrpvz{vxjsnvslpy~zzyz}zws~}wsksq||rsrsnsqy}rrnksqy}r{~vyvuu~}ym}vquz~wy{zsmksqklv||}~uvzrvu|}seqvz{yzukriksq}ytoomt|{~vzzrw}~ynu}rvyq~{|tpxzqqmlsmgw~{zzssq~su~{lkx{xr~tvomeimuurwvqu{|}~zvyvx~}vr|xy~xxvoosyzohmxrjsz|wovs|xy}tjqtyvpuyyjkjipt~sx}wxv|~}}wvzxqv{zxt{y|y|r\U[VT]kr{|vzz}wxz~~{opllh^Wdq|muxXUz{{xrzp|tqkopxz~zmblzzwpxreXWnw_\ju}qss}wy{xprjjggm~|tfhw{ihn~mdci}raantztormlyw||wptrupuzony}xpfvld\[^jyytvpovwzpq||ypwu~e\\uxtzskmrfdbklhiytuutxmroi^Y_]\coy~x{~usnknr{oliruoouv}xo``hnzobZZkpwtvxsimcXSVcgk|okssz~~veea\_o}|xy{uvlkhorpry{zvpmiqy~uqoxwpqmd\gkvyz~xzwkfcnoknzgZ^`l{{~}|{xu|zxmuuz~vvorvrxtlggffj{zthb`lv{~~thlpto||y{ynor~vr|}~tc_cblr|x}|puy|yxvrusvxommemjjh_X[_js}pkgjqrrypklowqd_dhffxy}}zsyxoryu{yw|w~}~vxspxyvvhkggfagtztkmiepqxyutvuvtyxt{wpimslbbhonrwmsqth`dq||{|~}}zxptkgupnieijns}}ypkgdi_`ingn~wxrsvyw}rg_U^c_fdwxrnlxpkruv|{~}p{|}z}zzvsqlfa\cn{}p]ac\hiosnppkdgr|zxwt|xmcvz}}lpsty}yt{wjalmv}~{z~xpty~~w}{~twuqpwq{}{owwuufgfktwpx|{{sbbYVZc`YY_hputmmwxf\ef{ywzourzsegasnecm~v{~xlgbtslchupgf`X\ege`eojcenl~xlnww~yqjq{}vu{|~uutti_jni]bffv}sls|wndo{zshl~~z}{zh^[_dx{lnqqjf`]kvyrqvwsq~}nlwz}vochrywxtkcbZcnkzylkvy}rqr}smdf]fek{|}tvoh_bZ`p{xw{qijmalzwp_UY]o}{|szyyxv|~tumhXP[gqwvomnpztjntz|w|y{ux{tum`WNW[fijovt}wyx~jjdkovqt|www~yvztvz}|ruxjjp}}sligapx{{ypxnnknkszqhXalw|}kkh{{zwwpwskadZZ_eho}vutvjnukjglbfvt|~u{sn_eo~}xrqszpjquvznko|}{jparxmz|z|kzrpkn][rjjks~~vqxvjokrfnzqoq|zyyk}v{{}~x|r_mbcV]bblmizrurvs}qvptz{quy{sr}urw{{}sce\dlqlyx||{|uykntb\e\lsuy|ytqwv{rx~}zt{}wwru|ktzyx{|}yxvxtr}xtiokm|y~y}vxpemcigjfdjmkw{{}uiuv|~wvibpcdbry{|xv{|wzrx_d`^_oiowx}{zolwo}qy|t|{yy}z~}ktqiejmb`ghew~p|vwgextzwlhvwxuwqnts|~|my{hi|x}}u~~xkh]kotv}uvy~vquwygTapzwy~x{|z~vstwosnjpv~ynyvhfr}ndeq~ssv}wzuuu|zqiYO\i{~~zsgo~yqjdlx|wqjhmwjho|}}wrsvogox|ha\`\cokibXYYfggkxqlgigjsrwvpnnof`ftvurvtkc{|yvxj`]W\alx~{geow{}~~|smqsu{tndrwxyzwrmi`edbnp|~r{~}qgh|}~z|dU^hhlz{z{zvxyxvnid^UZmn~wo~uzruz{~nsujtuwoq~vx~xs{|hgnojalnipts|po{ux}yxs|{pgX]cgxty{tdlw|vqnojorina`^_ors}z}|rn_m|r{{xvnYVTVkl|}xkw}tzyjnz{uqjusruhce{~yspmorp}}~{ty|rfiitqtrrontx}xyxs{{x}xy~y~ws}ywm\hkjqgm]RXUZ`pmz}xgtuwkgdg[\cftnwywt~uyz}z~wo}xssmfbluqz~wsffmnhjl||{ntpsnrsls|wuvykebmnuurenpijhuuv~yqoj_ejgrrppng_mjxuz~xpow}zz~{}}~yu~pxyzw{wzpgkbkbY]`b\ryyywzsv|qsjxytsyyzzx||zz{s{~w{vmnacX[X`lx|~~~|jmwzwt{|t~zpmuswm}yyzvywptpjc]gxw{ykolknvowwqpskn{yzorlqw{xlik~~{{nemnsuoy{vvtw}vlolimrli{|}}|}|wvsiY]iilfk|{qltfrz{sqrfopds~~~swfamv||n`[x|}{wnt|ythll\gro}}vgdy}}}cclz{tsdgsyrejjjytwztywsxsx{snpn|xvrztqf[`|{rgix}}xqfoxlerptjOQeoZZf[SKGA@OQ_tswu{vu}yxsfpvnllofmj]dpwvniw{|~~xxwvi]^o{||zzylfninwxsfecotmszzub\`ZRWjuqpsmyukpvpee][ew{saYSWenw|uqeledjsjlfhiuoYbfut^V_bgnz~~tyz{pmoy}hgfqfehccXPYY\`y~}p`^YWSXppq}knhjvqnps}vlvvy|lnvqzzgdjphjrx}{obhdmywz{}ufkoie]fsjjefu|zxv{zzwgS[\\Z`mtxogxy}}kkp|xszy{z}s}|uqq~~z~zspqrvicdnxz}}|zwxvp{~z[UUSUZprxqlkovmgfip_`eqvquottx||}{xsfsr|yzomtsujnigsxwss{y~srvssv{t|nntupv~}}~qqvvwq{slhlleeWXW]n{|vlgkmsjphbmotr}yvpnlyncbgy|{x{{wwump}xohee[brxxmlery|~stqrov}}x}pnku{v~zu}~zxy}{zwwxx}msnrzvwtvvvvoow{{x~}~nnglneq{rumgd^ahfmk}}{|oqhkzxyx~vmsrwnuv{zyuq{ymoswts|}kaaaijz|yz~x{tig[d]TajniaWSdcmgdajouy}upnrqvpqiv||yqrospijklji_]mkw{w|y~~~mejYW`chbb\o||zv{mnthg^eamvfibbuvuproed`h_\]gktxo}srtzxplphrnv~}~~~|}yq_kfds}xty|{~}xwwuumfXchc]X\W\^r|}tvw~o{{}ux|xrxy~}|ztekhw{}{rdkmyykghhnim{otvuulrl{qpcgz}wws~y{xz~umnv~q|yw|zyv}tnzwx}hoienoy{wolsxuspssgrsfjcg][`dvz{zyjrxrjqx|yn}{yywwy~}{rqkfevu{}kqfiljtrmdcaiwvk`]Y`\Shkku{}~{{rhtx}}vw|{kuyrjrv|riqlrjhccniszwz}x{|z}rtkqoltpia_V`oniinu~|tvxzhbkmutopzwgqtjbhkryhchhts{y}~|x{uuvpsq}||{n|zu}rqgolgpqpqxq~~z~r{|tkrovujelkV[^XS\gy|tt}x~vzggmqz~|{}vngmrtxotnimj{}{~vmtseqtttu~zxtryvwiols{vs~}y{{z~qlw{xzsko|zusx}x|usujdjt}suzrrvus{ti{|rszlpvuvqwtokbfantfos{wutw}rw}~~stmx|smoulsulorlswz|ywuvurzvfq|yxuidj]_bbhhpsw{y}yx|~z{upppr{~mkrxzpnlmj^oxzsw~{z|~vpuz~}|upvqoonltq`dmjji`\faku{~usy}wz~yypmnlky{v{hcjtytvx}~wx}tv|vt|tqmjyz~z{|}}vtuqonkmxsgpz~~z}|w|rqwztx}spj\ZZWVfhnwlo}}}y~ofkolmy{}y|~{utrs|~zllqcddfjor|u|ss|~wo}ulouwu~y}umsxx~|x{wvyr{wpoonvpo{wrt}|yu~|yuromr}{r|}|}ywxxzy~stmsvms~~~~vhitqna^clozuw}vw{wsyy{s{~rt~zyutoobcifjnrv~}}zysmjsieoyvtwztkdtz{vztwxts}|rlx~~yshmnt{vuwwmrwv}tu~wr|}xvvunn|yprlluupe`W`]U[mru{wuvvpvyyloz~~}{tjirs||{|{~{spqttmclpjs~wyxwmvxuvlsyzrnhjwrp}zw{{|xjiif`ffql]U]fnqy{zx~|xvuv{|wx~uqoy~~z}zyv}~{nfgfaZgkflmk||{}wtyw~{tmkmjinuww}{qz~vtntrxvnkkdefluz}}y}zy}v~~~plnsoo|zpyxon|tvy}}smilhsvlmsjjktu{~{}u~{wqwx{~}||yutufbpwmmrz|yxtq~ylfnyyzztnizvfn{{qs|yujgyvo{vn|}pzwnh^gijbcbcgdh}|pjy~kWPGOSRXjv{upko{wmrx|~ljdfi}ywj_iq||l`fywqipvpnpfmsyy{}tpqlw{|}~~}}updZOSRTe}mTQRX[mmiklzwujijszsq~~}kYZn}tvqrywxv{xjfYZ[]l}ypqnovw{tp{ztig_`_krv|z~}z{uf`PCId{x|wx~}~}vgckkpjnvy|yl]\QSUVdt|~zpgjin||vp~pkaa`mxrlloko}}}|~yz{oqlemzpsoswsqz|zjg\\ZZhxzmfkip}{uja[ggopv~ztvvwhgimipzwklc[dxuhf^a`hmhjlsk`dkp}z{qogbls{{}|yzvnozxvtywv{|wsqoekmy{w}yrspy}|}zveac]Xdtohd`d`mmmow|tpz~|phcYYel}~umqtp|}wlsx|{zyx{{y}tppqnnv|xffaYYfu~vmmhhroqs|zpott|zxtjjbY^emvqwtwx|zrr|z}xxlkjkggsxz~vtrpion{zzv}|wu}||}uwifhc_fpz{tnh\Yahz|{pdihomgkhhnlrsqoa`aea^emkroyxxvvorrv~}z|}y}yyqdc]VW_inu{}zzyy||yztmsxz||zwxk`_aa\]knv}~|twow{yxqmlflkouicagihlmgndbb_^dlss~|{wmims~}sglirvookjpqy{{sqsywrv~{|~{|~~xy}}~~}~qnha_cgjm|}}zs~~zv}~|xompxyqvt}wvzz{{zicbc]W\bcoks~ynqlhrstpjkikpmzyurvz{}|ursw{{|wofegs~voso{}ytlilmunrqd_`c^Y_bdmfkqssvuofmiisw|xtwvyzv~{vwxvzwzxplc\\^``gttzyyzuvv}ttte\Y[VPUZ_mkr{||x~}uursrksqolkkjnunssmmd^_bdbhts}|tldbcm{}y|t}xqrt}zx~tmikjdflm{{{~zy}|yxtt{vuwnneZY]_`dw|zx|xqkijo~~z}vxqs~wmkksununga``\\dfv}}x|{~{wxx}suwtwwvsrz||}~tquxwv|rifgjz~stllvx{rhffosnvqlgdc_]a`lqpsoov{|rnqjoorwtqsu|stvsx{{xv}~{}znfikkixyuvx{|wspwysxpkd`\XW\]nuy~y|xy|xuuw}stsotwvqntsuzswtibegdaor|z{|wkiimy{x|vsr{|{zxtqmhijixy}yy}y|}zxtyuyyruoc]^`\^mr}}|z{oeddkqs~|||xtt|x|zsqlf_[_\cqs|~{~~}{}tv||vwzy~~zqnppiqx~}zrd\]_ikw}xtyz|qkflpnvpnnke__a]joszvs{z||~~}w{|vzv{w{xogfgcbmq~~sqry|}z|sllrnurmljd\X[W^hkvxv{~|zpqv|xqrrs}x{}xqonmgopxpihjqny}}{|~zuqyy}~~}tmnlhssy}xu|~}||~xtw~yuwqyzuxtmfb`ZZacs{sonutt~||~~}~~wv{x~xxxrf`b\`fhrsqs~~xx}}}{zzvspioot{{}|xla]]eagooty~wyzy~|}~wlijcjknusnpy}~|z~zz}}wxz}|vqmgb]cakw|~~zrnpvpxzy|{vnqnovpooog[YXS\]dmnou~x|~|yupiflhr|{{{qicekenprx~|uwtv{{|ynkjemkqxwuw|{w{usw||xvnotqvtomjd]Zb_jv~|uopvpwww|~|~|{uvrrzwvwyujdc]ddhrttx~~{xyxv}{~}}woiolq{~}|x|{y|x~{smgcjfkppx~}wypnfkljsvuvy|~}{usy}yx}|tzwz}xuuri_cddqx}zuosqqvrw}~{}{{zxvnuustuuofc\`dcmsv{{|}~|uiklkuz|z}|}x}~~yu{w~}nbb^_adgrnknokjutywgmkpqzxihjirlfdfdfcbf`^^jgp||q^Zaqbrz}{|~rhgUZbjsy{vzrt{}|{~ykijpz{{vtr~}txqztx{tvw{zzytutyqu|ttpbYUOQWdn~xsp}~xthg_XXJHKIXgiihnvtu~tgl{~|ty}yw~yysnjjefhdv~zyqgkkrstst}~||uejkxpXMENfvyzx|tuyvrr{xvuluvjf]_gv~}|skipms{{zrq}{vznmwzyx~|z}ulsolslig_XXflrxwz{~{{yoslw~sfdqtzokrw}}~uoz{zsjbXNMXW[\Zadcdeku{z{qrvtvwuy}zxwzohlep}|z~w~wsx}zsqgm{~rjqtywtqqostg`fho}}lghqzwomyvneggfmc`^\VT`_^XXakojeeqxus{vusopx}~}}pllqpmtms~~vqyw{|uwvtzqou{vkmgipvp}}tovw|y~}y{o_]cmr~}pd_delhb\PH@JNRQT_gqkimx~}~}}~qsqoba`^krwqzyx||{|wvlb_g{~wotxttnx}v|~}~v~x|kfdfjq}}xts{snqquolc`^^ijmdhnwwvr{|zu||~x{wwu||yppqxvrsysodcbp{vz{ssjpquqowz|qiiop{{~{{{~|yv}prrnqpvga_dp{umty~{ylf^Yacg_ho{uts~~ywux|zu{}|xvlibi|~~yztnabjqvxvihbiovsksr{rst}|~}tx}|x|{{vwyvur~|x|uzogiiqx}|uu{wmnltw~tvz}~}z}~|v~~{xonrvyxsq}yvjgel}yt|xznkuz|}yijgjjomepp~}qnhljyzy~z{|}y}~wywxy{z~|usn|rlohoksmihdp{sx{rqllltjot{~{uz}xwns~}z~xmpkrxuytq]]imrxsfhdfhtpmspxqoovyy}~~vyy|qxvuxsswz}xuoqvzww{vxmmhkx~}}wwustvy}}}y~|{{~}xsmtux}znx~slniuxw{rfqxz~nhiiinshnmwrrjlhu|u~}vw~zywx|~~x{vty|w~{utm~xmmgplnlkjeov~x~wxurszput{}}vwv~xuktz{}zpto}{zz}v`empt{ofigdhtnsqt}srkmn|{w}|||xvy}}{ytysywquxt~~vtm{{rur{wvsplfqx}{~wwzwtz~~xyx|z~|zultuv{}ntwprm{~zzzjtz}tkmjhmvoqlr{tslkgt}u}|sszxyxttz~y{|zsx}v~{vtn|{olfolkjkhclpu|z}ztv~xyuw}|vtr~~{}{vlsux{ztvt}}{llqsv|sgkjefrqtqp{usljit~w{z{~yuv}{x}{x{v{zrsyru~xvnu}qnluxtrrqflqu}~w~}}wu}{wvzzz~zz|qqvtw~zlx~urtt{}yov|uopkgpxvulsxvrljiy}x~zqputzwppy}w{{{txzs}yuso{yoihomhhmhenns|y~~}wxxv~}wuq{~}{w}~z}{osttzuyvx{zxqsvw|{mlmifnuuumvywqkhj|wuwu|yrsz}wz}|{xy}uptvmyzyvroxvlhktupqwnkrqwz}}wy|wt~{z}|x~}wz|qtvtx{mx}vsx{{}|ruzxsrliny{ynqwxsmiiw~|}yqkooxxonvxy{|tswnv{wtpntxoggmojhpmhqptz}yyyv}yvsx~}}z~~ytw{|v{sturu}t|y|z|vuv{z}snnjhlwzxnpx{tnihuyqsryzpqyxx|~|vw|vpntkq~wuurorvmfgmsqpzvpvsu{{~yy}ztz{yzz~xuy}~v{}rvusv}|px}zu{{wtyzvsmloz{oqxyrmikx~|xoikmxvmpxxz}}|{trskwvvsonrtkfimqjlumnrpu|z}|{|}y~ytt~y{zz{~~}~|suw|wuztwrsv~xu~|z}xv}}~yrpjjks}}rlt|wpkhpz|zoonv{onu~yu||yy}wrlqkm|urtppqtofgipqnzxrvrtx{|||{v}zwx}y~wvv|}v~}tvrrt|zrz}{v~}zu}{xqonr~xos|wplioxxwkhhoxnjpx|u|{~|}~zqtol{wtvqpnrphhiopitslpmps}}x~}}z~{wt{zxx{x}~}}tutzys~|uvqqrzys}{}x}xskllq~vlr}xrmjpxz{pmmt|olpxzs{z}{yvmpljyvrtpqorqhhgnqlwyrtpqs|~y~~||x}|wv{|w{}{ywty}v~uuqqqx{ty}~}v|y}srsr~}rr~{tojpwzzpihlyqims~sz|||vttlw{twtspprlihkrlqwnnllmu|w{}}{zsw}uvvuw{|}{}zssrzvuytsppr{wu|}}~vmpntxnu~xsmlsx~yzonlv}ompyzt|z~}|ynrmlyvsurqoqqkifnplwyrrppq{}z~}||w|~wuw~uy~}z~|vss{vyyurpot|xx|~z{~~{svrzypyytkmsx}yxmjgsykknzyu|z}}|supozwuvusopqkigoqmwxpnlmnz|z~~~|xrx||usuux}~y{xyxspoxuvytrqor{yx{}{orpt{ot~yumlsu~~wypmks~snqyv{{}z|{ponitvstsrnnqnkgkqmu|vsqrqz}||y}uz~wvtwx||y{}z{{wspwwv~wrqop{{z|~}svuv~rt{wnkrt}xyqlhn{rlowvz{|x}}}srqluwttttpnpnkgkrov|vqnomw~}~|yzqvz~}vsrxy||~wz{wwwtomutv|upqnr{z{{~qtqv{pu}yukmst~|wwomju~sqr}x|{}}z}}zoplkstsstrmmpokgnqpy|vrqrr}~|{xt|}zwsx~xy}|zv~~~~ywyyvpswu|ztppmv{{||wuwuvq{|yqjqsxxxsoklzvnnsxxz{~x{|}urqmrvuuuvqnoqnhjqpv}xrnolu|}~zzqxz}|wssxx{}|w{~{vvwunnttx{tnpls{{|y|tvszxpx|xripsvyxtplkyzqps{yz{~z{|~wqpkostttvqmmpoiipou{yrnpmu}~~~~z|t{}}yuuxwz|{w{~{wwzxpqvu{|tnpkryz|x|vxv~zrz}ztkrtv~yxuqlkxwnlo}yvxz|~zz{~xsrnrvwwvxsoorqiiopv|yrlnjpy{~z{~sx{|}yus~ywx}x{~~}xvxxpmrsx~|umnkoxy|wzwxuz|rv|zvkots|{xurnju{soo{}xxz|}~z{|tsmnrtvtxuomorlgmoszzulmmny|}|~|wv}||ysz~xv~}yx|~~zvvzwortw}{qnnktx{{v|xxxzsz|{slssv}yxtrlmywplp}ywvyz}{y{xtqortxwwztonrsjinpw{zsknjox{~y~}ux|z}{ws{{wv}yy{~}yuvztmprv|}zomlktx}zv|yxx~wrxzyqkrru{xwsrlnxwros{xw{{~zx{xtpmpquuuxsnlqriimpw{{sknkrz~{}~uw|{~|yt|~zw~{wwy{|yttzvnqsw~|qmnlty~}wxxw}{sxzztlqssyxwssnmwysop|}yvyzz|ww}ytpmopuwuzvpmpunimov||xnomox|}||xu{y}}{zty{w|wwxy|~zusyxnopu|~}smnkrx}x~yxw{zrtxytkoqqwwvssnluyuqq}zvyzz|vv|yupmnnsusxuolotmilow|}ynnmpy~~|wu{y}~}|u{}y}~{uvvx~|~ysrywnprw~~snomuzxxwx|zsvxyrlqqrwwvssnowxups~}yuyxzztw|wtnmonvvvzuolrulkmqz|}vmomsz|}}~ux{y~{|xu~{z~~yuxv{~|}wru{snprz}~znnlov{{z|yw{~}tsuxvlnqosvvstqlsxwrpz|vxzx~wt{zvqmomqvsxwqlnupjlnw|}zonmow}}}ww{y}}}|v|}y|{uwux~|ysrzvnpqy~}qnmmuz}w~ywz}xtvxxonrpsvwttsmrwwtov~|vvyv~~xszzwrmonqxvzztnnvrkmnw|}|pnmnu{~y~yy|z}}}|w||x}|vyvz}ztt|wppqz~~|pmlmty{v~zy|~xtuxxposqsvxuutoswwtow}vvyv~ysz{xtoqnqxvzztnnvslmnw}}}qmmntzy~~{y|{}y}y}{}vyvw~}vt{zrqrzuonmsx~v{|x{}{wvx{tqtstvzwvwqswxwpt}}}wswuy{sv{yvoppowxz}yrouxponu~~wnnmswy{~z|}|~~{z{{|~}zxyv|zux~wsru~|pompt{yw}zy}~yuuyxqrtstvzvxvrvwyuqx~}x||wywzsha_fb\[`lpqukdcfjlurjpx~|~w}vicccca_`fojhlljjljinntunrufwjppr|yuqmptpwrcjxpvz~z|{xx}xx{t_ayq`Z\bj{|sjhabijproklusonp}zxy~~|}yt{wnnnnoligkqnnsvy}~|xxx}xv~ylfgca\`egt}~~qmlrzujiw~|vrt|ti`\f}yk_U\djkhljjrsvywqow|xxy~xtrqtqfejrwxz~xvxxwy}z{vtz}z|||}vyupe^^X^dfklhcfomjimz~~{soq}~z~}vc\[`ht~o^]__]X\]^jotywrpw{vsrx~}|sicbdgoohiow|{||xwupovy~}zw|}y{~~}}~zx}}|ury||urrmsxz}}xsv}||}|vqotwqfYY^go~ykifc[TVQT]`fgd^^fgcaalqqqgb_cgmvsnpv}~}}}yrrvwzxsnkprvxwvsw|yz{{|}{xz|}~zz{wrilrvuptrs}|wvywrpqzyqkg\Y]emv|wvsmb`\X^aege_[`ec`_gqrtoheglpy{tru}}{}}y}}~ytmqsuxxxtv}}~}wwy{{}~ythfjlmgijiry|~}zsrvwrnnu|wpki_[^djq~|qnjdjmprpieinkhfmwy{vplmqt}xuv}|yz}vx{}}~~xwxxx~ylgikjbcbajpvzzurx}|zw|wvxx~uppvvqmlr|yuokjb^_dhlxyvsnswz|ztnqvtqnt~~vqquwzvv}|xw}~xvvwu{~skllkca`]cjpstqlouvtpuxrpssz|uomsvrmkow~~}~z|}~yuqkkf``chks||ztv{}vv{|yuw}wswx}~xuz~yvyxutuuw~~rpopkdc__fkqrqkiorrnnw~ysmopszwpkmttpklr{z|~zz~}{yv{|{zuslijcabfkoy~zv{y{|x}~wvyz|wu|}xw{{trruvz~trrslfe`agmrspkjorqmnw|~}wpknorxtnjmssplnu}~y{}}~~xx||}{ywsxyyxtqjhhcbdins~{w||~zxwzz{vt{}zx}{zwspmwy{}zwxxviokenpsztmjru|xl{vz~yslnitvpncepmjgms|}~xtuv{}xuxp{zzwvsx~uurfhh`^fnxzy}~yyuu|zspquvxs}y{}mpnt~xyurnolpnlvswwttwwtypuy}yochnmzphe_ostqqrx~~}{vv}~}w}{p|wv{smiuw{zmoedjillorz|ytvs|~|xqq{w||~royppvtzwu}|umiojwzuyrjlyyvts{}}{tpojgrqgjcmuqfiwy{xvwtnw}|xptxzzuutsprwlof`kidbqx}~~xuzq|}uhlvz{|}voqptssy~wxyxlrrknnv|~{tv}xuovw~wzqfbijnljgosplt{{}}~~~zzx~ywttsvzsuskcopponjgjgelwy}|vyy~zutxx|z{u~~~z~wurrpr}z{|qjjqv~}xwusoyqqu~|pgnqmojiblnpnlju{~~uzyumxvopvy|xoinytsmmeggghhfp~}vtxzzxumo}yqy~||}vlxurjostoonov~wrs~usuy}~xrijcnqmdagqypmq{xy~{|~{sqwxrmqtzsqjkkrvpicceqlkjvv|vpvu~zrx~xwqx~u{typrlmsz}w{wstt|}yvtwwyvxtyzy~nmjqlkgbeorqnorq{~|zyxrstyurnmvoxolnurmkdbejkmjonzzqsu||~vtwzyurx{~vxwsqsqojy{|{tuwxzxwzyzuswt{yzupkljmij]iqquhop~wzryysuxxkpjpsqpnkonolkb^kgrihlywwn{z}~oqxy{txzzyw|~wpnwuupjv~~~uxqyzy{zz}~uvw{t~wumfgkolm]eoqrsot|xywvywsztplplrqkndjjrlncZeinqmozz{msyz~xpz~{{}~z{vs~}}~qmtvxtvp}xxwvzw~wyzvxysvyrpodjlokhdjrkusyz~}txvrvltpqmrmimjhnigonmhd]iipst~yyqsxt{r|}y|z~}xyys|txsptruqxr~~{vtvww|{{yzz||swrgmflikgfefsrr}zz{{{~uptqswqquiiokhkdichkjmheeftpst|~ttyrrvtyz|~}~{~|zyuxvzywtvuprwuyt{}|zsy{y~~~|}|}wz{~{xtehebknnggrtwuyyx~~~xzworosrhlhiqliaagknlmf_kkrrq~w{pnst{tx~|~}}~vx~yzsvvuux|snroyutzxyz}qx}y}y|~y}smiailjlfksvvxwsz|}~tsnosnoidpqlfafiimoidgfkopxuurnsruyxxyx}}uuz{|ywuqp{|utmnsqwuq}}twz{yzzx~~xznflkhgkonntyrpvv}zvosnotjjommjijhejoggidignvloywtsqtw}zpt{|}|w{wx~~~vmrzyutpqpmtrqz{~y}y||yz~{|uqqheglvojrsoopux}~|~xwuoopnqqhimpqgekfghhjhhkmlnqr~vquw{{rt|{{wy{|}yu{xntwrsqtvqooovysr||{~~~}~|y~ykfhmspnollnosvv}~~~|uqmnvsjhisulihcbdilmjjhgookt}vwv{vszyuvuz}~{y~{w{z{uxtqmluwxqnnpytns{}|y||}~wmklrlntkhhmttywx}{}vqpqunklmsoomca^bkmplhggmlpt{{ywpu|xsmv||}{yzx}||xuzzsmgmsyvroprrpts||}}}wwprnkmnnhdnryxwww~~xzwssokomrllqlg_]deonlkfhgkvtyx}squvzrmvz}|}{vw|~trzzxlfnnvsquqsmkvw|{}~|w|yxollhljhmoywvyux{{~yumhopsnkkkkb_ddjjkmihghrwytyxtttuqtvx{}wx{||uutvxmmlorjqvttlmow~{}}x{z}~upjhidonqvryyvxt}|~{mmkorooggjcicfiajlikfmmrwvy|~{uurkxx|zu{{}xxxvwttqnunrmfpstslpnsz~|y~y|z}~|skkdfoqwopxuwww}~|}zsoknppkihaikijadgejilnllqyx|}zukst~}rz~~~yrvtusxpoznzjconptqpvsqyuu~y{~wzq~zwplmkounrqjpuwx}|wrmrpxjikZgjoqfadedjnkmkfjrxyrxptws}z}y|}}|rtxwvnmu|onlinlgmnpywrwwux{zyzttnlrttqpqhov}{r|w|~}{~}xrkgsjjkappabb\djmpjhiip|zq~xuyw|vzzvy{{{|ox}pnnrutuunkgjqvwrqpnztv{|x|z~}orrhuqhmlpkwvxxu}z}peikkponqghlihcakfhogginfv{{~{yqwstuxuv~~y~yrvwwmwvkqhfilmnumrnnzz||{z|zo|omphmrtsnvztnsx|otwkmonojglfe[dg`egjirrjt|zzty{v{vot{~v{ywuv|tgyurmhiinptttqljvs{z|~x~{~}ppglmrthnsujrwsx{{ywvhkrnrdijlokhjb\afgjtgoqwvqz~{}u{ywqsrwy|qxsuxzppmlhekkqntmmqmr|z~}y}{wlkkjmqltqom}|u{uy|~z}trrqlgmgekjne`Zb`chnmtsssu}xwvq{|yws~|wt|wvuqkpoh`jjkoqoq|qzqt~~~||z|~qtroocinrnqwvt|zx|uz~zptgkoiaihnj__`e`nfhmowstz}z|yzzuszxxxw~ztuu~yqontjnedafqrrrrxty}s}v~yzusoqimgohp}u}w~yw}w{yzqgosiocdddij`\`hiqikti|~v|qu|{|u}x~|{~kvtu{xokirql]eii{r|uvxwxyxzzsw{uljjpektqzxvy}}}{v}|xrnkmqplXcdjgbiaigpniuxxssvzy{y~{|~vkwq|rlnpnembfnn|~tywy}}~zv~spqpjmsmwq{{uu}y~|wyv|kqlqiga^ieirgfjlrluqxv~|z~|s{ysy{x{~z}xxmtxqvyopotkmhhwqzyvtz||t~x~||{}|tqnimvuknrx{vx~|x{rl`_[T_clrts{xvyz{y|zr_c`ccp~}}ohirumlkfokhgiltsqofcbi\afdyzrr}|{|{wt|ltw~o`eotwuz}{}jfffzx{~~yjoql`shcmx}~xxmgacccppovxz|urv~}stxx~|tjghmy}{rxuqsgdpuutvss|z~w|tdgpmutqqtwx}xy{wjkimz|vmsx{{}l_WTr~vig`hyy}tokigsypwyzzyzuqlifklv|y||xqqmisspvv~}wqx}~uu{s~x~xvsgb^ZV^jcfmmvumhlv}}{oprlv|{wdUXWetiahlkmfcecbgvtpxu{}vnluuz}z|xqmjjnnqt|zz}}xuxporkhhkpvxxttxyywyuuvr|w}ywqjhpysz}}w}zxvmrrpz}ttwj[QX\qojmpng_X[TRU`_]eenmf[\fiqruwwuqrqtptx~~~|vuuqljmmpx~{xyyyvrzzyrsuy}upqp|zpmu{{zsu|}rsrtqxyt|{}~rfcmd\TYduxtuy|sl``WRT\_\abknh_[egru{~~uwvzutz~~}xyywqwssw~~xyww|{~}~ujnq{}}ymelr}xtwnpt~~|~}yxlopsu|x}y}||swrvxja\id_Z]jz}}}|xkld^^bhgjluuogcmoz{y{z~wu|}{~wz{yxrgnv~|xhbjn}wuolnfjktww{t{z~wwurhppsy|y}||pkqiroc\\kdc[ap|~rshhglrqrw{wmptxz~{}rx|}~{r{vmepz~xdghqzqrhicadfopqv{tquu}svttjjqow}}{z~~}y|}}temjitgbYehec[iw{xrnpmwywzytxvz}}~vs{x}qw{uqclvkhkm|qsjeb]b^jomp}}xtnrozyrsstmhqms{~|y}wy{}~}}~|wfkkfrhe[bhedXcr{vqtoy~{}~x{x{}}}ur{w~~}quyvq`is~qooq~rukea_a\iokp}zvrmomyw~osstjhpjry}|{z~xyx}~{|z}|wfnlgrih]bggdVan{|wuwp||{{x{}~~vr{u||}rrxvs`epx{ttrwyrgb`d]hplmzzutnokvw~|ppsslfojpx||~{wyy}wz{|y}{x|{jlpepkja^ggjX\js~yuyqy}}}x~z~zpyvy}z{wlsswh`msxzr}~x{mfagabqpkt~vwroloxy}~vlrrpelmkuz}}w{~{zyw|{x||u{|{vgqiflhi]aekeXeoy}vyxr~~{yx{}|ssyu~v}yzqkptvceqv{zs||{ndci`hspmw}vwqnkqx|{|qmqrmemloy|~~v}}}}zwx{{y|{r{yzsgpfehfg[_enf[jr|}v|yu~zzwz||rtyv~~sxuwphmswghty~{s}}}}qedkcitrmv}vytnkow|{zqkoqmemmq{~w|~|zwx{zz|z~rvxxugme_ccf][bnmalv~w{}u{yxwz|trzx{rqrtqfhqzokv{|sy~}vhdmfhtups|wyvnkmu|zxqjmpnfmnq|x{|yvw{y}~||x{qs~wvvije\_bf^Zaoqgpzx}wzyxwy|ut{zz|omorpfgr{poy~|sz~whgofkvvpt{wzumlnv}ywojlpmgoov~w}~~xtxzx|~|zvz}ms}yturgiaZ]bd]Zetpku}x|z|zz~ww{{tx{~|zvkkorldixyot{yu}sflmgrvuowxw{pklpz}vsljmqjkop|~zx}{uuzxy|~|vv|rlxztrtmgf[Z_eb[^oulqy{{z{|yvx}wvz{~yxmhmrogft{rrw~~v{{jlqjqvxqsyw|skmmwwtnkkqljonz}zw~wuzxxz~vuyujtxtrtphg]X^dc][ltnot}~~z||{wx}ywzz}zxnhnrqhetzrqt{y}}loslswyrsxx|qkmnx~vsojlqlkmnyzxv~wwzywz~vuxsjtvtrtrig]X`ed][krllpzz}|}xx~zxyy||xphnsrkdrzsqqy||}qpuosw{urww}tjnmuwsqljqolmlvy|~xt|~ywzyxxvtu|wiptsrrtkg`X]debYfrnllv{{~}{w}}zzx~xsiiqrqfjywspu{||ovtrvy{rtw{{mmno{ztspimqnmlqxy{tx}|}wyyyvz|stu|oiqqrprrhd\Wadf^\nromoz}{~|xx~|{zzz|uofjpqodozwtpy|}{rxtuw|{ruw|ylnnq}ystoioroolty{|tz}||yzyyv{{ssu{mionrorqgc[Xbdg__psror}{~|ww}}|y{y}~~{smdhnppenzxwrz~}~txvuw|}tuw}{mnnpzzstqinrrqnt{}uz|~zzyzuy}sss{phmmpooshb\W_chd^nwvttz|{~yu{}}|zzy||{}rlddlnqgjz{{vz}vxxvw{wuv{}pnopx{stsknrttpt|xy{~zzxzuw~tsqzrhlkoonsib\W^chf_oxyxvz{z}yu{}|{yxzz~z{pjccjmqhl{||w|}xyxww}xuv}|pooqyzsuskoruuqv}w{{}{zwysx~|srrzoiljpmorg`ZW_djebsy{xyz{{~ww{~{}}vxv{{yvlfafkpofr|}{wyzxxy}wuxxnoot|wsvomqtxtrz~}v|||xyws~~vspvvjkjlpmsmc]X\bhjajxzzvzzz~}zvz|}z}}xwvw|x~zngbcinqgly{|v}||zyxzvv|qposyyswrmqsxuqw|v}~}}}yxxr{}ytpsxlkjkqmspd^Y[afkbgtxyu{|{z~}|wz|~y~}|ywuw|y~{ngbdhnqgkwyztz~}{{y{vw{rqpuzytxsnqsyupv{u}|}~~zyyr}~yuquynljlqntqe^Y[`fjbfrvwry}}|{}}wy{~x~~|}zwuu|y~|phbcglqhitxytw|~}}z~xv~trpux~|twuoqrxwptyuz}{~~|yzry|vrryqlkiqosth_ZZ_dkdcntwsu|~}{~yzzz{~~{wvryzzvjdbeipmgovywt{|~~{|}vzysqrwz~xuyrpqu{tqv||u}{~~yzwr~~yupuwnlhlpoupd\Y]`hjcgquwry|~|{~~yz{y}~||||wtrzx{}rgbbejqkhrwzvv|}~~{~|u|xsptw{}vvyqqqv{srw~{w}z~}~xzur~~~xuouunlglpotoc[Z]`ijdirwxt{}{{}~zz{z~{{{{vspxwz|rfabeiqmjqw|yx}~~{|~uzxtpsvx|wuysrqu}vsw}~w}{~{}~yyvp{}~xuoruokfionsqf\Z]`hmgirx|x|~|y~~z{z}~||}x~|wsnuwx|uhaadgqpkqx}|z~~|{vwytpquv{xtytrqt~ytw}x}{}z{}ywvox|}yvoqtolfgnnrrg\Z]`hniksy~{~}|y}~{{z~z{|w}{wrmtvx{th`adhqpmrx~|~~{{uwxupruvzvuytsqvyux~y}z~~yz}xwsoy~|{xtnqsnkdhmnspe[[^aknjnt||~{z~~}|{|~xxy|yunnuuyypd`cdlsoot{|~}~z{s||vsotuwyuwxtsr{~vwy{{|zzx{|wwos|||ywpnspngelmqsl_Z]_enllqw~|~}~y}~~}}zz{{w{zwqlsuxztg`bdhqpnqw~}}~{}ty~xuptuvyvvyutqxxvw}z|z|xz~wwqqz}|zxrnrqoidkmqsnaZ]_cmlknt|{|~~z~~}z~z{|x{zxqmsuy{uh`bdhqonpu|{{||uz}yvquvvyvwyutqx~wuv|z{z|x{~xxqr{}}zysorrpieknrtnaZ]_clkkmrzyz|{~~z~{{~x{{zsmruy{vj`bdfoomorzzz{}~~wx~zxrtwuxwwyvuqu~yuu|zzy}xyzxsqx}}{zvorrrleimqtqf[\_`jkjknwyy{|}}{|~~~zyzz{wnotw{ypc`ddkpnnov{y{{}}v||zvrwvvxwyxwtqz}vuv|zyz~zv{~yvpsz}}zzsprrqhflostnb[__dllllqyy{{|{~{zyzz{umruz|yoccefoqppqy{{||}{v|{zuswuvxxzyxts}}xvx{zxz}yuz|yupsz}|zzrqqsrigmpuuoc]aagnnoos{{}||~|~{yyzy|vnruz|zqedghpsssrz}}}|~||v{{zwswuuwx{{zvt|zwx}{xy|ztx}xuprx|}yztpqrtkgmpvvsh_cdhpqrqt}}}}}~~~}{x|yyx{ypquy}|uieiipttutz}~{~}uyz{xsvvtwy||{xt{}yx}|xy{z~ulmtrnlimt{~zzsnoquokpt{}|tmqqrxwwvw~~~yxz~}}z}~{w~|{xx~}||wy|vtxuyzxxu{~{ysvy}{~~|vz{|yuyyz~~z}yxz~xurrwunhlsponlrx|}vrruyqpuy~vqutvzyzwy~|{ux|~~|zss{{xxuxyyyzvz}vyyx}{{yx}|{ww{}|}~{rsuv|~~{|x{{tkhoqmmjlrw|xxtmlmsqkpsy}}xpqsqvuvurxxxuty{||vrx|yyvw|wwv||vz|yxzw|{{{x|||zw{}|~}}~|squv{}~{~{|~yqkquqqnnrw|xvtmllqrmpsy}}zqprpttturvwwvswy}{~xsy}{{xx|~wvv|}x|}zyzw{z{{x|||zwz|~~||}ssuvz{~}{~{}zrntwttpqtw{wvsmllstortz~}yqpqossturvvwvsvx~|~~zuz|}yy|~}wvv|z}~{xzvzzz{x{||{wz{~}{}~vruuxz}~z~||}vptywwsqtvzxuuonmrvqtuy~}{tprorssusuvwxsuw|{~}~xx~~|y{}}ywwz||~xzwxzy|zy|{}yx{}~~|{{sttvz|||~{~{tryzxxstuwzuvsoopvvsvv|~|yrrqpsruusvvxvtwx~{}}}x||z|}}~xxx||~~}yzwyzz|yz{|}xz{~~{z~yrttwz}|}~{{uszzzytuuwyuvsppqxwuww}~|xrsqptsvutvvywuwy}{}}}x||z||~}~yyx|}}zzwyzz|zz{|~z{|}~{y|}~zrttw{~~~{~|wtz{zzuuuwyuwuqqqwxvxw|~}zutsqttvwuvvyyvxzz}|zz~z|||~zyy{~{{xyzz|{z{{{{}}~{yz}|}~{stuw|~~{}|xtz{z{vvvwyvwvrrrwywyw{}{vutqutvwuvvyzwy{~z}}z{~{||~|~zyy{~||xyzz}{{z{||~}}zx{|||}ysuvy}{~~|xv{{|{wwwxyvyvsstyyxzx}}zvvssutwwvvv{yxz|{|}~~z~}|}}}~}zzy}~~~|zx{z{}{{z}~{}~~~~~{yx}~~||||utvv{~}}~{wy}{~zxyxzxxyuttwzx{y{~|ywvsvuvxwwvy{xz{~~}{}||}}~}~}~{zy|~}|xzzz}{{z|{|}~~~~{yw|~||||wtvuz}}}yz}|~|yzy{yxzvutvzy{zz~}zxwsvvvxwwux{xyz}~~{||}~~~}}{zz|~|x{zz}{{y|~{|}~~~~~{yx|}||||vtvuy|~~~}z|~}}{{z{yyzvvuwzz|z{}zywtvvvxwwux{xxz|~~{|{~~~~}{{z{~}y{{z||{yz~{{|~~~~}~{ywz}}|||xtvux{~}~||~||{|{z{xwvwzz|{z~|zyuvwvxxxvv{xxy{~{|{|}}{{z~~~~zy{z{|{zx}|{|}}~~~|zxx}~}~||{{uuuuz|~~|~}}||z{{wwwy{{~{}~{{xuxvwyxwuy{xyz}~}}{|{~~~|{{{~~yz{z||{yy~|{|~}~~}}{ywx}}~}|{{yuvvvz}~~|~~}|z|{xxwz{|~{~~||xvxwxyyxuy{yz{~~}}{|{~~}|{{{~zz{z||{yy~||}~}~~~~||{yww|||}|{{zvwww{}~}~}}z||xxxy{||}}}zvyxwyyyvy|z{|~~|~~{}|~~}}{{{}~{z|z{||zy}}|}~}}~}|{{xwvz{{}|{zzvwxw{~}~}{||yyxz{|}}}}{wyxxyzyvy}{||}|~}{}|~~}}{{{}~{z|z{||zy~}}~}~}}}~{{zxvw{{|~||{zyvxxx|~~}{}|yyy{{~}~~~zwzxyzzyw{}{|}~||||}|}||{|}~z|{z||{x{~|}~~}}~~|~|z{ywvy{z}~|{zzwwywz}~||~{zzz{}~}~~}xyyxzzzwy}{|}~}|~}|}|~~~}|||}~z{{z{{{yy~|}}}~}~|~}z{ywvx{z}~||zzxvywy|}}|zzz|}}~yzzyzzzxx|{|}~~}|~|{}{}~~}|||}~z{|z{{{yy}||}~~}}}~~|~|z{ywvx{z}~||zzwwxwy|}}|{{{|}~~yzzyzzzxx|{||~~}|~|{}{}~~|||}~{{|z{{{yy}||}~}~}~|}|zzyxwx{z|~}|{zwvxwx{~~}}{{{|}~{z{yzz{yx||{|}}|}}{}|{~~}}|}}}{|{z{{zx{}{|}}}~~}}}zzzxwwyzz~}|{zyvwxwy|~||||}~~z{{z{{{xz}{|}~~~}|~~|{}{|~~}}|}~|{|z{{{yx|||}}}}~~~}}}yyyxwwzz{~}|{zxvxxxz}~||||}~z|{z{{{xz}|}~}~}||}~~{{}|}~~}}|}~|{|z{{{zx|||}~}~}}~~}||yyywwwyyz}}|{zywxyx{}~}|||}~{||z{{{yz}}}~}~}|{|~~|{}}}~}}|}}}{|{z{{zx{}|}~}~}}}}|||yxywwvyyy}}|{zywyzy{~~}}|}}~{||{{{|zz~}~|}}|{|~}~|{~}}~}}|}}}{|{z{{zy|}|}~~~}~~}}}}||{xxxwvwyyz}}|{zywzzz|~}}}}}{}|{{|{y|~}~~|}|{{}~}}{|~}~~}|}}~{||z{{{yz}|}~~~}}}}}}|{|zwxwwvxyy|}||{zxx{z{}}}}}~}|}{|||zz~}~|}|{{|~}~{{~}}}}}}~|{|{{{{yy|||}~~~~}}~}}}}{|zwxxwwxzy{}||{zxwzzz}~}}~~}}}||||{z}}~||||{}~}}{{}|}}}}}~|||{{{{yy|||}~~~~}}~}}}}{|zwxxwwxzy{}||{ywwzyz|~~~~~}}||||{z}}}||||{}~}}{z}||}}}}~}||{{z{yy{|{}}~~~~}~}}}}||{wxxwwxzy{}}|{zxwyyy{~~~~~}~}|||{z}~}~||||||~}~~|z|}|~~}}}}|||{zzzxz|{|}~~~~~~}~~}}}|{|ywxwwwyzy|}|{zywxzyz}~~~~~~||||{{~}~~{||||}~}~}{z}||~~}}}~~||{{zzyy{|{|}~~~~~~~}}}}}|{{xwxwwxzyy|}|{zywyzz{}~~~~~}|||{|~~~~{||{{}~}~}{z}}}~}}}~~||{{zzzy{|{|}~}~}}~}|~}}}|{{{xwwwwwyyy|}||{ywy{z{}~~~~~}}||{|~~~~{{{{{|~}~}|z}}}~}}}}~|||{zzzy{||}~}~~}}}}|~}}||{{{xvwwwwyyy|}||{zxy{{|~~~~~}}}}||~~~{{{{{|}}~}{{}~}~}}}}~|||{zzzy{||}~~}~}}}}}|~}}||{{{wvwwwxyyy|}||{yxz{{}~~~}}}}{}}z{{{{}}}~}{{~}~}}}}~}||{{zzyz|||}~}}~}}}}|}~}}|{{{yvwwwwyzy{}}|{zxx{{|~~}}}||~{z{{{|}}~~}{{}}}~}}}}}|}{{zzyy|||}~}}~}}}}}}}}||{{zwwwwxyzz{}}||zyx{{{}~}}}||~~{{{{|}~}~~}{{}}}~}}}~}}}|{{zyy|||}~~}}~}}}}}}}}}|{{zwwxxxz{z{}}}|zyx{{{}~~}}||~~{{{||}~~}{{}}}~~}}}~~}}|{{{zy|||}}~}}~}}}}}}~~}|{|{xwxxyz{{{~~}|{yxz{{|~~~}}|~~|{{||}~~~~|{|}}~~}}}}}}|{{zy{|||}~~|}~}}~}{{zyzz{{|}|{zyxxyz{}~~~~~~}|{zzzz{{{|}~~~~}~~~~~~~}}~~~~~~~}}|{zzyxxxyyyz{}~}xuy}zyyz{{{{zyxxxxyz{|~~}}}}~~~}||{||}}~~{{~~}||{{||~~~~~}ywvwz~~}{zyxvutsrsstvwxyzzzyxxxyz{}}||||}~~~~~~~~~~~|{|~~}|{zyyzz{}~~~~}{yxwxy|~}{zxwuttttuvwxyzzyyxwwxy{|~~|{{{{}~~}~~~}|{}~}|{zyyz{|}~~~~~|zxwwy{}}|zywvuttuvwxyzzzyyxxxxz{}~~}|{zz|}~}}~~}|{|~~}|{zyzz{|~~~~~|{yxwxz|~~|zywvuuuvwxyzz{zyyxxxyz|}}|{zz{|~~~}~~~|{{}~~|{zzzz{|}~~~~~}{yxwwx{}}|zxwvvvwwxzz{{zzyxxxyz|}~}|{zzz|}~~~~~|{{|}~}|zzzz{|~~~}~~~~}{yxwwxy|~~}{yxwwwwxyz{{{{zyxxxyz{}~~}{zzzz{}~~~~~|{z{|~}|{zz{{}~~~}}~~~~~~~}{zxwwwxz}~|{yxxxxyz{{||{zyyxxyz{}~~}|zzyz{|~~~~~~}{zz{}}|{{{{|~~}}~~~~~~~~~~}|zyxwwxy|~~|{zyxyyz{||||{zyyxyy{|}~~|{zyyz{}~~~~~~~}|{z{|~~}|{{{|}~~~}}~~~~~~~~~~~}|{yxwwwxz}~}{zyyzz{|}}}||{zyyyz{|~~|{zyyz|}~~~~~~}|{zz{|~~}||||}~~~}}}}~~}||}~~~~~}||{{zzzzzz{|}~~|zyxxyz{|~~~~}|{{{{{|}}~~~~}}}}}~~}}}}~}{yyyz|~|zyxyz|~}{zzz|~|{z{}~}}~~|{zz{|}~|zxvvvvxz}~~~~~~~~~~}||{zzzzz{}~~|{yxxyz|~}}~~~~}}}}~~~~~|{zzz{|~~~~~~~}{zyyz{|~~|zywvvwy{~}{zyyz{}~|zyxxy{}}{yxxy{~}||}~~}}}}~~}{zyyyz|~~~~~~}}|||}~~}||}~~}|{zyyyyyzz{{|||}~~~}{{z{{}~~|{zz{|}~|{zz{}~}|}}~}||||}~~}{zzz{}~|{zz{}~|{{|}}{{{|~~||{{{|}~~~}|zywvvvvxz|~}|||}}~~~}||||}~~~~}||}~~}|{{{{{|}~}{{{|~~|{{|}}{zz{}~~~~}|{zyyyzz{{||||{zyyyyz}~~~~||||}~~}}|{{{||}~~~~~}|||}~~|zyxyz|~}|||}}|{||~~~~~~~}||||}}~~}|{yxxxxyz{{|{{{|}~~~}|{{{|}~~}{zyyyz{|~~}||}~}{yxyz|~}}}}~~~}}|||||}~~~~~|{zzz{}~~~}|zxwwwxz{|||||}~~}~~}||{{{||}}}~~~~~~~~~~~|||}~~}{yyyz}~}}|||||}~~~~~~~~~|zyz{|}~~}{zxwwwxz{|}}~~~~~}}}}}~~~~|{zzz{|~~~~}~~}||||~~~}|{zzz|~~}}}}}~~}}|{{{|}~}~}{zyyz{|}}}|zywwvvwxz|~~}|||||}}~~~~~~~}}~}|{{{{{|}}~~~}||||}~}|{{|}~}}~~}{zyyyyzz{||||{{zzyyz{|}~~}}}~~|{{{{}~~~~~}|{|}~}|zyxxxy{|~~}|{|}}{z{|~~|zzz|}}~~{zyz{|}}{ywvuuvwxyz{|||||~~~~}}}}}}~}}|}}|{|}~|zzz{}~~~}{zyyz|~~~}}}}}}~~|zyyz}}||}}||}~~}{ywvvxy{}~~}{zxxxyz|~~}}}~~{zyz|~~~~}{{{|~}|{{zzz{{|}~~}}}~~}|||}~~}}~}|{zz{{|}~|{yxwwwwxyz{{zzzz{|~}~|{{|}~|{zzz{}~}}}~}||}~}{yxyz|~}|z{|}||||}~}||{{|}~~}}~~}~}{xwwyz|}}|{zxxwwxxy{|}~}}}}~|{{}~~~~~}||{zzzz{|}~~}}}~~|{|~~}|}~}|zzz{}~}}|||}}~~|zywvvwxz{}}}|zxvvwy}}|{zzz{|}~}~}|||~~|{{{|}}}{zzz{}~~}|{{{{|~~~~~~~|zyy{}~}{xwvvwyz{|||{zzzz{|~~}}}~}||}~~~|{zz{{|}}}}}}}}~~~~}}}}}~~}}~~}}}~|zyyz{}~}}|}}~~}|{{zyyxxyyz{||{zyxwwy{}~~}|{{{{|}~~~~~|{{|}}{yxxy|~~}||{{{{|}~~}}~~}|}~|zz{|}~~}|zyxwwxyzz{{{{{zzz{{|}~~}}~~|{|~~||{{{{||}~~~~}|{{{{|}~~~}}}~~}{zzz|}~~~~}|zyxxxxyz||}|{zxwvvxy{}~}}}}~}}}}}}~~~~}}~~|{|}~}|zxxy{|~~~}||{{{|}~~}||}~~}|{{|}~|zyxwwxyz{{{{zzyyyyzz|}~~}}~~}~~}}||{|||}}~~~~}|{{zzz{|~~|||}~~}|{{|}}~~~{zxwwwy{|~~|zwvuuvxz{|||}~~~}}}~~~~|{|}~~~}|{zxxyz|~~}{{{|~~~~~|{zzz{}~~}{{|}~~|{{|}|zwvvvxy{}~~~}|zywvvuvwxz}~~}{{{~~}~~|{{{|}~}|zywwvwxy{~~|{|}~|zyyz|~}|zzz{}~}|||||}}~~~~~}|{ywvutuvxz{||||}~~~~}||}~|||~~}|||||||{z{|}~|{zyzz|}}|{|}}{zyz{|~~|{{|~~~|{yxxxxyz|}~~~}|{yxwvuuvwxz|~}||~~}}~~~}}}}~}{zxxwwxyz|~~}}}~}||{{{||}~~~}}}}}}}~~}{yxvvvwxxyzzzz{||}}|{{{|~~~~~}|{zz{|~~|zyxxy{}}|||}~~}}}}}||{{zz{}~~}{zyxxyz{|}}~}}{zxvuuuwx{|~~~~}}}~}|||}~~~~}}}~~|zyyyz|}~~}|{zz{}~~}}|||{{{{{{{|}~~}|||||}~~|zxwwxy{|}}{ywuuwy|~~|{{|~~}}~~~}|{z{||}}}|{zzz{}~}}}}}}~~~~~~~~}|{{{|}~}|{z{|}~~}|zyyyz{||{zxwvvwy{}~~~~~~~}~~}}~~|zyyy{|~~}{{z{|~~}|{{zzz{|}~~~}~~}|{|}~}|zxxxyz||}|{zyyxxyyz{}~~}|}~}{zyyzz|}~~~~}}}}}~~~}{zzz|~~}||}~|{zz|}~~|{yxxxxyz{||}}}}}|||{{||}}~~~~~}||}}~~~~}|{{zzz{|}~}}|}}~}|{z{|}~~~~}}|{||{{{{{{|}~~~~}|||}~~}|{{{}~}}||~~}~~~}}||{{z|~}|{zzz{|~~|zxvvwx{}~}}}{z}{zzzz{|}~~|{{{{|~~}{{zzz{}~|||}~~}||}}}~~~~|{{}~}}}}~~}|{{||}~~}||}~{yxwwy||ywuuuuwxz|}}}}{zyxwwxxyz{||}}}}}||{zyyxxz||{z{|~}|{{}}|{{|}~|{zz{}~~|{zz{|~~~~{xuttvy}}|||}}~~~}{zxvuttuvwy{}~~}|{yxxxxyyyzz{}}{zz{}~{yxxy|~~~~~~}}}}~~~}{{{|}}{xvuuvy}~{zzz{|}~~~}{zxwvuvvxy{|~~~}|zyxwwxxxyz{|~~}|{{|}~~~~~}{z{|}}}~~|{zz{}~~}|{{|~~~}~|ywuuvx|~~~~~~}}|{zyxxxwwxyy{|}~~~}{zxwwwwxyyz|}~~~}}~~}zxwxz~~}}~~}{{{|}~~}}||}~~~~~~~~|zxvuvx{}}}~}|zyxxxxxyyzz{|}}~~~}|zyxwvvvwxz|~}}|}}~~~~|zyy{~~}~~~|{{{|~~}}}}}}}~~~}}~~|zwvuvx|~~~}{zxxxxyyz{||}}}|||{{{zzyyxxxxxz{}~}}}}~}{zyz}~}|||}~~}|||{||}~||||}~}{yvuuvy~~~~}{zxxwxyz{|}~~~~~}|{zyxwwwwwxyz|~~~}}~~~|zzz|~}}||}~~~~}}}}}}~}|{{|}~~~|zxvuux{}|{zyyyz{{|}}}}}|||{{zzyxxxxxyz|~~}|}}~}|zyz|~~||{||}~}}||{{|}~}|{{|}~~~|{xvuvwz~~}{zzyzz{|}~~~}|{zyyxxxxxxyz{|~~}}}}~}{yyy{}~}}}}~~~~}}}}}}}~}|{{||}}}|zywvvxz}}|{zzz{{|}}~~~~~}||{zyyxxwxxyz|~~}}}}~~~~~|zyyz|~}||||}~~~}}|||}}}~}||||}}}}|{ywvuvx{~~}{{{{{|}~~~~}}||{{zzyxxxxxy{|~~}}}~~~~~~}{zyz{~~}}}}~~}}|||||}}~~}|{z{{||}||zywvvxz}}|{{{{|}~~~}||{{zzyyxxxyz{}~~~~~~}}~~}{yyy{}~}}}~~~}||||||}~~~~}|{{{{||||{yxvvvxz~~|{{{||}~~~}|{{zzyyyyyyz|}~}}}~~~}}}~~~|{zzz|~~~}}~~~}|{{{{|}~~~~~~|{zzzz{{||{zywwwx{~~}|{{||}~~}|{zzyyyyyyz{}~~~~~~}}}}~~~}|{zz{|~}}}~~~}}||||||}~~~~~~}|{zyyzz{{{{zxxwxy|}}|||}}~~}|{zyyxxyyz{}~~}~~~}|||}~~~}{zyz{}~}}}}~~}}|{{{{|}~~~~~~~|{zyyyzz{{{zyxxxy{}~}}}}}~~}|{{zyyyyzz{}~~~}~~}||||}~~}|{zyz|~~}}}~~~}}||{{||}~~~}~~}|{zyyyzz{{{zyxwwxz}~}}}}~~~}|{zzyyyyz{|~~~~~~}||||}~~~~|{zyz{~~}}}~~}||{{{||}~~}}}~~}|{zyyyz{{{{zyxwwwy|~~~~~~~}||{zzzzzz{|}~~~~}}|||}~~~}|{zyz{}~}}~~~}||{{{{||~~}}}~~}|zyyyyz{{|{{yxwwwx{}~~}~~~}|{zzzzzz{|}~~~~~}}|||}~~~~|{zyyz|~~~~~~}|{{{{{|}~~~}}}~~~|{zyyyyz{{{{zywwwxz|~~~~}||{zzzzz{|}~~~~~~}|{||}~~~}|{zyz{}~~~~~}||{{{{{|}~~~}}}~~~}{zyxxyzz{{{{zxwwwx{}~~~}|{zzzz{{|}~~~~~}||{||}~~~~}{zyyz|~~~~~~~}|{{{{{{|}~~}}}}}~~~|{zyxxxyz{{{zzyxwxy{~~~~}|{{zzz{{|}~~~~~}|{{{||}~~~}|{zzz|~~~~~~~}|{{{{{||}~~}}}}}}~~~}|{yxxxxyz{{{{zyxxxz|~~}||{{z{{|}~~~~~~}|{{{{|}~~~}|{zz{|~~~~~~}|{{zz{{|}~}}|||}}~~~}|zyxxxxyz{{{{zyxxyz|~}|{{{{{{|}~~~~~~|{{{{{|}~~}}|{z{{}~~~~~}||{{{{{|}~~}||||}}~~~}|{yxxwxxyz{{{zzyxyy{}~}|{{{{{|}~~~~~~}{{z{{|}}~~}|{zz{|~~~~~~}||{zzz{{|}~~}}|||}}~~~~|{zxxwwxyz{{{{zyxxyz}~}||{{{{|}~~~~~~}{{z{{|}~~~}|{zzz|~~~~~~}|{{zz{{|}~~}}|||}}~~~}|{zyxwxxyz{{{zzyxxyz|~}|{{{{|}~~~~|{{z{{|}~~~}|{zzz{}~~~~}|{{zzz{||}~}}|||}}~~}|{zyxxxxyz{{{{zyxxxz{~~}||{{{|}}~~~}|{{{{|}~~~}|{zzz{|~~~~~}|{{zzz{{|}~~}}|||}}~~~~}{zyxwxxyz{{{{zyxxxy{}~}||{{||}~}|{{z{||}~~~}|{zzz{}~~~~}|{zzzz{{|}~~}||||}}~~~}|{yxxwxxyz{{{{zyxxxy{~~}|||||}}~~~}|{zz{{|}~~}}|{zz{|~~~~~}|{zzzz{{|}~}}||||}}~~~~}|zyxwwwxyz{{{{zyyxyz|~~}|||||}~~~}|{zzz{|}~~}}|{zz{|~~~~~~}|{zzzz{{|}~~}}|||||}}~~~~~}|zyxwwwxyz{{{{{zyyyz|~~}}||||}~~~~~|{zzzz{|}~~~}|{{{{|~~~~~~}||{zzzz{||}~}||{{||}}~~~~}|{zyxwwwxyz{{|{{zyyz{}~}}|||}}~~~~}|{zzzz{|}~~}}|{{{|}~~~~~}|{{zzzz{|}~~}}|{{{||}~~~~~}|{yxwwwxxyz{|{{zzyyz|~~}}||}}~~~~}{zzzz{|}~~~}|{{{{|~~~~~~}||{zzzz{{|}~~}||{{||}}~~~~}|{zxwwwwxyz{||{zzyyz{}~}}||}}~~}{zzzz{|}~~~}|{{z{|~~~~~~}|{zzzzz{|}~~}||||||}~~~~~}|{zxxwwxxyz{|{{zyyyy{|~}}}|}}~~}{{zzz{|}~~~}|{zzz|}~~~~}|{zzzzz{|}~~}|||||}}~~~~~}|{zyxwwxxyz{|{{zyyyyz|~~}}}}}~~~}|{zzz{|}~~~}|{zzz{}~~~~}|{{zzzz{{|}~~}||||||}~~~~~}}{zyxwwwxyz{||{{zyyyz{}~~}}}}}~~|{zzz{{|}~~~}|{zz{|}~~~}|{zzzzz{|}~~~}|||||}}~~~~~}|{zxxwwxxyz{||{zzyyyz|~~~}}}}~~}|{zzz{||}~~~}|{zz{|~~~~~}|{zzzzz{|}~~}}||||||}~~~~~}|{yxwwwxxyz{||{{zyyyz|~~~}}}}~~~}|{zzzz{|}~~~}|{{{{|~~~~~~}|{zzzzz{|}~~}}|{{{||}}~~~~}|{yxwwwwxyz{|||{zzyz{|~~}}}}~~}|{zzzz{|}~~~}|{{{{|~~~~~~}|{zzzzz{|}~~}||{{{||}}~~~}}|zyxwwwwxy{{|||{zzzz{}~}}}~~~}{zzzz{|}}~~~}|{{{|}~~~~~~}||{zzzz{{|}~~~}|{{{{||}~~~~}|{zyxwwwxyz{||||{zzz{|~~~}}}~~~}|{zzzz{|}~~~}||{{{|~~~~~~}|{zzzzz{|}~~}||{{{||}}~~~}|{zyxwwwxyz{||||{zzzz{}~~}}}~~}|{zzzz{|}~~~}||{{{|}~~~~~}|{zzzzz{||}~}||{{||}}~~~~}|{zyxwwwxyz{|||{{zyyz{}~}}}~~}|{zzz{{|}~~~}|{{z{|}~~~~~}|{zzzzz{{|}~~}|||{||}}~~~~~}{zyxwwwxyz{|||{{zyyz{|~~~}}~~~|{zzz{{|}~~~}|{{zz{}~~~~~}||{zzzzz{|}~~}}|||||}}~~~~~}|{yxxwwxyz{{|||{zyyyz{}~}}}~~}|{zzz{|}}~~~}|{{z{|}~~~~}|{zzzzzz{|}~~}|||{||}}~~~~~}{zyxwwwxyz{||||{zyyzz|~~~~}~~~}{{zzz{|}~~~~}|{{{{|~~~~~~}|{zzzzz{{|}~~}||{{||}}~~~~}}{zyxwwwxyz{||||{zzzz{|~~~~~~~}{zzzz{|}}~~~}||{{{|~~~~~~~}|{{zzzz{{|}~~}||{{{||}~~~~}|{zyxwwwxyz{||||{{zzz{|~~~~~~~|{zzzz{|}}~~~}}|{{{|~~~~~~~}|{{zzzz{{|}~~}}|{{{{||}~~~~}|{zyxwwwxyz{|||||{zzz{}~~~~~}|{zzzz{|}~~~~}||{{|}~~~}~~~}|{zzzzz{|}~~}||{{{||}}~~~}}|{yxwwwwxy{||}||{{zz{|~~~~~~~}{zzzz{|}}~~~}}|{{{|~~~~~~~}|{{zzzz{{|}~~}||{{{||}}~~~~}|{zyxwwwxyz{||||{{zzz{}~~~~~}|{zzz{|}}~~~}}|{{{|}~~~~~~}|{{zzzzz{|}~~}||{{{||}}~~~~}|{zyxwwxxyz{||||{zzzz{}~~~~~~}|{zzz{|}~~~~}||{{{|}~~~~~}|{{zzzzz{|}~~}}||{|||}~~~~~}|{zyxwwxxyz{||||{zzyz{|~~~~~~}|{zzz{|}}~~~}}|{{{{}~~~~~~}|{zzzzz{||~~~~}||{|||}}~~~~~}|zyxwwwxyz{||||{zzyzz{}~~~~~}|{zzz{|}~~~~}|{{{{|}~~~~~}||{zzzzz{|}~~}}||{|||}~~~~~}|{zyxwwxxyz{||||{zzzz{|~~~~~~~|{zzzz{|}~~~~}|{{{{|}~~~~~}|{{zzzzz{|}~~}||{{{||}~~~~~}|{zyxwwxxyz{||||{{zzz{|~~~~~~|{zzzz{|}~~~~}}|{{{|}~}}~~~~}|{zzzzz{|}~~}||{{{||}}~~~~}|{zyxwwwxyz{|}}||{zzz{|~~~~~~|{zzzz{|}~~~~~}|{{||~~~}}}~~~}}|{zzzzz{|}~~}||{{{||}}~~~~}|{zyxwwxxyz{|}}||{{z{{}~~~~~}|{zzzz{|}~~~~}}|{{|}~~}}}~~~}|{{zzzz{{|}~~~}|{{{{||}~~~~}}{zyxwwwxyz{|}}}|{{zz{|}~~~~~|{zzzz{|}~~~~~}|{{{|~~~}}~~~}||{zzzzz{|}~~}||{{{||}~~~~~}|{yxxwwxyz{||}}||{zz{|}~~~~}{{zzz{|}~~~~~}|{{{|}~~}~~~~}|{zzzzz{|}~~}||{{||}}~~~~~}|{yxxwwxyz{||}||{{zzz{}~~~~~}{{zz{{|}~~~~}}|{{{|}~~~~~~}|{zzzzz{|}~~}||{{||}}~~~~}|{zyxwwxyz{||}||{zzzz{|~~~~~}|{zzz{|}~~~~~}|{{{{}~~~~~~}|{{zzzz{{|}~~}}||{||}}~~~~}{zyxxwxxyz{||||{{zzzz|}~~~~~|{{zz{{|}~~~~}||{{{|}~~~~~~}|{zzzzz{|}~~}||{{||}}~~~~}|{zyxwwxyz{||}||{{zzz{|~~~~~}|{zzz{||}~~~~}||{{{|}~~}~~~}|{zzzzz{|}~~}||{{||}}~~~~~}|{zyxwwxyz{||}}||{zzz{|~~~~}|{zzz{{|}~~~~}|{{{|}~}}}~~~}|{zzzzz{|}~~}||{{{||}~~~~~}|{zyxwwxxy{||}}}|{{z{{|~~~}|{zzz{{|}~~~~}|{{||~~}}}}~~}|{zzzzz{|}~~}||{{{||}~~~~~}|{zxxwwxyz{|}}}}|{{{{|}~~~~}|{zzz{|}~~~}}|{{|}~~~}}}~~~}}|{zzzz{{|}~~}}|{{{||}}~~~~}|{zyxwwxxyz{|}}}||{{{{|}~~~}|{zzz{{|}~~~~}|{{||}~}}}}~~}|{zzzzz{|}~~~}||{{||}}~~~~~}|zyxxwwxyz{|}}}||{zz{|}~~~~~|{{zz{{|}~~~~}|{{{|}~}}}~~~}|{zzzzz{|}~~~}||{{||}}~~~~}|{yxxwxxyz{|}}}|{{zzz{}~~~~~}{{zz{||}~~~}}|{{{|}~~~}}~~~}|{zzzzz{|}~~~~}||{{||}~~~~}|{zyxwxxyz{||}}|{{zzz{|~~~~~}|{zz{{|}~~~~}|{{{{|~~~}~~~~}|{zzzzz{|}~~}||{{||}}~~~}|{zyxxxxyz{||}}||{zzzz|}~~~~|{{zz{|}~~~~}||{{{|}~~~}}~~~}|{{zzzz{{|}~~~}||{{||}~~~}|{zyxxxxyz{||}}|{{zzz{|}~~~}|{zzz{|}~~~~}||{{{|}~~}}~~~}|{{zzzz{|}~~~~}||{{||}}~~~~}|{zyxwxxyz{|}}}||{zzz{|~~~}|{zzz{||}~~~}|{{{|}~}}}}~~~}||{zzzz{{|}~~}||{{{|}}~~~~~}|{zyxwwxyz{|}}}}|{{z{{|~~}|{zzz{|}}~~~}||{||}~}}}}}~~}|{{zzzz{|}~~~}||{{{|}}~~~~~}|{yxxwxxyz{|}}}}|{{{{|}~~~}|{zzz{|}~~~~}||{|}~~}}}}~~~}|{zzzzz{|}~~}||{{{||}~~~~~}|{zyxxwxxyz||}}}||{{{{|}~}|{zzz{|}}~~~}||{||}~}}}}~~~}|{{zzzz{|}~~}}|{{{||}}~~~~~}|zyxxwxxyz{|}}}||{{z{|}~~~~|{{zz{|}}~~~}|{{{|}~~~}}}~~~}|{{zzzz{|}~~}}||{{||}~~~~}|zyxxxxxyz{|}}}|{{zzz{}~~~~|{{zz{|}~~~~}|{{{|}~~}}}~~~}||{zzzz{{|}~~}}||{|||}~~~}|{yyxxxxyz{|}}}|{{zzz{|~~~~}|{zz{||}~~~}|{{{{|~~~}}~~~}|{zzzzz{|}~~}||||||}~~~}|{zyxxxxyz{||}}||{{zz{|}~~}|{{{||}~~~~}||{||}~~~~~~~~~}|{{zz{{|}}~~~}|||||}}~~~~}|{zyyyyyz{{|}}}}|{{{{||~~}||{||}}~~~~}||||}~~~~~~~~~}||{{{{||}~~~~}}||||}}~~~~~~}|{{zyyyzz{||}}}}|||{||}~~}|||||}}~~~~~}}|}}~~~~~~~~~}}||{{{||}~~~~}}||||}}}~~~~~}||{zzzzz{{||}}}}}||||}}~~}}||||}}~~~~}}}}}~~~~~~~~~}}||||||}}~~~~}}}||}}}~~~~~~}||{{zzz{{||}}}}}}}}|}}~~}}||}}}~~~~}}}}~~~~~~~~~}}|||||}}}~~~~}}}}}}}}~~~~~}}||{{{{{{||}}}~}}}}}}}~~~~}}}}}}~~~~~~}~~~~~~~~~~~}}}|||}}}~~~~}}}}}}}~~~~~~~}}||{{{{{||}}}~~}}}}}}~~~~}}}}~~~~~~~~~~~~~~~~~}}}}}}}}~~~~~~}}}}}}~~~~~~~}}|||{{{|||}}}~~}}}}}}~~~~~}}~~~~~~~~~~~~~~~}}}}}}}~~~~~~}}}}}~~~~~~~~}}}|||||||}}}}~~}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}~~~~~~~~~}}}~~~~~~~~~}}}||||||}}}}~~~~}}}}~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~~~~~~~~~}}}|||||}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~~~~~~~}}}}}|}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m43.bin b/applications/SprPoP/dist/MUS/m43.bin deleted file mode 100644 index 86024ba..0000000 --- a/applications/SprPoP/dist/MUS/m43.bin +++ /dev/null @@ -1 +0,0 @@ -}}xtrp~~|}~jxnpzliwot{l_}t~Y~t|g~\vw|vrw}ooqb~yygsxzrv^o|gvfewpfhutscku|v}qrsxgv{}{nsrozvxs{xlnssmn|}z]cyrppc{rpu~unoz~lgzotxyy{~jsuwmnums}Yf}q[d{qqphtmwpu|}{kylpvywuk|psnWP^urajTzfkqegyylsvq^Ti|gt}uy`ysouhmtyy|v~hx|i|z{y~brgo}~glvkywS\u~rjsrtvrlcpdnqrfa|rk]s`{pvws}{qsoYtucwv|mmql`g~t\`da~npy~p|yt\glx`R[i|xzqpo|{}|sokxt}|uu|ye`sz_ggw|w|rz~jp|rzi]^{bryxtuxxqukbefhb^z{kszpkq}_nltrr|jxomtuv}}}uo~ssxvintQVy|sm{uqhpw~xcUOTaQkczku~v~si|}u|t~xjpp~yw~|jkv|}vj~fhxhhja~{{yn{mkusqtrcYildo}p}~nx~yvvz~qlfx}zxdex|vXl\yd^ewtXLh|wo`h|u_WandRf~zuv~}{]spyyndvx}}[cx\as~~vwjev{~v_{~`mpt}vg[\iq\pc~xtnl}|x~vl}v~~zumv~q[caxukqLq{mfxrgq~wky}j]PHRb]kwmjzo|}hcs|r|wvyqnjhc_[zdjc`jfRj}xafwewh\gppv~{iqtoqwyu|yf|lv]^uvrjeyj{m~sffjt|rz{mr}bJHW[J^s|ps{r|ynvooyhhklg^Tihso`Qwotx|xmkq{{qb`owgndiwp|}xyx~n}}~~}yunvthtRrtitehmmbS[xvsm]c{|un`Xqxyp}{xp~x{di~yrqqk|~sjksx{vlbrv_wjy{sxu~g[u~mlkb^ho^_~vfqi{{uwzy~mnw}}vnfppn}QcjoyhcrzkbnusuYLYcTch|vixvk}v{vi~nzw|ovrpmjjb_dvjtx^o{wmfqiqsaew~q{~p}y}yoitpo}|}~kv}n{lfmw}shgiewy^~ncoxz|qsug_n{ukg]JXpp~yv{qqz`nvr~|xslcdb_ZZksfaemiu]k}qsxwrgbnueoearzzrl|uuy~|}|~a]r_zdqzxeVRUg}{{hg|u_bumtvsotghutxxz|sxuntvolid\[o{|yfhwkm}vdi{xbW^m|oh~u}{w~|ox|}~urvxfbgiifgnxdjtVsxoz~~zmpqrthkdRfym{iuiw{|}zwppmmtutoyuutrr{w\bk}[rvqrnpt}jj}wlu{{pez|yxli~~tx|vwxokjqz~|upr}uzii|}vacpsrtrklxyeU^q}zgZlyq}vmykt}zpqyh]VTU_uqk]i}lw{w~z}}kfisuzu{|qzv{|sjl{hs}|xs}z~~vw|~{upkT]ywo[nwdckysqyszmi|sjts{mpstxl|s|fwrktwtoliebi}~|iupnftmXYjtmfirshg~zxj~|{xt}xrvvzw}|skkmops{|_gn`}yxzrkoxz{rcfruh{r}|c~uwtq~rvtt~utonuooz{mltmgdgonzsy]`xfeoy~y}wut~}x{|vvzzwuvvw{}zyy{vwpr}~~xlefikkox}pqqp~{~~zzxsrsk[Xmtgspf{w}}{|yy~z|{m{~|uollms{pbbbix{or~ynktyoou|nfutwxr{uu~yx~qn|ywzuwqxz~ywulut_lyoels{|xrrxxxwndrr~ngvz{}yyryv~ubh|~qd]\dtwgrsny|xpfclusmnpkk}wyw}x|sqy~{~zyzpruhgmtz|ykQTu~|ymnww~j^kouvemxq}wd}}nntzwz{qyz}|s{gykxxu{}tpt}{_m}nxioxx{ln{ysyy|rj{y~{xx}yw}uov{zrzuwxrqooqrtw~|nxvn|qv~ysnhis}}smkiqp}zmwnqrjn}uxxz|s{yrnljgbexywgg|z}}~{vw~xru{urw{rouhsuzzz~~vzzsou|rp~~t~}yyxvvywnqzqnv~|{{~xfg|vt}mizxytyv||vsu{l}zv~v{o{xlmswwvwzxrctlw{y~rgdlyn_ftoruzx~vxxu~tuxw{yx|sy~w|zvw{|wpgduzjm~yxxvuzztx|vws|y}o|uvzrvynuspu~zwvngznh|zxvlioxkh}wvsgp}~{op{|s|uyuxu~yrnr|vsyx}xuy~~tjgluyc_qwml~xtsu{yjbg|qzory{uvz|t|}sqw{n|ywt~uy|vx}|vqoq|uh|xumltufpzptzkqzzqv|t{}uzuot~~lxsv}~|{zy}|xwpkr{rsx~~~poz~u{s|vl{|~yzz~~{}yp{n{zzqeiw~~~xqs}z{sawyz|xtpmnu~ncmzvv~w{{|ywy}t}s{ysrv}}tqrrokhfjv}xw|{{~~xssqu{om~}nr}zz~tty~|~qp|z|y~}}{|}l]rx{wlqxrptpftxmq}xlhj{~}vx~z{v|wnx{t~{wtrrwtkqyjn{zy|somownbm|xu}}pywx|lm}ohn{y}}qu}zorynghp|to~~~|yzyxvtu{}lp|xsztrjyw}xsy~us}~zr|tx}rnqy|neiv~qmzwsvuu}nuzst}}}u|pmow{{|ss~tpt{{tvpm{vmy}w{}|{xusrpnq|wel{uoy{r}||{z~wrz|tx|vq|Pfnmn`c`xvpodq~qqzro{pmqko~~zp^qx^Zzlncfleq~zwy{mv}zs~hi{y}ygqucsuqqtm{{j_hkp~o}sv^Rxefs`fpoz~xupkuqqupuf{}}x~}~ugowk^Rp|~yjxgdi}sqqz}xxydayhjzs~SQsvvb^}[\Wkxoks}sx~t|xn|~rzijh\|x[pziqU_Mrbgz}r~cp{oow~zxypZ[yprstqR`t}|ozmshZywzzxkpqlw~mtvVYxnchgmt~tydIVir]QseukcyUdwwslsyaKebh{wwtl]Q_rdXimetcxojwnjkcvgd_`o~yyfz~tit~t]swv}kn}}}x{|t^Zrueymnsyvzrww}rqrt}t~cexy||kqss~tdtxknekseVjj[|pozsc{ch|w^_rtqgWweu}qhbmx^Qrux~pqk|vpsxwcw~]U[S[Qz~zpsvrywobr|ymm|lfwv}|zxm}x}pyezgU]mx}{cr~bar\P[f_]jis~iucwq`cwyyxn|{op}u_qsd]ScnlTUnYMg~unyraffhrewk`w|ke~}e~|zwu}oqsW]Zww}zwvzrmkrcbtzkfka\Ndp\ylkpk}o|}u}jqs\tr|}aP\SolgWdwgttlvxkq}vvxzqTSPV_k}~{|rz|qhatuuh~rkx|llrsqimoirZeyxbXpzwrYawj_zwbinw}vtoysz~~}f`x{mgtnojyr{_]bt{sssow|xyzp_h~rupllT`xcWt{t~|}vzs~|nxnrfrt^TXyx{eb]zqmnyvfi}~_jmhZRqow^ufesttmxwt~vvqumytlu[^X[noobgpui`pnemiguuxrgceuy{wquznj~zfky~p^|mmWn~rtgwaouspuzntihxkO_mWSueo}m|okqwofzv|lc~efy}ps[\z{krx{tvgtr{u}ngtw{emnbnyqShz`gouyyt~l~}uj|s{gxtpm}b{hosegm~kkhvuns}|m{xfy|UYvtr{~}rz{upyskmlbaqwi[syi||jt`iXc{sk{onpg|zqvu{jV{hn~z~qd~qCV~ppp`T\vu|h}ssssuwyc{scu~\iulp~~{\tQrxug]kuzy|uhxz}u|cc|iywptpuYnNsio^mxiTf}nllrle}qxslwpspPbnd{j|hjvST^i_Ocz|kVssUijr[Yjr~ka`\a[bcanlzhamqfk~|ruYhttdYen}tm{yy|rflzqmt{oj~yzquo\dzh`a~ptt{u~uv|~pv~xmeaqap{qwytmkgogqh{hddq|aXq}^j}z}yplry|lbz~xfzztsr|}txslrotfxUHjroq|sfjvtyxdWNP_X`q||ylv~ui|}yrmkm~{{utyhm{oud]ugk\x|tqeqpiohjtxk^jm^d|vx{tt{lzx|yrxpspasz|pcyzuYulx`V\qoUCV}}~x\\vtecnzter~x~ro{encvy~tjyzU\{[^p{|zy|zpkvz^uxrw~ep}ftwofep|jxg{}}vrn|u~}yy~}v~i^l}zoTW`{}qvPrqht}rhszuf|oaREQgejzeo{s}zecv}nyr{}~zxvwxuohlj~v]mjlpdNezdeyj`|f`heiwnyxo~uxz}}{x{j{dzpX]y}wmjsktzxzncafnx}tp~flvbUWeg[tny~t~}nuvzxt{dtrdgie]\Zs|n}n`Ziem~pt}vuphw}}vjiz{peo\yhyx{lswiyru}{sriojpqXtqtjorkZQ^}qrgVi~{r^]|wzzwyxw{udqvtjolyuheq~{xr|jZ|l}{rywyv]YzwlmbZ^ns`gob~spsu~t{syglz}splusv{Qmtyp_aquh^i{rpub[gnbvetokzrf|z}yznltvwnwrmeitbhi|s|~dismcamzkvn^jyzuztx}{{sigqywz}gp|p{lbfu}uojed~e~tgo{zqtyj_oztqtnWaz{}rzwxei}o}x}zytziegha\evsmjjvq|bg~~|~ty}|umiurue`tyzto{y~~tw{zx}x~|jbuelp}o]U]l{{xlctvt}{stjr||qm}owwv{~pxwmmz{zulhoz~w|tjnlz~qgm}}h]^fys}zx~{~rr|w}qpy|ibehgbiw|or~boyx~yu}}u~u|onxvtlxk|{|{}u|~kjuzr}kqzzxuv}derql|~{rlr~njx~y~~z~gw{vyxvqopzulzwqzwxq{vumpuz~{~{py{lrpz}unkpzr``gsqu{{{}sp}tt{vx}tzuy}yqkfabr}uik~{tmm}o{sx||}wqnq|~vw}{ziu}twwty{{z}w{~u_gklroz~vsvzk{tlmptypryo|z}wqs}f|~qiu~zrov}}wvxp{rfbhswoghq~zsu|s|~v}wpt|}~~{wu{yqllorsxnqqm|szuw|wqmutntrqr{z}|zz}{xplxwtsnuqk{ttxw|~r{up}ontr}xx|tysrz~vvrlkowxvy~vpwutvq{oysx~{uuyxzrs{x}toosvrf`j}~tqt}|}}{uv~}{t|t{owvvtrrtxqmmwsu~qlwzwzowt}ot{xuy~t|zy}y|yruslrnt||{||nnzoywu|||xs{|t||k~x||rlowsjq||uu||{tzt{wnhpyswum}{}|smknrtqotxqj~xt|x}xxzzxqt}~~rzyosoxn}uq{_fcRh~zwwsnbfzpc`{wb|{zxxxVqvwi~vuXzywz}rap|xghwhs|ttTpsk|obqhi|k|wzxhyyyfyw~wmo^sstzvkomstrdxm|oxvtXuR|}^ed~hs]Qkk}{py}xzuzgwqk~stqj}xaX{ysa~ooy}zzexz{yt}{opaY]zn[]kckrtyom|azldbks~fivygj[_|tunbhoqvt|vq}{W^yseptwv\ckp|mlolyaYw{qvry~_~_Xm}~Znwefigwusju{z\tndjuvusl}xiVmwm}xu}urjpe`g`wzwt_lejpvxm}]|sm}}}{z|jbzvnmn}w{yv^vyzvewxp~yshzl|y~wj|[o{vv~snvVefSy{rtrtgdzytrqv|qr|sosb|{]beT^|wfnpvlhw||xq}x{rihhoi]wlXvs~qlv{csq\thwoi{zrxy~||zp}rhXTTcluZQykvzu|{zkffytvswzrmvdkse~yu|z~yXanozc^xzmxypjvZt~lzkyxy{}mp|ppvwTxgvpmqyp_|sp}t\]~|gk\Z_fx~}yz|wzy~po[\^eszifhfoeu~pxl|zmnxwdwyyldyyn~}tkzudfpsgqr`vvvsdmtmt{r|}xtp~}mx}zprz]xxxgrom_hr}rWmwyu{reuhg_Gn|q\qrzj~x~}}veyukY\kneqp\o~mr~ls~||frqivrq||x~}vzz~gylyqimz{a\^sr\folrp}mp}utvyjwyyil|hq~rUfmdik[dfk{esv^crXkpqfooz~u~yxvndn~pcg`koqtok|~kZnv\rvx{v~qlrjslms}sv{hcrw}smwzpmak{zze}po~|u}{~|}n}`rri^Wdtvhxltzej{lr]^`d|_vzz{|{mzt~pv~hklgnl^lwbbx}|qv`okohg~{ss}}|znv}uq}~iuhs~wc[enxkmtx~pvxp}zqoxprqw}|kvz~hm{s{{ywwjivrtqhyxptvn~tv{|ufsSjjb~jvsxq~i~dgwttnRbwj}x}xpfkk}jx~yzo}qmxtphwpsr}|p}z`^{nj~lnl~oyjqvy~w}zq|zxqyv}yipybfhboyqn}ujxxk~jejtahgghovnwv{vul|os{p|||tinf`x|xsvvw{V^yyevyuqz}z|w~uuvuz|v{ufmxlkwxrsds}n{kw|v{zw{vq|ps|xs|lyf`qsncltks}}chtixx]l}}|l}zptsz|iq~wwznja^zzyrnn~xamgwqxpxw{x{{nul_~l|}~u|}jp~|ys}zcjv}{`dw}vww|}zwscory~kmxlshgklzv\ghosypriw`gxiqs{}|v}ou~sm}s{jp|uf_inu~z|uiqkj{ax{y~}sts~vz}wru}r~o{|ynfotorx~{wqj}uzwqtdkxnv}z|{vx}u}tz{~}xqywnhvwsuqu}v|xicms}uzxww|xqi{xna|~uolsshl~xwur~[f}zz|zty}wrky|icnrr~t~qdovlxp|ywvsmgqztp}uuxp|qyw{{eqvlio|yc]rseflsnhjwrs[lwxwxuvvottupu}ympolwv|{yqskvovt{zs}}ssytn{y{lo}ugj~mfznk|v~~hZozyyvzuvxuu}vxypoqryruzqt|vrz{}zrsuct|ux~{xxxu~wozrro}machcer|pqcsympwx}~n{z|}zx|zz|hw{vy~su{lsqp~|snkk{ysxxo}tzznncu|w}sol{tdbsxli}}z~tlZcn~~yv|z}~nxgtpd||xvmmefjo}ys}~pwtvow~{}rtypzuw}ogvrjvtfsrssitxmin|vw|yv}}{~|xzv~w~uil|znnr|tp`ntt~t}xyx{x{xwvpjm{yt`akprqos}x{r~zyvw~xes{}ztuwyzxzubn}yqt|}{}{wwu}}xxzrbpwuwlrymrnxwr|sw}qovqc_l|ndmxu{qoy|m]uwutwrwq{sxmtjxxjtyllorvx|~~qhjr}vu~wxwyymv~tjv~~}v|okywvprtkruwpxsx|zruy{y}ywv|rj}qu|}ump~yeblpgpx~}wz{}}zzwzm{qpqyrdjeXZcjlmqx~y|ysuz|{z~yjt}z{}zphq}q{v~s|tq{~ytxsrvqzzryzpompluykckit}{}}{zvolryyxwv{|zvuyvm_wxw}w~xpnoontskthcrg_kyrqzi_fpxy|z{yvx{~}|fopt|upvwvs{y{shorkcif_fovyux~yqlq{z~vtu{}rw}unoy}tjis~}}tqy{rzw}|w~nptskdernemkdhsyvqlo~lrw{octwoorpllyzq}u~xvx{voryvz{swxruuyuzoko{qmsvu|vzxoigit}|zwutlppffglv|tz|{xy|~rqutru~}yuftwjiqvicmeao}}~{|zxxytsxvvv{xmyxtyzz|{ywy~xj{|x~zonrvxkools{{ht~tzwrqqrsqyzx{ykjw{xottqqyseany~ylkv}xzzs|wu|trnjojWZedelnklmw~~prtvqvvwqkm}~xzty||{yyuuyst|}vrx~wmkoxqfgkvztu|}vrv|~skpyvrslijkuroxww}rggjnu~}|wafrjbdowngpkfr{o{}wz~oeuyrtxzwqmsotvx}qggou|~ohjhkqyznsk|~wne`iw~v}}st|nsvlmrsu{xnlovx}~{{zy}qrqx~tz{~zow}rxvpja`ha[lxwwwurln~~s{x}ypztlkihpwz|y~vqmpxxy}~vz|smkntvtvu|~zz}~ywzokpxwpov{ujfa^_ai|nwy}x~spty{wnmz|js{sllqvn^cnlu|z}k{|qsz~uwxy}ptvlmqrsv|gj|~y|ymnrrrx}{yz}|mszep|wsolgju}{||wlhmu~ryzmjnt}|snllr|~}|yy|~y~}x}v{|s~xurkini^k|}wttsr{}zwz~~}vy~zov}vyvqrtz|zzy}}|}~{vwzy||}ruyrlikqxxwvsw|wssw~yw{tx}vibgprqpkkmpxgo~rsxrkmsurnrwglsohfjsvlk{~}{u{q|}z~~|nfqshgloqt|wmx|}{ocgmpwytuvvwy{|}inz~vkbfp|~wx~xz{spsyvglrkhowzsqstty|{{|~~~~xtx{~{}vltysldeopo{z|n{|zxy|}zy}prztlkpz~xuyxoqwzssuv|vplikq{{xzppyvnprjdelrrokefjnw~q~ww{{}xmgpvv}}xrptywjh{|szuswz~|xy|~{y|~toxugdipz~qt|xu|~ztsy{|~|yx|wsmkt{|wsnmqw~}wss{tkjknu|~rnxvmmw~{wplosvz~|p|{~|}{yt|yvppy|u}~zwt{~{zyzu~unkmt~{rr{}ru}wyu{wot}~zy{||ns|rliikq{zurmkt||xtzzpq||yxricadjsxvstw{|uw}xt~|xsmks~y||xsnmqx|wxvw}~~{{~~pfmnecjuxlqwxz}ztvx|{wuqmqvrvsrv{{tpory~~xwx|{y~|uy}usstx||qelvqoy~ywutuvuvz~tov~|vy}slx|vojp|~~nv{tonpqv{|x{ykmvwv{{t{rnwxuvzzy~|s~zupkiny|ytu|~z|}zw{|ojsxwpgbadjrxwspqvyytr}yxqotzzv||xuw|{u~{zy{}xwwwx|wuy~znmtoegs}vz|ppx}~z|wstwcisssy~|wsswz}~}zz~yxsw|{vy~rlkkkouxrku}vr{ztpnou~sr~s~~tv~{xw}zy|z{w|}}~~{wsokjnuzsry|sp}x||rmtz||||~vvvpljmv{wtnnwwu|~zrlpxvnsynifcciry|}yw~wvvnjlt||y}}xursz{{|~qp}|~}zwvx~{}|mcjojjtxqwxw|}os~|}zrmjmzyfmzyuw|~}{yxwwz~{{}|}{{sszzusqqswwofmxvuz|}}{wuuvvx|~z|py~||}~~vns|}yuu|u}yxxuokjlptx~~|x{znp~|u}{rrv|zvw||xy|{zvrqtz{vv}|wvt|}}zvru|zliv{qgcabgpz~}tks~{z|tsvzvor~|~~|zz{u{|u||xustzzvuy|zojrwqqvzwu}voqz|}wrysrv~d^n{zx{~|xxy{{xy{zy{xpx~}tqvwoljiiouwqq{}yw{~ytprzyx~~vz{z~{ru~~}}~~uqqqqqrqppt}xrt{|uy}|svyuru}}}}}~z|zrnmqy|uonry{{y{{}|wqloxypq~umjhfhmu~{pqx~zsxzuqrvwrs~zwwyzx~vrtz}usyy}{z{~||}~wibkttsspnt}}vruzuq}|rmowvihxywy{|~}xrnqtux|~{{|t{yvw{}xtsqqty{skqyux|}zxvvwzzzt|~|}~xzxx{|}~~}|~}yolllllnqvz}zx|~uzyz{t|yrv~~zwxz{~ssyvuwy|yvx}zsy}~~}~}{wtsv|{qmrsmgdcekt~{phq}~|vx~}z|~xony|x}}zxx{}yz~vu~}ywx{zwuuxxrjlx~yqkkv{tqty~{}}w{zuv~znfixxvxyyyyxvuvvsuzxowvrtz}wqnljkpwzuq{xru{~~{wuw~|~xx~||vyvrx~x|{mghkosutsu{~|wrs{~ykq|zttz}{yz|}|z{wxzx}{vssv|vruy}y{|wvxz{zvqmmrwvqqttpmkkmqwwr}zxtmquvxxrlq~~||~{|wqpv{wvx}{w~~}~}{{||vjep}|sjjv~uqt{zv}}tor{|rnmp}{uuy}|uqpopu|~zy~zt|{xyzzzzzyvtrqrv||slvvruy{zxvx}~}z~z{|v{}y||zz|~{{}~qfcfjlnpry}~{yy~rr~ytv{{xvtuy~sr}z~||}~}{}|z}uq|~|~|{zyywusrru{{qggkkhginuymp~}}xzvot|}tou}|~|xvvy|yz}ysx|{{}}ywutuwtno||lfp{wvxz|~}xv|~|zxx{trspr~wsty{zvrsusqtz}wvwvwy{|}|ytomlmpw{zvz|tsv{}}|{{~}zyqu}}|~z}}vvz~||~z}z{|w}reaelsvxy|}zy{~}yusy{opy~{wwz}}zyy{{zz}zw}{qtxxxxzzz|zvvz}yxy|{wuuvxxvsolox{pfipqpprtx~tsyv~}tjny}snr}~~xssw}{z|}}{}~y~|zyyytllv|sgiu{uppt{~yz}~{xx~~pkrxyzwpqw{{wsqmijqz{vv{~~zvuxyyvttux}~zwz~xtuxyxwx}zwxsxzy~|~}yw||wvw{~~z{}vxukcbhnrru||{|}}{xwz{{zvw~ywy|~~|yutw~}vx}{sv}{wwzzz|wvy}|{{zxvvvutqpt~}n``hlmnpt{xnt|{wx}wnp}xuy|}wssw~~yy|xu{~zxusstutrw}ynlswusuz||{{|~}urz{usu{wot{~yw~~uqswwsoopoorx}~zy{{xyz{{zxvwyzxtpnoqux|{}zutx{|~yt{|sqv}|}~zzttz~~~|{{~{~yx}{zwwvmdafnsw|yvwz}~}yvw~|uos}{vuwxxwwxxx{{x|~qnu{~}yx|~||}}|z{{vsqqrturnjmuxpdaiqtwy|{vz}{yz~znjs}{srw~~{xvvyzxtw|~~zx{}w{~{yxz{yuruurrwwsqrvz~|vv~~xvuw||nip{}ysswxvvuqlkqx~}wtu}}xzzurrtvvutvx{~~ut|{vuwyy{zyxtwzw}{}~}}sqxzwwz}}tkmniddjorv{}|{{||}}{z{x|}{ut}xstwz||yusv}{z}wqw}zywvy|}}|ywwwutrssqnmrxypd\ajqtx|uv~yutwzxqpy|y}y~|zwsqtz|wuy~}{||tw~zwussvz{xtsvyzuonsz~}zyz}}vswzrnqzrnu}x|}vsssqoomlpuz}|yvwqpx}~{vssuwwurqrtvx~zw~zuux|yrw}vsuyz|w|qry|zz{|~}~~{{|slnqnihkqx~yvvxz{|zy{~}sqzyuuwyxwvwz{z~tmqzzvvwwy}~zvrppqttqmmoppmeafqy}zsv|yxxz}{ursttx~}{wvvvx{}ysrw}|yz~y}~{xxy}}shht~zojmswyz|}~~zvx|tprzsjlu{}yromoqqmkmqvz|}{wttztr{{vsqqsuwxz|}zrv~wuvz~~xv}xstw{yw~|try~zxwzwhcioolknt{~{xy{||{{{{}||{vwzttw{|{xx{zz~|}}vqv}{xtru|~}zxvuutsssqonppppng`blu|xt|~|yurrvzzwstxz~}vsrrtw{zvvy}}{|{w|{wtstx}xliusjkrz~~|zyy{}}zvw{yros}zpqy{uw}~yrmlnmjinuz|}|{xvvz{oow||xtrqsuvvuuvwy~zv{xuw}|tv}zuuwxz}}|y|~uuz~~{yxz}{y|}}vjdiqsolmt}~zxwwxy{|}}uqv{ustvvtsv~{y}zrqx}}zywuu{}{{|{xtqnnoponmnkhhkjffny~rq}{ywussw}~xnjq{wrqsx||wqptz}~|zxz~{}~}|zy{|lepvmmsxzzyz|~~{wvy~zrnnwqjnwxz}sllppnnqtwyzzzxuuyyrsy}}{xtqonptxz{|ytwzwz}vxwtvy{}|y{~}yy|~|{{}{{}{ma_hqspnrz}yvvy|}|{|~{|}yvw~xssvxxxy}{wx~{y{~}yvz~|vtw}y~zywurqqrrrpmmnnkhkooiejuyry}zyyxvsons{ynmx~uppswxvstx{}}|{{{~zz~zvuvy~~sjo}}rnqw{}}{zzzzyvwz~xsrvuot}wrw~~vmkmlijou{}|zxwvw|wqquwvtrppqtwxyxy}}yz~yz}vx~}wrswzzz{}|~~~}}|zxvx|uqu{~ukdenxyusw|}{zzyyxxy||vtz{vuvutu{|z}}yy|ysrtvyxtru|~yyyxvsqoonmmopkdaelomkq|{oo}zyxvrpr{vjlw|sorw|zustwz||}}|{|~~|{||zyy|ynkpwxvsrtwxwvwz}|xvz|zvrpt~ynmu~xty~umjjikoswxwwvvvwy|}}}~|wtuxzywtqpquy|}{wy}|}tqx~vrswzzz{~ww}}}{zzz{}{st|}zqhbfpxyyz{zwuvxz{{z{~}~}|xy~}wuuvy}yvy~yutx}{uruz|zwvy|~}~ywvspoprrpmmonhcdjqtqmoysmx~zxwwwuqnovwoq{~~vqrvxupqv|~}||}||}zz}}~}yvux}~wppuwxyywvvxyzz{{zxuty|ywutwxopx}uruzysnkhhjnsx{{ywvvy|~}}|smmqssqooqvy{z{}{~{vy~vppuy{{yy|xy|uyzxwwy}{pox{tnhfhqy}}|{yxyz{{yxx{~wsu{~zvssuy}}zz}~zvw{vootwvtvzyutsrqpomjiknmfa`dlsutu}vq{~~}}||zxvuspmpxzrpu}zsrvxvssuxxxyz||zz|}|}{zzzz{|wwwsquz|zxwwvvxz|zwvz~tpnnouvopx~xvx||xrlijouyzywutuw{|{ww{|tnnruutrpprvy|}yz|utz|vtux{{zy|{svyw}yxz}wrv}{xrmhehpy~yvvvxy{|}}}~~~zuu{}wssw}~xw{~zvtv|zrpuyyy{}{urpnmmnomlmmic`agpx{yw{xpt~zwutttrnmqw{{xww{z{~~zvuvtpnrx{||{||{{|{{~||||{yxz~~xtx{wqrz{wvwy{{zwssx~xpmnsz~plq{{tprvxuniimqtvxyxwvx{}zww}wmhjnppoprvz|||{xtxwposwzzyxy}~x{vt|~ywwz~xrw|wromlknv~wvxz{{yxy}|usxyuuz}|wx}~zvstyvmknpruy}{urqqrqnkikmkea_agox}}}{rt~|zyyxwvspnqv{|}~|yx{~z{~}zwurruyzzyz{}~~}}~{xuvx{{wz{phmx|wuuvwxxvwzshfjr~vnow~ztppuyvnijotxywussuy}}xttzypklprtttuvwy}}xyuqv{xrorvyyyxxy~{zzw}zwx||x{~{vssrolnx{wvwxwwy|zvv|yvy|yz~}zwtrtx}|uomnqu{|uqonnmlkmomgb_`dkt~}|xpmv~|xvtssqoorvvux~|}vsw}~ztporvxz|}~~}}|~}|||||zyxxy{~~xtx~}slnxzvuwz{yusv|ymfhq~wkjszqkmtxtnlnruuuuuvwy||yttx~vmggjlnpruwz}}{{sv}~xqoruxyyxyz~zw{|yy}|}~||upopqpnpz}xwy{{{z{|~{uqu}{xz}|wuz{wttv{yrlikqx~}upnoonkhijid`_adiq{~xpq~}{yvttuutpnnruvv{|y{yuw}{toptxyxxxz{{||}||~}zvuw||zwmls~}xvvwvtrtzyjacm|rkoyzrmpwzwqoquxxwussux{zvrrvy{zumhgjmpsvvvv{~z~zvz}xpnrx{{yxxy|}{|}{~~|}~~wsrtutpns~{yyyxxy|~~{wtv}{|~|yy||yvutvy|}}{vokls{~{~~wqnmlighlmic__bflvvlhp}{ywusrqommquursz~{}~uqt|zroruvvx{}}}||}wtvy{{ywwy}~xv{xpns{|xwxywttx|{pgen}rjlv}tljotvustuvusrsuy{|zurswz|}yqiddhlptvwy}zx||vppruwwwwx{}}}~}{{~~{wrprtvust{}{zzzz{~||yuvz}xvx|{wtstvxz|~|tjeiqy~xrpomkjjkgb__aeiow{mel}yvsrsutqoquvrrw|wxztsxysswyzzz{|||}~vtw{|{xvuw|zw{~vqpsx}zyxuqqv~zphgqtjks~xqllosvxwvvvuuttuwzzwttuvwwyytmgfjouxyyz~~||}xpklqvyyxxy{}|xvx}|zutvxzzyxz}|zxwx|}~|zyz~{y{}zvttuvwwvxzysljnv|~{|~ytqnkiilliebbeint}wgbn}zxwvvtqootwvssx}}|uqszzuuwxxxy|~xstvwwvuvy~}wtx~{ussux|}zxvttx|}wrmmuwnlr{}wrnlmquxyxwusrrtw{|zustvvwxxwrkfehnruwz~xrqsuwwwwxz|}||~{wvy|tu}|uppruwyz|~~~~zyz{|}~}zyy}~|~|zyyzzyyz{zuolmrw{}}}}|~~{wtqonoomifdehmrw~zlht}}zxwvvuuvz|zwx}~yusuy}}yvvxzzz{|}}}~zyz{{zzz{~|xw|}xwwwwy|~|ywuuy~~yurqt{|rlnu~|xtqoorvyzxvutsstwyxurrtuutstsqmjjmquxz}|uqqsvwxxyy{|{{|~|xutv|~ts{{uqpqtvy{}~|ywwyz{{|}~~~~}||~}|{{|{yxwxwtqopsvyz{{{}~zxxyzxvspnmnnmkjjlptztjm{}|~~|{zxvuvz||yx{{xutvxyxwxy{{zz{}~zyyyyyz|~|xvy}~zwuvxxyz}~}{xwx{|vrqsy}rmov~}yvsomoswxxvtrrrtwzyvssuvvttttspmlnqtw{|ursuvwwwwxzzzy{~|yvtuzupu~ytqpqsvx|~}|zxvvwwxy{}~}}~~~}{yxwwvsqpqrtuvwx{|yxxxywusqppqpmllorw|vnoy|zz|~}{yyz~~|~|zwvuwxyxwy{}}}}}~}{{z{{}~~{wux}~|xwwyyyyy|~~}{yz|~xsrt{unmrz~{wuronpsvwwusrrsuxyvsqsvvusssssrqprux{~}}ztrrstuvwxyyyyy{~|zxusstyvqu~~xsqprtwz~~|zxvtuvwxyz|}~~~}|zwvutsrrrrstuvwy|~~{ywwwwwusrqqqonnpsw|{spu}yxy}~|zz|}}~{ywvvwxwuux{}~}|}~|{zz{}~~{xwy}~|ywwyzzz{|~~}}|vqps{xpnqx~{xuroopsuutsqqrtvxwtrsuwwutssttsssuwz}}~yuttuvvvwwxxxxxz~|zxvttux~~tqv~xtqpqsux|}{xvtstuuvwyz|}~}{ywvutssssssstuwz~}|}}{ywvuuuutsrrrrqqqsw{xrqy}ywx{}||}{ywuuvxxvtux{~~~}~~}}~~{xwz}~{xwxyzzzz{|}~}|}{uqqv~uoot{|ywtrqqrtuutsrrtvxxurqsvwvtsrsstttuwy|~~{vtttuuvvwxxxxxz|}zxvtstw|urv~~xsqpqsvy}}zxusrstuvwyz|}~}{ywutsssssrrssuwz}}|zy{~~{yxvtsssssrrsssrsux|{tqvzwvx{~}|zxwvvwxywtsvz}~~}~|yy{~|yxxz{{||}}~~xtqrwvpptz~{xusrqrrstssrrtvwwtrruwxwutttuvvwxy{~~~~zvutuuvvvvwwwwxz|{yvusstw|xst{|wsqqrtwz}~|zxusrrstvwxz{|~~|zxvtssssssrrstvy|}|zy{~~|zywvutttttttuttuvx|{tqt}zwvwy}~}{zxvuuvxyxutuy}|yxy|~|zxxyz{{|||}~~{vsrv}{sprx~~{yvtrrrrsssssstvwxvsrsvwwvutttuvwy{|~~|xvtttuuuuvvvvwxz|}ywusrsux~wsu|}xurrrtvy|~~}~~~}}{zxusrrstuwxz{|~}{ywusssssssrrsuwz|{ywx{}~}{zxwvutssssttuuvvwz}~wrry~yvuvy}}|{zxwvuuwxywutuy}}zy{~~{yyy{||}}~~yurrv}}uqrv|~{xvtsrrrrrrrstuwxvtrsuxxxwvuuvwxz{|~~zwuuuuuuvvvvvvwy{}|yvtsstvz}usw~|wtrrsuwz|~}}}}}||zywusrrstuwxy{|}~}{ywutssstssrrsuwz{zxwx{}~}{yxwvuttsttuuvvwwy{~}vrsz}xvuvy}~}{zyxvuuvwxywutvy}~{{}~|zz{|}~|wtsty{trtx}~{ywuttsssssstuwxxvsstwyyxwvvwwyz{}|xvuuuuuuuuvvvwxz|~~{xutsstw{|usv}~yvtsstvy{}~~|||||{zyxvusrrstuwxz{|}}{ywutssssssrstvxzzyvvwz||{yxwvuttssstuvwxyz|ysrw{wuuvy}~}{zyxvuuvwxyxvtuy|~~|{}~|{z{|}~~{wtsuz{usuy~~{xvutttssssstvwxwussuxyyxwwwwxy{|~~zwvuuuuuuuvvvvwyz}|yvtsssv{wsuz}yvtttuwy{|}~~~}{{zzzzyxwvussrstuwxz{|}~~|zxvuttttsssstuwyzxvuvy{{{yxvuuttsstuvwxy{|~ytrv~}yvuuwz}~|{zywvuuvwxyxvuux{~}|}~|{||}~{xutuz~wttw{~{ywvuuttssstvwyywusuwyzzyxxxyz{|~}ywuuuuuuuuuuvvwy{|~}zwussstx|~wsu{}yvutuvxz{}~~~}|{{zzzyxwvutssstuwxy{|}~}{ywvutttsssstuwyzywuvxz{{zxwvuuttttuuvxyz|~|vsty{wuuvx{~}|{yxwvuuvwxyxvuvy|~}}~}}~~zwttvzxutvz}{ywvvuutttuvxyyxvtuwyz{zzyyz{|}~{xvuuuuuuuuuuvvxy{}}zxutsstwz{utw|}zwvuuvxyz||}~~~}|{zyyyyxwvvutsstuvwxz{|}~}{yxvuuttttsttvwyyxvuuwyzzywvuutttttuvxyz|~zusu{{wuuvxz}~}{zyxwvuvvxyywuuvy|~~~}}}~~}yvttw|}wuux||zxwvvuuuuuvwyzywuuvxz{{zzzz{{|~}zwvuuuuuuuuuvvwyz|~~{xvtsstwz~|vtv|~zxvuvvxy{|}}~~~~|{zyyyyxwvuuttttuvwyz{|}~}{yxwvuutttttuvxyxwutuwyzyxwvuutttuuvxy{}~|wttxzwuuvxz|~}{zyxwvvvwxyyxvuvy|~~~{xvtux}}wuux{}{zxxwvvvvvwxzzywvvwy{||{{{{|}~~|ywvuuuuuuuuuvvxy{|~}{xvtttuwz~~wtuy}zxwvvwxz{{|}}~}|}~|zyyxxxwwvuuttttuvwxz{|}~~}|zxwvuuutttuuwxyywuuuwyzzyxwvuuuuuvwxy{}~}xttw~{xuuvwy|~}|zyxwvvvwxyyxwuvx{}~~~|yvuux|yvuwz~~|zyxwwvvvvwxyzzxwvvxz|||{{{||}~{xvvuuuuuuuuuvvxyz|~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m50.bin b/applications/SprPoP/dist/MUS/m50.bin deleted file mode 100644 index 7f679a5..0000000 --- a/applications/SprPoP/dist/MUS/m50.bin +++ /dev/null @@ -1 +0,0 @@ -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}~}}}~}~~~~~~}~}}~|||~}}~~}}~~}~}}~~}|}{{{||{~}~}~}}~~}}~~}{|{zz{{z}~~}}|}~~}}}}~~~~}~}{{|yzz{z{~}}|~||~~}|~~|~~~~}}|{|zyyzzy}|}|}}|}}}||~|}}~~}|z{zyzyzy|||||}{|}}}|~}}}~~~~~}z{{yzyzy{|||{}||}}}|}}|}}~~}{z{yzyzyz|||{||{}}}|}}|}}~~}{z{yzyzyz|||{||{}}}|}~|}}~~}{z|yzyzyy|||{||{}}}||~|}}~~}{z|zzyzzy|||{||{}}}||~|}}~~~}{z|zzyzzy|||{||{}}}||~|}}~~}{z|zzyzzy|||{||{}}}|}~|}}}~}{z|zzzzyz}}|{||{}~}|}~|}}}~}zz|yyz{yz}||{}{{}~}{}~|}~}~~|y{|yyz{y{}}{{}{|~~}|~~|}~~}~~~|y{{yyz{y|~|{{}{|~~||}|}~~}~~~{y|{yy{zy|~}{{}{|~||}|}~}~~~{y|{yy{zy|~|z{}{|~||}|}~~~~~{y|{zy{{z|~}z|}{}~||}|}}}~~}{y|{zz{{y|~}z{}{|~}|}|}~~}~~}{y|{zz{{z|~}{{}{|~}|~|}~~}~~~|y{|zz{{y|~}{{}{|~}|~|}~~}~~~|y{|zz{{z{~}{z}{{~~|~~|}~~}~~~}|yz|zz{|z{~~|z||{~|~}|}~}~~~~~}zy|{z{{{z}~|z||{}}}}|}~}}~~~}{y{|zz{{z|~}{{}{|~}}~|}~~|~~~|yz|zz{{z{~~|z}{{~~}~|}~~|~~~~}zy|{z{{{z}~}z||{}}}}}}~}}~~~}{y{|z{{|z}~}{{|{|~}~}}~}|~~}{yz|z{{|z|~~|{|{|~}}}}~|~~~~}|yz|{{{|{{~~|{|{{~}~}}}~|~~~~}|zy|{{{|{{~~}{||{~}~}}}~}}~~~}|zy|{{{|{{~~}{||{~~~}}}~|}~~}|zy|{{||{{~}{||{~~~}}}~}}~~~}|zy|{{|||{~}{||{~~~}}}~|}~~~}|zy|{{||{{~}z||{~~~}}}~|}~~~}|yy|{{||{|}z|{{~~~}}}~|}~~~}{yz|{{|}{}|z|{|~|}}~|}~}~}}{yz|{{|}{}~{z|{|~|}}}|~~~~}}}{y{|{|}}{~~{{|{}~~|}~}|~~~~}|zx{|{|}|{~~{{|{}~~|}~}|~~~}}|zy||{|}||~z{|{}~~|}~}|}~}}|zy||{|}}|~z{|{~~~||~}|~~}~}}|zx||{|}}|~{{|{}~~||~}|~~}}}|zx{|{|}}|{{|{}~~||~}|~~~~}}|{x{|{|}}|{{|{}~||~}|~~~~}}}{xz}{|}}|~|z|{|~||}~|}~~}}}}|yz}||}~}~}z|{|}|}~||~~}~}}|yy||{}~}}~z||{~~|}~}|~~~}~}}|zy{}{}}}|{{}{}||}}{~~~}~}}{yz}||}~}~}z|{|||}~||~~~}~}}|zy|||}~~}{||{~~||}}|~~~~~|~}}|zy{|{}~~}|{|{~||}}{}~~~}}}}|{yz}|}~~}}{|{}||}~|}~~}}~}}{yz||}}~}~~{|||}||~||~~~|~}}|zy|||}~}~~{|||}||~||~~~~|~~}}|zy|||~~~~{|||}||~||~~~~|~~~}}|zy|||~~~~{|||~||}}|~~~~~|~~~}}|zy|||~~~~{|||}||~||~~~~|~~}}|zy|||~~~~{|||}||~||~~~~|~~}}|zy|||~~~~~{|||}||~||~~~}|~~}}}|zz}|}~~~{}|}|||~|}~~~}|~}}}{yz}|}~~}{}|~||}~|}~~~~||~}}}{y{}|}~|{}|~{|}}|}~~~~|}~~}}}{y{}|}~||}|~{|}}|~~~~~|}}~}}}{y|}|~~~||}|~{|~}|~~~~|}}~}}}{y|||~{|}|~{|~}|~~~~|}}~}}}{y|||~||}|~{{}}|~~~~~|}}}}}{y{}|~||}|~{{}}|~~~~|}}}}}{y{}|}~|{}|~{{}~|}~~~||}}}}|y{}|}}{}|~|{}~|}~~~~||~~}}}}zz||}~{}}}}{|~}|~~~~}{~~}~}}}{z|||~{|}|~{|~}|~~~~}{}~}}}}{y{}|~}|}|~{{}~|}~~~~||}~~}}|zz}|}~{}}}|{|~}|~~~~}{~~~}~~}}{z||}~||}}~{{~~|~~~~}{}~}~}}|z{}|~}|}}{{}~|}~~~~||}~~}}|z{}|~~|}}~|{|~|}~~~~~|{~}}~~}}{z||}|}}}}{|~}}~~~~~}{~~~}~~}}{z||}|}}}}{|~}}~~~~~}{~~~}~~}}{z||}||}}}{|~}}~~~~~}{}~~}~~}}|z||}||}}~{{~~}~~~~~}{}~~}~~~}|z|}}|}}}}{|~~}~~~~}{}~~}~~~}{z||}|}}}}{|~}}~~~~~}{~~~}~~~}{z||}|}}}}{|~}}~~~~|{~~}}~~~}{z}|}|}}~|{|}}~~~~|{~~}~~~~}z{}|~~|}}{{}}~~~~~{|~~|~~~~}z{}|~}|~}{{~~}~~~~}{|~~|~~~|z|}}~}|~}~{{~~}~~~~}{}~~|~~~~|z|}}~}|~}~{{~~}~~~~}{}}~|~~~~|z|}}||~}~{{~~}~~~~~}{}}~|~~~~|z|}}~}|~}~{{~~}~~~~}{|~~|~~~}z|}}~}|~}{{~~}~~~~}{|~~~|~~~~}z{}}~}|~}{{}}~~~~~}{|~~|~~~~~{{}|~~|}}|z}}~~~~~|{~~}}}~~~~{{}}}|}~~}z|~}~~~|{~}}|~~~~|{|}}|}~~~{{~~}~~~}{}}~~|~~~~}{|}}~~|~}{{}}~~~~~}{{~~|}~~~~{{}}~|}~~|z|~}~~~|{~}}|~~~~}{|}}}|~~~{{~}~~~}{|}~~|~~~~}{|}}~~|~~{{}}~~~~~}{|~~|}~~~~{{}}~|}~|{|}~~~~~|{~}}}~~~~|{|}}|}~~}{|~}~~~|{~}}|~~~~|{|}}|}~~}{{~}~}~|{}}}|~~~~}{|}}|}~~}{{~~}~~}~|{}}~}|~~~~}{|}}}}~~~{{~}~~~~|{}}~}|~~~~}{|}}}}~~~{{~~}~~}~|{}}}|~~~~|{|}}|}~~}{{~}~}~|{}}}|~~~~|{||}|}~~}{|~~~}~|{~}}|~~~~|{||}|}~|z|~~~~}}{{~~}|}~~~{{}|~|~~{{}~~~~~~}{{}~~|~~~~{|}}~~|~~{{~}~~~~~}{|}~~|~~~}{|}}~}|~~{{~~~~}~~}{|}~~|~~~}{|}}}|~~~{{~~~~}~|{|}~~|~~~~}{|}}}|~~~{{~~~~}~|{|}~~|~~~}{|}}}|~~~{{~~~~}~~}{|}~~|~~~}{|}}~}|~~{{~~~~}~~}{|}~~|~~~~{|}|~~|~~{{~~~}~~}{{}}|}~~~{{}|~|}~|z}~~~~}}{{~~}}}~~|{||}|}~}z|~~~}}|{}}~}|~~}{||}}}~~~{{~~~}~|{|}~~|~~~~{|}|~~|~~{{}~~~}~}{{~~}|}~~~|{||}|}~}{|~~~}}|{}}~}|~~}{||}}|~~{{~~~~}~~|{|}~~|~~~{|||~~|~~{{~~~}}~}{{~~}|}~~~|{}|~|}~|{}~~~}}{{~~}}|~~|{||}|}~}{|~~~}}|{}}~}|~~}{||}}}~~{|~~~}}|{}}~}|~~}{||}}}~~{|~~~~}}|z|}~~|~~}{||}}|~~~{{~~~~}}|{|}~~|~~~}{||}}}~~{|~~~~}}|z|}~}|~~~}{||}}}~~{|~~~}}|z}}~}|~~}{||}}}~}{|~~~}}|{}}~}|~~|{||}|}~|{}~~}~}{{~~}|}~~~~||||}|}~|{~~~}}~}{{~}}~|}~~{|||~~|~~{{~~~}~~|{|}}~|~~~{|||~~|~~{{~~~~}~~|{|}~~|~~}{|||~}|~~{{~~~}~~|{|}~~|~~}{|||~}|~~~{{~~~}}|{|}~~|~~}{|||~}|~~{{~~~}~~|{|}~~|~~~{|||~~|~~{{~~~}~~|{{}}~|~~~~{|||~~|~~{{~~~}~~}{{~~}|}~~~{{||}|}~|{}~~~}~}{{}~}}}~~|{||}|}~}{|~~~}}|{}}~}|~~}{|||}}~~~{|~~~}~|{|}~~|~~~~{|||~~|~~|{~~~~}~}{{~~}}}~~~|{||}|}~}{|~~~}}|{}}~}|~~}{|||~}|~~{{~~~~}~~}{|}~~|~~~}{|||~~|~~|{~~~~~~}{{~~}}}~~~{{||}|}~|{}~~~~}~}|{}}}}~~~|{||}|}~}{|~~}}|{}}~}|~~~~|{||}~}}~~~{|~~}}|{|}~~|~~~|{|||~}}~~~{|~~}}|{|}~~|~~~~}{|||~}|~~~{|~~~}}|{|}~~|~~~~}{|||~}}~~~{|~~}}|{|}~~|~~~|{|||~}}~~~{|~~}}|{|}~~|~~~~|{|||~}}~~~{|~~}}|{}}}}~~~~|{||}|}~}{}~~~~~~}|{}}}}~~~~{{||}|}~|{}~~~~~}{{~~~|~~~}{{||}~|~~{{~~~~~~~}{{~~~~|~~~~}{{||}}|~~{{~~~~~~}{|~~~|~~~~}{|||~}|~~{{~~~~~~}{|~~~|~~~~|{|||~}|~~~{|~~~~~}{|~~~|~~~~}{|||~}|~~{{~~~~~~}{|~~~|~~~~}{{||}}|~~{{~~~~~~~}{{~~~}~~~~}{{||}~|~~|{~~~~~|~|~|z~}}|}~}|{{{~~{}}}z~}}~}~|z~~~~|~}}}|z|}~~|}~~~z}~~}~}|{~~~~|~~}z|}|~||}}~|}~~}~~|}|~~~~}~}~|||~|~|}}~|}}~}}~~~{{z|~~~}|{{|~}}|~|zz~~~~~~~}}|{|~}|~}}|{zy{|~}~|{~}|}|}}~|}}~~}{}|{z|}~}{||~}}~}}~~|~}~~~~~~~}~~||}~~}||}~~~}~}~}|~|z{{}~~~~|}|{|~~~~}}|{{}~~~}~}}{{|~}}~~~|zzzz}~}~~||~~~|~}|~}}}|}~}}|{|{{{~}{{}|~~~~~|~~~~~~~}~~}}~{|}{{|~}~}}}|~{z||}~~~~|}}|}~|}}}{||}~~}}{{{|}}}~}{z{z{~~~}~}||}~~~~}}}}~}~||}|}z{{{z}~|{~}}~~~~|~~}}~|~}~~}{}|{}{z}}~~}}~|~{z}}|~~~}~~{|~}}~~z}|z}~|~~}}{z|~}~~}~{y{||~}~}{}~}~~~}~~~z|~~}|~}||{}zz{|{}~~||}}~~}~}~~~}}~}~|}~}z|}|}~}|z}~~}|~~~|{}}|~}}}~||~~~z{}{||}~~}~~|y|~~}~~~}~|zz}}~~|~{|~~|~}|z~}|~~~~{{}zz|}|}}~}~~{~}}}}~}}~}||}|y|}}~{|{{~~~|{~~}|}~||~~||~}|~~~~}{{}||}|~~~}||z|~~~~|~}zz}~}~|}~~{{|}~}~|z}~|~~|z|{y|~}~}|{|~}}}|~~~~~|~~~~}{}}zz}~~|z||~}~~~|}~}}~}|~~}}|~~||~}}}}|{}~}}}||~|{|{}~~~}|~|{{~~~~}|~}}{|~|}~~~}|{~~~|~~|z|zz}~~~||}{}~~~~|}~}~~|~}~|{}|zz~{z}}~}~~}|~|~~}~}~||~}||~}}{||}~~~}}~{}}z|}|~}~||~||}~}}}|~||}|~}|~{}~{}~}~~}{z|z{}||~~||}~}}}~~}~~~~}|||||z}~{|~|~~~~|}}~}~~}}~}|{~~~||~~|}}~|}{~}{~~~~}{|}|~~|~~z|}}~|~}{}~~}zyz{}~~~}||~~~~~~||}~~}||zzz|~||~~}~}~||~~}~~||{{{|~|{}}~~~}~~{~|y|~}~~~|{z{}~}~~{}}~~|~~~{|}y{}|~}{zyz}|}~~}|~}}~~~|~{z}}~~}{zzz|||~{{~~{}{|}}~~~|zzz|}|~~{y|~|z}}}||{}~|{|{|||~yy}~}~{|}|||{{~~}~~{{|{|z{}z{}}~|{~}~}|z}~}~}z{{{{z|~~{z||~~}}}~|}{~~|}}{{|{{{{~{z{{~~}~|{~}|~||~~~{|||}{{}~~{|z{||}||~}||}{}~}}~||}{{}}~}}{|~}|}}|~~}}|{~}}~~~~~|||z{}}~z|~}~}|~}~}|}{|}~~~{||y{}}~z||{~|~~}~}{|{~|~~}{}|{}}|~~{|z{~{~}}~{|~{{~~~}~~~}~{{~}|~~~~}}z||{}}}~{}{|~~~~}~}~~z|}{|~}~~~|y~{|}}~|~}|~}}~~}}}|z}}{}}~~{y}}~}}}|~}~~}}~~{|~||~|~~~z{~}}~|~{}}~|~~~}~|~~{}}|~|}{}~~~~}{~}|~~~~|~~~~~}}}|}{||}}|~|~|}~|~~~~}~~}||~~{}|z~~|}~}{}}}}}~~~}~}}||~|}~|}~~~~{|}~~|}~~~~~}}~}~|~|{~|{~~~~}|~~~~|}~}~~~|~}}}~~{}|z|~}|}}~~|{~~}~~~}~~}~{|}z{~~~}}|~|~~~~}~}~}}~||~{z}}~}}{~|}}~~~~|~}~~|~|y{}}~~~~||~|~~~}}}~~||~~|}}zz||~}{~~}}}~~~~}~}{}}}~~}||x}|{}}{~~|}}}~~~{}}|~~~~|}yx|{|~~||{~~}}|}~~}}}|~}{{y}|z}~}xy~~~~}~|~||~|~~~~yz~{|}~}~}{z}~}}~{{}}}zz~~~~}z|}z~~||~y{~~}|~~~}||||~~}}~|{}}zx{|{{|||~}}}}}}~{|~~~|~}~y{|y|}z{~~{zz~~|}~~~~||}}}{y}}{|||{z}}~}{{}}~|{{~~~~}~~|{}|y}}~{y}||||~}|~~~{|~~~~~~~{}}zx|~||{|}}||}}}~~{}~}}~}y{|y|~}|~|~|{{~}~}~~|~}}~}~~}~|}}{y}~}~}{{{{~|~}{{}~||~~~|~}{z~}|}~~~|zz~{|~|}~}}}}{~}~|{~}zz~}~~|{{{}}|~~~}||~|}zy}{{~}|{}}}~~{~}~}~}~~~~~~~~|{}|z}}~~}zz|}~}}~}||}~}~}}{}~}{~||}}~}}{{}}{|~{~|~~|~~~|~|}~|z|}~}~~||~}~~{~||~~|}}}~~|z{}z}}||{||~}~}~~}~~~}}~||}}z{~}~~}z{}}}|}|}}~}~}{}~|{}~{~}~{}}{}}~||}}|||~~}}|{}z{~~|}}||}~|~~{~~||~~}|y{}z|~|}}{~|{~}~}~}}~|~~z}}zy~~~~~zz~~|~}~~}~|~}~~{{~}{||}~|{}}}|~|}~||{~~}~~|}~z|~|{}}~{}~}~}{~}||}~|~~~|~~{y{|{~}}~}~||}}}}~~}~~}}~~{}}z||z{~~}zz~~~|~~}|~~~~~{z}|{|}~~}~{z}~}}~{|~||z}~~~~||}z|}{|~z{~~~|}}~}{{}}}||~|yz}{|}~||}{}~~|}}~{~~~~|{}zz}|z}}~~{z}}~~{~~}~~~}~|z{}||}~}|}z{~}}{}}|{~~~~~}{|}{|~}}~~}y|~|}|}~|}}|}}~~{~{xy~{~|~}{{}~}|~||~~~|}~}z|}zxz}z}~}}}~}~~~}~}}~~~{~}}{~~zy{}|}}|{|~}~~}{~}~~z|~~}~|zz|~}~~}}z|~~|}~|z~}}}{~}}~~{y{}{~{}{}~~||{~{z~~~~||~~}|}|yx|}z~}||~}~||}{~~~}}~}|||yy}~|~}{z|~~}}~~|~~~}|~~{}}zz|}}}|~}{{~~|~~|z~~~}~~{}|}~}{z|||~~|~~{}}}{{z}}~}{~~}}}|yx}}{~}|z~}~{|||~~~}~~}{}~|xz~}}}}~{z~~|~}|~~~~}~~z~|y|~|~|~|||~~~~}~|{}~~~~}|~~|}{z|}{~~}~~}}}}~}}~~}{}}~||}~}|zy{}|~~~~|||~~|}~|~~~~}}~~}}~|{xz}}}~~{{~~|}}{~}~~|}}|y{~|~|~}{|}~~|}{~}|}|~~{y|}{}~|~|{}~~}|~}{}~~~~~}|~~~|}zy{}{~~}~||~~~~|~~~|~~}~|~~~|~}{yy||~~~~}{}~}}}~||~}||}}|{z}}}~~}}z}}~}}|{}{~~~}~}zx}~|~~||{}~~}||{~~~||~~{~{x{}{}~|~~||~}~|~}|~~~~~|~~{}~{yz|||~}}{}~}~}~}}{~~~|~~||}|zz|~|~}}{|}~|}{~~~||~|~~{y{~|}|}}{{~~|~}z}~}~}{{}|yz}{|}}|{~~~~|}~{}~}~~|~~{|~|yz}||~}|{~~}~~}}||~~|~~~{|~|zz}}|~}|z}~}}||{~~~|}|}}zy|}|~||z}~~~|~|{~~~|}}|~}zy|}{~~|}{}}~}|~|{~~~|~~}|~}zy|}{~}|{}}~~}|}{~~|}~||~|yz}}|~}|{}~}~|}{~|~|}}zz}}|~|~|{}~~~|~|{~~~~|~|}~|zz}||~}|~|{~~~~}|~{|~}~}||}}|yz}||}}~|{~}|}~{}~}}~~{}}{y{~|}|~}{|~~|~}{~~}}~{~}{y|}|~~|~|{|~}~~}}{~~~}}~~|~}{y|}|~~|~||}~~~}~~}{~~~|}~}|~|zy|}|~~~}}||~}~|~}|~}}}|~|{y{}|~}~}{|~}}}{~~}}~|}}{z|~|}|~}{|~~~|}{~~}}|~}{y{}{}~|~}{|~~~|~}{}~}||~~{y{}{}~~}}~||~~~~|}~~|}~~~~}|}~{zz||}~}~||}~~~}}||~~|~}|~|{z|}|~~|~}{}}~~|}{~~}}|~}{y{}|}~|}}{|~~|~}{}~~||}~|yz}|{~~}}||~~~~~}}~||~~}~~}|~}zy|}{~~|~}|}}}~|}{~~}}~|~}{z|}{}~}~}{|}~~|~~{~~}||~~{y{}||}}~{{~~~|}~{}~~||}~|zz}|{~}|||~~}~~}}~||~~~}}}~|zz||{~~||{}}~~}}||~~|~}|~|zz}}{~~||{}~}~}|}|~}~}|~}zz|}{~~|~|{}}~~||{~~|}}|~}zz|}{~~||{}}~}|~||~~~}~}|~}zz||{~~||{}}~~}|}|~}~||~|zz}}{~}||{}~}~~}}~||~~~}|}~|zz}||~}|~|{~~~~~}}~||~~||}}{z{}||~~}}~||~~~~|}~{}~}}|~}{y{}{}~~}~}{|~}~|~}|~~}}~|~}{y{}|}~|~}{|~~~|~}{~~}~~|~}{z|}{}~|~|{}}~~|~}|~}~~|~}zz|}{}}|~~||}~~~}~~}|~~}}~|~}zz||{}~~|~}|}~~~|~}|~}}~|~}{y{}|}~~|~}||~}~}~}{~~}}~|~}{z{}|}~~|}}{|~~~|~}{~~~}|~~{z{}||~~|}}{|~~~|}~|}~~}|}~|zz}||~~}|~||~~~~~}}~~|}~~}}}~|zz|||~}|~~||}~~~~}~}|~}}~|~}{z|}{|~~|}}||~~~|}~|~~~}|}~|zz}|{~~}|~||~~~~}}~||~~~}~}|~}zz||{}~}|~||}}~~|~}|~}}~|~}{y{}{}~~|}}||~~|~~{~~~}|}~|z{}{|~~}}~||~~~~|}~|}~~}}}~|zz}|{~~}|~||~~~~}}~||~~~}}}~|zz}|{}~}|||~~}~~}|~||~~}}}~|zz||{}~}|~||}}~~}|}|~}~}|~}zz||{}~}|~|{}}~~~|~}|~}~}}~}zz||{}~}|~|{}~~~}|}|~~}~}|~}zz||{}~}|~||~}~~}|~~||~~~}}}~}zz||{}~}|~||~}~~}}||~~}|}~|zz}|{~~||~||~~~~}}~|}~~~}|~~|z{}{|~~|}}||~~~~|}~|}~~}|~}{z|}{}~~|}}|}~~~|~}|~~}}~|~}{z||{}~~|~}|}~}~|~}|~}~~|~}{z|}{}~~|~}|}~~~~|~}|~}~~|~}{z|}{}~}|~|{}}~~}|~}|~}~~|~}zz|}{}~}|~||}~~~~|~}|~}~~|~}{z||{}~}|~}|}~~~|~}|~~~~|~}{z{}{}~~|}}|}~}~|~~|~~~}|}}{z{}{|~~||}||~~~~|}~|}~~}}}~|zz}|{~~||~~||~~~~}}~||~~}}|~}zz}|{}~}|~||}}~~}|~~}|~~~|~}{z||{|~~|}}|}~}|~~|~~~}|}~|z{}|{}~||~||~~}~~}}~|}~~}~}~}zz||{}~}|~||}}~~~|~}|~~~~|~}{z|}{|~}|}}|}~~~~|}~|~~~}|~~{z{}{|~~||}||~~~~~|}~|}~~}}}~|z{}|{}~}|~||~~}~~}|~|}~~}}}~}zz}|{}~}|~~||~}~~}}~||~~}}}~}zz}|{}~}|~||~~}~~}|~~}|~~}~~}~}zz||{}~}|~~||}}~~~|~}|~}~}|~}zz||{}~}|~||}}~~}|~}|~~}}}~}zz||{}~}|~~||}}~~}|~}|~~}}}~}zz}|{}~}|~~||~}~~}|~~||~~~}}}~|zz}{{}~||~||~~~}~|}~|}~~}|}~{z{}{|~~||}||~~~|}~|~~~~|~}{z||{|~}|}}|}~~~|~}|~~~~~|~}{z||{|~}|}~}|}~~~~|~}|~~~~}~}{z||{|~}|~}|}~~~~|~}|~~~~}~}{z||{}~}|}~||}~~~~|~}|~}~~|~}{z||{|~}|}}|}}~~~|~}|~~~}~}{z||{|~}{}~||}~~~~|~}|~~~~|~}{z|}{|~}|}}|}~~~~|~~|~~~~}~~{z{}{|~~||~}||~~~}}}~|}~~}}}~|z{}|{}~||~~||~~}~~}}~~|}~~}~}~}zz||{}~}{~~||~}~~}|~}|~~~|~}{z||{|~}|}}|}~~~~|}~|~~~~}}~|z{}{{}~||~~||~~~~~}}~|}~~}~}~}{z||{|~}{~||}}~~~|~}|~~~}~~{z|}{|~}{}}|}~~~~~|~}|~~~~|}~|z{}{{}}||}||~~~~|}~|}~~}}}~|z{|{{}~||~~||~~~~~}}~~|}~~}}}~}z{}|{}~||~~||~~}~~}|~}}~~~~}~}zz||{}~}{~~||~}~~}|~}|~~~~}~}{z||{|~}{~~||~}~~}|~~}|~~~~}~}zz||{}~}|~~||~}~~}|~}|~~~~}~}zz||{}~||~~||~~}~~}|~~}}~~~}}~}zz||{}~||~~||~}~~}}~~}}~~}}}~|z{}{{}}||~~||~~}~}}~|}~~~}~~|z{}{{}}||}||~~}~|}~|~~~~}~}{z|}{|~}{}~}|}~~~~~|~}|~~~}~}{z||{|~}{}~||}}~~~|~}|~~~~}~}{z||{|~}{}~||}~~~~|~}|~~~~}~}{z||{|}}|}~||}}~~}|~}|~~~}~}{z||{|~}{}~||}~~~~|~}|~~~}~}{z||{|}}{}~}|}~~~~~|~}|~~~}~}{z||{|}}{}~}|}~~~~~|~}|~~~~}~~|z{}{{}}||~}|}~~~~|}~|~~~}}~|z{|{{}}||~~||~~~~~}}~~}}~~~~}~}{z||{|}|{~~||~}~~}|~}}~~}~}{z||{|}}{}~}|}~~~}~|~~|~~~~}}~|z{}{{}}||~~||~~}~~}}~~}}~~~~}~}{z||{|}}{}~||}}~~~|~}|~~~}~~{z||{{}}{}~}|}~~~~~|}~|~~~~}~~|z{}{{}}||~}|}~~}}}~|~~~}}~|z{}{{}}||~~||~~}~~}}~~}}~~~~}~}z{||{}}|{~~||~~}~~}}~}}~~~~}~}{{||{|}|{~~||~~}~~}|~}}~~~~}~}{z||{|}}{}~||~}~~}|~}}~~~}~}{z||z|}|{}~||~~}~~}|~~}}~~~~}~}zz||z|}|{~~||~~}~~}}~}}~~~~}~}z{}|{|}|{~~||~~}~~}}~~}}~~~}}~|z{|{{}}||~}||~~~}~}}~}~~~}~~|z{}{{}}{|~}|}~~}~|}~|~~~~}~~{z||{{}}{}~}|}~~~~~|~}|~~~}~}{z||{|}}{}~}|}~~~~~|~}|~~~}~}{z||z|}|{}~||}}~~~|~}}~~~}~}{z||{|}|{}~||~}~~}}~}}~~~}~}{z||z|}|{}~||}}~~~|~}}~~}~}{z||{|}}{}~||}~~~~~|~}|~~~}~}{z||{{}}{}~}|}~~~~~|~~|~~~}~~|z{|{{}}{|~}|}~~}}}~|~~~}}~|z{}{{}}||~~||~~}~~}}~~}}~~~~}~}{{||{|}|{}~||~}~~}}~}}~~}~}{z||{{}}{|~}|}~~~~~|}~|~~~}}~|z{}{{}}||~}||~~}}~}}~}}~~~~}~}{{||z|}|{}~||~}~~~|~}}~~}~~{z||{{}}{|~}|}~~~}~|}~|~~~~}~~|z{}{{}}{|~}|}~~}}|}~}~~~}}~|z{}{{}}|{~~||~~}}~}}~~}}~~~~}~}{{}{z|}|{~~||~~}~~}}~~}}~~~~}~}{{||z|}|{}~||~~}~~}}~}}~~~~}~}{{||z|}|{}~||~~}~~}|~}}~~~~}~}{{||z|}|{}~||~}~~}|~}}~~~~}~}{{||z|}|{}~||~~}~~}|~}}~~~~}~}{{|{z|}|{~~||~~}}~}}~}}~~~}}~|z{}{{}}||~}|}~~}}}}~}~~~}~~|z||{{}}{|~}|}~~~~}~|}~}~~~}~~{z||{{}|{|~}|}~~~~~|~}}~~}~}{z||z{}|{}~||}}~~~~|~}}~~}~}{z||{|}|{}~||~}~~~~|~}}~~~}~}{{||z|}|{}~||~}~~}|~}}~~}~}{z||z|}|{}~||~}~~~~|~}}~~}~}{z||z{}|{}~||}}~~~~|~~}~~}~~{z||{{}|{|~}|}~~~}~|~~}~~~}~~|z||{{}}{|~}|}~~}~}|}~}~~~~}~|z{|{{|}|{~}||~~}}~}}~}}~~~~}~}{{||z|}|{}~||~~}~~}|~}}~~}~~{z||{{}}{|~}|}~~~}~|}~}~~~}}~|{{}{{|}|{~}||~~}}~}}~~}~~~~~}~}{{||z|}|{}~||~}~~~~|~}}~~}~~{z||z{}|{|~}|}~~~~~|}~}~~}~~|z{|{{}}{|~}|}~~}~}}}~}~~~~}~}{{}{z|}|{~~||~~}}~}}~~}~~~~~}~}{{|{z|}|{}~||~~}}~}}~}}~~~~}~}{{||z|}|{}~||~~}~~}|~}}~~~~}~}{{||z|}|{}~||~}~~~}|~}}~~~}~}{{||z|}|{}~||~~}~~}|~}}~~~~}~}{{||z|}|{}~||~~}}~}}~}}~~~~}~}{{}|z|}|{}~||~~}}~}}~}}~~~~}~}{{|{z|}{{~}||~~}}~}}~}~~~}~~|z||{{}}{|~}|}~~~}~}}~}~}~~|z||z{}|{|~}|}}~~~~|~~}~}~~{{||z{}|{}~||~}~~~~}~}}~~}~}{{||z{}|{}~||~~}~~~~|~}}~~}~}{{||z|}|{}~||~}~~~~}~}}~~}~}{{||z{}|{}~||~}~~~~|~}}~~}~~{{||z{}|{|~||~}~~~~|~~}~~}~~{{||z{}|{|~}|}}~~~~|~~}~}~~|z||{{}}{|~}|}~}~}}}~}~~~~~~|{{}{z|}{{~}||~~}}~}}~}~~~~~}~}{{||z|}|{}~||~~}~}~}}~}}~~}~~{{||z{}|{|~}|}~~~~~|}~}~~~~~|{{|{{|}{{}}|}~~}}~}}~}~~~~~}~}{{||z{}|{}~||~~}~~~~|~~}~}~~|{||z{}|{|~}|}~~~}~|~~}~~}~~|{||{{|}{|~}|}~~}~}}}~}~~~~~~}{{|{z|}{{}}||~~}}~}}~}~~~~~}~}{{|{z|}|{}~||~~}}~}}~}}~~~~}~}{{||z|}|{}~||~~}~}~}}~}}~~~~}~}{{||z|}|{}~||~~}~~~}|~}}~~~~}~}{{||z|}|{}~||~~}~}~}}~}}~~~~}~}{{||z|}|{}~||~~}~}~}}~}}~~~~}~}{{|{z|}|{}~||~~}}~}}~}~~~~~~~}{{}{z|}{{}}|}~~}~}~}}~}~~~}~~|{||{{|}{|~}|}~}~}~}}~}~}~~|{||z{}|{|~}|}}~~~~|~~}~}~~{{||z{}|{|~||~}~~~~|~~}~}~~{{||z{}|{}~||~}~}~~}~}}~~}~}{{||z{}|{}~||~~}~~~}}~}}~}~}{{||z{}|{}~||~}~~~~}~~}~}~~{{||z{}|{|~||~}~~~~|~~}~}~~|{||z{}|{|~}|}~~~}~}~~}~}~~|{||{{||{|~}|}~~}~}~}}~}~~~~~~}{{|{z|}{{}}|}~~}}~}}~}~~~~~}~}{{||z|}|{}~||~~}~}~}}~}}~}~~|{||z{}|{|~}|}~~~}~}}~}~~~~|{|}{z|}{{}}|}~~}}~}}~}~~~~}~}{{||z{}|{}~||~}~}~~}~~}~}~~|{||z{}|{|~}|}~~~}~|}~}~~~~|{||{{||{|~}|}~}~}}}~}~~~~~~}{{}{z|}{{}}||~~}}~}}~}~~~~~~~}{{|{z|}{{}}||~~}}~}}~}~~~~~}~}{{||z|}|{}~||~~}~}~}}~}}~~}~}{{||z{}|{}~||~~}~}~~}~}}~}~}{{||z{}|{}~||~~}~}~}}~}~~~~}~}{{|{z|}|{}~||~~}~}~}}~}~~~~~}~}{{}{z|}{{}~||~~}~}~}}~}~~~~~~~}{{|{z|}{{}}|}~~}~}~}}~}~~~~~|{||{{||{|~}|}~}~}~}}~}~}~~|{||z{}|{|~}|}}~~~~|~~}~}~~{{||z{}|{|~||~}~}~~}~~}~}~~{{||z{}|{}~||~~}~}~~}~~}~}~}{{||z{}|{}~||~~}~}~~}~~}~}~~{{||z{}|{}~||~~}~}~~}~~}~}~~{{||z{}|{|~||~}~~~~}~~}~}~~|{||z{||{|~}|}}~~}~|~~}~~~~|{||{{||{|~}|}~}~}~}}~}~~~~}{{}{z||{{}}|}~~}~}~}}~}~~~~~~}{{|{z|}|{}}||~~}~}~}}~~}~}~~|{||z{||{|~}|}~}~}~}}~}~~~~}{||{z|}{{}}|}~~}~}~}}}~~~}~}{{||z{}|{}~||~~}~}~~}~~}~}~~|{||z{||{|~}|}~~~}~}~~}~~~~|{||{z||{|~}|}~~}~}}}~}~~~~~}{{|{z|}{{}}||~~}}~}}~}~~~~~~}{{}{z|}{{}}||~~}~}~}}~}~~~~~~}{{|{z{}|{}}||~~}~}~}}~~~~~}~}{{||z{}|{}~||~~}~}~~}~~~~~~~}{{||z{}|{}~||~~}~}~}}~~~~~~~~}{{|{z{}{{}~||~~}~}~}}~}~~~~~~}{{|{z|}{{}}||~~}~}~}}~}~~~~~~}{|}{z||{{}}|}~~}~}~}}~}~~~~~|{||{{||{|~}|}~}~}~}}~}~~~~|{||z{||{|~}|~}~~~~}~~}~}~~|{||z{||{|~||~~}~}~~}~~}~~~~{{||z{}|{|~||~}~}~~}~~}~~~~{{||z{}|{}~||~~}~}~~}~~~~~~~{{||z{}|{|~||~}~}~~}~~}~}~~|{||z{||{|~||~}~}~~}~~}~~~~|{||z{||{|~}|}}~~}~}~~}~~~~|{||{{||{|}}|}~}~}~}}~}~~~~}{||{z||{{}}|}~~}~}~}}}~~~~~~}{{|{z{|{{}}||~~}~}~}}~~~~~~~|{||z{||{|~}|}~}~}~}}~}~~~~}{||{z||{{}}|}~~}~}~}}}~~~~~~{{||z{}|{|~||~~}~}~~}~~}~~~~|{||z{||{|~}|}}}~}~}~~}~~~~|{||{z||{{}}|}~}~}~}}~}~~~~}{||{z||{{}~|}~}}}~}}~}}}}}~~|z{|{y{}|z|}|}~}||~~}~~}}||}}}~}{|}|}}}}}}~}|}~}~{~~|~~}~~|zz|{{|yz~}|~~~~~~{|~}}~}{|}|yy{{z{|{|}|{{}~~~}|}{{}~~~}{|~}}}}~}}}}|}~~~}{~|~~~}~~|zz|{{{xz}}~~~~~{|~}}~|||||yy{{z{|{|}{{|}~~}~~|||}~~|{|~}}}}~~|}~}}}~~}||~}~~~}~~}{{{||{zy}~}}~~~~~~}|}~}~}||}|zzz{z{||||}{|}~~~}}}|||}~}{||}}}}}~~~}}}~~~~||}~~~~}~~}{{|||zyz}~}|}~~~~~}}}~}~~|||||z{{{{{||}||||}~~~}}~|||}~~}{{|}}}}}~~~}}~~~||}~~~}}~}|{|||zyz}~}|}~~~~~~}}}~~~~}||||{{{{{{|||}}|{}}~~~~}~|||}~~~|{{}}}}}}~~}|~~}||~~~}~~}|||}{zyz~~}|}~~}~~}}~~~~|||}||z{{{{|||}}|||~~}~}~~}|{}~~}|{{~}||}}~~|}~~~}||~~~}~}|{}}{zyz~~}||~}}~}}~~~~~~}||}|}|z{{{||{|~}|||~}}~}~}{|~~~}{z|}}|}}}~}|}~~}~}||~~~}~~}{|~|{zy{~}{|~}}}~~~~~~|{|}}}{{|{{|{{~~|||}~}~}}}|{}~~}|z{}}||}}~}|}}~}||~~~~}~~||~}{zyz~~}{{~~}}~}~~~~~~|{|}}}{{|{{|{{}~}|||~|~~}}}|{}~~}|z{}}||}|~}|}}~}||~~~~~~~||~}{zy{~~|{{~~}}}~~~~~~~|{|}}}{{|{{|{|~~|||}}}~}~~}|{}~~}{z|~}|||}}}|}~~~}|}~}}~~}|}}|zzz}~}{{}~}}~~~~~~~~~~}{|}}}||{{{|||}~}||}~~}~~}~~}||}}~|z{|}}|||}}}}}~~||~~}}~~}|}}|zz{}~~}{{}~}}~~~~~~~~~~}||}}}|||{{|||}~}||}~~}~~}~~}||}}}|z{|}}|||}}|}}~~||~}}~~}|}}|zzz}~~}{{}~}}~~~~~~~~~~}||}}}|||{{{|{}~~||}}}}~~}}|}}}}{z{}}||||}}}}~~}}}~}~~~|}}}{zz{~~~}{{}~}}~~~~~~~~~~}{|}}}}||{{||{}~~||}}~}}~~}}|}~}}{z{}}||||}}|}~~}}|~~~~~||~}{zzz~~}{{}~}}~~~~~~~~}{|}}}|{|{{||{}~}|||~~}}~}}||}~}}{z|}|||||}||}~~~}|}~~}~~|}~}{zy{~~~|{{~}|}~~~~}~|{|}}}|||{{|{{~~}||}~}~~~~}|}~}}|z{}}||||}}|}}~}}|~~~~~~||~}{zy{~~}{{}~}}~~~~~~~~~|{|}}}|{|{{|{{}~}|||~}~~~~~}|}~}}|z{}}||||}}|}}~}}|~~~~~~||~}{zy{~~|{{}~}}~~~~~~~|||}}}|||{{|{|~~|||}~}~~}||}~}}{{|~}||||}||}~~}}}~}}~~}|}}|zzz}~~}|{|~}}~~~~~~~~}||}}}|||{{{{{}~}||}~~}~~~~~}||}}}|{{}}}||||}|}}~~}}~~}}~~}}}}{zz{}~~}{{}}}}~~~~~~~}||}}}|||{{{{{}~}||}~~}~~~~~}||}}}|{{}}}|||||||}~~}}~~}}~~}|}}|zzz}~~}|{}}}}~~~~~~~~}||}}}}||{{{{{|}}||}}~~~~}}|}~}}{{|}}}|||}||}}}}}~}~~~}}}}{zz{~~~}{|}~}}~~~~~~~}||}}}}||{{{{{|~}|||~~~~~~}}|}~}}{{|~}||||}||}}~}}}~~~}~~||}}{zz{}~}|{}~}}~~~~~~~~}||}}}}||{{{{{|~}|||~~~~~}||}~}}{{}~}||||}||}~~~}|}~~}~~}|}}|{zz|~~~}{|~}}}~~~~~~~~~||}}}}|||{{{{{}~}||}~}~~~~}|}~}}|{|}}}}||}||}}~~}}}~~~}~~||}}{zy{~~}|{}~}}~~~~~~~~|||}}~|||{{{{{}~}||}~~~~~~~}|}~}}|{|~}}}||}|||}~~}}}~~}~~~||}|{zz{~~~}||}~}}~~~~~~~||}}}}|||{{{{|}}|||}~~~~}||}~}|{{|~}}||||||}~~}}~~}}~~}|}}{zzz}~~}||}~}}~~~~~~~~~}||}}}}||{{{{{|}}||}~~~~~~~}||}}}|{|}~}}||||||}~~}}~~}}~~}|}|{zz{}~~}||}}}}~~~~~~~~~}||}}}}||{{{{{}}}||}~~~~~~~}||}}}|{|}~}}||||||}~~}}~~}}~~}|}}{zzz}~~}||}}}}~~~~~~~}||}}}}||{{{{{|}}}|}}~~~~~}|}}}}|{|}}}}||||||}~~}}}~~}~~~}|}|{zz{}~~}||}~}}~~~~~~~~}||}}}}||{{{{{|}}||}~~~~~}}|}~}}|{|~}}}||||||}~~~}}}~~}}~~||}|{zz{}~~}||}~}}~~~~~~~}||}}~}||{{{{{|}}|||~~~~}||}~}||{}~}}}|||||}}~~}}}~~}~~}|}}|{zz|~~~}||}}}~~~~~~~~}|}}}}|||{{{z{}}}||}~~~~~}|}~}}|{|~~}}||||||}~~}}}~~}}~}||}|{zz{}~~}||}}}}~~~~~~~~}|}}}~|||{{{z{|}}||}~~~~~~}|}~}}|{|~~}}||||||}~~~}}}~~}}~}||}|{zz{~~~}||}}}~~~~~~~~}|}}}}|||{{{{{}}}||}~~~~}}|}}}|||}~}}}|||||}~~~}}~~}}~~}|}|{zz{}~~}||}}}}~~~~~~~}|}}}}}||{{{z{|}}||}~~~~~~~}|}}}}|{|}~}}||||||}~~}}}~~}}~}}|}|{zz{}~~}||}}}~~~~~~~~~}|}}}}}||{{{z{|}}||}~~~~~~~}}}}}}|||}~}}||||||}~~~}}~~}}~~}|}|{zz{}~~}||}}}}~~~~~~~}|}}~}||{{{z{|}}}|}}~~~~}}}~~}||}~~}}|||{{|}~~~~}~~~~~}}}|{yyz|}}|{{|}}~~~}||{zzzz{}~~}~~~~~~~~~~}{zzzzzyyz|||}~~~~~}~}}}}~~~}}||{zzz{|~~|{z{{zyz{{{{{{{{|}~||}|{|~~~~~}|{{|{zyz|}|||||{{zyz{{yxwwxxxy{|}~|{{|~~~}|{||||}}}}|{zzyxxy{{{zz{}}~~}|||{|{{zzzzz{~~~}|{z{}~|{{||||}~}}~}||||}|||{{zzz{{}~~|||||{{}~|{zzzzzyxyz{{|}~~~~~~}|{{|}~~~}||}~}{yy{}||}}}}~~~~}|||||{z{{{|~~~~~~}}~~}~~|{{{zzzz{|||~~~~}}}}|~~~~~}~~~~~}|}|{zzywutuwyyzyxy{}~~~}~~~}~~~|{|~~}~~}}~~~~~~}}~~|||{|}}{ywxyyxxxxyz|~~~}}~~~~}|{{zyxxz||{zzzyxxy{{zzzyyz|~~~~~}|}~}|{{{{{zyyy|~~~}|{{{{yyz{zxwwxyxxz{|}}{zz|}~~}||}}|||}}{{{zywxz|}|{|{|}~~|{{|~~|||{zz}~||}|zz{|}}}}}}}}}}}~}~~~}~}||}}|{zyzzz|~~~||~~}{|~~}}|{z{zyyz}}}|}~~~}{{|||}~~}}~~|zzz|}|}}||}~~~}}}|{z{{||{|}}}~}|}~~~}||}{zzzz|~~}~~}}}|}~~~~~~}~}~}}}|||{yxwvvwwy{{yy{}}}~|{|~}~~}|{{}~}}~~}||}~}}~}}~~~||}|{|}~|zzz{{zzyxxy}~~}}~~~}~~~|{{zxwwy{|}}{zyz{||{||zyz|~~~~~~~~}}||}}||}}|z{zxxy~~}{z||{{{zzyyz{zxxyyy{~}}}|{zzz|~}}}|{{}~}~}|zxxz{}|}}|{|}zz|}}}}|{{|~~||}|||{zy{}~}~~}}~}||}|{zyzzz{}}}}~}}}|}}||{zyz}}|}}}~~}||{zz|~}||}}|{}}{{|~}}|}}{yz|~}{{||}~~}||~~}~~|{{zy{~~~~~}}}|~~~~~~~~|}||}~~}~}{yxyzzxvxzzz{~~|{|~~}{{}~~|{|}~~~}~~}|{{{|||~}~~~~~}}}|}~}}}}~~~{{|zxxz~|}~}~~~}}}|}~~|{zzxwyz|~}{z|~}|}}{z{~~~~~~}|||~~||~||~~~}}~~|z{zyy{~||~}}~|zyz|}}{yyxxy|~~~}|}~|{{{z{~~~}}}|~~{yz{}}|}~}}}}~}{{|}}}~}||}}~~~||}}}~~{yy|}~~~~~}~}||||{z{{{|||||~~~}||~~~~~}}|zz{~~~|{}~~~|{zz{~}}~}{|}||}~||{{}}z{|}}{z{zz|~}|{|}}~||{zz|~~~}}}~~~~}}}~~}}}{{|~~~~|yy{||ywxyy{||{{}~~|{|}~}{|}~~|~}|}~}{{z{}|}~}~~~}}}||~~~~}||}|}{zy|}{|}~~~~}~}}|{{}}|{{zyzzz}~~|{}~}}~~}}~~~}|z|~~~}{{}~~~~||{zz{}~~~~~{y{}|{{yxx{~|}~~|z{}|}~}}~}}}}~{||}}{|~~~~~}}|}~~}~||}}~{{}}||~~||}}}~~~~{z{}~~~}~}~}|{|||}~}~~|zz}~~~|{{~~~}}|}~~|{|}zz|~}~~|{zz|~~~{{||}~}}{z|~~|}}}|zz{zyz~~}|{{|~~}|zy|~}{{~~}|||}~~|}}|{z|~~~|}~~{{|}~}zyyxx{~{{|}}~~~}~{{~~|{|}~~}{y{|}~~||~~~}}~{z}~|{}~~}|{|}}}~~}|}}|}~}{z{~}||{{|||}~}||~~~~~~}~~~~}{|}}~~}|{{|}~~~~~~~}|||{{}~~}~~{{|~}}|zyy~~~~~~{|~~|}~}{{|}~~}}~~~~~}~~~~}zz}}~~|~~}}}~}}~}~~{z||||~~~||}~~~~~~~||}~~}~~}~~~~}}~}||||~~zy|~~~}|{}~~~}}~}}}}||~~}|}|{|~~}~~{z|}|||{|}}}~~~~~~}{{}~~}}zy{{zy{~~}|{|~|zz~|z{}}{{|~~|{~~~|{z}~||}}||}~~|{{{yxz}|||~~{z||}~~}||}~~{z{}~|z|~~~}}~}{{|~~{{~~}||zz~}}~}{|~}{{~~~}}|{|~}{{~}~~}~}|~~~}~~}{|~~||~~}}||}~~~}|}~~~~~}}}|{{~}|}~~|}~~}}~~|{|~||~}}~}~~{{~~{|}}}}{{|}~~}}}~~}~zy{||}}}~~~}~|z{{{{}}z{}}{{}~~|}}{|~~}~|{|}}|{}|z|}~~~|{}}{{}}|}}}|~~~~~}~}}}~~||}~||}~~}}{z{}|}~~||}~}}zyzzzz}~}~~~~||~~|}|{{|~|{{{}~{{~}{|~{z|}~~}}{{}|zy}~~~~}~}||{z|~|~}}}~~~}||}~|z|~~~~~~}~}|}}~~{|~~~}~~{yz}~||}~~{|~~}~~}|}}~~~|{}~~z{~}~}~|{}~|{|||~~{|~}}~~~~}}~~}||}}~~}~}|z|}~~||}}~~~}{~}}~{z|}}~}}~|{}~{{|||}|{|}~||~~}~|z{{{{~~~~~~~}z{|{z|~zz|~~{z{~~|}~{{}}}}|z{}}{|~|}~~~|}}{}~{{|~~||}|}~~~}|~}}~~{{|~~~~}~~}{z}|}~~~}~}|~}zzzyy|~}}~}~~}}~~}||}}}|z|}{}~~|}~~zy{}~}}z{}|{{}~||}}|zz~||~~~~~}~~~~~}{|~~}|~~~~~}|}~~}}~}}~}~~|zz{~~}}||}|}~}|}~~}~~}|}~||~|{}}}~~~|{}~~~}|{|||{z}}|}}|}}|~~}|}||~}}~~|{~~|}~~}||}~|z|~~~~|z{|}}}}|~~}~~~}{||{z{}}}}}|}}}~~}|{yz}~}~~}~}}{{|{z{}{{}~|z{|~~||~|{||||{z{}}|}~}zz}~~}~|{|}~}|{{~}|~|}}}~|z{}}|}~~~}|~}~~~|{~~}|zxx|~}~~}~~~~}~~~~~|{|~}}~~~|{~~~~~{z{}~}}{z||{|~~~~~~}{}~|z|}}}~~}~~~}{z}~~{{}~~}~||}~|{}~~~|}}|||~~}}{{}~}~}{|}}|~~}~~~}}~}|~~~||~~~~~}}~}~}}~{z}~|||zy}}}~~||}~}}||}}||}~~|}~}|}~}~~~|{}~~{z{|}~|}}{{{|}}}|}~~|z{||{z~}~~~~}}}|}~~|zy|~||}|}|{}}||{z{}|}~~|{{|}~~}|~}|||{|~|{|~~~~{yz|}~}{|}}{z|~}{}~~}}}}~}{{}~}{|~~~}|{}~}}|{}~}~}zxz}~~~}}}}~~}~~~~~~~}}}}~~~}zz|~~~}|{}~}~}}|{{{{}~~~}|}~~|}~~|~~~~}}~~}}~}}{y{}|z{|}}~}{|~~~}zz|}}}}}~~~~~}|{|~}{{||{}~}}~}|~~~~}}~~|{}}}}}|}~~}}}|{}~~}{z{~~}||}~|{{}}|}~}|}}|}~~~}|}~~||{z{||~|||}~~}|}}~|yy{{{{}}~~~}|}~}{|}{|~}||}}z{~}|z|}{{|}~~}~~~||}|{{~}~~~}{zzz{~|~}}}~~||}~~{|~~~~}}~}|}~{z|~~~~~~{{}~}|}~|{}~~}|}~|{|~~}}~}|}}|}~~~}}~~~~~}zzz{}~}~~}~~~}|}}~~}}|{z{~~|{|}}~~~~}~~}{|~~|}}}}|yz|~~|zz{{|~||}~}~~~}zz{{|~~~~~}|}}~}{{{{{|||}}||~~~~~||~}{{|||}|{|}~}|~}}}}~}~~|{}~||}~}{{{||}~~}}}||~}}~~~}~~~}|{z{~||~~~~~~}}~}}{yyzz{~~}~~}~}}}~~~~}~~}|}~}|{}~{{}~}||~{z{|~~~}{{||{{~~||~|{zyz}}}~~~~~}~~}||}~~}~~~~~}}}~}{|}~~}}~~~~|{{}~|||}}{|}~~|||}}}~~}~~}|}~||}~~~~|}~~~}|}|zzzz|~~~~~~~~}}||}~}}}|z{~~~|{{|~~}{|}|{|~}|||}~|{||}|{{{zz|~}}~}}}}~~|zzz{~~~~~~~~~~~{{{{z|~||~~||}~~|{}~~|z{{{||{{}}|}~~}{{}~~}~}||}~~|{z{|~~|}~~}|}~~||~~~}|~~~}|{|~~~}||~}|{yyz~}|}}}}~~~}~~~~~~~}|{}}}}~}}}~~~{z{}~}}~{{{{{|~~~~~|{|~}|zy{~}~~~~~~}|{|}~~||}~~~}|}~~~}}}|{|}~~~}||~~}||}|}~~}{{|||}~~~~}~~}||}~~}}}~~~~}~}}~|z{~}|{zz{~~~~~~}}}}||}}}}}||~}~}{{|~|z{|}~~||}~}|||~~~}}|zz{{zz|~}|||}}|zy{}}~~}|}~~}|}~|{{z{~}~~||}~|{|}~}|{zzz|~|{|~~~~~}zz{}~~~||}~}|{{|~~|{}~}~~}||~~|{|~~~~}|}~}}~~}||}~}}{yy|~||||}~~~}|~~~~~~}||}~~~~}|{|~}|{|~~}~~}~|{{{{|~~}}}~}{|}~~}|{|~~}}~~~{z{}~~|{{}}~~||}~~~}{zz{|}~~~~}}~~}||~~~~}{{{|||~~~~~~~||}}}}||}}~~}~~||}~~{z{}~}|{{}~}~~||||||}}}}}}~~~}{|}~}{{{{}~}~~}}~~}||}~~~}{yyzzz|~}|||}}{z|}|}}{{|~}{|~~}{{|~}||}~~|}}}}{{{zzz}}}}~~}zzz{|~}}~}|||}~}{{}~~~~}||}|z{}~~~~~~~|{}~}}~}{{}~~}~~}|{}~}||}~~}{{}~~~~~~~}~~~}~~|{{|}~~}}~~}}}~~}||{z{~~|{|}}}}}}}}}~~}}~~~~}}}}~}zz{}}|{{{|}}|}~~~}}~~}zyzz{}~~~~}~~~|z{{{|~}}}}~~|{|}}}{z{||}~||}~}||}|{{|}}}~~}|}~}}~|{{||}~~~~~}}~~~~~~~}}}~~~~~~}|{z{~~}~}}~~}~|zyyzz|~}}|||}~~~}}~}|}}|{{}~|{|~~||}||}~~}}~}|{z{zzz}~~~~}}}{zyz{~~}}}~}|{{}~~~~~~}}}}{z{|}}}~~~~~|{}~}}~|{|}~~}}~~}}~~}||}~~}{z{}~~~~~~}}~~~}}}~}|{{{}~~~~~~}}|}~}}||{{~~|{{|~~~~}}||}~~~~}~}||}}|{{|||{{{{|~~}}~~}||}}~|zyyz{~~}{{{{{}~}~~}}~}{{|}}|zz{{|~~|||~~}|}~~|||||{|}~~}~}}~}|{{|}~~}~~~}~~~}}~}}}}|}~}}|{{}~~~~~}|}~|{zyz{~}|||||}~~~~~~~}}~~}{{{}}|}}~~~~~~|||}~~~~}{zzzz{}~~~~~|{|~}|{zz{~~~~}|{{|}}}|}}~~}}}}~~}|||{{{|}~~}||}}}}}~|}}}|||}}~~}}}~~~}{z{|}}}}}~~~~~~}}~~}{{|~}||{{}~~}}}||}~~}}}||~~~~|{{|}~~}{{{|}~~}~}}|}~~}}||{z{{{{}~~~}|{|}~~~}{zyz}~~}}}~~}}}|{{{}~~~}}~}{{|}|{zzzz{~}||}~}}~~}}}|{zz|}~~}}~~}|{|}~~}||~~||}~}|{z|~~~}||}~|||~}|{zz{~}||||}}}~~}}}~~~~~~}|{|}~~~~}}|}~}|}~~~~~|{zzz{}~}}}~~}{|}~~}|{z|~~~~~}{z{|}}|||}}~~}}}~~~~~}}|{zz{|}}}}}~}}~~}}}|{{|}}~~~~~~~}|z{{||||}}}~~~~}}}~~|zz|~~}}||}~}}|||}}~~~}}}}~~~|{{|}~|{{{|}~~~}}}}~~~~|{zz{{{}~}|{{|}~~}|zz|~~~}||~~}|}}|{{}~}}~~~}|||||{zzyz{~}|}~~~~~~~~~~~|zyz{|~~~~~}||}~~~}||}~|||~~|zz{|~~~~~}}~~}~~~}{|}~~~}|{{}}||||}}}}|{|}~~~~~~~}|||}~~~}||}~~}~~~~~~}|{{z{}~}|||}~}||}}}}}}}||~~}}~}{z{|}|{{{|}~~}}}~~~~~~~~}{yyz{|~~~~~~~~~}{{{|}~~}{{{{{{z{{|}~~}}}~}|}~}{z{|}~~~~}}}~~~}|||}}~~~}~~~}}||}~~~~~|{{{|~~~~~~~~~~~|zzzz{}~}|{{{|}~~~~~~}|}~~}~}|{|}}||~~}}~}}}~}}}}|{zzzzz{~~~~~~~}}}~~}{zyz{}~}}}}~}|||}~~~~}|||}|zz{||}}~~~~}|}~~~~}|||}~~}~~}}}~~}||}}}}|{z{|~~~~~~~~~~~~}}}}}}~~|{||~~~~}}~~}||{{{}}|{{|~~}}}}|||}}}}~~}}}~~{{{|||{{{|}~~~~~}}|}}~}{zyyz|~~~~|{{{|}~}|{{{{{zzz{|}}||}~}|}~~|{{{||}}~~~~~~~||||}~~~~~~~~~~~~~}}|||||~~}|{{{}~~~~~~~~~|{zz{|~}||{{||}~~~~~~~~~~~~~~}|{|}~}|}~~~~}}}~~}}}}|{zzzz{|~~~~~~}||}~~}{zzy{}~}}}||||}}}}}~~}}|}}~}{{{{{{|}}~~}|}~}~}||}}}}}~~~~}}}}}}}|zzz|}}}}}~~~~~~~~~}|{{}~~}|||}~~~}}}~~}}|||}~~|{{{}~~~~}}|{{|}~~}}}~~}|||{{{{{|}~~~}||||}~~~~|{zyz|~~}~~~~~||{{}~~|{{{{{zyyz{|}||}~}}}~}|||{zz{}~~~~~|||}~~~~}}~~}}~~}{zz|}~~}}||}~~}|}~~|{{z|~~}||||||}}}}}~~~~~~~~}|{|}~~}}}}}}~}}~~~~~}|{{zzz{}~}}}}~}|{|}~~}||{z{}~}|{{|}}}||}}~~}}}}}~~~}|{zzzz|}~~}}}~~~~}}}}|||}~~~~~~}}{zzz{|||}}}~~}}}~~}{z{|~~}}||}~~}}}}}~~}}}}~~~}|{{|}~~~~~}|{{{|~~}}~~~}||{{{{||~~}|{{{}~~~~~}|{z{|~}}}~~}}~}|||~}|{||{zyyyz{}~}||}~~~~~~~~}}}|zzz{|~~~}|}}~~~}}|}~~}|}~~|zzz|}~~~~}}}~~~}||}~~}|{|~~}|||||||||||}~~~}}~~~}|||}~~~~~}}|}~~~~~~~~~}|{{{{{}~~}|||}~~}|{|}}}}}|{{|~~~~|{{|}}||||}~~~}}}~~~~~~~~~}|{zyyz{}~}}~~~~~}||||}~~~}|{zzzz{{{|}}~~}}~~}}}~~|zz{|}~~~~}}~~}}}}}~~~}~~~~~}||||}}~~~~~}|{{{|~~~~~~|{zz{|}~~}|{{{|}~~~~~~~}|||}~~~~|||}~~}|}~}}~~~}||||{zyyyz|~}}}~~~~}}}}~~}|zzzz|~~}}}~~}}|}}~~~~~}|||~~|zzz{||}}~~~~}~~~|||}~~~~~~}}~~}}|}}|||{z{|}~~~}}}~~~~}}}}}}~~~~}||}~~~~~~~}||{{|~~|{{{}~}||||||}}}}}~~~~~|{{|||||||}~~~~~~~}}}}}~~~~~}{zyyz{~~~~~~}||||}~}|{zzzzzzz{|}~}|}~~}}}~~|{z{{|}}~~~~~}|}}~~~~~~~~~~}||||||}~~}||{{|~~~~~~}{{z{|}~}|{{{||}~~~~~~~~~~~}}~~~~~|||}~~}}}~~~~~~}}}}|{zzzyz{}~~}~~~~~}||}~~~}{zzz{}~~}}}}|||}}}}~~~}||}~}{zzzz{{|}~~~}}~~~}||}}~~~~~~~~}}}}}||{zzz|}~}}}}~~~~~~}}||||}~~}||}~~~~~~}}||||~~}{{{|}~}}|||{||}}~~~~~}|{|||||||}~~~~~~}||||}~~~~~}{zyyz|~~~~}|||}~~}|{{{zzyyyz|}~}||}~}}}}~~}{{{{{{|}~~}|}~~~}}~~}~~~}|{zz{}~~~}}|||}~~~}~~~|{{{|~~}||{{{||}}}}~~~~~}}~~~~~}|||}~~}}}}}~~~~~}}}}|{{zzz{}~~}}}}}~}|{{|}~~}|{zz|~~}||||}}}}}}~~}}||}~~~}|{{zyzz|}~~}}}~~~}}}}}}~~~~~~}}|{zyyz{||}}}~~~~~~~}|{{|}~~~}}}}~~~~~~~~}}}}~~~~|{{{|}~~~}}}|{{{|}~~~~~}||{{{||}}~~}|{{{|~~~}~~~~}|{zz{}~}}}~~~~~~}|}~~}|{{{{zyyyz{}}}|}~~}}}~~~~}||{zzz{}~~}}}~~~~}}}~~}}}~~}{zyz{}}~}}}|}}~}}}~~}|||}~~}}|||||||||}}~~~}}}}~~~}||}}~~~~}}}}~~~~~~~}||{{{{|}~~}|||}~~}|{||}}}}|{{|}~||{|}}}}|}}~~}}}}}}~~~}||{zyyz{}~}}~~~~~}}|}}~~~~~}}|zyyzz{{|}}~~~~~}}}~~|{zz{}~~~~}}}~~~~~~~~}}~~~~}|||||}}~~~~}}|{{{|}~~~~~}|{{{||}~~}|{{{{}~~}}~~~~}}||{|}~~}|}}~~}}~~~}~~}||||{zyyyy{}~}}}}~~~~}}}}}}}}|{zzz{}~~}}~~~~}}}}~~~~}||}~}{zyzz{|}}}}}~~~~}|}~~~~}}}~~~}}||||{{{{|}~~~}}}}~~~}}||}}~~~~~~}}|}~~~~}||||||~~}|{{|}~~||{|||}}}}}}}~|{{|}}}|}}}~~~}}}}}}}~~~~~}|{zyyz{}~~~~~}|||}~~~}|{zzzzyzz{|}~}}}~~}}}~~|{zz{|}}~~~~~}}~~~~~~~~~}||{{|||}~~}||{{|}~}|{{{|}~}|{{{{|}~~~}}~~}}}}}}}~~~~}||}~~}}}~~}||||{{zzyz{}~~}}}}~~~~}}}|}}}}|{zzz{}~~~}~}}}}}}}~~~~}||}~~|{zyzz{{|}~~~~~~}}}}~~~~~~~~}}}}||{zzz{|}~}}}}~~~~~}}|||}}~~~~}}|}~~}}||||}~~}|{{|}~~}|||||||}}}~~~}|{|||||}}}~~~~~}}|||}~~~}}~~}|{zyyz|~~~~~~}}|}~~~}|||||}~~~~~~}~~~~~~~~~}}}}~~~~~}}}}~~~~~}}}~~|{zyz{||}}~~~}}}~~}}|}}~~||{{zyyyy{}~}}||}~~~}|||}}}}|{{|}}||||||}}~~}}||{|}~~~~~~}}}}}~~~~~~}}}}}~~~~~}}}}~~~~~}}}~~|zyyz{||}}~~~}|}~~~}|}}~~|||{zyyyyz}~}}}|}~~~}|||}~}}|{{|~~}||||||}}~~}}|{{|}~~~~~}}}}}~~~~~~~}}~~}~~~}}}}~~~~~~~}||}}{yzz{{|}}}~~}|}~~}|}}~~||||{zyyyz}~~}}|}}~~}||}~~}}|{{|~}|||||}}}~}||{{|}~~~~~~}}}}}~~~~~~~~}}~~}}~~}}}}~~~~~~}||}~~|zyz{{|}}}}~}|}~~}}}~~||||{zyyyz}~~}||}}~~}||}~~}}|{{|~}}}}||}}}~~}|{{|}}~~~~}}}}}~~~~~~~~}}~~}}~}}}}~~~~~~~~~|{|}~~{zz{|||}}}~}|}}~~}}~~~||}}{zzyyz}~~~}||}~~}||}~}}}{{|~}}}}||}}}~}||{{|}~~~~}}~}}~}}~~~~}}~~}}~}}}}~~~~~~~~~~~~}{{|~}zz{||{}}}}~~||}}~}}~~~~|}}}{zzyyz~~~|||}~~~}||~}~}{{|~}}~~|}}}}~~~}|{{|}~~~}~}}~}}~~~~~~}~~~}~~~}}}~~~~~~~~~~|{{}~~|zz{|||}}}}}||}~~}~~~~}|}~}{zzyy{}|||}~~~}|}~~}~}{{}~~~~~~~~|}}}}~}}|{{|}~~~}}~}}~~||~~~~~~~}~~~}}~~}}~~~~~~~~~~~}{{|}~~{z{||{|}}}~~}|}}~~~}~~~~~~~|}~~|z{zyz~~~|||}}~~}|}~~~}|{}~~~~~~~|}~}}~}}|z{|}~~}~~}}~||~~~~~~~}~~}}~~}}~~~~~~~~}~~~|z{}~~~|z{|}|{}}}}}|}}~~}~~~~~~}}~~}z{zyy|~~}|||}~~~||~~~~|{}~~~~~|}~}}~~}|z{||~~}~~}}~{|~~~~~~~}~~}~~~~}}~~~~~}~~{z{}~~|z{|}{|~}}~~}|}}~~~~~~~~~}~~|{{zyz~~~~||||}~~}|}~~~|{~~~~~~~|~~}}~}}|z{|}~}~~}}~}{|~~~~~~~~~~}~~~~}}~~~~~~~}~~~|zz|}~~}{{|}{{}}}}~}|}}~~~~~~~~~}}~~{{{yy{~}|||}~}~||~~~}||~~~}|~~}~~}}|z{|}~}~~}}~}{|~~~~~~~~~~|}}}~~~~~~}~~~|zz|}~}{{|}|{}}}}~~|}}}~~~~~~~~~}~|{{zyz}~~~}|||}}}~|}~~~}|}~}~}}~~}~~~}}{{||}~~}~~}}~|{|~~~~~~~~}|~~~}}~~~~~}~~{zz|}~~}{||}{{}}}}~~}|}}~~~~~~~~~}}~~{|{zy{~~}||||}}~}|~~~~||~~}}~~}~~~}|z||}~}~~}}~|{|~~~~~~~}|~~}}~~~~}~~~{zz|}~~}{||}{{}}}~~~}|}}~~~~~~~~~}~}{|{yy}~~~}|||}}}~|}~~~}|~~}~|~~}}~~}}{{||~~}~~}}}{{}~}~~~~~~~|}}}~~~~~~~~~{zz|}~~||||}z{}}}~}~}}}}~~~~~~~~~}~|{|zyz~~~}}|||}}}}|~~~~}|~~~|~~}~~~}|{||}~~}~~}}|{|}}}~~~~}|~~}}~~~~~}~~}{z{|}~|||}|z{}}}~}~}}}}~~~~~~~~~}~}{|{yy}~}~}||||}}~|}~}}|~~}~|}~~}~~}{{||~~~~~~}}~~{{|~~}~~~~~||~~}}~~~~~}~~}zz{|}~|||}|z{}}}~}~}}}}~~~~~~~~}~}{|{zy|~}}||||}|~|}~}~|}}~}}~~}~~~}{||}~}}~~}}}{{}~}}~~~~~|}~}~~~~~~~~|zz{|~~||}}{z|}|}}~~}}}}~~~~~~~~~}~}||{yz}}~~}|||||}~|}~~}}|~~}~|}~}}~~~||||~}~~~~}}~|{|~}}}~~~~~||~~~}}~~~~~~~}{z{|}~||}}|z{|||}~}}}}~~~~~~~~~~}~}||{yz}}~~}|||||}~|}~~}~}}~}~|}~}~~~}|||}~~}}~~}}}{|}~~}}~~~~}|}~~}~~~~~~~~{zz{|~~}|}}|zz|||~~~~}}}~~~~~~~~~}~}||{yz~~}~~}|||||}}|}~}~~}}~~}|~}}~~~}|||}~~}}}~}}}}{|}~}}}~~~||~~~}}~~~~~~}{z{|}~}}}}{z{|||}~}}}}~~~~~~~}|||zyz~~}~~}|||||}}|}~}~~}}~~~|}}}~~~}|||}~~}}}~~}}~{|}~~}}~~~~~}|}~~}~~~~~~|zz{|~~~~|}}|zz{||}~~~~~}~~~~~~~}~~|||zy{~~}~~}|||||}}|}~}}~}}~~~|}}}~~~}|||}~~}}~~~~}}~||}~~}}~~~~~~||~~~}~~~~~~}{z{{}~}}}}{yz{{|~~~~~~}~~~~~~~}~~||{zz|~}~~}|||||}}}~~~}~~~}~~~}|}}}~~~}|}|}~~}}}~}}}~||}~}|}~~~~~}{|~~~}~~~~|{z{|~~~}}}|zz{{{}~~~~~~~~~~~~~}~~|||zz{~~}~~}|{|{}~}}}~~}}~~}~~~}}}}}~~~}|}|}~~|}~~~}|}~||}~}|}~~~~~}{}~~~}}~~~~~|{{{|~~}}}}{yz{{|~~~~~~~~~~~~~~~~}~}||{z{}}~~}||{{|~}}}~~}}~~}~~~}}}}}~~~}|}|}~}|}~~}|}}||}~}|}~~~~~~|{}~~~}}~~~~}{{{{}~}}}}{yz{{|~~~~~~~~~~~~~~~~|||{z{~~}~~}|{{{}~}}}~~}}~~}~~}}}}}~~}}}|}}}|}~~~}|}}||}~||}~~~~~|{}~~~~}}~~~~|{{{|~~}}}}{yzz{{~~~~~~~~~~~~~~~~}~}||{zz}~}~}}|{{|}~~}}~~}}~~~~~~}}}}}~~}}}}}}}|}~~~}|~~|}}}||}~~~~~|{}~~~~}~~~~|{{{|~~~}}}|zzzz{}~~~~~~~~~~~~~~~~|||{z|~~}~~}||{{|~}}}~~|}~~~~~}}}}~~~}}}}}}||}~~~}|~}}}~}||}~~~~~~||}~~~~}~~~~~|{{{|~~~}}}}{yzzz{~~~~~~~~~~~~~~~}|||{{}~~}~~}|{{{}~~}}~~}|}~~~~~}}}|}~~}}}}}}|{|~~~~}|~~}}}}||}~~~~~~~||}~~~~~}~~~~~|{{{|~~~}}~}{yzzz{~~~~~~~~~~~~~~}}|{z|~~~~~}|{{|}~}}}~}|}~~~~~}}}}~~}}}}}}}||~~~~}|}}}}}|||~~~~~~~}{|~~~~~~~~~~~|{{{|~~~}}~}{yzzz|~~~~~~~~~~~~|}|{{|~~~~~~}||{{|~~}}}~}|}~~~}}}|}~~}}}}}}|{|~~~~}|~~}}~}||}~~~~~~~||}~~~~~~~~~~|{{{|~~~}~~}{zzzz|~~~~~~~~~~~~~}}|{{|~~~~~}|{{|}~}}}~}|}}~~~}}|}~~~}~}}}|{{}~~}}}~}}}|||}~~~~~~}||}~~}~~~~~~~||||}~~~}~~}{zzzz|~~~~~~~~~~~~~~}}|{{|~~~~~~}|{{{}~~}}~}||}}~}}}|}~~~~~}}}|{|~~~~}}~~}~}|{|~~~~~~~}||}~}}~~~~~~}||||}~~~}~~|zzzzz|~~~~~~~~~~~~~~}}}|{{}~~~~~~~}|{{|}~}}}~}||}}~~~}}||~~~~~}}}|{{|~~~}}}~~~}|{|}~~~~~~~~||}}}}~~~~~~~||||}}~~}~~}{zzyz|~~~~~~~~~~~~~~}}}||{}~~~~~~~}|{{|}~~}}}~}||}}~~~}}|}~~~~}}}|{{}~~~}}~~~~}|{|}~~~~~~}||}}}}~~~~~}||||}~~~}~~}{zzyz{~~~~~~~~~~~~~~~}}}||{}~~~~~~~}|{{|}~~}}}}||}}}~~~}}|}~~~~~}}}{{|}~~~}}~~~|{{|}~~~~~~~|||}}}~~~~~~~~}||||}~~~}~~}{zzyz|~~~~~~~~~~~~}}}|||}~~~~~~~}|{{|}~~}}}}|||}}~~~}}|}~~~~}}}|{{}~~~~}}~~~~}|{|}~~~~~~}||}}}}~~~~~~~}|||}}~~}~~}|zzyyz}~~~~~~~~~~~~}}}|||~~~~~~~}|{{|}~~}}}}|||}}~~~}}|}~~~~~}}|{{|~~~~~}}~~~}|{|}~~~~~~~||}}}}~~~~~~~}||||}}~~}~~}{zzyz{~~~~~~~~~~~~~~}}}||}~~~~~~~~}|{{|}~~}}~}|||}}~}}|}~~~~~~}}{z{}~~~~~}}~~~}{{|}~~~~~~}||}}|}~~~~~~}}||}}}~~}~~~|{zzyz|~~~~~~~~~~~~}}}||}~~~~~~~}|{{||}~~~}}~}|||}}~~~}|}~~~~~~}}{z{}~~~~~}}~~}{{|}~~~~~~}||}}|}~~~~~~}|||}}~~~}~~}{zzyy{~~~~~~~~~~~~~}}}}||~~~~~~}|{||}~~~}}~}|||}}~~~~}}}~~~~~~}}{z{}~~~~~}}~}{{|}~~~~~~}||}||}~~~~~}}||}}}~~~}}~~}{zzyz|~~~~~~~~~~~}}}||}~~~~~~}|{{||}~~}}}~}|||}}~~}}}~~~~~}|{z{}~~~~~}}~}{{|}~~~~}~}||}||}~~~~~~}}||}}}~~~}~~}|{zyy{~~~~~~~}~~~~~}}}}||~~~~~~~}|{{|}}~~}}}}}|||}~~~}}~~~~~~}|{z{}}~~~~}~~}{{|}~~~~}~}||}||}~~~~~~}}||}}}~~~}~~~|{zzyz}~~~~~}~~~~~}}}}|}~~~~~~}|{||}~~}}}~}||||}~~}}}~~~~}|{z{}}}~~~}~~|{{|}~~~}}~~}|||||}~~~~~}}|}}}}~~}}~~}|{zzz{~~~~~~~}~~~~}}}}}}~~~~~~~}|||||}~}}}}}}||||}~}}~~~~}|{z{|}}~~~~~~}{{||~~~}}~}}}|||}}~~~~}}}}}}}~~}}~~}|{zzz{~~~~~~~}~~~~}}}}}}~~~~~}|||||}}}}}~}||||}~~}}}~~~~~}{z{|}}}~~~~}|{{|}~~}~}}}|||}}~~~~~~}}}}}}~~}}~~}|{zzz{~~~~~~}}~~~~~}}}}}~~~~~}||||}}}}}}}}||||}~}}~~~~~|{z{|}}}~~~~~}{{{|~~~}}~}}}|||}}~~}}|}}||}}|}}}}}}}|{{{{|||}}}~~~~~~~~}}}|}~~}|{{|}~~~}}|}}~}}}~~}}}}||{{|}~~~~~}}~~~~~~}|||}}}}}}}}~~}}~~~~~~~}}|||{{{zzzzz{|{{}~}}}~~~}~~~~~~~~~~~~||}}}||{{z{}}}}}||}~~}|}~~~~~~~~~~}}~~~~~}}~~~~~~~}}~~}|}}}|||||||}~~}}}|||{yxxyyyzzzz{~~~}}}}}~~~~~~~~}}}|{{z{{zz{}}}}||||}~~~~}}}||{{z{{|}}~~~}}|}}|{yyyz{{||||}~~~~~}}}}|{{{{zyyxxxxxxyxxxz|~~}~~}}~~~~~~~}}~~~~}|||||{zz{{|~~}}~~~~~}|{zzz{{||{{|}~~}~~}}}}~~}}}}}}}~~~~~}}{{{{{zzyzzz{{{{|}~~~~}|{zzzyxxxxyzzzz|}|||||{{||}}}}|||{{{|||{{{{zzzzyyyyyyy{{|}|||}~~~~}}}~}|{zzyyyyyyyxyyxxyz{{zzzzzz{|}}}~~}~}}|}}}}}}||{{|}~}}}|}~~~~}}||{zzzz{{||}|{{|~~~}}}}}}~~~~~}~~~~~~}|||||{zz{{||||{zz{|}|{{|}~~~~~~}}}|{zzzyyyyzywwxz{{zzzzz{|~~}}}||||}}|{{{||{{{{zzz{zzyyzz{{z{{}~~}||{{{{zzz{{{zzzzzzzzyxxyz|||{||{{|}~~~~}}}}}~~}|||}~~}}~~}}}~}||}}~}}~~~~~~~~}}|||{{{zzzzzzyyz{|}~}~~~~~~~}}}~~}}}}|{{|{{{zzyyyzz{{z{|~~~}}}~~~~}}{{{zzzzyyxxyz{||{{{{{{|}}|{{z{{{|}}}}|}}}{{{|{{{{zzz{{||{{|}~}|{{{{{{{{{zyxyyyyxyxxxy{}}}}||||}~~~~}}~~~~~}}~}}{{{|||||{{{|}~}~~~~~~~}}}}||{{{{||{{zzz{}~~~}||||}~~~}||}~~}~~}|{{{{{||{{zz{||{{|}~~~~}}}}}|{|||{yyyz{zzzzyyz|}}}|{|{{|}~~}{{{|||~~}|{{{{{zyzzz{{{|{{|}}||}}|||{{{z{{{{zyyz{{zyyyxyz{|||{{{{{|}~~~~~~}}||{|}||||||||}}}||~~}}||{||{||||{{|}}}|{{{{|}~~~}}}}}}~~~}}}~~~~~}||{{zz|||||||{{||||{}~~}}|||}||}}|{zz{{|zyyxxyz{||{{{|||}~~}||||}}}~~}}}||{zzz{{{{{{zz{{||{|~~}|{{zyyzz{|||{zz{||{zzyyz{|}}||{|||}}~~}}|}~~~~~~~}}|||}}}}}}||}}}~}~~}}}|{{||||}}|z{{|}|{{zz{|}~}}~}}~~~}}~~~}}|{{zz{{z{{{{z{{{||~~}||}||||}}}}|z{|||{zzzyz{|}}|{|||{||}}{{{|}}~~~}}|{{z{{{{||||{{||}}~~}|{{{zyzz{{|{zyz{{{zyzzzz{}}}}||}}|}}~~}}~~~~}|{{{{||||}}}|}}~~~~~}|||{{|||}}}|{{}}~}{||{{|}~~}||}}||}~~~~~~}|{{zz{|{{|||zz{|}}~~}|||{zz{{|}}|zz{}}}{{{{{{|}}}|{|}|||}}}|||}~~~~}}|{zzzyz{{{|}}|{||}~~~}|{{{zyz{{|}|{zz||}|z{{zzz{|}||{}}||}~~~~~}|||{{||||}~}|||}~~~|{{{{zy{{|}~}|{}~~~}}}}}|}~~~}}~~||}~~~~~}|{{{{z{|{|}}|{{{|~~}|||{zz{||}}|{{|}}}|{{{{{z|}}||}~}||}}~~~}}~~~~~}|{zzzz{{{{}|{zz{|~~~~}|{{zzzyy{||}}|{{}}~~|{|||{{|}}||}~}|}}~~~~~~}|||{||||~~~|{|}~~}}||{{zz{||}~}|{|}~~}||}}||}~~~}}~~}}}~~~~}|{{zzzz{z|}|{zz{}~~}}|||{{zz||}~~}||}~~}|}}|{{|}}||}}||||~~~}~~}}|{{{zz{{|}}|{z{}~~~||{{zzzyyz{|}}|{{|}~}|}~}|{|}}}}~~}}}}~~~~}|{{{{{{{|~~|{|~~~~}}}||{{zz{|}~~}}|}~~}}~}|{|}}}}~~}|}}~~}}|{{{{zz{|}}|{z{}~~~||||{zzzz{|}~~}}|~~}~~}|{|}}}}~~}||}~~~~~}||{zz{{z{{|~~|{z|~~~~~~~}|{||zzzyz{|}~~}}|}~~}}~}{z{}}}}~~}}~~}}|{{||{{|}~~}{z|~~~~~~~~~~~||||{zzzz{|}~~~}~~~~|{|}}}~~}}}~~~~}}|{{||zz{|}~}{z{}~}~~~~}||}|{z{z{|}}~~~}|~}}~~|{z|}}}~~}}~~~~~|||{z{|{zz|}}}|zz|}}~~~~~~}}}|{{||zzzzz{|}~~~}~~~|z{|}}~~~}~~~}}}||}}{{|}~~~|{|~~}}}~~}~~~~~~~|||}|{z{{{{}}~~}~|{|}~~~~}~~}|}|{{|{zz{|}}|{z|}|}}~~~~~}}}||}}|{z{{{|}}~~}~~~}{z{}}}}~}}~~~~~~~}|||{{|{zz|}}}|{z|}|}}}}~~~}}|||{{||{zz{zz{|}~~~}{z|}~~~~~|}}|||}|{|}~~}}|{}|{|}~|}~}}}}}}|}}}{{{|{{}}~~~|{{}~~~~~}|}|||||z{|}}}|{{|~|||}}}~}}}}}||}}{z{|{{|}~~~~|zz{~~~~~~~~~~~~}|||||||{{|}}}}|{|}||}~}~}|||||||}}{z{{{{{}}}{z{~~~}|}}}}}}||}}}}}}|}|{{|~~}}~~}|}}||||}}}}}|{{||{|}~}{z{~~~~~~}||}}}}||{|}}}}}|}~~|{{|~~~~}|~}||||}}}}}|{{|{{|}~~~|zy{~~}}~~}|||}}|||{{||||}}|~}{{}~~~}|~~|{{{{|}}}|{{{|{{|}~~}|zy{~~~~~~}}}~~}}}||}}}}~~}~~~|zz|~~||}~~|{}|{{{|}~~}}|{|||||}~}{zz~~}~}}}||}~}}}}|||||}}~~~~}~~}{z{}~}|}~~|}}{{{{}~~~}}|||||||~~}{zy|~}}}}}}|}~~}}}}||||}}~~~~~|{{}~}||~}|~~|{{z{}~}|||{{||||}~}|{yz~}}}}}}||~~}}~}}||}~~~}}}~}{z|}~~|||~~||~}{{{{}~~}}}||||||~~}|{y{~}}|}}|}~~}}~}||{|}~~~}|}~|zz|}~~{{}~~||~}{{{{}}}}}||||||~}}|zy|~}}||}}|}~}}~}||||~~}}~~|{{}~}{|}~~|}|{zz{}~}}}|{||||}~}|{zz}~}}|}}}}~}}~~}|{|}~}|}~}{z{|~~{{|}~~~}|}}{{z{|~~}}}|||}||}}|zz{}}||}}}~~}~~}|{{|~~~~}||}~|{{|}~~|{{}~~~~|}|{{{}~~~}||}||~~}|{z{}}||{|}}}}~~~|{{|}}||}~}{{|}~~|{{}~~~}}~|{z{|~}}~}|||||~~||{z{}~}||}~~~~~~|{{|~}|||}}{{|}}}|z{|}~~~}|~}{{{|~~}~}||}}|~~||{{{}}|{{|}~~}~~}{z{|~|{||}}|{|}}~}{z{}~~~~}}~}{{|~~~~}||}}}}|{z{{~}{{{|~~~~~}{z{|~~~||||}}|||}~}|{z{}}~~~}}~|{{|}~}~}||}}}}{{{{|~}{{{}~~}{z{}~~|{{|}}||||}}|{z{}}~~~~}~~}|{|~~~~~||}}~~|{{{|}~{z{|}~~~}{zz|~~~~}{{||}}|||}}|{{{|}~~~}~~~|{{}~~~~||}}}|{z{|}~|zz{}~|zz{}~}~~|{{|}~}|||~~|{{{|}}~~~}}~~~~}|{}~~~~}||}}~}{z{||~}{z{|~~}{z{|}}~}{{{|}~|||}~}{{{{||}~~}~~}~~}||}~~~}||}~}{{{|}}zz{}~~|{z{}~}}~}{{{|~~}||}|{{{||}}~~~~~~~}||~~~~}||}~|z{||}|zz{}~}|zz{}~}~~|{{|}~~|||}~|{{{||}}~~~~}}~}~~}|}~~~}||~~{z{|}~~{z{|~}{zz|}}~~|{{|}~}|{|~|{{{{||}}~~~~}~~}~~}}~~~}}|}}z{|}~|zz{}~}|zz{~~|}~|{{{|~~|{|~}{{{|||}}~~}}~}~}}~~~}|}~}zz{|}}zz{}}|zz{}~}}~}|{{|~}||~~{{{||||}~~}}~}}~}~~~}|}~}{z|}~}{z{}~|{z{~~~}}~~~}|{{|~~}||}}{z{||||}~~~}|}~}~~~~~}}}{z|}~|{{|}}{zz{~~~~~|}~~}|{{|~~}||~}{{|||||}~~~}}}}}~~~~~}}~|z{|~|z{|~~|{zz|~~~~}~~~}|{{}}|}~|{{|}|||}~~~}}}}}~~}~~}~|{{}~|{{|~~|{zz|~}~~}}~~~}|{{}~~}|}~~|{{|}|{|}~}}||}}}~~~~~~}{{}~}{{|~|{zz|~|}~~~}}~~~}{{|~~|}~~}{{|}|||}~~}}}}~~}~~~~~~}{{|~}|{|}}{zz{~}}~~~}~~~~~|{|}~}}~~}|{|}}|||}~~}}|}}~}}~~~~~~~|{{}~}||}|{z{}~|}~~~~~}~~}|{|~~~}}~~||{|||{{|}~}||}}~}~~~~~~|||~~|||}~{zz{~}|}~~~}~~}~~}||}~~~}}~}|||}}|||}~}|||}~~}~~~~~~}|||~~}|||~}{zz|}|}~~~~~~}~~}||}~~~}~~}|||}}|||}~~}||}}~}}~~~~~~}||}~~}||}|zz{}~||}~~~~~}}~~|||}~~~~}||||}||{|}~|||}}~}~~~~~}||}~}}|}~}{z{}||}}}~~~}~~}||}~~~~}}}|}}}|||}~~|||}}~~~~~~~}||}~~}}}}}~~{z{|}||}}~~~~~~}}}~~~~}}}}}}}|||}~~|{|}}~~~~~~~}}}}~~}}~}}~~{z{}}||}}~~~}~~~}}}}~~}|}}}}||{||~~|{|}}~~~~~~~}|}}~~}}~~~}{z{~}||}}~~~~~~~~~~}}}}~~}}}}}}||{|}~|{|}~~~~~}||}~~}|}~~~|{z|~}||}~~}~~~~~~~}}}}~~}}}~~}||{|}~|{|}}~~~~~~}||}~}|}~}{z|~}|||~}}}~~~~~~~}}}~~~}}}~~}||{|}~|{|}~~~~~~~}||}~}|}~}{z|~}|{|}}|}}~~~~~~}}}}~~}}}~~}||||}~~}||}~~~~~~~}||}~}||~}{{{~~}|{}~~}|}~~~~~}}}~~~}}}~~}||||}~~~}|||}~~~~~~~~|{|~~||}~}{{}~}|{|}}||}~~~~~~}|}~}}}~~~}|||{|~~~~~}||}}~~~}~~~|{}~~||}~|{{~~}|{|}}{|}~~~~~}}|~~~}}}~~}||||}~~~~}||}~~~~}~~}{{}}||~}|{|~~}|{|~~|{|}~~~}}}~~}}}~~}||||}~~~~}|||}~~~}}~|{|~||}~|{|~~~~}{{}}{{|~~~~~}|}~}}}}~~|||||~~~~}}||}}~~~}~~}{{}}||~}|{}~~~}|{|~}{{{}~~~}|}~}}}}~~}||||~~}~}}||}}~~}}}~~{{|}||}}||}~~~}|{|~~|z{}~~~}}~~}|}}~~}||||~~}}}}}||}~~}}}~~{{|}||~}||}~}~}|{|~}{z{|~~|}~~}|}}~}||||~~}}}}}||}~~}}}~~{{}}||~~|||~~}}~}|{|~~|{z{}~}|}}||}~~}|||}}}}}}}}}}~~}}}|{{}||}~||}~}}~}|{|~|{z{}~}}}}||}~~}|||}}||}}}}}~~}|}|{{~}|}}||~}}}}}|{|}|zz{}~}}}~|||}~~}|||}}|||}}}}}~}|}}{{}}|}~||~}}}}}|||~~|zzz|~~}}}||}~~|||}~|||}}}}}~~}|}~}{{}}|}~~||}}}}}}}||}~|{zz{}}}~}|||}~}||}~~}|||}}}}~}||}|{|~~}}}}|}~}}}}}}|}~}{zzz|~~}~~|{|}~~|||}~|{||}}}}~~}|}~~|{}~}}~~}|}}||}}}||}|{zzz|~~}~~|{|}~|||~}|{||}}}}~}|}~}||}~}}~||~}||}}}||}~|{zz{}~~}}|{|~~}||}~~}{{|}}}}~~~~||}~|||~~}}~~}|}~||}}}}}}~}{zzz{}~~~~|{|}~~}||~~|{{|}}}~~~~}|}~}||}~}~}||~|||}}}}}~}{zzz{|~~~~}{||~~}||}~}{{|}}~~~~}~}|}~}||}~~~}||~~}||}}}}}~~|zzz{|~~~}|{|~~}||}~}{{|}}~~~~}}~}|}~}|}~~~~~}|}~|||}}}}~~~{zzz{|}~~}|{|~~}||~~}{{|}}~~~}}~}|}~}}}~~~~}||}}||||}}}~}{zzz{|~~~|{|}~~}}}~}{{|}~~~}}~}}}~}}~~~~~}||~}|||}}}~|{zzz{|~~~|{|}~~~}}}~}{{|}~}||~}}}}}}~~~~|||~}||||}}~|zzzz{|}~~~|||}~~~}}~~}{{|}~}||~}}~}}}~~~~|||~}||||}}~}{zzz{{}~~}||}~~}}~~}|{|}~~||}~}}}|}~~~~~|{|}~||||}}~~{zzzz{|}~~|||}~~}~~~|{{|}~}||~~}}~~}}~~~}|{|}||||}~}{zzzz{|~~}||}~~~}~~~}|{|}~~||}~}~}|}~~~~~}{{}~}||||}~~|zzzz{{|~~~}||}~~~~~~~}|{|}~~}||}~~~~}|}~~~|{|~~|||||}~~|zzzzz{}~~~}||}~~~~~~~~}|||}~}||~~~~}|}~~~~}{{}}||||}~~}{zzzz{{}~~}||}~~~~~~~~~}|||}~~}||}~~~~||}~~~}{{|~}||||}}}{zzzzz{|~~~~}|}~~~~~~~~~~~}||}~}|||~~~~||}~~~}|{|~~|||||}}|zzzzz{|}~~~}}}~~~~~~~~~~}}|||~}|||~~~||}~~~}{{|~~}|||}~~}|zzzzz{|}~~~}}}~~~~~~~~~~~}}|}~~}|||~}||}~~~~}{{|}||||}~~|{zzzzz{|}~~~~}}}~~~~~~~~~~}}}}}~~}||}~~~~|||~~}}~~~|{{}}||||}~~}|{zzzz{{|}~~~}}~~~~~~~~~~~}}}}}~~|||}~~~~||}~~}}~~|{{}}}|||}~}|{zzzzz{|}~~~~}}~~~~}~~~~~~}}}}~~~|||}~~~~|{|~}}}}~~|{{}~}|||}~~}|{zzzzz{{|}~~~~~~~~}}}~~~~~}}}}~~~}|||~~~~|{|~}||}~}{{|~}}||}~}|{{zzzz{{|}~~}~~~~~~}}~~~~~}}}~~~}|||}~~~~}{{}~}||~~|{{~~}|||}~||{{zzzz{{|}~~}}~~~~~}}}~~~~}}~~~~~~}|||}~~~|{|}}|||~}{{|~}}|}~}|{{zzzzz{{|}~}}~~~~}}}}~~~~}~~~~~|||}~~~~|{|~~}||}~}{{}}}||}~}|{{zzzz{{{|}~}~~~~~}}}}~~~~}}~~~~}|||}~~~~}{{}~|{|}~|{|~~}}|}}}|{{{zzzz{{{|~~}}~~~~}}}}~~~}}~~~~~|||}}~~}|{|~~|{{|~~|{|~~}}}}~|{{{zzzzz{{|~}}~~~~~}}}~~~~}~~~}|||}~~~~~|{|~~}{{|~}{|}~}}}}~~|{{{{zzzz{{|}}}~~~~}}}}~~}}~~~}|||}~~~~}|{|~~|{{|~~}||~~}}}}~|{{{{zzzz{{{}~}}~~~~}}}}~}}}~~~}||}}~~~}{{|~~|zz|~~|||~}}}~}|{z{{zzz{{{|~~}}~~}}}}~~~}~~~|||}}~~~~|{{}~}{z{|~}||}~}}}~}{zz{{zzz{{{|~~}}~~}}}}~~}}~~}|||}~~~~~|{|}~}{zz|~}||~~}}}~}{zz{{zzz{{{|~~}}~~}}}}~~~}}~~~}|||}~~~~~|{|}~}{zz|~~}||~~~}}~}{zzz{zzz{{{|}~}}~~}}}~~}}}~~}||}~~~~|{{}~}{zz{}~}}}}~~~~~|{zz{{zzz{{{}}}}~~}}}}~~}}~~}|||}~~~}|{|~~|{zz|~~}}}~~~}{zzz{zzz{{{|~~}}~~}}}~~}}}~~}}||}}~~}{{}~}{zz{|~}}}~~~~~}{zzz{zzz{{{|~~}}~~}}}~~}}~~~}}||}~~~~|{|}}{zz{}~}}}~~~~~~|{zzzzzzz{{{}~}}~~~}}~~~}}~~~}|||}~~~}|{|~~|zzz|~}}}~~~~}{zzzzzzz{{{|~~}}~~}}}~~}}~~~}}||}}~~|||}~|{zz{}}}}}~~~|zzzzz{{{{{|}~}~~~~}}~~~}~~~}}||}}~}||}~|{zz{|~}|}~~~|{zzzz{{{{{|}~}~~~}}~~~}}~~}}|}}}~~}||}~|{zz{|~}|}~~|{zzzz{{{{{|}~}~~~~}}~~~}~~~}}||}}}~~|||~~|zzz{}~}|}~}|zzzzz{{{{{|~~~}~~~}~~~}~~}}|}}}~~}||}~}{zzz{}~}|}~}{zzzzz{{{{|}~~~~~~~}~~~~~~}||}}}~~}||}~}{zzz{}~||}~}{zzzzz{{{{|}~~~~~~~~~~~~~}||}}}~~}||}~}{zzz{}~}|}~}|{zzzz{{{{|}~~~~~~~~~~~~~}||}}}~~~~}|}~}|zzz{|~~}|}~}|{zzzz{{{{{|~~~~~~~~~~~~}|||}}~~~~~~}|}~~}{zz{|}~||}~}|{zzzz{{{{|}~~~~~~~~~~~~}||}}}~~~~~~~~}}}~~|zzz{|}}||~~|{zzyzz{{{{|~~~~~~~~~~~~}||}}}~~~~~~~~}}}~}|zzz{|~~}|}}|{zzzz{{{{|}~~~~~~~~~~~}|||}}~~~~~~~~~}}}~~|{zz{|}~}||~~}|{zzzz{{{||}~~~~~~~~~~}||}}~~~~~~~~~}}}~~}{zzz{|~~}|}~}|{zzzz{{{||}~~~~~~~}|||}}~~~~~}}~~~}}~~}|zzz{|}~}|}~}|{zzzz{{|||}~~~~~~~}|||}}~~~~~}}~~~}}}~~}{zzz{|}~~}|}~}|{zzzzz{|||~~~~~~~}|||}~~~~~~}}}~~~}}}~~|{zz{|}~}}}~}||{zzzz{{||}~~~~~~}|||}~~~~~}}}}~~~}}~~}|{zz{|}~~}}}~}|{{zzzz{{|}}~~~~~~||||}~~~~~}}}}~~~}}~~}|{zz{|}~~~}}}~}|{{zzzz{{|}~~~~~~~}|||}~~~~~}}}}}~~}}~~}|{zz{|}~~~}}}~}|{{{zzz{{|}}~~~~~}|||}~~~~~}}||}~~}}~~}|{zz{|}}~~~}}}~}||{{zzzz{|}}~~~~~~}||||}~~~~}}|||}~~}}~}|{{z{{|}~~~}}~~}|{{{zzz{{|}~~~~~~~}|||}~~~~}}|||}~~}}~}|{zz{||}~~~}}}~~||{{{zzz{||}~~~~~~~}|||}~~~~}}|||}~~}}}~~}{{z{{|}}~~~}}~~}||{{zzz{{|}~~~~~~}}|||}~~~}}||||}~~}}~}|{zz{{|}}~~~}}}~~}|{{{zzz{{|}~~~~~~}|||}}~~~}}||||}~}}}~~|{zz{{|}}~~~~~}}~~}|{{{{zz{{|}~~~~~~~}|||}}~~~}}||||}~~}}}~~|{zz{{|}}}~~~~~}}~~}|{{{{{{{{|}~~}}~~~}}||}}~~~}}||||}~~}}}~~|{zz{{|}}}}~~~~}}}~~}|{{{{{{{{|}~~}}~~~}}||}}~~~}|||{|}~~}}~}|{zz{||}}}}~~~~}}}}||{{{{{{{|}~~}}}~~~}|||}}~~}}||{{|}~~}}}~}{zzz{||}}}}}~~}}}~~}|{{{{{{{{|}~~}}}~~}}}||}}~~}}||{{|}~~}}}~}{zz{{|}}}}}}}}}}}~~}|{{{{{{{{|}~~}}}~~}}}||}}~~}}||{{|}~~}|}~}{zzz{||}}}}}}}}}}~}|{{{{{{{{|}~~}|}~~~~}}}|}}~~}}||{{||~~}|}~}|zzz{||}}}}}}}}}}~~|{{{{{{{{||~~}||}~~}}}}}}}~~}||{{{|}~}}}~~}{zz{{|}}}}}}}||}}~~}|{{{{{{|||}~}||}}~~}}}}}}}}}}}||{{{|}}|}~~|{zz{||}}}}}||||}}~}|{{{{{{|||}~}||}~~}}}}}}}}}}}|{{{||~~}|}~}|zzz{||}}}}}||||}~}|{{{{{{|||}~~}||}~~~}}}}}}}}}}||{{{|}~~}}}~~|{zz{{|}}}}}|||||}~}|{{{{{||||}~}|||}~~}}}}}}}}}}}|{{{{|~~}|}~}{zzz{||}}}||||||}~}|{{{{{|||}}~}|||}~~}}}}}}}}}}}||{{{|}~}|}~}|{zz{||}}}|||{{||~}|{{{{{|||}}~}|||}~~}}}}}}}}}}}||{{{|}~}}}~}|{zz{|}}}}|||{{||~}|{{{{{|||}}~}|||}~~~}}}}}}}}}}||{{{{|}~}}}~~}{zzz{|}}}}||{{{|}~}|{{{{{||}}~~}|||}~~}}}}}}}}}}|||{{{||~~}}}~~|{zz{{|}}}|||{{{|}~}|{{{{||}}~~~}|||}~~}}}}}}}}}}|||{{{||~}}}}}|{zz{{|}}}||{{z{|}~}|{{{{||}}~~~||||}~~}}}}}}}}}}|||{{{||~~}}}~|{{z{{|}}}||{{zz{}~}|{{{{{|}}~~~}|{||}~~}}}}}}}}}}|||{{{||}~}}}~~|{{z{{||}}||{{zz{|~}|{{{{{|}}~~~}||||}~~~}}}}}}}}}||||{{{|}~}}}~~}|{{{{{|}}}||{zzz{}~}|{{{{||}~~~}|{||}~~}}}}}}}}}||||{{{||}~~}}}~~}|{{{{||}}||{{zzz|~~|{{{{{||}~~}||{||}~~}}}}}}}}}||||{{{||}~}}~}|{{{{{||}}||{{zz{|}|{{{{{|}}~~}||||}~~~}}}}}}}}}||||{{{{|}~}}}~~}|{{{{{||}||{{zzz{}~}|{{{{|}}~~|||||}~~}}}}}}}}}||||{{{||}~~}}~~}|{{{{{||}|||{zzz{}~}|{{{{||}~~~}||||}~~~}}}}}}}}}||||{{{||}~~}}~~}|{{{{{||}|||{zzzz|}|{{{{||}~~}||||}}~~}}}}}}}}}|||{{{{||}~~}}~~}|{{{{{||||||{zzzz|}|{{{{||}~~}||||}}~~}}}}}}}}}||{{{{|||}~~}~~~}|{{{{||||||{{zzz{}~}|{{{{|}~~}|||||}}~~~}}}}}}}}}||{{{{||}~~~~~~~}||{{{{||||||{zzzz{}~}|{{{{|}~~}|||||}}~~}}}}}}}}}}||{{{{||}~~~~~~}||{{{|||||||{zzzz{}~}|{{{||}~~}|||||}}~~}}}}}}}}}}|{{{{|||}~~~~~~~}||{{{|||||||{zzyz{}~}|{{{||}~~}|||||}}~~}}}}}}}}}||{{{{||}~~~~~~}||{{{|||||||{zzyz{}~}||{{{|}~~~}|||||}}~~}}}}}}}}}||{{{{||}}~~~~~~}|{{{|||||||{{zzyz{~~}|{{{||}~~}|||||}}~~}}}}}}}}}||{{{{||}}~~~~~}||{{{|||||||{{zyzz|~~}|{{{|}~~~}|||||}}~~~}}}}}}}}}||{{{|||}~~~~~~~}||{{{|||||||{zzyz{}~}||{{||}~~}|||||}}~~}}}}}}}}}||{{{{||}}~~~~~}|{{{|||||||{{zyzz|~~}||{{|}}~~}|||||}}~~~}}}}}}}}}||{{{|||}~~~~~~}|{{{|||||||{{zzyz{}~}|||||}}~~}}||||}}}~~}}}}}}}}}||{{{|||}~~~~~~~}|{{{|||||||{{zzyz{}~}|||||}}~~}}|||||}}~~}}}}}}}}}||{{{|||}~~~~~~}|{{{||||||{{{zzyz{}~}|||||}}~~}||||||}}~~}}}}}}}}}||{{{||}}~~~~~~}|{{{||||||{{{zzzz|~~~}|||||}~~}|||||}}}~~~}}}}}}}}|||{{{||}}~~~~~~}|{{{||||||{{{zzyz{|~~}}||||}}~~}}|||||}}}~~~}}}}}}}}|||{{{||}}~~~~~~}|{{{||||||{{{zzzz{}~}}||||}}~~}}|||||}}}~~~}}}}}}}}|||{{{||}~~~~~~}|{{{||||||{{{zzzz{|~~}}||||}}~}}|||||}}}~~}}}}}}}}}||{{{||}}~~~~~~~||{{{||||||{{zzzz{|~~~}}|||}}~~}|||||}}}~~~}}}}}}}}}|||{{||}}~~~~~~~}|{{{||||||{{zzzzz{}~}}|||}}~~~}}||||}}}}~~}}}}}}}}}|||{{||}}~~~~~~~}|{{{||||||{{zzzzz|}~}}|||}}~~}}||||}}}}~~~}}}}}}}}||||{{||}~~~~~~~}||{{|||}|||{{zzzz{|~~}}}||}}~~~}|||}}}}}~~~~}}}}}}}}|||{{||}}~~~~~~~}|{{{||||||{{zzzzz|}~}}}|}}}~~}}||}}}}}}~~~}}}}}}}}||||{|||}~~~~~~}|{{{||||||{{zzzzz{}~}}}|}}}~~}|||}}}}}~~}}}}}}}}||||||||}~~~~~~~~||{{|||}|||{zzzzz{}~~}}}}}}}~~}|||}}}}}~~~}}}}}}}}||||||||}}~~~~~~}||{{|||||||{zzzzz{}~}}}}}}~~~}||}}}}}}~~}}}}}}}}||||||||}~~~~~~~}|{{{||||||{{zzzz{|}~}}}}}}~~~}}||}}}}}}~~~}}}}}}}||||||||}}~~~~~~~~}|{{{||||||{{zzzz{|~~~}}}}}~~~}}|}}}}}}}~~~}}}}}}|||||||||}}~~~~~~~}|{{{||||||{{zzzz{}~~~}}}}}~~~}}}}}}}}}~~~~}}}}}||||||||||}}~~~~~~~~}|{{{||||||{{zzzz{}~~}}}}}~~~}}}}}}}}}~~~~~}}}}||||||||||}}~~~~~~~}|{{{||||||{{zzzz{|~~}}}}}~~~}}}}}}}}}~~~}}}}}|||||||||}}~~~~~~~~~}|{{{||||||{{zzzz|}~~}}}}~~~}}}}}}}}}~~~~~}}}}||||||||||}}~~~~~~~~}||{{{|||||{{zzzz{|~~~}}}}~~~}}}}}}}}~~~~~~}}}}||||||||||}}~~~~~~~~}||{{||||||{{zzzz{}~~}}}}~~~}}}}}}}}~~~~}}}}||||||||||}}~~~~~~~~}|{{{||||||{zzzz{|}~}}}}~~~}}}}}}}}~~~~~~}}}}|||||||||}}}~~~~~~~~}||{{||||||{{zzzz|}~}}}}~~~}}}}}}~~~~~~~}}}}|||||||||}}}~~~~~~~~~}||{{||||||{{zzzz{}~}}}}~~}}}}}}~~~~~~~}}}}|||||||||}}}~~~~~~~~~}||{{||||||{{zzzz|}~}}}}~~~}}}}}}~~~~~~~~}}}||||||||}}}}~~~~}}~~~}|||||||||{{{zzz{|~~}}}~~~~}}}}}~~~~~~~}}}}||||||||}}}~~~~~}}~~~}}|||||||||{{zzzz{}~~}}}~~~}}}}}~~~~~~~}}}}||||||||}}}~~~~~}}~~~}}|||||||||{{zzzz{}~}}~~~~}}}}}~~~~~~~~}}}||||||||}}}~~~~~~}}}~~}}|||||||||{{zzzz{}~~}}~~~~}}}}}~~~~~~~~}}}||||||||}}}~~~~~~}}}}~~~}|||||||||{{zzzz{|~~~}~~~~~}}}}~~~~~~~~~}}}|||||||}}}}~~~~~}}}~~~}}||||||||{{{zzz{|}~~}~~~~}}}}}~~~~~~~}}}||||||||}}}~~~~~~}}}}~~~}}||||||||{{{zzz{|~~~~~~~~~}}}}~~~~~~~~}}}|||||||}}}}~~~~~~}}}}~~~}||||||||{{{zzzz{}~~~~~~~~~~}}~~~~~~~~~~}}}|||||||}}}~~~~~~~}}}}}~~}}|||||||{{{{zzz{|}~~~~~~~~}}~~~~~~~~~}}|||||||}}}}~~~~~~}}}}}~~}}||||||||{{{zzz{|}~~~~~~~~~~~~~~~~~~~~}}}||||||}}}}~~~~~~~}}}}}~~~~}}|||||||{{{zzz{{}~~~~~~~~~~~~~~~~~~~~~}}}||||||}}}}~~~~~~~}}|}}~~~}}}|||||||{{{zzz{|}~~~~~~~~~~~~~~~~~~~}}}||||||}}}~~~~~~~}}||}}~~}}}||||||{{{{zzz{|~~~~~~~~~~~~~~~~~~~~}}}||||||}}}~~~~~~~~}}||}}~~}}}}|||||{{{zzz{{}~~~~~~~~~~~~~~~~~~~~~}}}||||||}}~~~~~~~~~}|||}}~~~}}}}}||||{{{zzz{|}~~~~~~~~~~~~~~~~~~~~}}}|||||}}}~~~~~~~~~}||||}~~~}}}}}}|||{{{zzz{|}~~~~~~~~~~~~~~~~~~}}}||||||}}}~~~~~~~~}||||}~~}}}}}}|||{{{zzz{{|~~~~~~~~~~~~~~~~~~~~}}}|||||}}}~~~~~~~~}}|||}}~~}}}}}}}|||{{{zzz{|}~~~~~~~~~~~~~~~~~~}}}||||||}}}~~~~~~~~~}||||}~~~}}}}}}}|||{{zzz{{|~~~~~~~~~~~~~~~~~~}}}|||||}}}~~~~~~~~~}}||||}~~}}}}}}}||{{{zzz{|}~~~~~~~~~~~~~~~~~~}}}|||||}}}~~~~~~~~~}|||||}~~}}}}}}}}||{{{zz{{|~~~~~~~~~~~~~~~~~~}}}}||||}}}~~~~~~~~~~}}||||}~~~}}}}}}}||{{{zzz{|}~~~~~~~~~~~~~~}}}}||||}}}}~~~~~~~~~~}}||||}}~~~}}}}}}}|||{{zzz{|}~~~~~~~~~~~~~~}}}}}|||}}}~~~~~~~~~~~}}||||}}~~~}}}}}}}|||{{zz{{|}~~~~~~~~~~~~~~}}}}}||}}}}~~~~~~~~~~~~}||{{|}~~}}}}}}}}||{{{zz{{}~~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~~~~}}||{||}~~}}}}}}}}||{{zzz{|}~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~~~~}}|{{||}~~}}}}}}}}||{{zzz{|}~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~}}|{{{|}~~}}}}}}}}||{{{zz{|}~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~}}||{{|}}~}}}}}}}}}||{{zz{|}~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~}||{{||}~~}}}}}}}}||{{{z{{|~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~}}||{{||}~~~}}}}}}}}||{{zz{|}~~~~~~~~}}}}}}}}}}}}~~~~~~~~~~~}}|{{{|}}~}}}}}}}}}||{{z{{|}~~~~~~}}}}}}}}}}}~~~~~~~~~~~}}||{{{|}~~~}}}}}}}}}|{{{{{|}~~~~~}}}}}}}}}}}~~~~~~~~~~~}}||{{{|}~~}}}}}}}}}||{{{{{|~~~~~~}}}}}}}}}}~~~~~~~~~~~~}||{{{||}~~}}}}}}}}}||{{{{{|}~~~~~}}}}}}}}}}~~~~~~~~~~~}}||{{{||}~~}}}}}}}}}||{{{{{|~~~~~}}}}}}}}}}}~~~~~~~~~~~}}||{{{||}~~~}}}}}}}}}||{{{{|}~~~~~}}}}}~~~~~~~~~~~~~~~~}}||{{{{|}~~~}}}}}}}}}||{{{{|}~~~~~}}}}}~~~~~~~~~~~~~~~~}}||{{{{|}~~~}}}}}}}}}||{{{{|}~~~~~}}}}~~~~~~~~~~~~~~~~~}}||{{{{|}~~~}}}}}}}}}||{{{{|}~~~~}}}}~~~~~~~~~~~~~~~~~}}||{{{{|}}~~~}}}}}}}}}|||{{{|}~~~~~~}}}~~~~~~~~~~~~~~~~~}}}||{{{||}~~~}}}}}}}}}||{{{|}~~~~~~}}}}~~~~~~~~~~~~~~~~~}}||{{{{||}~~~~}}}}}}}}}||{{{|}~~~~~~}}}~~~~~~~~~~~~~~~~~~}}||{{{{|}}~~~}}}}}}}}}|||{{||}~~~~~}}}~~~~~~~~~~~~~~~~~}}||{{{{||}~~~~}}}}}}}}}|||{{|}~~~~~~~}}~~~~~~~~~~~~~~~~~~}}|{{{{{|}~~~}}}}}}}}}|||{{|}~~~~~~~}}~~~~~~~~~~~~~~~~~}}}|{{{{{||}~~~}}}}}}}}}}|||||}~~~~~~~}}~~~~~~~~~~~~~~~~~}}||{{{{{|}}~~~~}}}}}}}}}}|||||}~~~~~~~}~~~~~~~~~~~~~~~~~~}}||{{{{{|}~~~~~}}}}}}}}}||||||}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}|{{{{{||}~~~}}}}}}}}}}|||||}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}|{{{{{||}~~~~}}}~~}}}}}|||||}~~~~~~~~~~~~~~~~~~~~~~~~~}}|{{{{{||}~~~~}}}~~}}}}}|||||}~~~~~~~~~~~~~~~~~~~~~~~~}}|{{{{{||}~~~~}}}~~~}}}}}||||}~~~~~~~~~~~~~~~~~~~~~~~}}||{{{{{||}~~~~}}~~~~}}}}||||}}~~~~~~~~~~~~~~~~~~~~~~~}}|{{{{{{|}~~~~}}}~~~}}}}}||||}~~~~~~~~~~~~~~~~~~~~~~~}}|{{{{{||}~~~~}}}~~~}}}}}|||}}~~~~~~~~~~~~~~~~~~~~~~~}}||{{{{{||}~~~}}}~~~~}}}}||||}~~~~~~~~~~~~~~~~~~~~~~}}||{{{{{||}~~~}}}~~~~}}}}||||}~~~~~~~~~~~~~~~~~~~~~~~}}}|{{{{{||}~~~~}}}}~~~}}}}}|||}~~~~~~~~~~~~~~~~~~~~~~~}}||{{{{{||}~~~~}}}~~~~~}}}}||}}~~~~~~~~~~~~~~~~~~~~~}}||{{{{{||}~~~}}}~~~~}}}}}|}}}~~~~~~~~~~~~~~~~~~~~~}}||{{{{{|}}~~~}}}}~~~~}}}}}|}}~~~~~~~~~~~~~~~~~~~~~~~}}|{{{{{{|}}~~~}}}}~~~~}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}|{{{{{{|}}~~~}}}}~~~~}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}||{{{{{||}~~~}}}}~~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}||{{{{{{|}~~~~}}}}~~~~}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}||{{{{{||}~~~}}}}}~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}||{{{{{||}~~~}}}}~~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~}}||{{{{{{|}~~~~}}}}}~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}|{{{{{{|}}~~~~}}}}~~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}||{{{{{||}~~~~}}}}}~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}||{{{{{||}~~~}}}}}}~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~}}||{{{{{{|}}~~~}}}}}~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}||{{{{{{|}~~~~}}}}}}~~~~}}}}}}~~~~~~~~~~~~~}}~~~~~~~~~}}||{{{{{||}~~~~}}}}}}~~~~}}}}}}~~~~~~~~~~~~~}}~~~~~~~~~}}||{{{{{||}~~~~~}}}}}}~~~~}}}}}}~~~~~~~~~~~~~}}~~~~~~~~}}||{{{{{{|}}~~~~}}}}}}}~~~}}}}}}~~~~~~~~~~~~~~}}}~~~~~~~~}}}||{{{{{||}~~~}}}}}}}~~~~}}}}}}~~~~~~~~~~~~~}}}~~~~~~~~}}||{{{{{{|}}~~~~}}}}}}}~~~}}}}}}~~~~~~~~~~~~~~}}}}~~~~~~~~}}||{{{{{{|}}~~~}}}}}}}}~~~}}}}}}~~~~~~~~~~~~~~}}}}~~~~~~~}}||{{{{{{||}~~~}}}}}}}}}~~}}}}}}~~~~~~~~~~~~~~}}}}~~~~~~~~}}||{{{{{||}~~~~~}}}}}}}}~~~}}}}}}~~~~~~~~~~~~}}}}}}~~~~~~}}||{{{{{||}}~~~~}}}}}}}}}~}}}}}}}~~~~~~~~~~~~~}}}}}}~~~~~~}}||{{{{{||}}~~~}}}}}}}}}}~}}}}}}~~~~~~~~~~~~~}}}}}}}~~~~~~}}||{{{{{||}~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}~~~~~~~}}|{{{{{{||}~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}~~~~~~}||{{{{{||}}~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}~~~~~~}}|{{{{{||}}~~~~}}}|}}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}~~~~~}}||{{{{{||}~~~~}}}||}}}}}}}}}}}}}~~~~~~~~~~}}}}}}}}}~~~~~~}||{{{{{||}}~~~}}}|||}}}}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}~~~~~}}||{{{{{||}~~~~}}|||}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}~~~~~}}||{{{{||}}~~~~}}}|||}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}~~~~~~}||{{{{{||}}~~~}}||||}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}~~~~~}}||{{{{||}}~~~~}}|||||}}}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}~~~~~}}||{{{{||}}~~~~}}|||||}}}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}~~~~~}}||{{{{||}}~~~~}}|||||}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}}~~~~~~}}||{{{|||}~~~~~~}}|||||}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}~~~~~~}}|||||||}}~~~~~}}}||||}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}}}~~~~~~~}}||||||}}}~~~~~~}}}||||}}}}}}}}}}~~~~~~~~~~~}}}}}}}}}}~~~~~~}}||||||}}~~~~~~~~}}}|||}}}}}}}}}}~~~~~~~~~~}}}}}}}}}}~~~~~~}}}|||||}}~~~~~~~~}}}}||}}}}}}}}}}~~~~~~~~~}}}}}}}}}}~~~~~~~~}}|||||}}}~~~~~~~}}}}|}}}}}}}}}}~~~~~~~~~}}}}}}}}}~~~~~~~}}}||||}}}~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~}}}}}}}}}~~~~~~~}}}}||}}}}~~~~~~~}}}}}}}}}}}}}}}~~~~~~~}}}}}}}}}~~~~~~~~}}}}}}}}}~~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~}}}}}}}}}~~~~~~~~}}}}}}}}}~~~~~~~~~}}}}}}}}}}}}}~~~~~~~}}}}}}}}}~~~~~~~~~}}}}}}}}~~~~~~~~~}}}}}}}}}}}}}~~~~~~~}}}}}}}}}~~~~~~~~~}}}}}}}~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~}}}}}}}~~~~~~~~~~}}}}}}}~~~~~~~~~}}}}}}}}}}}}~~~~~~~~}}}}}}}}~~~~~~~~~~~}}}}}}~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~}}}}}}}}~~~~~~~~~~}}}}}}~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~}}}}}}~~~~~~~~~~~}}}}}~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~}}}}}}~~~~~~~~~~~~}}}}~~~~~~~~~~~}}}}}}}}}}~~~~~~~~~~}}}}}~~~~~~~~~~~~~}}}~~~~~~~~~~}}}}}}}}}}~~~~~~~~~~~}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m52.bin b/applications/SprPoP/dist/MUS/m52.bin deleted file mode 100644 index ed44f97..0000000 --- a/applications/SprPoP/dist/MUS/m52.bin +++ /dev/null @@ -1 +0,0 @@ -{uqprvz|xtrpqruy~~{ywusrrqqqppqqqqqrstvxz~}ytrqqtx}|xusqqqrstvx{|}~~}|{ywvtsrqqqqrtvxz}}zwtsqqrsvx||zxwutssrrrrrrrrrrrrqrqrrrrrrsstuvwxy{|}~|zxwutsrrrstuwy|~~}|{zzyyyxxxxxxxxxxyyyyzz{{||}~~~}|{zywwvutsssssstuvwxz|}~~~~~}}}|{{zzyxwwvvuttttttttttuvvwxy{|}~~~~}}}|||{{zzzyyxxwwvvvuuutttttttuuuvvwxxyz{|}~~~~}}}|||{{zzzyyxxxwwwvvvuuuuuuuuuuuuvvvwwxyyz{{|}~~~~}}||{{{zzyyyxxxwwwvvvvvuuuuuuuuvvvvwwxxyyzz{||}~~~~~}}||{{{zzyyyxxxwwwwvvvvvvvvvvvvvvwwwxxyyzz{{||}~~~~}}|||{{zzzyyyxxxwwwwwwvvvvvvvwwwwwxxxyyyzz{{||}}~~~}}||{{{zzzyyyxxxxwwwwwwwwwwwwwwwxxxyyyzzz{{||}}~~~~}}}||{{zzzyyyyxxxxxxwwwwwwwwxxxxxyyyyzzz{{|||}}~~~~}}}||{{{zzzyyyyxxxxxxxxxxxxxxxxyyyyzzz{{{|||}}~~~~}}}||{{{zzzzyyyyyyxxxxxxxxxxyyyyyyzzz{{{|||}}}~~~~}}}|||{{{zzzzyyyyyyyyyxyyyyyyyyyzzzz{{{|||}}}~~~~~}}}|||{{{zzzzzzyyyyyyyyyyyyyyzzzzz{{{{|||}}}~~~~~~}}}|||{{{{zzzzzzzyyyyyyyyyyzzzzzz{{{{|||}}}~~~~~~}}}||||{{{{{zzzzzzzzzzzzzzzzzzz{{{{{|||}}}}~~~~~~}}}||||{{{{{{zzzzzzzzzzzzzzzz{{{{{||||}}}}~~~|zyyz|~}{zyyyz|~~}|{zzzyyyyyyyyyyyzz{|}~}{zyyz{}}{zzyyyzz{|}~~~~~}|{{zzzyyyzz{|}~~|{zzyyz{|}~~}||{{zzzzzyyyyyyyyyyyyyyyzzzzzz{{{||}~~~~}|{{zzzzzz{{|}~~~~}}}}}||||||||||||}}}}}}~~~~~~~}}||{{{zzzzzzz{{{|}}~~~~~}}}}||||{{{{zzzzzzz{{{{||}}~~~~~~~~~}}}}}}|||||{{{{{{{{zzz{{{{{{||||}}~~~~~~~~~~}}}}}}||||||{{{{{{{{{{{{{{{{{{||||}}}}~~~~~~~~~}}}}}}|||||||{{{{{{{{{{{{{{{{||||||}}}~~~~~~~~~}}}}}}}||||||||{{{{{{{{{{{{{||||||}}}}}~~~~~~~~~~}}}}}}|||||||||||{{{{{{||||||||||}}}}}~~~~~~~~~~}}}}}}||||||||||||||||||||||||}}}}}}}~~~~~~~~~}}}}}}}||||||||||||||||||||||}}}}}}}~~~~~~~~~~~~}}}}}}}}||||||||||||||||||||}}}}}}}~~~~~~~~~~~~}}}}}}}}}}|||||||||||||||}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}|||||||||||}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~wropsw|~yurpopsv{|zwusrqqpppppppppqrstvx{zvrppruz~yusqppprstwy{|}~~}|{zxvtsrqpppqrtvx{~~{xusqqqrsvy}}zxvutsrrqqqqqqqqqqqqqqqqqqqrrrstuvwxy{|}~|{ywutsrqqrstuwz|~~}|{zyyxxxwwwwwwwwwxxxyyyzz{{|}}~~~}|{zywvuutsrrrrrsttvwxz|}~~~~~~~~~~~~~~~~~~}}}||{zzyyxwvvuuttsssssssstuuvwxy{|}~~~~~~}}}}|||{{{zzyyyxxwwvvvuuttttssssstttuuvvwxyz{|}~~~~}}}||{{{zzzyyxxxwwvvvuuuutttttttttuuuuvvwwxyzz{|}~~~~}}}||{{{zzyyyxxxwwwvvvuuuuuuuuuuuuuuvvvwwxxyyz{{|}}~~~~}}||{{{zzyyxxxwwwwvvvvuuuuuuuuuuuvvvwwwxxyyzz{{|}}~~~~}}|||{{zzyyyxxxwwwwvvvvvvvvvvvvvvvvwwwxxxyyzz{{||}~~~~}}||{{{zzyyyxxxwwwwwvvvvvvvvvvvvwwwwxxxyyzzz{{||}}~~~~}}}||{{zzzyyyxxxxwwwwwwwwwwwwwwwwwxxxxyyyzz{{{||}}~~~~}}|||{{zzzyyyxxxxxwwwwwwwwwwwwwxxxxxyyyzzz{{{||}}~~~~~}}|||{{zzzzyyyyxxxxxxxwwwwxxxxxxxxyyyyzzz{{{||}}}~~~~~}}|||{{{zzzyyyyyxxxxxxxxxxxxxxxxyyyyzzzz{{{|||}}~~~~~~}}|||{{{zzzzzyyyyyyxxxxxxxxxxyyyyyyzzzz{{{|||}}}~~~~}}}|||{{{{zzzzyyyyyyyyyyyyyyyyyyyyzzzz{{{{|||}}}~~~~~~}}}||||{{{zzzzzzyyyyyyyyyyyyyyyyzzzzz{{{{|||}}}~~~~~~}}}||||{{{{zzzzzzzyyyyyyyyyyyzzzzzz{{{{{|||}}}}~~~}{yyy{}}|zyyyz{}~}|{{zyyyyyyyyyyyyzzz{|}~|zyyyz|~~|zzyyyyzz{|}~~~~~}}|{zzyyyyyzz{|}~~}|zzyyyz{|~~}|{{zzzyyyyyyyyyyyyyyyyyyyzzzzz{{||}}~~~~}|{{zzzyzzz{|}~~~~}}}||||||||||||||||}}}}}~~~~~~~}}||{{{zzzzzzzz{{||}~~~~~~~}}}}|||{{{{zzzzzzzzzz{{{||}}~~~~~~~~~~}}}}}}|||||{{{{{{{zzzzzzz{{{{{||||}}~~~~~~~~}}}}}}||||||{{{{{{{{{{{{{{{{{{{{|||||}}}~~~~~~~~~}}}}}}|||||||{{{{{{{{{{{{{{{{{{{|||||}}}}~~~~~~~~~}}}}}}|||||||{{{{{{{{{{{{{{{{{||||||}}}}~~~~~~~~~}}}}}}|||||||||{{{{{{{{{{{{{|||||||}}}}}~~~~~~~~~~}}}}}}|||||||||||{{{{{{{|||||||||}}}}}}~~~~~~~~~~~}}}}}}|||||||||||||||||||||||||}}}}}}}~~~~~~~~~~~}}}}}}}||||||||||||||||||||||}}}}}}}}~~~~~~~~~~~}}}}}}}}||||||||||||||||||||}}}}}}}}~~~~~~~~~~~~}}}}}}}}}|||||||||||||||||}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}||||||||||||}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}||||}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~zuqpquz|wtqppruy~~{yvtsrqqpppppppqqrstvxz~}xtqpqtx}|wtrqpprstvxz|}~~}|zywutsrqqqqrsuxz}}zwtrqqqsux{~|ywvutsrrrqqqqqqqqqqqqqqqqrrrsstuvwxyz|}~}|zxvutsrrrstuwy{~}||{zyyyxxxwwwwwwwxxxyyyz{{||}}~}|{{yxwvuttssrrrsttuwxz{}~~~~~~~~~~~~~~}}}||{zzyxxwvvuuttssssssttuuvwxyz|}~~~~~}}}}|||{{zzzyyxxwwvvvuuuttttttttttuuvvwxyz{|}~~~~}}}|||{{zzzyyxxxwwwvvvuuuutttttttuuuvvvwxxyzz{|}~~~~~}}||{{{zzyyyxxxwwwvvvvuuuuuuuuuuuuvvvwwxxyyz{{|}}~~~}}||{{{zzyyyxxxwwwvvvvvvuuuuuuvvvvvwwwxxyyzz{{|}}~~~~}}}||{{zzzyyxxxxwwwwvvvvvvvvvvvvvwwwwxxyyyzz{{||}~~~~}}|||{{zzzyyyxxxwwwwwwvvvvvvvwwwwwxxxyyyzz{{{||}}~~~}}}||{{zzzyyyyxxxxwwwwwwwwwwwwwwxxxxyyyzzz{{|||}}~~~~}}}||{{{zzzyyyxxxxxxwwwwwwwwwxxxxxyyyzzz{{{|||}}~~~~}}}||{{{zzzyyyyyxxxxxxxxxxxxxxxxyyyyzzz{{{|||}}}~~~~}}}||{{{{zzzyyyyyyxxxxxxxxxxxyyyyyzzzz{{{|||}}}~~~~~}}}|||{{{zzzzzyyyyyyyyyyyyyyyyyyyzzzz{{{|||}}}~~~~~~}}}|||{{{{zzzzzyyyyyyyyyyyyyyyyzzzz{{{{|||}}}}~~~~~}}}|||{{{{zzzzzzzyyyyyyyyyyzzzzzzz{{{{||||}}}~~~~~~}}}||||{{{{zzzzzzzzzzzzzzzzzzzzzz{{{{||||}}}~~~~{yyy{}~|{yyyy{|~~}|{zzyyyyyyyyyyyyzz{|}~}{zyyz|~~|{zyyyyzz{|}~~~~~}}|{{zzyyyyzz{|}~~}|{zyyyz{|}~}||{{zzzzyyyyyyyyyyyyyyyyyzzzzz{{{||}~~~}||{zzzzzzz{||}~~~~}}}|||||||||||||||}}}}}~~~~~~}}|||{{zzzzzzzz{{{|}}~~~~~}}}}||||{{{{zzzzzzzz{{{{||}}~~~~~~~~~~}}}}}}|||||{{{{{{{zzzzzz{{{{{||||}}~~~~~~~~}}}}}}||||||{{{{{{{{{{{{{{{{{{{|||||}}}~~~~~~~~~}}}}}}|||||||{{{{{{{{{{{{{{{{{{|||||}}}}~~~~~~~~~}}}}}}|||||||{{{{{{{{{{{{{{{{||||||}}}}~~~~~~~~~}}}}}}||||||||||{{{{{{{{{{||||||||}}}}}~~~~~~~~~~}}}}}}||||||||||||||||||||||||||}}}}}}~~~~~~~~~~}}}}}}}||||||||||||||||||||||||}}}}}}~~~~~~~~~~~}}}}}}}}|||||||||||||||||||||}}}}}}}~~~~~~~~~~~~~}}}}}}}}||||||||||||||||||}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}||||||||||||||}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}|||||||}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~x|prnlx{mvqttp}mhjjt{r{}~zrzrznrt||zsl}~}{yt|uwtq{ifhhsxs{w}p{p|ov{rvp~zxtx}yytszryqurq{jghlzuxx~{}wwzzxo~wyx~spvtsuvrvsrvpuotzhiio~rw|~~yp|ruzz}{jomujtyqov~nstpunxwhkjsqw|}xvlpzq}vfuzgxxgvwrp|wluqruo|vjln{{u}|}~urofvxuri}si}oi{|spspovqussplmrt}w{|vnlhk|t{nslq}jn}{us{{lutrwrwonpzrwzrmlftztyl}zi|vhv~xturovruvq~xmosvyy~|{wolii{tyotnp~ll|}xv}zkvssvstrpr{~rvztlkgvxs~yn{j{wht~zxxppvruwp}{oquuyy~z~{yolhm|qyqxntlm}|yv~xlvstvsttrtzswz~~}tmkk{st|vsvmtiv~|y|}kutsuvpzqszrv}z}xomivxpz}~smz|jq~}{zppwstwqxrsuvv{zy|{tnlp}ovzwyuqtjw~~|ykxsutwo{tv}}ru}y|zpnkyvq|~yq|{y~rnprvvx{zwvq~w~~zoz}eirmns{~vp}nt~}hej[lmsivhN_Zjvkft|~zt~}{q~sxkwnljlw~{y~xkiw_ckaeetpen|m|nx|z|gf}uk~jzy|jqtphdeWe}tsqor|}~}nxxihtmvaq{gpity|p{{xbhpkwzlwm{}y|~shx}ovnq~sjfyi[pftp|ttwzxvuq|lu{}risrkxgv|spg|mq}xbigmcbupln|s}{s{wzq`spw|vwowwg|q|ijtttimwk]mzwy~~wpsnc`vxw{qvum|}wy~tqt~ph~wmktvcy}yr|x|o[ffzww~{xixxosp|woww|tvvmmq}tz|}xtx{w_d\mtf|jznqweov~tyv~xv}x}}vw|y|rruf_Vezh}}ljuizrs{yuwzwv|vty{{{s}sl\X_unsxysqvhtmmzvzqmsqxt~u~}x{{vyi^d}tqz{ue}p`xxiswsvtfvzzxpv|{x}ywuifgx~}ni{rhrrgfu{z~seotytny|z}xy}yzyqxo~yzoert`lzefzsy{jgussrmo}tzx{o|wqn~atggb_w|r[jniemnrywvtyluphswpv}rtnmgnqd`cSe^nmxvz{y~lt{vkqf~ogosmljl|uqswyzuZYVp_Ycgzz|joqbexiqowpxxtvyzuovvw[O[pphl|yr|q}szpr||}jhpztwm`qw|ry^bqz}h}f~}qsyq{zryimpxqngx_rjr}{x|upzvmoowvz~xzwys~yzzr~kooy{lgoblfftvikttuszyontsvwwqkmcllWoiy}]q}cuuhpiexzoyv~rny}~lsnxig^`jjnn}kwvzjuy}tvvprhwftzwvv||t|{yoxofu|xdzecf]pw{xquvqt}wilvy}~xyw{t{{y~{prhrs_arfhwkckffnwwps{~yzxoyyn|u|vw|fsu]s|npmldkilkjo`fovyqs|}{o[`pilqwk\utoovievhwigwlrwzkhomzsvrnorb_hmz{ld~ttwgkbkwauxmmpzxooy{|{okwmlnmyrrph|uvnicfoimxvlwzstxwzx~~~xz{vuyw}rl~w}qnfksghvnmnutowxuq~{vwvpzqxyu~tbrr{pzssrj}yspsfgxk~tut}zromyzz{xm`lksfu~z|sn~vly|qrz{}xnll|srxx{wxw_}|c~ywSQJ@Un[m{axnwislsqyht}oleZbj_pp~s{nqgiintqhr||vds}p|}zu~y~qwrjl[VWeb\\BHFL^bJqme{o{nx~v~pr{udlt_e]WcR]QMHZmwj|{mzm`NZg\vx}`w}[vqTfwuvvshfonwarxjnonx{x}qnrnrrh^TX]x{~|m~ppx|t}pez{trmcphovpq{uzxlprsrti|yjewVbkT^hh_szfeg_ll^loignvm~}}}ymi}zsj|klw^p}}{y{qrnsthrjizz~tuww}rnglixyvonn[\xos{}}s~xt|~}y~}wvql_R[jhnpvx~z~~qmv_cyb^uxsuzdoup{mouxxvz{zxxpn^mxr|q~qp|{ts|vvo}xwqu{jgtl|wwt~~zz{pjiq}chmo`c[dogw}u}yq~r~zpoi~atqn`^_kkkps}|vq~g|yoo|~{{ookvv`cvnps{b_xwy|~zn}vni~||tz}zmjntpblm]nn{gjwu{wyx~}}plsmutrtgrttndlys}~~uvvxzrplu|z||ywtz~|rzlv{uqijrsxwy}~omnanqqtuVaeksjm|~x|vq|~~y}~v{wtymnlxx~io{|zuj|}w|~fhrfkzyxsysrz~vwou~}{t{uyqpzwyk}{k}~y~o}jq|y}|xv||ystujstwwzzkn|jlvurtw|sqjwzr_fsotwbpxvv{{w|r}z|}yrpkook`ltowsvbbumk{}urw}u|tku|qghnfuplvt|oou{ysj}{pn}uklwupiabanqwpr{yfahowgdmfxnf\qtsz~sjwy~}yxs}z}vqptv{ronw~}yfkhtol`s{tvztoxt}~x}v|xpjnnn|ufwv{y{wy~~vziuz|ltr}nr{x|}spn|yss~zzxj|}y~xquowna`iqynjjo}zvi{}x}qxw{z|z}zwswzpluy|{pxszuhrw|}urrxrq~tkluwu~znzxoqmr}{}wnttyxxm|uvvxstyzyprzxvrlll||t}}uxsylkrwhtppzyvfutvzvqkxyi~yy||{uuxk|z|rnoqtwt~tvfrwsrmljsr{}{yx|o~ztosjzy~shp}qjpn~tt_epoqpvpzv~~r{xoy~zywzm{xt~uklt{rkmduoscfxt{{xy{y}zvx}|}y~u}tkkprrf`id{|xyt~tnr}{s}}yv}w}}w~p}|x~qdensvnhmmxyvyx}vtt|}z~~}~yry|{v{rysls{|zy~~uzupxtny~y{}uwz~~wmogyzzs~}znm~{oyrnmzyzx~ww|yxv}}}}v{{unlby|yqy|~yz}{{rt|}xy|q~up}upu}zx~t}}ww~|yvpi{~x{|yyt{rw}fqvuv|to~|pt~uov|z|y}zqngj}u~yw}|s|nz}ozz{yv|sw~yv{|{ytt|okmusoklalzw}{|z}rq{v~z~}xy||{rqznpvzwuriz|rtyz{tptizy{lsswy~q{{s{xxuz{{~w|uz~|u~}{~~vw{s|}px{}|~t~wy~wsry|vv}wzvsw|qv{|uphzx|}{zx{su|zz|{xx~wv~{|}{vtm{u~|{w|wx{zssvk{zmuyzz|mz~zut~xtqy}|vz{zvp{z~~yw~q~rvx||~n||vpwrppz~zt}zvtqt{ztmmip}x}}~|zt}wx|uy}~w~yyvznqx{tqoko}~{}{{~z{||vpulp|xwqsuv{ppz{u}~z{ztosy~{wrqs||uyvs{{quz~wp~|}}~y|~wzpwklnuurnkhzx~yywwn}~zzwz~~~{runpstqnljt~|{zx{~zzwsotmt|{}x|y}~~x~}t~losxvtqqvz}vyyv|t~}z{|xv~x~szop~kfiprpkln~{~~x}~yxy~x}wntiiospljjv~~}}yxzx|}{|r{~p|||t}|~{~n}ljmqpomny~|~w|~}sw{~~~{}qvyw|vu{iuwdbejkkjjp~zzz{y|~zwt|kt|ifhkkjikqz}xxst|~~wv{~~~xz~yxu~pvkgioppnow}{wvwr{||~~~|}rqxsz{toxjn{ieeknomnt|zxzwuv}y~}vs}ns~mjkoppopwy{{ts}}|xu{}}|}tuzxwvszpnpttsqrz|{uvwwx}z{{|z{ptxu|}ut~oz~kikoonmox~|u{u|y~~}~|r{}{x{~o{llnqpnlnz~~zs|~}qx}||}|t}}~u}|qwkklnmmln|~ys~~|qz}|||w|}uwrqklmllklr~{vs}~{s}|~zw}yr~vsojknnmklr~}~|vu{v~zw~{~yr}supkknmkijq}~~{uu|xwv}|~xssvnjjlkjhiq|}|vu~xwt{{~xr~srmiilkkikq~}|yxw~}yu|{}~wp{tpojkmkihjq}}{{yw|}~ytzz{}wozwpphjlljhhn{{|xvz~~yrwyz}zpvyl}uiiijiggmy{~~yw~z~}~{rv{z}~zrs|lxxhhikihgku{}x{{z{}||~~tsxy|zrn{nr|jhhjjhhjszyv{~|{}~wrwxy|~zulvuk|pgijkihjr{wv~x}||~ypsxx{}|wnpzkwxhijmmkkp{~x{|x{|{{}{sqwwy||xrlypp~njkmmmloxxxt{~{y{~|upuwy{|ytmywn~rjlnomlnvywuwzy{}~wptwy|}wnu{m|wjlmommmt{w}w{w{yz}~zsuyz|~}ypq}nxzklmnmllr}{v{yyx{zz||styz|~}ypp}ov{jklnnmlp{|vzzxy|{|~}ttyz|~}yqn{ot|lklmmllq{|vy|w{~||~}usxz{||xqm{pr{kklnmllpy|wz~z|~}|~|tswy{}|wol{os{kklnmkkpy|w{}{{~||~{ssxyz|{won{ntzjjkmlkkpz|x}}|{~|{}zrrwy{|{wno{muxijlmlkkpz~}zzy|{|~xrtxyz|ytlqykyuikklkjlr|}}}{x{|z{~~wquxyz{xskvwl{phklljjks}}~|}x||z|~|tquxz{{wqjxsn|miklkjilt~|}|}x~~~{z|~{tqvxyzzwqkyro{kiklkjimu~}|~y}}{y{~{spuxy{zvpkyppzkiklkjimu}|~y{}{y|~zsquxyzzuokzpqzjjklkjjow~|~x|}zy{}zrptwyzzvokyqozkjlnmlkow~|~x}|}zy{}zsptwyzzvpjxso|nkmnnllow~~|}w~|~zxz|{sptwxyzwrkxvn|pkmnnmlnv~~||wyzxy{|uprwyz{xtltzm{tknnomlnt}~}}|y~w{yy{~~xrswyz{zunp|nxxkmnommms{~~{|zy|yx{~zsrvyz{zvpm{qs{llnonmlpx~{}w~{{y{}|urtxzz{wrkvwm|qjmnnmmov~~|{yy|zz|xrsvyzzytmp{mvwjmnonmns{~{~z{~{z{~zsqtxzzyuokyrp{lknonmmqy{}y|{y{}{tqsvxyyvqjuumzojmoonnpw}}zz|yz||upruxyyvrjqxkxsjmnonmou}}|~y|zy{~}wqqtwxxwrkmyluvilnonnot|}}}z|yxz}}wpptvxxwsllymswjlnonnns{}{z|zxz|~wqpsvxxwrlkynqxjkmonnnsz}z{}zxz|~xqoruwwwsmjxopxkknoonosz~zz|yxz|}vporvwwvrlkxnqxjlnponos{}~{z|yxy|}vposvwwvrlkymrwjlnpoopu|~}|y{xwy||vposvwxvrkmymtwjmoqpopu}~{~x{xxz|{topsvwxuqjpylxtjnoqooqv~yyzxxz}zsoqtwxxvqkuwmzqkoppooqwx~{~zxy{~yrprvyyyupkxto{nloppoorx~~~y}}}zxy{ysqsvyyyupkyspzmloppoorx~~~y|~}zxz|ysqtwyyyuolyrqzmmoponnqx~}z{}{yz|xsqtwyyyuolzqrzllnponnrx}}z}~{yz|ysqtwyyyuplyrpzmlnponnqw~}}z~~{yz|ysqswyyyupkxsozmlnponnqw~}}~z}|yz|ztqsvxyxupkvumznknoonnpv}}}~{{|yz{~{tpruxxxvqkrxlxrjnopnnot{~}~}z}zyz}|uqqtwxxvrloymuujmnponosz}~}{}zxz|}wqpsvxxwsmkxoqxklnponorx~~z|{xy{~~yrpqtwwwtpjtulyoknopooqv}~{y{yxz|{tppruwwuqknxltujmoqpopt{{{}yxy{~|vpoqtvwvsmjwqoylloqpppszy~{~zwxz}}wqopsvvvsoittmyokoqrqqsyzyzxxy|~yrooruvwtpjqwlwskoqrqqsx|}x{xwx{~ztooruvwurlnynuwloqsrrrw}}wwzy}zuvy}{|wwusmhfgmt{ysqsw{|{zxxz|sq~uvlgffgikllp|{zywqmijotxyxvux{~~z{~~{xxyzsn|sz}}}~}xvzzvx{}}}x~{vy|sollmqrrngbacghihe_^jbdk_adffghmt||wtssuutohcbfjmopoln|u{w{}~~~zx{zzx}{z|~zuuwz||{wqs~rx{oprsqqpty~|xvuvvy{y~xsstvwwtpkqwkusjmopooqv|~rzyp{vqnmoqtusnifhknpqpmjuwp}vrvxzz{}|}|yz{}|vpnoqssrojgrmhrheghgffintxyxusrtux|}|z{}~~}zur}xt~sqrtsrsuz~~||wq|rs{uqopruwwtojiknpqqoklxqq{qpsuvvw{~|zz{}~}ysnlmoppnjffphhpecdfeefintxyxvttvx{|z{}~~}zvtxvursutttv{~{|xq|ts|vsqqsvxxupljlnqrrolkwrozroqttuvy~~}zyz|}}ytnklmnnmjfeokepicefgfginty{{ywwxy||yy{|}}zws{}t~{stuuuuvz|{~}sx|qy{vsrtvxyxtolkmpqrqnkqxnvypqrtuuw|||yz{|}{wrmklmnnmjfgqhiqgefhhhimrx|}|zyyz|{xyz|||yvt}|tystuvuvw||{~}sy|qz|wutuwyzyupmlnprrqnlryovzqqstuvx|}~|zzz||{wrmkkmnnmjfhqjirjghijjknsy}}{{|~~zwwyz{zwts}zs~yssuuuvx||||t|{s||xuuvxzzxtpmmoqrrpnltwnxxqrsuuwy~~z~{yzz||zvplklmnnlifkqgmqhhikklmqw|}||~{wvvxyywtrw}sw}usstuuvz~}~wxux~zxvvxyzyvrnmnoqqpnlpxpszsqrtuvx|}}|zzz{|zwrmkkmmmligjrjjskhikklnqv{~}}~{wuuwxxwtqu|tv}ursttuvy~~wxvx~{xvwxzzyvrnmnoqqpnlqxoszrprttuw{|{~}{||~~|ytpopqrqpnlrwnsxpnoqqrtx}|z}{|}~~|xspopqrrpmmtvmuvoooqqruy~|z|||}~~{wsppprrqommvtmwuoopqrty}{{~}{xvvwxyxutsuwxywtqqxqmuojjjkkmotx|~~}{|}}ywwxxwtqmlmoqqqompxrpysoooppqsw{~~}{{|~|xuttsrpljlrjjrmjknpruzxyxw~}zyyz{||yvrqrstutrqu|uu}xttuvwx{}xtqqqqqpmknunmupkklmnorw{}}||}}{xsomllmmljhkrmktqlmoprtw|}{}}}~~~{wsqqqrrqonpwsnwuonopprtx|~~}yvuuvvusqryxpxysqqrstvy~zx|}{yyyzywtpmkllmmkjjpskpvpnnpqsuy~~~~~~}yurpppqpomlpvnnvqmmmnopsw{~~}~~{wutuvvusrszwrzytsstuvx|~yv{~|zyxyyywspmllmmmljkrtlqwqopqrtvz~~~}}~}|yuqooooonlkounmurmmnnoqtx||}zvtsttttrqtzvrzzustuvwz~|y~v{~|yxxyyxvsomllmmmlkmtsltwqpqrsux||}}}}|zwsponooomllrtlpvpnnopqsvz~}}~~{wtssstsrqrwzru|xttuvwy|~y|xx}}{yyyyywuqnmmmnnmllrvopxtqqrsuwz~}~}}}|{xtqonooonllqunnvsonopqsvz~~~}~~{wtsssssrqqvzsu{xutuvwy|z}yx~}{zyyzyxurommnnnmlmrunqxtqqrsuwz~}~~~}}}{xtqooooomlmstmpvronoprtw{~~|}zwtsssssrqsxxqwzvtttuwy|}{x|~|{zzzywtqonnnnnmlourmuvrpqrsuw{}~~~~|zvsqppponmmpupmutonooprux|}~{xusssssrqquysryxtsstuwy}|~}y~~|{{{{ywtqonnnnmlmruoovuqpqrsux{~~~~|yvsqppponmnrunnusonopqruy|}}{xusrrsrqpquxrqxwsqrrsux{~|~~}}|yvsqpppponnrvqovvrpqrstwz~|~}}}|yvsqpppponnrvrouvsqqrstwz~|~~}}|zwtqpppponnqvsntwsqqrstwz}|}~}}}|zwtrpppponnpuuorwtrqrstvy}~}|~}}}|zxurqpppoonotvpqwvrqrstvy|{~}}}|{yvsqpppponorvsouwtrrstvx{~~{}~}}|{ywtrpppoonnquuosxusrstuwz~~}{~}||{zxurqpppoonotvqpwwtsstuwz}~z~~}|||zxusqpppoonorvrovxtsstuwy|~{}~}|||{yvsqpppoonoqvtotxusstuvy|~{|~}|||{ywtqpppooonquupsxvsstuvx{~~|{}}||{ywtrppooonnquuprxwtstuvx{~~}{}}||{ywtrppoooonpuvpqwwtstuvx{~~}{~}}||{ywtrppoooonptvqqwwtstuvxz~~}{}}||{ywtrppoooonptvpqwwtstuvx{~~}{}}||{ywtrppoooonpuuprwvtsstvxz~~}{~}||{ywtrppooonoquuprwvtssuvx{~||~}||{yvtrpppoonorvtoswusssuvx{~|}~}||zxvsqpppoonosvrotwusssuvx{~|~~}||zxvsqpppooopsvqpuwtsssuvy|~|~}||zxusqpppoooptvqpvvtsstuwy||~}}|zxusqpppoooptuppvvtsstuwy|~|~}}|zxusqqppoooqtuppvvtsstuwy|~|~}}|zxvsrqppoooqtuqpvvtsstuvy{~|~}}|zxvtrqpppooptvqpuwtsssuvx{~|~~}}|zxvtrqpppooptvrpuwusstuvx{~}}~}}|{yvtrqpppoopsvsoswutstuvxz}}}~}}|{ywusqpppooprutprwvtsttvxz}~|~}}|{ywusqpppoooquupqvwusttvwz||~}}|{zxvsrqppooopsvrptwutttuwy|~}|~}|{zxvtrqppoooprutprwwuttuwy{~~{}~}|{zywusqppooooqtuqqvwvtuuwx{}{}~}|{zywusqppooooqtvrpuxvuuuvxz}||~~}|{zywusrppoooopsvsptwwuuuvxz||{~~}|{zyxvtrqppoooprutprwwvuuvxz|~}{}~~}|{zyxvtrqppoooprutprwwvuuvxz|~}{}~~}|{zyxvtrqppooopruuqrvxvuuvxy|~~{}~~}|{zyxvtrqppooopruuqqvxvuuvxy|~~{}~~}|{{yxvtrqppooopruuqrvwvuuvxy|~~{}~~}|{{yxvtrqppooopruuqrwwvuuvwy{~~{}~~}|{{yxvtrqpppoopsutprwwvuuvxy|~}|~~}|{{yxvtrqpppooqsuspswwuuuvxz|~}|~~}||{yxvtrqpppopqturptwvuuuvxz|~|}~}||{ywutrqpppppqtuqpuwvutuvxz|~}~~}||{ywutrqppppprtuqquwvttuvxz|~}~~}}|{ywutrqppppprutqquwvutuvxz|~}~~}}|{ywutrqqpppprutqquvuttuvxz|~}~~}|{ywutrqqpppprttqpuvvttuvxy|~}~~}|{yxvtrqqpppprtuqptvvutuvwy{~}~~}|{yxvtrqqpppprtuqptvvuuuvwy{}}}~}|{zxvtsqqppppqturpsvvuuuvwy{}~}~~}|{zxvusrqppppqsusprvwuuuvwy{}|~~}|{zxwusrqppppprutqquwvuuvwxz}}}~~~}|{zywutrqppppprturptwwvuvwxz|~}|}~~}|{zywvtsqqppppqsusqrvwvvvwxz|~||~~}|{zyxvtsrqppppqrutqquwwvvwxy{}||}~}|{zyxvusrqppppprturqtwwvvwxy{}|{}}}|{zyxwusrqppppprtusqswwwvwxy{}}{|}}|{zyxwutrqpppppqsutqsvxwwwxy{|~{|}}|{zyxwutrqqppppqsutqrvxwwwxy{|~~{|}}|{zzxwvtsqqppppqsutqrvxwwwxyz|~~{|}}|{{zywvtsrqppppqsutrrvxwwwxyz|~~{|}}||{zywvtsrqppppqsutqrvwwwwwyz|~~||}}||{zywvtsrqppppqsutqrvwwvwwyz|~~||}}||{zywvtsrqppppqsutqrvwy~xsqprw}{snmnoqtw{~{wtpnnmheehihgghikmoqsvxz}}{z|wrqpnmmnnnooppopqrsstvvsqrttrponnnopqstvx{~}xtplifdcdfgfhoux{~|}}||}}}}||{{{{{{zyxvuuvxvstvwxxyyzzzyyyyyzz{|~~~~~~~~~}{xurpnmkkjjlmliilnnnnoprtvxz|~}||{ywusqnljhfeddddfhklklquwxz|~||}}}|{{zxwvtsrrqqqrsutqqsuutsttuvwxyz{{{||}~~|{}~}|{zyxxy{zwvxzzyyyz{|~~{z{{xtsttsrponmkjihgggghjlnomnquvwxy{}~}{zxwusqponnmmnoomkmoponnopqrsuvwxyz{|}~~}}~}||{{{{}}|yz}~}}}}~~|zxxzyurrssrrqponmlkjjjjjkloqpoptvwxxz{}~}|zywutrponmlllmnnkjlnonnnopqstvwxz{|}~~}|{{zzz{}}|zz}~~~~~~|zxxyyvsrsssrrqponmllkkklmnprrpqtwxyyz|~~~~}|{yxvtsqonmllllmnnljmopooopqstvxyz|}~}|{zzyyyz{|}{y{}~~~~~}{zxyzyusstttsrrqponmmlllmnorsrqrvxyyz{}~~~~}|{zxwusrponmlllmnomklnppppqrsuvxz{|}~}|{zyyxxyz{|{yy|~~~~~~}{yyzzwttuuttsrrqponmmmmmnoqssqruxyyz{|~~~~}|{zywutrqonmmlmmnonlloqqqqqrtuwxz{}~~}|{zyxxxxxyz{zxy{}}}}}~}|{{|{xuuvvuutsrqponnmmmnoprsrqruwxxyz|}~~~}{zywvtrqponmmmnoonlmpqqqqrstvwy{|}~~|{zyxxwwwxyzzxwy{||||}~~}|}}{xwwwwvuutsrpoonnnnnoqrrqqsvwwxxz{}~}|zywvtrqponnnnopommoqqqqrstuvxz{|}~~}|{yyxwwwwwwxvttvxxxxxyz{}~~}}}}|{zyxvutsrrqqqrstsqrtvvvwwxy{|~~}}}}|{zyxwutsrrqqqrstsqqtvvvwwxy{|~}zvtrrsstvwwxxxxyyxvuwxyzzz|}~}~~{xwwwwvutsrrqpppqrtuwyzzwvvvusrqqsvy}}zyyxwutsrqppooppqstvxyxwy{}}}}}~~|ywusqonmmmmnnmklopqqqrstuwxz{|~~}{yxvusrqpppppnllnpqrrsuwz|}zyzzyyxwvvutsrrqqqrstuutsuxyyyz{|}~}|{yxwvvuuuvvvustvxxxxyz{|}~{zzzyxwvtsrponmllllmnoomnpstuuwxz|~~}}}|{zyxwvutsrqqqqrstsrqsuvvwwxyz{}~~~~~~}}|{zyxwwvvvvwxyywvwz{{{||}~{xxwwvvusrqponmmmlmmopqpopsuvvwxz{}~}}}|{zyxwvtsrrqqqqrssrqqsuvvvwwyz{|~}}}}}||{zyyxwvvuuvvwxyxvwy{|||}~~~~{xwwwwvutsrqponnmmnnopqqppsuvwwxy{}~~~}}|{zxwvutsrqqqqrssrqqsuuuvvwxy{|}}}}}}|{{zyxwvvuuuuuvwxwvwyz{||}}~{yxxxwvutsrqpponnnnopqqqpqsuvwwxy{|~~~~}|{zywvutsrqqqqrssqpqsuuuuvwxy{|}}}}}}|{zyxxwvuuuuuuvwwvuwyz{{{|}~~{yyyyxwvutsrqpooooopqqqpprtuvvwxy{|~~}|{zywvutsrrqqrssrqprtuuuuvwxz{|}~~~}}|{zyxwvvuuttuuvwvuuwyyzz{|}~~{zzzyxwvutsrqppoooopqrqpprtuvvwxyz|~~}|{zywvutsrrrrrssrqqrtuuuvvwxz{|}~~~}}|{zyxwvvuttttuvvvutvxyyzz{|}~|{zzzyxwvutsrqppooppqrqpprtuvvwxyz|}~~}|{zyxvutsrrrrrsssqqrsuuuvvwxy{|}~}}}}|{zyxwvvutttttuvvutuwxyzz{|}~~{zzzyxwvutsrqqpppppqrrqpqsuvvwxyz{}~}}||{zyxwvutsrrrrrrstsrrsuwwxxyz{}~}||{{zyxwvutssrrrrrsttsrsuvwxxyz{|~}|{{{zyxwvutssrrrrrsstsrstvwxxyz{|}}|{{{zyxwvuttsrrrrrrsttsrtvwxxyz{|}~|{{{zyxwvvutsrrrrrrsttsrsuwxxyz{|}~|{{{zyxwwvutsrrrrrrsttsrsuwxxyz{|}~~|{{{zyxxwvutsrrrrrrsttsrsuwxxyz{|}~|{{{zyxxwvutsrrrrrrsttsssuvxxyzz|}~}{{{zyyxwvutssrrrrrsttsssuvxxyzz|}~}{{{zyyxwvutssrrrrrsttsrsuvwxyyz{}~}|{{zzyxwvutssrrrrrsttsrsuvwxyyz{}~}|{{zzyxwvutssrrrrrsttsrsuvwxyyz{}~}|{{zzyxwvutssrrrrrstssrsuvwxxyz{|~}||{{zyxwvutssrrrrsstsrrsuvwxxyz{|~}||{{zyxwvutssrrrrssssrrsuvwwxyz{|~}|||{zyxwvutssrrrrssssrrsuvwwxyz{|~}}||{zyxwvutssrrrrssssrrsuvwwxyz{|}}}||{zyxwvutssrrrrssssrrsuvwwxyz{|}}}||{zyxwvutssrrrrsstrx~}|||||||}~~~||}|{||zxvuvx{~~}||zwutsrqqqrstvwz|~~|{zxwwvvvvuuw{~y|~~~|zxvutsrqqrrstuwy|~}||zxxyz|}~~yz~~}|zxwutsrrqqrsuwz|}~~zxy~~~~}|{zxwvutsrrqqrstttuwy{}}zxx{}|}}}~~~~~}|{zxwuttttsrrstvxz|~}{xustx~~}||{zyyxwvvvvusrrrstttuvvwxxz{|~}{yyxwwx{~~~~~~~~|zxwwvuttttuvwxz|~~|yvtrrsvz~|{{zxvuuuuuutttttttttuuvwxyyyz{~}|{yxwvvxz~~~~~~}|zywutsstuwxz{|~~{xusqrtx}}{yxwvuttsssssstuuuutuvwxxyyzz{|}~}|zxwwxz~~~}}}}~~~~~~}|zxuttuvxz}~{xutstv{~{xwuttttttsrrsttuuuuvwwxyyzz{{{||||}}{{z{}}{zyyyyyyyz|~~{yvuuvy||yvttvz}zwuttttttsssstttuuvwwxxyyyz{|}||{}~}}}~~|zxusqpnnnnnpqsvy|~~|zy{~~zyxzzvrpnnnnopqstvwxy{||}~~~~~~}}|}~z}{}|{zyzz{|||{ywvtssstuvxz|~||~|yuqnlmpu|}{yyyzz{||}}}}}}|||||}}~|wtv}}ywwxz|~}|zxwuttuvx{}~}~{xwz~~}|zxusppqv|~~~}}}}}~~~~~}|ywuuvy~~{zyz}|ywvwxz|}~~}}||||}~|wtrrtvz~~}}}}~}{yxy}~~}{zxvsqommosw|~|{yxwuuttvx{~}zxxxz}~}{zyyxxxyyyz|~}|zyyxxyyzz{|}~~}}~|yvtqonoqtx|~~}{yxwvvvvvvvwwvvvuuuuvy|~}~{xurqrtx||wsqrtvyz{{zzyyyyyyz{|~}||~yvuvx{~|zwusrqppppqrrstuvvwxxyyyyyyz{}~}{zzyz|~~|zyyzz{{zwtqopruxz|}}}}}}}~|wspnpt{}zwutsrqpppooooppqrstuvxyz{}~~|ywvvx{}{ywuttsstuvxy{{zxvsrsvz}|zywvutsrrqpppppqsstvwx{}}~~||||~~xsqpqsvz}}{xvutsssrsssstuvwyz|~|ywwz|~~~zwspmllnsy~}|{zyxxwxxxyz|~||}zz{|}}}~}~}zwtstwz|~~}||{||}~~}~~~{uy~}{yxvtsstux{}||~|vrt{~yuttuvwwxyyyxwvuspnlkjkmqw}zvuy|zzz|}~}|yvtstuwyzyxwutsstwz~~zupmns|}zwvuuuuttttsssrrrrrrrrssstuwz}~{xussvz~}|{{zyyxxwwwxxyz|}}zwutux{}zxwwz~{xusqqsvz~~|{zyyyyzzzzzzzyyxxxyz||ywutuvy{~~}|{zyyxxxxxxxyyz{|}~~zvssux}}||{{zzzzzz{{{{{zyxwwx{~}}}}|xusstx|}yvtttvxzzywusrrsuwy{|}~}xuux}~{ywvuttuuvwxyz{|}~~|zyxwvuvwz}}zwutrrqqqqqqqqqqqqqsuw{|zwtsrsvz~{xtpnnoqsuwxxxxxxyz{|}}xrnkjmrz~{yvtsqpoonnnnoopqstuwxy{|}~|zz}|xtqonnmkijlou~~{yxy|}xtromlkkklnt|~xrlhefjpv|~zwspnmlllmnprtwxz}|yokklnoqrstvy|}{{~~~ytqnmjfbacfijkkkmnnmlkklnrvz|~}~{yvsojd_[Z[]afkpty}}xqjhkrz}{zywvutsrqonorutrv|~~|yvtqpnlkihikosw}}{xvspnmmosw|~|zxwwxz}zupmllmnoonmkifcbeihgmssty~~~~|~~|zwutsrrssssrqppoopqtvy|~|zz{|{vwzutwursux|~|zwuttuwy|~{vqnkknszyx~{uwvsoljiihhijjkkjjjiijlptx|~~~}|{zzzz|}|xyztwwu{}{xvuttux|~}|{|||}||{zxwutuz~~}}~~{wusqpoooqtwz|~~~}}|zywtrqonmmnoppmlrpsvtz}}|||}~{zz{}~zvqmjhhiknprstvwyz||||{zxvuuxytxuwuruvwwxxyz{{zxvutvy}~|{zz{ysxxz~}}}|{z{{|z~}{ywtromkjhgffghlqv|}||}~~~}}zwusrqqqrtx~{{}|{}|xwuuuuuvwyyzyxwwy{~{xuux|~}|}~~}{|}~|yvsrrtuwwwvuuvxyzzxurpnnnpqrrrrtxuuwvy{xxyz{|}~~|xuttvx{|{zxwvuuvwy|}z~{{{z~}}}}~~~}{xtqonortvxy{}~~|xw{wx{{|zwtqnmlompy}{wtqpprsuusrpprvz}~~~}~|yvtux|{yxwvutsqomlstuxyzuutttuvwz}~}zyxyz{zxvtsrrstvx|~}}{{}zywy{}xussuwxxwvwy{}}|yuqonnoppqqqpolpsqssurux{~~|{z}}xtstwz|}{yxxz}~|{z|~|{~}~|xutuw{~~}}}{zz{||}}}}|yy~~~|zwutrpppvxvz||~}|zwuttux{}|xtpoquz}~}}~}zyy{|}}}~|zyyz{{{zzywsrwuvwtvyyzyyxxxz}|zxvvvvvvvwy|ywvvxz{zxvuvz}}yvuw{|yxy}~|{|}{ywupnsqssrwz|~ywx{~}|{{{z{|{vsrrrrpnnosz}{{{|~z||||wxxxxyz|~|{}}|~}{xvuuttqsyx||}|}~{xxy|}{zyzz|}~~{yxy}}{|}~}}}~|{z{{|{xtqopsx|}|{z{}~{wsrstwy{|}~}y~~{xutssrrqqqy{z~}{z{~}{yz}~~}}~}|||{{|}~~|yvttw{~|xtywwxux{{|{{zyyz}~{xvuttttux{yxy|}~|zwvx{}|||zyxwvvvwwvspmlmqw|}|}|}~{xusrrstuwy~}yvttuxyzxurrtw{}|{yxy{~~~}}}}|}}}}~~|zv{}{}}{z}}~~}|||~~{yz|{xuuwz|~}{ywuttstsuw~~~|{|~~||~~~}yvuwz~~}}zxxxyyzzyxvstzwxxzvvyyzzyyz{}}{|~}{zyyy{~||}}zxy{~|zyy{~z~}}~~~~|yvrqqtx|~~}}}~~~|z~|}~~~~}}~|yvussstv|}y~{xwwy{||zwtrtvy||zxvuvy|~{xwx{~|zxw~zyzy|vvwxyyz{}}~}zyy{}~}{xvtrqrrtw}{y~|||~{yxy|~zwtstvz}~}||~~{yxwwwwvusrszwwyx~zxz{||}~~}}~|zyyz|~~|{{}|zyz}}|}|{}~~{~}}~~~}{xurqruy|~~}||~~|{{||}}}||~~}}|zxvvvxz}yw}|~}}~|zyyz|}zxxz}}{zz|}}zwvvx{~}}~}~}{{z{{|}~}{z|~}zxxy{}}}|zxvtttuxzzu{~~}|}~~|zvsqprux|~~}}}{zzyxwwx|y{|{{~{zzz{}~~}}|zz|~~}{zyz{~}zyz}}{z{|~}{y}|}}yy~~||{{zxvtrqruz~~}|||{|~}}~|yxxz|~~~~}|{{|}z~|{zz{}~{xwwy}~|zz{|~~|z}~z~|zxyz~}||{{{}{~|~~|||}~|yxxz}~{ywwy{}~~}|{{{|~y~~~~|{}}|||~~|z}}~~|ywuuvy}~}|{{{{~z~~~}}}}}~}||~~{zz|}~}}}~~x~~}~|yxy|}{zyz|}~}{~|zxuttux|}|{{{{~x~}}}z~~}zyyz|}~~~}}}}~}|x~}}~}zxxy|}||}~~~~~}z~~}{yvtstuy}~}|||~~x|~{~}}|yxxy{|}~~}}}}~~|yx~}}}~}{yz|~}}~~|}~}zz}~}|zwusstwz}~~~~}~|~}}~||}}zxxxz{|}~}}}}}}{xv|{~~~}|}~}||}~~~~}{|{}yz{}~~}{ywusrstwz~~}|}{{|{{}}zxwxy{|}}}}|||{xuu~~z}|{|~}|{{||||||{|{|yyz|||{yxvtrqrtwz}~||~{~z~|{{}|ywvwxy{{|||{{{ywuv~|~~|{|~}|{{{{||{{{}z{~ywxzzzyxvusrrsux{{}{}}{z{||yvuuvwyz{{|||{zwvz}~|{{}~|{{{{{{zzz|y|{{wwxyyyxwvuttuwz}{{~{|zyy{}}zxvvwxz{|}}}}|zxvy{|{{|~}{zzzzzyyz{y~yzxxyzzyxwvutuvy|~~{{{{}{yyz}~{yxwxyz{||||{zxvw}~}}{z{}~|{zzyzzzz|z{zxxyyyxwutttuwz}~~}{zy{}|{{}}zxvvwxyyzzzzyxvvy||zz{}}||{{{||~|}||zwvvvvuttsstvx{~z}~{z}}|{|}~{xvuuvwxxyyyyxwvw}|}{z{|~}}|||}~zx|wuuuuttssssuwz}~|||}|}{zz|~{yvutuuvwxxxxwvuv{{}||}~}}|||}~yx{~yvutttssrrsuwz}~~~~}}|~~|{zz{}|ywutttuuvvvvuttv|z~}|}}}|||}~x~yyzvttsssrrrstvy|~~~}{|}{zyz{}~|yvusssttuuuutttw}z~||}}}||}~|xxyzvtssrrrrrsuwz}}|{{~~|zyyy{}~{xvtsssstttutttuy{}||}~}}}}yyvz~yvtsssrrssuwy|~}|~{|~}{yyyy{}}zxvttttuuvvvuuuw||}~}||~~}||}~}w|{w~}yvuttttttuvx{}~~}~~}|z|~|zyxyz|~}{xvuuuuvvvvvuuux}z~~~|||~}}||}~|x~{y~ywuutttttuvx{}~}~}~}~|z}~|{yyz{}~{xwuuuuuvvvuutuw}y~}~|{|~~}}}}}y~{y~yvutttssstvxz}~}}~}}}}|z|}|zzz{}~{xvutttuuuuutttv|z|}~|||~~~~~y}}w~~yvuttsssstuwy|~}}}~}|{}~|{zzz|~~{yvuttttuuuutttuy|y}~}}~~~~~zyxz{wutsssssstvx{}~}}}~~}}|~}{zyy{}|ywutssttuuuttstw|y}}~}~~}}}~~x{}v|~yutssrrrrstvy|~}}~~}||}{zyxy{}~{xutsrssttuutttux~y~}}~~}}|}~|w|yv~|wtsrrrrrrsuwz}~~~~}~{}~|zyxxy{}|yvtsrrssttuuutuw|}}~}}~~}|||}~~xx}uz}xusrrrrrrsuwz}}|{{~}{yxwxy|~}zwussrsttuvvvuuwz|~}|}~}||||}yv~vwzvtsssssstuwz}}}|{}}{yxwxy{}~{xvtssstuuvvvvuvz{~}|}~}||{|}zv}xw|wusssssstuwy|~~}}|{}}{yxwxy|~~|yvtssstuuvvvuuvy{~~||}~}|||}~zwwx{wtsssssstuwy|~~}|{{}}{zxxyz|~{xvtssstuuvvuuuvzz~||~~}}}}yxvzyvtsssrrstuwz}~}}}|{}}{yxxy{}}zwusssstuuuuuttv|{|}~}}~}}}~}w|zv~|xusssrrrstuxz}~}|}~|~~|zyxxy|~|yvtsssstuuuutttw}y}|~~~~}}}}~{w}yv~|wtssrrrrstuxz}}||}~|~~|zyxxz|~~{xvtsrssttuuttstw}y~|~~}}|}~{v|yv~|wtsrrrrrrsuwz}}||}~|~~|zyxxy{}~{yvtsrrsttuuutttv|z||~~~}|||}|vzzu||wtsrrrrrrsuwz}~}}}}|}~|zyxwxz|~|ywusrrsttuuuuttvz}z~~~}||||~xwux~yussrrrrstuwy|~}}~}|~}{yxwwy{}}{xvtsssttuvvvuuvx~z~}zvtvy|zrlheccehmt{|yyyyz{|}}}}}}|{yvrrw|w~xqliikov}}wplhfffghjklnqv~{}{xvsrqsvz~~zvrpopsx}}x|tpprttuttrqomljiijlnprsrqomkjijkmoqsuwxyz}|yvsqooopruy~~z}~}|{zyyz{}~~|ywwwwy|~|z~{}~}}}~|xusqpoooonmlkjjnswsnv|vv}~}}~xu|~uu}}xusrrrrrstuwz}~~zvx~}sv|wqpruvwxxxwvvutttvy|~~~}|zzyyz{}~{upvypnuvqmjihhggghikmptvxyzyxwwxz}~~~{xvuttuuvvvuutux}~w}|y}{xusstuxz|}}|{yxwvvvvxy|ywz{}{{{||}}~||}~|zxusqppqsuvwwuspmkihhhijkkllllmqx|xt|zz~{~}ywvuutttuuwy|~}{yxxy{}{wuzyt|~wstwz{|||{zyxvuuuwy|}{zyyz{|}~~|zyxwwwxz{ytnrxplsvrmkiiiiijjlmpsvy{|||zyyyz}}zxwvvvvwwwvuttw|~wz}x}}zxvvvxz|~~|{yxxxxxz|zv|yx~{yxxyyyz{|~~|~~|zxvtrqqrsuwxxwurpmlkjkllmnnnnnorx}{uz}y}||ywwvvuuuvwy{}~|zyxyz|~|xxyx~xvxz|}~~}}|zywvvvxz}~|zxxxxyz{|}}}|}}{yxxxxy{|{vpszsntxtpmllkkllmnortwz|}}}{zyz{}~{ywvvvvwwwvvutuyzwyz~{yxxy{}}|{zyyyz|~~xwwz|ywwwxxxyz{}~|~}{ywusrrsuwxyyxvtqomlllmnopppppqsy~}vz~z|~y{xvuuutttuvxz|~}{zyyz|{y}x|xx{}~~}|zywvvwy{}~|zxwwwxyz{{||||}~|{yxxxxy{|zupuzrovxtpnmmmmmnoprtwz|~}|{|}~}|}~~{ywuuuuvvvvvvuuw{zyz||zyy{}~|{zzyzz|~}ww}vz{xwvwwwxxz{}~}}~|zxwwwxz|~}|ywusssstuvvwwvvw{{~}|{{{|~~xu|~uv}}yusssssstuvxz}~~}}~~|zywvwxz|~~|ywuuwwvtrqpprtux}~~ytx{rpwwtpmmlmmmnoprux|zwyz{|}}}|{{yxwwwy{|~~|yvsqpoooppqqqqqsw}|}|||}}ysovyqr{|zxwxxyyzzzz{|~~|zxxy{}}z|z}yy|~~}|{{{}}{yyyyzz{{{{zzz}{}{~}|{{|}~{tt{yqu{yurpppppqrstwy|~~|}~}{ywusrrstwy{||{zxvuuuvwyz{|||{|}~}|||}~{vzyu{~zvtsrrrrrrstvxz|~~~|{yxxyz}~}{{|}|zwvutuuvvwwwwvwy~z~}}}}~~|{yxxwwxz|}|wsx}ut{}zvuttuuvvwy{}}{ywwwwy{|}}|zxusqppppqrrrrqqrty}{uz|x}z|y~zwvvvuuvvwxz|~}|}~~~~}{y{}~~~}{zxvuttuwy{}}}|zwvttttuvwxyyyyy{}||{|}ztw}wrx{xtqpooooopqrtwy{}~~}|zyyz|~~}|{|}~|zxwvvvwxxxxxwwx|}{}~~||}~}{zyyyyz|~~|vt||tv}|yuttttuuvwxz|~|zxwvvwxz{||{ywtrqpppqqrssssrsuz~x{~zy|}x}~zxvvvvvvvwxz|~}}~}|~zz|~~}{yxvuuuwy{|}}}{ywutttuvwxyyyyyz}~}|{{z{|~}xsx|ury{wsqpppppqrstvy{~~||||~~}{zz{|~}{ywvvvvwxxyyxxxz~}|}}||}~}{zyyyy{|~~{uv}{tx}|xvtttuuvvwy{}~}{ywvuuvxz{||{yvtrqqqqrsttttttux~~y~}~~~{xxy|xvvvvvvvwxz|~~}~~}{z|~~}|zywvuvwxz|}~}{ywvtttuvwxyyyyyz}~}|{{{{|~}wsy|tsz{wtrqqqqqrstuxz}~}||}}{zyz{|~~|ywvuuvvwxxxxxxx{{}~}~~}{zzzz{|~}wt{}uv|}yvttttuuvwxz|~|zxwvvwxz|||{ywusrqqrrsttuuttvy~}y}}~~~yy}vy~}yvttttttuvwy{}~~~~~|zxwwxy{|~~~|zxvutsttuvvwwwwwy}|}}~}||||}zvyxu{}zwttsstttuvwy|~~}}~~}{zxwwxz{}~~~|zxvtssttuvvwwwvwy~{~}~}||||~zvz~wu|}zvttsstttuvwy|~~}}~~}{yxwwxz|}~~}|zwvtssttuvwwwwvwy~{}~}|||}~zvz~xu{}zvtttttttuvwy|~~}}~~}|zxxwxz{}~~~|zxvutttuuvwwwwvwy}{}}~}}|||}{vyyuz~{wuttttttuvwy{}~}}~~~|zyxwxy{|~~~}{ywuttttuvvwwwwwx{~{~~}|||}~}xv}}uw}|yvttttttuuwxz}~}}~~}{yxwwxz{}~~}|zwvttttuvvwwwwwwy~{~}~}||||}{vx~ztz}{wuttttttuvwy{~~~~~~~|zyxwwxz|}~~}{ywuttttuvwwwwwwx{{~~}|{{|}~yuz~wu{}zvutttttuuwxz|~~~}{yxwwwyz|}~}|zxvutttuvvwxxxwxy}}}~~}|{{||~|vv}{tx}{xutttttuuvwy{~~~~~~|zxwwwxy{}}}|{ywuttttuvwwxxxxy{|~}|{{{|}}xu{}uv||yvtttttuuvwy{}~~~~|zywwwxy{|}}}{ywuttttuvwwxxxxx{|}||{{|}~~yuz}vu{|yvtttttuuvwy{}~~~|zywwwxy{|}}}{ywuttttuvwwxxxxx{|}||{{|}}xu{}vv||yvtttttuuvwy{}~~~~|zywwwxy{}}}|{ywutttuuvwwxxxxx{|~}||{||~|wv}{ux}|xutttttuuvwy{~~~~~}|zxwwxyz|}~}|zxvutttuvvwwxwwwy}|}~~}||||}zvy~xu{}zwutttttuuvxz|~~~~}{zxxwxy{|~~}|zxvutttuvvwwwwwwy~{~}~}||||~zvz~wu{}zvutttttuuvxz|~~}}~}{zxxxxz{}~~}{zxvutttuvvwwwwwwz~{}~}}|||~zvz~xu{}zwutttttuuvxz|~~}}~~|zyxxxy{}}~}|zxvutttuvvwwwwwwy}|}}}}|||}{vy~yuz}{wutttttuuvwy{}~~}~~|zyxxxyz|}~}|zxwutttuuvwwwwwwx|~{~~}|||}~}xv||uw||xvtttttuuvwxz}~~~~}{yxwwxy{}}}}{ywvutttuvwwwxwwxz~|~~~}||||}~{vx}yuy}{wutttttuuvxy{~~~~~|zyxwwxz{|}}|{ywutttuuvwxxxxxx{|~}|{{|}~~yuy}wu{}zwuttttuuvwxz|~~~~}{yxwwwyz|}}}|zxvutttuvwwxxxxxz}~}~}|{{{|}{vv|{ux|{xvtttuuuvwxz|~~}|zxwwwxy{|}}|{ywuuttuvwwxxxxxy||~}||{{|}~}xu{|vv||yvuttuuuvvxy{}~~|zyxwwxy{|}}|{ywvuttuvvwxxxxxy{|}||{{|}~}yuz}vu{|yvuttuuuvvwy{}~~|zyxwwxy{|}}|{ywvuttuvvwxxxxxy{|}||{{|}~}xv{}vv||yvutttuuvvwy{}~~~|zyxwwxy{|}}|{ywuuttuvwwxxxxxy||}|||||}|ww|{ux|{xvtttuuuvwxy{}~~~}|zyxwxyz|}}}|zxvutttuvwwxxxxxy}}}~~}||||}~~zvy~xuz|zwuttttuuvwxz|~~~~}{zxxxxy{|}}}{yxvuttuuvwwxxxwxz~|~~~}}|||}~yvz}wu{|zvuttttuuvwxz|~~~~~}{zxxxxz{|}}}{ywvuttuuvwwwxwwxz~{~~}}|||}~zvz}xu{|zwutttuuuvwxz|~~~~~}|zyxxxy{|}}}{zxvuttuuvwwxxxwxy}|~~~}|||}~{vy}yuz}zwuttttuuvvxy{}~~~~|zyxxxyz|}}}|zxwutttuvvwxxxxxy|~|~~}||||~}xv||uw||xvutttuuuvwy{}~~~~}{yxxxxy{|}}|{ywvuttuuvwwxxxxxz~|~~}||||}~~{vx}yuy|zwutttuuuvwxz|~~~~|zyxwwxz{|}}|zywvutuuvwwxxxxxy{|~}|{{|}~}yvy}wu{|zwuuuuuuvvwyz}~}{zxwwxyz{|}|{zxvuuuuvvwxxxxxyz}~}~}||{{|}~~{ww|zux|{xvuuuuuvvwxz|~}|zyxwwxy{|}}|zywvuuuuvwxxyyyyy|||unklostvyroorqkovvsw~}~}{{wrrsqppoprrorvw}||wupmkkmrw{~~wrvzutwwwsnoonnnmnprrv|{|~}zvtqpqolprqqruwxxvwyspqrvwqs{{xw|{z{}}{xyysqpnmmnquz|||{yvussuutx|{|}|yusppqonststvx{||{}~xvvw{{ux~}xy~~{~{}~vtxvtvtqpqsuvvuwvqqqoooqsx||}z{{xx||||~zyy|~zv||x|}z~~|{{zxxwvwwssxxy||vtyuoqsqrsqooqtvvuvyurrqppqsvz|}|{{{yy{x~zy|}}~~xwwvutsstspqvvwzystxsnqrprsqpqsvwwwxzvstsqrrtx{|~~||}zzzy~yx|||}~|vvvutssssroqvvwzxstxsorsqtusrrtwxxxz{wuusrssux||~}|}{z{x~yx{|||~~|wvvuttsstsopvvwzyttyupstrtvussuxyyyz|yuutsstux||}~|}}y}x|~{wz|||}~~~xvvutttsttqouxxzzvsxysruttwwutuxzzyz}|wvvtttuw{}|}|}z|yy~}xx{|||~~~{wvvttttttsorxyy{ytv{wstvtvxwvuwz{zz|~zwwutuvwy||}~||~~y}}xz~{xy{}|}~~~zwwvuuuuuurptyyz{xtx{wsvvuxyxvvy{{z{~~zxwuuuvw{}|~~|{|{y{x}~}zxz}}|~~}ywwuuvuuvuqrx{z{{vu{{utvvwyzxvx{|{{}|xxvuuvwy|||~~}{{}~y|~xy~|xy|~}}{xwvuvvvvvsqv{{{|yuy}xtvwwy{zwwz||{}~zxwuuvwx{}{~~|{|z{xx~|yy{~~}|xxwvwwwwwtqu{|{|zux}zuvwwy{{xxz||{|~{yxuuvwx{}{~~}|{{zzxx~|yy|~}|yywvwxwwxtqv|}||yuy~{vvwwy||yx{}|{}~{yxuuvwx{}{~~}~|{|~yz~xy~}}}{y{z{~~{{{zz|{x|~|{||z|{wx|}|zzzywvuvy~}{~{~zux~{}}~}{xxyywxvrsz}|{|vw~~yvxyz|~{xz|}zz~}zwvrprssuxvw~~zyxz{wtz{trx~~|~}~}x{~xzxwyzz|~zxz}|y|}{ywsrtvux{x{~~}{zyz}|z|~}z|~{xvutwxtsyzssz~}~}}}}~|}~}~}{wvxzx{}{}~~~{}~}~{~~||}}ywvvvy|zxy{{xy|zwuutvxvsy|utz}~}}~}~~ywy{z||}~~}}z{~~|}y{|~{y{|yvuvvw|zxxz{xx}{ywvuvyxtw}xty~~}}}~~~|xy|{{~}{~zy|~{|zy~|}}yz|{vuuvvz|yxz|zw|}zxwvuxzvu||vv}~~}}}~~}}~zxz}{}}~~~{}yz}~{~~y{{}{x{|yvuvvw}|xxz|yx}|zxxvvyzvw~{ux~}}|~}}~~yx{}z~|~|||y{~}{|y|}{~yy{|xvvwvy~{xy{|xz~|zyxvxzyuzxv{~}}}|}}~~~}~{wy}{{~}~z~yy}{}zz{}|x{}{vvwww}}yy{}zx~~{zywwz{ww~|vx~|}|~~}~|{~}}xx||z|z|{y|}}|z~{|}xz}|wvwxw|zyz|{x}{zzyvy{xv}~wx~~}|}|}}|{|~xw{}y~|~{{|y|}|}z}||~yz}}xvwxw{{yz||x}{{zyvy{yu}~wx~~}|}|}||{~|~~ww|}y~{z|{y}}}|z~{|}x{~}xvxyx}{z{}|x~|{{yvy{yv~}wx}||{}~|~|{}||vx}|y~{xzz~|{|z~|x||vvyyxzz{}zy~|{{xw{{wx{w{~|{|{|}~|~{}{}yvz~z{|}~|y~y||{}~{yz}zvwzx{}zz|}x|}||{wx{{u{xw}~}{||z~{~~|z~{~~xv{~y}{~}{z~{~~}~{}{yz|{}}z~~~|yz}|w~yy~|{|{z~}z}}}z}~~~w||y{~~|}vuy|w{~z|}|z{~~}~{||yz|{~}}~z~~~}yz}}w|zy~~}{{|z}~z||z|}x{~y{~|~vty}xz~z{|}x{}~}}}{~yy|||}}~{|~~~zy|~yy|x|~{z|y~}z{|{{|{xyy}~~|ztv|{w~|z~~y}}{~}z~zy{}z~}}~z~|x{~|wyz~|z{{z}}z}|~z}}w|}x{~~|~vty}xzz{|}w{}~~|}zyy|}|~}}|{{x|~zx}x{~{z|y|}~{z~~|{}~}w~{y|~~{}uu{}w|~y||{y{~~}}{|}xz}{}}}}z~yy}~x{{y~~}}zz{y{z||{||yyyy~}}xtx}zx{z|~w~||~~~|z~yy{}{~}}}z|x{~{w~x{~zy{y{}~|z}}|{}}w{y|~~||tv||w}}y~~}|xz~{~}z}{x{~{}|~~z}xz~}w~yz~~{y{zy~|}z~|}{~}~~w}|x{~~|}uu{}w|~y|~~{zy{~}~{}{xz~{}|~~z~xz~}w~yz~~{y{zy~}|z~~|~{}~~x}|x{~~||tv|}w|}y}~}{xz~|~}z}{x{~{}|~~z}x{~|wy{~~zyzyz|}|z}}|||}x{y|~~|ztw}|w~|y~~{|w}|}~~|~{yy|}|~}|||{x|yy|y}~}~yy{x~{z|{{}|yzyz~~|wtz~yzy{}~~y{w{}~~||}xz~|~~|}zyy~~x|zy}}|xyzx{~z}{{~}x|~x{~~|~uu{~x{~y|}~}zzx{}~~{}|xz~{}|}zyz~~w}yz}}{xzyx~|}z~{{~~x}}x{~~|~uu{~x{~y|}~}yzx{}~~||}xz}|~~|}zyy~x|zz}}|xyzw{~z}{{~}y{yz~~}vuz~yz~yz}}}y~|v|~}~}}{yz}}}~}||}{y}zy|y}|yxzw|{z|{~|}||yzy}~~~ztx~|x~|y~~}~{{w{}|~}{~{y{}{}|~~{~x{}x~y{~}zxyyx}|}z~{|~y|~yz~~~}vu{zz~yz}}}y}|v|~}~}{yy|}|~}||||x||x}y|}yxzxz|}|{}|}|}~y~|y{}~}}uv|~x|~x||}|yzw|}~||~yz}|~~|}{zy~y{{y~|}xxyw~zz}{|~}{zzz}~~~xty~{y~zy}|}y|~w}}}}}{~zy|}|}|}||x||x~y|~}~zxyxz|}|{}|}|}~y|y|~~}|uw}~x}|x}~||zyxy}~~~{}|y{}|}|~~{~y{~x}y{~~}{xyxy}|}{~{~|~y}~y{~}~}}uv|~y|}x{||{yyx}}~~|}|y{}|}|}~{~y{~x}y{~~}{xyxx}|}{~{~|~y~}y{~}~}|uw}~y|}x|~||zyxy}~~~|~|y{}|}|~~{~y{~x~~y{~}}zxyxy|}||}|}}}~z|y|~~~~zux~}y}{x~}||y{~w|}~}~~|zz|}|~}|}}{y}{y|y~|~xxyw}{{}{|~}{{zz}~~~~wv{zz~yz|{{x~{w}}~}}~y{}|~|||zz~y|zz~||xxxx~{~{~{|~z}z{~}~~~vv|z{}x{|{zxzx}}~}}}y{}|~|}|y{y}yz~~}{xxxx}|~{~{|z}z{~}~~}vw|z{}x{|{zx~zx}}~}}~z{}|~|}|yz~z|zz~|}xxxw~{{~{|~{|z{}}~~~wv{{z~yy|{zx}|w}}~~|zz}}}~|||~{y}{z|y~|yxyw|{|}{~}~}}{|z|~~}zuy~}y}{x}}{{yyxz~~~~~|~|z{}|}{}~|~y{~y}~y{~~}{xxxx}|~|~{}{}z{}}~~~~wv|{{~yy|{zx||w~}}~|{z|}}}{~}}|y}}y}y}}~zxxwz|}}|}|~}~z~~z{~}}~|vw}z|}x{{{zx~zx}}~~}~z{}|~~|||zz~{{zz|~xxxw}{|~{}~|{{z}}~~}xvz}z~zx|zzxz~w|~}|~{z|}|}{~~}}y|}y~}y|}}zwxwyzy|{|}}yy}z}~trssnnrwz{sptqqrspqw|{|}{wuuvsy|zzxrlpuvrturu~vsvsrsspqw}||{zyvx{uqnoqotxwxwqlnuwrstoqz|wzvtx|zqmlkkrsrtsrmmttuwyww|}|zxvuzywwusmmsrsuwtuz||~~xqsxxstsnqy{vyuuy~{sppoqxyyzyytu{{|}~{{}|~{~}~|wz|~yx|{ronmntttutsnpuuvyywx}||~~z{yyx{~y}}~|~}~~}}|~~~~|z}|}}usw{xtwrov~xxxsvz|unmmlqvtvvvsouxxz{zx{z~~}}|~||~~~|{~}yz{z}~zwxzx|~~|wppuxsrsnov}|uwurw|{sonnntwvyxxtryyz{}zy}~{~~||~}~{{}{xyzy||xvwyw{~}}zuopuwrssmox~{vxuty}|tpoopwxxzyyttzz{|}{z~}{}~||~}~{z}|xxzx{|xvvyw{~}}{vopuxsstnow~}wywty~~vqpppvzy{zzvtz{{}~|z}~{}~||}~}|z|}xxyyz~yvuxwy~}}|xqotxurupnu}yxyux}ysqrqu{z{|{yty||}~}{|{}}~~~}}||z{~~zxyyx}{xuwxw}~}}ztorwxstsorz}x{ww|~vrrrsz{{}|{ww}|}~|{~{}~|~|}|}z|~}yxyyyzwvxxy~~}zsptywsurou}|z{wx~}ussru{{|}|{vy~}~||}|~~~~~|~|~}}{z}|xyzy{}ywwyx{~~|xqqvyvtvqqxz{zw{zustsx||}}|yv|~}~~|~|~~~}~}}|||~z|~~zxyzy{xvyyy~~{uqtyxtvtpu~~{|yy~~wttsv||}~}|wy~}~|}}}~~~~~}~~|}|~}{{~{yyzy}}ywxzy~|wqtyzuvuqt|{}zy~xuttu{}}~~|xx~~~}|~|~~~~}~~~~|~|~}~{{~|yzzz}}zwxzy~}wrtyzvvvqt||}zz~yuutu|}}~}xy~~~}}~|~~~}~}y}|vw~{}{}~~~}||xvwyy~{}zvvuv|||~|zvw|||}}z{}|}|xvxyz}{}~~~wtsru{z{|{yuy|{|}|y{~~yz|yz||z}{xy||wwxxy~{xx{{}{y~}|~}~~zyyyz}ywwzz~zz}~}{zz~zx}~}}}{y|~|x{zxz~{{~zz|}yz{{}}z{~}~yy~}}}y{}|||~}}{~}{|||~yy{}{wxyx{}zxy|{~yz{~~~~~}}~~}}~~~|}~}xz~|||wz~zyxx~~zy~~~~~{z}~{x{zxz}~z{}yy{}{xxzy}~{yz}|~xz~~~z}~~~~~}~~xy}|{{wx~~ywww|~~}yw|}|}}{y{~{xzzxz}{{}zy{~|yyzz}|zz}|~yy~~}~z{~}}~}zx|}z{xw}}ywvvy~}~~}zwz}|||{yz}}yy{yy|}z}|y{~~zz{{{~{z}}~{x||~{y~~}}}|xz~~z{zvz~~|wvvv|~~}|xx}||}|zy|~{x{zy{~||~zz}}zz{{~}{{~}~yy~}|}y{}}|}zx{}z{xv{}~zwvux}~~}{wy}||}|zz}~zy|zy|{}~z{~|z{||}{|~~}xz|}|y|~}|}~~~xx||zzww~}~ywvvz~}~}zw|}|}}|z|~}y{|y{~~||{}{{||}~|{~~zx|~|}zy}||~}{wz~}z{xv{~~{wvvx}~~~|xz~}}}}z{~{z|zz}|~~{|}{|||}{}~~|x{|}{y}}||}}~|xy}~zzyvy~|xwvw}~~~}yy~}}}}{{}|z|{z}}~{|~~{|||}{|~~}xz~|||y|}|{|}~|xy}~zzyvy~|xwvw}~}yy~~}~}{{~|z}{z}}~~{|}{|||}{|~~|xz||{x|}|{}~}~|xz~~zzyvz|xwvx~~}yz~~~~}{{~|{}{{~}~{}}{||}}{}~{x{~{|zy~|{{~~~}zx{~|z{xw}~zxww{~{y}~~~|{}~{|}z|~}||~||||~~||~~~yy}}{|yz~|{{~}}~yy}{{{xz{zyy~}|~}}~}~~}}~~}}}yy}{{{xz{zyz}|~~}~}~~}~~~~|}}yz}~{{{x{{zzz}|~}~~~}~~}}}yy}~{{{x{|zzz~}|~}~~~}~~}}~yy}~{{{yz|{zz~~|~}~~~}~}}~zy|~|z{yz|{zz}~|~}~~}~}|~zy|~|z{zz~}{{z||~~~~~~~~|~|y{~}{{zy}~|{z||}~~~~~}~|}}yz}}{{{y||{{{}}~}~~}~}}zy|~|{{z{}|{{}~|~~~~~~}|~|y{}}{{{z~~||{|}}~~~~~~}}}yz}}{{{z|||{{~~}~~~~}}~zz|}|{{z{}||{}~|~~~~~~}}~{y|}|{{{{~~||{}}~~~~~~~~~|~|y{}}{{{z}~}|||}}~~~~~~~~|}}z{}}{{{z}}|||zy|zz}z{~|{~z}|~~zy~{zz||~~|zzz{||{{|||||}|{}}|yxz|~|vyz}y|~{z|{|}}~~|xw|{~~z~wx~~}}|yx|~{|z}z|yz~v|}z{wy{{yz}~vsx}v{}~}|z}}~yux{y}z~yyzzz|zy}}y{}t{{w}}xy~}}wty}uzy}|}z}}xz~}~y}{|}zxz}|{~}{uv~|v}{x~vw}~}~yrtz|{zy~~~z}wuzwyz|}y~}z~|}|wqs{usuw|~|~ww~|v}y~|yw}~}}vqw|tv{vv{}{~{|~ytx{x}ywx}~|}}xx~ty{v{~~|~|wry~vy{|~vz}}|xsw|yz~|{uz|xyuz}x||w|~~~~{wzvv||z}su{zz{su~}x{~~|y}}}~~~wu}z|zy~rv}|z}zx}yzyt||z~|xx~|~ut|v{||y}|z~{z}~~y}~||yuv}|}~~vv~u{~z}~zu{{}|y~~yx~||~z{{~{~~ww~v|z}}~x{{}~{zvqv}w{uv~|}~xww}z~{|w}|y~vrxvu|yz{z~~|}~y~wu}}w~{y}xv~}yzzz}{wz}yw~{}~}vy}sr|~wy|~wu}zx|y}}uv{~~z~wywx||~~||wuyxyx}||y|}~|zxywu{zy{zvo{}z{~us||{zx}~{~|~~|{}}|}z~xuvs~zvzzuy|uzu{}~~vx~}}~{y}~}~|||xy~{w|yv~~wy||wx}wy{~|vvzux}v}|ux~zwxx~{vz|~}z}}}z|||{|vy~}yzyuz~uyrq{}{}yvs}~~~~{z~}z{}t~}sy~z{~wqtwvxzu{xz~}|uwwwz}x~zuyz{~v{~y||uw~}~~{xuxw}}yyw{}{yruwox|w|}xv{}{}~yyxx|~|~~}yzvu}}}}|uzzvxwquxow||}xru}{z}wx{~|}}~|{uw~|v~sxwv||utuurt|}xux{z}x}vu~~||z~~y}yz}zu~}}}wwtv{}}xwvqyuuy{z}|z}}qp{|{|}y}}|{yz{|ztx}z{~}zzwu{wq}svzww{|z}woq|}~zyz|yx}vwvu{~wwyvr|x{ywyz|}}|wurpuz~yy~~~{{~vw{x~~|}|zxtx}vvvwwvzz|{qs|~vrw|~ztu~}zz~{rowyy|uy|xz}ysrtsyzyz~{tvzyy{}|wvz|}~}}~~~~|}~}wxu}~~~x|tx}w{ytstx~}{}}zv|{{zzurtz}{v{{{z}}vy}{uxxy|~{wvxss{zv}wv}y{~wv{{|{xzxzy|utzvw{z{|z}|~~|t|sq}|s~|z|{~vuqu{|{xv|ut}z~~x~|xz{w~wn|~s|{z}|}vzzv~{wu{~z{~{~|yy{}}|ozymvysy~{~~~~w|rx~~|z{|xxy|z{}wvyxos|y}~{{{~}tsxv~z|~{y}||}~xz{r{xx}uxw|{zy}zw~wzsry{{}|}y{{{yp}{}yzv{y||}~}z{~y}{vz~x|||vwy{~pyx{yy{~sy~z~|{{}~{{y~x}||v~{zzx||u}wy}st~|zyzvvy|~}yyu~||zz|~}~xyyt}uu|z~{}}~zxxxw{zwv{|w}{||}y{|}}~r~zy|y}u~|z{}w{|uwy{xxw~~|}~y{~}{}|t~vv}~xy{v~|ypy}~vx~|}|yt}z}|xv~|r}~tlsttyywxzsv{wbemnr}yy~uyzu}sx|igpolttluyxv}u~|mormknel{z|stxpwzxgjrkgqkfsyyuto~untsqswqv|zvtzqu{zkdmopusktxxs|ytuytotniwzvr{rny~{w~{~~u}|rwwtpsrpuyypxvu~yktytv|qr{|y{y|tsxxz~~yy~~~}}~|~|y|sszyuvwpv|{sxunw}zosurptpt~}~v{}|~~~{x}~y}|}}|zwzzy}}||vr}|v~yz~}qmurosvkq{}y{|qz{qssqossq||}xzxs{~uz|}zsz||~z~xwwxyyxt}z}yyywmqsssyqluzyqxsqxztrwvsszru|}xtzsxrsyvswys}|y}y~}yxzwxw}wrwxwywjqzyvv|twxuorqopsrmw}~yyyw}~{qtusszyv}z~yw}y~~{y~}}~}~tquxuwxrz~z~utyworsssvqlv{xpvvrx|xsxwtw~vu~~{x~y|xx{wvx|w}~z}~y|~|~|t{}{z~{u{~{txxyvuooxyyuzwsvwtoppqrvqt~y||wuwvwzxz}z~y}y~~~~~~xz}}|}{}|quxwuxws|~~{~zpuzuostttvpn{zvtzvt{zxzvx|~xy~|z}yyzxwz~x}~}y{z~}{||uz{|{{{vz~{txzyssrpyzzx{xsvzwprstsxuy}z~|wuxxz~w{}}{~|}}y|~}}{{}~wqxxwvzxu}~}~uqy{urvuutuqu{wvwzvw~yxyz|}w{~|~z{zyy}~z~~}~|}y}|}|{}wu{}}{}xy~~wy|ysrwrw{{yyzsu|zpruutx{x}}|{vzz}~zz|}}~}~y{~}y{||wtyywwzyw~|yuuz|vswvusvuvywxy|x{zx{}}~~x|~~~}yzzzz~~z~~}{zz}~~||{|tw~~|{}y{}x|ztrwxtz|{y{wt{}tpvvvw}||~|xx|~~}z~}~}~}}~|~{{~~|~}~}z|}||~||}|~z}}{y}~z}~~||~~{{|{|~|z|z{||z|y{~~}}z{~~z}|~{}}zy~}~w{}|}|yxvv{{ww}~}{wz{wxzxw|}~z|~|yy}xy{~yz{zy{~||~xx{wvxvxy}{}{~vx{{z{~}~zw}~}{{zw|~|{yuxwyx{z~||~{y}|uuuwxyzv{}~}}}~~z{|zwvusz|{z}|}{}}}|}y|~zz~x{{zz|}y}y{}~||}z}}z{~{}}|}xz~~|}~}~yz}{x|zz{z~}}}|ytx|yxzz|}~}|}~y|}~}|x{}~}~{}y||~~||}x|||~||~~~~zz}{zvxvw||{zzy}{ywxwz}}~z{~~}}~{yy}{z|}{zxyz{{{|y|{z{}x|~||yvy|xx|y}||~~}~xx}{z|~~|~}~~{}|z~}~|zx~||}|{{zv|{y{}z{~}}~{}}}|~~~|{{|{ywxxx|~}}{|~|zw{z|}~{x~}}~~~~~~}~|wzz}~~~~zx|~ywxyyz|}~{y{{|~zx}|{yx|}z~~{|}|~~|y{z{~||}}}|}~~~y}~}~}{x}~{||{zyxz~zx{}{z~}}|z~}|~||~|{|yxxzy{~~}}}{x{}|~~zx~}~{~|}~}zw|{}}}}~yx~~wvyzzz~~~~|z{|~zz~}{|zy}}~}}~}|~}|||z|{{~{}}{}}~|||~~|z{~}z|{{xy{{zxz|}{~|~~z|~|~}z{|zzxz|z~|}{y{}}|}|y}~|~~~{~~{{~yx}}}~~}}~zy~~xvz{{{~}~~{{~|{}~}{|{{|~}~}}~{|~}~|}|x}}{}{{~}~}~}~}~}z}}~}{{|{x{}yxy||}~~|~}{|}}{||{{zz|||~~~~{|z{|~}{~}z|~~}}|}}|~~}~z||y|}~}}}|}|{|~|xw{|{~|~{|{}~}|{{}||}~~~~~|z~~~~}~zw~}~|~~{|~}|~~|z~~~{z}{y|~xwz||}}|}||~}}~zzz{||~~~}|{z|}~~{|{z~~~}~{|}~~}~~{{~~|y}~~|}}~}}~}{z{}~~~}}~~~~~{}}~|xz}|}||~zz|}|~}|}~~{{~~}z{|{}{|~yx|~~~~||}~~~{{~~||yz~~}{}zz|z}}}}|||~~|z|}~~zy}}||}}||z{||~~~}}}~}~{|}}~}xz}~}|{}}zz||}}}||~~}z}}}~{z~~||||~{xz}~~~||~~~z|||~}x}~~|||z|}z{~}}|||~~~z{}~~}}x|~|{}}{~~yz}}}~}}}~}|{~|~xz}~~~~}{|~zz|}}~}~{~~{}~|~~zz}~}|{|zyz}~~~~}{~~}z|~|}{y~~}~|}{z}{z|~}~~~}{}~~}z|~~~}~{x~}|{}|{|y{~}}}||~}|z}}}zx}}~}}|{{y{}}}}~}}}|}~|}zy~}}}}{{{yz|}~~~~|}~~}{}~|}{z}}}}}{{~~{y|~~~~~}|~~~~}{}~}}~zz~~}|{|{|{y|~~~~|||}z{~~~~y{~~~~}}~{|~zy}~}~~~~~{}~~}{x|}|}}z}}yz||}~~~}}~~}{}~}}|z~~~}|~|{}{yz}~~~~}}~~}{}~~|}|y}~~}{|{{~}zz}~~~~|{}~|y|~~{y}~~}}|}}|~|yz}~~~~}}~}{~}~zx}~~{}|{~}yz||}~~}}~~}{}~~}|y~~}{}}{~{yz}~~~}}}}{}~|||y}~~}{|{z}}z{|}~}||~~}z{}~~|y|~}}|}}|~~zz|}~~}}}~~}{|~~}~xy}~~}|}|}~{z{}}~~~}}|{~~}~zz~~}|}||zx{}~~~}}~~}z}||{y}~}{|{z}}z{}}~}||~}{{}~}}y{~~~}||}{}~zz|}}~}}}}|{}}~zy|~~}}}|||z{}}}~~|~~{|}~{y|~}|}||~}yy|}~~|}~~{|}|~~y{~~~|||z|~zz}~}}}|~~{{}~~}~yz~~~}||}||zz|}}}}}~}{{~}~yy}~~}}}{|{z|}}~}~|~~}{}}~zy}~}}|}{|{yz|}~~~|~~}{}}}{y}}}|||{~{z{}~~}|}~~}z}~}~{y}}|{||||y{}~~~}|~}{z}}~zy}}~}}|~|||y{}}~}~~}~~||}}~~yy}~}}|}||{yz|}~~~}}~~~|{~}~~zz}}|}|{|~zy|}~~}|~~~|{~~}~y{~~}{{|{}~{z|~~}||~|z|~~~}y{~~}}|}}|~}zz}~~~}~~}{}}~{x|~~}|}||~}yz|}~~~}~~~}{}~}{y}~~}|}|{~|yz}~~~}}~|{}~}}{y}~}}{|{{|z{}~~~}|}~|z|~}~{y|~~}||}||~|z{}~~~~}~}}{}~~{x|~~}{}||~}zz|}~~~}}}{}~}|y|~~}||}{~|yz|~~~}}~}{}~}}~|y{~~}|||{}~zz|}~~}|~}{{}}~y{}~~}||}|}{z|}~~~~}}{{}}~zy|~}}|}|||z{}}~~~}~~}{}~~{y|~~}||||~}yz|~~~}}~}{|~|~}y{}~~|||{}~{z|}~~~}|~}|{}}~zy}~}|{||||z{}~}}}}|{}~~~{y{}~~}||||~}zz|}~~}}~}{|~}|y{}~}||||~~zy|}~~}}}{{~~}~~yz}~~}|{|{|~{z|~~~~}}~~{{}}~zz}~}|{|||{z{}~~}}~}{{}}~zy|}~~||}|}|z{}}~~~}~}|{}~~zy}~}}|}|||yz|}~~~}~~|{}}~{y|~}}||||~|z{}~~~}~~|{}}~zz|~~}|{|{}|z{}~~}|~}{{}~~zy}}~}|{}|}|z{}}~~}~|upopqsx||z~~yurqqqtvtsw{|vvwwwusqqqqrtrnprsuw|~~~~|wtsqruvttx|~zsnoqqqonlmnnqsqoortuwzzyxxy{{ywuvuuwwuqqsssqooprtw{{yy|~zxywvsqopqruyywx{~}{zyyz{wstuuvxzyxwwy{{yvwwwxz{xuwyzzxwwxz{~}|~~}~|zyxwxzyvrsuttrqqrtux||zz}~}||~~|{}~~{xz|}}{zyyyy{{xuuvwxy|zywwyz{xuuuuvxyvssuvvutstwx{~|{~~|z{}~|{|}}}~}xuvyyzxwuuutvxuqqsttvyyxvuxyzxvuuuvxzyvuwxyxvvvxy{~~|~~~|zy{}~|zz|||}}yttvwxwutststvvspqstuwyxwuwy{zxvwwwy{{yvxzyzxxwxz{~~}~~}{yz|}}{zz{{{|{wssvwxwutttstvuqprstuyyxxvx{|{xwxxxz||ywz{{{yyxz{}~~~~|zyz|}|zyz{{||zvsuvwxvtsstsvwtqqstuwzyywwz|}{yxyzz}~|yy{||{yyy{|~~~}}{yy{}}{yz{{{|{wssvwxwvttttuwvsqrtuwy{zyxz|~}zyzzz|~~{y{}}}{zz{|}~~~~~|yy{|}{yz{{{||ytsvwxxvutttuwxtrsuvwz{zzxz|~~{zz{{||z{}}}{zz{|~}}~|zy{}~|zz{{{}|yusvwyxwvtuuuxxursuvwz|{zyz}~~|zz{{}|z{}}~|{z{}~~}}~|zy{}}|zz{||}}zutwxyxwuuuuvxxustvwx{}{zz{}|z{||~|z|}~}{{z|}~~}}~}{yz|~~|{{||}~|xtuxyzywvvvvxzxttuwxy||{{z}~~|{||}~~{{}~~}|z{|}~~|~~}{z{}~}{{|||~~|wuxz||{zyzz{~~|yz|}~}||~~|{}~|zzxxyyvtuwy{~}{zz{{~|{{|||}{yyy{~~|{yyzzvtuwy{}}~}}zywwyz|~{z{|||}|yxwy|}|zz{{{|}zvsvwyywvuvvwzzxvwz{}{xz{|{yxvvvvyzwuvx{|~~}~~~}~|zxwx{||zy{{|}~|xuwy{{zyxyyz|~{yy{}~~|{|~}{|}}}~|xtuwyzxwvvwwy{zwwy{|~|}~~}yz|}~||{||}~{z{}~~}|}~~}~~~~|zxxz{|{yyz{{||yutwxzzxwwxxy||yxy{}~~}{}~~}|}}}~{{}~~}~|{{|}~~||}}|}|zxvxz{|zyy{{|}|xuvxy{zyxxyy{}|yyz|}}|~~}}}~}z{}~}}}|zz{||{|}}|}|yxwy{||zyz{|}~|xuwy{|zzxyzz|~|yy{}~|}~}}}}|{|~~|~~|{z{|}~||}}|}}zxwxz|}{zz{|}~~zvwyz|{zyyzz|~~{y{|~}|~~}~}}{{}~|}|{z{|}|{}}}}}{ywxz|}|zz{|}~~{wwy{||{zyzz{~~{yz|~}|~~}}}~}{{}~|}~}{z{|}}|}}}}}|yxwz|}|{{||}|xwy{||{zyzz|~~|yz|}}|~~}}}~}{{}~|}}{z{|}||}~}}~|zxx{|~|{{|}}|xwz{||{zzz{|~~{y{|~~||~}}}}|{|}~~}~~|{z||~||~~}~}{yxy{}}|{|}}~~zwxz|}|{zz{{}}zz{}~~~{}~}}}~~{{}~}}~|{{|}~|}~~}~}zyxz|~}{{|}}~}ywy{|}|{zzz{~~|yz|}~}{}~}}}~~{{}~}~~|{{}~~}}~~~~|zyx{}~}{{|}~}ywy{|}{zzz{|~~|zz|}~~}{}~}}}~~{{}~}~~}{{}~~}}~~~~}{yy{}~~|{|}~}ywy{}}|{zz{{~|zz|}~~~~{|~~}}}}~{{}~~~~zxwy|~~}}~~~~}|{{{{||||}}{|~|~|{|}~}~{zy{}~~{|}~~|||{x}||~~~~}~~|~|xxz{|{zyz{{z{~~{}~}{}~}}{z{}~~~~}{yyyz|}~}{z||{~~}}~||}}{zz{}~}}}|~~zy}~zy}~|||}~~{~}xwz{{{zz{||{|{}~~}z{}}|{z{}~~~~~|zyyz{|~}||zy|~|{~~}}~}||}}{zz{|~~|~}|~}yy~~zy}~|||}~~}{|xxzz{zzz{||{}~{}}|z|||{zz{~~~~~~|zyyz|}}}|{yz}~{|~~}~~}|||~}{z{|}~}}~|}~zx||z{~}||}~~~{}~ywyzzzzz{||{|{|}~}z{||{zz{}~~~}}zyyz|}}|{zyz}~||~~}~~~}|||~}{{{|}~~}}~|}}yx|}z{~~}|}~~}{~~yxyzzzyz{|||}~{|}}{|||{zz|~~}~}zzz{|}}{{zy{}}{|~}~~~|||}}{{{}~~~~}}~}|}{xy~|z}}}}~~{||xyzzzyyz||||{{~}~{{||{zz{}~~}~|zzz{}}{{zzz|~||}~~~~~}|||~|{{|}~~}}}}}|~}yx{~{{~}}}~~}{}{xyzzyyz{|||}~z{}~|{|{{zz{}~~}|zz{|}|{zzzz}~||}~~~~~}|||~~}|||}~}}}}}}|}}yx{|{~~}}~~~~~}{|}yyyyyyz{|||}{{~}}}{||{zz{}~|~~{z{{|}{zzzz|~}||~~~~}|||}~|||}~~}}}}~~||~~~zxy}~|}~~~~~~~~~|{~{yyyyyyz{||}~}z{}~|||{zzz{}~~|~}{{{{||zzzzz|~}|}~~~~}|||~~~|||}~}|}}}~~||~~~zxy}~|}~~~~~~~~|{~|yzyyyyz{|}}~}z{}~}||{zzz{}~~|~~{{{{||zzzzz}~}|}~~~||||~~~}||}~}|}}}}||~~}zxz~~|~~~~~}~~~~~||~{yzyxyyz||}}~|z|~}|||zzz{|~||}{{{||{zzzz{}~}|~~}|||}~}}||}}|}}}~||}~~~{yy|}}~~~}~~~~||}|zzzyyyz{|}}}}z{}}}|{zz{|~}|~}|||||{zzzz{}~}|~~}|||}~~}}|}}|}}}}||}~~~{yy|}}~~}~~~~||~|zzzxyzz||}}~|y{~~}}|zzz{}~||}|||||zzzzz|~~||~~|||}~~~}}|}~||}}}~~||}~~}zyz~~}~~~~~~}~~}|}~zzzyxyz{|}}}~~zz~~~}}}{zz{|}}{~~|||{|{yzzzz}~}|}~|||}}~}}|}}||}}}}|}}}{yy{}}~~~}}~~~~}|}}zzzxxzz{|}}}}yz~}}}{zz{|~|{~~|||{|{yzzzz}~}|}~||}}}~}|}}||}}}}||}}|yy{}}~~~~}}~~~~}|}}zzzyxyz{|}}}~~zz~~~~}}{zz{|}~}{}}||{||yzzzz|~~|}|||}~~~~~}|}||}}|~~||}}~}zyz}}~~~~~}~~~~||~|z{zxyz{||}}}|y{~}}}{yz{|~|{~~|||{|{yzzzz}~|}~||}}~}~~~}}}||}}|~~||}}~}zyz}}~~~~~}~~~~||~|z{zxxz{||}}}|yz~~}}{yz{|}|{~}||{|{yzzzz}~|}~||}}~}~~~}}}||}}|~~||}}~~|zy{}~}~~~~~}~~~||~|{{yxyz{||}~~{y|~~}}|zz{|}~{|~|}|{|zyzzy{~}|~}{|}~~}~~|}|{}}}}||}}}}zvqppppty~{xvvvrpqrqsw{|yxtsssqqssrrtvwx||z{}~}}xpmnpppppruw||zzzxwtqppqsrpqqpopqqqstuuwtposxyzzyxxuutrrrqqtusrsvxy}}|zxvtollmpqoquwx{}~}xutttuwustvvwxwvwx{{wuutrrstuvxz{|~{wx{|||{z{zzywwy{{{xxzzyxvttrqpnnpqrtxywvvxy{~~~~{z|~~{zzyyz{||}}~}~~}}}{yxwwyywxzzwurppqrtqmllklortvxyyy{yutwzzzzz{{{}}||}~~|{~~{zzzz}~}{ywuwyz{}~}{|{vsuyyxuqppoonllmmnsyzyy{{z{}|{|~}{yvtvx{}|||zxy{|}~|z|~~|{|{z|}}~}yspqswvsssqoqtvxz{{{{zvux|}}{yxxxyyxyzwuvxwuuwyyyzxz}|{}~}yy}}zxvxwuvusrtuutvxyy{zxxz{zzxustvyyvuusppqstwxz{}ztruz{{zxwwvx{|~~~}{zy{zxwurpprtw{~ywx{zzywutqrsrstuwzzxvvwxy|~{yz||{yvsstwzz}~zxvuwyxy|}|z{}|xvvvwxwxyxvxxwuuvvvxzwttwy{}{yywuvuw{||}}{||}~~||}|~}|||z{|~~}{zwxzzxy{|xtssttwxwxywuwwutuvwussutuwz|}{z|}~~~~{xy{|}}zz{}}}~|yxxvuwy|||{z{|yuvyywuttutrromoqsx~~~}}}}}{y{|}{{{zz|}}}|z|~}}zyxy{~||~}~~zursuvxwtttstx{{{}}||}{wx|}}~~|}}}|zxxz{zz{|zy{}~~~|}}~~}~}}zxy}yyzzzxwwusux{zyz|{{|{zz}~}|yvx{}}{{wrpsvwz~}zxy|}}|zyy{|~~}~}~}yy{{ywspqtx|{xy||}{ywvwxwuvxyxy{|zyz}~~{|~|wttv{~~~~|yxy|zz~~~zxyyzzzz|{zz}}|{}}}~yy}|{|}}~}~}|||}~~}}}}{ywwyzz||~|yyz{||z||xuwyz{~z{}||}}~~{z}}{|{z}ywx{|}}{z|||}|{y{}~}{{{{z{zzxy|}}~}{yy{|~}z{}|zwuw{|{|}zxy{}{yy{~~}}~}~~{yz}}|~~~~}{{~}zxwy|}}}~|{{xx|~yvvvvwyzz}~~|zz~}|}}}|zz{||~}}}|xy{~~~~}{}}zyyzzz|~|}}}~~}{|~|zxwy~|||~{{|~}|}~~}}y|}~}{yyyxz}~|}~}|~}{|~}|zy{|}~zvsqtux}~}}~~}~|}~|}~~~~~|~~}~~}}zy}}yvsruy~{yz}}|{xy|zyxwyxvvz~}z|~~}}~{wuw{}zwz|||~{zy|{xxxvx||wtuy}}zz}}xvvxxwz}~|vty~{zzz}~}}~}{}~}~}zy{~~}}yxyyz}}z{}|yy}|{|}~|zywwwx{~~yx{}ywyz~~~|zzz|~|{}||zz|}~|{|~}||zz}}||}|zwxyyy{~~}|xwz}}{zxvvtux{{}~}}}~||~}}}|}|}|}}{~~{z{{}}{ywy{{{~{z{{{~|}|xvwwxwwy{}||}}|}|}}~~zy|~{y|}{|}{{~}{~{y{~~~|z{}~~{z~}}}|{{}}|z{~~|zxvwxz{~~}}|||}~~~~~~}~}~|zyvtw}~}}}~}}}~}{|~}~~~}}~}|||~|z|{ywutx|~|z|}}|{}~|{|zxvwz~~}{}~|yx{~}~~~}~~zy|~|{}{}~|ywuvx||wuy{|}|~}zxxyyx{|{yz}~|||~~~}~~~zxz}~}}}zwy}}}{yyy{|}zz~}{|}}{{~}zz{{xy}|z}}yy{}~}|yz|}}~}}{y|~}}||{|||}||~~|{|~||zwxz|~}xx}~{zyxyx{{{~}||~}{{{~}{|~{z}|z|~~{yx{}~|{|||{}}~}|{yyy|||~~~~{z||{{}|}{z}}~}zzz{}}}}~~{wx{}}~||}|z|{|}|~~}}|}~~{zzz}~~}|xv{|{}~{zz{}||~{wz}z{~~}|z{|}}}|zz~~}}~~|{z{}~~~~~zxy{|}{}|{{~~~~}||}~}}}}~|}}}{|~~{{|}~}|{{{{}|~}~~{wwxz}||||}}|{||z|}~}~~~~~~}}}~}~~{vw{}~}zyxy~~}yw{}{z{z{}}{|~}~~|}~|~~||{yz|~~}{{}}~|{~||~}{{|~~}||z|~~~|{|~||}}}|||}{|ywy}~|~~}}}}~}|~~~~~|xy|~||}zy|~~}zz~~||{~~}|~}|~{vvx|}~}}~}{{~~|}|xwy{~~~|}~}y{}{||z|}{}~~~~|xxy}|}}zz|||||~~}}yyz{~~}{{}~|z~}{zzz}}~~~|{{|~~}~{{|~~{zyz}}~{{}}{{~}{zz{}~{zz}|}~~~~~~~~~{{||}~{yzzy}{{~}|~}}||~}|{{~~~yy}}{{|}}~~zy}~}|zxxx{~~~{{}~{|}|z}~}~||~~~~~~~~|||}|{yz{|~}zxw{||}yz|}~|z{{{}~}{~}~}|}~~}~}}~}}~zxy|~~|~~|yy}}z{|{y{|}|{{}~}~{z~~}~|{z{||~|}~zwy{|~~~|}~~~~||}{xz||}zy{~~~~|{}|{|}}}|}~}z}~zwvx|~}~~~|{}~}}}}{xwy|}|||~}{~~}||zz~}|~~~{wwy}}~~||~}{{zz}|yxy~~~}{{|}}~yy~|{z{|}~~~~~~~~|{z{}{|~|zz{}}~~~}}|{}~~}|zyzz}~~~|z|}~~~}}~}~~~~||~|{~~{|~|{zyy|||~~|~}}|}~|{zz~|||yx|~}|||}~~~z{~}|~|{zxy|~}}~|{~~|||{|~}{}}yz~~}|}~~}{}~|~}}~|zz||~}zxz}~|{|}~~}}{{|||{{{}~~||~{z|~}}}|~}zx{~~zwx|}{z{}~{y{~~|z|~}~{}~|zxy{}~}{yxy}~~}}~|||}{{~{zyz|~{z{~~}}}}~~~|}}{{{}~|{}{ywx}~~~~~{z{}|~|zxy}~}|||~~}~}{z}~|{~~}|~~~|xw{~}|}~}{yxy{}yx{~{{||{}}z{}||}~~~~}|~{ww|}~~}{|{z{}~|xx{~}|zzzz|}|}|}~}|~~~~}|~~|z|~~||}}~}}|yy|~}}~~}~~}}~~|{z|~|z|{yz|~~~}||}|~}}}~}|{|~}z}}|{{z|~~~}}}}}||~}|{}~{y{{yz~~}{|}~}|~}zxxz}|{{|||~}}||~}zzzz|||}{{}~{z{}}}|{}~}zz{|~~~zwx{~|{{}}z{}}~}|~~~}}~~~|yyyz}~}{wy}}||}~}|{|}}{|}{xy}{z|~~}|{{}~~}|zz|~}~~}~~|zyz~~~{yy|~}z|}zyz~~}|}~~~~}}|}~~}|{}~|{|}~~|zz|}}~~~~}zwwx{}zz|~~|{||{|~}}}}~~~~~~~||~~xvx}}}|zyz|~zwx|~|{zzz{}~|}~~}}~}~}}~|{{}}{{}~~|zz|}~}~~}||}~||}~~~|{z|{{}|z{}||}}||}~|{~|yy{~|{~~}}||}~}~~~~{z{}~|}|zy|}zy|}|{|~}~{rkijmqx|{zyxvurqqppqtx|~||}}|zvsrtuuuusrruwy{yy{}|zxututspnnrw{~~ytqommmoqqpqrsrppruvvxywtstx{{yurommmnrtttutrqqrsvyxxx{{vrrstromnpqpppqsw{|{|{|}~}|ywwxwutuuuutsssrtxyz{{|||{wvvw{~~~~~{xwy{|~~~~}}}~|yvvvuqnnpssrrpnopruz}}~~|{|~}|{{}~|{|{z{~}~~||~~~~~~}{zy{~~}~}zxvutux{zvqnmnoqrstuuwxxwvwyz{{zzz{{ywwz~~}{|~zwwy|~|zy{|{yxwwtttsrpoqvyxurpoprux{}}|zvsqrtvxxwvvwy{{|~}~~~~}{{{}{vrrtvvwxxwsqqstuuutssqqty{|}|ywusssuuuuuxzyxy{}|||}}~}|~~||~}}~|{}{ywwuvx{{yvuuvwvvvvvuvxz{zyxwwxyywwtqoorwz|}~{wvwxy|~}zxxyyyz{}~|zyxxwwx|~}{vttvx{}|{{yvtrqrtvxxxvwwvwz}|{{|{xvuuvvvvwyzyxwwwxz~}{zzz|{yz||~~}}}}{{zxwxzzyxwvvvvtssvyzzzyxvuwy{zyyyxvsrtxyyyxwvvwz~~~~}zy{}~}|zyyzzz{zxvuvyz{{|}{yxwwutw{|{wtssrstvwyz|~~|}~~~~~~~}{ywxz~~~}~{xvwz|}}}~~{z{{yyyyxwwwvtqprw{|zwustuy}}zwuwy{|zyy{|||}||~~~}~}{z{~}xtsuuxzzywtsuvvvvwwwwwwxz}~|zyxxxwxxz|}}||}}||~~~~}{|~|zz}|{}~~}~}{zxwxz{{xwxzyxwvvvxyz|~~~~|zyz{}}|zvsruy{}|zyy{~~{z{|zyxxz|}~|}~~|yutwz~}|zwwz|~~}{ywuuuvwx{}~|zz{}}}}~~}|{yvuvz~~}|{zyz{}|yxxz|}~zz{{{|}}{yxyyy|}{{{{{{zzzzxwwx{~~{{}{ywxz{|~~|{{|}~~~zvtvy{{{yxvuuuwz|~~|{z{}~~~~|ywwwwwxxxxy{||{y{~}~{zyy{|}{zz{|}}}|zyy{}|zzz{{{|}~~}~}}}}~~~|yxy|~~|ywwyzz|~}zxx{~~~~~}~|xwy|~{yxy||yvuwxyyxy{|||}~~~}{xx{}{{|}~}||}}}|zxx{~~|yyz{zzyyzz{|}}|}}|{zxwvwz|}}}}|{yz|~~}|}}|~}}~}{z|~~~~~~|{||||zz{}}{yxwxy{}}{z{}|yvuuwy|~}zyz}}{{}|{yxwxz~~}{}~~yuux{}~}{{}~~}{yxwwvwy|~||}||}~~|xttwz||{zyxxz|~~}{yy{}~~}|{|}~~|zz{|||||zxxyz|}~~{wwy{~}ywwxyyzzzzzz{}~}~~||~~~~}~}{{}}{yy}~|{{|}~}|{{{|{|~}||}|{{{{{z{|}{yvttux{~}}~~|{{{{|~}|{|~~~~}||~~~|zyyz{}~{yz{||}}}zwtsuwy{}}|zyz|~|zyzz{|}~~~|}~~~|||~~||~}|}}{{|}~}}~|zwwy{|{yxyzzyxxwxz|~~}~}{zyz|~~|z{|}~}~~~~}}~|{}~~~}~~~~}}}|zxxz|}}|zxwwx{}{{}}{zyyy|}}~~}}}~~~|zxvwx|~~}~~|||}|xvvxz|}~~~}{{ywvwz~}{|~}yvvy{|}|{zz{|~~~}~~}|{{{{z{}~|{z{~}zzz|}~~|{zz{|}}~|{yy{~{{{{zyyzz{|~}}}zz|~~{{|~}zz|}~~}|{{}}||}}}}}}~~||}~~~}}~~~~~}{yz|}{xutuy}~~~|zxxy{}{z|~}~}||}}~~{yyxy|~|zxxz{~~|{{||~~}}}~~|||~~~~~}}}~~||}~}|~}}{z{}~~|}}}}~~}}~~}|}|{{}~~|zz}~~~~~{xy{~~~|z{{|~~}|zy|}~~~~~~~|{}~~}|||{z|}~|zyy{~~|{}~~|||yxx{~~||{|}|z{|}}~~~~~~~}~~}|}~}}}{}}{{}|{zxwy|}}~}~~}}~~~~~~}}}|}~~~~|||~~~~~}zwwy|~}|{{{~~}}{zx{||}~~~}||{|}~~}}}|{|}}~}|z{}~}||{|}~}}~{zy{}~}||}~~~|yyz{|}~}}~~~~~~~}}~}}{z}|z}~~~|{zwy{}~}~~~~}~}}}~||}~~}||~~~~|yvvy|~~|zzz{~~|{ywy{|}~}~~~~||~~}}~}{{|{}}|zy{~~}}|}~}~|zyz{}~}||{~}~~}{yz{}}~}}~~~}}~}||{{~}|~~}~~~||zxz{}}~|}}}~||}~}~~|{{~~~~~~|yvwz}~}|{{{|~}{yx{|}~}~}{}~}|}}|z{|{}~}{yy{~||}}}}{yxz|}~~}|{{|~~}~{z{}}~~~|{}~~}}~}{~~{|}~~}|{xxy{|}}~~~~~~~}|}~}~~~}|{}~~~~~}zwwy}~~}||{{~}~}{y{|}~~~~~|z|}~}}}}}{|||}~}{zz|~}{{|~}~}zyy{}~~}|{{}~|z{{||~~~~~}{|~~~~~|||{}}~|{xxyz{|~~~~~~}~}}~}}}}}|}}~~~~{wvx{}}{{zz~~~}{xz{|}~~~~|{|~}}~}{|||}~{yz}~}{{}~}{zz{}~~|{{~~~~{zz|}|~~~}~}|}~~~~}}{|~~~||}}~~~}|{xyz{|}~}~~~~~~}}~~}}}~~||~~}~~~}zwwy|}~|{{z{~~~}zx{{|~}}{}~~}~~}z|{{}~{yy|}||}~~{yy{|}~~|{z|~}~~|zz|}}~~~~}||~}~~}~{{~~}{}|~~~~}|zxy{||~~~}}~~}~||~~}}}}|}~~~~}}}ywxz}~~}|{{z|~~~}zz{|}~~}{}}~}}~~}{|{{}~{yy|}||}~~}{yy{|}~~|{z|~~}{{|}}~~}{{}}~~~||~~~{||}~}|zxyy{{}~}~~~}}}}}~}}}~||~}~}}~|xwx{}~~}||{z}~}~}zz{{}~~|{}~}}}~}||{{}~{zz|~}{{}~~}{zz{|}|{{}~~|{{|}|~~}~}{|}}~||}}}||{~~~~}|zxyz{{~~}~}}~}}~~}|~~}}}}}~}}~zwwy||~|{{y{~~~~~{zz{|~~}|}~~}~}||{{||zy|~}{|}~}{zz{|}~}{z}~~|{{|}|~~~}}{|}}~~{|}}~|||~~~~}|zyzz{|~}}~}}~~}}~~}}}}~~}}~}|}|xwx{|~}{{zz}~~|zzz{}~~|}}~}}~~||{z{~}zy{}~{|}~~{zz{||~|{z|~~|{{}}}~~~}}{|}}~{|}}~}|{~~~~}|zyzzz|~}}~}}~~}}~~|}~}}~~}}~}|~{xxy{|~|{{z{}~~|zzz|~~}}}}}}~~||zz||zy{~}{|}~~}{zz{|}~|zz}~}||}}}~}xqnoopsvy}}{ywvuvwxz{{zy{~|{{|{vqrttsrstuvwxy|}~|uqmllosuvy{wsommopomrwywsrpooprtvwxxwuttstvyxroprqpoooopqrstvy|~~{z~~{ywvwxxvtrpprvz|}~}yvrpopqrstuutssrrvz{wrqsuuuuvwwy{{{}~|y{~~}|}~ztqsvutuy{zxwwwwyzywvtstwz||||ywwy{}}xy{||||||}~~|{|}~{xwwx{|~}~~yx{|{yy{||||~~}ysmmqsrqswyywxxy{|}|zxutuwy{z{~}|~~|yz{||zxusstvxyxz}~yuuwxusttuvwwy{}~|xsopttrruxzyxyyz{}~~}{xxz|~}z{}}zz{||xurqrssuvvyz{xtruwwuuvwy{{|}~~~{xsnnrvuux{}~}}}}~}zxz|}~~~{yyyz}|zwtsrstuwxxz|}{vstvwvuvwxzzz|}}~}{uposwwuwy|~}}~~~~{xxyz|||~|~}{yyyz}|zxusqrttuwwz|}|xstvwustuvwxyz|}}ysrtxxvwy{}|}}||~}z{|~~~}~~{{}}{yzz|~|yurqrsstvwyz{yvstvxwvxxz|}}~~{vrruyyyz|}z{}~}}|~~}}~~~~{yyy{|{zwtrrtuvwxz|}}zvtuwywxyy{|||}~}{urswzyxz{~~~{xzz{{|}~~~~}}zzzz||{yvtrstuvwx{~~}zvuvxxvwwxzzz{|~~{vtvz{yyz{~~~~~{{}}~||{|~~|{~}{{{|~~}zwtrrttvwx{}}|xuuwyyyz{{}~~~~|yusv{|{{|~~{{}}}}~~}|{|~||}~}|~~~{zyz|}|ywtstvwxzz~}ywvxyyy{{{}~~~~}zvsv{|{z{}~{zz{{{{~}}|}}~}}|{zz|}|zxusstvwyy|~{wvwyyxyzy{||}}~~}yvw{||zz{~~|zxy}~}{|}~~|{{||uqqtwy{}~}{z|}}{xurqrtvxyz|zyz|~~~{}}{|}}xvussvy{|{zxwwwz}}|{|~~{vrrrsttuvwvsrsux}~ytrtx{}~}}}yvvy{|{z{}}|{{~}{zyy|}}{z|~{wttuwwwxyywttvy}|xwy}~}zxxz|~}{|}}}~~~~~~}{ywx{}}||}~}~zxy{{{{|}|ywwz}zwwz}~{{}}{z}~}{xuuuxz||~|yxxz{{|~~~~~~|zxy|~~~}zyz|||}~~{yy{~}xwy|~|{}|zxy|}|zxuuuwz{|}|ywwxyz{~~~~{zy{~~~~~~~}{}~~~~|yyz~{y{}|{|~}zyy{|{ywtssuwy{|~|yxwxyz{}~~}~~~~|{{|~}}}~~|}~~}zy{~~zxy|~}zyz}~~}zyy|}{ywtstuxz{}|yxwxz{|~|{{}}}}}~|z|~~}~~|zz|}yxz}~~~|{|~~|yxy|}{ywuuvxz|}|xwwyz{}~|{|}~~}}~}}}~}}~~}zyz}}z{|||{}~|yyz||zwvttvwz{}~{xxxz{{~~~|{}~~~|||}~}}~~}zy{}~{yz{}~zzz|}~~{yy{}}zwvtuvxz|~~{xxy{||~{{}~~|{|}~|{|}~~~~|zy{~~zyz{~{{|}~~|yy{}}{xwvwxz|~|xxxz||~|{|~~~~}}}||}~~}}~~~|zyz}|z{|~|{|}~}{y{|}{wvuvwxz|~}zxxz||}}{|~~}{{|~~|}~~}{yy||yxz|~|yz{}~~|zz|}}zwvuwxz|}~{xxy|}}~||~~~~{{{|~|{|}}~~~~{zyz~}zxy{}|{|}~}|z{}}|ywvwyz|~~zxxz|}~~|{~~~~}}~~{{{}~~||~~~~}{yy{~}zyz|~||}}|~}zxxyz|~}{{}~}}~~~}}~zxyy|~}}~}}}|yz|||}}}{xwx{~zxxz|}}z{}~~}}~|ywxz{}~{{|~~}~~~}}~|yxxz}~|{|~~}}~|yx{{{||}{ywxz~{xwy{}||}}}~~}zxy{|~|z{}~}~~}}|||~}yxyz}|{|~}}~~{y{|{||}|zwwy}}zyz|}}||~~~~}zxwy{|~}{{}~~~~~}}}}yxxz}}||}}~{z{||||}|zwwz}|xwy{|~|{|~~~~|zxwz{}}{{}~~~~}}~|ywxy}|{{~}}~}zyz{{z|}{ywxz~|xwy{}~}|}~~|zxx{}~|{{~~~~~}}|{}~{xwx{~~|{|~|}}{z|||{}|{ywy|{xy{}~~~}|~}zywx{|~}|{|~~~~~||}zxwx{}|}}}}{z||{{||zxwy|zwxz|}~}|{}}zywx{}~}||}~~~~}||}yxwx{~|{|}}|zzz{{z||zxxz}{wxz|~~~~~}|}~}zyxy{~}|||~~~~~~~}|{~}zxwx{~~|{|~}}~|{{||z{|zxxy|}yx{}~~~~}}}~zyxxz|~}||}}~}~~}|}~zxxwy}~}|~~}~}{{{|{{|{yxy{~zwy{}~~~~}}|}|yyxyz}~|}}~~}}~~~}|||yxww{~}{|~~}~{{z{{zz{zxxz}|yxy|}~}}~~}}~{yyyz|}|}}~~}}~~~}|{|~~{yxwx|~}||~~}~||{{{z{{zxx{~|yy{}~~~~~~~~zyxyz|~}|}~~~}}~~}|}}zxxwy}}}~}~}||{{{{{zyxz|}zxy{}~~}~}}}}~|yxxyz}~|}~~}|}~~}|}~{yxwx{~|}~~~~||{zzzzzyxy{~|yxz|~~}}~~~~~~zyyz{}~||}}||~~~~}||}{yxxxz~~}|~~}}|{{{{zyxy{~}{zz|~~}~~}~~~~~{xxyz{~~}|}}|{~~~~}}~|yxxxz~~}~~~}|{{{{zyxy{~|zyz|~~~|~||}~~~{xxzz{~}||}}|{}~~~}|}~{xxxxz}~}~~}}|zzzzyxxy|~{zzz|~~}|~||~~~~{yy{{}|||~}{{~~}~}||}~~zxxxy{~~~~~~~|{z{zyxxz~~{{{{}}}~{|~~}zxyz{}~||}|{{~~~~}}|~~yxxyz|~~~~~|zz{zxxx{~}z{{{}~~}}~}z{~~~~}zxy{|}~{|}|{|~~}~}}}~~yxxyz|~~}~}|zy{zxwx{}z{{{|~~}}~}{|~~~~~{yy{|}~{{}}{{}~~}}}||}zwxyz|~~}~~~}{y{{ywxz~~{{||}~~}}~{{}~~~~|yxz|}~|{|~~{{|}}}}|~|xwxz|}}}~~~~|zz{zxwy|~{z|||}~~~}~{z{~~~}~}{xy{}~}{{}}{|}~}}}|}~zwwy{|~~}~}}~}{yzzywwz~}{{|}|}~~~}~{z|~~~~}{yz|~|z|~~|{|~}|||||~}ywxy|}~~}~~~~}{yzzxwx{}{|}}}~~~~~~{{}~}~~|zyz|~|z|~|{|}||||~|ywxz}~}}~~~~~}zzzzxwy}}{{|}}}~~~~~{z{}}}~~}{yy{}~}{{}}{|}~}|||}~zxwy{}~~|}~}~~}|yzzywx{~{z{}~}}~~~~}{{|~}~~|{zz}~|{|~||}}{{{|~~{ywy{}~}|~~~~~}zzzywwz|{{}~~}~~~{{|~}}}|{zz{~~|{{~}|}~}|{{|~~|ywx{~{vpmmov|}wtrrttqooquy|~~|~|zyzywtuxwvsstsstvy}~~~~|zyywutrpooptz~~zwtrpooonnmllllmpu{}}zurrrsuutssssstwxwwvspmmnrvxvuuwz}~~~~{xvrooprtuuuutrrrsv{~|zxvtsrruy}~|xtpqrtuutstvvvx{|}|{yvtuwz}~|zz{}}||}}}~}}~~~~|xurrpnmnqsvxxxxvuuuux|{|~~}~|zz|}|~}||||{}~||{|~|}~}{yxxxwwxxyyxwwxz{{{{{zwtrrqonpsvy{{zzxwvvvx|~~~~|{||{|~|zyxy{~}|}|{yyzzxvuuuttutvwvuvwxz{{||{yvsssqpqtx{}}|{zxwwwx{~|{{|{{}}{yxwwy{}~~|zzzzxwxywutttttutuvwvvwyz{|}}}|yvuutrrtx{~~|{yxxxxz~}{{|{{{}~}{yxwwxy|}~}{zzzyxwxxvtttuuuuuvxxxxyz|}~~~}|yvvvtstw{~~|zyxyyz|~}{{||{|~~|zxwwwxz|~~}{zzzyxxxxvutuvvvuuvxyxyz{}~~}yvvvusuy|~{zyyyzz}}|z{|{{|~}{yxwwwxz|}}|{zzyxxxxxvuuvwwwvvwyzyz{|~}ywwwuuw{~}{zyyzz|~~}{z{|{{}~{zxwwxxy{}~~}{zzzyxxywvuvwxxwwwy{{{{}~~|ywwvuvy||zyyzz{}~~}|{{|||}|zyxwxyy{}~~~}{zzzyxyxwvvwxxxwwx{{{||~}zxwwuux{|zyyz{{}~~~~}|{{|}}}~}zyxxxyyz}~~~}|zzzzyyyxwvxyzyxwx{|||}~~{xxwvvx{}zyyz{|}~~~}}|{{|}}}}zxxxyzz{}~|{{zzyyyxwwxz{yxxy{}}}~~~{ywwvvx{}yxyz{|}~~~}}|{||~~~|zxxxz{{|~}{{{{yyyxwxy{{yxxy|~~}~~~{xwvvwy|{xxyz}~}{zzz{~~}~{zzzyzzz{|||}~~|||}~}~||{|{zyzzyz|~}}}|zxxy{}~}zwtvxz}~}}{xvvvwwvwwvutvz~}{xvvvy{{{zzz{|}}~~}zxxz{{||}}~~}}}}~~~{xuttuwz}~|xvvxz}|||{{zzyywvw{~}zwvvvxz{{|{{}~}{|}~~}}|}}~~~|}~|}~|zwtttuwz}~zwvxz}~}||}{{{|~~~~}|{||}~|zz{}~~~}{z{{{|}~}{zz|~~}{yvuuvx{~}ywxz|~~~~}}}}|{{||}~|yxy{}~}|}|zyyzz{|}}|zyz|~}|yvvwxz}{xwx{}~~~~~}{{{||}~}zxwy{}~~~~|||{yxyzz{|}}{zy{}~}|ywvwy{~~{xwy{~~|{z{||}~|ywwy{}~~~}|||{yxyzz{}~}{zz|~~}|ywwy{}}zxxz|~~}{zz{||~}{xwxz|~~}|||zxxz{{|~~}{z{}~}{xwxz|~|ywx{}~~~~|zzz||}~{ywwy|}~~|||{yxy{||~~~|{{}~}|ywwy{~|yxxz}~~~~|zzz{|}~{ywwx{}~~}||{yxyz|}~~~}{{}~|zxwz|~}zxwy}~~~~~~~|zzz{|}~{ywwx{~}}|{yxy{|}~~}||}~|zxxz|~|zxwy|~}}~~~~~}~~|zzz|}~}{ywwy|~}}|{yxy{}~~}|}~~}|ywxz}~{yxxz}~}}~~}~}}~~{zyz|}|zxwxz}~}||zyxy|~~~}|}~~}{yxy{~}{yxx{~}}}~~~}~}}~~|zyz|~|zxwxz}~}|zyyy|~~~}~~}{zxy{~}zyxx{~~}||}~}}~~||}~|{z{|~}{zyz|~}|{{~~}{zz|~}{zyxz}}{zz{{{{|~}zz{|}}}}~}}{zz|~~{zyy{~~}|{{}}|{z{}~|zyxy{~|{zz{{{{}~{yz{}}}}}}~~|zz{|~}zyyz|}||{|~~}{z{|~|{zyyz}}{zzz{{{|~}zyz|}~}}~~}~~|zz{}|zyy{}}|{{|~}|{{{}}|zzyyz}}{zzz{{{||yy{|}~}}~~}~}{zz{}~{yyy{}~}|{{|~}|{{|~~}{zyyz{}{zz{{{|~~zyz{}~~}}}}~|{z{|~}zyyz|}|{{|~~|{{{|}{zzyyz}~~|zzzz{{}|yyz|}~~}~}}~}{zz{~~{yyz|~~||{{}~|{{||~}{zzyzz|~~|{zzz{{}|zyz|}~~}~~}~~~}|zz{}{yyz{}~|{{{|~~||||}~}{zzzzz|~~~}{zzz{{}|yyy{}~~}~}|}~~~}|zz{}{yyz|~~|{{|}~~|{|}}~}{zzzz{|~~~}{zzz{|}~|yyz{}~~~~}}~~~~}|{{{}~zxyz|~~|{{|}}|{|}~~|zyzz{{}~~~~|{zzz{|~}zyyz|~~~~~~}}~~~}}{{{|~}zyy{}}|{||}~}||}~~{zyzz{|~~~~~|{zyz{|}zyyz|~~~~~}}~~}}}|{{|~}zyy{}}|{|}}~}||}~}{yyyz{|~~}}}}{zyz{}}zyyz|}~~}}~~}}}|{{|~}zyy{}}|{{|}~~~}|{}~~{yyyz{|~~~}}}{zzyz|}zyyz{}~~}}~~}|||{{|}~{yy{}~~|{{|}~~}|{|~~|zyyz{|}~~~}}}|{zyz{~~{yyyz|~}}}~~||||||}~}zyz|~}|{|}~~}|||}}{yxyz|}~~~~}}}}|zyyz|}zyyy{|~~}}}~}||||||}~|zyz|~~}|{|}~~~}|{|}|zyxy{|}~~~}}}}|zzyz}|zyyz{}~~}}~~}||||||~}{zy{}~|{{|}~~}}|{|~~|yxyz{}~~~}}}|{zyz{~~{yyyz|}~}}}~||||||}~|zzz|~}|||}~}}|{|}}zyxyz|~~~~}||}|{zyz}|zyyz{}~~}}~~}|||||}~}{zz{}~}||}~~~}}|{{}~}{yxxz|}~~~}|}|{zzz|}zyyz{|~~}}~}|{||}}~}{zz{}~}||}}~~~~||{|}~}{yxxz|}~~~}|||{zzz|}zyyz{|~~}}~}{{{|}}~}{{z|}~}||}~~~~}|{{|}}{yxxz|~~~~||||{zzz|}zyyz{|~~}}~|{{{}}~~|{{{|~~~}||}~~~~~}|{{|}~|zxxyz|~~}||||{zz{~|yyyz{}~}}~~|{{|}~~}{{{|}~~~}}|}}~~~~~|{{{|~}{yxxy{}~~}||||{zz{~{yyyz{}~}}~~|{{|}~~}{{||}~~~}}}}}~~}~~~|{{{}~}{yxxy{}~~~}||{|{{z|~|yyyz{}~~}~~|{{{}~}{{||}~~~}}}}}~~}}~~|{z{|~~{yxxyz|~~~||{||{{{~}zyyz{|}~}~~|{z{|~~|{||}~~}}}}}}}~~~}}}{zz|}|zxxxz|~~~~}|{{||{{}~{yyz{|}~~~}|{z{}~~|{{|}~~}}}}}}~~~}}~~|{z{|~~{yxxyz}~~~~||{{|{{|~}zyyz{|}~~~}|{{|~~|{|}~~}}}}}}}~~~}}~}{zz{}}zyxxy{}~~|wsqruxz|}~}~{xspmllnqtx{~|zyyz{{{{zzyxxwvusqqqtx{~}xsppprvy|~xtomlllmnprsuvvwxxxwuuuwyzyzzyxvusqonmmopqrrssrqpprtwy|~}}|}}}|{yxxxwvtrqqsuy|~}}||||}}}}||{zywuuvwxyyyyxwvvtsrqrtvxyzzzzywvwy{}~|{z{}~}zvsqppprstuuuuvwxwvuttvx{~~|{zz{}}|zz{}~}||}}~~~~}|||~~}{yvtrrsuwxxwtqnmlmnprtuvvvxyzzyxwwy{}}||}~|{zzz{{{{zyyxyz|}~~~}|{zywusqppqsuwwvsqnmmnoqtvwxxyz{||{zyyz|~~}}~~}{zyyyzzzzyxwwxy{|}}}}|{zxwvtrpoprtvwwvsqonopqtvwyyzz{}}}|zyz{}~~~~~}~~}{yyyyzzzyxwwwxy{|}}}}|{yxwvtrppqsuwxwvtqpopqsuwyz{{|}~~}|{z{|~~~}}}~~~~~}{yyyyzzzzxwwwxz{|}}~}|{zxwvtrqprtvxxxvtrppqrtvxz{||}}~~}{{{}~~}}}~~}~~}{yyyyzz{zxwwwxy{|}}~}|{zywvusqqrtvxyywvsrqrstwxz||}}~~|{|}~~}}}}~}~~}|zyxyzz{zyxwwxy{|}~~~}|{zxwvtsrrtvxzzywusrrstvxz|}}~~~}||}~}}|}~~}}~~~|{zyyzz{{zyxwxyz|}~~~}|{yxwutsrsuxzzzxvtsrstvxz{}~~~}|}~~~}||}~}|}~~}|zyyyz{{{zyxxxy{|}~~}|{yxwutsstvyz{zxvusstuwyz|}~~}}}~~}}}}~||}~~}{zyyyz{|{zyxxyz{}~~~|{yxwutstuxz{{zxvuttuvxz{}~~}}~~}}}}~~|||}~}|{zyyz{||{zyxyz{|~~}|zyxvuttuwy{{{ywvutuvxz{}~~}~|ywuvwz}~}}}~~}{yvtstwz}~|{zzz{}~~|{zyxxxy{~}{zyz|~}zwvuvvwxyz{|}~|{{|}~}|{zyxwvuuvwxy{|{zxwvwy{~~|{zz{|}}~~~~~}|{{|}}|||||}~~~|{{{}~~}|{zzyxxxyz{}~~}{yxyz|~~}{zzz|}~~|zxvvvvxyz{|}~~}||}~}|{{{|}~~|zyz|~~}|||}~~}}|}~}|zxwwwxy{||{ywvuuuwxz{|}~~~}}~~~~~~}}}~~|zz{}~}}|{{{|}~~}|{{{|~}|zxwvvvwy{||zywvvvvxy{|}~}~~~}}~~~~~~~|zz{}~}|{{{{|}}}|{zz{|}~~}{yxwvvvxz{||{ywvvvwxz{}~~~~~~}}}}~~~~|{z{}~}|{{{{||}}|{zz{|}~}|zxwvvvxz{||{zxwvwwyz|}~~~~~}}}}}~~~}{z{|~}|{{{{{|}}|{zz{{}~~|{ywvvvxy{|||zyxwwxyz|}~~~~}}}}}~~~~~|zz|}~}||{{{{|}}}|{zz{|}~}|zxwvvwy{|}}{zxxwxyz{}~~~~}}|}}~~~~~}{z{|~~}|{{{{{|}}||{z{{}~~}{zxwvwxz|}}}{zxxxxy{|}~~~}||}~~~~}}~~|zz{|~~}|{{{{||}}|{{{{|}~~}{yxwwwy{|}}|{yxxxyz{}~~~}}|}}~~~~~}}~}{zz|}~}|{{{{||}}}|{{{|}~~|zywwwxz|}}}|zyxxyz{|~~}}||}~~~~~}}}~}{zz{}~}}|{{{||}}}|{{{|}~~|{yxwwxz|}~}|{yxxyz{|~~}}||}}~~~~}}}~~~|zz{}~}|{{{||}}}||{{|}~~}{yxwwxz|}~}|{yyxyz{}~~}}||}}}}~~~}}}~}{zz{}~~}||}}~~}}}~}|zyz{}~~}{{z{|}~}||{{|}~~|{{|}~}||{{||}~|zyz{}~}}||}~~}}}~~}{zyz{}~|{z{{|~~}}||{{|}~|{{|}~~}|{{{|}~}{zyz|~~}||}}~~~}}~~|{zyz|~}|{z{|}~~}}|{{{|~}|{{|}~~}|{{{|}~~}{zyz|~~}||}}~~}}}~~|zyyz|~}|{z{|}~~}}|{{||~}|{||}~~}|{{{|}~~}{zyz|~~}|||}~~}}}~~|{zyz|~}|{z{|}~~}}|{{{|~~|{{|}~~}|{{{|}~}{zyz|}~}|||}~~~}}}~~}{zyz{}~|{z{{|~~}||{{|}~|{{|}~~||{{{|}~~|zyz{}~~}||}~~~}}}~}|zyyz|~~}|{z{|}~}}|{{{|~~|{{|}~~}|{{{|}~}{zyz{}~}|||}~~}}}~}{zyz{}~}{{z{|}~}||{{|}~}|{||}~}|{{{|}~~}{yyz|~~}|||}~~}}}}~~|{zyz{}}|{z{{}~~}||{{|}~|{{|}~}|{{{{|}}{zyz{}~}}||}~~}}}~}{zyy{|~~}{{z{|}~}||{{|}~}|{||~~}|{{{|}~~|zyz{|~~}}||}}~~}}}~}|zyyz|~~}|{z{|}~}}|{{|}~}|{||}~}|{{{|}~~|zyyz|~~}}||}}~~}}}~}|zyyz|~~}|{z{|}~}}|{{|}~}|{||}~}|{{{|}~~|zyy{|~~}}||}~~~}}}~}{zyyz|~~}{{z{|}~}||{{|}}|{|}~}||{{{|}}{zyz{}~}|||}~~}}}~~|{zyz{}~|{z{{|~~}|{{||~~|{{|}~~}|{{{|}~~|{yyz|~~}|||}~~}}}}~~|zyyz{}}|{z{|}~~}|{{|}~~||||}~~}|{{{|}~~|zyyz|~~~}||}}~~}}}}~}|zyyz|~}|{z{|}~~}|{{|}~~||||}~~}|{{{|}~~|{yyz|~~}|||}~~}}}}~~|zyyz{}}|{z{{}~~}|{{||~~||||}~~}|{{{|}~~}{yyz{}~}|||}~~~}}}~~|{zyz{}~~|{{{{|~~}||{||}}|||}~~}|{{{|}~}|zyy{|~~}}||}~~~}}}}~}{zyyz|~~}|{z{|}~~}|{{|}~~||||}~~}|{{{||~~}{zyz{}~}|||}~~}}}~~}{zyy{|~~}|{{{|}~}|{{|}~||||}~}|{{{|}~~|zyyz|~~}|||}~~}}}}~~|{zyz{}~|{{{{|~~}||{||~}|||}~}||{{{|}~}{zyz{}~}|||}~~}|}}~~}{zyyz|~~}|{{{|}~}|{{|}}|||}~~}|{{{|}~~|zyyz|~~}}||}~~~}}}}~}{zyyz|~~}|{{{|}~~}||{|}~~|||}}~}|{{{|}~~|zyyz|~~}}||}}~~}}}}~}{zyyz|~}|{{{|}~~}||{|}~|||}~~}|{{{|}~~|zyyz|~~}|||}~~}}}}~~}{zyyz|~~}|{{{|}~}||||}}|||}~~||{{{|}~}{zyz{}~}|||}~~}|}}~|{zyz{}~~|{{{{|~~}||||}~~}|||}~~}|{{{|}~~|zyyz|~~}}||}}~~}}|}~}|zyyz{}}|{{{|}~~}||||}~~||||}~}|{{{|}~~|zyyz|~~}}||}}~~}}|}~}{zyyz|}}|{{{|}~zurqrtw{|yvsqpppqsvz~}zyxvtsqqqqrstuuuttuxy|~|yuqomlmorv{}zwtrponmmmoqsvxz{{zwutssttuvwxxxxxwvusrqqstvwxxxwwvvvwxyz|}zwtrqpqrrrqpoooprux|~|{zyz{|}~~|yvtsrrstuvwxyyzzzyxwwwyz|~~}}|{zzz{|}~~~~}||||}}}}|{yxvtqomllmoqrstsssssuwy|~~~~}|{{{|}~~}{zyz{}~~}|{yxxxxyyyyxvutttuuvwwwwwvusrponmnoqtvwwwvvvvxy|~}||{{||}~~~}|zxwvwxz|}~~}}|{zxwvutttuuvvutsrrrrsuvvwwwwvutsqpppqsuwxyyxxwwxy{}~}|{{{||}}~}|zxvuuvxy{||||{zyxvutsssstuuuussrrssuvwxxyxxwvutrqqrsuwyzzzyyxxy{|~}|{{{{|}}}}|zxvuuuwxz{||{{zyxwuttssttuvvutsssstuwxyyzzyxwvutsrstvxz{{{zyyyy{|~~}|{{{||}}}|zxvuuuwxz{||{{zyxwvutsstuuvvvutsstuvwxyzzzzyxwvtsssuvyz{|{zzyyz{|~~||{{{|}}}}|zxvuuvwyz{|||{zyxwvuttttuvwwvuutttuwxyz{{{{zyxvutttuwy{||{{zyyz{|~}||{{||}}}}{zxvuuvxy{||||{zyxwvuutuuvwwwwvutuuvxyz{{|{{zyxvuttuvxz|||{zyyyz{}~}||{||}}~~}{ywvuvwy{|}}}|{zyxwvuuuuvwxxxwvuuuvwyz{||||{zywvuttuwy{|||{zyyzz|}~}||{||}~~~}{ywvvvxy{|}}}||{zywwvuuvwxxxxwvvuvwxyz{||||{zyxvutuvwy{|||{zyyz{|~~}|||||}~~~}{yxvvwxz{}}~}}|{zyxwvvvvwxyyyxwvvvwxy{||}}}|{zxwuuuvwy{|}|{zzyzz|}~}|||||}~~~}|zxwvwxz{}~~~}}|{zyxwvvwwxyyyxwwvvwxy{|}}}}||zywvuuvwy|}{zyyz{}~}|{zz{}~|zxvuuvwz{|}}}}}|{{zzz{|}}{yvuttuwz}~|zyyz{}~~~}|{{zyxxyz{}~|yxwvwxyz{|}~~~~}}}~}|{{{||}}}}||{zxwvvvwy|~}|{zyxxxy{}~|zyxyyz|}~~~~~~}|||~}|{{z{{|||{zyxwwxyz{|}}}}}|{zyxwxy{}~}}}~}|||}~}||{{||}~}|zyxxy{}~~}|zyxxxxxyzzzzyxwwxyz{|}~~~~}{zyxxyz|~~~~~~~~}|{zzz{|}}}}|{ywvvwy{|}~~~~}|{zyxwwwxyz{{zyxxxxyz|}~~|{zyxyz|~~~}~~}{{zzzz{|}}}|zxwvvwxz|}~~~~}|{zyxxwxxyz{{{zyxxyz{|~~}{zyxyz{}~}}}~~|{zzyzz{|}}|{zxwvvwxz|}~~~~}|{zyxxxxyz{{|{zyyyyz{}~~}{zyxyz|~~~}}}~}|{zzzzz{|}}|{yxvvvwy{|~~~~~}|{zyxxxyyz{||{zyyyz{|~~|{yyxyz|~~}||}~}|{zzzz{||}}|{ywvvwxz|}~~~}|{zyxxyyz{|||{zyyyz|}~}|zyxyy{}~~}}||}~~}|{zzzz{|}}}|{ywvvwyz|}~~~}|{zyyyyzz||||{zzyz{|}~}|zyxyz{}~}|||}~~}|{zzz{{|}}}|{yxwvwyz|~~}|{zyyyyz{||}|{{zyz{|}~}|{yyyy{}~~}|||}~}|{zzz{{|}}}}{zxwwwxz|}~}}|{zyyyz{||}}|{zzzz|}~~|{zyyyz|~~}|||}~~}{{zzz{|}}~}|{ywwwxz{}~~}|{zzyyzz{|}}||{zzz{|~}|zyyyz{}~}|||}~~~}|{{zz{{|}~~}|zxwwwyz|~~}|{zzyyz{|}}}|{{zz{|}~~}{zyyyz|~~}|||}~~~~}||{||}~}|zyyz{}~}||||}~~~}|||}~}|zyyz{}~~}|{{||~~}}|}}~}|}~}|||||}~~|{zyy{|~~}||||}~~}|||}~~|{zyyz|}}||{{|}~~}||}~~}|}}~~}||||}}~~}{zyyz|~~}||||}~~}|||}~~}{zyyz{}~}|{{|}~~}}}}~}}}}~~}|||||}~~}{zyyz|}~}||||}~~}|||}~~}{zyyz{}~}|{{|}~~}}}}~}}}}~~}||||}}~~}{zyyz|~~}||||}~~}|||}~~|{zyyz|}~||{{|}~~}}}}~~}|}}~}|||||}~~|{zyy{|~~}}||||}~~}||||}}|zyyz{|~~}|{{{|}~}}|}~~}|}~~}|||||}~}|zyyz{}~}||||}~~}|||}~~}{zyyz{}~~}|{{|}~~}}}}~}}}}~~}|||||}~~}{zyyz|}~}||||}~~}|||}~~}{zyyz{}~~}|{{|}~~}}}}~}}}}~~}|||||}~~}|zyyz{}~}||||}~~}|||}~~}{zyyz{}~~}|{{||~~}}}}~~}}}~~}|||||}~}|zyyz{}~}|||||}~~}||||}~}|zyyy{|~~}||{{|}~}}}}~}}}~~}||||}~~}{zyyz|~~}||||}~~}|||}~~|{zyyz{}~~}|{{|}~~}}}}~~}}}~~}|||||}~~|zyyz{}~~}|||||}~}||||}~~|{yyyz|}~||{{|}~~}}}}~}}}}~~}||||}}~~}|zyyz{}~}||||}~~}||||}}{zyyz{}~~}|{{||}~}}}~~}}}~}|||||}~~|{zyy{|~~}||{||}~~}|||}~~|{zyyz|}~}|{{|}~~}}}}~}}}}~~}||||}~~}{zyyz|~~}}||||}~~}|||}~~|{zyyz{}~~}|{{|}~~}}}}~}}}}~~}||||}~~~}{zyyz|}~}||||}~~}|||}~~}{zyyz{}~~}|{{|}~~}}}}~}}}}~~}||||}~~}{zyyz|~~}||{||}~~}|||}~~|{zyyz|}~}|{{|}~~}}}}}}}~}|||||}~~|{zyy{|~~}||{||}~~}||||}~}|zyyz{|~~}||{||}~}}}~~}}}~~}||||}}~~}|zyyz{}~}|{{|}~~}||||}~~}{zyyz{}~~}||{|}~~}}}}~~}}}~~}||||}~~~}{zyyz{}~}|{{|}~~}||||}~}{zyyz{}~~}|{{|}~~~}}}~~}}}~~}||||}~~~}|zyyz{}~}|{{|}~~}||||}~~}{zyyz{}~~}||{||~~}}}~~}}}~~}||||}}~}|zyyz{}~~}|{{||}~~}||||}~}|zyyz{|~~}||{||}~~}}}~~}}}~~}||||}~~}{zyyz|~~}||{{|}~~}||||}~|{zyyz{}~~}|{{|}~~~}}}~~}}}~~}||||}}~}|zyyz{}~~}|{{||}~~}||{|}~~|{yyyz|}~}|{||}~~}}}}~}}~~~}||||}~~~}|zyyz{}}}|{{|}~~}||{|}~}{zyyz{}~~}||{||}~}}}~~}}}~}|||||}~~|{zyy{|~~}||{||}~~||{||~~|{zyyz|}~}|{||}~~}}}~~}}~~}||||}~~}{zyyz|}~}||{{|}~~}|{||}~~|{zyyz{}~~}||{|}~~~}}}~~}}~~~}||||}~~}{zyyz|}~}||{{|}~~}|{||{tnkiilqw{~}}|{{{{zxtw~}{yxwwwwxvqou}{uqqrux{~|zyxwvvvtrqx~zvtsrsstvvvuttttuwxyzzwuv{{vspppqrrrrqqqpqqqrsuwy{}}yvuy}|{zywvuutuuvwwwvvtrqsy~}{zwvx{{wtsssstttuuuuuvwxy{}{xvw{}{{|{xvuttttttssrrqqqqrstuvvwwvtsrruz~~~~~~~|ywx{~~}}}~~|{ywusrqsvy{zxusqppppqqqqqqrrrrstuvwxyyyxvuttvz}zxy{~||{{{{|~~}{zywutrponorvxxwurqpppqqrrssssttuuvwxyzzzzywvuuvz~{yy{~}|{zzzz{|}~~~~}|{zyxvusqonmoquxyxvtrqqqrrssttuuuvvwwxyzz{{zyxvuuvy~|zy{~~|{{zzz{{|}}~~}|{zyxvusqonnoquxyywvtsrrssttuuvvvwwxxyyz{{{{zywvuvy}|zz{}~}|{zzzz{||}}~}}|{zxwusqpnnoquxzzywvtsssttuvvwwxxxyyzzz{{{{zywvvvx|}{z{}~}||{zzz{{|}}}}}|{zywvtrponoqux{{{ywvuttuuvvwxxyyyzzzz{{{{{zyxvvvx|}{z{}~}||{{{{{|}}}}}}|{zxvusqpoprux{||zywvuuuvvwwxxyyzzz{{{{{{{zyxwvvx{~|{{}~}}||{{{||}}}~}}|{zywusrpoprux{||{zxwvvvvwwxxyyzzz{{{{{{{zzyxwvvx{~~|{{|~}}||||||}}~~}}||zyxvtrqpprux{}}|{yxwwwwwxxyyzzz{{{{{{{{zyyxwvvxz~}|{{|~}}|||}}}}~~~}}|{yxvtsqqqrux{}}}|{yyxxxxxyyzzz{{{{{{{{{zzyxwvvxz}}|{{|~~~}}}}}}~~~~~}||{yxvusrqqsux{}~~}|{zyyyyyyzzzz{{{{{{{{{zzyxwvwxz}~|{zz|~}|zyxwwxz}~~~~~~~~~}}|{zyxxwxz}~}}|||}}}}~~~~~~~~}}||{{{}}{zzz{~~}|zyxwwxz}~~~~}}|{zyxxxxz}~}}}}}}}}~~~~~~~~}}||{{{|~~}{zzz{}~}|zyxwwxz|~~~~}}}|{zyxxxxz|~}}}}}}}}~~~~~~~~}}||{{{|~~}{zzz{}~}|zyxwwxz|~~~}}||{zyxxxxz|~}}}}}}}~~~~~~}}}}|{{{{|~~}{zzz{}~}|zyxwwxy|~~~~}}||{zyxxxxy{~~~}}}}}}~~~~}}}}}||{{z{|}~}|{zz{|~}|{yxwwxy{~~~~}}||{zyxxwxy{~~~}}}}}}~~}}}}}||{{zzz{}~}|{zz{|~}|{zyxwxy{~~~}}|{{zyxxwxy{}~~}}}}}}}}}}}|||{{zzz{|~~}|{zz{|~}|{zyxxxy{}~~~}||{zyxxwxyz}~~~}}}}}}}}|||{{zzzz{|~~}}|{zz{|~~}|{zyxxxy{}~~}}|{{zyxxwxyz|~~~~}}}}}}}|||{{zzzz{|~~~}}|{zz{|~~}|{zyxxxyz}~~}}|{{zyxxwxxz|~~~~~}}}}}|||{{zzzz{|~~~}}|{zz{|}~}|{zyxxxyz|~~}}|{{zyxxwxxz|~~~~~~}}}}}|||{{zzzzz|}~~}||{zzz{}~}|{zyxxxyz|~~~}}|{{zyxxxxxz|~~~~~~}}}}|||{{zzzzz{}~~}|{{zzz{}~}|{zyxxxxz|~~~~}}|{zzyxxxxxz|~~~~~~}}}}|||{{{zzzz{}~~}|{{zzz{|~~}|{zyxxxxz{}~~~}}|{zzyxxxx{}yvuuvy|~~{xwuuuvwvspw~|{{zxxwvupiiqxursvz|yvtsssrqnnv~|zxvuuuuuuuuuuuvvutrnklsyxutstuvwvuttssssstuuvwwxxyzz{{zyvrrx~}|{{zyxxwvvuuuttsqonpv~}{xuqnlnsxzzxwvwwwwwwwwwwwxyzz{|}}~~~~~|zxutuy~}zwvwz~~||{zyxwvuuttttttuuuuuuuutsqpoorw}|zy{}{yvuvy}~|zwtqnmorvxyyxwvvuuttsssssttuvvwwxxxxwwutrqqrv|~|zwvwy}~~~~~~~~~~~}}|{zxvtqnlklosvxxwvvuutttttttuuvwwxyyzzzyyxwusrqsv{}zxwwy}~~}}}|||||||||{{zywuspmkklosvxxxwwvvvuuuuuvvwwxyyzz{{{zzywvtrrsv{}{ywwy|~~}}|||||||||{{{zyxwuspnlklosvxyyxxwwwwvvvvwwxxyyz{{{||||{yxvtstvz~~}|zxwvuwz~~}{yxwwy|~|zxvuvy|~}{ywutuwz~~}|{ywvvwz~~}|zxwxy|~|zxwvwy|~}{yxvuuwz}xsnljjlnru{}{yxyz||zw{~{xvuuuwz~~~}}||xsqw~ywwy|~}{zzzzywv|}yvsponnpsvyz{||||||zxtqqw}}yurqqpponmlkjihijloswz|}}~~~~}{xx|~}zxvtrrstvxz{{{{{{{zywvx}~{xustw|~}zwusrqppooonnnnorux||zyz~~~}}~~}~{ywvtrqonmlklmoruxz{{{{{zyxwwy}}}|{zyxxwwxy{~~|z{~~}|{zzyxwwwxz|}zwutux|~}{xvtrqponmmllllnpswy{}}~~}|{zyxz}~~}|||}}{{}~|{zyxwvvuttuvxz}~~}{ywtrqruy|}|zwutrqpponnnnnoprux{}~~}|{yyz}~}|{}~|{yxwvuutsstuwy{|}~}|{zxvsqqrtx{}|{yvutsrqqppoopprtvy{}~}{zyz|}||}~|{yxwvuutsstuvxz{|}}|{zxvtrqrtx{}}|zxvutsrrqqqqqrsuwy|~}{z{|~}||{zzzyzz{}}{{|~~}|{{zzyyzz|~~|{{|~}||{zzzyzz{}}|{|~~}||{zzzzz{|~~|{{}~}|{{zzzzz{}~}|{|~~}||{{zzzz{|}~}|{}~}||{zzzzz{}~}|{|~~}|{{zzzz{|}~}||}~}||{{zzzz{}~~|||~~}||{{zzz{|}ztonnrw}}yusrqrsuvtt}~}~~|zxwwvvvuurmoy|yyz{}|xvtssstsrt}}zxxz{||{zyxwvvuuuuuusrnlpx~}yvtrqpomkjkloruwxyxxxwwwwwwxxxvusu}}|{{|}~}{yxwvuutttsrqru}~|zwsqnnqw|}zwusrponmnpsvy{}}}}}}}}||{{yxvttw}~~}~|zxy|~{xvtrppqrtvxxxxwwvvvuuutsrqqsw}~|z{~}{zyyz|~}{ywuux|}|{zyz{~~}|zxusqopsw|}{xvsqpnnnopsuwxyyyyyyxxxwwvusstw|~|zxvvx|~|zyxwwxz|~~}|{yxvtrpnmmpty|}|zxusrpoooprtvxz{{{{{{zzzyxwvtttw{}{ywwx{~|zxwwwwyz|}~~~}|{zyxwusqomllosx{}|{ywusrqppqsuwy{||||||{{{zyxvutuwz~|yxwx{~|zywwwwxz{|}}}}|{zyxwusrpnmmosw{}}|zxvtsrrrstvxz{|}}}}}|}||{zywvvwz~}|{{|~~|{ywvwy|}|{{|}~}|zxwvwz~~}|{||}~|{ywwwy|~}|||}~}|zxwwxz~~}||||~~}{yxwwy|~}|||}~~}|zywwxz~}}||}~~}{yxwwy|~}|||}~~}|zyxwxz~~}||}~~}{zxxxy|~}}|}}~}wqnmosy~{wutsttvwvv{~|{{|~}{ywwxxy{|}ztt}zvvwxyzz{{}{xvuvxz|{yz{xvuvxz{{{{yxwvvuuvwyz{yvsu|{urpnmkjhgfgjmqtwxyyxxwwwwxz{}~~|z{~}{yxxyz|~~|{yxwvvvwxyzyxx{~|zxutv{~~{wspnlkjijknqux{}}~~}}}}}}~~~~|{z|}|{zyyz|~~}yvsqomlmnpsuwxxxxwwvvvvwwxxwxy|}zxwvvvx{~~}|{{}}{ywvvvwy|~~}|{yxvuux{~~{xuromkjjklortwxyyyyyyxxyyyyyyxy|}|||}~}|{}}zxvuttuwy{}~~}|{zyxwvtsrrtx{}|zwtromlkklmpsvxzz{{{{{zzzzzzzyyy{~~~}||}}zxvutstuwz{}~~}}|{zyyxwvtsrqqswz||{yvsqonmmnoqtvyz{|||||||{{{{zyyy{~~}}~}|{|}{xvutttuwy{|}}}||{zyxxwvtsrqqsvz|}|zwusqpooopruwy{|}}}}}||||{{zzyy{~~}}}~}|{|~~|ywvutuuwyz||}}||{zzyxwvusrqrsvy|}}{yvtsqpppqsuxz{|}}}}}}}|||{zzyy{}~}~~}|{|~}zxwvuuvwxz{|||||{zzyxwvutrrrsvz|~~|zxvtsrrrstvxz|}}~~~~}}}||{{zyyz}~}}~}|{{}}{yxvvvvwyz{||}|||{zyxwvutsrrtvy|~~}|ywvtssstuvxz{}}~~~~~}}||{zzyyz|~~~~~}|{{}|zywwwwxyz{|}}}||{zzyxwvussstvy|~~}{ywuttttuwxz|}}~~~~~}}||{zzyyz|~~~~}|{{|}{zxxwxxyz{|}}}||{{zyxwvutsstvy|~~|zxwvuuuvwyz|}}~~~~~}}}|{zzyyz{~~~}{{{|~~|{yxxxyyz{|}}}}||{zyyxvuttstvy|~}{yxwvvvwxyz|}}~~~~~}}||{zzyyy{~~|{{{|~}|zyyyyz{||}}}}}|{{zyxwvuttuvy|~~|zyxwwwwxy{|}}~~~~~}}}|{zzyyy{}}|{zz{}~}}||}}~~}|{zzzz|~~}}|}}~~}|{zzz{}~}}}}~~~}|{zzzz|~~~}}}}~~}|{zzz{}~}}}}~~~}|{zzzz|~~}}}}~~}|{zzz{}~~}}}~~~}|{zzzz|~~~}}~~~}|{zzz{|~}}}~~~}|{zzyz{~~~}}~~~~}|{zzz{|~~}~~~}|{zzyz{}~~}~~~}|{zyzz|~~~~~~}|{zzyz{}~~~~~~~}|{zyzz|~~~~~~}|{zzyz{}~~~~~~}|{zyyz|~~~~~~}|{zzyz{}~~~~~~}|{zyyz|~~~~~}|{zzyz{}~~~}}|{zyyz|~~~~~}|{zzyz{|~~~}||{zyyz{~~~}|{zzyz{|~~}}|{zyyz{}~~}|{zzyz{|~~}}|{zyyz{}~~}|{zzyzz|~~}||{zyyz{}~~}|{zzyyz|~~}||{zyyz{}~~}|{zzyyz|~~}|{{zyyz{}~~}|{zzyyz|~~~}|{{zyyz{}~~}|{zzyyz{}~}|{{zyyz{|~}}|{zzyyz{}~~}|{{zyyz{|~~}}|{zzyyz{}~~}|{{zyyz{|~~}}|{zzyyz{}~~}|{{zyyzz|~~}}|{zzyyz{}~~}|{{zyyyz|~~}}|{zzyyz{}~~}|{{zyyyz|~~~}}|{zzyyz{}~~}|{{zyyyz|}~~}||{zzyyz{|~~~}|{{zyyyz{}~~}||{zzyyz{|~~~}|{{zyyyz{}~~}||{zzyyz{|~|wrppquy~}{yxwxxywuv~}}}~||{{{{{{wqpy}xsrsuwxz{}|zxxxyzxvv~|zyxyyz{{||||{zzyyyzzzywsoqx|xrolkjjiihijloqtvxzz{{{{{{||}}}|zww~~~~~~|{zzz{{}~~}|{{zyyyyyywvux~|yvrqsw{{xtqnlkjjklnpruxz}~}{xxy~}{{zzz{|~||~{wtrponnoprtuwxyyzzzzzyyyyxwvuvy~~~}{yxwwxyz|~}{yyz}}{yxwwwxy{}~|zwussux{|{yvspnmlllmnprtvxyz{{{||||{{zywwwy}~}}}~~|zyz}}{ywvuuvwxz|}~~}|zxvtqppquxzzxurpnmmlmnpqtvxy{||}}}}}}}|{zxwwy|~|zzz}~{ywvuuuvwxz{}~~~~~~}|{zywusqooqtwzzywtrponnopqstvxz{}}~~~~~~}}|zyxwy|~~}{zz||zxvuuuvwxy{|}}~~}}||{zxwusqpoqtwz{zxvtrqpppqrtuwy{|}~~~~}|zyxxy{~~~}{zz|}{ywvvvvwxyz{|}}}}}||{zywusqppqtwz{{ywusrrqqrsuvxz{|}~~~}|{yxxy{~~~}{zz|~~|zxwwvwwxyz{|}}}}}||{zywutrqprtwz||{ywutsssstuwxz{}~~~}|{yxxy{~~~}{zz{~}{yxwwwxxyz{||}}}}||{zywvtsqqrtwz|}|zxwuttttuvwyz{}~~~}|{yxxxz~}{zz{}~|zyxxxxyzz{|}}}}}}|{zyxvusrqrtwz|}}{zxwvuuuvwxyz|}~~~~}|{yxxxz}~}{zz{}}{zyyyyyz{{|}}}}}}|{{yxwutrrsux{}~~}{yxwvvvvwxyz|}}~~~}|{zyxxz}~}{zz{}~|{zyyyzz{||}}}}}}||{zxwvtsssuxz}~~~|{yxwwwwxyz{|}}~~~}|{zyxxz|~}{zz{|}|{zzzz{{||}}}}}}}|{zywvusssuwz}~~}|zyxxxxxyz{|}}~~~~}|{zyxxz|~|zzyz|~~}}}~~~}|zyyy{}~~}}~~~|{zyyz|~~~~~~~~}|zyyy{}~~~~~~|{zyyz|~~~~~~~~}|zyyy{}~~~~~~|{zyyz|~~~~~~~}|zyyy{}~~~~}|{zyyz{~~~~~~~}|zyyyz|~~~~~}|{zyyz{~~~~~}|zyyyz|~~~~~}|{zyyz{}~~~}|zyyyz|~~~}|{zyyz{}~}{zyyyz|~~}|{zyyz{}~}{zyyyz|~~}|{zyyy{}~}|zyyyz|~~}|{zyyy{}~~}{zzyyz|~~}|{zyyy{}~~|{zzyyz|~~}|{zyyy{|~}|{zzyyz{~~}|{zyyyz|~}|{zzyyz{}~}|{zyyyz|~~}|{zzyyz{}~~}|{zyyyz|~~}|{zzyyz{}~~}|{zyyyz|~~}|{zzyyz{}~~}|{zyyyz|~~}|{zzyyz{}~}}|{zyyyz|~~}|{zzyyz{}~~}|{zyyyz|~~~}|{zzyyz{}~}}|{zyyyz|}~~}|{zzyyz{|~}}|{zyyyz{}~~}|{zzyyz{|~~}||{zyyyz{}~~}|{zzyyyz|~~~}||{zyyyz{}~~}|{zzyyyz|~~~}||{zyyyz{}~~}|{zzyyyz|~~~}||{zyyyz{}~}}|{zzyyyz|~~~}||{zyyyz{}~}}|{zzyyyz|~qxqv{qmylvqt|xkq|}}tsxw{{ryvq_sszy|qg}olrswnouotsciynkjqow{kgl|w|{W^ug}v{}xoky~qt|wrrytw{znt}rlr{wtqo]]vzfdefsrxfjlz}}sbt~ypuhjegji{}urnenwkrpzxv~mpvzrjZIUal{toeghfgulvnvwlxqvyp]im}upoqmwz~rq}roswyznz}x}srp}ghl`gt~xtpuhowtcj~xwtnmftkpzv~pyugn}~~vzzqw}mv{r}vvz|vyjpnn}~q^jnnzxq}tnuy{|~mtgoe^alpqtul^mlpz|}s{s~}||wqplo||yqpw~{tkxlqppv~rsopfec]kanfithls}~|{}z}zytuplshnww|n~yv|purtov{xirjnn__ggegjm_lnizx||t}s}v}{{wuspksp~yxzz{sosrn}x|osophb`]h[hadpfnpv{~x}yy~|~szsswil{z~r~txzw{vwqw}sjuiqm_cjcedimfvlltn}yx}t|y{~vxnjip}w~{{{y~s}nu{ztotothf`jd^hYffhrotr{}~}{y{sxz~|r|rhqvt}~y~wtz||vt|xitpoxigojed`h`kngvkw}yu||q{}}}znlmxmzyw}~|u{xxxypuotjeclkal[`abnlror~y{}}tsv~}x|qwuotq{|y}}xzmxpoxjkrkkjfnfmidqfpxzt||}yw~{{pmsviwnntx|~ty}ttvrxniiujin^b^ehflhmvmqz~~~sqxt~||x|sqrnxy~x}~xy{vq}qv|putlnjhojnbclbowvowx||zyzx}{wt{ylwjikp|zz}t|}yzyswohkvklsddaihdifgqjnzp|~}{w}s~|w|yuunruvwn~{z|z|wws}tuunpplsssjbm`cmmhptt}}{}~}y{xy|w|qxuiijtsv|z}xv|y{zpowtiulfdjmggf_cfctrq~wzzwvuwyzwryxxnozpw~y~|{|x~y|tprnlusreil`fmghooqxut}{{{|yz}}s}uliotoruqtyuy}}|qqzslxnhfnnijiaddbrlm{y|zxvsu||ysxyvlmxnr}zy~|~w{~|~urtrowxuhjo`bhacjlowwt~~~~}||zvy}v}}qlptopsspwsv~}w}zmvtkhoqkjic]`]kmhw{~{~{u|tr|}~}yw~zrjuoistr||}~|~zuutorxtkhrfbhcbhhjoupx{}}}}xw}}x}uoswpoppionzv|{|pyxoksrmllh`b^jjbpuzyu}w}ut~||~~~yxzqmwnirpqzyz{z~|zzxxrv{vlnsebf^cffhnuqz}t}}}vztyzws{vrqrnhmn|ys}zzu~wqryqqmnd`_clcdqtyqvy}|z~|v|y||z~zypvuhijgrsww}w{xs{xpmwofjedjeedmjpwqt}{}}vzutzx}}x|vurqdfgsvny~y~|{zvu|turtmeefoe`jlpinsxy{|~zz|y}stx{zsz{nlkgqnqow{y{vy|sr{rikehlhifojnqin{zx{|v~vuwu~|{yywjjisngrw{tw{|z}vyswmhgmqfgmnlckkrou~|z}~~}}}~suw{|y{onikpjokwxy|tt}~|ux~uopjrnljgoilibjuuu~~~yutsr}y{{xllltmgpuxosvwzt~}z~z}w}toosuhgmni`iemkoywu|ttu~}v~{vuqqvlogorrvpnz~|~}}|zpplttoqjtqplacmppx|w|~{|wxuyzzz}vssxtfinqkktpysy~zy~}|{ppsyrjoqqehheibloou{~~wy|vs{}}ttpyuqqjtrtrjoxzy|}{utr{stpltpqjagmllvsv|{~~zvtww}w|~tsu|uknpqhlpnulswuy|~uuw|slopndlihjbjkks~{vxy~~vx}wu{~~y~ywt}wutkrppngmwyw~|y|xtr|uwvqyutmdgmmkroqyv|{~{xxy|xu||xy}znnpoghpkuoqusv{xz{wx}ypsutjkngmbdffkx}xy{|z{zsvyzs}|xuz{t{qvxtpggqww{{tz{~zzuyoxwuofhopmqmjur}yxxx{|w|w~yy}|rruvmjphqlkqor{}z{{qqttknpirhghfisvstv|{}zty|{s}||}~{|u{pwyvqggptuzwr|v}{{{s||zrhjprqsjkplyyy~}|x}wv~}vt|yqrtskqojriloos{zuvw~{uwxupxnnoehfeksqosxvsvxtu}u}{}{y}xu}{yohlqrtwoxvs}y{~uy|{wonrtrtohqiqvtz}~{}}v~yrv}wuwxquwlqhehhnx{utvz~zxxupzutwkkjjmqnhim{{|~|yyyvxt{xswz~|t{{yrmqtrrristr|wx}~xz~~zrrxyvvplshnplpx~z{}x~}|wsw|~{{xvupyxrymihgltuqopyzxzyxtyynlikrvpjjnvpz}x{{{ys{|wvsuv|~~~zsqvyvtnhrkpxssx}zwz||}ztv|~|znpultrnostrtx{z~~~vty~~~|u~{u}xt{plhfkrropryts~vy|}{x|~uuqoswrljlqiqwt~{~}z~}uxrqqv~~|tsx||zsiqkkutuwxsqtwwvxw{wswmrvpnpqlmsxy}}{{uw}}ty}w~{|}y|}t~zrqnmpqljlsvnwvu~~yyzwsstvxwsrqumirkoz}}z|}||{}{{xxvuy}|z}~}uty}}{rrsgmpnrvvposxw|y}|}v{zowwutsnhgkps}}}yz}}u|xt~~~}~u|uqppsvsnlmtroxpox{{ww~{wvvz}yqnqvnlsjmuyzx{}}~utuw{~|z{}yxz||{suymprpqstpmmoqzy~v{}swurtvrkiklnyz~~|z~~|w~}t}~|y{{sssuxxrkkoojvupuwyxw{xuuw}zvsvsmtmhnprsw~}|}xuv{~xwy}~~xy}~{yts{rqupnpqnnqrqt{|||~xy~uy~yuutojhhioyv~y{}u{v{|{}xtposx{xurrphpqmsusrt{}yz}|ywvwqkwrlopompy{~ywux~{{yxyx||sr{stzwsqpkhjnoszt|}}{}ty|zyvrnnnmkrunz|}~{~x}{x}}~zwz~~z{wstxyussvmhrlksvuppx~~zy|~rptlnqokjmu~~}||}}}{z{}{t|~y||~~uyvpxxvvsmiijlpzwpz{yz{{z}}}zqjjllnsmqyvz}}uz~~|zvvx}~wwxyyzwwwzvmqlfknpopsw}||yy}wvzonttoijpuy~}}zz}{|vyzsx}}~z{ry{zyxsjeffhstrzvtx{~~||}xrnmnswlnsmms{||~{sqrsw|yy~}vsuzupxoimmkimswwyy{|}v{}rqprpmmpstts~}z~}xxtyzzz|}z}x{xxzytpmihkqloxqmnu}|wusplrwousmmms}~~}}xuttu~~}}{y|zy|wsyqmmkifhovxvpt{}y|xzytnnpssrqmt{x}x|tvvvsu~|}|zyzvpnnjhokjtrqpot|}{}zvwyvvwotwniimry~~|xwvsqx{u~~|{xqzxqttngfinsvupwwv|xuz{vomswwtmmvqv~|{z||xtrsyy|}zwssrqpsqgmniknrux{}{zwuuv|zovxspmnsutw}~wwzzx|xt{yz||}{rxsqtsqkgjopqpqzsr~}~|z{{ytrquxxqounqx|ux||vqsx|~~zqnoposwmnpkikpwyutyzzzyv|~uxsmooqsuutw}|wsuw~|t|{vx||z}~x|votvtomnnopmkwwrwx||~y{zwstwxuusqxrnwxy~zz{{wuvz||z~||~{tpoptyqqtiggiowzywy~||xvy|u{~tnnrvwsprvzz~}|yu}x{ysssy}v}|qqqrpnptsomjqrq{|y{}z{~}ztrv{|xpoyuputvw{~}~|~zvux{|{{ww|wutoloxrsxrlhknqstvxyu~~{~~tyzurnnuzxtswxr}}zz{{y|zy}vtux|}w}yxytpmoruvuljrllvxyyxz|~~~ogfcpz|wpq}}z{iiwjwzoqnpfhksj^bqzmcq{pjgrqmssy{ni{eyrhef`guv|z~vyjl|mfp~pu~mq{ojlnpvzp_etrkowzv|~}yytmsw`oxuvzy{}~orvzunjm{wpgovzyvsu~uiqy|~|}}re^bs}~st~tprjiqoeo~~}wquuovwqvtx|tmnvtq}vidlw}ujicagrwyz~ztwx|xxyyywmjouustvvw|t||zzyz||~{y}}~uijfiorvzukgee`fqnotzzvrrv{~}}{nllfmnjqsstrsx{|{{{{{wrlijjlsmmlgfdcbdfggnxstvrtywpokfccghotqwwtssw}qgcejmljebea_gfgkihjmqstyywmnncbegefnux{{uy~|{{~zrjemkekjecda^aeggkkluqlou}zsnifb`dkgmxttvwx{}}~~wohgkomjjkf^cc]diidbjrvvut}ptrllieccgpwxxw~zv~}~}rkojjolljd`]\^bgnhhtssrt{{plnmjilfinnpqv|}|}|yxvpnmlkiklcde`behhffksxyt{~zzrjoolecgkouwt}y~}~~~|yuumgmnlklg__aacmmimkmqtx}zrkiklqjgpmkpuy~}{}}~|urtwtoklophik__cccdimnpuuyzz~~snoplhgjnpqr{~y~|ztzxmonnomjfa^_`fhhrpjlrx}~~|xvqkltokmihhlv}}{{|~xuvyvmijnijngedeb`dlqqonw~y}{~}vuqlkklnmnnkv{y~~|~x{vuunklnkgggbciddmkjijs|~zvvupoqmrskijlqx|{}{}}~}{zzywrqlipkfjgb`adjornktvyzz|xpikprqnjnspy~}}}{z}|u{{vqnkigfgfhedjhiklmpuxz|}zzyzznnsnjghnrtx}|}~}|~yvxyqjpnkmljc_cgilmmsmoz||}~{||xsoottrjmqlry}~|yz~zy|{}z{ztmiklkkphchcachnqppv{y{}{y~wwtppnmmonms{}z|~~{|z~||ytskknmjhfegihekrmmrvz|zwy|~{yywtttproioqry||{|||~~{|}}}vmjjkmqllledcdjoplkqyvy}y~|{pmnqrqonptxw~~{}}}~|~|z}~~zytoopojilliheemjmqnouxwuw{~~z{~~ztpuvnoqqqtxwwy{z~{~|zxupnmtqlqldabeimoppqqs{wuz~~~xrosvsnlnrnt~z||z~zxxz~y{|wtqnllmoqoicijfnrrpprsttux||~yqutqtqnpqpswz|}|}|{~yuuuqnroptoliefiihkosmnxvxz{||zywtruvtsrrmputy|~~|}}}zz}~~}vyztmjlprqpiijeilnqromqvyy~~}xytovwtqpolntxz}}z}yyvvwwwqkqokjjjjjjilrrosory{zy|yxyyxspqsuomtqrz~}}}}}||~}yuvwunkmprqmnpghmkmqroklrv|}|x{|rqsttspmnpsx~~z}zvsszypspmnmmpmhghllmusnsx{zy}zwvx|{uqswtpsnnruz{}{}}zz|~}zwtsrqppqnpslkkjjkmnopor{|~x||wzvrtusmklnpz~~{~}|}~}}zx{vvwnjjlorrokknijqopqqtvz~~{zz{zwtrspuvnprsrv|{y||}}{}}ysrvxuqnmtpnsokkkihknnowy|~~~wy|xtrrqponotwu~|}}}{{}|||~zu{zsnlnrronnmihnklrspmry}~}{{|zpqvpppoonr{~~~}}|x~~zuux{ysnsqosrpmjhfgknqupr|zyy||}~}|ysnnoomptpw}}~~|||zwy~y{vutpprtsrstlhlggknollry}~{~{z}}vwtpttqllotz|y}xx~~|{{{{{xuvprvtssogdefgntpquuwyy}||}zsopssurntsrw{~~xuuvy~zwvxxurprtuppphikkklnprvzy|{~z}ypqsspmnruwz||~{~ywy{}|{~}{xy~urttttspkhhhkpnqtnmrv{~}|xuqoouxqttqqtz~~xvwww}~yutw||vstxuqvpjijigjostssw|{{{zwurrqqrstusy}~vwxwvx|}wz|yzwuuvtpnliglmkrtqootz}|{~~zxxxurvvswrnnpt|~~}zwutv{y}}{xxyzyvturtxrqplighlruuqr{y|~}|}wxxupnquvutqu{{y~{{ywusw}{|zv{{wtssrqqqnonjnnmoopt{}~|wuuvuxtqxwsqpsvy}zyz{yw{xu{|zyz{{zyz{ztpvsqrrnihknqtsswux}zwyyvqnqvxwssyux}}yz|zvstvz|}yx{{{xsonppprtlmpmmoruvvy}{xwvvzzsssqrqruxy{xuwyy|yuzyxz{|~}yvw{ysvsqttrolkmoppqxvsy|{wxvutsssuwuuxtv{~zx{|yutvxy{{~{{{xxzxroprruwqpnjijntxwvyzwvuyyvzupqsuvvuvy}{xx{zwyussw|}{z{xxzqorsropqpoolovtwyx{~}|{xtrtwwtssxysvxx{~|zxxxwxxxyww~|~{yxwtqonqwuswqlkkmqtuuuz{~~z{{uxytompvywvwz|{|||zw{}z~zuttv{~||||xu{zutrrpqrutqmlqoqwwvvz|}~|xutwyyvpsxuwxwxy{}~zvvy{{ywuyzz~}|yvuvvtrvuswsokiloqruwyuw}}yz{wvsqrvvutvwu{~~{y~zvssuxz}~|xzvwxuqoqtvvuomqmosuvvtsvy{{}yxyz{yuvtrwwuuuuuy~~ywy}~|wvzvx~~}xttvvvwsuyusronnmlnsvsuww{~}|||~{uuuvvutvwyvrxww~~~~{~|{yvvwwvy}||~xwyzwqpsvxwssslmpoqttqpty{}~~}|zx{xruwxwusqqty~|{{}~{y{vuxy|~}{ywvwz}vtvsrqrsspnnpsqtvprwzzy{|ywwyyuqswxsvvsvz~|wwxyzywwx}~{|xxwwvutuvwvtxtoqoopqqpoorx}}~yy}xwvsuwvsqrtv}~}}|z~}vxyxy||zxxwuy{y{xssstuvrnmoomturstvwy{~|ywxz{ywuvvuxtqtuwy~{xx{|zvuvyw{|{|yvtvyxwvtuytttonnnnoqsru|{}||~{{|wz{wttusqpqrw|}}~}x|~yzywvwxyz{{z|zv{ztqprvwvusrplppostrruz~{zzywwwwtqxwtuuutv}}{xx{}|zyzwu{zz{{yutvz{ytrxvuxvspnlkloqswvy~~}~xwyyxvsqrtutuzwz~}|}y{~{xwvstwyy|~xy{xwtrsuuttutmoqmpsusqsz~||zxxzysttruutrsv|~}}|{zyzz{xtywv{|{xwwxy{yuwtswvutrmklmpsywtyz{|}{y{yxxwrnorsuyvx}}~yy|{zyvtstwz}z{yttuuuuuuvxxrrpkmopqrtvz~zxwxxuxtptvusruy|}{{~~zy|~zwytsvyzxyz{yywy{ttwwvvupljklpvtwyvx{~}{ywwwtrpqsxytwxvx}}}}{{|{wsrrsv}}||wuuwywsrtxutwqmnnmmptwwy{||{vxysrqrsstvxz{|~~~{y{{y|xtvvuvxz|}{xv{yvwttuvtrpnmnrprwtrrv|~{zwtssqoswuxxuuvz~{yzzxvutuyyz~{xvuvy{ywvwvuxtqpmljlpuwwtw~}xzzvwvropsvwwwv|{y~zvvutrtz}~~zx{xy|xvuusrqqnnpmptssrrv{}}~}zvuvvuvtrwvrqruy~{xwwvtuxux~|{zyyyxxyxxsvyuuurljknqtusuww|~}~zvwwuqoquwwusyyz~{}zz{xurruz}~{|}xx{zywussttttsmnolmoqsux{}~zwvvvzwrvvtsrsuwx|{xxzzyzvuxvwxy{|{yy|}wuwstutrnlmopqqtwrv{}~~~}~{zyxvsrrtwwtvvtx|{wzzyustvx{{{|z{zz{zuqqrstxtprnllnrttruz~}}~{zxz|wwupprstuvwy}ywwy||wyxuuxz|}{yxz{y{wsuvusqppooonttruuw{}||}}|zwuuvvuttuxutxy{{zzywvvwwwwz{|}zxyyxusstvyuutmjjkosuutw{{~}|yz{wzztqpsuvttuy|~~~}{y|}yzwsrrv{}}|||zw{zuttsrrstsqnmqrqvvuvy{z{|~~zvtvxxvsrwvuwvwx{~}}{wvwyyxwuu{{|~|zxvuvtrswvvyuqmlmoprsuwvy~~}~xwyusqoqvwwvxzy|~|||{{|y{}xussuxz{{|}xyzxxwtrqruvvsnoqnqtuvvvx{}yvvwxxwstutxxwwxxy}~|wuwz{zwuxvw|}||{xuuwxvwuswtrpnmoppruywuzz|{z{wutssrstuwyvx}}~}}~yyzxvttuwwy|~{{zuvxwspqtvwvrsqmprsuvusuy|~zxyxxwuxtquwwwwvvy~~~~}zxxy{{xwyuvy{|}|yvtuvxzuuwutsrrpnmnqutwxvy}~~~zusstvussvyyuxyw{~}}}~zxxwwwvtux||{~zxxwwusstvwuuwqopoprttstvz~|zxuxytutuvwvssux|~|{||zzzy|xuwvwy{{yyxww{ywwsqrrtutpoprqrwtsux{{}|xvuvwvsrtvuxxuwy{~~}wuvxzywvwyyz~{yyxvuvwwvvuuxutspoopopqrsw~~|xyyvxvsrtutstuw}}|~}yy}zxywvwxxxxyxxzxz|wtrsuvusqppnpsrttttvz}}yxyyxxwvtswvstuuvz}zxy{zxvwxux}{{|zwuuxzywtvxuvvspnnlmprsuww|~{|}}~{xzywusrqrrrsxy{}{}z{{ywvvuvy{{|}xxzxvsrtwwvvvsnopmprrqpty}~~}{xwxyvruutuusrsx~~}|{{{{{{zvvxvy{{ywvwy{{wvxuwxwusolkkmosvsvzz{{|{{zyxwtpqsttwwuy{}|y}}{ywtrsvyz~|z|yxxwvuuuuvwtqrnmopqpprvz}|~~|z{{wwuqrttrqsvz~}zyz|{wywuwyzyxyyzzzy{xtwwvvurnllmottruttwy{~|zyxurpprtxuuyxy|~||z{zyvttuv{~{||xuuwyywuvxyuutnmmmmnqstvyz~~}{{zwyysrsssrruwy|~~~|{||y{wtuuvwxz{|{yw|{wxwvvvtqomlmqqrvustvz}{yxvutsrrwvuwutux~}zzzxutssuzz||ywvwyyvuuwvvxtqpnmlmqtvvvy~}yxyutsqpqsuvwyx|~}|{x{|wvvustwz|}|xyzxzyvtuusssqopqnrtrrrty~}zwtsssrtttxxuuvx|~}zyxwwwvuxww|{yxwwxyyyxxuuxussqmklnrtvuuz{~{zzvuvuqoptwxxwy}}yyzwxwusrtx{}|z{{wz{ywutrrrrrrqnqrqrstvx{~~yvuuuuwtruutstvy|~{wuvwvwxuvyyzyyzyxwxzzuuwtuutqmlnprttwxvz~~zvuutrppruwwvyzy|}ywyyvsrtvy{{{~zxyxyywtrsttuwropmmnqtvwx||xvutwxtutsstuvwxz~~|ywwxy{xuwutvxz{{zyy|zxxtsttsqpoopppswuvxz}|zxvtsssstuuxxuy{}~}xxxxvuuvwwxz~|{|zyyywtrrsuxuttommnpsttux}}}zyzyvwupopsuvvwz~~|zywx|zyzvttvy{{zzzzxz|wuuutrrsrponpsrtuuvy{|}|yvtuwvtsswvuwwxz~~|ywwwxxwwux|z|}{yxxwwvuuwxuvupmklnqrstvww|~|~}}{xzxurpqtuttuyy|~~~|{|zz{vtssvy{||}}yy{xxvtsrtuvurnoqorttttuwxz|~zwwxyxusuttvuuvwy{}zwwyzzxvvyw{}}|{yvvwwvvxvxxuspnmnnoqtusvzy|}~~{{yvtsrrtuuvwwvz|~~~~~|z}{xvuuvvxz|}{z|yxzyvssuvwvsqrnnqqrssrswz|~~{{zzyvvvruvvuuutvz|yxz{{zwxwuxz{{{zwwwyy{xuwvtsrqpoooqttsutuy{{{}{xvuutsrsuwuuywy~~|{xwwvvwvvx{~|||xwxxwutuvwwuuuooppprrqpruy}~}{ywyxstttuutstwz}||{{{zyzxvwxxz{zxwwvw{{xyvuttttronnqqrvtsuxzz{~}yvuuwvtstwvuxuux{~{xwwxxwuuvyz{~{zyxwvvvvvvuvxtrqonopqqstuy~~~|xxyvvvsstutstux|~||}}|zxz{wwwvvwyyyyyxz{xzxtrrsuvurqqqortrttuwy}{xvvvutstttxwuwxyz~~}zwvwyyxwwxwx|{zzywuuxyxwuuxvuvspoonnprsuz{~{zzvvwusrrstuvwz}~}}~~}yz{xzywvvuuwxyy{zxz{xusstuttssqnqrqtuttuy~{yxwvvvwtruutvvvvy~~}{yxxxxwxxuvz{yoeacp|xpqzwrewqjhirpkvw|wsotpjkssmnkm~tfyvifgkzfozaj|~ulzu|}ysmnxxf{|glx}{uns~~~nszznm|kmtvsmjtwkhnsphgv}g_s~~}|tmxvnnmihq{fex|st~{xvv~}qllllq}vfjyriku~rgkw|rgbbdgn{wwz}z}zljq|wnhgioy~|spt|}sknw|uqqsx~wrppszse`eowxzwpkilt||{}}qfcisvxyupnnt|~|}|qihovxzysniimqsssspjjmqx{yursvy}|xuoknqw~}zyy{~zussuwwvtrnnqtz~wogacefjkigfhlprtvyz{~xqkghijljhhilrwz|~~}wpkjllpspomklmnmllljmprwzzywxz}|yuqstuz}}||~}ywvuttsspptvy}}yuqlhgccedddegjmoqtvw|zuqmljgikihhhknqtx|~~}zwsooljmnmmmmnnnnopnmqsuyzzxwxz|}~~{yzxy|~}{{|~~}{yxwxxvvxw{~~~}}}xurpnmkfggeefgijjmptww}{wsqqojkjhiijlnpsw|{wusrrpnonnopqrqpoqssruuvz{{yxwwxyyz~||}|}}|{zzz{}}}~|zz{z{}{{}~~|||}~}yurqppqmkjfeegjlmnptxy}~ytsuyy|~{zz|}}}vvy|fqh}~||laxhl~}hwukkomgp}k|ymqsqr}wz}|zs}qkjkpxnsru{y{wuw{omzrpuuvztu}tbyxdgrxrfZ]pzm`dpxyvs||uszgekng_\hz}odfnttt{yjuxjfjjdaftrhhjjhmynm|sjisrq}{}}tt~|plnx|t|~~|xvtx|or{xpor}vkhr~wlhfdejqxxsmjjknz}x~xy|qnlknrx|{vqopsy{qnu|usppsx}|yxxy~ynjlrxzzvnigglrvwvrljnyyrrtwxvsmkijptuuspmoxyrnosvwvrnnlotwxvsmijpy}|yxxxyyxvvx{|zvv{~~zyz{||||}|}ytsv|~~~~}vpkiknoolkjefghhihgglt||vsrqpomnkijkllkjilrzyspoppqpoollnnpppnmnrw}~}||||zyy{}}}}{yvtux|~|{}~||}|{|~|xtpnmmmmlmlhhgeefffgjnu}|yuspopmkkihijiilpu|~ywutrqpqqnnnmmopooqsuz~~}}~~|yxyzy||zywvtstvxz|~}}}}}}{{zyxz|~~~zvsrrqponppmmkhffffhjmpv{~|xtuspqomkkkjkmpt{~{zzywtsusrrpooopqsuwz}}|{z{}}|{zyvwxvvusqprtvxz{~~}||{y{|yyxvttv~|~sr|vvnjukkkl{k~mhqiav}zzgxownuxxsi}ievsjzxwwz~{{yvxmsjhuxlynuznjmvletylimuxezsr~~}zkkywwznk{x~~~ssjo|{krsllokhlxrd_`bado~oiy|xvqpwtibbcel{~po~{{|trros|vlhhjp{zmku~}umikpw{zywqgcm}yuvy|~tljmqx{yyunghtvuz~~xpllpsxzxvrmkp||xxxy~}vplmqx~umhkrx}~|z{}xutvz~tnjmtz|yy|wqmlnprrnjhhkkmokhebchr|~ysonnlnnjheehox~xutttspnllnmpokjijnu~~}|||{zuolhfgjortuvy}~~~}||vrolklosxz{}~xurmkjjlmmlkkkjkjgfeeglsz~}zwtrolmkiihilqx~|xuttsrqqpmmnlkklmqw}~~}zxupnlihghknpruxz{~|zvrqnkijlptwz}}|xvurpnmmmlmmnmkkljjkjjjnsx|~}|xsrollllmnsx|}{ywutturpqoopoopsx}}}}}|zxqnlhgghjlnqtwyy|~~}||xsromlklnprvz~}xvurqppqqqpqrsppommmmmlnptvyz}}}}{vsqpooprux{|}~|{{zxwurqrrqqsvy}~|{{{{{yuroi~|xustzipyt~tzu}w{||s{zowwhwzj^aa^emqj|zlvlo~jwxj`ajqav}x{wxp|zxw|yy}yvuyoko|sjlv{xigwtaz}tylmjsvoptx|wsef|tu{vb^sruo{w}yszxt|xwtw~mj~yn_W[px~wpprqp}ns~sxopjsy~ujjkknudqx}y{vrsvu}|}uqpxzww}ytp~tlmqy}wtppyqz}~ymrtqxthf`cokquuxqq|{yvnljgddotsqus~z}zuffhfbow~~}yqlsnkgfjprqtxpjkrv{~yx|~vjaegdgqqtwz}ztuvmfddguyzusrpr}xqsw}zxqnb\^abp}~|zwwwsqvvooty}{|wvw~|{}uspimmjjnqvwqmturt|zvmllicnu{vrvnd`bk{}xvstnonhbigc_bcosuw}tnmt}}{vrnlks}{rtry}ywtjgjigjlkka_dkhjikpwvvtqq{vutpmif`fmrszxxvlhjjekr~vvrnkv{|wytptz|wtukh^[[`gpwwrsokjtszror}|~zx{|{wriljs{}zuuyzwz|y}uv|{~~ysmgihqx~z{lfgjlux|{z|}{uxz{vwvrpomiaabeiz}{qlpnqrs}{xpw}~yqelm|wonjkji_bbeicfijehejmntxzxvnlihmpuwvt{upq}}yuuxsmjnikgr}y~xvqoffeiw|yvqpppgnmuwtuwtrsoz}|~~yukoy~|xpmvt~xvswuzut|srifgadfgfnmvvuwqw{~}}{{}~|{~x~x{{z{ootpqkfjoxxvuvpssw}zx{z~~|x~{{vnkehx~|{ulge^hekd__dcc]Ygnuzvuypmkjkrnqtt|}}}xyvqyxzupjhfjjhw{}{pomov|~vuxtnqpu}y~srquv}wy~|{zxuv|~}wswr{xvtqtrollfvrrtosj^adbdeest|y|zt{y~}z}}rvqqxw~~}}rqosswnsw{z}}yzz|zutwy|}r{}urvvx~xxuoqqmqgmhhcaaiis}~}u~zuvllpjbghs{yryv||tokljilh]gmvy~}ty{wxztwnqortvuwqqzvwzqx|w|zuwpmsnpggdgn|x|yrtiglb^`[[hr~z{zyz{v{zvpslmy~ywxxxuqolfqu}{xzw{~v|wyz}|urzxruoryt}{z{yspptifaegoz|{}prnmpb\ddhz{{~y{~}tpxy}yyzwxuuleglfr|xzz~|strwvuwusopvuinqt}zwwx}~rqvqskims|}yxlnogfe^jv~wv~}yuususspov{yuwx~wurmhdcmmowty{~|uxy||xtxzwuysw}}{ss|z}ytuppqnv{zyzssw|{piolhhbdliv~z{v}{xz}utv}}~|rjhhdeqqlqvx~rusmotwwkfkomggsry}~~vvvw~|vs}vnorx}}~~vnntkefejwy}{{xvnrtsogkr{}x|}z}}phiikjjphirtw|x}~y~}~|wwwutsyz{utu{yy}~squz{vyxx|}}vyxy|wtqrsmkmgjmq}}|~~~~{~{~~{{zy~ytlhknmdcklqrs|{{urumqytrnourjhkoqu}}wtw{|{vtvy{xw|{y|ru{zqgnnow}~zz~~~ulyxsppsuv{z~{|wsutnjpojbdpmlmrx|{}||{tw~zpqx|xzwwyxyuifosxzw}~v~~|qryty|}vpmrupkfpv~~x~y{w~ytvzwuuvurkidcd`f`bfmxyyvuyzrtmorsqniitv}}tnkry~x~{{}x{{ztopnoir|vuq{wpu~{|uqrosw||uutz|phnppffjjnx}uw}xwswxvv{yww}vwpmlicituuny~}xwvxt{yvv~|tjnsu||xytz}~}xowx|uuwpsv{vkdhic_Zdgqz{}}}tuszummqy}~~~~wsqtrqnstqwv~~}~}~{xyx~y{uszyzrmjqy~xzzvnltty{{~|vxt~~xxtuxvzvvttlkhfjgvzzxtuzstonrw{~|xtphkgkoleckpvptz~zxnmhilhqyez{|{u~|fgkrhpwx}vip{zvutxtgcfkldj`^lsrysnvsyzwx|{n}{nbdo}xzttxwweaz|gqtjmjpwvyywwgszonnpqpcptkzqtyyzv|gowvzyy|vu|quzhctt_Xaeorxzxz|ut~yilw}zpyxuqzyrc_Z]ds}k\\ks|}}zxnst~tjejgotvy{zooq~}pagrxqqv~z|vnlpqz}~skd`fn{zrfouxuorttsxz~{~{~wpoks}|ubabcbisv{|tz|{y}}~x}yyt}{{}znjgkmqopgab`kyzvwz~njmjlnslgagechqxrqx{rpkqpmjnrt~~z{tvvzukjff^YZho|{|}~toy{ylinqpknea]``fs{{txzppikmqokdchdf_elv~wwsyxqrptqx~{}|}~}trptnhda^cu|~z~vryunmiqonic]]\Zegnnwyqpmoiiqpkbhhibbehp|{mowvtinrtx{|}zsktrohdhdelxz}|yssrwqrtpecc_acjddjw|ulropqoqiijmgd`ehlz}}z}{}wvquz{uxvxsmomqtws|~{x~{|}tovvvonkcb]^^c_^Zbkovw|}wposuvkikpmieehgtx~~~~vvsvtu~y|zxsvwtklimkvzvtuwty{~}~{w}|yykjjidhig`\ffjhny|{rowvrhgpolcgfhmwyy||zy~yx|z}yv~{{~z{z|wprusvvz~xz~~x~x{|||tvunfad`dcc[W[^b\`fr{|zvxnpqpqklknr}|~||{|||zwv~~w|~~zwyzzwv{wunnmluz||sms|wvr|}|yyqnnejnogaaefbbbjn}{|ujkornehjllwz{||zsx}}xuz~{xttvsu~wyvyvyxzzzyyuy|z{yyw~{wxx|~ymdhddadf_Y[a^]Xadjytmsxtrlqty|~~~wuyvz|{wz{{}{{vsx{yyx{rtvxztrrvw{uz|ukpu|~{}{~|ywnjmkonhecighbcfjr{|}{}wrqpnpkpno{{xt{|tst~zuwpmpkquyvtu|wxyywvy~y{|~|}{sox~|zxwzwx}~|swz~~wrmg`hjibYbef__aeht~~z{}vqwx|~~yx~sqqqpqywsnx{|xy|qtppry~wnnwvvprxx|yxzvurtsx{}wryz}}|xwwpmnrkhhjnjggllw{~}swy|yqmrw{~|{ruuyxz}sjgjgnoomkruyvy{|yxxv}|}yyyzyv{y~}}xy}{vvzrsqqv{~zwv~}~yzzrtoppohecgkddcffjtxz|{}|{y}z||ytqjmqsqhmrvrrsxz}xutpvuuppyytnqrvw{}}~vsvy|wtmmnqpr|~|v~{{xxv{{shirqpgimns{~}||z|v}~|yw|yz}y{|{zwskpnjgllgefoomiosw{}}ysv||uryy{{}}{phnnottwstx}|yy{xsuptwvqihimggeeksxw|zxsx}~||}urrrturnkhpoonloox~zyy|ysopvuvsros}{vt{yyxvoiedkotolrz|{{{~~yx||}utvuonnoqo{~{}yz}|zyy{~~yqmjjiprlaagijbfilp{~uz|{yv}}|}}srqlhntqnktyyuw{|yzutswuomoupibhjmuv{}~wvw|{uut|yz~~}ttsotuxqkjookkjps}~~}}}{rtuxsoqsy~yzz|xvvmjhihhgihgopttuy|~~||ytt{yysqos|~zx~~zxw{~~~{~{~~{qnkikjmgfhjfdcchgr|~|~~{zy}xsnomnsqjelqvrpvz}|zxsvxunipprmmomlt~}}zyy}{wqtuzxyztxyvu~~xxtsqwsnkowtnhnos{~}~|}xuy{wtqwu{{{{}zyvpffebgilgablpppqz~vvw|yvwy}~zzz~|{}vsrwwyy|zw~~~upmlimkjcbijmhfdioy}|~}tplklmqlkjmmopqwwzzvtxzwpnospomort}}zz{~wtrwvuurnjnnu{|wv|xtvywkks~j]b^Zgpyrgbn{jYbaesypw}yyjmz|ecpyu]Xb\o~~voylpzyqx{kfdmo}yqpimoo}d\}p{~z~vzirwmnqic]e^ahpznx{}{evtdmvx|{wvrppxwrqzqtvylpz|aLr{rliusw|pmpsgfrtzwh]^isqilofgcoz}zljwsopzmjg_isw|kqvu~{y{{}zcR]xjhnilhbifjt|vhlccj__ZRPWhswxqnv~jcusiniadajwt}rntqxzqxxww|tzpi_JJazulnrnsso|{~vjja^d``aqwwxrlkw}m_gzytumojessxw}yyywxxzy~ow~~wl^[NJZp}y~{{rtoojryqruvoovz}wwtxz{vxua]hx~|wqngkvq~~~}|wsv||v|y}~~tgbcTYjz|ttqso|yvwwulsssnknprot~{~xtzrsn_dr|yzxottr||s}||xzy}z~wwy}yufc\R`qzwttuuuoorrmwtvpkkmlhn{s}}wv}|~spipkckt~txqqw{{}y}yyzvtxyoiY]UOaszrsqowjkkqpo}xvkbfjhelzv~xvvrv||mi`je]ht{y|y~yysooldfY]UO`q}pmjivjjinmjwoog\afgfjunzz{ywuy}}nj^gfeuz{{z|xsmnmfh[]YP_lrlngtjjkmndtsto_`eeeanmy|yxzvrglupy{|yyutxsvsrnok^_ZY_Tcq||{nmalkkrszpz|yzhdifihst{wwwzwojtn|}y{}tuutxqqmmgfcVYWWaYis}z|lmblnp{w|~|qlqnqmtu|zws|vrgank~z~zz}~yzwsxplfgcgeVXUU`Zhrssdmkpz|u|zzrpslmiqr}zvpy{npfckjzy}~zsywysuyrmknkohX]WZaat}~vqckenvw}uy|mnmgifmo|wsoyxpsdfhlzty~zu~}y}t|}yqqnmsg]cZffm|vwjfjgvy{|pyrkrlhfehp{x}vuorzomk^fdt{ys}z~~~}||u~zqknkpracb^ebqz}ttekfkrx}wu~rttjibbctx|~vspippgna_bh|xz}|{svyslnkjrd\_U]\eq{sufcbaln~s|sovmjfbcpyyxrphmtjnh\`_pz~yu|xnmmhoiX_XZ\]inwtl_d`his~|rtyowsmmhdhvwyyupytouc`_iy{vwxoilijqc^_V[Yagnwzjeeflkztxw|spmgepx{}{rszpwsedds}z|y~}}}ouqhgjhno]^WUZ\fiv}~|hfgnps|z~}tsojjuv{|zxosumyqeads|y~zux}}~yzzqulddebigY\RTYakly|igjrsy|~ywrkmvryz{wmpnhtj`\cq|x~z~pu|wzz}{y}}tvkcba`ki]`SSZdnp}kfhqqt~{xupjmvpvzzxmmibpjc^coyyy|nqvrx{}{|~xznfggdmmbeWSZiuyoefnos|w}{vtpikvqq~wxwoljamleacmu}}~z|sqxsy~{~}|{~tjifbfnei`U\kz|vifosu}{uy|wuqifqogu{stuqll^ejfegow~|~zs{tu~y{}{}ufdb^^icifXXfx}~xymhnrsy}xx{xwsjcnofqxqqqoih\agehmu{}xqyrr}z{|xzsdcb]Yc`feWUdw~~{~|uxmflorwyvyxywohrsiqwrsqpkmaehfkrx}{urzqnzyw{{{w|sdca\W_\feYWfw~z~zv}unkprtyyxx{}}smvsltvuwutppdihfmw{|stynlz}usww{}v{maa_ZV]]jeZYiwzz}ystuvx~}zx}}}ppwpmvtxxuqqmflfgr|~|xnvukm~zurvs~ttuc_^_WYZaiaY_n|~}}xvywz|xx}|qvuloqovsrmphijdgvz}~{yqnwpjs~zstsv{sxob_^\UXYgf^\ht{}zwzxx|}v}wtwnjnjsusmmkeia_kww}}y{uktrhiyxuwuwyxid_aZ[[ekc]cky{z}}vyyswyvy||uxrkoknvspjlehc]brswzz{pptmhr~x|x}~x}sleec]^`li``goy||yv|ssv}|ry{yyxnlmfpttmlkija\hssv{}{qwunnzy{vzz{rncf_^]emgbght}vx|~vz}svwwty~xzsilheqstlnkkh^^nuxy{uyunr{}zzxxyuqkac\][glfciguxrt{|u}}stw}xvxz{rkngfpstjmknh_bsy~|~yuvqmsx~|yx~vvqrkaaZ[Zehcdjfuwqqyzt|}uuw~zyyvnqihnsulooskacry~|}yvvplrt|~{xw}vvqrnb`YZWafdfmgr~~zrqx|v~{yx~~|yxnrkjnsyoppunccqx{}{wvpkqrvy~zxt{{vunoqdaY[Y`ffhqlr|~usu{y~~w|}oomjllwpnnutidpv||~z~yyvwqkourw{{yrvwsrnjrhd[Z[^dgjvvv~}xts~}~}yvqkkgjdppllswnfnt{wvyuwuxrkowq|xx}|rturspjqkh\Z\_eglz|}}xrpz}~~|urjhfjblnjjpvognr}{u}~wwsvvytknwqzxxwwwsurlrlk_[_bfgl{~~xqpx}|}|trjggjakmjjpvpipp}{v}{xswx|tlsxt}vz}xxuquqormo_]`effm|}}xqqx||}yt|rjdhiakkllswrmrqzx~~vty|voxxx}u{zxwqotoronm\]agffr~~~~vpsxz}~~vx|{ohckeekmnovwqrruxy}~rv{~sqyv|{yvxy~xslnqnqjpgZ]dhdfvz~~}tpuxzy{~xpfildjjooswvsuq~|wzwpv{~xottw{zuzv{vnjnmqllo^Z_fc`kvy|zrtwx~~~wmhogijmrrvvtuotzy}~nry|rpsrztxyuyyqjlnqpiqg[[ce`cqs|ytwwz}ujnmiliqsuuttrmz}xxxnt|ypqntwtvxr}wnikmslmqd]_ge`iqv~zyyy}zrjoikiirtuttwoq{~zwwrmt}}wrqnyuw|yvrukhjptjqpf_bhecmr||zxw|~yojlfkegquustwnsz~~yvvpls}}wupozty~|vuukiirskrog`dhfenr|yvv}|{wnkjekcdousqsxnuy~}zwvrlr||xwpozuzwvwlihqtkrnjcdhffmp|~zvt}~z}}~{xqmjdkcakttqtzrvz}}{ywwnp{}z{tp{wzxv~ylhfntlqnlgdggflnz}wsz}x{{zxsnkckfagtxtu}xy~}~{{w~{rmv}|}zqzyz~{ry|~ogdgrlonkkffghmnzxyztwy{}xwwupodhjcdozxv~~yywz}}~vnqz{}~sxzz~~qvx~rfbbmkmmiljgghnnuxv}~ww|zyuvvrregjdckxzw~wwvyzy}vonuy|uxyy|svvz~tga_iimmhklifhootyu{yx~}{ztuvstffiddiwzx~wwvzxw{upnsw{uxwx~z|svuy|tha`igomhlnkgiqpvyw|zz{|~yqstssfhhddjvyxvuu{vwyvqoqv|}vyuy}y{}}tvtx{tiabhgqmjnrnhktqzxw}{}}z}zunrruqfiedekvxz~ttw{sz|wuqoqw{yzu}}x}~zxwuyyqh`ffksllqsmhosr~wx{~{~z|xqmqqvkggbdfmuw|}{utzxs|xwuror{y}wx{yz{vwywpfcgeqpkmsriirpxxvy||z}{~y~xlnpuqhibcdhoux{{yuy}tz{xwvqou~z|zu}}y}~~zxyxuldhdkrllqtmflop{}ww|{x|~|yrlotwmlfcegkrw|yywyx}yyyvpp{}{~ux}zxyywvsiggerpmottjgnmuz{wz}xz~zx}}pmpxuonefhknt{}zx{w{zyzzvos}{~|tz{xx}xwuuqhjdjrnoqvsijmoxxzx|~x|}xzomrztsngikmou~|~w|}w{wyz{vos|zzuzywy~|xurtpiickpopsxtkllqyx{y}y{~yzpmsztvngimnnv|w}zwytwz{vnszzzwyxvx~|ytqsqljclopqtxumnlsyy}z~zz~~y|rlqytwphjnoms}wnknqoqmhqrsrnv}g^\^`dmt{~pis^_uwxqmp~xsu~klwp|~f[Xfg_ZWTWkzo|}ylrrdkquntocq{~qu|wnXZ`acelwletmTs{z~~zwpio}zma`r~zwiebYX_mv{}wg`ribhcbfie]]m}vrqw~iir{|v|tiihpyogxuY_x{xqjcacqw{~|keZ`lzzx}pb[qyih_UUZ]_bnvsmeadhgdce^jx}{tv}|}qjypd\l}{slgegly{}~|{}}rddbmv|~|q[X`|urg\\bjmouu~wtjbaca^bkgpy{{vtz|~z|zrkx|}zpcacvwqonquwnnksw{xy~xbTXe}yldelsuvxt|zphhihdksnux||xuvzywwty~|xzrnwyolc_cj|zyyyzytouv|~~{~{|yux|}unYSZltmnvzz{wt~uopqnisupuw|}ywuz|rrnmsz{wvzpquw~~sgd]aeo~~}rquu}~yx|yxwttx|}yrpeUW^sztv|}|}uxvsvuomwrstw}|yxv|wpnhkqxxtxwpsrvywy~yic^]dht{ptsv~u|zxvvtrvv{wqmn]WYcz~z~{t~}vwxumsurtsx{yzx|~tqigkswtuyrsqrwtsx}{ofb\cdkutrsrx{y{zwusqtux|wsmqg[Y[j~vz~zxyxppvrtrtxwxz|{wogiouusxvssnttpry{ujf^`dfmv}qsqsxz~{zwurrus~}xtmlob\Y^o~w|}yxyumqrrspuwwz{}zmikrvttytvpourosyzqjc]ddhowvqqpsx}~}{xtqsss{vqjnla^Zbr|z|~~yxxqlpnsqquwx{}{nkmtvtwxvwoqspmrwwmj`]dekrztqopsx~~|ytptrs}zunglib_\eu}|{~~ywvnklkrpqvx{}~|olouvuxxxyorsnmqutlj]]cfnu~spnquy~~{tqtrs~|ytlejfc`^hw~~z}~zwtliigpprx{~~qmotvvyxy{pqqmknrqki\Zbhrztpnqw{}}vqtrr{~}zxsjbfcccakz{{{wskhgcnqtz~~toosvvzyz~trqmjlnoii^X`huurory}~z~ystsqxz|xwsj_``bggp}y}yskee_gpu}}}yroruvz{yysqmihikffaX^hw{ttrwx|xwyzyjwss}{vrqi\XW\bgvz}y}}|xnfhZftv}y}ztzqx{x|rnlgheha[aQUcs~{yyu}zzwouqszyumnhTVR\qpyvy{w|wsfqf_kx~}}{xwwuy~zslk_b`aib_Y`]nz~ynz}xx~~{zwksjlvqzuni^RJXbq|}{|zxyvcbl_jmx|{yysw{yzy}ojhdV^X[^Tclz}{}zxwzxt{|vy~otoskpninb\[UXnz~r{~xwhacehm}}|}xvxysyyoocdhb\[eYZckx}}|}z{vzz{uyjppltupoth_YYMZlv~xuz{tfaecjn|}v|rvy|w|jbddZUZ\VYbny|suzzx{wz{tvpjkrljnieVWVYcp~t{|{sjdfZjmu~yvz~~ysxnd\aaaX_bV^bv}z|z~w{w{wsplmaiotomp\YWVWkw}~{yr|vnl`eblz|y{txvwxvq_a`cWaaZbbls~y~y|{ywzx}uludgjsnrqhb_ZR^dwv{x|{tpjdbppy}w|wyyw{wiffgYVXZc\bk{~w|t}yq~~~~x~}suokmrlmiil[\S\Zm|~}{usnoeZifsx{z~|{uuln`c`]TZ__d[fxs|}wwr|}}y|unjrggjolgpadUKYantpw~yvdifhfitxr}{v{zpvgf`SUWYT`_fp~|{{{{ws|z~{{{~{htmii\gjjb^_WX^ltz~y{y|nlidjlpv~|yx~vrwunba\`V`^]`aguz}|x|~x||{{svlnceaehnqi_]XWhhu}z{{}vpqk`hfrzywtt|}w~vj_]Y]^V^]jjp~trxn}x~{uxwxuvkca_frhh]]_Q`fz}}nwqtojfdnh{~|xrzw{qp]`USUZZ^ply{|}~tvuu~|}vwx}{nna`_WcejYa_Wb^t~~}{swq{nriZjlsy|~{}mysttuoccTVOP[cpsxz~mksqty}}suvuspp^f[Z^Xe`aaY[huxlrnqjjdilsz~|xtxswlpqhbVMMXRgw}}}|}|wwrr{{~uzpzniobc[SR_^]fhahoz{yyzovxomicryu{xtu~tnrgad[VNXU`t~{}tmsqs}~z~{nsukrm``\XYb`]_^_hz}x~ttshlqblqvw{zvn}xrymeh]XNZZXku{}~vz}rqomz|||uvystudb`WUXY]]ddetpz~{z|txtlqpiignr|}vytovonpibVTTSblsz|{zsklov|{wpqwumh`Y\\Y[Y]_cnnz~~z~{|qinjorkpqvxz~}zuxxqqkhgaZVY]_k{}y~~yvirprv{uuzoro_\\WTV`aaeafrx~{{y}vtriongnrvw{wqxtvxqpoeWP\YWhotwyumqkip~txynwpaeb\ST\Z`f`iqrz|}zuurnpsnqnmnyy}wtsortmbV]UTcdm{~w{}vxurdixz}{tpwshf`ZR[_\^a]alrw}|~z~}}}xpikqsppnwwz{{~wwuxske[[WV\]jw}}}y|zv{rqejwy~ustqrnh^SZWWZ_eckonrzyyz{woksujljrswvpqzwqmf^UZYWbjtw}yu~zvihkpv{yvwntqhbX`\ZVU_afnotxz|~~yphkxnompos~}xr{umlhf[[\T`fprw~uxuvlsomrs}~|{qowri\_YWTT[bihgnq~yzxyulqkkinpry}|zqtzqrjid^`ZX^clqz{wypothjlx~zqnnpbhb\VOUT`mdnq}{uvptknliorw}|~wxmojhigc`]\_ihxvrrmwvpfkx|{|zwkqlef]ZQUYTdgaiq}|xxxwvtcjegrw{~~x}tnpgghigbbW_em~~wwmnorpeszyz}~tgkimgdWNVQUabijz}{qsq{pjicgsz~kmfdhjjhhd]cerv}zxrpsovkjss}|}}toekec_SZWU\T_bm}~uwpwrliadku~}yyskaeijlkf`a_nt~~~zqqimmpur~~}uvijlgfWZVQ\SXbertsunttqrbchly~}yymbfiimojej_eis{}~}}xwopgmulz~y}sjfed[c^V]QPV^jo{|tsomredlio~}yweghhmphajfegqtuzvoodmzoyww}v|yujke[d\X]SVSZ`cvv}tuxvvpnkhklqyyyurtnppdqwhjoje`jisyz}{uuhnmozuw|wotujo\bqadaUURXZdnmtwxtuljqokowow|{tuxrlrrfjjle_j_p{}z{rmwgquhtot{yws_fhah]\VMVP`fg~yz{gjqjpmtuwx|}uzysonmhrojpi]^qgs{~yroustrwvesv|{svpegdejb^aXPQ]S[wy~ywxwsvtvkhho|{wvxxcgnjcnldefitx~zsmmjmptvtt}}|yqus\dfc]`YUYWT]hn|qpntry}lgq|wz{ux}~uzlsnogdfgledsqv|}ztpprqossmwy|xt}nvzgnefd[WW_TNZddx|tt{ss{rjiposy|~wkvmlklfbmjaehrzx{ukjljjmr}xx~vrtesdbgdZS]XPOV^lz~yqsrrugkstntx}z}lvsnd_gcihjlhq}zyxtqwsqlrjnwwtxwt~homcdWZY[XOWV^dxy}u}{xq|pirsqjx{yzssskoemilmelflw}|}yqypulnmn{}xy~rnmknbhb\`^fUTTU[kyv~zwyzksxzplrq~~}uxoeagfumlnmxoy~}|vzzzyzjjap{pz~ywuwpsrjiZYZgeUZS_Zemuuvkfzpqovot|qxwreih`mfqftvtxtyvxm~sssokczsy}wvulnwnngZ^T`W]WT[\i^q~}{wbsursirpz{|ruxtnbj_ka`nrztwu}zx|mt|v|jfimoiz|zspgkmo]^Y\[N]^]]c]dzomnrjllxv~{xtzjmtoffa`lk|~~z}poqqrtopqtzpwyrsimhelca^WR`[_fjaft~us}vpmtrt~|wloqrrsigkn}uuww|}zxvrw}|tpmz}jyt{~~upigihdlm`[b`ic`]Zpnyv|ormqtuzuy~vuu~skf^iiruwz}{|ru|tzvossvnzqzyz~{urphnne`W^\^ffa`skuyy|qvosttxmuqstrxumeoltsix~x~wwovu{uuyvwltxvxovkkegqlhbg_eiY`chdgnlpwswtkqwuzwy{jogijiisy~}r|otwwxk|z{|mvrtonvwywvs}qiqcljflqqtx}{{}{yktkvyqoiuenw~zqxnyunkryyyzyu||~{zwsh}rulr}z||~te}ode\qqqqlzxyrqvmlyvvpzwttx{xgYpomodwx|ss|q}{vsgzugqmmegt{sprrv{urjz|{faqikhappuyuht~}{nkk`lz{tjwzzmrsyuq}umg`odfbnlcokxvgoxmzxqkvvptywmhuvxqoucuz~|}}xt\bd^k|yxkopyoww{fgm^e`mkdfsuuzqrqywxzxwpk|~tpr{tertsrvuoz|}x~zw}qfbbbjs}wv}qxoowxzmacf\]smkuuv}{{kjmtu~~}xwrvxrlutxspwspvwmz{u}xz~}rl`[mapuyoupvp}zzvilab`amjpvvv{~omwqy{{nxzqwvorp}tqsgwuuw{~{y}quud`Vhctzsuu|r{}zu{kc\_\`qn~~xt}{pihv{stqsz{letxzsvpwuqsvz~}z~}|{quqodZlbv~{|yvottvx{{tq\d\^oi{|mz}uiotu{poo}|snp{tstfruvz~}|~|}zyosiifXcisutvtzu}z}|sk\YWhgntt|wkkovv}ssmfynen}}{xxqv~z}y}|x}{zsutgiodbne|t|t|n{{~z{xk[aXdml~mwvruo|ry{zpiqwykv|}smmvr~yy~{v{}y|trm\k``dg~ru~}sx{~y}|sg[XWjduoottppy}s}{ongr|}}qot}tyorx}ux{}~zx|ztrstaglfnavys~wut{y{{}ubhVegf~{xxsxwqytwyy|rogxvznx}}zkqvsv~y|x~|~{t~icj^hd^vusvzt{}cd_Z_cpvz|ovunorz}y|wicsjywwz{uoz|}z}z|zxz~zxs{wtrechhf^nu|v~u}}tkieYf_dw{|w~wy|xzo{wrxwhdks~{zzuow{xu~z}~s}xr|vv}i]k[jd]jz{zx}yy{}gcbZ]Zntuyrtwopsy|}}n]giv{}}}~}kwy}zxytyp~~w|ymuyttbcdamgllvy|yxxu{hk`h``svx~zxxsts{}|vx~{v^jj|x~|~vuqsz{vz}wp}tmwxtbgcdfdpp}wywz}wyojYca_k}y||pxwmm~|yx~s|rc_h}||{{xvu~x|tuu~y{upsozy^_d\elrt}y}z{zwsg]e_fptxzuyzxn}~mxwitcfix}z}}r|{y|t}yn~{}yx~vqngmxued\[ge{x}sv|~syxmp[\heirz}ysstxzwsqjpvkabxyvznuxp|so{tw}|w{tigghzh]\Zfk}rx{vx}{slwf\fpvo|{wrvpw|~tksjkyrpkt|zzz{tp~zwsxxmz}x}}y|wjgidevqi`Zhqyp~}xw}zi{occkvvz{}xvmszws}oqmkzolmy{xxt|q~ytx{oruz}~}~~~pekegpigiU`t|}s~{zz|{mqre]nxt~~z~yqrq}{zspblsmsmy|}||vosygstt~zulhadcijifZbn|{}t|wvumzpabkzzy}}un{}t}u|yoqdhonmw}~zxwy~{~stnkqnz||~{y|uehf^^kaqiU^u}y{}xuwu{xmsngfs|x}}lovrxxuxuh^oooq|wzwzw{zzkrmqrp~~y|{xmjb_]egguhYc{}yvtwzxx~uzrnjpxx{~}tltwr~|xxxzgcrpusz}zr{v~z~|nppzpl{zx{}uknf]akinl^^kwwy}ztr{qfmqs{|x}|rr~}x}|~}vuogjkplvz~xyx~~uqgxrft|}ighgYdhepcWdx{zv{yvtvtnijqt{~~}t|sxy}}studkkgjssx~{{wrmntjfxzpeidc]c`mgb\ewy{~zzrxsu~phmpux}uz{usvy}|trqsjhdhqxw~r}{u~{qcsuiky|~|}|jchkZ[chqgaemzttvv{t{|tkqt||x~vvyqt{ryzxsuqpl`jz~x~vw~uy|ufsvnp{xz{t|~lfgm`Zaoug^cr~yyrw}{{vqvqr|wwy|u}zsvywyx{wwklled}|vy|r{}wnmtpmk}}{|{vy~rkcg]`[mscafm}~|vp{xxunuwrwy{~zvh^c_gqxeYY_n}qw{~}jq{eisvaT]vw}xvq^Setuafx~~~{pa_ekZi~sadhafgot~pu}kist^V^uxsowvv{jkn}wpsv}zxuzkn}xpk~qmrlely}jvov}uo|pcozuxkimtnt{xtfp|ylk{tcddguxtohg`hmx{touzzkq~{xpyw{pmfmjsxriakmnw{y~sveW]i~~}tz~sovqqmbdoo~{ngp||tjfgz|~{xifq{~yvec\bkhceblhggcabosptsku}}u|o\Z\lwwpw|rmvrsrfenlv}m_blu{~~|x|zrrps}nknifgeqnkifdaggdkmhs}xtppk{{ttzwqcej}yoxtll~|~}{|}oemv}vwyw{y~z~y{z}{qpmfcdh}||v{yvsoels}{okbdffaV\f{qtxrn}~|}{~z|souxsv{uympqtuoqyw{qlomoopidloo{{tqp}tje_hhnaWV^kmjqssnbjh`aqyzw|{xikkhs{}lkfggkibcnjnw}}x~}rqh_YZgp{x}}iaff]kx~{{{zxrrpokw~~~~|vuvtuv{xslY]c]c^eprz}}}x}~}wyx{{|}zmntqbklc_Vfszwyqqpqxovpz{wy~}{zttnqsr}x{vfjsvlsrqxlry{}~~yuojlvqmvnuz~}yunw{z|}zw|u}rfjuxz|zxxcbj^aftryvaZVcsrrxujbduouykpsuxp{m]\gutthtyvpq}tu{xt|{xg^\l~vfzd\q{i`_pyuwyraS[ziZs~zopk|xxw}{vmnnpsuo`chzx|ust~}pnuhv}wxonv{k]\SHWp}y}lbhohi}ysu{xz~odem|wtwknqz}{vqve_\ZUeqyzmu}xjny~rlrsvx~qmcklkXQLHT`w~y}{xxo||wa^jwpliurwwtx}|vkmgou}~`WXadstwzyy~wnfiffgzvqx}yvq~mux{x{}yxg`bnz{vlvzkfda]ikuw|pru}uuz~kgalw~qdcdhdoy~~w||yoyx|prllsxurz}zz{z~wpqe`d_g_bo}|umnptvrliZilp{y}nsqipvurvfg[h{z|rzwvt{wv|~wz{vxust{~wvxtxuq|{~xpicdmwheh`fjspxn^and\j^dgimu}{|rk}utunrnu~t{|zq~mpnxrrpiisurzupjm{rx|s|}wssqpz||skopvkbgceblsvtqgnl[kj|~wjwe^il_i}y~x~}wxzoo}nnop||zyuqmr`e`T^^bY]_dlvymvv{}xtqnzzzstxpu{}wslonstmbZ^kjoxxzy~y{}yxyzvur{~}qnzqucfY_b`d^c_khn|xz{|twywunqifnmyyztzvu}t~vxebnbhiocceit~xz}qZpv]utyyws{pdyttvuvysu~x]{|^l}}|yqssfrwj]_ig]UUg~{vccdlkqvw|xro{tjc{vdrvettla^jknkw}n]Xk~{}~unot{}qj`dlyzrp|wqimvsslc]\lr|s{|~s}ymlovyxxmaXgzypl{nejxvnt}~{|uyusumibelqsi]Ujs|sl}zvzbZ^kb`ur{}rlwzuxnlmtz{wguywonkdlx~truhkajjdtou~~}qvyu|rqhzvvpcbaYbgtqz~ws}~|zz~~oa_hmwu|wovxxpnkgrvsxxuymotj\s}}|~~yzwnqpq~~wz~zxpotr}|td[]`ffkwsjqqq{z}{rccmlmkmfr~nsop{ws{rx{vytr||rnefgajqx}wnywrljso|~t}xxh}yxy}|~ysr}~zpqounk{s}~|sk{x~qpiq]\hi~qwxokpzx~{{||{yxtmrrgw{{}wwv~{yrmsmufjrnmdhpx|sdZYWXYjwuvy~{prqptuxtx|z~~{{zx}phd]bcb``hr{y|ys|vyuxyzzvpflnfkfec_ah^ZTUTSnp|y}|{zzt{~z~}y~wx|}lmpvnspoh``\`QYb[qpxt|}yux{x}}||utxzzuehhtnozjjkmfag_njollsy~zv|}vyhqolkw{{xu~|sqlkwmz{wvisl|rrqkhrqu}{uvsrjixop]^prvxpilv~z}p|~nmidVQ^Yd||tpzuxe|lmyy}xl_sphzwqmx|xp|tos~vmhifkljkl||qy|}|~o_WL`qzzo}uz{zyvv~kYl~}q]woyqefmrgopsvnvxq|o{{p~|~|luvwrcaiz}xs}lkxze[ffo|o\av|rfeffs{u~r~mnpt~|diu}}ywuuv~v{{~peMKX[hs|plocfmktk`^kx|wuqwwvn~vv~~}}w|rmwtsxy}{pX^oz~rsmqzp]^lw}x~}skjehwu~~x|ysmhj||ghlvnzwv~wsouk`ahuz}ui]]`^ciy~lmls~qbfqw~}tg]P^cnsa_nyulmijhmkotinxvzxyy}{vww||rliuz~~mvw|{wwyi_ggjktusl^cpipmpdjohr~}~uo|~xwuwtmsf\^e{|x}}wtrw~pvy{vuvnhcpkks|{`\\VX]o}tk]gcboz|ytsdeq|syv}}|vuluvx~wutxqxwwwlikqy~uuwy{rpkckqy{xqmqsrrp}~zx~um|y|{qmwswz|y|}~xu~|z{wxxsfa`aehjUWTbgltsxulkef^jus~{}z~~wmo{nuomeclrqgbnlxsuggjefp}y{tyoagp|{vsj[VW]j{}|turkfae[gmlusqet}~oktvupltopidowxqirq{qljc_alqhon}}}qouu}~}}xusv|}xzt~{z~tilllhz~|vxztytuodmwvrfhcqmod]^_dj{zotwwrmosmyryvspiklzqpjovz}vx{~vipjjrn{z}mkxumheRR\]`Z`czxvry~xzy|~|{ovqyw}wzuvk|{trskkjwxvqnxzwvtzs]^]Zhj}{ry|{vrlirhl}{qxyrkoow}}~s~|{qxuzo~ywymkgorrjbihlgiky~nrtovn|v~ql]flq}{qktuztoditt|sjaedkst}ztvwqtwxuvxwyspp`[TZalkl|xvrosl|y|~}||trxzv|tmsddmpxtqfnu{ymegfmnlwlhnhnlzzvo{{yyx{}|{}p}t~}{~x|~|}ztjdl|yu}yssvhmqu{vrgppy{qa[[\edlw|}y~kqrmn^]X`cdyysvuxyyy~{~uvmvouv{}}v{qilgmle[V[]nrojrwvqy{tz~ujyz|y{}uv}sohfk}zntrqvzutqfdf^hmy{y}y}~sywzrsfkilxyzz|yz{{|~neqqjpv{ytyx~w~ysmm{uvtnnupqynhaYb`kvx~{zwsorzuwntqcmkt||tqnwwmyz|}}|}xq}}}}wkdgwmcc\Xaik|{~~t}{vpwww{szkmn~yww{|{rysnp{ppywt{|rrkqrtymyvxvz~~~sx{zzuz|yqhfu~txv{|}vsyw~ztundlms|}||qtvov~~}|w|votzktztqq|{z}~}~w~~xknkja^qhjru|zw|~xkoputtx{uojmstuzxptzwv{ketrgaYR\b[mx~zvnvx}wovyx~stwpu}}|sginplw~~yeils}zzzz}x|spvy~{nurlotsv|ni}}}x~|}vroloxoooixtv~vy}xgflnir~v}|xyx~wsww{|solZ_]Z_jq||x~{}||zs{{vzu{utvty}xt~|~sedgimmdsnacq~xxut}vnqps~{~~~w}~tpwpwtrwz|}~vqeoyolfhrzwwpy}yy{xny}|xoxxwqrxwzwv~~vne\ikljpu{|{{|~}}v{vzzqw|}|}{ulr||~x~ymr|wtrobghnlcflibnvxzy}|qr{qm}zzyxyyutpusm``Z_`W^mv{}y|~{lm{~{wusqirt|{vuwy}|omrsqt|~vtukrsvuqdhvsv~}usuusuywsqz|{txv~~vtvtjjlkrfXZjquy~~{{y{~usturuz{swxryz||~y|zy}y|seccc_dvwrxu|w~~|}}zqqttlrvzyu~uolilrlrkc\\\bepz|z|}x||vt{y{wmtx}{~zzyqzvnx{yqlnmoxso}|wv}||~}rvzwopnx{}w|vrxx}ytile`hla__nxyvzzxvsv||yy~}yy~tsytvuwrvqood_jmrprw~{umlsolee[]gpqnzxyyxztrgkv~yrb^mwfZ|fwj`jtwnw{~s`mrtxrt~yt{}}~xivwplkvqWO\jowx~nZnfjukZ]hph`XkvlZjx}}stv~|vlvofuo\[bbgr|~zce{xqq|{vfdcglr}vgggvyyxz~zxwxz{xu|xz{vu~sghn~zkjdcjiY^i{~yxqlv{sx~p]agu{vibhqqj^gn~yvmw}{ww|||zxy|~l_hx}nlqtxxzt|}vrilgmrpqv|cYdjyv{}rjqyxspfnqunjlovtt}|obZgnqrxvvnnkuvsyy{ymjx~~wmiemju}}kkjvxqoy|zqz{}yu{tkqr}vww~xlm~t{}}z{w|yuqpzuunle]]io{mhghhoxrxu||xwu~w{}qsszvv}uwvwurwsgfuzxwx|okniqt~}y}}v{~}~z~z}}ysu~{uxeb`jng_cff\^djpluukihkxwykfelrmjx~x|zytqjellkeknn{}wwvon`cbbbbc^_mq{{vrzxz}}zmhiojenu}~zyqy|txyxzt{tnnhdlqqjmrluuzwooroshjmqvz}xw}|vqspzrmflvslkmnhfkp|ztoktt}vkjnxxv~~}tncZ^a`Y\fdnqxxporpvkjlnty{tq{{|}}xuzx~{qv{utssllos}wpkqnvma]_ddcmr~xxw{ys|xuqhemtunw}~|}y}{wonrr}zmt{wssqphljq}|tpgfjhtmfekqsu~}~}}}wrzutka^fkicosz}|z~zonhmrxullsowyu~|zzwrmsn|~{swuzwqnqqotvw~z~~uz}{zoghoqjiqovty|toqrzrpliry}wpuuv~xrvv~rlrpllie]abcu{zytrvrxtvyy{}{mgiljaiiqvxzzzwvlpx~vvzw|uwz~y{~}wkqtsturkkrmz|uxqv|uolmkjpmnzy~}yu|{}|qikpsjnoowtx{vuu|{wu{vxyz~xy|z}}|~{tkrnp|ywqqnjvtrmnnlpunwyxzxy|y|w|tgabda[b_hmnwzz|~vv|xutpyu{|uvz~yx~xx{}ztrnlromwuz{|twtuzpfehkgiljuttwvuuy~{qu~zzux~ytlnq{zy|z}~|xwywpcfgclrzzrponofostuttsu|zomnngkjjusuwxzzvy{{sy{w{vx}zzzwuw{zshmkktyzywywowwyxuqllrmsuu|xw~{vzuvxqww|w~|ttvwszxzzrqs}wx|wuv|wx}~xlpoow{~|~}szx{}{wrqyuz|z{uy}}{|}ux|ukged_fdfqstssww}wu{~{zqvyu|yz}}}z}zy}{ulhmjnroxxsv~ww~|tonmhnqmxz{vqrrt}{zwz{yz}}{thdcmqowtvw{~y~wstowx|~|}yy{x~zuy{}{sxxrkfb\_gcnuz{vx|}||~~snmu|z~~{tx~wrsltvw}}zwv|rppimrrjbdfitrz~}|wy|v|~}zwups}z||{oheksr{xy~{|~yyqy{{~|{{yysw~{rttv}|~zx|}w|vqr{z{unrrqokg`dmlw}~|x{{x}zw|~~|u~~}}yru{|rijfktqx|{z~~~zyz}y|{{{vqinss}~wstw|w~|zz{|zxytywu|~{~yqkhpqv}x|~}xu}|~{y}}wyu~}wvurrqkb[ddksv|ystv~}||vpqxu~|xz~yxz}zvz~{}ty||}|~}~}|rrv}yoliitsx|}}~zz~z|zz~}wmouuyy}~{}~yyvpjdihkrlnszxv~{~}zvx{tsluxx}{zz{~}}{yxzxxuruwrgaihqwz~xx{~}~xv~~}zuwzy}z~z}|zqpsxulibdljpqprux{~|zz~|{wx~~wlpru|vsy|z}zux~zzwvrnrntytuz~{}~~zx|}{xy|zs|}~yyz}yxtprvsh`ebkpqtrnklwy}~~zw|||}y}~{}{~~~}xx}ywx}~xumiqotvtuz{{|zz|~|y}uruszuu~|}}|wu{zwspqllifnjikqtpput~}~yx~{}~||}}zz}~|urvzvihhgsuxxwxovhqof]`egx~unx}wylmtxzxsoqqlxtkelt{skg^ZWi{}i}lxtz}y|rorqxqrxxe[_kuxwk^fhrwwt~xmiglzv}yjkjxlT_yr{}vqpyoprwvsv|}upkhaga\d\doxvspgr}|qspktorkchzjm{umehlwzvtkhmy|tz~~}uqukmsv{jW]n~mgnuv{rjmgjh_ZUY\kwqwukmiaZ\fmxwkimlpt}}~omlfs~~}|xz~{~vmaRPevtnqjgf]dkotnlnopuxuwrmktx{zytvx|xpde_XQ\m|~{oj`_hihd^aeikv}rqhahd]U^ehompwxyzyw{zywwt{vzoefg`]bs|}{yrntusrlmrpu{{}y{nkst||~vj_Z[TRUhx~}xropsuuuqvolnvws|{wovu|x{nikllkskkortuuqru~|{|~uh\]ZWWaqvqty||z{ztrmwtuz}~wrprswk^RW\[^co{|{yty{{~yv{xshrw~}tuu|~vvqngonmmlmkssuy}xruu}}}wtvrvlaYaidefq{{yyqvz}y~|}z|w~ztnmqnwtw}~}{y~qg`fjaa_l{{{xnomrpru{~t{z|~{}{}wqpmokslf`elislkny|v{y~}prxwvlbfe\\W`m{~~~wzx|{~x|zxu}~zmmqmxneZ\`[^Y`k~|ypvu{{{{}~xwmsu|qtorz~y{uyqokmrirnpv}trv}|xonumvlbZ\fbe\am|vvmrqx~}~{x~s|xu{|zvpyqyse_\ibcYZhyy{rtqovtsy{{{~|}w{yv~~yyqtuyxpjbnimqhmnztvupy|~vgplqxjg^ga^YS_l~~{xp|~{z~yfklgrfcZ\^[_Wan|zrwrz}}{zrtq{}xw{{xlrkmw|~}v|{}tohkvjpkiovyvotx~}xzpjwnurgc[edmget}txwu|wzuzqwxvz{yksqmugdY]beh[etxtqwntxst}~{yyu~}zwxvtmwqu}vnuw{urkktiqpnsu~xxt~|qyntumkajinf_ozy}~yzz|~{q{~xy~~|}jfi_g`^XV]_f]ewyqvor~|y}}vsnt{}ypwxypwut{ztpwwvqwxyy~vfpjjmcbW\ajmgx~uvxq~~~}{ywyquv{yxz}}wzttefj`ga`XS\cofkz~vr{uy}yz|y~|yr|{~ty~{xrq{pooouu|yzry||~}vksgghee]bhtohvzu~}z}}|~}v{~||}}xq|xswtzyvz|uw{xudg`Y^Z]UW`knes|w}vs|rsuwrszt}{x~zqx{svqttx}{|~~zt~z{lhj`fcd]X^hrisxw{r{|ywww||z~}y}xwqttv{}|z{zv|wxmhlcfegc\`kwpuyv}v}{~~yw|uty{vqxv}uz|yru}rplqsuzzyuxyx||tmqfddfd]`jvnmx}ts}x}~}y{}z}~xt{y{|}zxw}v}~vmn`^_b_X[grjkv}|zwsv}}urtvrpwtz|uxy~usrvuuy}}|{{p{{x{sjj__bfaXXdnehs{yx}tz{yx}{z}~}y{yy|~|z}r|zvwphi``bgd\Zeqilv}}yx~~~vvz~wtv{xswt{w|}vw~vpmpqrvzzwvtzs{}yzulme`aec\Zerkipu||x}y}{}~wty{{}|y}sv{xyxnld[\`aZU^nkgmt|{{wuy|wsruuossv~~}tv~y~{spsvttw}|wzsp{zvwyrmh^^ehd\^mpiov~}yy}zxz{}~yy{xvx~}xx{vmxzustoge\Z`ed]Zhqkms|}{}~}stu~|wrqwvsvszw|{y{uruutv}{vzvy~}~uqkcdkkg_gwwsv{y~}{|}z}{{z{~}ytuwwx{vw{xowwttuskg]X]`_YWgqmor{~~}vwu{~yvsqxwsvu~w}zw{{|{{~vu{yxwytnh^]fhg_ctustz~~}wxx|ytv|yz}~|~xxzxvu}||tsu{vhnrppnofaZSYaca[guttv~www|}ztz}{~}}}yvyxwv{~vxzuqzyyxyvmg_]gijccu|{|~{}|~vyx|{vw~}}|{~z~}zsstsst~~~trryvjnpnomqic]V[abb[gwxzz{|z}{zwry|z{yy|~{xzyzx~~xz|uqurtrtsie\[dficew{zy|~|z|wtut~{yxpsyxzy~|{~}{wxwxuv~}}{qrszrhmmpporga[V`ejhdu~uwu}||ut{|}|}|~~xyyyxvxvu{znqtuwtyqhb[ahkkbn||}{{~{|~|}~susy~yzuqy|}|zyzyxwwu{wv{|opsrtqupgb[_fhjajz{}{xyx{xyuovz{zwx~~}|{z{w|wx|sqsruqurhc[]dfjagwyzu{}|z{tutwy{xqwz||y~~}||wztsw|omonsotqf`YZbfkciy{}xvvux{}xrxz|{xz{{zyyt}{vuy}poqrwsyujd\^fimbgvxyu|xxw{|~ysy}~zz~}zyys}|wux}pnppupurgb[]dgkadsvxszyxxzyzwotw{zuzx}}~|z{u}~xvyvqsrwswwkd_^ehmddtxytx}|xvvx~y{zrux|{vz|~}z|uz|trq{skmkqoquia[X_cjeapwyvv||uuv{{z|ssw{}ww~{~~z}yw{yv~|qsqvyw~vkf`ejppfpz{{w|wvvz~y}vrvyzw~x~~yyxq|~zuqvznllmsourf_Z\bhlbdruwrx~zwwz~y{yqsv}}vz}z}}zx}tusyww|qgb_ejqmeqz|zwy}|tstx~yvzsqtx}wu{z~~yzxr}}vrmrwljhjolrpd]XZ_fkbfty{v|}|xwz}z}~vxzz}}yz|yv{rqnuvv}tid`diqpir|~y~|x{ussw{{v{vpsv{v|w~~|z}|}xvwnuz}ysooxojhgomqsh^[[`gojgtz~z{zy{~~|wvx~}|~{w~zt{}srnsvsytga^aforioz{~|xw|spptw}zswtoqt~{ty~z~|{}}~yvxov|}ysmmuoigfnlpsi_\\ahqljv|ywy|{{xxy~~{}xu{wrzzqojpsrytha`chqslr}|~~|z~uw{tqquw}xtytqrv{w|x}}z||~xwvnx{}xuorxplhjqouth_]`entnp{~}}yw|}{}wwx{}~vz{ut{vqmktsvxnb^_cjsnlu|}~~z|u}vrosuz|uwxrss}xy}~~~}}~xzrt~|wqnurlifnopuma\^agqokt}{~zvz{~{{~xyx~~{}vu{vrxxrmiprsxre_`cgrplrz||~u{{vrvxz~xx|vut{zz}|}}{}wysq}~|{wrnutojfmoqvpc\^`fpplry~|||w{|~||yxv~|{~}~xw}xtzytokrtvzse_`bgqolqx~|{~w~{vquwy}xx{utrzxwz|}~{~xysr}~|xsmsrnidloptoa[]_cmmimu||{||vz|||{zxv}~{~~~yvztxzvqkquvzwjabegrsnpw~|~xy|xsuxx|yw{wurv{vxz}|~y{~xxtox}|{xtmqtqleiootsg\\_`joklrz|y{~}zx}||~{xx{|x}xx|zvnnvwz{readdkspory{|~}v~zvqvuwzvwytspzwvx}{}||x|xxqq{}|zxqmqqngbjmpsnaY\^bmljlrz{z}~yz~}}~|y}}w~~vwzytlntvyyocaeentqps{}|}t|}yuqvuwyvxyutq|wvy}{{zzvz|vuopy{zwvolpqogcjmqsob[^_dnnmou~}}~{|}{}}w}vwyyulmsvyyqd`ddlsqqsz~|tz}yvosssvuvxutpwyvwz{z~}ux~~wvpnvz{xwslnooibglpsqg\]acnqqqu~}|y~|~~|{~|vx~vsuvukipswxre_dekttttz~~z|svzwuortsvvvywvqx|xx|{z}|tv}~wupmtz{xxsmnpqldinrutj_`dfpttuw}|z~|}}}{}zuy~vtuwvkkquyysfaffmttut{~~z}~sx{yvpttsvvxyyvr{|yy|z{~{tx~xvppw{|yzsopqrjekouvsh_ceisuvuy~~y{}{}{||}wuz~{tuuxrjnsxzxocdhisvwvv~|{yu{zytswuvyz||{vx|y~|z}~vt|zwrnsy|zywppptpgjouxwoccghqvvvv~}}{||}}}y}xuywvvzwmnsx||uheiiqvwwt|~|}v{{|wuxvuxz|||wv|y|pkgcioouz|{jiqvy~q|{yuc`kv|ribl|jcjqy~z{sktyYkyt{~z|sqmtyrpwzmtrpt}~}wnlejtz|undh|uqnov~|xfqza`pzv~yv|wu}~vorx}ttzqqnikmsqousu|}|vqhesztux{|~{}leszhensrs~~yxxzwxwyvrrsy|{y||~{uwl`bqytmd^dnmhb[Z]begqsy~ulknsuulfkkmpnkb`hlsswsnx}~|xxumqzngcgswj^]gieb\]bjru~}}}xmnpqwwxur{{}|ud\^co}{j`ehc^XWZalntyxz{trswyvpc_bagknojpwywrsnq|}|xuuuyupu{xi_afq}wihnmiebflv~}stux~~|~~~}|zxpdXY`kywnsuokddhnwy}||{wxz{oknnuy||ux{zvprot}yvyzyz||tqprtzwsuyywula\clzuszzvrnqu~~}~}{yz{~|~sllhaX[fsx{|xqnqt}~||{}|tww{xuwvuprrt~}ytsuttty}z|zronqryzuuw}}~}|qoojc^fr|w~ztvy}~~~~zxz||}{{|zohhgc\`mz|~wtvx}~}xyy|~~upstuqstu~ztrsrrqwzx{yrnnqszzvuv|~|}}zx|~}wmkljdakw|y|vxz{}~~~~~}zx{||{~{wx}{yvkghgd_fs}wwx{}}~}xxx{{|yonqssqutyytstsrryxx{wqnpru{xutw}|{{zvv|~{{rkkkidfq}{}zxz||y|}~}yy{}}{{~}{wuy|yxqhhhgccnz|xyy|}~wwvxyyzqknqspssvxtuutrwyx{ysonrsyyvtt{}{{zvty}{{ulkkkgdmw}}xz{xz}}yx{{}zy~}|zvrv{xxrihhhdbjuyzz}~}~xvuwxxzrkmpsopqt}zvvwvswzy|ztoorryyvtsz|{{yvsx}{{vmlllgdktz||yz}|}}yxz{|zx~}|yurv{xysjihhdahp||}{~~~yvuwxxzslmpsoooq{}xxyyux{z|{vporrxywury|{{yvrw}||xpmmmh_fsx{{~}|sxyzz}}usuy~zvr}|{wypvuytihca`cfr~{|vxyzquvutupnxppnkuyx}~x{xvwzy|{ovpqyyyvzy|typr{wtvwnpkefany|}~wt|}{xzw|}||zuzv|yqlqtv}rrphigkgdcq|~}xwwsxz{|tjhwtqtlu}}{xu|}xwmtuqzxokz~|zprrv~{}zpfiofjfm~~|{~{{zo{|ry{}|uqq}}zpu|~{uvrmnvvorcbhh^^jq~{zyxvqxzthjioutuxxy~y|uy{xyztonsz~}vxzyvxtotnw{wlihcjkkov}~}yt|y{~{yz{{{w}{xxzuq|vwvpdfnnponjgdefory}}xwywsrutmqtvqzwx|x}~~zsqsrzv|zumuysqoqrzvrpvmggminx||z}|{{{uxt{~~x~}}}{ywowyvqqw|}qjiqoqmmhhebhjik~|urwqtsrgltvsruyz|yx}uqntw~vot{wnliostrpidjjlgpu~{yyt|{{ttqy~}{sqxzymmrytpihbkrngcd`nnpo{|nroyzvrqrpvsvw}~|z|}~}xrmqv|{vptt{uodhks{rqkmdjklr~zy~xvwsst{|~~}xsssw|umhtpwpjgjnjkcdbejrptuzrprtvzurlovwtr~{{~~~}}vulqvxsuv|qngdjptvrnicjmnj{~z{uxwyntx{|}zw|wutxyqofpsqomfihmjjabfgtknr}zpowr}sojsxywnv{{z~qtmnwzzytxxsnkhdtqyqjfhmoqgq}~vvv{y|mmu|y}z|wzxtywulojuqmnbehrloc]bgoslq|ukrtx|tkqw{xxnz{~~}~wrsqq~uyqsturmmakrtwrfhkqprkwuvy|{zomzw{~~{ytxntswoqljpllnefopnkf[feonpyzymrstyorrxswqr}z~~wvswlsyuvyruuqlmhksltkkhklnnl}~zwxuwtviq{{{~wzusyvowwqntkmhchdlmkfhb]hlrmo{|uutxwwrkuvywu}xx~{yopww|trquyvsjfnkrpnsgafkurvz~wsru|xsus}xwzv}~nloowuvolmqsiffbkhijadhiokqw|~xwuuvpssoxu|{sx}ruwtusywttyvklkglkssghghqtxxz|~}tqvyuxtpwu|{t~urporsyuliruljdbeajjckjfglvy{{uwrnttqrr~}tx{}{ysqrz}xsvtoqkjieqphmkikqz{u{~wtyuptrvspzyx}z|tpqswvnnromlefc^eegokfdiv|y~~|womqswrrzxx|z|}tqt{{xzuloqnmghkgjolllpwxv}~~}|~xqnmsxsqsu~zy||xtsuunpwqijjlibb_`lnhggltuz}tnmouwvvrs||y~}xuzzu{{qllqsokkddmmkppsop|~}}~vnkotutqnw{xz|}}{wvmnwuofemojda_ejfginqls|xx{~unpsuwysouyy}~yuyxuqkosvrmgdghkrsuljv|{wy~}~|~smqrsrrqswtwz~~|wlptrqhikonhecec`hjqpjnqsur}|uutvuvurrqw{~zxxttqsrrwtqkggbkruskmqsvqv}~{~vxsupnsuuolvy||}~yrtrqllqmokklhg_]ekrmmlilmrx}}}yystuttmt{~x}~}wtroxwwtruplhcjmtqmqoommxy|{{zomrsvpjou~{{{z~~|xsrhmrtshjomk`_`fnmnlhhdpvz~~tvsruquty{z}}~}wsnww}ypsrpnfkhllnrqqjfsx{yzzvosprpnpmux{zvzv}{wjkprxmjmlogbb`egmljlciovx||}zwpsqwxttv|}~w{pvuz~uwrmrmpkhffnosqgnpz~v}vwysppjpoqqtxvyws~xlqnurlrmjlghbcbdfglignkxzx|vrrqxvwrsy|zyytuut{rnwnulcffijwkpzoztr~~}t{~~|tyoioiqtomp|v|zwrurywkumfhfnhj`df_mggrmrlw~ywtoonvuwws}xyuvwvjowkogcfeicpryny~{}~|z{{zwyoumkqnsls}y{z~|}|}xfqvktedhfb_c[eibqjqun|}|}twquyzvt}v}|y}oxwqmjriqplkagdgyvzrtwz}~{||x|vmkmkwmltlmy}}}ygkrksonkaeflhba[hgrwjsuls~tqpnlu}{tz}w{yqrp|wlhnndnc_jjnrtyxsy}~~y~}tstnnlikxsrxzw}{|wvrojnjghbic_\_ccfpjt{qu|{}xyvus{rvzvxsqvqhiplhjebnjtrw|rptv~|}}y~{xmkfjpsqrvqyu{~~zz|ojookcgfihjicj]cfhqunst|vu}|}xqs{rqzs}xzutu}strvhimfhgjmkuwkuorpz~|}|~~~{{pmelgnvs}rq~z}y~~|~{|usrdhgkcdjiih\d_dlirr{rnt}~|s}you||uzwxtruxtkinimaniiunouzurlv}|z}|~{{{rsqmjjnxrszuxx}wvu}xsfhklbbldsgcdcgjggmrsnqu|u|wzzvzs{zv~y|ortw{lrlqpoldbmlprruortxqy|zvy|sxmqonlnnuyv}{rvx~}v~kimrhlgbfikp`cecjomjmjywx{}yxtx||x}s~y}zmqspyzlpksskfffixvvovruqv{zsx}}usmlqetspy}{uxx|y}w~|zullgrombbiiljgahfqifrtuzwzvz|}~{||x|~{jntszroooisdbkmuyux|ur|{|~}uxywttokwlrxurvu|w{}~xzxyzklmminbcgiqohglmimtoxqv{yuv|z~}~y~{vkpttwztksvkkkgrivxwtt}|{~|z~zttvjrqurnwtt{vx}~fkaYdbccljnz~txzz{s}ypwyinyxkjt~viggoz|ouuwtxvujvwz}o{}s~zix|rr~{gbblz~zgflrv{~qqsyypkgvzxutswk]mjw|vuljdq|v{wrmzwnpxlfgvzll`^kyuwwq}|lnriqoov}oaYxndlzyleffqtpr~mlq}xox|~}xux]XXnxnwxqncjnkobVRTU^uxs{zzuf\Ydrzmrrmegnvysfirx~~{~|sur[[]lx{xmg]bjhmf]_aci}z}ogluz|~|zzttz|~|iTUZeuh]bjikcWY[]btxpxzug_gmwzuwvqrvutsw|xuhimlnpzw}|sq~{z|kXX[e}mbgoqtndhiim|yxqytw{{{{|~{qp|ufUTU_wory{xpcgedhtvnuy}phqv~}yy|~ywiknmmmtlr{|yvyw{{zy{wqxyz~~pkzxl]YZ^}zrv~}oqpmq{zyxy}~{wy|{xz|~zmersfZRV[w|~rpohjqystxyovx|v{{|xxnkqorownjpttourqx}}wwvsv{|xqtyx}qfp{oeXY[n{|uxprv~}}y~}w{~z~{}roxt~~pedtkdWUYftwpnouxtv|tsy}vvxwwwvlrstsuxkmnvpmtmqy~{{tv{}vv}~~tkttvulcsulbV[by}{xxrvx~}~}~yzy{y}{mmvp|xk`brjeWV^mvxoppuzvwuvy~~prtqvyxqvsvw{zmmnyrptkpw}}|sv|yv}~thsrtrj_nqleX_h|~yyswv}y}{y~z}x~{|}nhskuwja[lih]Xdpyvqqqr|zv}{xyyy}pmrnt{}wzwty{~rolyxnvmot|tty~xyyhnsl{rk`dpkj\bn||xysxt{v}}}}y||y}zz}oeriosgaXhhiaZit|wrssp||w}|yyxw}}zmkrnv~z|wty}tpmzyovnqt|tuzwzyfnqjwni]allm^es~xwtyt|u~{~}x{{y}y~x|kfqenmf^Wfgl_^oz{vqvrt|w|{v{u|{wioqpz{|tw|}topussnsw|~rx|t}~tftkmsjgZejoi^mwxtxwu~ww}y}y|~x|}z}x{}vcmjenfeW\eji[iuytvwp}z|{yvuy{znlrnw~~~wu}ytn}|ruost~~}uvzvw~{hoogqjj^^hmn^gt|ztvzs}yu}xz|}x|{|{~yv{zfkmbkef[Ycik[cqy|uwyq}|{|zuux{znlrnv}~~xu~}wp~tupts|}}vwz~vu}{iopgnjk_]fmn]epw{sxzs~}~xv|x{||x}{{|}vv}{ygmkcifg[YbjjZbmt~uz{s~{}yvux{|pmrnw{}}wuzsvuput{~~xwy|vs~|}lnpfljlb\dmo^cmrtx|t}|zv{w}}|x}{~||}yr}z|ljnbeeh`W^hm]_jo~wyu~{zw|xvz|tlrnu{}}xt|w|zvqvuw~}~xyy}q|{~umshgjli]_irf_joxyvwz}~|uzx{|~yz}{}}zqvyzwing`cfg\Xamh]fmtw}x}}yxvwz{oprpz||vv~xxtsxuy}|{xzvq|{}ropefile\`mqcems~vyw~}||~|uzw||x|{z~|yov|wyuild^aee[Wbpiaiqxx}z~|yx}vuzzpqqs|}}vx~yyttxvy}}|xzup}{z|rnncbgje[^msgipvwyx||}~z{}uyw||w{{y}|w{nq|zuvujid[]ce]V_pogmt{y{~~yy{~wtv{spsr}~ywz|vtyww{yx~znx~zwzvlmd^chg_[jvolsz|x{~{|{zx~~vyw~|}wz|y{|vxnnxwssukgcZZad`W_qsmpwz|}xyz}wsu{trttyx{}vuyxw{yx}ynw|xvxvlkc\bhg`\lxrqu}|y{~y|z}yw~}wyx{{w{zxzzux}lovtsrtjfaXZcc`Xctsqs{|~zyy|~{wrwzttsxwz|{vxyxx}wy~soyxwvxskh_\egf^`swsswz|~y{{|wyzyxz||yy{yx|vvytjrssqrqgd\V_cc]Zmurrux{y}ztu{xvuvyx~xxzyxzxxzynuwwvvwmib[bghb]mwtss}~}}~x{y~xw~|zyx~zzzzyxywuw~wjprqrpsid^V]bc`Yitrqq{x{y~}vt|zxuu~zw~zzzzyzyyz{otvwwvyoic\cghd]lusqpz|}w{y~wv~|zww{z||{{yzwww~wkpprsqtjd^X_ceaZirpomy{y{y~~vu|{yvu}~zw~}}||{zzzz}ptuwyv{rje]cgig]jtrqmwz}wzx~xu||{xu}}w~}|{zxywv|{moqqtqvofaY]cddZdqppktyy{y~}~ztz{zxtz~~v}~|}yz{~usvuzw{xmh``ghkacrssnry{}yxxy~~|tx{{zux~x{~~{|x{wwwumqosrsujd]Yacga\kqqnnxyy{z}~vvz{{vu}{x}~|z}{zsutvyw|uke_bgjj_htssnvz~}~wxvz|}zsxz|zty~w}~{|v}~wvw~smpnsqttib\Zacha^mrspqz{zzz}~vvz|}wv}|y}~|z}{ystsuxv{tjd^afjkahuvvqx|~||wvuy{|zswy|{uy~x|~~~z{vywvt}umolqqqukb\Y_bhd_ltvus}~yyx}|~xuy{zw}y}~x|y|vsrrxuywld__ehmeftxzux~|zyutv|zz{suw{}vx~{z}~~y{vvwurzvlmjoppulb\Y^bhg`lvxxu~~zyx}{~yuy{|x~z~~x|y|usqqwtywkc^_einfhuy|w{}{yxusw|yzzsuw}}vy~z|}~}y{tw~wts{tmlipoqtj`[Z_cjgcowzyx}zxy~~|~wwy}zz|~{y~|x~}tsosvuzsha^belmemw{{w~}yz~vttx}|x{vsvxzv{x~~~zyxr}zvrvyomjkqotqe]Y]agkchtx{v}|yx||{wyzy}|~~w}x{wtppwtyxlc^`djogity{w{|{xwusw{~x{ysuv~|uy}y}}}~~|yyry{wstzrnjjpossh^Z\`ekdfqvyvy~~}zx}|}xyzy||x~y{xtppwuzymc_adinghrwzuy|}{xxusx{~x{xsuu~{tw{w|}}}~|zyr{{xsuyrojjqptsh^Z]_ekdeotwsw{~~|y~}~yzzy{|xzzzvqpwvzzpd_achnhgouxtvz}~}wyvsxz~yzztut|}uvyyz}|}}z{sw}ytsytpkippsul`Z\_ckfdkqvtty|~}y}zzy{z}~{{}y}xtouwx|ui``cemlglrwvtyzx{|xtuy{{x|vutwytw|w}{|}{zwr|{xruwrohkpqusg]Z^_gjdgntvswz~|z~{zzz|~~y~{z{wrovvz{re_acgnjhnsxvwz|~w|{xsvyz~yy{vusxxuw~}x}{{|zzur}~zwquvrngkoquqf\[^`hjfiouxuy{|z~|{z{|x}zy}zwpntuyzre_acgoljntzxy{}~wz{xsuxx|yx{vvswzvw}y|{~zz~zyuqy~}zwqsurnginotrh]Z^_glhjovzx{}}}y}~~||z~~yy|w{zxrlqtwzui_`cennlos{|z}}xw{xsswwzywzwvtu|wx|z|{}yy}{xuov}}}yxpqsrofgmossj^Z^`gmjkpv|{}|~y}~~}}zyy{wzyxqkqswyuh_acfoonpt|}}~}ww~zxrsvvyxwzwwsw|xx}{|z~xx}zxtow|}{ywprrqmehmotrh\[_`imlmqy}|}|z~}~~||}w|yxzywnlrtyyqd_ccipnoqw}|~~|~uz|yvquvvxwyywvs{zxy~{{{{vy~xwprz|}yysprqqieknrsnbZ^`dmlmot||~~z}~~~{~yy|wyyyqkqsxyui_bcfooops{|}~}~wx}zxsuvuxwxzxwsx{xw~{{z~}vx~yxrqx|}zzuprrslejnrtqf\^`clmmnqy{|}{}~{~yy}xzy{smruy{wkacegpppprz{||}~wy}zytvwvxxz{yxsy{xv~~{yy}|vxzxrrx}~{{vrsstmglouvrg]`admmnnpxy{{}}~~|~~zyz{z|vosv{}yodeghppqpqyz{{{~~~yx}{{vvxvxy{|{zuw}yv{{yx|}wv}{ysqw|||ysssvqilpuxulaaddlnnoovyzzz~~~~~}|y}{{z|{rrvy~|uiehhnrqrpu{z{y||}~w{||zuxwvxz}||yu||yx}}{~|xutty{{}{spsx{{zsmmmotvrmnswwwxy|}~|xz{xtrrsx}~}{|~|vv{}||~~zzy|~yttx|}}xqoopswvqotyzyyz{}}~zy|zvsrru|~~~{~|xz}|{zyx|}yy{xrpsx{|{tooorwxuqsx{zyy{~{}~{wttty}{}|vw{|ywvwy|yz{vrrw{|}yspqruxxtrvzzyy{~zxxz~}ytwz{yxxx|{vtvuomoswxxsmlmnrusoosvvvwz|}~~{wvvx~|xx{~yvy{zwvww|~zvvxwrquz|}|vrrrtxyvruy{{{}|{{|yz~{vw{{zxxy{|wuvwsoquxzzvpnnortsonsvwwx{||}~}ywvx}}xx{~yvy{zxwwx|}yvvxwtsw{}~}wsssuxyvsuz|||}|{{xz}zvw{|zyyz|~zvuvwtrsxz{zvpooortsoptwxwy|~{|}~}zxwy~{xx|}xwz|{yxxy}~}{wuwywuvz}|vsstvxxutw{||}~|}~zy{~~xvy||{zzz~~|xuvxxutw{||ztpooqstqorvxxx{~|{|}}zyx|~yxz}zwy||{yyy{~~}{xvvyzwwz}~ytssuwxvtvz|||~~}{xz|zwy|}|{{{|~|ywvyzwvx{||{vqppqstrpquwwxy|}{|}~|zy|zxy|~|xz}~|{zz{~~}{yvwz{yxz}~yuttuwxvtuy{||}~}{xy{}{xz}~}||{}~|zxxz{yxy|}}{wrqpqstrpquwwwx{~||}}{z|{xy{}}z{~}|{z{}~|{ywwz|zy{}~zvtttvxwuuy{{{|}~|xxz|~}z{~}|||~~}{yxz|{yz|}}|ytrqrsttqqtwwwxz}}|}~~|z{}|yxz|~|{}}|{z|~}|zxwy||zz|~}xuuuvwxvuwz{{|}~}~zxyz||z|~}}|}~}zyy{|{z{}~~|xtssstutrrvwxwy{~}}}}{z{~{xxz}||~~}|{{}~}|zxxz|{z{}|xvvvwxxvux{|||~}}}yxy{}|{}~}}|}~}{yz|}{{|~~~|yutstuvtssvxxyz|}}}}{z{~~~~{xy{}||~}|{{}~}|zyxz||{{}}ywvvwxxwvx{||}}|~~}yxx{}}{|~}}}~}{zz{}|{|}~~}{wuttuvuttvyzz{}}}}~}{zz|~~~~~|yy{~|{}~~}|{|~}|{yxy{|{{}~~{xwwwyyxwx{}}~~|{|~~}~|yxy{~~{|~~~}}~}|zz{}}||~~|xvvuvwvutwyz{|~}}}~}{zz|}~~}~~|zy{~}{}~~}|||}}|{zyy{|||}~|yxxxyyxwx{}~}{z{}~~}}}|yxy|~}{|~~}~~}|zz|}}|}~~|ywvvwwwuuxz{{}}}}|zyz|}~~~}}~}{yz|||~~}||}~}|{yyz|}||~~{yxxxyyxxy|~~~|{z|}~~~}}}}{xxz}}|~~~~~}{z{}~}}~~{xwwwwwvuwy{{|~~}}~}{yy{}~~~~}}}|zy{~~|}~}}}~~}|zyz|}||}|zyxxyyyxx{}~~|{z{}~~~}}||{yxy{~~}~~}|{{}~~}~|zxwwxxwvvyz{|}~}~~}{zyz|~~~}}}|zyz|}~~~~~~}|{zz|}}}~}zyyxyyyxxz|}~~|{z{}~~~~}||{xwxz}~}~~}||}~~}zyxxxxwvwxz{{}~~~~|zyz|~~~~}}|zyy|~~~~~~~}|{zz|}}}~~{zyyyyyxxz|}}~|{zz|}~~~}||{ywwy|~~~}||}~~|zyxxyxwwxz{{|~~~~|zzz{}~~}}|{yyz}~~~}|{z{}~}}~|zyyyyyxxy{|}~}{zy{|}~~}||{zxwxz}~~}|}~~|zyyyyxwwy{{|}~~}{zyz|}~~~}}|zyy{~~}|{{|}~}~~|zzyyyyxxy{|}~|zyyz|}}}}|{{yxwy{~~~}}}~~|zzyyyyxxz{|}~~}{zyz|}~~~}}}|zyz|~~}|{{|}~~~}{zyyyyyxy{}~~~|zyxy{|}}||{{zxxy{~~}}~}{zzyzyyxz|}}~~}{zyyz|}~~~}}|{zz|~~}||{{}~~~~|zzyzzyyy{}~~|zxxxz{|}|{{{zyxy|~~}}~}|{zzzzyyz|}~~~}{yyyz|}~~}}}|{z{|~~}||{|}~~~~|{zzzzyyz|}~~}{yxwxz{|||{{{zyyz|~}}~}|{zzzzyz{}~~~~|zyxyz|}~~}}}|{{{}~}||{|}~~~}{zzzzzyyz|~~}|zxwwyz{|||{{zzyy{}~}~}|{zzzzzz|}~~}{yyyz|}~~~}||{{{|~}|||}~~~~|{zzzzyyz{}~~}|zxwwxz{|||{{zzyyz|~~~~|{{{{zzz|}~~}{zyyz|}~~~}||{zz|~~}|||}~~|{zzzzyyz{|}~~}|zxxwxz{|||{zzyxxz|~~~~~}|{{{zzz{}~~}{zyyz|}~~~}||{zz{}~}||}~}|{zzzyyyz|}~~}|zxxwxz{|||{zzyxxy{}~~}||{{{zz{|~~~}|zyyz{}~~~}}|{zzz|~}}|}~~|{zzzzyyz{|}~~}|{yxwxyz{|||{zyyxxz|~}||{{{{{|}~~|{zyyz|}~~~}||{zz{}~}}}}~~|{zzzzyyz{}~}|{zxwwxyz{||{{zyyxyz}}||{{{{{|~~}|{zyyz|}~~~}||{{{|~~}}}}~~|{zzzzzzz|}~~~~}|{zxwwwxz{|||{zzyyy{}~}||||{||~~}|{zyyy{|}~~}}||{{|~~}}}}~~}|{zzzzzz|}~~~~~~~}|{yxwwwxy{|||{zzzyz{}~}||||||}~~}|{yyyy{|}~~~}|||||~~}}}}~~}|{zzzzz{|}~~~~~~~~~|{zywwvwxz{|||{zzzzz|~~}}|||||}}|{zyyyz{}~~~}}||||}~}}}}~~|{{zzzzz{|~~~~~~~~~~}|zyxwvwwyz{||{{zzzz{}~}|||||}~~}|zyyyz{|~~~~}|||||~~~}}}~~}|{zzzzz{|}~~~~~~~~~~}|zyxwwwwxz{||{{zzyyz|~~}}||||}~~}|{zyyz{|~~~~}|{{{|}~}}}~~}|{zzzzzz{}~~~~~~~~~~}|zyxwwwwyz{||{zzyyyz|~~~}||||}~~}|{zyyz{|~~~~}|{{{|}~~}~~}|{{zzzzz{|}~~~~~~~~~~}|zyxwwwwxz{||{zzyyyy{}~}}|||}}~}|{zzyz{|}~~~}|{{{{|~~~}~~}|{zzzzz{|}~~~~~~~~~~|{zyxwwwxyz{||{zyyyyz|~~}}}||}~~}|{zyzz{}~~~~}|{{{|}~~~~~|{{zzzzz{|}~~~}}}~~~~~}|zyxwwwwxy{{||{zyyyy{|~}}}}}}~~|{zzyzz{}~~~~}|{{{|}~}}~~}|{zzzzz{|}~~}}}}}~~~~~~~}|zyxwwwwxyz{||{zzyyz{}~}}}}}}~~|{zzyyz{|}~~~}|||||~~}}~~~}|{{zzzz{|}~~}}}}}}~~~~~~}|zyxwwwwxy{||||{zzzz{}~~}}}}~~~|{zzyzz{}~~~}|||}~~~}}}~~}|{{zzz{{|}~~}}||}}~~~~~~~}{zyxwwwxyz{|}}|{{zz{|~~~}}}~~}|{zzzz{|~~}|||}~}}}~~~}|{{{{{{|}~~}|||}}~~~~~~}|{zyxwwxyz{|}}}|{{z{{}~~~}}}~~~}|{zzz{|}~}|||}~~~}}~~~}|{{{{{{|}~~}}~~|zxwwvvwxxyyxxwwxy{}~}||{{{{{||}~~|}~}}}}~~~~}}||}}}}}}}}|{{zyyyyzz{|}~}xw|{yyyz{|||{zyxxxxyz{|~~}}}}~~}}||||}}~~{|~~~~}|{{{{{|~~~}zwwy|}|{zyxvutsssstvwxyzzyyxxxxz{}~}|{{|}~~~~~~~~}|{|~~}|{zyyyyz{}~~}}~}{yxxy{~~}{yxvuttttuwxyyzyxxwwxxz{}~}{{z{|~~~}}~~|{|}~}|zyyyyz{|}~~}~~~|zxxxz|~}{zxvuutuuvwyzzzyyxwwxyz|}~}|{zz{|~~~}}~~}||}~~}|zzyyyz{}~~~~~~~}{yxxy{}}|zywvuuuvwxyz{{zyyxxxyz|}~}|zzz{|~~}}~~|||~~|{zyyyz{|~~~~~~}|zyxyz|~~}{yxwvvvwwyz{{{{zyyxyyz|}~}{zzz{}~~}}~}||}~~|{zzyz{|}~~~~~~~~}|{zyyz{}~}{yxwvvwwxyz{||{zyyyyz{|~~|{zyz{}~~}}~~}||}~|{zzzz{|~~~~~~~~~~~}}{zyyyz|~~}{zxwwwxxy{{|||{zyyyyz{}~~}{zyyz|~~~}~~~}||}~}|{zzz{|}~~~~~~~~~~~~~~}}{zyyyz{}~|{yxxxxyz{||}|{{zyyyz{}~~}{zyyz{}~~~~~~}|||}~~}{{z{{|~~}}~~}~~~~~~~~~~}|zyyxyz|~}|zyyyyz{|}}}}|{zyyyz{}~~}{zyyz{}~~~~}|{{|}}|{{{|}~~}}~~~~~~~}}}}}}~~~~}{zyxxxy{}}{zyyyz{}~~|zyxwxy{}|zyxwxz|}|||}~}|||~}{zyyz|~~~~}}}}}}~~}||}~~~~~~}|{zzzz{{|||||||}}~~|zxxxxz{}}|zzyyz{|}~~~~~}}|||}~~~~~~}||}~~|zxxy{}~|{zz{|~~}{zz{}~|{z{}~}~}|{{{|~}{zxwwwwxz|~~}}|{{{zzz{{|}~~}{zyxyz{}~~~~}}}}~~}}}~~~|{zz{|}~}}}~~~}{zzzz{|}~}{yxwvvxz|~|{zz{|}~|zyxxy{}}zxwwy{~}}}}~~~~}|{zzz{{}~~}}}}}~~}}}~~|{zyyyyyzz{{|||}}~~}|{zz{|~~|{zyz{|~~|zyyz|~~}||}~}||||}~~||{{|}~~|{{{}~~|||}~|||}~~}||||||}~~~~~~}|{zxwwvwxy|~~}|{||}}|{z{{|~~}||||~~~~~~~}|{{zz{{|}}|{{|~~}|||~}|{{|}~~}}~~}|{zyxxxyzz{|||||{{{{|~~~~}|||}~~}|{{{{||}}~~~~}}|||}~~|{zyz{}~|||}}||||}~~~~~}|||||}}~~~~}|zyxwwxyz|}}}}}}~~~}}|||}}~~}|zyyyz{|~~~~}|{{|}~}{zyz{~~}||}~~~}}}}|||}}~~~~~|{zzz{|}~}|{yxwwxy{|}}}|}~~~~~}|{{{{{|}}}~~~~~~~~~|{{|}}{zyy{}~}}|||}}~~~~~~~~~~|zyzz|}~}|{yxwwxyz||}}}~~~~~}}~~~~~}|{zzzz{}~~~~~~}|{|}~~}|zzz{|~~}}}~~~~}}||||}~~}~~~|{zzz{|}}}|{zxwwwwxy{|~~}}||||}}}~~~~~~~~~~~~~~}||{{{{||}~}|{{{}|zyyz}~}~~}|||}~~}|{zyyxxyyz{|}}}|{zyxyz}}|{{}~}{{{{|~~|{{|~~~~~||{|}}{xwwxz|~~}|}~~}|{{{}~|zxyz}~~~|zzz|~~|zxxxyz{{zywvuuvx{~~|{{|}|zyy{}~||}}}~~~}|zyyxyy{|}}}}~~}|}~~}|||}~~~}}}}~~~~~~}|{zyxwwwxyz|}~~}|{zzz{}~~~|{|}~~~}}}}}}~~~~~}{{|}~|zyy{|}~}}|||}||}}~~~}{zzz{}~|zz{}}{zzz{|}}|{xwutuvxz}~~~~}|||}~}}~~~~~~}{zyxxxyz|}~~}}~~|zz{}~}}~~}}}}~~}|{yxwwwwxy{||||{yxxxz}~}}|}~~}|{{{|}~}|{|}}zyyz{}~~}|{|}~}}||}~~}{zzyz{}~}{{|}}{zz{|}}|{ywvvwxy{{||||||}~~~~||||~~}|{|~~~}|{{{{{||||||}}~~~}}~~~}{z{|~~||||~~~}~~~~}|{zyxwwxyz|||{zxwvwy|~~~~}}||}}~~~~}|{{|}~~}{zyyz|~~~~~~}|{{{{|}~~}|}~{zz{|~~|zxwvwxy{||||{zzyyyz|}~~~~|{{}}|{{{{||}~~~~~}||{{{{|}~}|{|}~}|{{||}~~~~~~}|{zzyyyzz{{|{zyxwvwxz|~~}}~~~}}}}}}~~~~~|zz{}~~}{zzz{}~~}}}~~}|{{{{|}~}||}}}~}|||}~~~}|zyxxxyz{|||{{zyxxxxyz{}~~}}}~~~~~}}}}}}}}~~~~~~}||{zzyz{|~}||}~}|{{|}~~~~|zyxxxy{|}~~|zxvuuuwy{}}~~~~}}}}~~~~}||}}}|||{zyyy{}}|{{{}~~}|zyyz{}}|||}~}|{|}}zxvuvwxz|~~}|zxwvuuvxz|~~~~}{z{}~~}{zz{|}~~}{yxwvvwxz}~|{{|~}{yyy{}~|{{|}}||{{||}}~~~~~}|{ywutttuwy{}}}}}~~|{{|~~~}~}{zz{{{{{{{{|}~}|{{|}~~|{z{|~~}{zzz|}~|{{|~~~}|{zxwwwxyz|}~~~~}|zyxwwwwxyz|~~~}||~~}}}~~}||||}~~|{zxwwwxy{}~}}}~~}|{{{{{|}~~~}}}|||}}}~~~}|zywvvvvwxyzz{||}~~~}}|}~}}}~~~~}|{zyz{|~~}{yxxy{~~|{{{|}~}}}}||{{z{|}~}{zyxxxyz{}}~~}{yxvuuuwy|~~~~~||||}}~~}}}}~~|zyxyz{}~~~}|{z{|~~~~}||{{{{{{{{||}~~}||{{|}~~|zxwwxy{}}}{ywuuvy{~~}||}~~~~~~}}}~~|{zz{|}}}}|{zz{}~}}||}}~~~}|{{{||}~~|{z{|}~~}{yyxyz{|{zyxwwwy{}~~}}}~~~}}}~}}~~|{yyz{|~~|{zzz{}~~}|{zzzz{|~~}}~}|{{|}~|zyxxyz|}}|{zyxxxxyz{|~~~}}}}}|zyyyz{}~~~~~~}}}}~~~|{zz{}~}||~}|{z{|~~}{zyxwxxyz{|}}~}}}||{{{{|}~~~}~}}|||}~~~}|{{zzz{|}~~}||}}~~}|{z{|~~}}}~~|{{zyyyyz{}~~}~~~~~~|{zz{|~~}}}}}~~}|||}}}}~~}|{{{||||{zzyz{{|}|{yxwwwy|~|zy}}|zywvvvwyz|~~~~|{zz{}}{zz{|}}~}{zzyz{|~~||||~|zyz|}|||}~~~~~{xvuw{}{yxwwwxyz{{|||{yxvuuuvxz}~}|{{{{|}}{zz{|~~}}}}~{ywwz~}|{{||}~~~~~}}||}}~~}||}~}zwuuux|}{xwvwwxy{{||{zyxwvvvwxz|~}{zyyyz|}}{zzz{}~~~|{{}|zyyz|~}{zyyz|~~}||}~~~~~~~~}zxwvwz~}||||||||{zyxwwwwwxxyz|}~~}|{zyyzz{|~~}}}}}~|zxxz|~|{{|}}|zzzz|~~}}}~~~}}~~{ywvvwz~~|||}~~}|zyxwwwwxyzz{|}~~}|zyyxxyz{}~}|{{{{|}}{zz|~~}}~~|{zzz{}~~~~~~~~~~~{xutuw{}||}~}{ywvvwxyz||}}}}}}}}}}|||{{zzzz{|~~}}|}}~}|{{{}~~}|{{{||}~~}|||}~~~}}~~}}~}zwuuvy}~}~}{ywvvwxy{|~~}|{zzyyyyyyz{|~~}}|||}}~~|{z{|~~}|{{||}~~~~~~~~~~}||}|zwuuvy}~~~~}|zyyyyyz{||}}~~~}}}||{{{zzzzz{|}~}||||}~|zzz|~}{{z{{|~~~}||||}~}|||}~~~~|zxwvxz~~|zyxxxyz{|~~}|{{zyyyyyyz|}~}|||}~}{yyz|~}|||}}~~~~~}}}~~~}||||}}}|zyxwxz}~|{zzzz{{|}~~~~~~~~}||{zzyyyyz{}~}}}}}~~~~}{zz{}~}|{{{|}~}}||}}~~~~~~}||||}}}|{yxvvwz}~|{zzzz{|}~~~}|||{{zzyyyz{|~~~}}}}~~~~~}{zzz|~}|||}~~}}|||}}~~~~~~}|{{{{|}}|{zyxwxz}}|{zzz{|}~~}}|{{{zzzyyz{|}~~~~~~~~~~~|zzz{~}}||}}~~}}|||}}~~~~~~}|{{{|}}}}{zxwvwx{~~}|{{{||}~~}||{{zzzzzz{|}~~}}}}~~~~~~}{zyz{~~}|||}~~}}|||||}~~~~~~~}{{z{{|}}}|{ywwwxz}}||{{|}~~}|{zzzzzzz{|}~~~~~~~~~~~}|zyyz|~}||}~~~}}}|||}~~~~~~~~}|{{{{||}||zyxwwxy|~}|||}}~~}||{zzzyzz{|}~}}}}~~}}}~~}{yyy{}~}||}}~~}}||||||}~~~~~~~}|{zzz{||||{zxwwwx{}~}}|}}~~~}}|{{zzzzz{|}~~~~~~~~}}}}~~|{yyz{~~}}}}~~~}||||||}~~~}}}~~~|{zzzz{{|||{yxwwwy{~~}||}}~~}|{{zzzzz{|}~~~~~~~}||}}~~|{zzz|~~}}}}~~~}||{{{||}~~}}}}}~~|{zyyyz{{||{zxwwxy|~~}}}~~}||{{zzz{{|}~~~~~}}|||}~~~}|{zz{}~}}}}~~}||{{{{|}~~~}}}}}~}|zyyxyyz{{{{zyxxxz|~~}}}~~}|{{zzzz{|}~~~~~~~}|||}}~~~}|zzz{}~}}}}~~~}||{{{||}~~}}}}}~~~|{zyyyyzz{{{zyxxxy{~~~~~~~}|{{zzz{|}~~~~}~~~}|{{|}}~~~}|{zz{|~}}}}~~~}||{{{{|}~~~}}}}}~~~|{zyxxyzz{{{{zyxxyz}~~~~~}||{zzz{{|}~~~~~~}||||}~~~~}|{zzz|~~}}~~~}||{{{{||}~~}||}}}~~|{zyxxyzz{{{zyxxxxz|~~~}|{{zz{{|}~~~~~~}|{||}~~~~}|{zyz{}~}}~~~}|{{{{{||}~~}}}}}~~~}|{zyxxyz{{|{{yxwwxy{~~}||{{{{{|}~~~~~~}|{{|}}~~~}|{zyz{|~~~~~}||{{{{{||~~}}||}}~~|{zyxxxyz{||{zyxwwxz}~}|{{{{{|}~~~~~}|{{||}~~~~}|zzzz{}~~~~~~}|{{{{{{|}~~~}|||}}~~}|zyxxxyyz{{{{zyxwxy{}~}||{{{||}~~~~~}|{{{|}~~~~}{zzzz|~~~~~~~}|{{zz{{|}~~}}|||}}~~~}{zyxxxxyz{{{{zyxxxz|~~}||{{{||}~~~~~~|{{{{||}~~~}|{zz{|~~~~~~~}|{{zz{{|}~~}|||||}}~~~~}{zyxxwxyz{{{{zyyxyz|~~}||{{{|}~~~~~}|{{z{{|}~~~}|{zz{}~~~~~~}||{{zz{{|}~~}}|||||}}~~~}|{yxxwwxyz{{{{zyyyy{}~}}|||||}~~~~~}|{zz{||}~~}}|{z{|}~~~~~}|{{zzz{{|}~~}||||||}~~~~~}{zyxwwxxyz{{{{zyyyz|~~}|||||}~~~~}|{zzz{|}~~~}|{zz{|~~~~~~~}||{zzzz{|}~~}||||||}}~~~~~}|zyxwwxxyz{{{{zyyyy{}~}}||||}}~~~~}|{zzz{|}~~~}|{zz{|~~~~~~}}|{zzzz{||~~}||||||}~~~~}{zyxwwxxyz{{{{zyxxyz|~}}||||}}~}|{{z{{|}~~~}|{zzz|}~~~~}|{zzzz{{|}~~}}|||||}~~~~}|zyxwwxxyz{{{{zyxxyz|~~}}|||}}~}|{{z{{|}~~~}|{zzz{}~~~~}|{{zzzz{|}~~~}|||||}}~~~~}|{yxxwwxyz{{{{zyyxxy{}~}||||}~~}|{zz{|}}~~~}|{zzz|}~~~~}|{zzzzz{|}~~}||||||}~~~~~}{zyxwwxxyz{{{{zyxxyz|~~~}|||}}~~}{{zz{||}~~}}|{zz{|~~~~~}||{zzzz{{|}~~}||||||}~~~~~}|{zyxwwwxyz{|{{zyyyyz|~~}}||}}~~|{zzzz{|}~~~}|{zz{|~~~~~~}|{zzzzz{|}~~}||{{||}}~~~~}|{zyxwwwxyz{||{{zyyz{|~~}}}}}~~~}|{zzzz{|}~~~}|{{{{}~~~~~~}||{zzzz{{|}~~}}|{{{||}}~~~~}|{zxwwwwxyz{||{{zzyz{}~}}}}}~~~}|{zzz{{|}~~~}|{{{|}~~~~~}|{{zzzz{||~~}||{{{||}}~~~}|{zyxwwwxyz{{||{{zyzz|~~~}}}}~~}|{zzzz{|}~~~}|{{{{|~~~~~~~}||{zzzzz{|}~~}||{{{||}}~~~}}|zyxwwwxxz{{||{{zyyz{}~}}}}}~~|{zzzz{|}~~~}|{{z{|~~~~~~~}|{zzzzz{|}~~}||{{|||}~~~~~}|zyxwwwxyz{{||{zzyyz{}~}}}}}~~|{zzz{{|}~~~}|{zz{|}~~~~}|{zzzzz{|}~~}}||{||}}~~~~~}|{yxwwwxyz{{||{zyyyyz|~~~}}}}~~}{{zzz{|}~~~}|{zzz{}~~~~~}|{{zzzzz{|}~~~}||||||}~~~~~}|{zyxwwwxyz{||{{zyyyz{}~}}}}~~}|{zzz{|}}~~~}|{zz{|}~~~~}|{zzzzz{{|}~~}|||{||}}~~~~~}|zyxwwwxyz{{||{{zyyyz|~~}}}}~~}|{zzz{|}}~~~}|{zz{|~~~~~}|{zzzzz{||~~}||{{|||}~~~~}}|zyxwwwxyz{{||{{zyyz{|~~~}}}~~}|{zzz{{|}~~~}|{{{{|~~~~~~}|{zzzzz{||}~~}||{{{||}}~~~}}|zyxwwwwxz{{|||{zzzz{|~~~}}~~~}|{zzz{{|}~~~}||{{{}~~~~~~~}|{zzzzz{|}~~}|{{{{||}}~~~}|{zyxwwwxyz{||||{{zzz{}~~}~~~~|{zzzz{|}~~~~}|{{{|}~~~~~~}||{zzzzz{|}~~}||{{{{||}~~~~}|{zxwwwwxyz{||||{zzz{|~~~~~~~}|{zzzz{|}~~~}||{{{|~~~~~~~}|{zzzzz{|}~~}}|{{{{||}~~~~}|{zyxwwwxyz{||||{zzzz{}~~~~~~}|{zzz{{|}~~~}||{{{|}~~~~~}|{zzzzz{{|}~~}}||{{||}}~~~~}|{zyxwwwxyz{|||{{zzyz{}~~~~~~}|{zzz{|}}~~~}|{{z{|}~~~~~}|{zzzzzz{|}~~~}||{{||}}~~~~}|{zyxwwwxyz{|||{{zyyz{|~~~~~~~}|{zzz{||}~~~}|{{z{{}~~~~~}||{zzzzz{|}~~}||{{||}}~~~~~}|{yxwwwxyz{||||{zzyyz{}~~~~~~}|{zzz{|}~~~~}|{{z{|}~~~~}|{zzzzzz{|}~~}}||{{||}}~~~~}|{zyxwwwxyz{||||{zzyz{|~~~~~~~}{zzzz{|}~~~~}|{{{{|~~~~~~~}|{zzzzzz{|}~~}}|{{{||}}~~~~}|{zyxwwwxyz{||||{zzzz{|~~~~~~~}{zzzz{|}~~~~}||{{{|~~~~~~~}|{{zzzzz{|}~~}}|{{{{||}~~~~}|{zyxwwwxyz{|||||{zzz{|~~~~~~~|{zzzz{|}~~~~}}|{{||~~~}}~~~}|{{zzzz{{|}~~}||{{{{||}~~~~}|{zyxwwwxyz{|}}||{zz{{}~~~~~}|{zzzz{|}~~~~}||{{|}~~}}~~~~}|{zzzzz{|}~~}|{{{{||}}~~~}}|zyxwwwxyz{||}||{{zz{|~~~~~~|{zzzz{|}~~~~}}|{{{|~~~}}~~~}|{{zzzz{{|}~~}||{{{||}}~~~~}|{yxxwwxxy{||}||{{zz{|}~~~~~}{{zzz{|}~~~~}}|{{{|}~~~~~~~}|{{zzzzz{|}~~}||{{{||}~~~~~}|{yxxwwxyz{|||||{zzzz{}~~~~~}|{zzz{|}~~~~}||{{{|}~~~~~}|{{zzzzz{|}~~}||{{|||}~~~~~}|{zyxwwxxy{{||||{zzzz{|~~~~~~}|{zzz{|}~~~~}}|{{{{}~~~~~~~}|{zzzzz{{|}~~}}||{|||}~~~~~~}{zyxwwxxyz{||||{{zzzz|}~~~~~}{{zz{{|}~~~~}|{{{{|}~~~~~}}|{zzzzz{|}~~}||{{||}}~~~~~}|{zyxwwxxyz{||||{zzzz{|~~~~~~|{zzz{{|}~~~~}|{{{{|~~~~~~~}||{zzzzz{|}~~}||{{{||}~~~~~}|{zyxwwxxy{{|}||{{zzz{|~~~~~~|{zzzz{|}~~~~}}|{{{|}~}}~~~~~}|{zzzzz{|}~~}||{{{||}}~~~~}|{zyxwwxxyz{|}}||{{zz{|~~~~}|{zzzz{|}~~~~~}|{{||~~~}}}~~~}}|{zzzzz{|}~~}||{{{||}}~~~~}|{zxxwwxxy{||}}||{{z{|}~~~~~}|{zzz{{|}~~~~}||{{|}~~}}}}~~~}|{{zzzz{{|}~~}}|{{{{||}~~~~}|{zyxwwwxyz{|}}}||{z{{|~~~~~|{zzzz{|}~~~~}|{{||~~}}}~~~}}|{zzzzz{|}~~}||{{{||}~~~~~}|zyxxwwxyz{|}}}||{zz{|}~~~~|{zzzz{|}~~~~~}|{{{|}~}}~~~~~}|{zzzzz{|}~~~}||{{||}}~~~~~}|{yxxwwxyz{||}||{{zzz{}~~~~~}{{zz{{|}~~~~}}|{{{|}~~~~~~}|{zzzzz{|}~~~}||{{||}}~~~~}|{zyxwwxyz{||}||{zzzz{|~~~~~}|{zz{{|}~~~~~}|{{{{}~~~~~~~~}|{{zzzz{{|}~~}|||{||}}~~~}|{zyxxwxxyz{|}}|{{zzzz|}~~~~|{{zz{|}}~~~~}|{{{{|}~~~~~~}|{zzzzz{|}~~~}||{{||}}~~~~}|{zyxwwxyz{||}}|{{zzz{|~~~}|{zzz{|}~~~~~}||{{{|}~~}}~~~}|{zzzzz{|}~~~}||{{||}}~~~~}|{zyxwwxyz{||}}||{zzz{|~~~}|{zzz{{|}~~~~}|{{{|}~}}}}~~}|{{zzzz{|}~~}||{{{||}~~~~~}|{zyxwwxyz{|}}}}|{{z{{|~~~}|{zzz{{|}~~~}|{{||~~}}}}~~~}|{zzzzz{|}~~}||{{{||}~~~~~}|{yxxwwxyz{|}}}}|{{{{|}~~~~}|{zzz{|}~~~}}|{{|}~~~}}}}~~~}|{zzzzz{|}~~}||{{{||}}~~~~}|{zyxwwxxyz{|}}}||{{{{|}~}|{zzz{{|}~~~}|{{||}~}}}}~~~}|{zzzzz{|}~~}}|{{{||}}~~~~~}|zyxxwxxyz{|}}}||{{z{|}~~~~|{zzz{{|}~~~}|{{{|}~}}}~~~}|{zzzzz{|}~~}}||{{||}~~~~}|zyxxwxxyz{|}}}|{{zz{{}~~~~~|{{zz{|}}~~}}|{{{|}~~~}}~~~}|{{zzzz{||~~~}}||{{||}~~~~}|{yxxxxxyz{|}}}|{{zzz{|~~~~}|{zz{{|}~~~~}|{{{{|~~}}~~~~}|{zzzzz{|}~~}||{{||}}~~}|{zyxxxxyz{||}}||{zzz{|}~~~}|{{z{{|}~~~~}||{{{|}~~~}}~~~}|{{zzzz{{|}~~}}||{{||}~~~}|{zyxxxxyz{|}}}|{{zzz{|}~~}|{zz{{|}~~~}||{{{|}~~}}}~~~}|{{zzzz{|}~~~}}||{{||}~~~~}|{yxxxxxyz{|}}}||{zzz{|~~~}|{zzz{|}~~~~}|{{{|}~}}}}~~~}||{zzzz{{|}~~~}||{{||}}~~~~~}|{zyxwxxyz{|}}}}|{{{{{|~~~}|{zzz{|}~~~~}||{||}~}}}}}~~}|{{zzzz{|}~~~}|{{{||}}~~~~~}|{yxxwxxyz{|}}}}|{{{{|}~~~}|{zzz{|}~~~}}|{{|}~~}}}}~~~}|{{zzzz{|}~~}||{{{||}~~~~~}|{zyxxwxyz{||}}}||{{{{|}~}|{zzz{|}~~~~}||{||}~}}}}}~~}||{zzzz{|}~~}}||{|||}~~~~~}}|zyxxxxyz{{|}}}||{{{{|}~~}|{{{{|}~~~~}|||||}~}}}~~~}}|{{zz{{|}~~~}}|||||}}~~~~~~}|{zyyyyyz{||}}}||{{{{|}~~}||{|||}~~~~}}|||}}~~~~~~~~~}||{{{{||}~~~~}|||||}}~~~~~~}|{zzyyyzz{||}}}}||{{{|}~~~}||||}}~~~~}}|||}}~~~~~~~}}||{{{||}}~~~}}}||}}}~~~~~~}}|{zzzzz{{||}}}}}|||||}~~}}|||}}~~~~~}}}}}}~~~~~~~}}||||||}}~~~~}}}}}}}}~~~~~~}}|{{zzz{{||}}}}}}||||}}~~~}}}}}}~~~~~}}}}~~~~~~~~}}}|||||}}~~~~}}}}}}}~~~~~~~}}||{{{{{{||}}}}}}}}}}}~~~~}}}}}~~~~~~}}~~~~~~~~~~~}}}|||}}}~~~~~}}}}}}}~~~~~~}}||{{{{{||}}}~~}}}}}}}~~~}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}~~~~~~}}}}}}~~~~~~~}}|||{{{|||}}}~~~~}}}}~~~~~}}}~~~~~~~~~~~~~~~~~}}}}}}}}~~~~~~~}}}}}}~~~~~~~}}||||||||}}}~~~~~}}~~~~~~~~~~~~~~~~~~~~~~}}}}}}}~~~~~~~~}}}}~~~~~~~~}}}||||||}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}||||}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m53.bin b/applications/SprPoP/dist/MUS/m53.bin deleted file mode 100644 index 378e098..0000000 --- a/applications/SprPoP/dist/MUS/m53.bin +++ /dev/null @@ -1 +0,0 @@ -~t|vx}zx|zwq|q|urttvo}rz|v}v~~}~{y~}nwuzyyzpu}pr~}u{wv|zy{{vq|q{vrtswn}~rz}v}v}}|z~pys|{xy{pv}rq}u{xu||yz}ws}qzxsr}ryoz{~p}|x}v~}x}~p~vs{yyzqw{pu}~}wxy~}w}|zt|p{wtp}~strx~{q~{z}u~~w{{rtsz{y|o{~}~py}}~wu}}u{{|u{p}uvq{|sutu}xryz~v~|wzztsrz{y|r|{{|s||}~zx~~v}|~w|p|yxqz}sutu}yq{{}u~~|vz{tsrz{y|r{|z|s{}}~}zy}~w}}}v}qz{wo|~sxtuz{p~||u~{zv}{{}wp||xy|~vy}u}{v{~|{~{~zx|xtxyo|yu|s}wu~p|{}x{{zzmv{fwusmvsyshuwx~nyyn}~{wzysz{e~~zj|tprn|o{}rw}u{vznv{wyxp|tstzr}{z~mwfxnyzx|u{|uzg}sw{zhk|nlvzyw|s{}xo}rg~wsq~n~vpypu{|~uzx~yr~xsjyrr}u{w|sktzyxzvyu~t~swtxsx{}ox{j{szsuo~quw|}yyvqqxvqhxyrwvw|yzv|}~totnwoxp}r~w|ovpyrx{tz{z{qz~x{q~w|~qxy|z{~xx}rs{yyrzv|~oyx}zwrxzwlsxi{yj{z~u~~}~}x|vxut~xqwx~wwy~xvywvx|wx}qwoupy{rw{{{wwyovw~{vxxpm||uy}qnw}|xws}}yzu{y}}y~}|}|rv}{yxuxus{unpnzqtwzumz~}q~qu}wj{x{vpx~}toywvw~{|w}u~}wy{rws|rwqyvw{y}~~xswsym}|~z}x~q{vlkdq~~}{xrujzp{uw}yvo~z}ztyvrl]||rgs{xqwrd}vv}{ziwy{fu|wvwo|tyqrzrr}~wl|p|z}}|wy|ws|yzut~w{usqw}}ryqu{rqw}~{jv{xgq~{jwq}z}snytipqu}roukw{~}yxxvos~mmpkrzsyby}mlovwz{p~zotg~mw}ywkpwwzo{~zz~z}{|~ufnwvsz|}ojmnsvxsp{t{r~ytz{|{|~gx|pq{ypw~xswyus}o{u{uy}xxzstqw}ot{o|kt}tzz{}z|zrvwzsw|v|~szml~}vx~~{{{}{|y}wt{{vvxoxxwzzz~w}yqw}rizmw}yyr|wxvy|z{}w|{y~}s}}|y}yzx}p||u{zr{zyxnx{rwzxy||zzzoy}js~vpz~y~|}px|y~}wqvz{ywy|xh|owzy~zSvqvaxUPXdVSWaostyvltblmdrp{}uxpxvjg[vcvlvbmanranez{pztopes~o}~e`_g|sltmorakfihl{}{wn}o~|rtvlfsuy}p|ys~ym}wu|}svjmisw[otmskjwtwuynmvpxotigymj}_t_es_or|tszqg}spZescx~sh^]]h`glodgywd_PVl]loRhaikpd]z]_bYaWdaibRNlpXuxiz|t|kwyruvqfcyoZb_cccf\jnazyvteurugbpjra{xwdahdvb[{yzuvthg^~}{~zqzsmqfjfntexywltijk`^eg[puf]h}uigZmncoZi{runhm}nqppruzq|lpxvmlx}}s}sw~yxueivlmcgxhxv|}~umtrpZpjrdkyd~xsc`WPTGYlb~}mnm`bik\hmdfrnglWao\dVaqgrdzml~opaa^WqtgWXbjXWe`a^avtuyiekvvrzvnrsznjaifbebpwgs{}yr||rha`foactoksx}y}u{omp]lpqjaswzzik]Z\]k__ou}|l{}nnymsqcutkbUpcoqesdx{wthnlv~zuxtlr}{{|z}xx}y}}yokaha]ksulh}~~souqxqhuzps|{q||m~~{z|~m{z{}qonepmpkaxywops}~szxu{|otwo]hreymxyo|txto}nsypxqmhfutqrpwvvylv}tuzuv~tjqjnvz~o}|vtxzpoxrbkvynsm`quoef~n{xty~{}{yuhpqkqw|q}zuylrwf]cmiek[\haTOa\QdXceZhml][cftynjw}{{wyz|szxo{{y||x{{|vyqrt|{ssy~rzkrxrgeygntgsjo~}mw{~yz}wx}{rsyzvkWSTKR\a^Rkt{~}owtfU[iZhg`jeivxrmumkwxuicztemzqio||}rw~r``aZhruj^ws~w{w|o|o~uvstzxqprmx~{yrx{qy~{{~msy}zk\[XTbnpc^tn~~xtwyvjnvk`hkmmrjeplc]nlZhaeoagundfpkjrojbjysjhvqjkrwpv~tnu}|zs~z~w{~|~~}lhf^foqnnyvpoq{rjp~{mlkmqzneqhYTjgZi_bojnzznjrloxrh]ene[^njd\blfkqlhovrttz}l{pwwls{xnz~yhkrure]d^Wbmnbhvk}vwyu||vzsgejqvfahvo`ekjn{xx}vmozwyz}{ytvrp|~uxxxyzosnwu|}v~}||}~}~z}wvtv~vnvyzrhfjhkvxmjv~rry{onvzmjjlntsnrslhvsfuolxwu}x|x{yystzyyzrqtkinsvwldptqbeorof\ZXU_ijet~xqxqv|v|ztu{s{~||}|v||utupt}~vu}}xwy~{vxyqwu{ur{~zz~}}wqmy{vv||yty}|}{xyzxuz|mjrtpbdilkfba^bhf`auts{{|~qpt{qhdchrqjkjdcmi\if`hidjpnnromomifq{uplx~|ywusx|z{||~~~zrwsz~xqpojkkd`o{s}{}xvw{~yurkdio^`kbfnigptsvzwtphdhsphaconjfda`fklrzvpwzp~}q|~||vsz~wmrxyrfhmolfdfcbfc]bqhlzny}wz}zzzwtx}wnou}unllpwwrrqptxp~zt}~x|~~zuz~~|||z|zut}~~|z~z|z}yx}|{~tw~}qpuxwtstroolhm{su{~|rrv~xnihjopmmkhkurhtsltxqqx|}zyzsy|yuolouz~wz|y|vrmknrsogajprkcgkmljigdcd`^kyq~{w}y}~y}xywy|~}~}{zxrr{|xwz~|ywtu{uuxv~zqrwz}~zwspr||vqz|xuvy||w|w{yv}|zvvy|zsignqqf_aegggijihd`drsk|xzz{rnmpsrpnlnvxkkumjsrkkquwxwusnkp{}yur|~zwy}{}{}}|}||{z|~|vopx||tlkmponnmkifdj{{~|wsqrwuilwpmuvopuz~~ytomqywqlhpvwuqmklotz}xszvz{w~}vmmstsibbfhijlmlidaiwtn}|t|zz{|}vtv}{tqqstrpqsx~{qxxxxwz~}{}zsv{}zqlnsuvx{}|zvu~|}{|~}}~{trx{{uljmprsuvusolp}xzvwz{tnlmoomllpwypq|xt}{wz~~zxx{ztpt{}{xtrsw|zv}wwwnqttqnkkllhc_ckoogaacfghijigegs|{{|z}tqrvxz||{xtqw|zxxyywuv{~tvxt|}uprvz{zwsqsxztrw~|ywx{~}uu}}sv~vpuz|{xvvvtohehmome__begjlmkhfjt{ty~{zzxvuuzvt~{tyztsvz||yvtuz~zvz{}}vvy|~}zz}|}|{z{~z{~|{~|xvuspkgjqvwrjgilnnmliebdnywu~tzvt}yuw{|zy{{tqu{}|yvtux{|wrv|u~|x}yrpuyzwnhghkmnonjgekuvnuzw{{{xvvuttv{}tv{ty|utvy{{zwvx~}y|~~~{|ztoqvxxrkijmprtuspnr|~||ytrvz|yrlkmorstsqnms|}tz}}|~zusrqppsx}|tu~uy{xy|}}{ywx||vpptxzxvuuwz|ysqy~wt}|rmprrpmkigc_\_eknkfcceghiheb`cmwvs~}}~z||uqpruwxwurps|}y}{zxxy||uyytz{trstuusqruz~}ytqsx}~~}{{|}|vqs{yprzxonquwwvusplgdgknnjc``behjjjhhnw|wu{{}zz{}|{z{}|~~~~zwy}|vrsuwy{{zwwz~~}}}~~~}wssw{}~}~}y|zx}snoqrqonljgddhnstqkgffghihfeflw}z{{z{ww{~~}|}~~|wsw~vyyw{|xsqtxyytnjijlnoomlnt|~ww}}~}{yyy{xu|zv{~xutuvuuuw{|xvx}~yv{|tx|usvy{{zyvsnkjnrtsnighjmprrqqt|}{{~~{wspquxywrmklmopqqpqu}}x}}|zxxxz}{w|y}{yyyxwuuwz{ytokknrtuvvwyzxsqu{ypovtkedfggfedb`^`djnokfcbcdefeeeiq|{~~z||~}}ywx|}wspqrstutsty|}||~zv{{tw~~ytsrrrqqsvz}{xspruy{|{{{{ytpotyumpwvoklnpqpomjgdehlnmieccdfhijjlqy~|w}~|||xuux|~}zurqqstuuuux~{~~~}|{{}zupoptwxzz{|{yutx~~vsy{tliijjihfdbabflprplhffffeeddhowyux~~|xtv|zuzyvx{}~~}{xustx{|zvqnmnoooooqu|~yu|}~{w|~ww~~yvuttuuwz~{wttvz|~~~~}{wssx}zrry{upoqsttsqnkiilprsplhggijkllnrz~y{}~zz{|}|{yvsrsvxzxtpmllmmnnnpu{}yu{}|}y~}|}{yxxwxy{|zupljknprsttttqonsyxqmsuphdcddcb`^\\]agknmjgeddddccdgnuwtu|yy~zz|z{}~}{ywwz}}yusrrrsssux~{{yx~}vw}}yvtssstvx{|{wsppruwxxxwvtpmotwsllsuqkiikllkigedehknnlifdeeghikpxwsuwy{zywuttstusmhgmpkegmqqonoqux}zvxwpfa__^]\ZWUTTWZ]][WSQPQSUY_hx`ynh}xolnokfa^\[[]_cgjlllmpv|tlhec`][XVTUX[^^\YVTRSUVX\bly{n}{x}xvy{xsomkkklmprsrpmlmquy}}zwvwwxz|wor{nhgikmnopqruvvtplmstnhhkmkea^\[ZZ[]^^\YVUVY]adgikllmou}||vuy{xvtojgkrrlilqsqmkjiiijkmnmjfcbbdgiklmmlkkmtz{wv}sjdbciq|yrrqkb_beeb^\ZXXXXYYYVRNKJKMOQSUUUTSSW]a^XY_ca]Z[^adgiknrw~}~~xspoooprttspkgdcccdcca_]ZXY^cb][`gifddfjlnprsux}zri_Y\gs{}tmiedb`]YURQRTVWWWWURPPSXXSNPUYVROOPRTTUVWY]bgknoooprux{yleglszyqkhggggfeca_^aegc]]bec^[YZZ[[ZZYZ\`dgiihghijmpsw|{qha\\`hr~}wrmhd`\XWX\^ZSQTWUOIFEEEEEEFHKOUX[[[YXWWWY[^bhorssy|xurppry{usqld]^bc_ZWWWXXXXWWX[^`bb`^\[[[\^`chmonkls{}{z}~ukb[WVW\bku~{ywqh`[XVUTRQPPRTWYYXVTSRRSTUX\aeeb`dlrpnotz}wqkfa_ags~xsnhd`^^_``_]ZWUTTTTUW[^`^ZX]deb_`dimprsuvwwvvwz~|wqjeba_ZSS[fnrw{{wrommljgb^ZWTRQPPRVY[YWYaikihkosvwxxxxwvuuw{|piihd^Z[_gpzztoligfgikjeaagkidbdgjlmnoonmljiiknruwyz{|}~}{rib\XSQPQU\eq}{xvutpjddilidbdgjmnnoonmkjhijmqtvwwxyz|}~|zy}~ysmgb][[]`dint{|xyyslgfhijjjiihfdb``adgjllmmnoqrrqnmpw{yuuz}{zxusqonmkgb]XTTUY`hr~~{xuqnkjjlptwyz{{{{{zxupmnsurmnsy|}~~wqkfb_^^bhoux}ztpmlmnonnlkjjjjhea_bgieaafjllkjkkmpsuxyyxwwxz}~{wtqomlje_\`ioru}}zxwuusqmihlppjgimpqpnnnnprtwxyxvuuvy|~xojkkf_YY]bhov~|ytojjnplfdfijheb`____abba_]\\^aehknopqqsx}|wx||yxyz|~}{zxtqnlklnqv}~vposuusrqqqrtvwxwuromlmnnooonmllotxxtsx~~|wrlfb`aejqx~}}{wrnkihhhhihfc`^]^_`bcccba`bgkmjgiossqooqstvwxy{~}xtrqpnlkkloty~}|zwsokiiijjkjjhgefinolhhnrrollmnoqqrsuw{}yuroljhdbemv|}xrmigffgggfecaacgie_]`cb^ZXXY[\^_adhmrvyzzywwwwxy{~vpnnlf`agouz}ytolkmnlfbdijhdbabcdeefgilpsvwwvuuvwxz}~|}|vnhghgda__`dipx~{{|ztnloqnjfcccccbbabdgilmmkjiijklnquy}}zy|{vqmigffgijkmpu{~xspnlkjhfffgiklkjhfedddegilprqmlqwyvtuy}|xtpmjgc_ZUQPQUZ`fnu{zurpnljhhiknpqpnkhfdbbabdgjljhkry{yx{|vqnkigedcbbdhpz~{wurqpoopruwvsopuywsqswz|~~}~~yvtrpnkhd`\YX[_b``eox}|yvtrqqrtvtplmrurnlnrtvxyyzzyxwvwy|{wrnkkljd]\`cdccfjpw}}}}}ztonrtrljkmpqrrsssrqoooqtx{~~zxxywofaaa]WQNNOQUY^dkt}}}~}{yxusqpqsw{~wuwxtnieb`^\[ZZ[^cioty}|yvtstvxyzzyyyyzzywtsv{}zvw|}xurpnkheb`_`aaa`_`adhmsz~{zz{|}}|{{{{{zyvssw{{wtv{~}~}{zxusponnmkhd_\YWVWX[_elqsuz~}{wttx{xspsvwvtrqppqrtuvvusstvx{~~~zyxxyyzyzz{}~~}yuokfb^\ZYZ\__^^dnvyz|}~xttx{{zyyz{|~}~~{ywvttttrlfdgihdbdinty~zxz||{xvutttvwxxwurqqqsuvwxxxwwx|~{}~}|||}}|yrnoqpjdbbcdefghjlnpsw}}|||}}}{xvtsstuvwwwvutux}~|xx}~|{{|}}}}~~~~~zvwzzuollmmljhgec`^]]_chotz}}|zxurpnnoqrttttsrrtwyvqoruurnmmoprsuvx|~}}~|yupkgcbbcfhjloquy}}zwuuwzxsprvxvsqqrstuuvwy{~zvtrqqqpnljhgggfeddhovyy||zy{}~zuuy{zwtssstttttuwy|~~||{{||}|{{|||{ywtrpnlie`^`dfcacjquy|{|}xutssssrqqrtvxyyxwuttttuvx{~}zy|||~|zvqljlmjc__bcba`abehmsx}}wtrqqqqqqrtwz|~}|ywutsrrstwz{zxy|z{{vnjjkkhecbbcehkosuwy|~}}}~}|{zzz{|~}~|}|vtuvvsoligeddddcb`_^`bgkpuz~}{zyyyz{}~{wx}~zyz}~~|{}}zwtrqponmkiea^\\]^`bcegilpx~|{zz{}~}yuuy|zvtvx{}~~}||~}{xvtrqqqpnlieb```````_^]\]afiigjqx{}~~~|zyz||ywutsrpmjfb_^_aa^Z[aegghkoty~}}~~~{xwvvvvtsqnkhghjhc^^bcb_]]_begknrw}~||}~{{~~|}}{zz{{{zywtrpprsoiefhgc_[ZYYYYYZ[^bglqtwy|}}|z|~~|zyyyyz|}}}{zyyz{}~~|zxwxzysmjlmjea^]\\\[[[\]`befffefgilpty{|}||}xtvyyuqnlkjihfdccdeffeca`_`abehmrx|}}~~|||}~~}|}{||ywvvutrqonnoponkheb`_]]]^aceecchosstw~|{zz{|}~~}|{zz}~zz~|yxwwvvutsrrsttsqnkhfdba`_`aa_[XZ^`_]^bglptx}~}}~~~~|zxxyz|}~}|{}~yvy|{xusstuuvvwxz|~~{xtromkihhijieabfigcbcegiijjkllmnqtz~~}y{~}zxwwxxxxxxy{}}{zyxxwwxy{zwrpqtsnihijjjihhgfedddgkotx|}}}|||}}||||}~|{zzyyz{}~|xttwxupmmnonmljigda_]\]_bdfgikmptwz{||~}zyyyyyxxxxy{}~~}{yxwvvvwxy{|yusuyyuqpqsttsrqpnkhfdddeggggggghjkkjilrxzyz~zxwwxxyyz{}~|zyxwwxz|}{xy}~|}}{xurnkjijkmmmmlllllkifcdhkjggkquxz}~}}}~~~~~~~|ywvuvwwwvtrqpnmkhdabefc__bfhijkmosw{~~~|}}{|~~}{xwvvwxyyxwutsrqokgdeghd_]_bbb`__`acfjmprstvy}}}|||~}zz~{z{}}|{|~~}}|zvronpplfcdeeb_][ZYZ[]^`aaaceinrw{}{yyz|{xxzzuomnoomjhfeeefgghgedccdfikmoqrsux}~{y{~~zvuwyywtrponmmmlkifc`___`aabbbbcfkpsssw}}~|yxz}}yuvyzzxvtsrqqqqpnlhecbaaa``_]\ZZ[_ba__cilllnqtx{}}~ywxz{ywusrrrrsssrpnlkjkkkkjhfc`_`a`\XWZ]][Z[]aehlptx}||~~|{{{|}~~|zxwwwwvusqnkiijjgb^_aa^[YXYZZ[\^`chlqux{}}{yxwxxxxwvtrppqsrniikljfca``__^]]^`begiijjlnpsvz~~}zxwwwxxxxwusrqruuqmlnpnjfdcba`^]\[\]^__^]\[[\]_aeimrstuz~~~~}|{{}~~}{yxxzzwroqsqnjihhhhggggghjjjheb_][ZYYZ\_bcccgntvvx}~~{xx{{xurpoonmljjjjkllkigecba```acdec`_cgigfhlptx{~}}~{yyxxwvutttuvvvtromkihgfffghfb__bda^]^`cefhjkmnopruy~~{|~{xwwwvvutsstuvvvtqomlkjihijjjgcabeea^]^`abccdddddefhlquy|~}zxxxyyyzzz|}}zwusqonnnopomiilpokiijkkjigeca_][[\^aehjmpswz~~~}}|{zz{|~~}{zxxwwwxyz{zwssvwuqnnoppponmkigecbbcefggghijkmnnnory}~}}||{{{|}~|yuuxywsppqrrrqpnmjhecbbbcddcbbaaaba`_^afiihintwz{}~|{{zzz{}~}zwwz|zwtuvxxxxwwutromllmnnnmljihgfeb_]]`b`]]`dfghikmptx}~}}~|}}~}|zxuromllmopqqppppoomkgddfgc_]_abbaaacehlpswy{|~|zxwxxyzyywvuttrqnjhikkgcceffedcbbcdfhikkkklnqux|}{ywwwxyzyxwvuutsqnjikmkgddfgfdb`_^^__```_^]]^`begilmoqsw}~~}{xtqprspkhijjifdbaaaabcbb`_^]^`abddeeeefjnppoqv|~|z{}|wsrstsqnlkjjjkllkigeddddeeedcbaabehgedhmoooprtwz}~{z{}}|zxwvuutttsqolihggggfedca``bdeb_`dggfefhjmoqsvy}~|z{}~}{ywvvuuuuusqnkjiiihhgedb`_`bb_[[]__\ZZZ[\]^_`ceimqsuwxz|~~~~|zyyyyz{{zxvutsttttsqomjiijifa^`a`^[YYZ[\\]^_adgikkkkklmnprvz~~|{{{{||{zywusstusokkllieb`__^][ZZZ[]^_`__^_`acegkosvwwy~~}{yxwxzzwsrtutpmkjjihfeddddefedba__^^_`acfhihgjoststx|~|zywwy{yursuvspmlkjihgfedeeffeca_]\\[[[\^`a`^]`egfegjorvy{~~ywxyyvsqpppppppppqrrrpmkheca_^]^_``^\\`ddbacfiklmnoppqqrsvz{yxwvutrqqqqrrrqomkihggffghhheaadfda_`bceffghhiiijknquy|~}|{{||}|zxvtrqoonmnnomifehifb``abbbbbaa`_^^^`behjlmoqtvy{{|}~}||||}}}|zwutsrqppqrrqnkkmolifghiiihhgfecba`acdfghhijklnnnnnqvzzy{}zxwuttuvwvtqqtvuqoopppomkigeb_]\\^_abccdeghijihhkoqqoptx{|}vpnighe]YZZXXXVTTRONKIHIJKMNNNPQRUXY[\^dkprtx~|zzyyxvutssttuuuvx}js_k{|ywtqlgb^]\YTNMNNLJHGGHILOSVZ]`chmsz~|{{{{zwsrtvvttux~saV[qo]QMLLMORW\]\]`dfgfdeeefhikllkjjijklmmmmlkkkmpqpmnswxxy{}|ti[NE@BL[m|tja\WUTUVWY[^bhmnmoswyyyyyz{||{{{{|}~~}|zxwvuuuvwy{{zxx|~~~yuqnjf^YZ_gpy}|{zzz{|}}{xvwyyurqqrrqponmkigedddegghhhhiijkjjijnqrppsvyzzzz{|}~~~~zxy~zvronmnnnmlkjihgec`\[\][WTTUVUTRQQQQRTUVVVVUVWY[]_`aabbcfjmmkmqvyyz|~xspoosy~vrnje`\YWVUUUUUSRPONNOOOOONLKJJKMLIGHKLLJIJKMNPRTWZ^chlpsvy|}vnhda`bfkrz|tlfc`\VQOOPNKHGFFFFFFGGIKMOPQQQRSTVXZ]adhjjjntxyy{~|yuof\WTPKGFHNU]fow|ztnhb^[XWVVXZ[[ZZ^beddeilorsuvwxxwwxyz|~~ytoid_ZURONNNOU`mw~|{z{{{{{{|}}}|zyy{|zvttvutrpnmmmmnooonmmmoprtuwwxxxy|}~}{ywvvw{}{zxuromlkkjjjihfeccdffc``bdc`^^]^^^^^__acefhhhhhhijlmortwxwvx}{wusrsux}ytsqmhc`^\[YXVUTTUUVVTSQONNMMMNOPRRPNPTVVUUWZ]`cehjlnoqsux}yspmic_^_bgms{ztnieb`^\XTSUVTQOOPQQQQPONMLKJKMORUXZ]_behkmopquz~~~zupkgb^[YUQOQW]bflu}{uqmkigeca`_^___]\\_cedcdhkmnooprsuxz||}}|}~}zupkfb^[XUQOMMNRV\cipx}|{ywusrrrsstttssrrruvusqsuvtrqqqrrrrrstuwyz{{zzz{||~}{|||yvspnkihgggggimrz~}{zxwvvvvtromjhgeedeeghhfefjlkihjlmnnnnmmlkjjjlnqsuwxz|~}wspoopquy~}yxwtokjjjihgfedb`_]\\]^____^^^___^\[]`bb``cgijjklnprux{}|ysmihjjiilry~zvrnkhd`\Z[[YUSSUUUSRQQQRSTUVVUUUVWY\^`bdeghkoswwwz|yuqlhd`][ZYYZ]dkqtx|xtokhfdcba`_^\[Z[]_^\\_cdddegilnprtvy{~|wsokgda^ZVROMLMPTY^ekqw~~{ywvvwwwvutsrrrrrstvxyxvuwz{ywxz|}~~}}~|xtpppmhd`^\ZYXYZ\`flsz~{ywuttttuutssrrrqpnllmppnllnpqqqqqqrstuvwvvuuuvwyzz{{{{{|~{wtstspnnptx}~{vrpqqomkjihhhiiiigfedddeeeeedcbacefebbehiihijlnprtvxz}}~vwqfcb]XYXUTTTRQPPRTUSRRUXZZZ[]`behkorv{|yvsolhedccehnt}pjmjxz|}|xvvvvuttuwzrXnvTYfr{{wsokfa\[[\ZWUUWYZ\]_aceghjmqv{|xtqomkfa`cgjlnqty{|{voeZPMWo}zvsokjhhhhhhhhjjkjifa[UPKJP\ionhb_^`chlptvvwxz}|ric_`hu}yrh]SMMOSY`ege`[UPLJGGFGIKNPSTUVVWX[]^][\^aa`___`acdegiloruxz|}|rjdachq|~umgc_ZTMJJIHEB@@@@@A@GUS[_efjbeYp{j^XQNOQVX[ny~{vrpoqv|~xqkfca`cfkrx}d`r^JOTY]aeinuy}xj]oqkv~yuqqponmkifeccceglsy~yt~{|}vsstvxyyxvtsrolf`\YY\`flry{|~~zm_VU_koke`_``^\[[_fozxrqtx}zxuonookbYRORXaku}~xph_WPKHGHHHFDCDGKQW]dimpsx}sjb^^clyznc[Y`o~}wrlhc`^_bfkmnopqtwz|~~}|{z{|}}{||||zwrnkjknsx~{rjb^]^`b`]XSQWcnuvvutromnpty~~}|{ywtqnid`[XVVX\`elu~}|}{xwy||tle_[YZ]cimnkhffhkosvyyzywvtpkd]XY^a`^^_acfjmqux|tibeoz}{wvx|{uqnkhgfgghijllllkklnqtvwuspmkihgghjnsy~zxwxyzyxtpnlllmkhc`co~~|{yxwwyzzyupjd__adc`\[\^aceghjklmmnooopppqqrrtvxz|{wtuy|xtstuutsrpnnorx~{xustz|{|~zts}|ywtqoljhfda^\[_hszzwssx~{ywutsrqqqolgel~}yuqlgc]YUSSTX]chmopomjfca^]\[[\]^_abcegjlnopu}~|~}zwtng]SPXhsuuw{~~{xtpmieb__`a`^YTRTXXVUX\aejnrvz|wsolkkmptx{}}|zxvtrqpmjikmjdfs~}}~}~|yvsqpoonligdccdfggdaajz|xuwywrmkjjjhfc`]YVSRRSW\biov{~~yvw{|ywx|ysnifddfimpstsplhecdgknnmkgdba```abdfjmoonnopqqnmotz|zwsokgb^ZWTSSTUVVV[i~}}}}|{ywuuuvwwwutssssuvxy{|~zwz|{yz~~{zz|~|{zzyvqmq~}|{zywvutrqpnnmlllmnnmkgddgjiecdfimprrpliilkgddhnt{}{xvuutttrpmkiijmpuz~xsw}zwtplhd_[WTQONORUZ_cgjkkjklosx{|~~~|{yxwwwxz|}|ytps~~{xusqrsvz}{vrqrsuvwwvvutttvy}|wof^ZYXSNMPV]cfffkx}~{ywtsrqqqpnkifecbbaaaaaaabbbbcdfimrvxxuqquy{zxvwz}~{yvsqomlmnpsx{|yuqnljihijkmmkga]bp}|{zyyxuqlhjnomnqx}{zyyz{}~ztpu}zwtqnjfc`]ZXWXZ]aglprrrty}}ywwxyxvux}~{wwxz{}~~|zxwwwwyzzytnjlu|{vrrpmklmoqrtuwyz{{zwusssvy|}}{wtqonmmllkkllnopqrrstuusporwywuux{}yuqmjgdccehmrx}}{|}}zxvutuvx{~~~|wrootyzxsokhkw}{ywvtsrqponmllkjihgfedcb`_]\[[[[\]][[^fmnmmr{~|{{{{|{zxvtsrqppppqrtvy|~}ytrx}woifdcccdefilpswy{{{zz{~{xxxyyzz|}~}xqkkszvuwz}{ywvtrponnnpruvwwusqtz~zxz~{xurnjfb_[WTQONNOQTX]afilmkhegr}{ywvutsrrqpppooonnnnoopqsuwxywtrtxzxtrrsttuttssstvwyzzz|}zxwuuuwz}}xx{wuvwtoiecbbbcdfjmqtvw{|zwusrqpqqsuvvvuuwwsnnw~{wsolheca`__abehloruwyzzzyxvutsssstuvvuropuyzyz|}}}}}zxurpmjgecba``aaa_[UONU`ijhfhmsz}{ywvtsrqomifeglprrsuz~}||{{{|}~~{xutttroieccdeeefffgiknruy|}yurpnmlllmpsvy{|zvqnr|~}{zxvtrpnmkigeca_]\[Z[\]`dinswy{}~}~~~}|||}~|~~{wtpnljjjkmorux{~~{yvtqnkifdbaabcehiif`XTZhv|zvvz}zxwutssssssrqqpppqqrtuwy{}}{ywz~~||}}|}~{vronlkjhfdb_][ZYYYZ[]_acdfgghjmqw~{~}ywy{zwsqqqstvwy{}~{wsppswxxwxz|}}zxx~}zwtqnkheb`][YXWWXY\_ciotz~}zx{}{ywusrponmmmnpqstsqlhfjt|}ywwy}}zz{{yurppqtwz}~|{zzzzz{{|{zxxz~|zyy{}~{{||zupligfdca_^\\[\]^__^[YY`ly~|zxvtrqonmllllmnoqrtuvwxxyz|}{xx}~}|zyxwutsqpomlkjihhggfda]YXZ\[XVVY]aflquy{{zyz~}|{zyyyyzzz{|~{vv|~zvqooqpnjgffhknqtx|~}|{zyxwvuuuuuutsqoorw{{wrmhfis|zusssuwxyzz{||}~|zxusqnlifda^\ZXWVVWY[^acedcbdmz~}|{{zyyyxxxxxyz{|}~|zwuvyzxtqpppqponmkigeca_]\[[\]_adgjmnmmmpsusqoosx~~}}}|||{ywtrswyytnjimrtutvy}}zxvtrqonmkjjjjkmpsuwxvspqv}~}zwtpmjgdb`^]\\\^_bdgjmprtuwxz|~}|zxvtrqoonnoprtvwwurlhipy~~zuqqrtw{}|{{}~|zxvtromjgda^[XTQNKJHHHJLPUZ_chlpuxywvz~}}zxvuwy{yvromllmmnopqrstvwxyzz{{{zyxwvuuttuvxz{}~}zupos}~~~zvsrqqponljhfdb`^]\[[\\^^^]\]binopqtwz}~|}~||~~|zxvutssstuwxz|}|{zxwutrpnljhfdb`_^]\\]^_aaa`\WQOR[ejjgeefikou|{wsqqrqnida^]\[ZZZYZ[\^aejoty}~~|z{}ywwy{{yvtstuwxywtqprtsokgfeeeeedddccddeghjlnpqsssssrrstwz}~}~~|zxvtrqonmmnnopponmoty}~~}{zxwusqomkigffeffhjlnqrsrnjfejrz|zuqnnpqtuwwxxwvvutssqomjgfhkoqppoprtvxy{|~~}|{zyxwvtqnkijmprqppppomiffks{~{xxy}~~}}}~{xx|~zvttuwxyzzyxvusqomjhfca^\ZXUSRPONNNOPQSVY]aeimpsuvuttv~zxxyzxuqnnmnnnmmmmmmnoprtwy|~~||}|xvuttttuuvwxz{}~~|xtpqw~}~~{yvtqomkihgfedcb`]ZYY]adghikmoqsv{~}|{{{|}~}}||}~~{ywwyzyvsomlkkjihgeca_\ZXVTSQPPPQRSUVWWVSPNQXbkppppquy~~|zxvtrpnljgecb`_^]]]^_acfilnprtx~~|{ywvtrqonlkjiiiijihgdbbcca]YY[_abccegknruxz{}}}}}~}{zzz|}~|{ywvutsrrrstvx{~~}{zyxxwwvusplifeeffeb`^]^`behjmnonlhcachossokgfefhjlmopqrstuvwyz|}}{ywtrpnljhfdcbbbbcegiklmlkjjmu~}|{{{{{{{zywtqpqrroligggggggffedc`]YVTTUVVTQPOPRTWY\^dkorsrpnllmqxyustx|wpkjkmnoqux|~~|{ywwwwyz|~~}{ywurpnljhggghkorw{wplmsyzwx||vsqqstvwxwusrpnlkjiiijlnpsvz}~yurpopqtw{ytqommllkkjigfdcb`_^^]\]]^__``_]YTOJFDCB@@@@DJRZbhlmmmnry~{xwy~zurqrtwy{{zwsojfda_^^_`acfhkmpruxz}{wttx~~~~|yuqnifb_\ZYXXWVTRQRTXZZZZ[^adfiklmnnoqsvz~}wspopsv|}|xtqooqtuvwx|~}}||{{{zzzzzyyxwvtsqpnljhgecbaa``aacdeffea[TLGHNW`ddb`adimruxz{||}|zwtromljiihhhhhiijkklnoqsuvxxwurppu}}yvtttuvxz|~~{vpjfccb_\XUTSTUUVVVVVVVVVVWWXXXYZ[\\]^^_______`bgmu~}{ywvtssssttsqnkhimtz|}|}~~~}~~~}}}||zyxwuutssstuvwxyyxuqmjjov}|{z{}~~~}}{vroljihhggghijlnpsvx{}~{wrnkiijknqtx{zvwzzurqqsux{~{zywvusqnkifc`]ZWTROMJHFDB@@@@@@@@@@@@ADINSY`fkoqrqopt|~~|xurppqqpnkigffghiklmnopqrstuvxz|~~{wrmigffeb^ZWVUVWY\^adhknqrsstvz~~|{{{{|}}{xvutssstuvwy{}~|zxvsqnkhec`^[YWUSRPONMMLLLMNOQSUXZ[[ZXURQSZbjmnmlmosx~~{ywtrpomkigd`]YVTUVXYXWWY\`ekqx}}|{zyxxwwvusqnmmoqrpmhdbcdeecbaabcdefghijklmnooppqppoonmnnprux{~}{yxvuttsstuvwxyyxwvwz~~~{zyyywsokhfdcbba``__^^^_`abdfghhgea][[^djmlieb`_`abdfhijjkkllmnoprtvxz{{{zyxyz}~~~}{ywutsrrrrstvwyzzzzyyyxvttw}~|{{}|xtpmllnoomjfc`]\ZYWVTSQONLKJIHHHIJKLNQSVY\_bfilnpqrrru{}ywvwwwtqnlkkkkllllllllllmmmnnopqrssttsrpnllmprsssssuwy{}}|yvuw{~|zxvtrpomlkjiiijkmoqsvxyz{{||~~}}~~|xurqpooonnmmlkkiheb_[XVUVUTQNJGEDCCCCCDEFGHJLNPRSTSQPPRX`hnqqpopqsvy|~|zxvtsqonmkjiihhhijklnqtw{~|zxvtsqpnmmlkkjigdbaadfhhgfgghiijlnruwwvusqppqrtvy|~~}{zzz{|~~{yxxwwxxxyz{|}~~}{yxvtrpmkjhfdca`_^^]\[ZXVUTTVY]_``_^][YWUUY_ejlkifedegjmptwz}~|zxvtsrppoooopqstvxyzzxvtsuz}{ywvutsrpnkgdbaaa`]ZVTRQPPOONNMMLLLLMMMMMLKJJLNQUWXXXWXY[^adhmrx~}zwutuwz||zyxxy{|~~}|{zxxwvvvvvwxz|~|xustx}}yvutuvx{|~~}|{zyxwvvvvvvwwxy{|}~~}}||}}~}yurprv{|xutstvx{}|zyyyxvrnlihgffecb`_][YVTRPNLJHFDBA@@@@@@@ABDFGIJLOTZ`eijhfefjqy}|{zzzyyxxwwvuuttsssstttuvwxy{}~|zyxvusrpnljhfecba```````_]\[\_beeeddefgijkklllmnprux|}yvtstuwwwwwwy{~}{ywusqomkigeca_^\[YXXWWWXYZ[]^`aa`^\ZY[`fknmjhhimqtwz||yuqnkjihggggggghijlnpsuwxxxvtrru|~~~~|zwspnllllllljhda_^_`_]YVSRQQQQQRRRRRRSSTTUVVWXXYYYYYZZ[\^aejov|}}~~}{yxvutsrrrrrsstuvwvurojgdeinsutqmjhghilnqsuwxxwvuttvxz{{zyxxxy{|~~|zxusqpqsuvvvuuuvwxxwuronosy~}{yyz|~}{zyxxwvrokhfeefffffedba_][ZXVTSQPNMLKKJJKKLMOQSVY]adhlorstsqporw}zxwwwwxxyyyyyywvtrppqrtvvusrqqqrtuwy{}}zxx|~zxwvvwxyz{{{{{zzyxwvutssrqponlifca__`bcddcccdegijjjkmptvvusrqqrsuxz}}{yxwvvvvvvwxyz|~~|{{|||zwspljhfecba`_]\ZYXVUTTSSSSTUWXZ[\]]\ZXVVX]dkqsssrrtvy}~|zxvtrpnljigfedccccccccccbbdfjotx{|}}}|{zz{~|zwusqonlkjiiiiiiigea]YVVX[]^\YVTSTVX[^acfhiklmnnnnnmmmmnoqsvy|~{xuqonnnnmkhecaaaabdeghjkkkifc_\[]aeijifc`^]^_acegjlnpqsuvxy{}~}{yxvtrqonmlkjjjjklnprtvxyyxvspopty}{xtqnmmnnligecbba`^\ZWVUUVVVURPNMMMMOPRTVXZ\_adfiknqsvx{}~~~}zwusrrrrrsssssssssrrrrrrrqqrrrsstuvvwxxxxwwxz}}xsolmpuz}~|zwtsrrstuvvwwwwwvuutrqponmkjihhgggfgghijkmprtwy{||{zyxyzz{|}~~{xvtsrqpnmkjhfecba_^\[XVSPMKJJKLMMLKKKLNPTWZ]_`a```aelsy}~}|zzz{}~}|zxwusqonlkihgfedddcddeeghjlnqsvy|~|zxvtrpomlkjihhggedb_^]^_acccbbcdgilnpqqqpnmlmnpsuvwxy{|~~}{ywuspnljhfdb`^]\[ZZZZ[\]^^__^]\[ZZXWUTTVY]`a`_^^^`behlpswz}}|{ywvusrqpoonnoopqstvwxyyxvtrqsw}~|zwsoljiiigea^[YWVVUUTTTSSRRRRSSTUWXZ[]^____^^_behknqsvy}~}}}}}|zwussstvwy{|}}~~~~}}||{zzyxwvutsrrqppppppqqrsttutsqnkhghkpuxywsnifddfilnppooooopqrsttuvvwwxxyyzz{|}}~~}||}~|{|~{wtpnljihfeca_][YWURPNLJIGEDCBAAAABCEGJMQUZ^cgjlmljhggiov~|yvtsstuvvvtromkjjjjklmmnopqrstuvxz|~~~}||}|{xuroljihhhhhhijklmnnnmlkjkmpsttrqpooppqqrrstuvxz}~~~~{zyyyyyyxwvtrpnljgeca^\ZXWUTRQPPPOPPQRTUVWWVUSPNMNQX_fkmnnmmmnoqsw|}zwtqnlkllmmlkihggikmptwz~~|xutsstutspmhd`][[\^_^]ZXWVVWXYYZ[\\]^^__````a`````_``acehlpuz~|zyz|~~~~~~}}~}{xvtsrrqqqpppooooooppqrrssrqolhda_`chlnnkhda`__`bdfiknpsuwy{|}}}}}}~~}|||{||||}}}||{{{}~yuqqsw}~zwtsqpnljhfda_\ZXWVUTTSRRQPONMLKKJJJJKLMNPRTWZ]`cfilnooomkiikov~|yvtssssrrrrrrrsssttuuttsqpopqsuxy{{{{|}~}|~~{zyyyz{{|||||{zzyxvutsrqpoonnnnnnnnmmlllmpruwxwusqqrtuutsrrsuwz}~}|{{zz{{||~}|{zzyywuspligeddcb`]ZVSQONMMMMMNOOQRSUWXZ\]]][ZXWWZ_elqtuutsrstvxz}}{zxvtrpnmkjhhgffffghjlnpsuxz|}~|zxvtrpnmkjjihhgfdca___`aa_]ZXWWZ\_accccccccdeeffgghhjkmprux{~~}|{zyxwvvutsqpnkigdcccefhhhhgffeedb`^\ZZ\`eiklkhfdcbcdfhkmpruwz|~}|zxwvtsrqqpppqqrstuwwxxxwutssuy|zvsolihggfda]ZXVUTTTUUUUTSRPOMLKKMNQRSTTTTUWY[^adgjmpsvy{}~|zyxxxxxyyyyyxxxwwwvvvuuttttssttuuvwy{}~{wspoqty}~zuqnmllmnpqstuvvwwvvvuutssrrqqpppppqqrstuvwyz{|||{yxxxz}~~~~~~~||~~zwsqomkjhgeca_][YWUSRPONMMLLLLLKKJJJJLOSW[_acdccbaabdiow}}zyxxxxxwwvvutsrqponmllkkkkllmnoqsuwy{~~~}|zywutrpomlkjjiihhgfdb`___acdeedcbbcdfghjklmmnooppqqrstwz}}{ywusqnljhfdb`^][ZYYXXYYZ[]^`abba_\YURQSW\bgjkjhedcdfilpuy}|yvtrqqpppoopppqqrtuvxy{||}|{ywutux}~}}||{zywtpmjhgggfda]ZWVTTSSSSSSSSSSSSTTUUVWXY[\]_`bdeghikmosw}~~~~~~~}|{zyxxwxxyzz{||||||{{zzyxwwvuuutttuuuvvwwwwvtqmjfdcdhlpstspnkjhhggggggikmpstuuutssstuvxy{|}~}{{{|~}{{|}{{zywtqnkjhgffdcb`^\ZXVTRPNMKJIHGFFFFGHIKMPSVY]adgjlnnnmllmpty}|{zzzzyxvtrponnopqqpomlkjjjkmoqsuwz|~}|{zz{{|}~}zxurpnmnnoonmkjihhijkmmnnmmmoqsuvvtsqonmmnnopqstvxz|}~}}|}}~~}}}~}}{xvspnlkihfedba_^\[YXWUUTTSSTTUVWXYZZYXVTQPPRV\bhlnnnnnoqtw{~~}{zyxwvutssrqpomljihhilnqtwxzz{||}}}||{{}~}|{zxwuspmkhfcba````_]ZWUSRSTUWY[]_`accdeeeeeedddddddegikorvz~}~}|{{{|}~~|zxvuuuvvvtspnljiiijklmnoqrrrrpnkhda__adhkmlkheb`_^_`acegjlnqsuxz|~}{zyxxwvvvvwwxz{}|yvtsuy~{xvtsttsqnje`\YWVVVWWWVTRPMKJIIIIJKLMNPRSUWZ\_adgjloqstttsqpooqu{}zxutsrrrrrqqqqppppppqqrsstuvwwwwwwwxz}}zwuvx|~{yxwwwwxxxyyyxxwvvutsrqponmlllllmmoprtvwyz{zywusssvy}~}|zzz|~{{yyvstttuuvwyz|~}{yxwy~~}}|{{ywwxz|}}|yslgcadhmnleZPJIMTZ]\VOHCABGMRX\`bccbcb`][ZZ\_ekpuwyyzz{}||{yxwvuutttsrpoljgedegmv}|wuw{}zxvsqomlkjjklmnpqrrpmiea][YXXXYZ\^aegijjigedb`_^]]^_behlptwz}~}~|zxwtrqommllmnoqrtuuutroliea^[ZYZ]`dhlnnkhd`][[]^``_]\ZYYZ\_bfilosvx{}~}|{{|~~||~~}}|}}~{wspnmosw{|ywutsqnkfa]XTQNKIGFFGJMPTX[]^]\XTQMKKLPTZ`dhloqsuvxyyzzyyyy{~|zxwwvvuuuttssrqqppoooooopqstuwy{}|xtpljijnrw|~{xtplhecaabcegjlnqsuvxyz{{|||}}}}~~~~~~}|zxuromlmoruxz}~{xusqpqqsuwz}|zxxyz|~zvrnkigedba`_]\[ZYXWVUTSRRQPPONNNMMNNOPQRSUVXZ\_behjmpty~}zwsolihgfghijklnopqrstuvvy}}{{{{{~~~}}}}}|~}zvrnjfcbbdgkptwxwtoic_\\^bfjnpqqpnlifc`^]]^_beimqvz~|xvuvy||wrmifffgiklmljgc^ZUQOMMNPSUW[]`abaa`__adinruvvtrqqsux{}}|zyy{~~|{{{{||||{zxwussstvxz|}}|{ywusqnllllnqtx}~ztnga\XWWZ^adeec_ZUPLJJMQUZ^abb`^[WURQPPQRSUX[^aejoty~~|zyxy{}~|yvrommmnpqstttssstuwz|~~|yvromlmosw|zunhb^ZY[^djptvvtrpnmmmoprtvxyz{{{||}}zxwvuvvxy|~}}}~{upmklpv}{xvuvxz{zxtpmjhgffeedba^\YURNKHFDCBBBDEHJMQTX\_cfilnopqpnljgffgkqw}}{xvsqppppqstuvwxxwvusqpnmllmnopponmmmmnprux{~|xusrqqpqqrsstuuvvwwxxxxxwwutrpnlkklmnpppomjgdbaacfjmprttuvvvvvvutssrrstvxz}}{xvtsssstuutsqoligeca^]ZXVTRPONNOQSVY\_bccca^[YWWY\_ceggffffhrv{{wsqpqsuxz||{xtokgdcdgkpty|~~}|zyxwwwwvutstv{~~xplmqx}~yrjc^ZYY\_cfhiihfca^\[ZZ[[]acegijlmnonnmljigfeefhlqx|{~|zz|~vqnptz|wsrsvy|}zvrpnlkjihgedcbaaaaa`_^\ZYZ[^chnsvwwusolifc`_^^`bfjouz~}}}}xtpnnorv{~}yvsrtw{~{yyz{}~~xrlgedefhjkkjjhgec`]YUPLIGEDDEEEEEEEEEFGILNQTWZ\^acegijklmnopruz|xtqolkjiijklnprtvxzz{{{zywvuttttuvwyz|}~~~~~~~~}}{ywusqnlkjiijlnpsvwyyxwurponmnpruwz|~~{xwuuvwyz||}}}}}}}}}|||||}}}~}ywvxz~|vpkheccdefhjkkkjhfc`\XTPMIFDBAABDGKNRUWYZ[\]^^^]]^`bglqvy{|{ywutsstuwz}~~}}|{{yxvtrpmkihfeeeefhjmprux{}~~}|zyxxxy|~}|{zyxwuspmiea]ZXWWWXXXXYY[]`cfilmoooomlkkkkmnpqrstuvxz|~}|{wvvuurmmmllfddcca^]]\\[YXVUROKHFEEFKQX`eilnnnnnnnnonoooooprtwy~~~volmsz{upoptz}xtsrqsuy|ysnjiikmoomljgc`]YVTQPONOPQTVY\^beghhhgec_[WSPOQU\enw}~zupoqw~zxwwwwwvvwxz|}~~}{xvsponnopsuwyz{{{zyxwvuuuvxzz{{{zwtqlhd`__aekqw{|yvroljiijmquy{|zvqmjijntz}|||}}}}}~|ywvwy|}{zzz|}~}~~wpid`^]]^__``_^][YWVTRRRQQRRSUVXZ\_adgikmnonmjfb]ZWWY\bhnswy{|~}yurpooqsvx{}}|zwtpkhecbcfjotz~{zy{}}zwusrstvx{~~zvqlhdb``abehkmoqrsuvwy{}}}{xuqnkgec`^]\[Z[[]_cfjnrw{~}{{}|ywwxz|~~|{yvspmjfc_\YWTRQPOOOPQRTVY[]_`a`_\YUQONNPTY^beghhhhhijknquz~}}~}zxwvvvuutsrppopprsuvwxyz|~~yuromkigda^[YYZ[]^_`__^]\\[ZYXWVVTSRQPPPOOPPQRSTUWY[^acgjnrvz~{xutssttttsssstvxy{|}~{wtqomkkjkkmnprtvxyz{zywurpopqtwyzzxuqnkhffffhjmorvy|}|zzyz{{}~}zxvtsrqppqruy~~{xvtsqpmjhfdccccbba_^\ZYXWVUTSSRQQPPPOOPPQRSUWY\^aceghhhgedccehlqv{}zwutsrqqqrrsstuvwwxxxwwvutssstvxz|}~~}|{ywutsrsuy~~~~}|{zyxwvvuuuuuuuuuuutssrqonlkigfeddddefhiklmlljjiijlnqtwz~}{zyyzzzzzywvtrponmlkjheb_\YVTRQQQQRSTUVVWWWVUTSTUX\bglpsttsrqpoppqsuwy|~|zxvtrpnmkjjjjklmoqsuvxy{{||{ywurpnmmosx}~|{yxwvvtsrpnljgedcccdcb`^[YWVWX[]`ceffffeedddeefggijlnpsux{~}{zyyxxxwwwwvvvvuuuuuuuuttrqpnmlkkklmnnnljgc_[WTSSTW[_cfghgfedddefhjlnprtvwyz{{||}}~~~||{zzzzz{{|}~~~}|zywwwy|{xusrqpnlifc_\YWUUTUUVVWWWWWVVUUUVWY[]^_`````aabdeghjklllmlmmosw}}|||}}}|{yxwvuuvvvwwwwwwwvuutsrqqqpqqrstvwy{}~zvrnlkloswz}~}{xurpnmmnoqrtvwxzz{{{{{zyyxwvvuuttuuuvwxxyzz{zyxwvutuvxyz{zzyxxxxy{~|xuromkigfdb`^\ZXWUTSRQPPPPQQQRRSSSSTTUWXZ\^___]\[[\_chmquwxyyyyz{}~|zxwvuttttuuvvvvvvvuutttttttuvvxyz|}~~~|{zyxwvutsrqpnljhfdbbbbdeeeedba`_^__`abdefhiklmnoopqsuwz~~||{{{{{|||||{zywvtrpnljhfdcba``_``abccdeedcb`]ZWUSSTVY\_bdeefffgghikmoruy|~}}}~}|zxwvuuuvwxyz|}~|zyxxz}}zwtqmkigfffedb_\YWUSRQQQQQQQQQQQQQQRRSTUWXZ\^acegikmopqrtuwxz|~~|zxvutsstttttttttttuuvwxyzz{{|||}}}}~~~~|zxurpmkklnqtwyzyxvtqomlkjiiiiiklnqsvxzz{{zzyyzz{|}~~|{{{}~ywusqswx{|unhb][ZYY[]]^^^__^][ZZWUSPMJGECBBDGLSZ`ehgd_[Z]cjpqplhgjqz}xtux~xspnnoqsvxyyxvtroljhfffgilosw{~}yvuvy~{xwzxpjggjotz~~{wrnieb__^^_abdeedca`_^^^_`bdfjnquwz|~~~~|{ywwvwy|~{yy|{wtpligecbbbbbcccccca```__^]\[ZYWVTROMLJJKOTY`gmswxywuspmljjkmorvz}~|{{{|~{wrnlklnquy|}}{xuqommnqtx|~xrke`\ZYYZ[]]]\[ZYYYZ[\^_acdefghijjkkkklllllllllmmnnnprux|}zwutrrrrrsstuvwxyz|}~~}{ywvtsrrqponljhfcba`adfilnopoonnnnnoppqqqponllkklmpsw{}{ywusrppoopqrtvx{~|ywvwx{~~{wrlgb\XURQQQRTVXZ[[[ZZYWVUUTTUUVWYZ\^acehjmoprstttttuvx{~}|zywusrpmljhgggghjmorvy||ywuuvwz|}~~~|{yxwvutsssrrrqqpoonnmlllkklllmnoqrtvxy{}~~|zxvtrqqppponmkjihhhhiklnpsuwyz{}}~~~~~~~~~~~~{xurpnljigedba_][ZXWUTTSTTVWXY[[\]]^^__``aaaaabcegjnrux{|}}||{{zz{{|}~~|zyxwvuutsrqpoonmllkkkkkllmoprtvxz}~}|{zyxwvutsrpnmlkjjjiihfdb`^\ZXVUTSSSSTUWXZ[\]^_abdfilorvz}}|{zyxxwvusrqponmlkkjjkklmnopqrrsstsssrqponmlkkkkklmmmlljjihhhhijlmoqrtuwxz{|~~}}|||{{{zzz{{{{|}}~~~~}}}~|ywuspnlifc`]ZXVTRQPONMLLKKKKJJJKKKKLMNOQTVY\^acefhijklmoprtw{}{yxwvvvuvvvvvvvvvvvvvvwwxxyyz{|}~~}|{zyxwvuttssrrrrssssttttuuuvvwwxyyz{{|||||{{zzz{|}~~~~~~~~~~~~}}~~}zxusqoljhfdb`][YWUSQPNMMLLKLLMOPRTUWY[]^_`aabcdegjmptwy{|}}}}}}~~}|{zxwutrqponnnnnnoopqrsuvwyz|}~}|{yxvutsrqppooonnmmlkjhggfffffeedcbba``___```aabbcdefhikmoqrtvxz|~~~}|{{zyyxwvutrqponmlkjjihhggggffffeeddcbbbbbdehjlnopqppppppqrsuwxz{|}~}|zxvutsrrrrrrssssssttuvwxyyz{{{{{||}~~}{yvtqoljiggfecba_]\[ZYXWWWVVVVVVVVVVVWWXXYZ[\^_acegjmorux{}~}|||||}}}}}|{{zyyxxxxxxyyzz{{||}}}}}|{{zxwvutttuvwxxyyxwvusrqqppppoooooonnnnnoopqrstuvvwwxxyyzz{||}}~~~~~~~~~~~~~~~~~}{ywtromkhgecba_^]\\[ZYYXXXXXXXYYZ[\]^_abcdfghhiijklnqsvz}~~}||{zzyxwvutsrrrrrstuvwxyzz{||}}~~~~~}}||{{{zzyxxwvvuttsrrqpoonmmmmmmmmmmmmllkkkllllllllllllmmnoopqrstuvwxyz{|}~~}{zyxwvutssqqyyxwvutssrrrrrrrrssrrrrrqpoooppqrsuwxyzyyyyyywvwxyywxyz|{z||~~}ywwz}yz~~zy|~|{yxwvuttssttuvwxyz{{zyvqnjfc```bfjnrtutrokgca`adhlquwxxvsolihghjmquz|}|{{{{|}}~~~~}|{{zz{{|}{wussuw{~zuplifffhjnqtwyzzywtqnjgdb``_^]]]]]^^__`abcdfgjlotwz|~~|zvspopsx~}|{zyz{}~}||}~}||||}}~}|{zxwvusrpnljihhhiknqtvwwurokfca`beinruvvtqnkhffgimptwz}~}|||}~|zyyz{}}zvtsrstvxz|}}|{zxuspnljhfedcbaaa`````````abcdefhikmortvy{}}|{yyyyy{|~}||}~}}}}~}{yxvtrqomkjhgffeefghikmoqrtuvvuusqomkihgfffghijkklmnopqrtuvxy{|~~~}~~}{zyxxwwwxxyz{|}}~~}}|{zyyxwvuutsssssttuvwxyyzz{{{||{{zyxwusrqpopprtvy}~}|{{zzyyyyyyyyxxxwwvvuutssrrqqqpppppqrrssttuuuvvvvvvvuutsrpnljgfdcccdefhjloqsuwyz|}~~}}|||||}~~|zxvusrqpomlkkjihhggggggggghhhiijkkmnopqrstuvwxz{|}~}{zyyyyyyyz{{|}~~}}}}}}||{yxvusqpomllkjjjjiiiiiiijjjkklmnopqrstuvwxyyzz{{|||||}}~~}|{{zyxwwvuttsrrqqppppppqqqrrsstuvvwxxyyyyzzzz{{|}}~~~}}|{zyxxwwvvwwxxyz{|}~~~}}}}}}}}|||||||||||}}~~~}|{zyxwwvvuuttssrrqqpppppqqrrsttuuvwwxxyyzzz{{{{|||}}}}}}||||||||||||}}~~~}|{zyxxwvuuttsssrrrrqqqqqqppppoonnmmlkkjjjjjkllmmnnnnnnmmmmmmmmnnooppqqrrsstttuvvwxyz{|}~~~}}}||||{{{zzyyyxxxyyyyyzzz{{{||||}}}~~~~~~~~~~~~}}}|||{{{{{{{{{{{{{{{{{{{{{{{{{z{{{{{{{{{{zzzzzyyyyyyyxxxwwvvuttsrrqqqqqqqqppoonmllkkkjjkkkklllmmnooppqqrrstuvvwxyz{|}~~~}|{zyyxwvvuttsrqqpoonnmmmmmmmmmmmnnnnooooopooooooooppqrsstuvvwwxxyyzz{||}}~~~~~~~~~}}}|||{{{{{{{{{{{{||||}}}}~~~~~~~~~~~~~~~~~~~~~~~~~}}~~~~~~~~~}}}}}}}||||||{{{zzzzyyyyyyyxxxxwwwwvvvvvvuuuutttssssrrrrrqqqqppppooooonnnnnmmmmmnnooppqqqqqqqqqqqqqrrrsttuvvwxyzz{|}~~~}}}|||{{{{{zzzyyxxxxwwwwxxxxxxwwwwwwwwwwwwxxxyyzzz{{|||}}}}}}}}}}}~~~~~~~~}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}|||||||||||{{{{{{zzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzyyzzz{{{|||||||||||||||}}}~~~~~~}}}||||{{zzzyyxxwwvuuuttsssrrrqqpppppppooopppppppppppppqqqrrrssssttttttttttttttuvvwxyyz{{||||}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}|||||||||}}}}}}}}}}}}}}}}}~~}}}}}}}}|||||{{{{{{{{zzzzyyxxxwwwwwvvvvuuuuuuutttttttuuuuuuuvvvvvvvvwwwwwxxyyzz{||}}}~~~~~~~}}}||{{{zzyyxxwwwwvvvvuuuuttttsssssssssssssssssssttttuuuvvvvwwwxxxyyzz{{|}}~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~}}~~~~~~~~~}}}}|||||||||||||||||||||||{{{{{{{{zzzzzyyyxxwwwvvvvvvuuuuuutttttttttttttttuuuuuvvvwwwxxxyyyzzz{{{{|||||}}~~~~}}}||{{{zzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyzzyyyyyyyzzzzz{{{{{|||}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}||{{{zzzyyyyyyyyxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxyyyyyyzzzz{{{||}}~~~~~~~}}}}||||||}}}}}}~~}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}||||||||||||{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzz{{{{{|||||}}}}~~~~~~~}}}|||{{{zzzzzzzzzzzyyyyxxxxwwwwwwwwwxxxxxxyyyyyyzzzzzzzz{{{{{{{|||||||}}}}~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}||||{{{{zzzzzzzzzzzyyyyyyyyyyyyyyzzzzzzzz{{{{{{{{{{{||||||}}}}}}~~~~~~~~~}}}}}}||||||||||{{{{{zzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{||||}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}||||||{{{{{zzzzzz{{{{{{zzzzzzzzzzzzzzzzz{{{{{{{{||||||||||}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}|||||||||||||}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{~~~}{yxvttttuxz{}~|xvvw{zvvy~{wuwy}|zzzzzzz{|}~{xvuvx|}ywvwz}|xvuuvx{~}yvttuy~~xttx~|wttw||zxvuttttuuvvvvwvvvuuutttuvwxz}}zwuttvy|}ywutuwy|}{zyxxwwwwwwwwwwwxxyz{|~{xvuw{|xuvy}|ywuuvwy{}~|zywvvvwxz|~{xvvvwy|}zxvutttttuvwxz{|}~~}{{yywvuttttvwz}~zwutvy}|xvuuvx{~}zxvuttuvxz}~{ywvvvvwy{}}{ywvuvwz}~{xvvwy{}{zyxwwvvvvvvvvvvvvwwwxxyyz{}~}{ywvuuuvxz|~}|{yxxwvvvuvvvvvvvwwxxyz{|~~{xvuuvwz}|zxvuttuuvxy{|~~}|zyxwvuuuuuvwxy{}~|{yxwwwwwwxxyz{|}~~~~}|{zyxwvvvvvwxy{}}{ywwvwxz|~~|{yxwvvvvvwxyz|}}{ywvuuuvwxz}}|zxwvvvuuuuuuuvvvwwwwwwwwwwwvvvvvuuuuuvvvwxxy{|}~~}|{zyyxwwwvvvvvvvvvvvvvvvvwwwwwvvvvvwwwxxyz{}~}{zyxxwxxyz{|~}|{zyxwwvvvvvvwwxyz{|}~}{zyxwvuuuuvwxyz|~~}|{{zyyxxxwwwwwwwwwwwwwwwxxxyyzz{||}~~}}||{{zzzyyyyyyyyyyyyyyzzz{{||}}~~}|{zyxwwwwwwxxyz{|}}~~}}|{{zyyxxwwwvvvvwwwxxyyzz{||}}~~~}}|{zzyxwwvvvuuvvvwxyyz|}~~~~~}}}}}}}}}~~~~~}|{zyyxwwwwwwwwwxxxyyzz{{|||}}~~~~~~~~~~~~~}}}|||{{{zzyyyxxxwwwwwwwwwwxxxxxyyyyzzzz{{{{||||}}}}}}}}}}}|||||{{{zzzyyyxxwwwvvvvvvvvwwxxyyz{|}~~}}|{zzyyxxwwwwwwwwwwxxxxxyyyyyyyzzzzzzz{{{{{{{{{{{zzzzzzzzyyyyyxxxxxwwwwwwwwwwwwwwwwwwxxxxxxyyyyyyyyyyyyyyyyyyyyyyxxxxxwwwwwwwvvvwwwwwxxyyzz{{||}~~~~}}|{{zzzyyyxxxxxxxxwwxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwxxxxyyyyzz{{{||}}~~~~}}|||{{{zzzzzyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxwwwwwxxxxxxyyyyzzzz{{{|||}}~~~~~~}}}|||{{{zzzzyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyzzzz{{{{|||}}}~~~~~~}}}}|||{{{{zzzzzyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyzzzzz{{{{||||}}}}~~~~~~}}}||||{{{{{zzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzz{{{{{||||}}}~~~~~~~~~}}}}|||||{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{|||||}}}}~~~~~~~~}}}}|||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{||||||||}}}}}~~~~~~~~~~}}}}}}}|||||||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}||||||||||||||||||||||||{{{{{{|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~yvuutuxz{|}~{zwutuvx{~|xvuuwy}|ywutuuvw{}~}zxvutux{yuux~yuuy{vttvz~~~~|ywutuwz~{wutuw{{xvutuwy|{xvuuvz~}xuuy~zvuuy}}{yxvvuuuuuuuuuuuuuuvxz{}|ywuuvx{~|ywuuvxz|~}{zyxyyyyxyyyzz{|~{xuuvy}|xuuwz~}zwvuuuvxy{|~}~~}|{yxwvuuuuvwy|~|zwuuuvwz}~|zyxvuuuuuvvwxyyzz{{{{{{zxxwvvuuuuvvxz|}zwuuwy}~zxvuuvxz}~{ywvuuuvxz|}zxwvuuvwxz{}~|zxvvuvwy{~~{xvuuwy|}|{zyxwwwvvvvvvvvwwxyyz{|}~~|zxwvuuuvxy{}~}|{{zzyyzzzzzz{{||}~|zxvuuvxz}~{yxvvuvvwxyz{|}~~}|{zxxwvvvvvwxyz|~}{zywwvvvvwwxyz{|}~~}|{zyxwvvvvwxz|~}zxwvvwxz|~~}}}||||}}}~~~|{yxwvvvwxyz|~~~}}}}||}}}}~~}{yxwwwxz|~|zyxwwwwxyyz||}~~zsponlmoprsu}~~}zxusrpppqtvz}yvuuwy|~yvsrqpqsvy|~|{zyyxxxxxz||xwz}vstyzwvw{}|{||||}~~}|}~zvtstvz~~|zz{|~yuqnmmptz{wuv{zvuvy}}{xusqpppqqstuwxyz{||}}}~~{xusrqrsvz~|ywuuuwxz|~}|zxvutttuwz}}zyz|zvssvy~~|{{{|}~}|{zyxwwvvvvvvwwyz|~|yvtttuwy|~}|{zxwvuttssstuvxz{}~~~~}|{ywvutuvwy|~{xvuux{~zwutstuwy{}~{ywtrqpppqsux{~}zxvusssstuvxy{}~}|{{{|~}zwuuuwy|~|{yxvutsrqqppqqsuvxz|~}{ywvuuuvwy{~}{ywutstuuwxz|~~|zxvutttvx{~~{yvtsrqqqrrsuvxyz{}}{ywutrqqpppqrtuwz|~}{ywutsrqqqqrrsuvxz|~}{yxwvvvvvwxxyz{|}|zwvvvvxz|}|{{zzzz{{|~}{yxwvvvwwyz|~~|{zzz{|}~}{zxwvuuvwx{}~|ywvtssrrssuwy{}|wurpnkjiihinnopqx||~|xtrpmlllmorux{~}{yxxxz||yvspnmmnoprtvx{|wspnmmptz~}~}wrpqty~~{vtrqqruy}~~|yvtttvy}~~~{zy{}|vpkikov}~zvtux}~zwtqomlkjjiiiijklmoqtvy|{vromllmpsw{~~|zyxxyz|~|zxvwwvuuvvwxz{}~~|zxxy||{}{wtsrstvxz}~{wurponnoprux|}}}~~~}|||||}}}~~~~~~}|zxwvtsrqpppqrtuxz}zupmkkmquz~~|yxwwy{}~~~}zwtrqppprsvxz}~|{zyxxxxxyyz{|}}~}zwtrqqrtvz~~|zzz|~~~~~~~~~}||{zyxwwwwwxz{}~}||||}~~}{zyxwvutssttvwz||xtqommnprvy}~|{yyxxxxxxxxxxyyyyyyyzz{{|}~~~}{zyxwvutttuvwxz|}~|{yxwvuttsssttuvwxyz{}~|ywusqqqrsvy|}{yyyy{|~~}||{{||}~~}||{zzz{|}|xusqpopqsvy|~}{zyxxxwwwwwwwwwwxxxxyyzz{{|}~~~}}|{zyxwwvvvwwxy{|}~}zyxwwqpppqqprstuy~~{xxxusqquuvwy{~~zwuttux|~}|{{{||}}}~~~|}}|{zz{|zvvzysrv}}}}|||||}~~{xvvvwyz|}}|zxvutuwz~{wrpnmnpruwy||xtqnmmpuz{xwz{urqsvz~}{xusrqpppqrstuvvwxxyyyyyyyxxxxyz{|~|xvutuwy}~}|{zyyxxxxyyz|~|yxy|{wtstx~~~~~}||||zz{{|}~~}{ywutttuvx{~|zwusqpoooopqrstuvwxyyyyzzzzyxwvutsrqqqrsux{~{ywwx{~}yuqonoprtwy|~~{yxwwwwxy{|}{yxxxxyz|}~}|{{|}}zwutuwy}~|{yxwvuttssssttuwxz||zxvtssstuvwy{}~~|{zxwwuuvuuuuuvvvwwxyyz{{|}~}{ywvvvwy{~|yusponmmnoqrsuwxz|}}{ywusrppooprtvy{~~}}|{{{{{{{{||}~~~~}||{{zzzyyzz{{|}~|zxvvvvxy|~|ywutsrrstuvwxyz{|}~~}|zywvuuuuvwxz|~}{ywutsrqqqqqqrsstuvvwxxyyyyyxwwutsrqppoppqsuwz}~}}||{{{{{zzzzzzzyyyxxxwwwwwwxxxyz{|~}|zyxwwwxxz{}~}{ywusrrrrsstuvwxyzzz{{{{|||}}}~~|{zxwvutttttuuwxy{|~~}{{zyxxwwwwwxxxxyyyyzzzzyyxxwwvuuuuvwxy{}~~~}}}||||||||||}~~~}|{zyyxxyyyz{|}}~~|zxvutrrrrrstuvwwxyyyyyyyyyyyyyyyzz{{|||}}}~~~~~~~}|{zyxwvuttssssstuvvxyz{}~~~~~~}}||{{{{||}}||{zzzzyzzz{{||}}~~~|zywvutsssstuuvwxyyyzzzzyyyyyyyyyzzzz{{{||||}}}}~~~~~~~~~~}}||{zyyxwvvvuuuuuvvwxyz{|}~~~}}}|||||}~~}}|{{{zzz{{{||}}~~~~}{zxwvvuuuuuvwwxyyzzzzzzzzzzzzzzzzzz{{{{||||}}}}}~~~~~~~~~~~~}}}|{{zzyxxwwvvvvvwwxxyz{|}~~~}}}}|}}}~~}}||{{{{{{|||}}~~~~}|{zyxwvvvvvwxxyzz{{{{{{{{{{{zzz{{{{{|||||}}}}}~~~~~~~~~~~~~~~}}}||{zzyyxxxxwwxxxyyz{{|}~~~~}}}}}}~~~}}|||||||||}}}~~~~}|{zyxxxwwxxxyyzz{{{||||{{{{{{{{{{{{|||||}}}}}}}~~~~~~~~~~~~~}}}|||{{zzyyyyyyyyyzz{{||}~~~~~~~~~~~~}}}|||||}}}}~~~~}||{zzyyyyyyyzz{{{|||||||||||||||||||||}}}}}}}~~~~~~~~~~~~~~~~~}}}||{{{zzzzyyzzzz{{{|}}~~~~~~~~~~~~}}}}}}}}}}}~~~~~}||{zzzzyzzzz{{||||}}}}}}||||||||||||}}}}}}}}}~~~~~~~~~~~~~~~~}}}}|||{{{{zzzz{{{{|||}}~~~~~~~~~~}}}}}}}}~~~~~~~}||{{{zzzz{{{||||}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}||||{{{{{{{{{|||}}~~~~~~~~~~}}}}}~~~~~~~}}|||{{{{{{{|||}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~}}}}|||||||||||||}}}~~~~~~~~~~~~~~~~~~}}}||||||||||}}}}}~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}||||||||}}}}~~~~~~~~~~~~~~~~~~~}}}|||||||}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}~~~~~~~~~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}||{{{zzyyyyyyxxyyyyzz{{|}}~~}||{{zyyxxxxxxxxxxxxxxyyyyyyzzzz{|}~|}}{}~~}}||{zzzyyyyxxxxyyyzzz{{|}~~~~~~~~~~~~~|zy|~{zyyyzz{{||}}~~~~~~~~}}}|||{{zzzyyyxxxxxwwwwwwwwwxxxxyyyzz{{||}~~}|zyxyz}}{zyyxxxxxxxxxxxxyyyyyyyyyyyyzzzzzzzzzzzzzzzz{{{{||||}}}}~~~~~|zyxxwxy{~~|{zyxxxxxxxyyyzzzz{{zzzzyxxxwwwwwwwwxxxyyzzz{{{|||||||||||}}}}~~~}|zyxxxxyz|~~}|{zzyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyzzzz{{{||}}}~~}|{zzyz{|~}||{zyyxxxxxxxwwwwwwwwwwwwwwxxxxxxxxxyyyyyzzzzz{{{|||}}~~~~}|{zz{|}~}}||{{{zzzzzzzyyyyyyyyyyyyxxxxxxxxxxxyyyyzzz{{||}}~~~}{zyxwwxy{|}|{zzzyyyyzzzzzzzzzzzyyxxxwwwwwwwxxxyyzz{{{|||||||||}}}}}}}}~~~~}|{zyxxxxxyz|}~}||{zzzyyyyxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyzzzz{{{{|||}}~~~~}|{zzyzz{|}~~}|{{zzyyyxxxxxxxxxxxxxxxxxxxxxyyyyyyzzzzz{{{||||}}}~~~~}|{{{{{|}~~}}||{{{zzzzyyyyyyyyyyxxxxxxxxyyyyyyzzz{{{||}}~~~}{zyxxwxxyz|~~}}|{{{{{{{{{{{{{{zzzyyyxxxxxxxxxyyyzz{{{|||}}}}}}}}}}}}~~~~~~~~}|{zyyxxxxxyz{|~~}}||{{zzzyyyyyyyyyxyyyyyyyyyyyyyyyyyyyzzzzzz{{{{|||}}}~~~~~}|{{zzzzz{{}~~}}||{{zzzyyyyyyyxxxxxxxyyyyyyyyzzzzz{{{{||||}}}~~~~~}||{{{z{{|}~~~}}|||{{{zzzzzzyyyyyyyyyyyyyyyzzzz{{{|||}}~~~}|{zyxxxxxyz{}~~~}}}}||||||||||{{{zzzyyyyyyyyyzzz{{|||}}}}~~~~~~~~~~~~~~~~~~}}|{zzyxxxxxxyz{|}~~}}||{{{{zzzzzzyyyyyyyyyyyyyyyzzzzzzzz{{{{{|||||}}}~~~~~}}|{{zzzzzz{{|}~~}}|||{{{zzzzzzyyyyyyyyzzzzzzzz{{{{{|||||}}}}~~~~}}||{{{z{{{|}}~~~}}}|||{{{{zzzzzzzzzzzzzzzz{{{{||||}}~~~~~}|{zzyxxxxxyz{|~~~~~~~~~~}}}|||{{zzzzzzzzzz{{{|||}}}~~~~~~~~~~~~~~~~~~}}|{{zyyyxxxxxyyz{|}~~~}}||||{{{{{{zzzzzzzzzzzzzzz{{{{{{{{||||||}}}}~~~~~~~}||{{zzzzzzz{{|}~~~~}}||||{{{{{{{{{{{{{{{{{{{{||||||}}}}}~~~~~~}}||{{{zzz{{||}~~~}}}||||{{{{{{{{{{{{{{{|||||}}}~~~~~~~}}|{{zyyxxxxxyz{|}~~~~}}|||{{{{{{{{{{|||}}}~~~~~~}}|{{zzyyyxxxxyyyz{{|}~~~~}}}||||||||{{{{{{{{{{{{{{||||||||}}}}}}~~~~~~~~}}||{{zzzzzzzz{{|}~~~~~}}}}||||||||||||||||||||}}}}}}}~~~~~~~~~}}||{{{{zzz{{{||}~~~~}}}}|||||||||||||||}}}}}~~~~~~~~~~~~}}}}||{{zzyyyxxxyyzz{|~~~}}}|||||||||}}}~~~~~~}}||{{zzyyyyyyyyyzz{||}~~~~~}}}}}}}|||||||||||||||}}}}}}}}}~~~~~~~~~~}}||{{{zzzzzzzzz{{|}}~~~~~}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~}}}||{{{{zzzzzz{{{|}}~~~~~~}}}}}}}}}}}}~~~~~~~~~}}}}}}}}|||||{{{zzyyyyxxyyzz{|}~~~~}}}}}}}}}}}~~~~~~}}||{{{zzzyyyyyyyyyzz{{|}~~~~~~~~}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~}}|||{{{zzzzzzzzz{{{||}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{{zzzzzzzz{{{||}~~~~~~~~~~~~~~~}}}|||||||||||{{{{zzzyyyyyyyyzz{|}~~~~~~}}}~~~~~~~~}}}|||{{{zzzzyyyyyyzzz{{||}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}||||{{{{zzzzzzzzz{{{||}}~~~~~~~~~~~~~~~~~~~}}}}||||{{{{zzzzzzzzz{{{||}}~~~~~~}}||||{{{{{{{{{{{{{{zzzzyyyyyyyzz{{|}~~~~~~~~~~~~~~~~~~~~~}}}}|||{{{{zzzzzzzzzzzz{{{||}~~~~~~}}}}||||{{{{zzzzzzzzzzz{{{||}}~~~~~~~}}}}||||{{{{{zzzzzzzzzzzz{{{||}}~~~~~~~~~~~}}}||{{{{{zzzzz{{{{{{zzzzzzyyyyyyzz{{|}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}|||||{{{{zzzzzzzzzzzzz{{{||}}~~~~~~~~}}}}|||||{{{{zzzzzzzzzzz{{{{||}}~~~~~~~~~}}}}}||||{{{{{zzzzzzzzyzzzzzzz{{|||}}~~~~~~~~~~~~~~~}}|||{{{zzzzzzzzzzzzzzzzzzzzzyyyyzzzz{||}~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}||||||{{{{{{zzzzzzzzzzz{{{{||}}}~~~~~~~~}}}}}|||||{{{{{zzzzzzzzzzzz{{{{||}}}~~~~~~~~~~}}}}}}||||{{{{{{zzzzzzzzzzzzzzzz{{{|||}}~~~~~~~~~~~~~~~~~~~}}|||{{zzzzzzyzzzzzzzzzzzzzzzzzzzzzzzz{{|}}~~~~~~}}}}}}}}}}}}}}}||||||||||||||{{{{{{{{zzzzzzzzzzz{{{{|||}}~~~~~~~~~~}}}}}}|||||{{{{{{zzzzzzzzzzzzz{{{{|||}}~~~~~~~~~~~~}}}}}}}|||||{{{{{{zzzzzzzyyyyyyyyzzzzz{{{|||}}~~~~~~~~~~~~~~~~~~~~~}}}||{{{zzzzyyyyyyyzzzzzzzzzzzzzzzzzzzzz{{||}~~~~~}}}}}}}}|||||||||||||||||{{{{{{{{{{zzzzzzzzzzzz{{{{{|||}}}~~~~~~~~~~~}}}}}}}||||||{{{{{{zzzzzzzzzzzzzz{{{{|||}}~~~~~~~~~~~~}}}}}}}||||||{{{{{{zzzzzzzyyyyyyyyyyyyyzzzz{{{|||}}~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{zzzzyyyyyyyyyyzzzzzzzzzzzzzzzzzzz{{||}}~~~~~}}}}}||||||||{{{{{{{{{{{{{{{{zzzzzzzzzzzzzzzz{{{{{||||}}~~~~~~~~~~~~}}}}}}}||||||{{{{{{{zzzzzzzzzzzzzz{{{{{|||}}}~~~~~~~~~~~~}}}}}}}}|||||||{{{{{{zzzzzzzyyyyyyyyyyyyyzzzzz{{{|||}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}||{{{zzzyyyyyyyyyyyyzzzzzzzzzzzzzzzzz{{{{||}~~~~~~}}}}||||||{{{{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzz{{{{{|||}}}~~~~~~~~~~~}}}}}}}|||||||{{{{{{{zzzzzzzzzzzzzzz{{{{{|||}}}~~~~~~~~~~~~}}}}}}}}|||||||{{{{{{zzzzzzzyyyyyyyyyyyyyyyyyyzzzz{{{|||}}}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}|||{{zzzyyyyyyyyyyyyyzzzzzzzzzzzzzzzzz{{{{||}}~~~~~}}}}|||||{{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{||||}}}~~~~~~~~~~}}}}}}}||||||{{{{{{{{zzzzzzzzzzzzzzzz{{{{{||||}}~~~~~~~~~~~}}}}}}}}|||||{{{{{{zzzzzzzyyyyyyyyyyyyyyyyyyyyyyzzzz{{{|||}}}~~~~~~~~~~~~~~~~~~~~~~~~}}}}|||{{{zzzyyyyyyyyyyyyyzzzzzzzzzzzzzzzzz{{{{||}}~~~~~}}}}|||{{{{{{zzzzzzzzzzyyyyyyyyyyyzzzzzzzzz{{{{{||||}}}~~~~~~~~~~}}}}}}|||||||{{{{{{{{zzzzzzzzzzzzzzzzz{{{{{||||}}}~~~~~~~~~~~}}}}}}}||||||{{{{{{{zzzzzzzyyyyyyyyyyyyyyyyyyyyyyzzzz{{{|||}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}}|||{{{zzzyyyyyxxxyyyyyyzzzzzzzzzzzzzzz{{{{{|||}}~~~~}}}}|||{{{{{zzzzzzzyyyyyyyyyyyyyyyyyyyzzzzzz{{{{{|||}}}~~~~~~~~~~}}}}}}}|||||||{{{{{{{zzzzzzzzzzzzzzzzzz{{{{{||||}}}~~~~~~~~~~}}}}}}||||||{{{{{{{zzzzzzzyyyyyyyyyyxxxxxxyyyyyyyyyzzzz{{{|||}}}~~~~~~~~~~~~~~~~~~~~~}}}}|||{{{zzzyyyyyyxxxyyyyyyyzzzzzzzz{{z{{{{{{{{{||}}~~~~~}}}}|||{{{{zzzzzzyyyyyyyyyyyyyyyyyyyyzzzzzzz{{{{|||}}}~~~~~~~~~~}}}}}}|||||||{{{{{{{{zzzzzzzzzzzzzzzzzz{{{{{||||}}}}~~~~~~~~~~}}}}}}||||||{{{{{zzzzzzzyyyyyyyyyxxxxxxxxxxxyyyyyyyzzzz{{{{|||}}}~~~~~~~~~~~~~~~~~~~}}}}}|||{{{zzzyyyyyxxxxxyyyyyyzzzzzzzz{{{{{{{{{{{{|||}}~~~~~}}}}|||{{{{zzzzzyyyyyyyyyyyyyyyyyyyyyyyzzzzz{{{{|||}}}~~~~~~~~~}}}}}}||||||{{{{{{{{zzzzzzzzzzzzzzzzzzz{{{{{{||||}}}~~~~~~~~}}}}}}||||||{{{{{{zzzzzzzyyyyyyyyyxxxxxxxxxxxyyyyyyyzzzzz{{{|||}}}~~~~~~~~~~~~~~~~~~~}}}}}|||{{{zzzyyyyyxxxxxxyyyyyzzzzzzz{{{{{{{{{{{{{|||}}~~~~~~}}}|||{{{{{zzzzyyyyyyyxxxxxxxxxxyyyyyyyzzzzz{{{{|||}}}~~~~~~~~~}}}}}}||||||{{{{{{{{zzzzzzzzzzzzzzzzzzz{{{{{{||||}}}}~~~~~~~~}}}}}}|||||{{{{{{zzzzzzyyyyyyyyyxxxxxxxxxxxxxxyyyyyyyzzzz{{{{|||}}}~~~~~~~~~~~~~~~~}}}}}|||{{{zzzyyyyyxxxxxxyyyyyyzzzzzz{{{{{{{{{{{{{{|||}}~~~~~~}}}|||{{{{zzzzyyyyyyyxxxxxxxxxxyyyyyyyyzzzz{{{{|||}}}~~~~~~~~~}}}}}|||||||{{{{{{{zzzzzzzzzzzzzzzzzzzzz{{{{{||||}}}}~~~~~~~~~~}}}}}|||||{{{{{zzzzzzzyyyyyyyyxxxxxxxxxxxxxxxxyyyyyyzzzzz{{{|||}}}~~~~~~~~~~~~~~}}}}||||{{{zzzyyyyyxxxxxxyyyyyyzzzzzz{{{{{{{{{{{{{{|||}}~~~~~}}}}|||{{{{zzzzyyyyyyxxxxxxxxxxxxxyyyyyyzzzz{{{{|||}}}~~~~~~~~}}}}}||||||{{{{{{{{zzzzzzzzzzzzzzzzzzzzz{{{{{{||||}}}}~~~~~~~~}}}}}|||||{{{{{{zzzzzzyyyyyyyyxxxxxxxxxxxxxxxxyyyyyyzzzzz{{{||||}}~~~~~~~~~~~~~}}}}}|||{{{{zzzyyyyyyyyyyyyyyyyzzzzzzz{{{{{{{{{{{{||||}}}~~~~~~}}}||||{{{{{zzzzzyyyyyyyyyyyyyyyyyyyyyzzzzz{{{{||||}}}~~~~~~~~~}}}}}}||||||{{{{{{{{{zzzzzzzzzzzzzzzzz{{{{{{{|||||}}}}~~~~~~~~~}}}}}||||||{{{{{{{zzzzzzzzzzyyyyyyyyyyyyyyzzzzzzzz{{{{|||||}}}~~~~~~~~~~~}}}}}|||||{{{{{zzzzzzzzzzzzzzzzzz{{{{{{{{{{{{|||||||}}}}~~~~~~~~~}}}}}|||||{{{{{{zzzzzzzzzzzzzzzzzzzzzzz{{{{{{||||}}}}~~~~~~~~~~}}}}}}||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{|||||||}}}}~~~~~~~~~~~}}}}}}|||||||{{{{{{{{{{{{zzzzzzzzzzzzzz{{{{{{{{{|||||}}}}}~~~~~~~~~~~}}}}}}||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{|||||||||}}}}}}~~~~~~~~~~~}}}}}}|||||||{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||}}}}}~~~~~~~~~~~}}}}}}}}||||||||||||{{{{{{{{{{{{{{{|||||||||}}}}}}}~~~~~~~~~~~~}}}}}}}}|||||||||{{{{{{{{{{{{{{{{{{{{{{{||||||||}}}}}~~~~~~~~~~~~~}}}}}}}||||||||||{{{{{{{{{{{{{|||||||||||||}}}}}}}~~~~~~~~~~~~}}}}}}}}||||||||||||||{{{{{{{{{||||||||||||}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}||||||||||||||||||||||||||||||}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}|||||||||||||||||||||||||||||||}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}|||||||||||||||||||||||||||}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}||||||||||||||||||||||||||}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}|||||||||||||||||||}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}||||||||||||||||||||}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}|||||||||||||}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}||||||}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~zyxtqqommmlllllllllllmmmmnnpqqrstuvwxy{|~}|{yxwvuttssrrrssssssssssssssrrrrrstuwz~toppx~~|{yxwutsrrrqqqqrrstuvxy{|~~~}|{||{{{{z{{{{|}}zrrzztppqruvwy{|~~~}}|{{zyxwvusrqqponnmmmllllmmnnopqrtuvxy{}~{vsru|{wtrqppoppqrrrssssrrrrrrqqqpppponnnnnnnnnnnoopqrsttuwxyz{|}~~}yuqommosx~|xusqoonnoppqrsstttssrqqponnmmmmmnnoppqrsstuuvvvwwwwxxyyzz{|}~|yurommmosx}}zwusrpponnnnnnnnnnnnnnnnnnnnnmmmmmmmmnnnnoooppqrrstuvwxyz{|}~~{xurqpqtx|~{xusrpoonnmmmmmmmnnnnnnnnnnnooooopppqqqrrsstuvvwxyz{|}~|yvtrrrtw|~|zxwvutssrrrqqqqppppoooonnnnnnnnnnnooopqqrstuvxyz{|~}yuromllnpuz}ywtsqqppqqqrrssssrrqqpoonnmmmnnoopqrstuuvwwxxxyyyyzzz{{|}}~}zwtromlllnquy~~|ywutrqppoonnnnnnmmmmmmmmmnnnnnnnnnooooppqqrrsttuvwxyyz{|}~}zwusqppqsvy}|ywusrqponnnmmmmmmmmmnnnnnnooopppqqrrssttuvvwxyzz{|}~|ywusrrrtvz~}{zxwutssrqqpppooonnnnnnnmnnnnnnoopqqrstuvwxyz{|}~}zwtqnmllmpsw|~{ywutssssssssssssrqqpoonnnmmnnoopqrstuvwwxyyzzz{{{{|||}}~~|zwuspomllmnptw{~{ywvtsrqppoonnnnmmmmmmmmmmnnnnnnooopppqqrrstuuvwxxyz{|}}~}{xvtrqqqrsux|~|zxvutrqqpooonnnnnnnnnnoooopppqqqrrsstuuvvwxyzz{|}~~|zxvtssrstvy|~}{yxwvutssrqqpppoooonnnnnnoooppqqrsstuvwxyz{|}~~{yvsqonmmnpsvz~~|zyxwwwwvvvvvuttsrqqpooooooopqqrstuvwwxyyzzz{{{{{{|||}}~~~|zxvtrponmmnoqtvz}~}{yxwutssrqqqpppooooooooooooppppqqqrrssttuuvwwxyyz{||}~}{ywutrrqqrtux{~}|zywvutttsrrqqqqqqqqqqqqqrrrrssttuuvvwwxyyz{{|}~~~|zxwutssstuvy|}|{yxwvuutssrrqqqqqqqqqqqqrrsstuuvwxxyz{|}}~}{ywusqonnnnprux|~}||{{{zzzyyxwwvutssrrqqqrrrstuvwwxyzz{{||||||||}}}}}~~~|{ywutrqonnmmnoqsux{~~}{zyxwvvutttsssrrrrrrrrrrrrssssttuuuvvwwxxyyzz{{|}}~}{zxwutsrqqqrsuwz|}|{zyxxwvvuuutttttttttttuuuuvvwwwxxyyzz{{|}}~~}|zywvutsrrrstvxz}~|{zyxwvvuutttttttttttuuvvwwxyyz{{|}}~~~}|{zxwusqponmmnoqsvy}~~~}}||{zyxwwvuutttttuuvwwxyz{{|}}~~~~~~~~~~~~~~}{zxwutrqponmmnnoprtwy|~}|{zyxxwwvvuuuuuuutuuuuuuvvvvvwwwxxyyyzz{{||}}~~~}{zywvusrqqqqqrstvxz}~}|{zyyxxwwwwvvvvvvwwwwwwxxxyyyzz{{||}}~~~}|zyxwutssrrrrstuwy{}~}|{{zyxxwwwvvvvvvvvwwwxxyyzz{||}}~~}}}||{{zzyxvutrqponnmnopruwz~~}|{zzyxwwvvvvvwwxyyz{||}}~~~}|{yxvutrqponnnmnnoprsvxz}~}}|{zzyyxxxwwwwwwvwwwwwwwxxxxxyyyzzz{{|||}}~~~}|{zxwvutsrqqqqqrstvwy{~~}}|{{zzyyyyxxxxxxxxxxyyyyzzz{{{||}}}~~~}|{zyxvuttsrrrrrstuvxz|~~~}|{{zzyyyxxxxxyyyyzzz{{||}}~~~}}|{{{zzzyyxwwvutrqpponnooqrtwy|~}|{zzyxxxxxxxyzz{{|}}~~~}|{zywvutsrqppooooppqstvxz|~~}}|{{zzzyyyyyyyyyyyyyyyyyzzzzz{{{|||}}}~~~~}|{zyxwvutssrrqqrrsstvwy{}~~}|||{{{zzzzzzzzzzzz{{{{{|||}}}~~~~}|{zyxwvvutssrrrrsstuvxy{}~}}||{{{{{{{{{{{{|||}}~~~~~~}|{zzyxxxwwwwvvuutssrqppooopqrtvx{~~}||{zzzzzzz{{||}~~~~~~~~~~}|{zyxxwvutsrqqpppppqqrsuvxz|~~~}}}||||{{{{{{{{{{{{{{{|||||}}}}~~~~~~}||{zyxwwvutssrrrqqrrsstvwxz|~~~~}}}}|||||||||||||}}}}}~~~~~~~}}|{zzyxwvvutssrrrqrrrsstvwxz|}~~~~~~~~~~~~~~~~~~~~~~~}|{zyyxwwvvvuuuuutttssrqqppooppqrsuwy|~~}}||||||}}~~~~~~~~~~~~~~~~~~~}||{zyxxwvutssrqqppppppqqrstvwyz|~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~}}||{zzyxwwvuttsrrrqqqqqrrstuvxy{|~~~~~~~~~}~~~~~~~~~~~~~~}}|{{zzyxwwvuttsrrqqqqqqqqrstuvwxz|}~~~~}}}}}~~~~~}|{zyxwvuuutttttsssssrrrqqpppoppqqstvxz|~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~}}}}|||{{zyyxwwvuutssrqqqpppqqqrstuvwxz|}~~~~~~~~~~~~~~~~~}}||{{zzyxxwvvuttssrrqqqqqqrrstuvwxz{}~~~~}}||{{zzyxxwvvuttssrrqqqqqqqrrsttvwxy{|~~~~}}}}}}}}~~~~~~}|{zyxwvuttsssssssrssrrrrrqqpppppqrrtuwy{}~~~}}}}}|||||||||||||||{{{{{{zzzyyxxwwvuuttssrrrqqqqrrrsttuvxyz|}~~~~}}}||{{zzyyxwwvvuutsssrrrqqqrrrsstuvwxy{|~~~~~}}}}||{{{zzyyxxwvvuttssrrrqqqqqqqqqrsstuvwxz{}~~~}}}}}||}}}}}}~~~~~~}|{zzyxwvutssrrrrrrrrrrrrrrrrqqqqqqqrstuvxz|~~~}}}|||{{{{{{{zzzzzzzzzzzyyyyxxwwwvvuuttsssrrrrrrrrrsstuuvwyz{|~~~~~~~}}}}|||{{{zzyyxxwwvvuuttsssrrrrrrrrrrsstuvwxyz|}~~~~~~~~~}}}}||||{{zzzyyxxwwvvuuttssrrqqqqqqqqqqrrsstuvwxy{|}~~}}}}||||||||}}}}~~~~~~~}}||{zyxwvuutsrrqqqqqqqqrrrrrrrrqqqqqqqrssuvwy{}~~}}||{{{zzzyyyyyxxxxxxxwwwwwvvvuuutttsssrrrrrrrrrrssttuvwxyz{|~~~~~~~}}}}}|||{{{zzzyyxxwwvvuutttssrrrrqqqqqrrrsstuvwxyz{|}~~~~~~~~}}}}}||||{{{zzzyyyxxwwvvuuttssrrrqqqpppppppppqqrrstuvwxyz|}~~~}}}|||||||||||}}}}}}}|||{zyxxwvutsrrqqpppppppqqqqrrrrrrqqqqrrrstuvxz{}~}}||{{zzzyyyxxxxwwwwwwvvvvvuuuutttssssrrrrrrrrrrrssstuuvwxyz|}~~~~~~}}}}|||{{{zzzyyxxxwwvvuutttssrrrrqqqqqqrrrsstuuvwxyz{}~~~~~~~}}}}}||||{{{zzzyyyxxwwvvvuuttssrrqqqppppoooooppppqqrsstuvwxyz|}~~~}}}|||||||||||}}}}}|||{{zzyxwvutssrqqpppppppppqqqqrrrrrrrrrrrrstuvwyz|~~~}||{{zzyyxxxwwwvvvvuuuuuttttsssssrrrrrrrrrrrrrsssttuvwwxyz{}~~~~~~}}}}|||{{{zzzyyyxxwwwvvuuuttsssrrrrrqqqqrrrssstuuvwxyz{|}~~~~~~}}}}||||{{{{zzzyyxxxwwvvvuuttsssrrqqqppppppooppppqqqrsstuvwxyz{|}~~~}}||||||||||||||||||{{zzyyxwvutsrrqppppppppppqqqrrrrrrrrrrrrsstuvwxz{}~~}||{{zyyxxwwvvvuuuttttsssssrrrrrrqqqqqqqqrrrrssttuuvwxxyz{}~~~~~}}}}|||{{{zzzyyyxxwwwvvuuuttsssrrrrrrqqqrrrrssttuuvwxyz{|}~~~~~}}}}|||{{{{zzzyyyxxwwwvvuuuttsssrrqqqppppoooooooopppqqrrsttuvwxyz{|}~~~}}||||||||||||||||{{{zzyyxwvuttsrqqppoooooopppqqqrrrrrrrrrrssstuuvxyz|}~~}||{zzyyxwwvvuuutttsssssrrrrrqqqqqqqqqqqqqrrrssstuuvwwxyz{|}~~~~}}}}|||{{{zzyyyxxxwwvvvuutttsssrrrrrqqqqqrrrrssstuuvwwxyz{|}~~~~~}}}|||{{{zzzyyyxxwwwvvuuuttssrrrqqqpppooooooooooooopppqrrsttuvwxyz{|}~~~}}}}||||||||||||{{{{zzyyxwwvutsrrqppoooooooopppqqqrrrrrrrsssssttuvwxy{|~~~}||{zzyxxwwvuutttssrrrrqqqqqqpppppppppqqqqqrrssttuvvwxyz{|}~~~~}}}|||{{zzzyyxxxwwvvvuutttsssrrrrrqqqqqqqrrrrssttuvvwxyzz{|}~~~~~}}|||{{{zzyyyxxwwwvvuuuttsssrrqqqppppooooooooooooopppqqrsstuvwxyy{{}~~~}}||||||{|{{{{{{{{zzyyxwwvutssrqppooonnoooopppqqrrrrrsssssssttuuvwxz{}~~}}|{zzyxxwvvuutsssrrqqqpppppppooooppppppqqqrrsstuuvwxxyz{|}~~~}}}|||{{zzzyyxxwwwvvuuutttsssrrrrrqqqqqqqrrrrssttuuvwwxyz{|}~~~}}}|||{{zzzyyxxxwwvvuuuttsssrrqqqpppooooonnnnnnnooooppqqrrsttuvwxyz{|}~~~}}}}|||||||||{{{{zzzyxxwvvutsrrqppooooooooopppqqrrrrrsssssssttuuvwxyz|}~}}|{{zyxxwvvuutssrrrqqqpppppooooooopppppqqqrrssttuvvwxyz{|}~~~}}}||{{{zzyyyxxwwwvvuuutttsssrrrrrqqqqqqrrrrrssttuuvwwxyzz{|}~~~}}}||{{zzzyyxxwwwvvuutttssrrrqqqpppooooonnnnnnnnooooppqqrsstuvvwxyz{|}~~~~}}}}|||||||{{{{zzyyxxwvuttsrqqppooonnoooopppqqqrrrssssssstttuuvwxyz{|~~}}|{{zyxxwvvuttssrrqqpppoooooooooooooopppqqrrsstuuvwxxyz{|}~~~~}}||{{{zzyyxxxwwvvvuutttsssrrrrrqqqqqqqqrrrrssttuuvvwxxyz{|}}~~~}}|||{{zzyyxxxwwvvuutttssrrqqqpppooooonnnnnnnnnnnoooppqqrrsttuvwxyzz{|}~~~}}}}||||||{{{{{zzyyxxwvuutsrrqppooonnnnooooppqqrrrsssssssttttuvvwxyz|}~~~}||{zyyxwvvuttssrrqqpppoooooonooooooopppqqqrrsttuvvwxyz{{|}~~~}}}||{{zzyyxxwwwvvuuuttssssrrrrqqqqqqqqqqrrrsssttuuvwwxyzz{|}~~~}}||{{zzyyxxwwvvvuuttssrrrqqpppoooonnnnnnnnnnnnnnoooppqqrsstuuvwxyz{|}~~~~}}}}||||||{{{zzyyxxwvuutsrqqppoonnnnnnoooppqqqrrrssssssstttuuvwwxy{|}~}}|{{zyyxwvvuttsrrqqpppooonnnnnnnnnnooooppqqrrsttuvvwxyz{|}}~~~}}||{{zzyyxxxwwvvuuuttssssrrrrqqqqqqqqqqrrrrssttuuvvwxxyz{{|}~~~}}||{{zzyyxxwwvvuuttsssrrqqqpppooonnnnnnnnnnnnnnoooppqqrrsttuvwxxyz{|}~~~~}}}}}}||||{{zzyyxxwvuutsrrqppooonnnnooooppqqqrrrssssssstttuuvvwxyz{|~~~}}|{{zyyxwvvuttsrrqqppooonnnnnnnnnnnnoooppqqrrsttuvwwxyz{|}~~}}||{{zzyyyxxwwvvuuuttssssrrrrrqqqqqqqqqrrrrsssttuuvwwxyyz{|}}~~~}}||{{zzyyxxwwvvuuttssrrrqqpppoooonnnnnnnnnnnnnnooopppqrrsstuvvwxyz{||}~~~~~}}}}||||{{zzzyxxwvvutssrqppooonnnnnooopppqqrrrssssstttttuuuvwwxyz|}~~}}||{zzyxwwvuutssrqqpppooonnnnnnnnnnnooopppqqrsstuuvwxyyz{|}~~~}}||{{zzyyxxwwvvuuutttsssrrrrrqqqqqqqqrrrrrsssttuuvvwxxyz{{|}~~~}}||{{zzyyxxwwvvuutttssrrrqqqpppopooooooooooooppppqqrrssttuvvwxxyz{|}~~~~~}}}|||{{{zzyyxwwvvuttssrrqqqppppppppqqqrrrsssstttttuuuvvvwxxyz{|}~~~}}||{{zzyxxwwvvuuttsssrrrqqqqppppppppqqqqrrrssttuuvvwxxyz{{|}~~~}}||{{zzyyyxxwwwvvuuuttttssssssssrrrssssssttttuuvvwwxxyzz{{|}}~~~~}}||{{zzyyyxxwwvvvuuutttssssrrrrrrqqqqqqqrrrrrssstttuuvvwwxyyz{{|}~~~~~}}}|||{{{zzyyxxwwvvuuuttssssrrrrrrrrssssstttuuuuvvvvwwwxxyyz{{|}}~~~}}}||{{zzzyyxxwwvvvuuutttssssssrrrrrrsssssstttuuvvwwxxyyz{{|}}~~~}}}||{{zzzyyxxxwwwvvvvuuuuttttttttttttttuuuuuvvvwwwxxyyzz{{|}}~~~~}}}||{{{zzyyyxxxwwwvvvuuuuttttttssssssssstttttuuuuvvwwwxxyzz{{||}~~~~~}}}|||{{zzzyyxxxwwwvvvuuuuttttttttttuuuuuuvvvvvwwwxxxyyyzz{||}}~~~}}}|||{{zzzyyxxxwwwvvvvuuuuutttttttttttuuuuuvvvwwwxxyyzz{{||}~~~~~}}}||{{{zzzyyyyxxxwwwwvvvvvvvuuuuuuuuuvvvvvvwwwxxxyyyzz{{||}}~~~~}}}|||{{zzzyyyyxxxwwwwvvvvvuuuuuuuuuuuuuuvvvvvwwwxxxyyyzz{{||}}~~~~~}}}|||{{{zzzyyyxxxxwwwwvvvvvvvvvvvvvvvvvvwwwwwxxxxyyyzz{{{||}}~~~~~}}}|||{{{zzzyyyyxxxwwwwwvvvvvvvvvvvvvvvvvwwwwxxxxyyyzz{{|||}}~~~~~}}}|||{{{zzzzyyyyxxxxxwwwwwwwwwvvvvwwwwwwwwxxxxyyyzzz{{{||}}~~~~~}}}|||{{{zzzzyyyyxxxxxwwwwwwwwwwwwwwwwwwwwwxxxxxyyyzzz{{|||}}~~~~~~}}}|||{{{{zzzyyyyxxxxxwwwwwwwwwwwwwwwwwxxxxxxyyyyzzz{{{||}}}~~~~~}}}}|||{{{{zzzzyyyyxxxxxxxwwwwwwwwwwwwwxxxxxxyyyyzzz{{{||}}}~~~~~}}}}|||{{{{zzzzyyyyyyxxxxxxxxxxxxxxxxxxxxxyyyyyzzzz{{{|||}}~~~~~~~}}}||||{{{{zzzzyyyyyyxxxxxxxxxxxxxxxxxxxxxyyyyyzzz{{{|||}}}~~~~~~~}}}}|||{{{{zzzzzyyyyyxxxxxxxxxxxxxxxxxyyyyyyzzzzz{{{{|||}}~~~~~~~}}}}||||{{{{zzzzzyyyyyyyxxxxxxxxxxxxxyyyyyyyzzzz{{{{|||}}}~~~~~~~}}}}||||{{{{{zzzzzyyyyyyyyyyyyyyyyyyyyyyyyzzzzz{{{{|||}}}~~~~~~~}}}}||||{{{{{zzzzzzyyyyyyyyyyyyyyyyyyyyyyyzzzzz{{{{||||}}}~~~~~~~}}}}||||{{{{{zzzzzzzyyyyyyyyyyyyyyyyyyzzzzzzz{{{{||||}}}~~~~~~~}}}}}||||{{{{{{zzzzzzzyyyyyyyyyyyyyyyzzzzzzz{{{{{||||}}}}~~~~~~~~}}}}|||||{{{{{{zzzzzzzzzzyyyyyyyyzzzzzzzzz{{{{{||||}}}}~~~~~~~}}}}}|||||{{{{{{zzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{|||||}}}~~~~~~~~}}}}}||||||{{{{{{zzzzzzzzzzzzzzzzzzzzzzz{{{{{{|||||}}}}~~~~~~~~}}}}}||||||{{{{{{{{zzzzzzzzzzzzzzzzzzz{{{{{{{|||||}}}}~~~~~~~~~}}}}}}||||||{{{{{{{{zzzzzzzzzzzzzzzz{{{{{{{{|||||}}}}}~~~~~~~~~}}}}}}||||||{{{{{{{{{{{zzzzzzzzzz{{{{{{{{{{|||||}}}}}~~~~~~~~~}}}}}}||||||{{{{{{{{{{{{{{zzz{{{{{{{{{{{{||||||}}}}}~~~~~~~~~~}}}}}}||||||||{{{{{{{{{{{{{{{{{{{{{{{{{{|||||||}}}}}~~~~~~~~~}}}}}}||||||||{{{{{{{{{{{{{{{{{{{{{{{{|||||||}}}}}}~~~~~~~~~~~}}}}}}}||||||||{{{{{{{{{{{{{{{{{{{{{{||||||||}}}}}~~~~~~~~~~~}}}}}}}||||||||||{{{{{{{{{{{{{{{{{||||||||||}}}}}}~~~~~~~~~~~}}}}}}}}||||||||||{{{{{{{{{{{{{{{||||||||||}}}}}}~~~~~~~~~~~~}}}}}}}||||||||||||||{{{{{{{||||||||||||}}}}}}}~~~~~~~~~~~~~}}}}}}}||||||||||||||||||||||||||||||||}}}}}}}~~~~~~~~~~~~~}}}}}}}}||||||||||||||||||||||||||||||}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}||||||||||||||||||||||||||||}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}|||||||||||||||||||||||||}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}||||||||||||||||||||||}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}||||||||||||||||||||}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}|||||||||||||||}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}||||||||||||}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~trnihghkjjopnpspoqolllifeda`_]\[[[[\^`beiosx}~xrnkgfda`^]\[[[[[[\\\\]]^^`afls|fkq}yvrmjgdb`_^]]^_acfilptx|~{yvtrpnmllmllkllmopsvz~~i`qpdachouz~}|xusqnljgedba```aabdfhjmpsvz}zma\ctrg`]\^_adfiknoqrstuusrrrqoonmlkjihgffeeedeefghjknpsvy|~vmd^\_jwyqid_\ZZYZ[\^`bdeffffedca_^][[ZZ[\]_acehkmpsuwy{|~zrjc_^ait}unida^]\[[[[[[[[[[[[[[[ZZZZZZZZZ[[\]^_`acegikmoruwz|yrkfehnx~vojfca_^]]]]]]]]]]]^^^^^^^^___`abccefgikloqsuwz|~yrlhgjpy}xsomkihgfffeeddccbaa`__^^^^^^^_`abdfhjloqtvy|~yqha][^dnzyrlgdcbbcdegghhggedca`_^^__`acegijlmopqrrsttuvwyz|~ysle`\Z[`hr}ysmigeba`_^^]]]]]]]]]]^^^^^^__``abccefgijlnprtvx{}|unidbbeluxrmieb`^]\\[[[[[[[\\\]]^^__`abcdefhikmnprtvy{}~xqlhffjpy{vrnlihfedcba``__^]]]\\\\\\\]^_`bcegjloqtvy{~|tlfa^^bir}}wqmjhggggggggfedba_^]\\\]^_aceghjlmnoppqqrstuvwy{}xrmgc_^^afnw|wsoligedcbaa```__________```aabcdefghjkmoprtvxz|~~ysnifccfipxzuqmjhfdcbaaa````aaaabbccddefghjklmoqrtvwy{}}xsnjfedgkqy~zvspmkihgeddcbbaa`````aabbcdeghjlnpruwy{}|voic_\[\`gpz}xspmkjjjjjjiihfedba`___``bcegikmoqrsuuvwwxyyz|}~zupkfb_]\]aflt}|wsoljgedba``__^^^^^^^___``aabcdefghjkmnprsuwy{}~ytpkheeehlsy|wspmjhfecbbaa`````aabbbcdeffhijklnoqstvxz{}~yuqmjhghkou|{xurpnljigfeddcbbbbbbbbcdefghjlnoqsuxy{}|wqlgb_]]`dks|~zxutsrrqqponlkigfedccddfgiklnprstuvwwxxxyyzz{|~~zvrmiea^]\\^bgmu||xurpmljigffeeddddddddeeefgghijklmnpqrtuwxz{}~}yuqlifcbbdgkqx~zwsqnljigffeeddddeeefgghhjklmnoqrsuvxz{}~~zvrokhfefgjot{{xurpnljigfeddcccdddeffghjkmnprtuwy{|~{wrmida_^_aflt||zxwvutssrponlkigfeeeefgijlnpqstuwwxxyyyzz{{|}~{wsplheb`^^`bfkqw~|yvtsqonlkjihhgggggggggghhijjklmnopqrsuvwyz|}~|yuqnkhfddegkot{~{xvtrponmlkkjjjjjkkkllmnnopqrstvwxy{|}~{wtqmkhfeefhlpv||zwusqpomllkjjjjjjkllmnpqrtuwxz{}~}|ywsplhda^]]^aflsz~}|{zywvtrpnmlkjjjklnoqrtvwyz{{|||||}}~|yurnjgda_^]^_bfkpv||zwusrponmlkkjjjjjjjjkkllmmnopqrstuvxyz{|~~{wtqnjgfedefhlpuz|zwutrqonnmllllllllmmnnopqrrsuuwxyz|}~|yvspnkihgggikosx}}{xvtsqponmmlllllmmnopqrtuvwyz{|}~~}|{zxwurpmjfda`_`bejov}}|zxvtrponnmnnopqstvwyz{|||}}}}}}}}}}~~}{xurolifda`_^^`behmrw}}{yxvutsrqppoooooooopppqqrsstuvwxxyz{|}}{xuspmkhfecccdfhlpuz~|zywvutsrrqqqqqqrrrsttuvvwxyz{|}~}{xvspnlihfeeefhjnrw|}{ywvtsrqqpppppqqrstuvwxy{|}~}|zyxwvutsqoljgeb`__`beiou{}|zxvtrqppoppqrtuvxyz|}}~~~~~~~~~{yvsqnkifdcaaaacehkoty~}{zxwvutsrrqqqqqqqqqqrrsstuuvwxxyz{|}}~|zxusqnljhgeeeegikorw{~|{yxwvvutttttttttuuuvvwxyzz{|}}~~|zxvtromkihfeeefgilosx|}{zyxwvvuuuuvvwxyyz{|}~~~}{ywvutsrqponlkigeca`__acfjou{}{yxwvuuuuvwxy{|}~~~|zxvsqnligeca`__``bdgkosw|}}|zyyxwwvvvuuuuuvvvwwwxxyzz{{|}~~}{zxvsqomkigfedddegiknrvz}|{zzyxxwwwwwwwwxxxyyz{{||}~~~}{ywusqomljhgffffgikmptx}~|{zyyxxxxxxyyzz{||}~~~~~~~~~|zxvusrqqpoonmljhgecbaabcehlqw|~|zyxwwwwxxyz{|}~~~}}}}}}~~~~|{ywusqomkigedcbbbcdfhknrvz~~}||{zzyyyyxxxxxyyyyzzz{{||}}~~~}{zxwusqomljhgeedddefgilpsw{~~}||{{{{z{z{{{{{||}}}~~~}|zywvtrpnmkihfeddddefhjmpsw{~~}}}}}}~~~~~}}}~~}{xvtsqpnmmllkjihgfedbbaabcehlqv{}|{zyyyz{||}~~~}}}~~~~~|{zxvtrpnljigfdcbbbccegilorvz~~}||{{zzzzzzzzzzzz{{{|||}}}~~}|{yxwutrqomljihgfeeeffhjlnqtx|~~}}||||{{||||||}}}~~~~}|{zyxwutrqonlkihgfeeeeefhjloruy|~}}|||{{{||}~~}{ywusqonlkjjiiihhhgfedcbbbbcdgjmrw|~}}||}}~~~}}||{{{{{{{||||||||{{zzyxwvusrpomljhgfedccccdefhjmpswz~~~~}}}}}}}}}}~~~~~~}||{zxwvusrpomlkihgfeddddefgiknqtw{~~~~~~}}~~~~~~~}||{zyxvutrqonlkjhgfedddddefgilnqtw{~~~}||{{{{{{|}}~~}{zxvtrpnlkjihhhhhhggffeddccddfhknsw|~~~~~}}|{{zzzzyzyzzzzzzzyyyyxwwvutrqpomljihgfedddddefhjlorux{~~~~~~}~~~~~~~~~~~~~~~~~~~}}||{{zyxwvutsrqonmkjihgfeeddeefgikmorux|~~~}}|{{zyxvvtsrqpnmljihfeedccbbccdegikmpsvz}~}}|{{{{{{{||}}~~~}{zxvtrpnlkihggfffffffeeddcccccdfhknrv{~}}|{zzyyyxxxxwwwwwvvuuttsrqponmkjihgfeedddeefhikmpsvy|~~~~}}||{{zyyxwvutsrqonmljihgffeeeeefghjlnpsvy|~~~~~~~}}}||{{zzyxwwvutsrponmlkihgfeeddcccdefgijlorux{~}}|{zzyyyyzzz{{||||||{zywvtrpnljhgfeeeeeeeeeeeeeddddefhjlosw|~}|{zyyxwwvvvuuuutttsssrqqponmlkjihggfeedddeefgijlnqsvy|~~~~~~}}}||{{zzyxxwvutsrqponmljihgffeddddeefghjlnpsux{~~~~~~}}}}||{{{zyyxwwvutsrqpnmlkjihgfedccbbbbbcdefhjlnqsvy|~}}|{{zzzzzz{{{||||||{zywusqonljhgfedddddeeeeeeeeeeeefghjmptw{~}|{zyxwvvuttssrrqqqppoonmmlkkjihhgfffeeeffghiklnpsux{~~~~~}}}|||{{zzyxxwvvutsrqponmlkjihggfeeeeeeefghjkmoqtvy|~~~~}}}|||{{zzyyxxwvuutsrqponmlkjihgfeddccbbbbbccdegijmoqtwy|~}|{zzyyyyyyyzzzzzzyxxvusqomkihfedcbbbbccddeeeeeeeeefghjmpsw{~}{zyxwvuttsrrqqppoonnmmllkjjihhgffeeeeeeefghiklnpsuxz}~~~}}|||{{zzyxxwvuutsrqponmlkjihhgfeeedeeeffgijknortvy|~~~}}}|||{{zzyyxwwvuutsrqqoonmlkjihgfedccbbbaabbccdfgijmoqtvy|~~}|{zzyyyyyyyyyyyyyyxwvtsqomkjhfedcbbbbccddeeeeeeffffghjlnqtx|~}{zywvutsrqpponnmmllkkjjihhggfffeeeeeeeffghiklnprtvy|~~~}}||{{zyyxwwvuutsrqponmlkjjihgffeedddddefggijlnprtvy|~~~}}||{{zzyxxwvvutsrrqponmlkjihgfeddcbbaaaaaabbcdfgikmoqsvx{~~}|{{zyyyyxyyyyyyyxxwvusrpnljigedcbbabbbccddeeefffffgghjlnqtw{~}{zywvutsrqponnmllkkjjiihhgggfffeeeeeeffghijkmoqsuwy|~~~}}|{{zzyxwwvuttsrqponmllkjihggffeeeeeeefggijkmoqsuwz|~~}}||{zzyyxwwvuttsrqpponmlkjihgfeedcbbaaa```aabbcdfgikmoqsvx{}~}|{zzyyxxxxxyxxxxwvutsqpnljhgecbaaaaaabbcddeeeffffgghijloqtx{}|zyxvutrqponmlkjjihhggffeeedddddddddeefghijlmoqsuwz|~~}||{zzyxxwvutsrrqponmlkjjihggfeeedddeeefghijkmoqsuwy|~~~}||{zzyxxwvuutsrqpponmlkjihggfedccbbaaaaaaabbcdefhikmoqsvx{}~}|{zyyxxxxxxxxwwwvutsrpomkihfdcbaa`aaabbcdeefffgggghijkmorux{~}{zxwutrqponmlkjiihggffeeeddddddddddeefghijkmnprtvx{}~}}|{zzyxwvvutsrqpoonmlkjiihgffeeeddddeeefghiklmoqsuwz|~~}}|{zzyxwvvutsrqpponmlkjihgffedcbbaa````_```abbcefgikmnqsuwz|~}|{{zyyyyxxxxxwwvvusrqomljhfecbaa```aabbcdeefffgggghijkmorux{~}|{yxvusrponmkjiihgffeeddccccccccccddefghijkmoprtvx{}~}||{zyxxwvutsrrqponmlkkjihggffeeeeeeeeffghijklnprsvxz|~~}||{zyxxwvutsrrqponmlkjihggfedccbbaa```````aabcdeghjkmoqsvxz|~}|{zzyyxxxxwwwvvuttrqpnlkigedcaa`````aabcdeeffggghhiijlnprux{}|{yxvusrpomlkjiggfeddcbbbaaaaaaabbccdefghjkmoprtvy{}~}}|{zyxwvuttsrqponmlkkjihggffeeddddddeefghijkmnprsvwz|~~}|{{zyxwvutsrrqponmlkjihgfeedcbbaa```____``aabcdefhjkmoqsuwz|~~}|{{zzyyyxxxwwwvutsrpomkjhfecba`````aabcddeffgghhhiijklnprux{~~}|zywvtsqpnmljihgfeddccbbaaaaaaabbccdefghiklnoqsuwy{~~}|{zyxwwvutsrqponmlkjjihggffeeeddddeeeffghijlmoqrtvxz|~~}|{zyxwwvutsrqponmlkjihgfeedcbba```______``aabcdeghjlmoqsuwz|~}}|{zzyyxxxwwvvutsrqonljigedca``___``abccdeffgghhhiijkmnprux{~~|{yxvusrpomljihgfedcbbaa```````aabbcdefgijlmoqsuwy{}~}|{zyxwvutsrqpoonmlkjiihggffeeeddddeeffghijklmoqrtvxz|~~}|{zyxwvutsrqponmlkjihgfeedcbba```________`aabcdfghjlmoqsuwz|~}}|{zzyyyxxwwvuutrqpnmkihfdcba``___`aabcdeefgghhhiijklmoqsux{~~}|zywvtsqonlkjhgfedcbbaa````````aabbcdefhijlmoqsuwy{}~}|{zyxwvutsrqponmlkjiihggfeeeddddddeeefghijklnoqrtvxz|~~}|{zyxwvutsrqponmlkjihgfedccbaa``________``abbcefgijlmoqsuwy|~~}|{{zyyyxxwwvutsrqonljigedcba``_```abbcdeffgghhhiijklmoqsux{~~}|{yxvusrpnmkjigfedcbaa``______``aabcdefgijlmoqsuwy{}~}|{zyxvutsrqponmllkjihhgffeeeddddddeeffghijkmnpqstvxz|~~}|{zxwvutsrqponmkjiihgfedcbba``___^^^^^___`aabcdfgijlnoqsuxz|~~}||{{zyyxxwwvutsqpomkjhfecba``____`aabcdeefgghhiijjklmoqsux{~~}|{yxvusqpnmkjhgfedcba``_______``aabcdefgijlmoqsuwy{}~}|{zywvutsrqponmlkjjihggffeeddddddeeefgghijkmnpqstvxz|~~}|{zywvutsrqpomlkjihgfeedcbba``____^^^___``abcdefhiklnprtuxz|~~}||{zzyyxwvvusrqonljigfdcba`____``abcddefgghhhiijjklnoqsvx{~~}|zyxvusrpnmkjhgfecbba`___^^^^___`aabcdfghjkmoqsuwy{}~}|zyxwvutsqponmlkkjihggffeeddddddddeeffghijlmnpqsuvxz|~~}|{zxwvutrqponmlkjihgfedcbba``___^^^^^^__``abcdefhiklnprtvxz|~~}||{zzyyxwvutsrpomljigedcba`____``abcddefgghhiiijkkmnoqsvx{~~}{zywvtsqpnmkjhgfedcba``________``abcdefgijlmoqsuwy{}~|{zyxwutsrqponmlkkjiihggfffeeeeeeeeffghhijkmnoprsuwxz|~~}{zyxwvutrqponmlkjihggfeddcbbaaaaaaaaaabbcddefgijkmnprtuwy{}~}}|{zzyxwvutsrponlkjhgfeddcccbcccddeefgghhijjkklmnoprtuwy|~~}|{zyxvutsqponlkjihggfeeddcccccccdddefghijklmoprsuwxz|~~}|{zyxwvutsrqponnmlkkjiihhhgggggggghhhijjklmnoprstvwyz|~~}|zyxwvutsrqponnmlkjiihhgfffeeeddddeeeffgghijklmnpqrtuwyz|~~}|{{zyxwvvutrqponmlkjihhggggffgggghhiijjkllmmnopqrstvwyz|~~~}|{zyxwvutsrqponmlkjjihhgggfffffggghhijjklmnoqrsuvwyz|~~}|{zyxwvuttsrqpoonmmllkkjjiiiiiiiiijjkkllmnopqrstvwxz{|~~}|{zyxwvvutsrqpoonmllkkjjiihhhhhhhhhhiijjkklmnopqrstvwxz{}~~~}|{zzyxwvuttsrqponmllkkjjiiiiiijjjkkkllmmnoopqqrstvwxz{}~~~}|{zyxwwvutsrqpponmmllkkjjjiiiiiijjjkklmmnopqrstuwxy{|}~}|{zyyxwvuttsrqqpoonnmmlllkkklklllllmmnnoopqrstuvwxyz{}~~}|{zyyxwvuttsrqqpoonnmmlllkkkkkkkkkklllmmnoopqrstuvwxy{|}~}|{{zyxxwvuttsrqpponnmmlllllllllllmmmnnooppqrrstuvwxy{|}~~~}|{zzyxwvuutsrrqppoonnmmllllllllllmmmnnoppqrstuvwxyz{|}~}||{zyxxwvuutssrrqpppoonnnnnmmmmmnnnnooppqrrstuvwxyz{|}~~}}|{zyyxwvvuttsrrqqppoonnnnmmmmmmmmmnnnoopqqrsstuvwxyz{|~~}}|{zzyxwwvuutssrqqppooonnnnnnnnnnoooppqqrrsstuvvwxyz{|}~~}|{{zyyxwwvuutssrrqqppoooonnnnnnnooooppqqrsstuvwxxyz{|}~}}|{zzyxxwvvuttssrrqqqppppoooooooopppqqqrrsttuvwxxyz{|}~~~}|{{zyyxwwvuuttssrrqqppppooooooooooppqqqrrsttuvwwxyz{|}~~~}||{zyyxxwvvuutssrrrqqqpppppppppppqqqrrssstuuvwwxyz{|}~~}}|{{zyyxwwvvuttsssrrqqqqpppppppppqqqrrrsstuuvwxxyz{|}~~}}|{{zzyxxwwvuutttssrrrrqqqqqqqqqqqqrrrsttuuvvwxyyz{|}}~~}}|{{zyyxxwwvvuuttsssrrrrqqqqqqqqqrrrrssttuuvvwxxyz{||}~~~}}|{{zyyxxwwvvuuttsssrrrrrrqqrrrrrrssstttuuvvwxxyz{{|}~~}}||{zzyyxxwvvvuuttssssrrrrrrrrrrrrrsssttuuvwwxyyz{{|}~~~}||{{zyyxxwwwvvuuttttssssssrrrsssssstttuuvvwwxyyz{{|}~~~~}||{{zzyyxxwwvvuuutttssssssssssssssstttuuvvwwxxyzz{|}}~~}}||{{zzyyxxwwvvuuuttttsssssssssssttttuuuvvwxxyyz{{|}}~~~}||{{zzyyxxwwvvvvuuuttttttssssttttttuuuvvwwxxyyz{{|}}~~~}}||{{zzyyxxwwwvvvuuuutttttttttttttuuuvvvwwxxyyz{{||}~~~~}}||{{zzyyxxwwwvvvuuuuttttttttttttuuuuvvvwwxxyyz{{||}~~~~}}||{{zzyyxxwwwvvvvuuuuuuttttuuuuuuuvvvwwwxxyyzz{||}}~~~}}||{{zzzyyxxxwwwvvvvuuuuuuuuuuuuuuvvvvwwwxxyyzz{||}}~~~~}}||{{zzyyyxxwwwwvvvvuuuuuuuuuuuuvvvvwwwxxxyyzz{||}}~~~}}||{{zzzyyxxxwwwvvvvvvvuuuuuuvvvvvvwwwxxxyyzz{{||}}~~~~}}|||{{zzzyyxxxwwwwvvvvvvvvvvvvvvvvwwwwxxxyyzz{{||}}~~~~}}}||{{{zzyyyxxxwwwwvvvvvvvvvvvvvvvwwwwxxxyyzz{{{|}}~~~~}}|||{{zzzyyyxxxwwwwwvvvvvvvvvvvwwwwwxxxyyyzz{{||}}~~~~}}}||{{{zzzyyyxxxxwwwwwwvvvvvvwwwwwwwxxxyyyzz{{{||}}}{}|{z}{}y}|wyyw~zut{nwvozxnrvxkq{tnuvxwxyyxwklv}wknv|w~{||v~y{~}wy~o~stxvys|{q{ywyww|i~~rjm|zyjjuvho}ssnun{|wzr{y}xw~~xy}x|t}|ly|~xwptxx{zzts|ypnolxmnt|}rxsutmwxy~yuqqz~|~}{|w|r~ys|uwvqtwy}uzpxttkrxnhhm|lwqu|{x|z|~u~wx}~r~y||vzy~~x}zzvy|x}xxns||w{sw|q{qkv~zsmqrw~yznv}|}mnzkwtq}s}zxvtp|}zu}xvuv{p{|ttpwp|}fytor|~|sv{sq{ur|}t~|ztr{kq|x~x~~x|zqzy|nv{v~zq{v|j{rpgpuqdpqzdrnzvt{gwye|ztvkp|w~pmu|t{wjuqo{vrsyxrx{~wnrvr~mcum{vntzqqwq}wlxqwjqxyzrwoxwvx~mxz|sw}}v}zq}wy~m{xt}zwjigau~v~{~z`ikqxwuqtxw~ypuu}{|w~uv}~~ry|n|nuty|s~s}symwsurmouszr|~rqtzrvzrpvsgxwvmt~vmt|srszn}v~~jylvvs{ghwozvlxmkt~qzzsfqns{uw~yux|xvmmyvpxpsuy|rp~|rr|pnwu|g|ytilkxqyqnid}u{yz|osvs}|{{rkrtsrx~qks~}w}w|xtxz{xtjxv|vzyshjwuuumzz{xpuvpurx|tkxzw_mfZ|u~d{z{zo~wyvzqpu}x}gtp~tr}`vys{]pztpysztzqu{r{up{r|l|uuo~kxuprix^~z}vwvoyskty~l~jujnzoox}jl}ovx|nsyjmo~zujiqrwt`ukz{u}w}}~sv||yuxv{{ypt~uxpqw|xox|pskqwr|xmk~pj~|{z}~lytwkonvy~~vt{{zs{f~xxw}~lta|ostx|{obnict{qq|uukovt{\zny~umwz|~{putpx~gy~txrilxwru_}ziywhryy{~{pywydz|z~qw}vwroiorxpwrsw}uwpv{yrt|{||izsW~}x{i}{l|r{s~dyzujzu{phtlvr~iztmlzw{v~itiszr}vuupxrowsm}}s}iwzy{p~}tc~mz~ox|vzpeq{|jy[||xjurszpxwg}o}}yjuo~osltk}yjyu{fuvutvplvivunhq~t|uxsnxmw{ms{qzlzuvwkwzdvx~ld}byz{pyoxt~e}~okt{rsli|~~u||}coktvuqwc|etwt||{tomzgwz{l|ks}xufx{}vml~}yg{wqzlr}{zz{u~yuvs~r|we~oq{vytpzmmv|yfw|yjzgy|q}~zqrsoxksuxzww~~xx~xu~mgowvozdk{szx|z{pxqz}qt~x}kr}X}|~~mzr{w|t}stom~zgtlzyx~unz~rl~y}}q|wdu`|{|vn}y{o{xtrvm|s}wxzpyr}zpu{~w{t}wyttoosy}wsw|txvxw{thhsy{}w|s{yy}{m~y}xtvptfhluw~s|ovyc|wtxwsyqktus~zzuyq}}~xvtzj~n|pxrwvcvv~{u~yzyxvzvs}|fww{syw{c{u}qwxu~wl}}|t|ztx{}pzey{ly~lgzrrpqrwqzx|xnpx{{jrxgu}x~wqtzitr|pyywg{islxu|ozrtq|yxoyyvwxv|sy|u{{t|txys~sxttr~vrrwz~pvjl~x{s{you}ulkvyxtkw{yoyrfn|w|uz{~nwypkzz}||vt}rwiuvs}s{{}|tqo}}w}omvpw~n|~yz}{xpym}|qzvr{{}z|wyy{|}qy{z{swlnls{tqv~pz~||uu{k{|vjq{~zv|st{sz{}{zozi}np|l}|r|~nywzs{vuxzvzsywr}o~}|{to}{zy~|r{wtxvt}pzy{y~y~~vu|zqsy|vyt~uuu|ww~yx||x|~{{{|z}ssnvxt}zr|qiyp{xvsx{|p{|q|ursxi|v~tx~u~|}qv~wt{qvxwt~zr|}uu~xquyiwxzr|zym|znxvwtwy}~}w|uw{sqzsuz}~~i~qq~}}|xp}{w}}yy~{u}|uwu}pw{{wuuxp|{x{}y~zxvuy}{}{yur{zotuw~|uslssz}|sxy|vqq||~wvtzzy}p~~y|ww{|~w|svrpv{{}|wy~wn}zz}~}mhzp{s{y}}twvzxyxu{y{nz}z{{~u~o|}{uyrtzw~~}u~vx{zvvy||~xnu~ym||{vv~v~susz~|w~y~w~|y~wz}q~{~z{ly~i{{{u|z|~xvy{vzprupt~zzy~zxwt}yys|~{wvyrmvyvrtzyyot{zuy{xq~|}|vwtrt}~yyyyyy|y{wy~p|~q~|~|{}{}wt}}|w}|{jq|}zzvtyx~r~yt~uwxwwzz}z}wzs}{w~~~|uv~wy{pyx~xuypzzs|~{y{~tz|{{z|{q{v}xys||}ry~~~{z|~{x~uw}w|{y{|y~|szw}~}uzyzux~x~}u~pv}vr|}}|r||zvu|zt~{yxyt{}u|z|v||w{vo|wqqy}y~zuyy|{|}}}z~{|yyzvxvz{v}y~{~ys}v{z|~|r|~x}zy~y~txyp{q}~y|~{zyu|~~|uxzx|vxx{u{v|~z|~|~st}}{{{{st}tyw|y|uu~~}vvvyvx~}~|x~|}w}vpwz~~v}q{}w{}yw~}z~wu~|~ywxz}vt{{yz{|x~{w{w|~~zryrzt{vwvn|~~{yw~~u{~zw~{yvyv~wsyou{uvv}z{~|wx{}|{y}{{~t~x~x}xxt}stz|y|~yt|xzwz{~sy|~y~y}{}tx|~xt|uwwtz|}{}st}wzqz{|y||~zx|}v~}y{|~~vts|xw{}}v~~yywu}wz||~{xz}zxtzs}{}{z~|w}~~p~}sp}xu~~}z|x~|~{~~oz{{~{zz}u|}~y~{wwtyvzyxz}z{}x}y}yx~~w{z~vq~yjs{tyzw||y~~~|zz~}|u{~z~}w{{}ywwuzv|}z~rzx{}{zy|y~}r}u|~~|}~}zvwyx}|}}svv|y~p}|~syxvy}}~~u{}x}x}~t}y|vzv{~}szzr{~x}{~~zv~||}uyx~~u{w|z{z~t}|~v|y}z~~y~~|wv~~|~}~{~||}|t}~{~zz|y|y~|}vxw|{}|~yvz~y{}{{z|wv~w||yx}}yzxxyr{x{z~zy{v}~}|zzz}yw~x}}{{|y}v}}~{}vx}}{ty}~{~}|~~~}y}|{x}u}wy{z{~{~yv{wy{{{|{~}~~~x|~|~~|~~w{z{yy~~}}wy{}|~v{{y}~{{~yx|}{v}z}z}|}yz~{~{}s}}sxv~{}~}}}zv~~|~|x}x}}~~}}w|}s|~|w}vx|~}yw~}y|v|w~z~|z~~{zz~wq{w}|}w~}ywxz|~yw{wvx~}{wz|y{wu|~{x}yy~}}{v{}wv~w~{}||}}~}~yx~{~{}~{y~xy{~~}|z|zxz{zz{s|zyx{{|w{}}~|wwyy{|{w{~}uwzw~{|zzv|}}w{xwu}~yw{v}yvw~w{wu~wxyz{|zzws}|q}{uz|~z}}{|||xz~u~}wz}z}w~z|xyz~~{{~|{x{}zwz{s|}z}}vz}}}x|}}x~xv}v~x~zvyy~wwu{u}~vu~z{{x~~~~z~u|{~wx|r}z||v|wy~~y}z{{}~{z}xzy|q~yuuywx~|wzz~||zzuz~}zzwzwywy~}w~zxw|}~||}x|}tu}{z{~{s~z~~|}}{}y}|wz~z}{ux~xuvv}~~xq{x~|z~x}wvp~w{~}yyy~{vwxyzz}~||{w|x{y|~~|ztyzn}x~yyz~}~y{|}|y{u{}zz}p~{{~zw~|y}yz|~||}}zuyxx~~{yr||z}~z|}||~x{}{yu||z}|zzyx|u{v{~}~pzzux}~xx{w~{|}~~yt|y|zzy~zz~~xxyt~~v|v}~x{x}vz|wxv}~}{~y~w}x{}ow}}{}}yxy}~v{~z}w}~{|}xt~z{|}~s|{x~~ruu{~}x{v||z{||}z~~r{z{x{}~yxvy~xzrwz~|{}|}}}~~~w~}}||z{||{|~vr~}y|||vwy{}y~~|~{}~~|y{ty|z{~|{}}zz~yp}|}|y{||zz}wvvwt}uyz}}}~yyx|y|xuxzxy{zv||y}}~zzx~}|t|v{~~~z}o{|}|t~|zy{~|~~zwzv}w|}v{z{{w{vs{zq|w{~~|}y~{yzx~x~w{xzuywv|}zw|}y|ysz~xw~uyx}|w}}}}u|~|uz{w{{}zx~ut~}}{ywyyw~x|}~r|x}}{}|t|{z}~~~y|~~z~{{}vv~|}x}xy}vz~|}}|uzx}~|~}{}|z~~x}yx{|x}x~z|~}~u{|x}~{~x~v{}~}~}~|zxyrz}}x{v~v{|ty~~~}~sx}}|y{~y}yyv|zwzq~{y|{}w|{|{}y~y{z~|x|z}vyv~}|tu|w}|{|}|}|~{{~w~x|x|y}zxs}~xzw~~}|}{{{|~~v{|{~|v~z}}z|z|t~|}|xyzz~{|zw~}zx~|~{y{wz}}ztz~}z~zyt{|}~{y{w|t~|}~z||y~z}}{w|}|}}t||~z||}{~w}v~|xz|~z~|{z{w~zz|zy~~}wzxy~}}{}{}v~|}|}~}|~~ww{{y|~z}~x}z~yz|~|vt{~}}|{~}~}~~xu~x~}}}{~v|z}|{|{~}~}w}{~|wzy~z}}~xz||y}{t}z{z}~{x|z{z~|y~~|~}|wyz~~|}xy}~z~zx{wz}x}v~~~|}~{~z~}}z{||}}|{}|~zzy{||z|~~{t|~{|vyz~~z}}}~|}|wxzy}{~z}~~|r}~v|~~}v~{~|w}|{~z}~~xy}{|{|{|{x~t}|w}xy~}x{xz~}{{zzwy|~z~|}y}~yyz|{{|z{y}~y}zzy~|{w|}wz}{~~}}{y|~~~||u~~|}z~y~||}}{y||yx{zv}~x|}~z|}~{|{yw~y|~~{{{}|{|~~~|||z{|}}}y}~zy}~x}z}xz}|}{}~{|z{}~~}~v|{}|{||v~z}|vz~~{||w{{~~}||~z}}{~~z}z~}|}}t}x~x}|z{}|yu~}}{~|}~}}z|y}{}}~y~x|yz~|xt}~x{}{~y~~y}~|~~}~yz~|{v~|}{}}~zxyz}y{}{|||{zz{}~y{{s||zz}|~~}~}}zz|~z~|z}~~~}z|~~}~{}zy}{{{{~y~|v}y{z||{{|{}~}}~}}y|}{{|~z~}~~z|}~~yy{~~z~}{}x}}{|~~|{z}|||~}z}~xy~}}}~{~z}|y{~}~zw~y}y|y~}}yv{}y}|}||~w~{}}~}{~}}||}~}~z~~~~y|}x~|{|x~|ux}~{||~~|}{x~y}w}{{|y{x~|}|||}}~{t|~~~~}~}z~|{}z}~z{y~}{{{{}}~~~}~}y}}{}{x|yy~~}y}~}}x||z~{~|{~}|~}~}~|{~{|}}{|wz}z}z{~|}|||~||~~~|~|~zy|zy{~}y{}~|}~{}}{y|||~~{}z~z~{|}{}}||~{|~|~~|||~~{}~~y{|~|}~{}wz}yz{~|~~{~}|~}y{|{~~}{|||}~{z|}{}~|{~|y~}~~~|~}z~z{~}{zz{}|}y|~~{z|}||~|~~}}{z{}|~}z|wy~{|{~|y{~|{~}|~~|||~}}z{~~}}|x|}z~}y{~z{z~}~~~~}}~|}|~}~|~{}y~~}wx}~~y~|}z}z|}{|{{||z~z}~}xx~~~~{}~{w~~~~zy}}}}|{|}~}~{~zwzy}~~|{~~{~~xyw}}{|~}|z}|}}||z|||}|~|{}|{|z}~}}{~~|}}|}~{y{{~|~zz~}|{}|x|~}x|}{|~~~|y}||~}}~~}}{~zz~{}}{}~{{|}~~}~{{v}~~~|{~{|{y}y~||}z|}{}y}x||}~z}~x}}~~|}~}||~~~z|x}}y}~~}{{~~~~}||}|~|y|z{z}}}~{y|~~~|zx}}zx~}}~y|~|~}{x}y{~{|}~~|{}|~}}~}}}~{{~}}x}{z}~~~zzz|{|}}~z{~~~}||~~y}y~~}|}z{~}{~wzy{xz}|~}{zzz~~}zx~z}~~~}}}z~|~~~|||~~~|w~|}|z}~~~{~}|}}||}}|{~{|~~{~~}~|xzy~z~||{~~{||{~~|}}y{~{~z~~~~~~~{|~}|u~~~~z{{}~z{{z|~{~~{y|}~~~~}w|~}~z||~~{w}|}~~~y{~{}|~|x}}|~~{~~z}~|~~~}|{~z~~z|}}|}{}}{~}y~~~{}|{}{~}~z{{}|}z|{~~}|}|}}{|y|~{{}{~}~~~~}~{|~~~}|~}{{}|}|}~|zv|~}~~wz}|{|||~}~}|~}|~~~}}~|~{{|{{wz{|||~{~||~{||}z}~z~}}x|~|}}|||~~}~|}~}}||~}|~~|}}}}|}~~z}|z|~|~{}}~}}|~||}||}z}y~zxyz~}~{|}~z}{}~~{{}z|yz~wzzx}{~}~|}}|~~~}~{{}~|}~~~|yy}z{|{~{}~~~}}}~{~~{|y}~~}}|~~}}}|~~||~~|}{||}w{x|}~~~}}~}~{}|~~{}{~~~}}|z||}}zw|{}~{~}|~~|~|~~|~~}|}|{~~~~}~{|}~~}yy}}|~}|~~~|~}}{~~~~~|~z}{~~}yy~|z}|x{~w~x}}}}~~}|}y{{~~}|~~{~{~{~~~~|{zx|~{}~|}~~|~{}{~}{~{~~{}y}|~~~}zz~~}|~}}|{yz}|~}~~~}y|{~|~~{|~}~}}z~}}y}y}}}~z~|~~{~}|~}~|~|}{|~{{~~y}{}|}|}|{~|}|z||~~~|{}}~~}}|||y~|~~~~}~{||~{|y~}|z|~}|~||~}~|{y~~z|{~}{}||y{}~||~}~~}~~|{{|}}~y}~~|~|||z~}}}||}|~~~y}{}{~~~{~~|~}}~|}y}y}}~~|z|||~y~{~~~~~}{}~~~}}}}{||}~~{~{|~~~y~{~{y~z~|}||~}}}{}|~~z}~|{}~}|~|{~z~|||{|~}}|w{}|}|}z|~~}~}{y~z~|y}{~~||~z}~~~~}~|}~~|~{~}|~~~}{|}~~{~~~|{}~~~~~}||~~|~~}|z~{}}~}~}~~~}}|{~z|~}~|}z}}}{zz}~}~{}}}}~~}{z~}|}}~~~~~|z}|}~{{~|||||~}|~}}~{~|~|~}~~~~z~~~~}{}|~{|||~}}}~~}|}|x||~~|~}~~}~}{~|{{~}~{{}|||~~|~}|}}}}~}~~}}}}}{~z~|}~~z{|~~~}{{{z~~}|}|~||}{{~~y}||zzy}{~~~}|~~}|~~~{}~}~~~{}}~}z|~{{~}~}~}~}|~}{|~}}}}}~~~~~~z|}~|~}~|z~}~~~{{y{||~~}}~~}~}~~}}~z}}|~{||~~}y|{z|~}~~~{~}~}}}}~~}~~~{}{}|{}~}~~}}}~~}~{~~~~}|~}~~~}}}~~}~~~~~}||~}~~~~}~zz|~~|~}{}}||}~~y~{|~~~}}}~z|{~}}xzy{}~~{~|~~~|}|~~~}{{~~~|}}{~}|}}}}}{|{}}}}~}~~~|}~~~}|{~}}}~}|||{|y|z}~}}}||~}}}~}~~~z}}|}~{z}|~~~{~~|~|}}}~~~~~{~~|~~{}}}~~~~}~}{~~}}~~{}z~}}~~z~}{~~{z}}~~~z|~}}z}}}~~}~~~~~~~~}}}~~~}{}{}~{z|z}~}~~}~~~}~}|~|~~}|~~|}~{}|}}}~}}x|}~~~~}~|{z|~~}}~zz~~}~}}{~}~}|}{~|}|~}|~~}~~}}{~}}}~~~~}}||}}}}~}||~|}~~~~~~}}}}}~}}~~}~~~|}~z}}~|}|~}}|~|~{}}~}~~{}|~|}}~}}|}~~~~|~~|}|}~}~~~}}~}~}}|}~~~{~~~}}}|{~~z~{~~|z}~~~~}~~~}}~}|}{~|}|~~~~}~|~}~~}}~}~}}|}}~}}|}}~{~~}}~~{}}~}~|~}||}}~~|{z~~}~~z~}~}~}{{}|~~}~~||||~}|~~~~}|~{|}}~}~|~}|~~}{}~}~}~}|~~~~{~}~}}|}~}}}~}|~}~~~~}}~}}~}~~~}}|~}|}~~~~}{~|}}|}}~}}~~}~~~}|~}~~~~~~}~~~~~~|}~}}}}}~{}~}}}}}}||~~~~~~~|~|}~~}}~~}~~}}~~}{}}|}|{~~}}}~~|{~~|~}}~~~}~~}~}}}|~}}{~~~|}|~}}~~{}~|~~~}{{~~~~}}~}|}|}}|}}}{{{~}~}~~~~~}~|}~~~|}~}~}~~~~~|~~}~~~~|~||}~~~~~~~~~~~~~~~}~~~}~|}}}~}~z}}||~~}~}~~~~~~}}~}|~}|}}{~}~}~|||~~~}~~|~|}}~~z~~}}}~}~~~}~~~}~~~{~~}~}}|}~~~~~~|{|~}~}}~~~~}~}~~}~||~{}}{{{~~~~~~~~|}}~~~~|~~~~~~}}||}{|~~~~}~~~~}~~~}}}~~~}}|}~~~~~~~~|}~~}}}~~}}~}~~~}}}~~~~}~~~|}~}~~~}~}|~}~|~~~}}~~}}~}~|}~~|~}}~~~}~~|}~~|}}}~~~~}}{~~~~}~~~|~~~~~~~|~}~~}}|}}~~}|~}~~|~~~~~~~~~}}}}~|z}~}}~~~|~~~~}~~~~}}}{}~}}}~~~}~}}}~~~~~}}}}}~~~~}~}}}~|~}~~~~}~~~~~}~}|~|}~~~}~}}~~~}}}~~~|~~~~}~~|}~~~~~~~~~|~}~~~~~~}~~~}~}|~~|~}|}~}}~|}~~~~~~}~~~}|}~}}~|~~}~}~~|}~~~~~~~~~}}~~~~~~~}~~~}|}}}~~~~}}}}~~}}}~~~~~}~~{}~~}~}}}}}~~~~~}~~}~}~~|~~~~~~~~~~}~~~}~~~~}~}}~}|~~~~~~}}~~|~~~~~~~~~~~}~~|}~~~~~~}}~~~~~}~~~~~~~~~~~~}~~~~~|}}~}~~~~}~~~}~~~~~}~~|~~~~}~~}~~}~~~~}}}~~~|}~~}~~~~~~~}~|}~~}~~}~}~}~}~}~~}~~}~~}}|}}~~}}}~~~~}~~}}~~~~~}~~~}~~~|~~~~}}~~~~~|~~~}~~}~~~~}~~}~}~~~~~}~~~~}}~~~~}}~~~}~}~~}}~}~~~}~}~~~~~~~~~|~~}~}~~~~}|~~~~|~~~~~}}}~~~~~|~~~~~}~~}~~~~~~~~~~~~}~~~}~~~}~~}~~~~~~~~~}}}|~}~~~~~}~~~~~~~~~}~~~~~~~}}}~~}~~~}~~~~~~~}~~~}~~~~~~}~~~}~~~~~|~~~~~~~~~~~~~~|}~~~}~~~}}~~}~~~~~~~}~}}~}~~|}~~~~~}}~}~~}~~~~}~}~~~~~~~~~}~~~~~}~}}~~~~~}~~~~~~}~~}~~~}~~~~~}~~}~~}~~}~~~~~~~~~~~}~~~}}~~~~~~~~~~~~~}}~~~}}~~}~~~~~~~}~}|~~~~~~~~~~~~~~~}~}}}~~~~~~~~~~~~~~~~~~~~~}}~~~~~~~~~~~~~~~~~~}}~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~}~~}~~~~~~~~~}~~}~~~}~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~}~~~~~~~~~~~~}~~}~~~~~~~~~~~~~~}~~}~~~~~~~~~~~~~~~~~~~}~~~~~~~}~~~~}~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~~~}}~~~~~~~~~~~~~~}~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}~~~~~}~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m54.bin b/applications/SprPoP/dist/MUS/m54.bin deleted file mode 100644 index cad125b..0000000 --- a/applications/SprPoP/dist/MUS/m54.bin +++ /dev/null @@ -1 +0,0 @@ -~~~}}}}}}||}||||{zzyxxwxz}~~~~}|{zzyyyyyz{{|}~~~}|{zyyxwvvuuuuuvy{}~w~}rv|~}|{zyyxyyzz{}~{x}~}}~}||{{{{{{||}~~ztszzusrstuwxyz{{|||||{zzzzzz{{{||}~~~~~~~~|yxy~~~~~~zvssv}{xvtsrrrrrrssssttttuuuvvwwxyyz{{|}}}~~~}{zxwx{~|yvtssux}|zxwvutssssssssssttuuvvwwxyyzz{||}}}~~~~}{zxxwx{~~|yvtsstx}~{yxwuuttsssssssstttuuvwwxxyzz{{||}}~~~~~}|{yxwwx{}~{xvtsstw|}{yxwvuutttsssstttuuuvvwxxyyz{{||}}}~~~~}|{yxwwxz}}zxutsstw{}{yxwvuutttttttttuuuvvwwxyyzz{{||}}~~~~~~}|{yxwwxz}|zwutsstw{~|zyxwvuuttttttttuuuvvwwxyyzz{{||}}}~~~~}|{zyxwwxz|~|zwutsstwz}|{yxwwvuuuuuuuuuuvvwwxxyyzz{{|||}}~~~~~~}|{zyxwwxz|}{ywussstwz~}|zyxwwvvuuuuuuuvvvwwxxyyzz{{{||}}}~~~~~~}|{zyxwwxy|~~|zxvussstvy}~}{zyxwwvvvuuuuvvvvwwxxyyzz{{{||}}}~~~~~~~}}|{zyxwwxy{~~|zxvussstvy}~}|{zyxxwwvvvvvvwwwxxxyyzz{{{||}}}~~~~~~~~}||{zyxwwxy{~~}{yxvtssstvy|~}{zzyxxwwwwwwwwwwxxxyyzz{{{||}}}}~~~~~~~~}}|{{zyxwwxy{}~|{ywvtssstvy|~|{zzyxxxwwwwwwwxxxyyyzz{{{|||}}}~~~~~~~~~~~~}}|{zyyxwwxy{}~|zywvtssstvx{~}|{zzyyxxxxxxxxxyyyzzz{{{|||}}}~~~~~~~~~~~}}||{zyyxwwxy{}~}{zxwutssstvx{~~}|{zzyyyxxxxxyyyyzzzz{{{|||}}}~~~~~~~~~~~}}||{zyxxwwxyz|~|{yxvutssstux{~~}||{zzzyyyyyyyyyyzzz{{{||||}}}}~~~~~~~~~}}}|{{zyxxwwxyz|~~}|{yxvutssstuwz}~}||{{zzzzzzzzzzzz{{{{|||}}}}}~~~~~~~~~}}||{{zyxxwwxyz|~}|{zxwvutssstuwz}~}||{{{zzzzzzzzz{{{{||||}}}}}~~~~~~~~}}}||{{zyxxwwxyz|~~}|{yxwvutssstuwz}~~}||{{{{zzzzzz{{{{{||||}}}}}~~~~~~~}}}}||{zzyxxwwxyz{}~}|zyxwvttssstuwy|~~}||||{{{{{{{{{|||||}}}}}}~~~~~~~}}}||{{zzyxxxwxyz{}~}|{zywvutsssstuwy|~~}}||||{{{{{||||||}}}}}}}~~~~~~}}}}||{{zzyxxxxxyz{}~}}|zyxwvutsssstuwy{~~~}}||||||||||||||}}}}}}~~~~~~~}}}}||{{zyyxxxxxyz{}~}|{zyxwvutsssstuwy{~~~}}}}}|||||}}}}}}}}}~~~~~~~}}}}}||{{zyyxxxxxyz{|~~}|{zzyxwvutsssstuvy{~~~~}}}}}}}}}}}}}}}}~~~~~~~~}}}}|||{{zyyxxxxxxy{|~~}}|{zyxwvuutsssstuvx{}~~~~}}}}}}}}}}}}}}~~~~~~~~}}}}|||{zzyyxxxxxyy{|~~~}||{zyxwvuutsssstuvxz}~~~~~~~~~~~~~~~~~~~~~~~}}}}||{{zzyyxxxxxxyz|~~}||{zyxxwvuttsssstuvxz|~~~~~~~~~~~~~~~~~~~~~}}}}||{{zzyyxxxxxxyz|}~~}}|{{zyxwwvuttsssstuvxz|~~~~~~~~~~~~~~~~~~~~}}}}||{{zzyyxxxxxyyz|}~~~}||{zzyxwvvuttsssstuvxz|~~~~~~~~~~~~~~~~}}}}||{{zzyyxxxxxyyz{}~~~~}}|{{zyxxwvuutssssstuvxy|~~~~~~~~~~~~~}}}}||{{zzyyxxxxxyyz{}~~~}}}||{zzyxwwvuutssssstuvwy{}~~~~~~~~}}}}||{{zzyyxxxxxyyz{}~~~~}}||{{zyyxwwvuttssssstuvwy{}~~~~~~}}}}||{{zzyyyxxxxyyz{|~~~~}}}||{zzyxxwvvuttssssstuvwy{}~~~~~}}}}||{{zzyyyxxxxyyz{|~~~~~}}}||{{zzyyxwwvuuttssssstuvwy{}~~~~}}}}||{{zzyyyxxxxyyz{|}~~~~~}}}||{{zzyyxwwvuuttsssssttvwxz|~~~~~}}}||{{zzyyyxxxxyyz{|}~~~~~}}}|||{{zzyxxwvvuuttssssstuvwxz|~~~~~}}}||{{zzzyyxxxxyyz{|}~~~~~}}}}||{{{zzyyxwwvvutttssssstuvwxz|~~~~~}}}||{{zzzyyyxxxyyz{|}~~~~~~}}}|||{{zzyyxxwwvvutttsssssttuwxz|}~~~}}}||{{{zzyyyxxyyyz{|}~~~~~}}}|||{{{zzyyxwwvvuuttsssssstuuwxz{}~~~}}}||{{{zzyyyyxyyyz{|}~~~~~}}}|||{{{zzyyxxwwvvuuttssssssttuwxy{}~~~}}|||{{zzyyyyyyyyz{|}~~}}|{{yyxwvutsrrqqqqrsuvwxxxxyz|~|{{y|~}}||||||}}}~~~~~~~~~~~}|{{zzzzzzzzzzzzyyxwwvuuuuvwxyywros{unqswy|~~~}}|{zzyyyyyyyyzz{|}~~|{|||}}}}}}}}}}|||||||||{{zzyyxxwvvvuutsrqppruxzyyyyz|}|z|{zxxxxxxxxxxxxwwwwwwwxxyyzz{|}~~}|zxurommotx|}}{yxwwxyz|}~~~~~~~~~~~~~~}}~|{zyxxxxxxxxxxxxxxxxxxxxxwwwwvvuttsrqqqrtwy{|||}}~}{{|}{zyxwwvvvuuuuuvvvwwxxyz{|}~~~}|{ywurpnnorvz}~}|{{{{|}~~~~~~~~~~}}}~}{zyyxxxxxwwxxxxxxxxxxxwwwwwvvuuttsrqqrsvxz|}}~~~|{{}~|{zyxwwvvvvvvvvvwwxxyzz{|}~~}|{yxvsqpooqtx|~~~}}}}~~~~~}||}~|{zyyxxxxwwwwwwxxxxxwwwwwwvvvuuttsrrrsuwy|}~~|{{{}~|{zyxwwwvvvvwwwwxxyzz{|}}~~~}|{zxvtrqpoprvy}~~~}|{|}}|{zyyxxxxxxxxxxxxxxxwwwwwvvvuuttssssuvx{}~}|{{|~}|{zyxxwwwwwwwxxyyzz{||}~~~~~~~}|{zywutrqpprtw{~~~~~}|{{|}~}|{zyyyxxxxxxxxxxxxwwwwwwwvvuuuttsttvwz|~~|{{{|~~}|{zyyxxxxxxxyyyzz{{|}}~~~~~~~~~}|{zyxvusrqqrsvy|~~~~~~}|||}~~}}|{{zzzyyxxwwvvvuttssrsstuvxyz{|}~~vv~uv|~}||||}}}}}}}||{{{zzzz{{|}}z}~~~}|||}}}~~~~~~~~}}||{{{{{{{{{wqmsyrnrw|~|{}~~zvv||yz|~}|{{zzyyyyyyyyxxxxwwwwwwvvutsqrtwz{{{|}~}xvx|~}}~~}||{zyyxxxxxxxxyyyzzz{{||||||{{zzz|~|xtqnnosx{}}|||||}}~~~~}}}}}~~~~~}|{{}~~{xvvx{~~}|{zyyxwwwwwwwwwwwwwwwwwwwwvvuutsrstvxz|}~~}zxwxz~~}|{zyxxwwwwwwxxxyyyzz{{|||||||{zzz{}}zwtronoruy|~~~~~}}}}~~~~~}||{|}~|ywvvx|~}|{zzyxxxwwwwwwwwwwwwwwwwwwvvuutsstuwy|}~~{ywwy{~}|{zzyyxxxxxxxyyyzzz{{|||||||{{zzz|}~|zxusqooqsw{~~~~~~~~~~~}|{||~~|ywvvwy|~}|{zzyyxxxxxxxxxxxxxxxxxwwwvuutttuvx{}~~|yxwxz|~~}|{zzzyyyyyyyyzzz{{{|||||||{{{zz{|~~|{xvtrpppruy|~~}||||}~~|zxwvvxz}~}|{{zzyyyyxxxxxxxxxxxxxxwwwvvuutuvxz|~~|zxwwxz}~}||{{zzzzzzzzzz{{{{||||||||{{zzz{}~~}{ywusrqqrtwz}~~}}||||}~|{ywvvwx{~~}|{{zzzyyyyyyyyyyxxxxxxxwwvvuuuvwy{}~}{yxwxy{~~}||{{{{{zz{{{{{{|||||||||{{{zz{|}~|{yxvtsrqrsux{~~~~~~~}{yxwwx{~~}}}||||}~ztqz}soquxz|~~~|yyyz{}~}}}|||{{{zzzyyyzz{|}|wz}uuwy{}~|ywwz{yvtsstvx{~~yx}~yvvvxz|~}{{{}}{yxxz{~}{wtqsw}~zvsrrrtvxy{|}~~~}~~}|{|}}zwvuw{}zyxxyyz{||}}}}}}~~~~~~~~}}|zyxwwxzzzyxwwwxz|~~|zxxz|~|{zz{{||}}}}}}~~~~~~}|{z{{}~~~}|{{|}~}{yvtrrsvz}}{yxwwwxyzz{{||}}~}}}}~}}|}~~|zxvuvy|~|{{z{{{|||||||}}}}}}~~~~~~~~~~~~}}|{zyxwwwxzzzzyyxyz{}}{yxxz}~}||||||}}}}}}}}}~~~~~~}}|{zzz{}~~~}|||}~~|zxvtsstw{~~}{zyyyyzz{{|||}}~~~}|||}~~~~~~|{ywvvwz}~}||{{||||||||||}}}}}}}}}}}}}}}}||{zyxwvwwxz{{{zzzz{|~~|zyxy{~~}}}}}}}}}}}}}}}}~~~~~~~~~~~}|{zzyyz{}~~~~}}}~~|{ywutstvy|~}|{{zz{{{|||}}}~~~}|{{|}~}{yxwvwx{~~}}|||||||||||||||}}}}}}}}}}}||{{zyxwwvwxyz{|||{{|}~~|{yxyz|~~}}}}}}}}}}}}}}}~~~~~~~~~~~~}}|{{zyyyz|~~~}}||{zyxwvutttuwy}~}|||{{{{||}}}~~~~~zy|svz}~|~~zwwwy{~~||{{{{|||||||||}}}~|ww~}vsrsttuwxyz{{||||}~~}|{zzz{}{xx}}zwvvuuuuuuuuvxyz{{|||}}~~|{zxwwxy{~|xww{|zyxxxxxwwwwvvvwxy{|}}~~~~~}|{{|}|ywuuw{|zywwvvvvvvvvuuuuutuuvwxyz{{||||}}~~~}zvtuwz}}{zxxwwvvvvwwwxxxyyxvtux|{yxy{}~}|||}}~zz~}||{{zyyxxwwwwxxyz{{zxuw||zxyz{||}~yw|zqpqtx}~|{{zzyyzzzzzzzzzzzzzz{{{{{zxvuwz{zyxy||uoosy{zy{~}|{zyxxxxxxxxyyyzzzz{{{{{{{{{zzxwustvz}zvrprw}~|zzyyxxwwwwwwwwwxxxyyyzzzzzzzzzzyyxwwwwxy{}~}{z{|~|{zyyxxxwwwwwwwwxxxxyyyyzzzyyyyyyz{}~~~}|{z{}~}xustvy|~~~}|{{zzzyyyxxxxxxxyyyyyxxxxxyz|}~}|{zyyz|~{xtrqqtw{~~}}||{{zzzyyyxxxxwwwxyz{}~~~~}}|{zzz{|~|ywtrqrtx{~~~}||{zyyxxwwxxz{|}~~~~~~~~~~~~~~~}}|{{{{}~}}}~|zwusrsux{~}|{zyyyzz||}~~}}}}}}}}~~~~~~~~~~}}|{{{|~~}||{{|~}{yvtssux{~~}}}~~~~~~}}}}||}}}}}~~~~~}}{zzyyz{||||{{|}~|zwusstvy|~~}}}|||}}}}}~~~~~}|{zxwuutuwy|~~|ywtsrsvz~~~}}}}}}}}~~~~~~~~~~~}||{zxwvuttuwz}~|yxvvvwz}~~~~~~~~~~~}}}}||{{zyxxwwxxyz{||}~~|zyxxy|~~~}}}|||{zzyyxxxxyz|}~~~|{zzz{|}~~|zxwwx{~~~}|{{zyyxxwxxy{|~~}{zyxwwxyz|}~~|zxwwxz}~}|{zyxxxxyz{|~~}||{zyxxwvvwxz|}~~|zxwwxz}~}{zzyyz{|}~~~}}||{{{zzyyxxwwwwxz|}~|zyxwxy{~~~}}~~~}}|||{{{{{{zzzzyyyxwwwwxz{}~~|zxwvvwx{}~}}||{{{{{{{{{{{{{zzzyyxxwwwxyz|~}zxvtsstvy|~}}||{{{{{{{{{{{{{{{{{zzyyxxwwwxy{}~|zxvutstvy|~}}|||{{{{{{{{{{{{{{{{zzzyyxxxyyz|}~}|{zyyxxyz|~~~}}|||||{{{{{{{{{{{{{{{{{{{{{{zz{|}~~}|zyyyz{~~~}}||||{{{{{{{{{{|||}}}}}||{{zzz{|~~|zywwwxy|~~~}}|||{{{{|||}}~~~~~}}}||{{zzzz{|}~|zywvvvwx{}~~}}}}}}}}~~~~~~~~~}}}}}||{{zzzz{{}~~}{zxwvvvwxz}~~~~~~~~}}}}}}|||{{zzzzz{{}~}|{zxwvvvvxz|~~~~~~~~~}}}}}}||{{zzyyxxyz{|}~~}|{zxwvvvvwy{~~~~~~~~}}}}}}||{{zzyxxwwwxyz|~}|{zxwvvuvwy{~~~~~~}}}}}|||{{zzyyxxwwxxy{}~}|{zyxwvwwxz|~~~}}}}|||{{{zzzzyyyzzz{{|}~~}}|{zyxxxxyz|~~~}}}||{{{{{{{{{||||||||||||}~~}|{zyxxxxz{}~}}||||||||}}}~~~~~~}}||{{{{{|}~~}|{zyxxxxy{|~~}}}}}}~~~~}}||{{zzzzz{|}~~}|{zyxwwxyz|~~~}}}||{{zzzyzz{|}~~}|{zyxxwwwxy{|~~~~}}}|||{{zzzyyyzz{|~~}|{zyxwvvvwxy{}~~~}}}}||||{{zzzyyyzz{|}~~}|{zyxwvvuvvwy{}~~~~}}}}||||{{zzzyyyzz{|~~~}|{zyxwvvvvwxz|~~~~~~}}}}|||{{{zzzzzzz{|}~~~}|{{zyyxxxxxyz{}~~~~}}}}|||||{{{{{{{{{|}~~~}}|||{zzyyyyyz{|~~~~~}}}}}}}}||||{{{{{{||}~~~}|{zzyyxyyz{}~~~~~~~~~}}}|||{{{{{{{|}~~~}|{zyyxxxxyz|~~~~~}}}||{{{{zz{{||}~~}|{zzyxxwxxyz{}~~~}}}|||{{{zzzz{{|}}~~}||{zyxxwwwxxy{}~~~~~}}}|||{{zzzzzzz{|}~~}}|{{zyxxwwwwxyz|~~~~}}}|||{{zzzyyyzz{|}~~~}}|{zzyxxwwwwxyz|~~~~}}|||{{{zzzzzzz{|}~~~}||{zzyxxwwwxxy{|~~~~}}}|||{{{{{{{{{||}~~~}}||{{zyyxxxxxxyz|}~~~}}}}}}}||||||||||}~~~~~}}||{zzyyxxxxxyz{}~~~~~~~~~}}}}||||||}}~~~}}||{{zyyxxxxxyz{|~~~~}}}|||||||}}~~~~}||{zzyyxxxxxxyz{}~~~}}}|||{{{{||}~~~~}}||{zzyxxxwwwxyz{|~~~~}}|||{{{{{{|}}~~~~}}|{{zyyxxwwwwwxyz|}~~}}}|||{{{{{{||}~~~}}||{zzyxxwwwwwwxyz|}~~~}}}|||{{{{{{||}~~~}}||{{zyyxxwwwwwxxy{|~~~~}}}|||{{{{{{||}~~~~}}||{{zzyyxxxxxxxyz{|~~~~}}}||||{{||||}~~~~~}}}||{{{zzyyyyxxxyyz{|}~~~~}}}|||||||||}}~~~~~}}}}||{{{zzyyyyyyyz{|}~~~~}}|||||{|||}}~~~~}}}||{{zzyyyxxxyyz{|~~~~}}||||{{{|||}}~~~~}}||{{zzyyxxxxxyz{|}~~~}}}|||{{{{{|||}~~~~~}}||{zzyyyxxxxxyyz{}~~~}}}|||{{{{{{||}}~~~}}||{{{{{||}}~~}{z|~{{|}}~~~}||{{zz{{}~|{x|xrxy{{|||}~~~}z{|||}}~~~~~~~~~~~~~yuw~urrtuvxyz{|}~~}{z{~~}}}}}~~~~yutx}wsrqrstuwxz{|~~}{zz{~~~~~~}yvtuy}~{wusssstuwxy{|}~}|{{{|~}zwutux|~|ywuttttuvxyz{}~~}|{{|~|ywutux{~{ywvuuuuvwxy{|}~~}||{|}|zwutuwz~}zxwvuuuvwxyz{|}~~}|{|}|zwuttvz}~{yxwvvvvwxyz{|}~~}|{{|~~|zwuttvy|}{yxwvvwwxyz{|}~~}|{{|~|zxvttux{~~|zyxwwwxxyz{|}~~~}|{{|}~}zxvutuwz~}|zyxxxxyyz{|}~~~~}|{{|}~}{xvutuwz}}{zyyxyyyz{||}~~~}|{{|}~}{ywvuuwy}~|{zyyyyyz{{|}~~~~}||{||~}{yxvuuwy|}|{zyyyzzz{|}}~~~~}||{||}~|zxwvuvy{~~}|{zzzzz{{||}~~~}}|{{|}~~|zxwvuvx{}~}|{zzzz{{||}}~~~}}|{{|}~~|zxwvuvwz}}|{{zzz{{||}}~~~~}}|{{{|~~|zxwvuvwy|~}||{{{{{||}}~~~~}}|{{{|}~|zywvuvwy{~~}||{{{{||}}}~~~~}}||{{|}~~|{ywvuvvxz}~}|||||||}}}~~~~~}}||{{|}~~|{yxvvvvxz}~}}|||||||}}~~~~~}}|||||}~~|{yxwvvvwy|~~}}||||||}}}~~~~~}}||||}~~|{yxwvvvwy{~~}}|||||}}}~~~~~}}||||}~~|{zxwvvvwx{}~}}}}}}}}}~~~~~~}}|||}}~|{zxwvvvwxz|~~}}}}}}}~~~~~~~}}||||}~~|{zxwvvvvxz|~~~}}}}~~~~~~~}}||||}~~}|{zxwvvvvwy{~~~~~~~~~~~~~}}||||}}~}|{zywwvvvwy{}~~~~~~~~~}}|||||}~~}|{zyxwvvvwyz}~~~~~~~}}|||||}~~}|{zyxwvvvwxz|~~~~~}}|||||}~~}|{zyxwwvvwxz|~~~~~~~}}|||||}~~~}|{zyxwwwwxz{~~~~~}}|||||}}~~~}|{zyxwwwwxy{}~~~~}}||||||}~~~}|{zyxwwwwxyz|~~~~~}}||||||}~~~}|{zyxwwwwwyz|~~~~~}}||||||}}~~}}|{zyxwwwwwxz{}~~~~}}}||||||}~~~}|{zyxwwwwwxy{}~~~~}}}||||||}~~}}|{zyxxwwwwxy{|~~~~~}}}||||||}~~}}|{zyxxwwwwxyz|~~~~}}}}|||||}~~~}}|{zyxxwwwwxxz{}~~~~}}}|||||}~~~~}||{zyyxwwwwwxy{}~~~~}}}}||||}}~~~}||{zyyxwwwwwxy{|~~~~}}}}||||}}~~~}||{zyyxwwwwwxyz|~~~~~}}}||||}}~~~}||{zyyxwwwwwxyz|}~~~~}}}|||||}~~~~}}||{zyyxxwwwwxyz{}~~~~}}}|||||}}~~~~}||{zzyxxwwwwxxz{}~~~~~}}}|||||}}~~~}}||{zzyxxwwwwxxy{|~~~~}}}}||||}}~~~~}}||{zzyyxxwwwxxyz|~~~~}}}}||||}}~~~~}}||{{zyyxxwwwxxyz|}~~~~}}}|||||}}~~~}}||{{zyyxxwwwwxyz{}~~~~~}}}|||||}}~~~}}||{{zyyxxxwwwxyz{|~~~~~}}}|||||}}~~~~}}||{{zzyxxxwwwxxy{|}~~~~}}}||||||}}~~~}}||{{zzyxxxwwwxxyz|}~~~}}}}|||||}}~~~}}||{{zzyxxxwwwxxyz{}~~~~~}}}|||||}}~~~}}||{{zzyyxxwwwxxyz{|~~~~~}}}}||||}}~~~~}}||{{zzyyxxwwwxxyz{|}~~~}}}}|||}}}~~~~}}||{{zzyyyxwwwwxyz{|}~~|||~~}||}}}|{{z{}{yy|}xutvz}}{yxxxyyxvttuwxwvwz|z}~{xwvvwy|~{}}}~~|ywwzwux|~~~~~~~|{}}xtrsuwxxxyz{}}xy}xuuvwz|yst~~{u~}yyyyxwwvuuw~wsz}|xzyvxz|||zvw~|w{~zy|~{z}x|{{x{uu~ww~~u}~zz||spqwqw{y|~}~|xx||}~}{wxzyq}yy~{}xztsztvxoxutypn~~vwz{}x{{{~}~}xt{}yyuuw~xx{wvwtorzopwmy~|vkfjv|}x|qryv}{~w{qw}{xyz}}uznednvjgilsyprwzxz{|y|rs~|t}}wrrux|}yvxpizur~{ys|}xr{ut|xy}on}vwqttsu}unxw}{slw{wwu{~yy|}y{zvu{|{z}sar{ou|qhekt~z{}~v~|xuu}~z}rmq~v{xmkkffwxjmjmuyvq~xs}yw}||}ytw~~zgoxoopxzuvyt|vzyx|~|~x}sq}}{{v|pxxuwhhovwxtozw~vz~z|r~v}x{zqmprtszqpsoxv~~{yxy~xzvz||~wv~z}zml}wx}~{wkuvsxqwvnqq|xrxoz|}v|~zuxs{yxouqpjx|q~y{wzz{z~|t}~vwwww|~|~||~xy{~z|x~ut{{y{}}wtwqt|zwpwtx~ru{ywuy~tutu|rurnlnuu|x}wz{}|zy|}z{~x}|y{z{zv|uyu|{tx}~vwz}uv}{omqz|quzyqy{r}vrz~{|z~|wyyxutumwxz||~~}xwx{s|y}w}yr~~u{y}{}{z{s||~zznv{~|tywxsyxsy}wsy~{|~y~t{zy{u{}rpuvqjwzvw|wz{zz~w}~{y{x{z~|yx{vz}~{po}}y|~{u{{xmsw|tp}{xuzprwwv~~~|{|x{zx}zrsywlozy{}y|z}}w|~~}|s{~}v~u{~vzxsvxwsywyqu~~tzzqv|{xw~}|{~|{}}w|syxruupsvtokw{}t~}}u~}|}qr}vwx~|{||rv~|}tq|{lx{xvsw|zz}uywvw}{y|v~~y{zsqxvspz{}~~|{u|||~zz~|y~}~~ut}x{z{}zy{vuy{zsuzw{wytxqz{}wy{}rx|{{zzxrqsrqmz{w|}}zz}{~y}|}vsxzvuxz}y~{uu~~ztz||ryyxrq|vz~xytyw}{y{{}}yqwysqp{zz{v{|yz}x|w}wzxwyzuy{}yy~{yz}t~yzuvzyzzxz}uv}}}}{|zx{wxx|{qoxwrkrvwvz|}~|x}w}~}~zttwxrzxxxw}zwyvz||wvzwwuyv|y~~{x}}~~~{||~{y{}qt{ytluw}xzz}yz{t}z|}}{~wxxws|xxwz{z{~|y~{xx}w}x~}|z{z{}}z}wzzyxuv~{opxvpkutz}t~}}{|z~x{}|z{wwutpxwwvt|ywz~~||zx|ww|x~y{}~{zv||ywxsnw{vmwyzx~|}}w|yz}~yzy|}wytrpx{w{r~}~w||xz}||y~~}{w{~~wwzxyxur{wjovvprxt~yz||{z{z~~~zz~xwtpnq|vytu}~}zz~zv~{v~{x}}y{~}w~{zxvxrnvxuqzx||~}}~v}zy}~z}}|~{x|zvwronw|w|s}~{x{x|y~~|vz}zu{yvsrw{nmsusqyw}}}|y~z~}|~~~}~|yw||tsvpllwwy|r~}~xyxy{~}{~{yz{{|{xut{yortvtz}}|yxx|}zyz~z}~~}x{{yuw{xtvtplr{wwzx}x~|{~~|wz||}w}{zvsru~}qnrttv~y|}{{|w~}||~~||y{zvvzxrtvpkmyw|wt~zyz~~y|~yy~{~|}{wtwupsuwx|||x}v{~}z{}{{}|}w~xywuuzyqqvsmnyw|{t~y}z}~yx}~~|z|x{yuru}}snpstw~y~}}}zw|~~|}{}|}y~wwwuvzwoqurmoxtzwt|~~x~}z|zz{}~x|{xux}urrsu{z{||w{|{y}|x~||{z|wvvuy{tnsvrnswu}uw}{z~x{}~u{uzyuu{~xrqpqs~}|zxzx{}|||z|~}~}xuuwzyqnstpnusvzqx~|~~z~y~z||v|w|{wx~zusrsx}{yxxz{{}{x{~~~{}~|wtsv{ypnrtqovswzr{}|~y~~{|~zu|uyywx}}xtrpqv{~yz{yz{{~y~}}}xtsuzyqmqsqpvrtxqy{|}{y}}{vwy{yy~zvusrv~~{z}yyzz|yz~~~}}}~z{zztqsxyrlostqvtszsw~|}~~}}|zzwyuwyxz~{uusqr{{|}|}{yzz|~x}}~z|x{xsquzvnmpssuwqxws|}{|{~~~yw~xwzyz~~ywwtsw}{zyyy|{x~{|z}~v~|vyuqqvxsmnqstyvs{uv~|~~}~~~}w~v}}vtwxz~|wvurry}|~~|~zyzz~{y|}||uzvytprwwpkmqsuztuzsy~|~~}zz{z~|vuxz~{wwvtv}{|yxy{~~yyy~y|{~txtuvqosvsmlorv{zs{xv}~}~~|v~~x{|xsux{~|wvwutz}~}~~|yyz~}y}|{{|xsxrvtprvtnknqtz|tyyt|~|~~x}z}||vuy}~yxyxvz~~~}~}zxy}~}}y{x{x|{~zp|{qtuqqutolmptz~vyzt||}w|}z}y{wsv{}xvxwuy|}~~~~~{yz~~}~z{y|y|{yq}{rsurrutnlmpt{}tyxs{||~}x~||y{xtw}~yxyyx||}}}zyz~|{}z{|zzx{}zus|yqrursurmlmpu}{uzuu}}}~yy~x~~wzwsw~{wwxwx}{|~|~{z|||~|{zz}{~sw|xqrututpllmqw~wvyrv}~|}~~~x|}xx{yuz{xyzz{~y~~~{z}~z{}|{~}yx{y}}svzwqquuusollmpx~vxyrw~}~|w||x~wzyux{xxyy{}w}|~~|{~~{{~~}~zz{~z~~twyxspuwvtpllmpv}wxzru||~~|z|}u}uuxtu|~zxwxz|~x|~~}~{~|~~||{x|xwx{~~yz}rsutrlntuusqrpnq{pytu}}{|{{z}~wzyux}|xvxy{{x~{|}wuy}}zxy}~{y}}zz{~|yyswuvrntxwsmjikr}{~zsy~~|z{ztpuup{}urvuty|zwxy{~x{}|~z|{|z|~|z~|}~~||}~yvz|xpifhnxxqvyz{||yx}|~~|y{xzx}|{}~~yx}x}~yusuyztu}vs{}zwvvwz|zv{{svvsupntwsmjkns{yy}||}~}uvypu|wpquttxxvvxz}yw}}}|yz}{{y{~~~~{zzxrrwuoihkqywz|}}|{{y}~}}{|~~|y{x}x|}xy}x~zutv{|ruusz~~|ywvvvyzwv|xsxuruqntxtnlmpv~~z|}||}~|w{wqy}wqrwwvxxwwy|w{~z~{|}|{}|{}}zz~~~z}wxwqsxtmijnu~}|~}{{||}z|{~~}~~z}wzy{z}w{}xvw|ur~ysw|~~|ywvvvwxvu|ysyvrtuqsywqmmpu}}|}~|{{{}{yt|}ztrwzxvvwxz}}w||x~z{}y~|{~{||~}~||wxytuyunjknu}}}{{~{z~||~|}{z}}y|{z~~}yv}~}~zy|}sy}wux|}|ywwwvvvutz{txztquvsuyvpnorx|y}||ywx|x|~{vt{}yuvwy|xy|vx|z}~~{y~|}|}{~~}||{zvxzvvyuollouyw~{}z|}||}~{|yy|z}{}}~~|yv|{z|}|~w{|yxxz|{ywwwwvtssz{ty{vqswvuxxspprwzv}~{}ywvy~{~~{vy}wuwz|zz}vv}}{|}{~y~{{~~}}~}{y{~}wvyzwvwsonotxvz}}{}}~z}{~zyy}}|~}|}|zzwy|zy|~~|{}yzzyz{zxwxxwusqt|xt{{ursxxvwwtrruzyw~~z}~|{{~}}yx~~wsvz}z{~vu{~{yzx{}~}|}}}{~~}~~~}zwv{xy~{usv{{xvvpsroyx|~y}xzzy|{~{}|z}|}~tx~zvux}~~|zy}|z{|{zzzywtqqxysx|xsqtxurtxyyz|}x~~{sv}wuw{z{~}{x{}wvy~}{zuy~|zz{}|tw~}}{~wtuuonsx{|}~xv~{~}yx{{|}z}}}wx~wy~|~}}{to{~|}{xx{{wx|tsyyvvvvvvwwuroqywsy{wrorwwsuy{|~z|w~|usy}vz}{z{ywz~{uux}y}~vuy~}zyzz|~zt{}|}|}vqpstpouz}{~~}zwx}{x{y{~~~zw~{x{}}|zzwqv~{z||wywrx{xuuuuvwwvspqxvry|xtqrwyusw|~{|y}{wtv}~x}|{||zz}|xvy~}z~vvy}~|zz{{{|zv{}~{~}}wsrvvpnsy}~}~}yxz{w|x~~~x{|{z{}zyvtyz|}~~zx~utz|xttuvwxwuqqvwrv|{wtruyztrv|{~{}yxvvz~~~{}}}~|{}|yw{z}yuwz}}|zz{|{zzzz}|~~}~||{|xttwwpmqx}|}zy|zw}y~~~~|y~}z}{z}|ywwwxvyzywtx}zustvwxxurrwvqv|{xvtuy{wrtzz~~|}xyxuw{}~~~|{}}z{~~z|{vwz{|{zy{||yxy|}y{~}}}}{z|}yvtvxuont|||{|~yz{|~~~}y|zz}{y|~~~}yvuwzwt|~x|wuy}{ustvwxwurtxtqy}{xvuvy{xsu|{~}{zuxyvvz~~{}}|~}y}{wxz{{zyy{|{xwx}yy}~~~|}}{xy}yvttvwspt|~||{z~}~~}~y{zx|~{z~~||||yuru{{txzy{wwz}ytsuwxxvttxwqu{}}}~~~{z|}~~~}z|xqrxzxz~}~|{~||zvw}zttwy{{yxw{|qqu}~z}|~~}|y}}ywy{|yz|yy|~~{y}|{}|{{}~}zxz|rnsyywz~}}~|yvux|~zuqt|}uy|~|{~{z{~{suy||xxxvtx{}|}}|zz}}y{{tuzzwx|}|zwspos{}wv{}sqz~|{}|}}~{{}}{|zsw}~}|}~}z||wttxzy}{z||wsuxyyyxxx|{|zzzywttvy||yuv~xv~~}z{|tnry|~{y}}y}~ywz~|zz|~{yz|zwz}ww{}ywy~~zywtqoorx}~|~xv|~|{|~~y~}}~{z|~~|{~wty}}}~~~||{wuvwuu~}~zvwyzyyxwy||z}{z{zxussvy{zww|~wz~~||zqqwyy{~~z|{vx{~~}zz{|~~}~}}}~}{z{zuv~ywz}~yvy}~|{zxuqoorwz||}{|~}}~zz~~|{~~zy{||{}|tv{|{{}~~~~|xwwuqt~}}~yxz{{yxw{z~~~y~~zz{zwtrsuxyxww~z{~~~}~{rswxxy|~{~|wx{}}|zz{|}||~{z}}~{zzytu|~ywy}}xw{~|{{yvrooquxzz|~{y}~{{~|yyzzz{}uty{{z{}~~~zxwvrqy~|}yy|||zxy~|{~{yzyyzxusqsuwxwwz~}~}}}||xqtxxxz}}}{wy{}}|zz{||||zz}|~}zzzwsv}~yxy|~zx{~{zzxurooquxz{}|z}~~{y{~}zxxxxz~}usx{{{|~~}~zxwvsqx}}}z{}}|zz~}z~y|{wxyxusqqtwyxxy~|{zy{~zqqvyy{~~|yyz|}|{{{|}|{}}x}||~}{yyxtt{{xy{~~zy}~zxxwurooquy{|}}z~}}~}zy|}|xwwwy}}vrvz{{|~}~zxwwtrx~~{z}~}{{}zz|~yvwxwurqqtwxxxz~~{yxy|~wr}}zy{~zuv~z{}~~~{{~}yuw~~yvvx{}|xx{z}}z{||zxvuwyzyxy||y~~wr{zvvwxxwwz}ww~~}||x{~{|}~zy~}~~z|~ywxzzxvttvwwvttwxv{~{z{{vt}}{zxwwz~|uu~~zz|}~}}~~|{~|{|x|}{{|}}~}~{|}}~|z||wuwyyxvtuvxwvuwzzx~}xvvyyuu}}ywvy}~yrt~|z{}~~~~}}|}|z~}}~{{}|zz}|x{~}|z|~zuuwxxvuttvvuttwyuu}{wuw{xsw~{{}ysx{y{||||}~~}{|~z}~yz~{{{|}|yy{{{||x}}|{~zuuwyywvuvwxwvwz{ww~{vuwzwrv~|zy|~wrw~}zz{}}}}~~}~|z|}}}~~zy{{{}~zv{|yz~yttvxwvttuvvutuyytt}~|wuw{xsv}|~{tw~~{z{|||{|}~}{|{}~yy{{{{|~~zwxyz|~|w{~{z|vuwyyxvuvwwwvwz|wu}}|~|wtuyxst||{{~|uu{}{z{||||}~}}}|z~}}}}~}xwxy{~}xxzx{wtuwwwuttuuuttwzxsw}}~zvuxzvt{~}{vz~}{{{||{{|}~}|{}~}}x{~{{{z{|~}yuuwy||y~zy}wuwxyxvvvwwvuw{|xv}}zz|{wstxxuv~}||}}xvz}{{{|}|||~~}}}z~}||||~}yutvy}|z|wy{utvwwvuttutssuzzvw~}zz|zvsvzzw{~~~yy}}{{|||{{{|}}|z{zy{zzzyz{|zursuz|{x}zvvwxxwvvvvutvz}{x|}ywxywsswzwx~}|}~{y{}{{|}}}||}~~||}z}~||{{z{||xtrsx}~}}wx|vtuwwvuttttssv{{wx{xwyyvtx{zy~}{|}{z{|}|{{{|}|{z}}y{}zyyyxxz{xtqqv}}zxyvvxxwvvvvutux|}zy~{wuvwvtvyxw|~~~~~|z|}{y{|}}|{|}~~||~z||{{zyyz{zvrqu||yw~yuuvvvuttttssv{{ww}|xvwxxwx{yx}}|~~zy{|}|{z{|||{{~|y{|zyyxwwxzyuqqu|~|zyzwwwxwvvvvutuy}|xy~zvtuwwvxyuv}~}{}}xx{}}|{{|}~}|}|{|{{zzyyz{yurrw~{|~xz}xvvvvuuuuussuz|xuy{wuwyyz{xux}}|xx{}}|{z{|||{}}~yz}zyyxxwwxyxtqrx~}z|z}~zxwwwwvwwvuux|}yw{}zvtuwxyzvsv~~{||wx{}}|{{|}}|}~~{|~{zzzyxxyzyvrsx}|y|~y|}zwvvvuuuuuttv{{wuy~{wuuwz||xtw~}}~xwz|}|{{{||{{~}}yz~zxxxwwvvxxvrrv||z{|}|zxxwwwwwvuuw|}ywz~}{wssuxz|xst{|{yvx{}||{{|}||~~|z|zyyyxwwwxxurtz}{{|{~}zxvvuuuuutsux|zvw|}zvstw{~}wuz|}~xwz|}|{{{|{{|~~|y{|yxxwvuuuvwvsrv||{~}~}{yxwvvwwvuux|~zx{~~}}|zwsqrv{~{vv||z~|wwz|}|{{||||~|z}|yxxxwvuuvwvtty}}||}}|zxvuuuuutstw||yw{~~}|zwsrsx~|xz|{{wxz||{{{{{{{~~}yy~}ywvvuutssuvutv{}}~~~}{xwvvvvvuuw{~|y{~~}}||{zwspptzyw||z|xvx{||{{{|||}|y}|ywwwvutstvvuvy}}}}~~||{xvuttuuttuy}}yy}~~~}}|{yvsqsy}y|{|{wwz||{{{{{{|~{xz|xvvuttsrrtuvvy|}}}~}{xwvuvvvuvy~~{y}~}||{{zxvrpqv~}yz~zz|wvy{|{{{{{{}|y{}ywvvvutsstvwwy|||~}{||zxuttuuutuy}}yx|~~}}||{ywtrrx~z|{|}xwy{{{{{{{{|~zx{{wvuutssrrsuwxz{z{~|}~|ywvvvvvvw{~}yy~~~}||{zyxvsqrx{x{}z||wwy{{{{||{|~~zz~{xwvvutsrstvxz{zx{|{|}{xvuuuuuux{}ywy~~}}||{zyvtrt{zy~}{|xwyz{{||{{|}{xy~}yvuuutsrqrsvxzzxvz|{}~|ywvvwvvwy}}zxz~}||{{zyxusqsy~yy}|{|xwxz{||||{}~|y{~ywvvvutsrrtwz|{wvz{z|}|yvuuuuuvy||xvy~~}||{{zywtstz{z~}|~zxxy{{||{{|}{xy~}yvuuutsrqqruy||xux}z|~~{xwvvvvwy}~zwz~}||{{zyxvtssw|~{z}}{}{xwxz{|||{|~~yy}{xvvuutsrqrtx|~{vv}zy|}|yvuuuuuw{}{ww{~~}||{{zyxvutv{~|}||~zxwyz{||{{}~zwz~|xuuttssrppruz~~yuy}z{}~|ywvvvvwz}~{xz~~}{{zzyxwvuttw|~}|~{}}ywwxz{||||~zx{|xvuttssrqqsw|yw{{xz|}{xvuuuuwz}}yx{}|{{zzyyxwvuvz~~}{|ywwxz{||{||xx|}xutsssrqppqsx~~xxzy{}}{xvvvvwy}|yz~}~|{zzyyxwvuuuw{~~~~{|~{xvwxz{|||~zxz{wuttssrqpqrv||x|{xy{|{xvuuuux{~}zy|}}}{zzzyyxwvvvx{~~||}zwvwy{|||~{wy}~{wtssrrqqppquz}xz~yy{}}{xwvvvx|~{y|~}|~{zyyyxwvvvvwz}}}|{~}{xvwy{|||~|xz~|xutsssrqppquz~yz~yxy||zxvuuuw{~}zx{~||}{zzzyyxwwwwy{}|}}}~{xvwxz|||~{xz}~{wtssrrrqppquz}xz~zyz|}{ywvvwy}}yy|}{|}{zyyyxwvvvwy{{z{|~~~{xvwy{||}{y|~zwuttssrqpprv|{x{|yxz||{ywvvwz}~zxy}~{z}|{zzzyxwwwwy{|zy|~}~|xvwy{||~~zz}|xutsssrqppprw}~zx|}zyz|}|zxwwx{~~zxz~~{z~|zzyyyxwvvwy{{yw{~}~~~|yvwy{||~||~~{wtrrrqqponorw}zx{}}}{xustvy|}{vstx{~yvuw~|y{pow{}|zvtw}}yusuz}}{zyyy|~}}}vpmpvywrnllmpuyyvw}}y|~zxvutrrsvyzy{}~}~~yxyxwtsrrsuwy}~zy|||{|}|xxy|}{yz~yxywwvsqoopqpmlq{yw~~}yvvz}}zw}vz|}~}uqu{{x}umuwtsssv|}}}{{}}wz}rw~xuv{z{xs}zuzustx}|~}zv|pvxpqppry{wvuss||z|~}}~x}{uxxzyx|{uvxyzy~}{wsrrr}~}~|vuxs}trromqxtllnnmzx|x~~z{y|~z{z~vtxxuv}zuz~z}xxs}xvvxus~{|yx|y{~zv}uyu{~y~}||z}~~v}}{|yyy|~}v{svrzztvz|tv}xt{|y|z~sux}w}}{{{yz{|t}}vu|vss~~uy|}uxyxyxyooqxsmuytst}y~||{{}~{z|~}zxpsw}vs|~{}w{~ysq~}}|~}z||{xwyvvvvuz|}|~{{~y~|}~{|~}{wvtust~|yrz~uuz}|y|}y|zx~zxxy}}{xy|w~~~w|ynry}y||yw~wy{|yrxvsmszsswxvr|z}{|~~}}v}}|~wu{xtoy~z}{z}{|vu{z|{~~}y|yu|x~yzvz{w|~~~z{}~v{|{}~|}|~||}~{~yyt{vtw|{sw|zy|}z}x|xvz{xszvzz}~|~~{}~xqx|zy}||z{zyruywyw~vstzxrx{uswz|z{{{}||zz|~}~}z}vuvzuv~}z~}yzx||{y{v{}{vyy}zw~}zw}~||~{~yy{}~x|ywvzxts||vxxzuz}z~}|}x}zy{xzxz{z|}{~}|uv{xuxxxs}}~zx}z~wxsu|yus|{pry}x{|z}}{}z}~~~{y{uz}yst~|u{~{{~y~~z~~w}}{w|zz{{wv|uywzz~~|~x{z|~zw}|~{||w{x~xx||vzzz}~{~y~y}zsx}zt}z~{{~|~~|{}zsyywz~x}~x}yvyw}upv{zvxytww~}||}~x~~}|v}xztrups|upxxxz{xxvwtzzv}}{x~{upz{xv}~y}{}~{|yy}{w}}~|zv~|~}~~~{t}{xvz~z}}{y}|y|vs{}t{qzy{|x~~}v|{t~zyw{zv}v|~yyry{|nz|{wvvx}}}ryux}}y~ux~~}}}y~||s~mtsswu|y}|zy}~{}}tzz{zsu{y{{}|x|}}~uyyw~~~~|}xxxzwxzx}{}||{z}zvt}{y}{}}vz|{{vywwr{wy~|~{zx{|}yw~|{}y{z|vw||uz{z}vx|{}z|{|zz|~xy{y}}}v|~{{{wsyyt{yy~}z|z~z~w~~{w}{wvyz}~zy}~}z~~{~|vz{w~~{}~~zv~yryxy{{{~yyzyyy~}uy~yw~|~|{~~|{~{}uv{yv{zu{}~~~~}}yyuv~y{vyz}z~t}z}|~z{|z|zyxwvuwyuw|y|}}~~{zy~{x}xy~~{{~xz{wv{~|||{|~z}{}}v|}y~~ww~~}|}}y|z{{|~{}x|}z||{vwtr{~{z|}~|{|~}{|xz|tw}y~{{{}}zx~|{y{z{~vz~|yyz||}~~{|z}{x~zy|suzut|{v}|x||u||z|{~}|}zw}~{x|}|~}~}yzvwx|x||xtx}y}w~y~{~{|xz}|~x~}w{xqx|vzvw}~~}}|v|{v{xxywvv||}~zz{yw|yz{y~ytz~w~z~{{|{z|}~~{|ywx{tpvttxzzzz}y{~z}~zv}{z||{{|}~~yzwz|{{x}{y|{u{~~{}~z}txxrw|tust||{y~~~}{}}{~zw|wurzzw}{~zz~|z}~y{~{{}~|y}|y}|z{~|}y{wv|wqvvuzv||}~~}y{zy{x}}}x~zy~z{|z~|z~y{~w{vx|zz~|~ws}~}}||x}{x{|x~|uvty}}{~~~|}x~|}z|tqxwwu|zw~zuyw{{wyz}}|xzzyzzxzsw}}~~zx}~xxz|{txvvyyw{yvrw}ww}stut|}~}y~~{}|wvr~z{||{~|uwzurzyuupnvzvx}|vz|{}{y{zus{zzx~y{}}x|yw|~y|~q|uww|yuxzxz~}{}x{}~vvu~vxwt}yxs|~xxuzx{~{~xwy|ysutytz{sy|}~~{vy~xut{{xxyx~xy{{{}ywszyw||xyytpzwrtppy~{~y~~}{~{xyyxvt}y~sw~}{}{|yxwx}zzxxmswxszv{|}||v}}}v}{vv{yr|z~{z~|~}wzw{yoy~}zxsyuy|u|~z}~}~~}|}{x}~w}w}{{yz|}{~{}}}~sv{xxz}{v{y}}wsw}z}~xzww~{~~wxvz~{{ypxy}}z{~y|{||{{xss}sorwus|~z|~{~|~}||ww~}~}u}yys|}~~~~~}{{{yv~yoxy{ruxyy|y}~~z~xyzx}|s|~{{}yx|~~}~|{~~{{w}~~|t|~{vt{vyy|v~}yz~y~w|~xu||~|~w|}{x}yz~}~{}uwz}uzw{ty}xu{xu{x}}{}~x{y||}y}|szzuyty}x}x|zyy~~y|}vtwzs|{|us{|xy{~~{}{{}~usy~zu{w{}~}|{~|x|{}|||x|xyzxpw|{}yzw~~{~}yy}|vzzwuzyz}}|}~zz|suz}{xys{w~zsuz{zzy|}}~}~|w|yu{}}}y~y|~|z}|}}szzyswwwvvs}}ysv||}{z}{{z{{zzzw~z{~}u}x}~~z||zx~~y~}y}~zuyyrty{xv|xx}yz}}||u{wyt~}~~|{yz|w||{wywsyyoszxstz|{{~}}zy~{w{u}}vy||}|~z{|z||v|}}v~{rx{zyzvyw}~~yww}zvvywyzv|}}zy|}yw~z}u}{|{stxztwyxy}|y~~~yuuyxurvmvvwvpsxzx}~}yzzxy{xut|{wv{{~{qw~~~z~x~{}~~ywuy~|uz}|us|vnutqssvzznsywtssz~|~}}|y|zzyt~{w|y{u|zs|yp|}}v{{|}y|}}uvwvx}~{~}|vwqyxvpkuvysu|zvypu}yroor~~xrxxuuxxsyxwzzy}{uyttsxzqyv}~~{xrv~ty{uuzyvwsltxxts}su}rgvksjiqhkhzoprtsv{ys}|}|}wxxvyqsrnwv}}}v~{yvx}~|}z{zw{z|qku}|ynqzrkwrqemtjrovzkv}}|vypw{{~z||{v|}xy|suy{pu~t}}}}wy|x}{}zu}~}qntv|vnpzvyrpjl~{stkvxpy}~v{rv}|yuz}wxy{yxu~|t{xvtw{q~}zy~~t{|~~~~}|uy}~vmomvzzwwyxonoq||urnzvnux}xt{twu}ysv~~|}y~~{yzvvxvuq}{t}~|~{~}{|zy|}|pv}zoqlz~}zy|ytqnnv{qmpx}jlw{ws}qwwsxw~~{~{~u{|zuxwyru|t}w|yv}}~z|wsy{zuurvy~uyzxuritzwjqvytlv{xsztt}uv}{|}~wz~yw~z~{{}{zsrzy~}}|wz{v}~x~~|prw|{uxq~xz|v~usooz}qmxzympwyszztwuysw~{|~}x{~yyz~}}}|xp{x}zy{zz}{~}umvwztttt~{x}wuku{zloxxymqstp{wtv{zvy{}}~{{z}{zzzyzyts}{|~u~}|~x~{{x}pmuywqysy}~w{uunyzvipzwwqrvss~yxr{z~x}y}}x}||{vuzyxry{~{~z}~{{y}ysx~ukprxux}u~uxuwqvzzqjy}wroqwt}~wtt||y|}~|ty{}}}yxvz}yvv{}}{}|}|}zx~~~~uv{qosvvv~vzt{|tryyznvvrpqxuzyvs}y{y{~}|}wz}}~~}vsy}zwt||~z|{y}~{~}{ut{{rmntvu~vz~v|wpx{|rsyrtpuswyyr}{|zy~}}xxz{|||ysx{wur|y~||{y}z{}{|}|utxxqmnruu~v{w~wty|{qrypwsrpx~xxr~}z{|}xxyxxy|}xtx{yut~{|}z~zy|y|}x{|{vuxwroqsqw{t}x~vw{zwqvvpvsrq~{xu~}x{{}~|uyyxyzurxz{uy|y}{z~xz}|~|x~}xtuxwsqopoyxww~wy|{wv|usxrpuz{sx}xu}}{~wxxyz|}sqx|zv}y{|~}{~w~~z~{y|{vsvxwtroopyvx~|yy|zzwx|upyuov~xzsy~wu|}}w}y{{{}}srw{zv|w}~}~z|v|}~x}}{y|ywtuwtsroopztv}zzx{|zxz{~wowwpuy|uzxv~|||xyzzyz~uqvyyu|wz~}~z|vy|{{~{|vyywuvurrsoonwwvz}z|}|yz~~|svysr}{}xwzu{~{yz}{{yxx}yqrxzx}|x|{{}xx{~z}ytuvwttvsrtspotzs}~~~|~{zy~~zrwwpz~{~v|~uv~}{~y}{{yx|~urtxyyw|~x}~|x|z}z}~w}tvwvttsqqtrpnyxx{~}}zx{}yswws}{|zv{tw}|~}}||yxx|{squyz}~w}{x~~{y~y}~y|{}vyrtussurprsqoq{x~}~~~|{y}~{xsywwy}xxyu{~|}{|z|zyy|}wqrvzz||{xz~|xyy~|x~yv|tqqrqstqpstrqy{x~}~|z|{zvuyv~{{}v~uv~}y}~x{{ywz~|usswz}z}}~x{{yx~{{yz~w~|uyqqrrstqnpuuru|w~}~}{z~z{uyy|}y~w|xu|~y{xz{zxy}}wsrux}|}zx{{zy~x}|x~~y{t~|rqqqrtqnpuvst{w~|{z{vvz~ywzzu{~yyyx{yxy}}ytrtw|{~{xzzzy~z|~y~}~yz}u|zuqqqrsrmotvtt{v~||y{xu{~y~w{yv||yyywzzxz}|xtrsw}z}|yzzyy~y{~z}{}xzzw|yuqppstqmosusvyu~|~x{xv~{{|wxx~}|v}vx{zy{}zvtssv{{~yzzy{x|}|~|z~xzx|zwupppttomptuuyuw|~}y}ywy}zy~xzz~tvwzzz}}zwussx{~{}zyyx}x{~yy|yxw}wwuqpqusolotuuysx|~~y}{xz}xz~y{yuwwz{{}}ywussxz|}zxxy~yyxx|zxvwuvqpqtsolosuvyrw{y{}x|~yy~{|y|w}yux{{}}zwvtsv{~|{yxx}{x~}}yw}}zys~zswsppssolnquvztv{{z~z~~{w||zyzz}vvy{}~{xwust|}{|}zxx{~yz}}|uxy{rwstvqoqtrnlosvzys~~|~z{~|~w{}|~}w~y~zuwz|~|ywvtsx~z}|yxy~}x|{~xtz{zxp|{qttporsplmpux}ww}~~y}~}w~}}{x{z~}ytwz|}|ywwutz}{||yxy~~|x~{}~vt{x{up}xpstppssomnrv{~v|}~}z~{x~|~xyy|}|xtvz}~zxxwuv}|{~{yx|}~{yz~|ysu}zxzot~spsrpqsqnnpty|x~|{w|~|}{v}{{~{{vtx|~}yxxwvz}z|~}zxz}}~z|y|~}usy}w{to{yoqtqpsronorw~x~~~z~yy|}|v{|zzzxtuz~}zxxxwy}z}~~{yy~}|~{zy||~wsw}}wzvmx{rossqsspnoqv}z~~~{}|y~{||v{{y{yzuuz~~zxxxxz}y}~~|yy~}{}}{z~{|~wsv||w{vnwzsnrtrsrpnnqv}z}~|}|z}{|{v|zy{xzvty~~zxxxx{{x|~|z{|{}~|{}y|}vsw|zw|toxysnrttsronnqv~y}~|~}{||z~~yw|w|xxzvu{~}yxxxy|xy|}~|{}~{{~~~~{wzuty}~xy{qsxwrnruusqnnorxz|}~}|{~~{}xz{t}~rqtontwusstuxztz~~|~|}}}vt|~~{vrsw{~~{vyxortspkotutrqqpqvyv|}z|~zw|}x}}xywzwuy{yvuvwz{t{|~~{vy}}{yz}~}wu~zvwz}}w{zquvusnpvwtpljkoy~}}wtx}vpsupz~vrvvswzzwwwy{|uy~{}z|{~}|v~|yy{~|x{~|}xv{}yrlhhlv|}|~~|}~}|zxx~{{~w}xy}{{~}|}~}{z~}u|~xuuy}zsyynqy||{wsrsvyzzvv{trwtttoqwwrmklnt}~~|zxvv{}wzuq{|tosurtvvuuvx{yrz|~|{z{|{~~{}v}}{{}}z}~z{xsuyvpjikpx~|{}|}~}|zyx}{zxz{x|~|}~~|y{}v{{wux}~tt|rnu{|{xtrsuwyywuzwqwvsusosyvqnnpu}{}|zyxwzyzzqw~yrpuvuuvvvvx|}vv}~|~z}y}{|yxy~{{}~}{{zyyurvxsmlmrz|}}{|z|~|{yx|}{zxyz~~}y{}v{|xvx~us|tnty{zwtstvwwwuuzwrxvrttqsyxsoprwzz~}zyxw{z}ys{~ztqvywuvvwxz~|uz{~}{}~|}zz}|~yxz}{}~}|y~xxyuswxsnmpu~z}{}zx~|ywy~|}w}y|~~~~~|{|~ww{xy}zryxqrwzzwtstvwwvttzxryxssvurwzvrprv}}zw}~{xvux~~{v}}xsu{zvuvwy{|u{~y|}{~|{|yz~}}|~{~z{~||~~}{}}z~xwyxtvxtonpu||~z}|~}wy~zwvx~z{~z}~|~~|y||uz|z|}|~xwyuuuwyxustwxwvtrwzsw{vrswvuwyvsrtx{xwz{vsswz~~zvx~{vtvx{~}vy~yx~{{zy}x}}z{~~}~|}}~}{}}zzz|ww{zvwxtpoqvxv~}}~|vz~zvuyz|~}{}~{x~zv{ywy~{{wvxwwxwutuxxwurryysy|wsswyvvxwtsuxyv~|z{tomou{~x}}yx}~xstvx{ztv|wv|z|{|~zyz~~{|~|y}}|||zxy|}}{zz{}wox|{xyywtuxyv|~~}}~{|y~yvw{z{}}{}{z}ztuwy{xxz}vx{xwvustvxxvsprvqr}|wvz|ywyyxwy{xw~{rrwzwsoptz{{z~zuux||v|~vux{~}w~|~{{~}~wswwr{{yzxroponorspv}|}}}~z|{xy}zxz}xqx|tsvtrtw}}}||~usyzwuuttvxxvrnnuur|~zxz~~yx{{z{~|y{uuyyupnpuz~y|~~}zz~yvw{~xyytvz}zz}~|~~~}xroswty}{|~yrqsrrtuqt}|{~}~~}{~}zy}xvx|{tw|vuxvttw|}~|}vrx|yutstvxxvrnovts}{yz|y{}}~}z~|xwyzvqpsw{}~~y~~|{~{wx||w}wtvz}{}~}|~||}~{upouus}~|~{tsuvvwtqy{|~|~~~~}|z|~{uvy|zy}yxzxutvz~~||{ru||wsrsvxywsooutqz}{{zz}}y}|~zy{xtrsvy{||y|}~yy|{w~xuvy{}~~~}~||{|~~{upquqq|xuwyzxsr{~{~z}}~~||}{}xvwxz{|~}y}}yuuw{~{xtx}|wrruxzywrptuot}}||}{{y}~{{~~||yvuvxyzz|z}}}~{{~|x||vvwyz{{|~~}}|zz|~}ytrusmt|~~~{y{~}xqv}{y~~{}~||~}}~xwwxxxy{~||{wwy||{yux}}wsruxzywsrvtou|}|||~~{y~~{z}~}|zxyyzyy|{}|~~|}|w|}wwxxyyyz|~~~}}~}~zxz}~}zususmqz|}}~{||uw~|y|~|~||}~~|}zwvvvwwx{~{|yy{z~~xvx||wrsvyzxusuwqpx}}~}~}~}ywxz||z}~~}}|{yy|}x}~~|~}{wtz~xstuvxz|||{}t|~yz|}|yvuxwqu|}}z{}xy|~~}{yy{}~}z{}}}|{{|zx{u{}z{}zyyuuuutssw}~||}|}}~||~{y{~~}ux{{~yz~x}~|~~||}~~|yutvz~~{{|}}}}|{}~}|{yvrqsx}}ty|z{}~~}}}xwxz{yvtvunnw~}|}}yutw{}~yuvxyzzyxy~~|~xv~ytssrrrtx}|}~}zzzrmsz~|{w|zxz}~|{|}~~}~{zz|}}{wutw{~}|{}~}}|zwvsqqruzx|}{{~}}~}|{}~|xvxzywuvxsmrz~}}~{wuvy{{zvx{{{zxx{{~yu{zuttsrqruy}~}~}zz{unrx|~}y}{wy|~~|{{|}}}~{~|zz{}~|yvuvyzy}~{}~~{zxwtrpprw{}}}~{}}}|zz||ywxyywvxyrntz|}}|xvwxwv}|yz}~|zxy~{{}~~vv~~xuuusrqqsvz|}~~}|~{rqvyy{~|~yxz|}}|{zz{{{}{y{z{}~~|ywwxxuv~~}~|~|}{zxurppqtx{||{}|}|yy{{yxxxwwx{wpqwyz{~~|yxxwst}}~{}~{y|~y}}~|uy~{vvvutrpprtwyz}~}|~{rswyxy|}zyz|}}|{zzzzz|{w~|yz}~~|zxxxwtu|~{{~}~}}{{xw|}~~zvuy}|yzz{}~~{xy|}{zzwuyuu{{y~|{}~}}|{}z|w}{zz||z}yz|~zz{}{xyzyxwvvwxxww{{tt~ytrsuz|wty}~}~~|~}}~~|zy}}~~}}}{xzzz}}}}~~xsvzwtuvwvuttuvwwwx~~{z}~{zyz|~wx~}{yyz}~}|su||xwy|}}|{{|{xv{{wwy|~}zwy|~|z{|~{|}}}|{{}~||}~~~~yxyyvsswyyxxxz~|vz}}vw~}}wx}z|~~yxyz}}}zz~}{zz{|}}|xuvzwsvtrsw{|yqutpvvvv{{vw~~xvwzyw{}|{zz|~}|||{zyxxxwwx}~spv{z}{vv{woryzyz}~~~wrqqponnquz~~}||{wssz{xxyyrmt~~~}}~}}{ux~zvtuwx{~|}{yy{}}{}~~~yxxxxyz{{{ywuw|~{|~zywsmjmtyzyz|~wx{|{{|}~~~~{~}w{}}~|}|xxyz|~|z{~|{yyyy{~xvuutrqpqrstux~}~|~|z|~ytx|y~~}|{yxwvvwy~~xy}}{{{zyxxxuru{{xwxz|zxxxxxyz|}~}}}}}~~}~}|zwrnouyyvttxztquyzz{|~~}~~}~{zyyyywsrtvx|}|zwtssx}wttuuussrstuuw}~{|z|~xx}~|z{}}ysx~|{zywvw|~ysrtw{}}}}~~}||{yttz|xvvxy{~|{zywuttuuuuw|~zy{{zvvwvtpmouyywwz}zuuz{{z{|~}zyxuru}~~~}~~}ywwy{}xz~~}|yuqs|}wuvvvutttuwxz}~|yy|~zxz{wrt{|vw}}{yyzzzzzzzyxxy~{uuvvy|~~~~yy~|z{||}|vu}|z{||{zyxyyzz|{wz~zz}|{|xwwwurnnrx{{{~{z||~~~{wv{|yyyxwwyzzwuvy~zy~~~~~{vszzz{{{zxwwwwxz}~||~}~xtw{}}}{x{~|zz{{zzyyyzyyz~|z{{z}|y{|z{||}~~xyzwvvvutttuvwy|{x||{}~{{{zyvsruz}}}zz~~}}~}}}{wv{~{zzyxwxz{yvvy|}wsw|zz{{zyxwvvvw{~xx~~|yvux|~}{}}ytv{~}~~~~~}}|||{{}}yz{{|{vuz{xvvxz{}xu{~xvvvvuuuvwxy||~~{zzzywussw{}||~|wuy|}}}~}~}~}}}~}zy}~~}|{{}{yz||wpr|yvvwxyyzzzzz|~|~~}|ywuvz}}}~~zwz~|}~~~}||{{zyy{~xuwxww{{|{yyzzzz}}}}wv~{yyyyyyyzz{||~{|zvuuuuttsuy~z{~}z|}}}}|{|~~zxzzz}wu||zzzzyxwvvvw{|y}}{yxz}|wy~~|xxyzzzyyz{{{|}z|~|z|{xy~}zxxyyxy{~~|yw}{yyyyyyxyyz|}z}}~{zzyxwussuy|{w{}~~~zx|~{zzzzxxz}~{z~}wrs{|zyyyyyxwvvvx}}yz~|zxwx{~ww{~|}~}|{{zz||xy||yxz}yy}~{xxyyyz{}{vy{xwvvvuuuuvx}|}~zyxxwwvusrsw|}xy~~~~}{{~}~}{xz}}~~}}}|}}{uru{}|{zyyzzzzzyyz}~z}~}|{zxvttw|}xy~~~}yz}~~}|{zzzz}|xy|}{yy~||~zyzzzyz|zvx~~}{{zzzyyyyz}}~||{yyxxwwwvttw|{z}|||~~~~}zz~~{z{||{{|~}~}|{vv{~~}}|{{{{{zyyy{zz~~}|ywwz{wy~|{xz|~~}|{{{{}|z}}{{}{}|yxyyxxxy|~~{x{~}||{yxxxwwwwxz~|~~}{zzyyxxwutv{{z~}zy}|~~~||~|yy}|z{{|{{|}~{wvz}||}|{zzyyxxwwx|zy|~~}|{zyxvtv|}wx|~~{z|~~}|{{|}yz~~|z||yz~}zyzz{{{|~{wx||{{{{yxwwxxxyz~~~}}zyyxwwwvussw~|y|~{{|~}|~~|{}}{z~~||}}}}}~~}~zvvz|{{}~~}||||{{{|}{}~}|{zzyyxvuv|yz~}~{y{}~~}{zzz{~zxz~}|~|~~{zzzyyyz|~}~zx|~|z{}|{zyyyyyz|}|}~{zyyyyyzzxwx|~}}zz|z{}~~~|}}}~{y||{||||||}}z}zy}}{|~~|{{zzzz{~|y{~}}|{zzyxvuyzz}|||yz|~~~}}}~~{}}}}{}~zyyyyxyyz|~|~zy|~{xx{|{yxwwxxy{}|~}|{{zyyyyxvv{|}|y{}{{~~~|~~||}|xx||zzz{{||}~}{xz~~zxz}~}{zzyyyz}zz~~}||{zzyxvuuz{xz}~}}~~}}~{{~~}}}{||zzzzzzz{|~{|~zx{~{vtvyzyxwwwxy{~~|{{zyyyyywvw|}|~zz}z{|~~~~{|}{{|{y{}|||}}}~~|}~zy}}xvy|~~|{zzz{|~z|}|{zzyyyxwvw{|{~}}}|}~~}}~{|~}~~{}}{zzzyyyzz|yz{z}~yuvz||{yxxxy{~|~}||{zzzzzyxx|~}~zy}}zyy{}}}}}~}}}}~}yy~}{{{{|||}~}{|z||wvy}~~|zzzz{~{z|~}}|{{{zzxwy|~|{||~}|}~~~~|}~~}{}{zyyyxxyyz{~y{~zz~}wtux{{zxwwxy{~|~}||{zzzzzyyy|~}}~zy}}{{|}~~~}|~~}||}yy}}{{{{||}}~{~|y{}xvw{~~}{zyyz}|z}~}|{zzyyyyxwxz|{z|~}}}|}~~~{|~~||~|{{{zz{{{|~|y}~zx{yusvy||zyxxy{~}}}|{{zyzzzzyz|~~}~|z|~|zz{|}}}~|z|~||}{z}~|{{|||}}~~{||y{zwx{~|{{|~}{}~}}}|{zzyyyyyxxy{|zz}|}}{{|}~~~~|}}}|{zzzzzzz{~zy}|yy}~yutvy||{yxxy||~~~~}||{{|||||}~}}zz~~{yyz{|}}~~{{~~||}}z{|{{{{{||}}~~|||}ywy|}{{{}~{|}||~|{zzyyyyyxyz{{xw{|}}|}~}~~}{{zzzzzzz{}|z||xy}~ytrtwz{zywwy{}{~~}|||||||}~~{z|y{~|zyy{||~~|xvwzywuttuvvwxxyz}zz~|zz|}|zwutux|~{xwz}|yx|{w|~|}}|~~}}|zxxy|~{z|}|ywwwwxxyyyz{~~|}{xy}|zyz|~~}{zz{~~||~{{~}|~~{|~{{~|~~~~~~~}}}~||~~}zyyyyyyyzzz{~~{|~~|{|}}|{}}~}yx{~|zz{|{wuv{~}}}}}~}|zxx{}||}}|zxxxyz{~~}}~~~}}~~yvttvwyxwvvwy||zwxz|}~~~{yz~~||}|}~|{zyy{~}{zz{zyyz{zxwvvwwwxxyyz|~||}~~}|}~~~~}|}~}{z{~~~}{wuwz}~}|{{{{|||zyxy|~~|||}}|{yyyzz|~~~}}}~{xvuvwxxxwwxz{{yxxz|}}~~~{yz}~}||}}}~{zxxy{~~|zzzyyyz{zywwwwxxxyyz{|~~}}}}}}~~~~}}~}zz{{zz|~|yvvy|}}|{{{{{||{zyy|~~}||}}}{zyxxy{~~|{{{|}zxwwwxyyxxxz{|zyyz|}}}~~~|yy{|}||~~~{xwwxz}}|zzyyz{{{zxxxxxyyyzz{|}~}|||||||}~|yyyzzz|}zwwy{}}||{{{{{{{zyz|}}|{{|}|{zyxxy{~~|{zzz|~}{yxxxyyyyyyz{{zyy{||}}}~~}{yz{|||}|zxwwx{~}|{zyyz{{zyxxxxyyyzzz{|~~}}}}}}||}}~}zyyyyyz}|zyy{|}||{{{{{{{zyz|}}}|||}}|{zyyz{}~}{zyzz|~|zyyyzzzzz{{{zyz{|}}}~~~~|{{|||||}~|zxwwxz|}}|{zz|||zyyyyyzzz{{||}~~}}}~~}||||}~|{zzyxxy{}~|{|}}}}|||{||{{z{|}~}|||}}}||{zz{|}~~~~}{zyyyz|~~}|{{{{{{|||{zz{|}}}}~~~~~~~}|{{|{{{{|}~}{yxxyyz{|~||}}}|{zzzzzzz{{{|}}}}||}~}}|||||}~}{{zyxxxy{~~~~~~~~}}||||{{z{|}}}||||}}||{zz{{|}}}}{zyyyz{}~}}}||||||{zz{||}}}}}}~~~}}||||{{{{{|~}|zyyyyzz{}~}}~~}|{{zzzz{{{{||}}}||}}}}||{{||}~~|{{zyxxyz|~~}}||||{{{{|}}|||||||||{zz{{|||}~|{zyyyz{}~~}}}}}}|{{{|||}}}}}}}}}}|||{{{zz{|}~~~|{zyxxyzz|~~~~~~}||{{{{{{{|||}}}|||}}}|{{{{|}}~}{zzyxxxy{}~~~}}}|||{||}}}||||||||{zzz{||}}~~}|{zyxxyz{}~~~~~~}|||||}}}}}}}}}}}||{{{{z{{|}~~}}|zyxxxyz{|~~}|||||||||||}}}|||||||{{{{|}~~}|{zyxxxxy{}~~~}}}|||||}}}||||||||{{z{{|}}~~}}|{zyyxxyz|}~~~~~}}|||}}}}}}}}}}}|||||{{{{|}~~~}||{zyxxxyz{|~~}}|||||||||}}}}|||}}|||{{||}~~~}|{zyyxxxyz|~~~~}}}||}}}}}}|||||||{{{{|||}~~}}||{zzyxxxyz|}~~}}}}}}}}}}}}}}}|||||{{{{|}~~~}}|{{zyxxxxyy{}~~}}}}}}}}}}}}}}}}}}||{{{||}~~~}|{{zyyxwwxyz|~~~}}}~~~}}}}}}}||||{{{{{||~~}}||{zyyxxxxyz|~~~~~~~~~~~~}}}}}|||||{{{{{|}~~~}}|{{zyyxxxxyz|~~~~~~}~~~~}}}}}}}|||{{{{||}~~~}||{zzyxxxxyz{}~~~~~~~}}}}}}}|||||{{{{{|}~~~}}||{{zyyxxxyz{|~~~~~~~~~~}}}}}||||{{{{{||}~~~}}||{zzyyxxxxy{|~~~~~~~~~}}}}}}}}|||{{{|||}~~~}||{{zyyxxxxyz|}~~~~~~~}}}}}}|||||{{{{||}~~~}}||{{zyyxxxyyz{}~~~~~~}}}}|||||{{{{{|}~~~~}}||{zzyyxxxxyz{}~~~~~~~~}}}}}}||||{{{||}}~~~}}||{zzyyxxxxxyz|}~~~~}}}}||||||{{{{||}~~~}}||{{zzyyxxxyyz|}~~~~~}}}}|||||{{{{||}~~~}}||{zzyyxxxxxyz{}~~~~~}}}}}}|||||{|||}~~~}}||{zzyyyxxxxyz{|~~~~}}}}}||||||||||}~~~~}}|{{{zzyyxxxxyz{|}~~~~}}}}|||||{{||}}~~~~}}|{{zzyyyxxxxxyz|}~~~~}}}}}}|||||||}}~~~~}}||{zzyyyxxxxxyz{|~~~~~}}}}|||||||||}~~~~~}||{{zzzyyxxxxxyz{|~~~~}}}}}||||{|||}~~~~}}||{{zzyyyxxxxxyz{|}~~~~}}}}||||||||}}~~~~}}}|{{zzzyyxxxxxyyz{}~~~~~}}}}||||||||}}~~~~}}||{{{zzyyxxxxxyyz{}~~~~~}}}}||||||||}~~~}}||{{{zzyyxxxxxxyz{|~~~~}}}}}|||||||}}~}}~~}|}}}|zxxxz|~~}xsprw~|{~ytu|xtrrsv{}}zwvyvtx|}}~~{xzyvvwxxyy{}vx|}|~{x}}tt}urqqqqrsw|z|ut{|{}st|zxxzyxx}wvwtz~}}z}x||z|yz~~~uzyw}|xy{}{}z{}|}syru|w~snou|xrnuxz}zz~{xx{}w}{{}{~zty}~z~}}~}u}vvyw{{|ysyw||~xpqs|{t|~~|zyywwxxuxpouxv|t{x|~xvyzyxutxytv|uyyx|vt{{{x{yqt|ps|{{ou{|yuuw}}~|}}}|yyyv|wuqonxzw{z|z~s~{~xwxz~w{x}qyo~tspprrwuit|suvy{}qw~|uoytruw~pzsyty}{{v}wyzwmgnvzpmmqtvlx{zymqlqwwoy}v{~w{}|y}|y|wwml~|zzz~{}|nkjmus~}{qppuzymrtitzrtzrt|}~t}ps}|zzpk}x}|~x~vqjeeiswzvw|w}xupzxjyxu~|sv~}p|y~z}znxow~vwy}zw~|||~|tqoqvsxyyv~uuxvv|po|txqu}|y}yxp~zv|vxxqxx}y|~{~z}{{}||tuqyvtwvsuxrxnm~vvz{ty{trz~ovzz~x~ylr{xs}y||u~}{{{zvuspn{~|{rsyvuqwovwosxuy{vqz{{wv|y{zux}lkxyvz|{~{}}v~}}xyrlslz|~||vvuolupvxoqyqs}zuv~||u|ytyw{wy|nlrvuz{}|{}zumrnt{ww}wzurkumo|srusqx|xs{x~pyzzxxz{|wy{pu{uz|{~}~~{tptpnzx{|uwyvposlwwrqrkt{wqyyzxx~~{vxtvyx|}}}{}|~zxuorhquz||wywwmulq|truon{{w}wy|wy|ruwxz~~~|~~|~|z}uutpqlgwr}zz}|utwlyyvvow}y|yu}z{xt}vowzuz~}~|{}|x{xutqoskoyxy}~vxup}zvvvpyyutz{z{xyt|}uvyywx}}}{{{{{yzxwvruox|}y~|txtqwqvuqwzzv~|s}|xy}|vu{ytuywx{}{~}~{{}{}~z{xwsrul}{}z~}{qzss{vsutqxzyv}szyv{zts|zsqvxy{|{~~|w{wxspoup{|}{}y}ryuq{vsurnx}yt~~~sy~~xwyurrzyqrwz}|~|ryxtronso}~{}y}sysp}xpsqrz{yr{}}qy||~yw~xvru}ytu{~~~~xqwusqopsmy||{pxqu}vpqosy{wu}{t|}zywux~|vw|~|z}uqtqsqonpr}}}}}ypxqy}truqv|~yy{zz~wvt{~yux|~~|y~{}opsrqqmonx||~vxys~vuut|y}x{~y|uss||vuy}~~~|~|}}}z|z{wmrrrqmnrp~u|uz{vvsx}{x~zw}}~zzwtrw|zvxz}~}||~|{~~z~z|qsursspsq{~yyxu~vuusz}~w|}t{|~|w{urs||wvy{}z~}{~}}{~{xrvtssrrrs}}u|s|yutry{~xx~~vx|}|}x}vsqy|wtwy|{{~}~~}}{|{rvtttporqzv{tzyutrx{~yu~vu}}{}x{vsqxzvtwxzz}~{|{|rvutrqoqo|{{wzvx|utsw{|{t}~xt{}~z}{wyuru{yuwyz{}~|z}w}vsvusqnont}w|w{u~wutu{{}vx}|uy|}}x~xuu|}zxy{}}~~|{|yyzqutsrolnn|v{}w|x{{wvty|~}v~zw}}}z|wty~|wxz||~~}{wzvztptsrpmlls|tz~x|xvw~~zyxz}~|{}yut{~zvxz|{~~}~{||xxuyprsrqolnnxzx~~}}{{xvy~x{|w|}|}y~wtu||xwxz{||{~z}yxwuxqttssqnop~{}|~{|zxw{~w|{w|}{}~x}urt{|wvxz{|}~{yyyvxyquuutrpqs{{~{}zxv|~~v|~zw{|y|}x{urt{{wvxz|||{yyyvvyqttttroqs{|}{|zxv{~vz~}yvz|xz}w~{tqsz{wwyz|}}|y}xxutxpstssqprt||}{|zwv|~}vz}|xuz{~wz}v~|urt{|yy{}~~}w~~vwrtvosssrqpru{z}z~yxw}}}~u{}|xw{{~x}|x|vsw}}{{|~}~~|v~}~tvpttorrssqpryzz~y~zxy}}w~~xy|~}y{{zutz~|z{}|}|xv|~xstovqpsrrrpqs{~~|~{y}zz|x||zzy}wtu{}{z|}~|~}}}|tz{}tuppuoqrssqprw{~}}{{x}~yy||}w~{xzusx}|z{|~~~|~{~{wu~{}xsunurqssssqrt||{~zy~}{x{{xz~w}~vsu{}zy{|~~}~{|~|~{u||}tvptvqttttrqs{z~}{}~}w}}{xzz}zw~y{wtty}{yz{}|}|~}}uz|~uvrrwqstttrprxy~}{|w|}|xyz|{v~zyytsx|{xyz{{~}~|~wx{}vvtpwrstttrpqtz|||xz}|yxzz}v{}w|vsv{|yyz{|}||xu~||ztvouurtttsppr{~w~|~|x}|{x{z}xxy|xuu{}|zz{}|~}~~|{ty~z|tvqpvrsttsqopuv}}~~x{~}zz{|}w}~y}wtx}}{z{||~~{{}~wt|{zysunstqstsrpoqz}u~y~~}z|{~zx{|zvuz~|z{{}|}{~z{tv}yzutrmurrsssqops~yx~}z~~|z|{wzy}yuv{~|{{|||}||}zzsv|wystqnurstttsqrwy}{z~}{z{z~v{x~wtv|}{z{|||}z}|z~xrx{xysuppvstuvusrtzyzz}}zz{z}u{~x~~wtv{}{{{||}}{}|z~ysw{wytuqovstuvutstzz{z}|zyzy~}uy~w}~wtv{}|{||}}}{|}y~xrw{vxtupnustuvuusu{{{y|{yyzx~|tz}w}~wuv|~}|}~~~~z}|y~}wrwyvwsunnustuvuttv|zyy|{yzzy~{t|}x~xvx}}}~y}zy~~{uqwxuvrtmoustuvvttwyy{||z{y{yv~{y|wvz~}~~~~}y}xz~|xqryuussrkrtsuvvutvz~{z}}{z|y}~vyy|zvw|~~~~}yz{w{}{upuxsurtnmtstuvvutw~z{{}}{{zzzu}|x}xvy~~~~~~~~}x|wy|{wqqxttssrkrutuvvvuv{~|z|}{{{y}}uyx|yvx|~|}|~{}swvu}xsxxuvtunousuvvvuuyyz|}}||z{~vv~yy{wvxz~}{puxmnqmpoqljrrstuuttw}w}|~~~||wvvz~xy{z{{|{{}~|~}ywysw}{uw{uvvwsowxxyzzyy{~~|}|}ywyyyzyw}zt{}w}~wtuy|~vwrry~{y|wrrppglqpqrsrqrv}zu{~}~~ztrux|~}|zz}{{}~|vwwtx|}zv{|wxxysqyzz{|{zz}|~~}xwyyxyxx}yt|}w~}wtvz|~{svrry}}zz{wwvtkotstttsrsw}ztz}|}{ztruy|~}{z|{z||wvytw|}zwz~xyy{wqy{{|}}|{}~~}}}|wyyyyyw{|uyx{zvvy}~ttuqv|~|z}xxwwqmtuuvvuttv|uz||~~zz|}wssx|~}{{}~z|||}twwty}}yw||wyz{us{|}~~~}}|~|{}~~{wyyyzyw|{u{~w|~yvw{{qtusy~|y~}wxwxqnuvwxxwvvy~v|||~~~}y}xz|vsuz~}{{}}z||~|}tvyvz}}zv{|wyz}wt|~~}|z{}~~|wyzzzyw{{uz}w{~ywy}~xptvu{|y~|wxxyqpwxxyyyxx{v|}~}~~~|}w}}vz{vux~~|{{}~|z~{}}ysxxw|~|xw|{wz|}uv~{}}zz|}yxyzzzwx}xv}zw}|yx|~{srxvy~~z|ywy{wouzz{{zyy{zyz~}}~~{~xz~ww{xuw}~}{{}~}z||}|~{twzwz}}yw||xy|~xv}{~||zy{~~{xzzz{xx|yv}{w}}zy{~}{sqwvx}~{}zy{}yqv{{||{zz{yxz~}||}~~{~~w{}vx{xvx|}||}~}z}|~}~zsxywz}|yx}|x{~wwz{|}{yxy}~zyz{{zwy|wx~yx~|yy|~yqtwux}~|{z{~wtz}}}|{z{}~u~{z~~|{{}~||{y~zv{{wvy}}|}~~{|vw{wx|}zy~{||w~}y~{~}zxx{yrz}vqqxxt{|w{~{yz~~{}upvutx|}|~z}xu{~so}|uw|}|zxxy{{}|~z|zy}{xx{{tw}wzzxz{{|wqtwsuxyww||xz~yv}~}}{~{~}zyy{wu{zuxtqwwrwzwuwz|~~~}|}}}{|xtzyx|}|~{xwx|~w~ttwyxvutuw~|}|||z~{{~|zywuy|{{{{wpqurruvuty|wx|{tz~~~~~~{}~{zz|}z{w{}~}wy{uuzyvvy{}~~~~}|{|}|{|ux|y{~}vz{zyyz}|}vxzzxvuuvz~|}z~y{~|}~{yxwwx}}||{vptvstwwuv{zvy~xu{~~~{z}~}{z{}z{|z~|~|wzztw{ywx{}~~~}|{z{|||{vz}{|~ztyzzz|~{|vxzzxvuuw|~~~|~z}y{}~{ywwxy{}||||wquxuuwwuuz{vx}ytz~{z|~~}|{|~~y{~{}|wz{uw{zyz|~~~~|{zz{|}~{w|~|~~|~wsxz{}z|wyzzxwvvx~}|}{}z~~y}|ywvwy{~}}}}{urwxuvxwuw{yvz~vu|z|z|~~}|{}|y}|~zx|xuz|zz{~~}~|{zyz|}}~xy~|~}}{|suz|}~}xxz{zxvvw{~~}}{}~{}z{|ywvwy{}~}~~}xswyvvwwuvzzvx~xu{z~y{}~}|{|}y~~}|z}zvz}{{|~~}~|{zyz{|}~xy~|~~}|}{}tv{|~~yyz{zxwvw{}}}}{~{~z{|ywwwyz}}wsxyvvwvuw{yvzwv|z}z|}}|{{|{y}|yx|}|{|~}}}|{zyz{{||w{|{}}|}{}yuy|}~}zz|{zxvvx~{|}}}~}}z~{yxwxxz{uwzxvvvuuz{vx~{uz~{z{}}}{zz|~z||~~~y{~}||}~~}|~~}{zzzzz{}yx}}z{|{{~}{ww|}}~~{|}{zxwwz|{}|}}}~{{}{yxxxxzzuy{wvvvuv{zvyzv{{z{}}|{zz|~z}~}~}}~~~~}{z|~~|zxvuuuwz||{y~zz{zxvuvy|y~~{x|~}|z~}xrqz|{vx}zxxxww{|xy~x{~~}{z{|{zyy{vsz~}zx{}{|}ww|}|{|~~~}{|~~|zywvuwz{zzzy|x|~vuvvutrrsx~|vx}}zvz}}~}xsqqu{~}}}{vsxzwuvuuw{zvyyuy}|||}~~|||~}yurx{}}z|zuw{{zz|}}}}{z{}}|ywvuuwz}}}~wqtz~|vuvvutrrtx~|vx}~|zx}~}}~yurqruz}~~|wtx{xvvvuw{zvx~yty||{}~~}||~~zxvv}|~~{|{vw{|{||}~~}|{{}}{ywvuwy}}~z{}tpv}{}vvwwvtssuz{vy~}}|zz}|}~zvsrruy~~~{vuz{xvvvvx{yvz~wu||{{}~}||}~~{yxxz}}}|}yvz}}||}~~~}{{|}|zxvvwy|}}}zx|~xqt{}~yvwwwuttux~~ww}|~~}|z~~|}~~{wtrruy~}xuy|ywvvuw{zvx~yuz{{{}}}|{|~}~|zyxy~}}~{wy}}}}}~~~}{{|}|zxwvwy|~}~~~||zw{~yrt{~{wwxwvutuy~}ww~|~}|z~~|}~~zwtssux}}xv{|ywvvvx{yvzxv||{{}}|{{|~|~|zxwyzx{~~}}~~~}|{{}}|zxwwwy{~|~}|{}}xy~~vrv}~zxyyxvuuw|zvz~}~}zz}~|}zwtssux|{wy}{xwvuw{{wx~{vy~|}{|}}{{z||~~|ywv{~yz~~~~~~~}|{|~}|zxwwwy{~|}~}|{{~{x||tsy}zzzyxvuvy~~xw|}~~|yz}}|~|yvtstux|zw{}zwwvvx{zwy{vz||{|}|{zz|{~~|ywv|z{~~~~~~}{{|~}|zxwwxy{}||~|{z{}zx{}uty}{z{zywvvy~yw|~~|yz}~~|~~~|ywuttux{|xz~|xwvvw{{wx~}wy~|~{{||{zz{{{}~}zxvz{z~}{{|{yxxxyyz{}|}|~|z}~{{{{~~}|x~|z{||zyxy}~yz~~~~}y|{yxwwx{{{zzzzz{{|}|}||}~|||uqy|vuvwxwvvx}}wx}~|yy||ywvwz{zyyyyzz{|~~{|||{~}}~~vw|zu{ywxyyxwwy}|vv~|tsx~|zxxz{zyyyyyz{||~~}~}}}|}ztwzuz~wuvwwvvvw{~zuvztu{|zz|}|zzyzzz{|~|z||{}|||~~z|wty|xyxxyyxwwy|~zux||}{vrt|~{zz||{zyyzz{|}~}}}}|||}y|~vu|xv~|vuvwwvvvx|}wtz~|wsx~|{|}}|{zzzz{{|z{~~{}~|{{|zy}zuwy{|xxyyxwwx{~|vv~|z|~}{xrsy}{{|}|{zzzz{{||~}~|{{}}yzxuzzv}xuvwwvvvx{}xtx}}}wsw~}{|~}|{zzz{{{|{{}~{}~|{z|~~zy}{uw~y|}yxyyxwwx{~|vv~{z|~}{xrsy~||}}|{zzz{{{}~|~}}}{{{~|y{~wu{zx~wvwwwvvvy||wtz~|~|vsx~}}~~|{{{{{{|~}z{}|{~|zzz||yzxtx~z{xyyyxwxz}~yuy|z{~~}{uqtz~}~~|{{{{{{|~||~|~|{{}~z{{uw}~x}zwwwwwvvx{~zuv}||~ytuy~~||{{{{{}{yz}~{|}{zy{}~{y}~wuz}}zyyzyxxx{~}xv{~{y|}ztruz~~~}|{{{{{|{|}~}}{zz}}y{zux}~zzwwxwwvvx|}yuw~~||~ytuy~}||{{{|}{yz|~{|}{yyz}~zy|~wuz}}{yyzyxxx{~~xvz}{y{~~}ztqty}~||{{{||~{{|}}{zz|~}zz|vw|z~}xwxxwvvx{}{vu|~{z}~zutx|~}||||||}zz{|{~|zyy{}|xy~zux}|~zyzzyxxy}~{vw~~|yx{~}{wrrw|~}|||||}~{{~}}|zyz|~{xzyvy~z|xxxxwvwx|}yuw~}zz}}xtty~}}||||}}yz|{{~~{yxxz|zwx}~yvy|~zyzzyxxy|~{vx~}{xwz||zvrsx~~}}||||}~{|}||zyy{|zwx}~yvz~z}xxxxwvvx{}yuw~|yx{~}|xtu{}}||||}|z{~~{{~~zxwxz{yvx}}xw||~zzzyxxxy|}zvy~}|yvvz|{yuru{~}}||||~}|}||~{yxxz{xvy}}xx}|{{xxxxwvwy||xv{~|zwy|}|zvtx~}}}|||~{z}|z||ywwxzzvvz~{wz|{zzzyxxy{}|wx~}|zvux{{zwsty~}}}||}|}||{yxxz{yvw|~zx|}{}yxxxwvwx{|yvy~|ywx{}}{vtw~~}}|||~{z||z||ywwxzzvvz~{wy}|zzzyxxy|~|wx~}|yvux{{zwssy~~}}|}~~{|||~{xwxz{xvy}}yw|}}|yyyxwwwz||xv{~}{xwy|}}zutx~~~}}|}~zz|{z}~zxwwy{yvx|}ywz~{zzzyxy{}~yvz~|zwuw{||ytquz~~}}}~{z|}|~|yxxy{zwx|~zwy~}{yyyxwwy|}zvw~~|ywx{~~|wsty~~}}}~|yz|}{||ywwxzzwvz~|ww{~{{{zyyz|~}xw|}|ywvx|}{xsquz~~}}~zz|}|{yxxz{ywy}~yvy~~{zzyxxxz}}yvx~}|ywx{~~{wsty~~~}}|yy|}{|}ywvxzzxvz~|xw{|{{zyyz|~}yw{}|ywux{}|ysqty~~~}~{z{}|~|yxwy{zwx|~{wx}~}zzyyxxy|}{ww|}|ywvz}~|xtsw|~}}~~zy{~{{}~{xvwyzyvw|}zwy~}|{{zyy{}~{wx~~|zxuuy||{vrrw|~}~~zz}||~{xwwyzxvx}}ywz~|zzyxxxz|}zvx~}{xvvz}}|wsty~~}}~}zy|~{z}}zwvvxyxuw|}zx{~}{{zyyyz}~{wy~|ywtux{|{wsty~~}}~~{{}~{{~~{xvvxzxvw|}zx{}|zyyxwxy|}zwy~|zwuuy|}|xtu{~}}~}zz}}zz}}yvuvxywuw|}zy}}{zzyxy{}~zxz}{xvsux{|zvsu{~~}}}{|}z{~}ywvvxywux}}zz~~~{zyyxwxz}|yw|}{xvtvz}}zvux~~}}}{z|~{y{~~{wuuvxxuvz}|y{~|{zyxxz|~|yy~~{yvtswz|{xtty~}}~{{~~{{}}zwvvxywuw|~{y}~|zyyxwxz|}zx{}{xvtvz}}{wuw}~~}~|z{~{y{~~{wuuvxxvvz}|y{~|{zyyyz}~|yy~~{yvttw{|{xtty~~~~~{{}}{{~}ywvvxywvx}}zy}~|zzyxxy{}|yx|}zxutw{}}zutx}~~~zy{}zz}}zwuvxywuw|}zx{~|{zzyz|~~zx{}zxutvz||yuruz~~{y{|{}{xvvxzxvw{}{xz~|{zyyy{}~{xy~~|yvtvy}~|wsty~~|yy||z|~|yvvwyywvy}|xx|~|{{zz{}}yx||zwttw{}|xsruzzy{~||~~{xvwyzxvx|}zxz~|{zzyz{~~zxy~~{yvtvz}~{wstx}|yy||z|~|yvvwyywvy}|yx|~||{zz{}}zx|}zwttw{}|ytruz~{yz~|{}|xwvxzyww{}{xy}}{{zyy{}~|xx}|ywtux|~|xtsw|~zyz}{{}~zwvvxyxvw{}zxz}|{zzz|~~|yz~~{xustx|}{wssw|~zy|}{{~~zwvvxyxvx|}zx{|{zyyz{}~{xz~{xvtuy|~|xtty~~~zy{|zz}~}ywuvwxwvx|}zy{}{{zyz|~~|yz}zxustx|}|xttx}~~{z|}zz}}zwuvwywvx|}{z||{zyxy{}~{yz}zxustx|~|xuvz~~~zz||yz|~|yvuuwxwvx|}{z}|{zyyz|~~{y{|ywtrtx|}{wuv{~~}zz~{z{}~|yvuvxxwvy}}{{~}{zyxxz|~}zy||yvtsvz}~{wux}~~|z{}zy{}}zwuuvxwvwz}|z|}{zzyy{}~}zz~~~zwursw{}}yuuy~~~{z||zz|~}ywuvwxwvx}|zywwy|~}zww{~~~~~ytqsy}xvxwx|~}{||yvuy~wqqx|{vpns{|y|yvvvxz|}}|zywqnmnquwx{|wt~~}zzz{zyx}|zyyzxqlw}}~{{}}wwxwtww}{z}|{|{wqz{z|{~~w{x{}wxz}ysrx{|vw}|rwxu{qnq}}}|{||z~zx~|{wwlkks|wurtwszx||~}~}}{{vzuqmoz||zy}w~{rurx{||~~|}y|xxu}~~~z~~}w{zs{z}|wzux{xztxyvuwrw~x}~{|{{y{xuvoqxsw~vwwtx||}z~{~~yx|ttzru{y}~yz|}w}~w}}}{v{{w~z~z}{}~~{}||~~tz{u}}~z|zu}|w}x}xuwut|zy}|~|{z|y|zw{ywvswws{xpurrx{~~}|}|txukruv}}{{~xv|w|y|{x|{{xz~~vw|y~{x{{w{~ws~z{|y~{y~wsxtvwwx{z}|x~}{wy~}yzxxxwu|v{wvzy~|zz{z~z~~{}}|uzzqtxw~|~y{~{yv}yz|{{z}~}{|zzz|zz}~~|v|{w~|{|{}}|y~w{ywzuxywv~{||||tvyvy{~yxzvquxs{~tuvx|~x}}~~{~{uqtnq{ww}}|~}{}y}|v{{z{y||~z|~{xx|z||y|~|y|z}~{{}zvxys|~stst{yx~|yu{z||}~yxu{zuw}}s{~~}~}~{~z}}|y{w|}wtzzyzszz{}~~}~{~y{~xz|{x{{z|zz~}|{|y|y}}~xxytyywx|x}~xs}}{}zz|~}~{xyrw~vtvy|{xw}xyyx}zw|{}~}{}z{zy|xuytruu}~||~{x{ww}vwt{}zz{~{|~{~~~w|~~~|{y}~|z}}wyw{zuwwy|{{|y~~{}~tv~||zy|x|z~{}~xz{~}z{zw{}}~}}{{zx{uxtwwy~|zyzy}|{z~}||}z}|~{~{|}w}}srqyy|{suuy~~xw~{rzzztx{{{}{z~|~yx|z}{}{zy}z~~~|||wqy|yv{~~xzz}~|{}zuwzpssqp}z~~z~zyw}~|w{~~}~}|}vzwxy||zx}y~}}y}|zwvyxvz~yrw}|{|}~vv{}wqr|~{st{{w|{w|}zy|y}{|}~|x~{~yzwt{zyuv{}z~}~ywwxu{|y~|v{~x~}x{uwxxzyz{v{xx{wz}}|y{~~~{~x~sqrp{||~}y|y{yzx}|{x||}{}~y|~}z{}{}~yzruz}y~~~~~vyvxywuyy}~~{y}}}~}}ztywz}{{x||}z}{yw{~z{zz|~|}z{uvuyz{{{}{yz||z|{z}|~zz}v~~{~~|{y|}x{qy||~w}~~{yyz||ww~{||~z~}zyzzrwvv~w{}}~~z{{{y{|}|{z}}{x{}{}}~|~}zwwuxyy{z~|z{{x{wy|{}}{~y}}x|z|}{}uu~}{yx}~~}yzv~vvyzx~z{}}~z~~z||wxvt}yyz~~{}||}~|~~x}{zv{z{y}}~}}|ywwxywzyz~~{y{y}x{}~|}|yz}}~z{{z~z~~uv|zvv|{|}~{zzy}xv{{{y{}z|}}vwuu~vuv}~}}|{||}|z{~~zx~{xyw}~|}zyx{yywzz}w{|}}}z}}{|{z}|y{{~~~}z{{y~|}v}|yvx|wy|{{yxwy}x~|x}{~|{~yxvt~ztsx~{~~}~~{}x{|xwv{}z|~|~~{zy}{{xyy|wx|}{|~}~z|~~yz|}|~}|{z~~v}}zwwzttw~||||zzwy~{{x}}~|}~{yxu{sswzwz}~{}x}{xxu{zxz{}}{|||y{||vw|}x|~~|~}{z{|z{}|{|~~|}{{}}yzyzzssw{xxz~}|yx}~|y{~}{}||{~~{zxyyutzzvz}x~|}|}ywv|yw{~}|zxz}ssx~{|{wuwy|y}|xvuywx}zxzx|}{}|y||}z{~{{}{vrz|~{|{xv||zwvxxx}}{wz{yuttzvu{ww|z{~}~~}s~stx}zw~}}yy~~yxy|zs|v{|uv}{{zvx~}wx}v}~~}z{z{~{|x~{{xsvz~{w{~~~|zyv{ywx~}||wwttsqy{wy}{|}{}~{{|~}ywyy|v~{{}~{}|t}~vwvxzvux~}{|x}}z~}}~~z}~y}{{zwu|zqxxqyxw~}|z|}w{~vz}{y~|v|x{{|}ys{}x{}|~y~wwvu{}~~~}|}}}{uz~ry~wzxtz~z}~~}{~|y|x~{~||~~}~xw{wwyz|}v{v{~{{{~zz|{~|y{v|}|~~{{u{xw}y{xvy}w|{{}~{}{x|}~~|z}}|{|zzz|}vxzqrutzz|~z|}~z~{zw|}~|}|{xz|y{yutwsx{}~~zy}zx|}|~|}}{}}}y|y~||}t{{||xxzy|{~~|w~{x~y|w}}z}|y|}wz{|{}x||sx|y{x~|}{~}~~{{z~xy}x|~y{{w~wywv{zuy|x~zv}||z}ww{w~}y~{|z|{y}uryvwwy|~}~}~|}~w~z||||~}x}{wsupwvtww~{}~}}~~z~z~}~}{{}|}|v||{xvzxxzv}}x|y{xyw~zzz}{~{~}|}}~z~{w{zzyu{zyw}y}~~~}~z~~x{y|}}~{yz}wwv}}xv~}}{ry}~}{}zx{vw}yzx{~}z}z}{vywyxxzwzx~|y|y~~}z~}y~|||y~zuyvu~zrtqx{yy~}~}y|y~{~}}}~{yz~zx{{twvyztxz{~}~|zzx}~zzz~~~|}}~xz}|~vxyxuuwty{|}~}{z|z~{~|~~~~y{xwz|y|}xs}}{}yyyz|}~}z|v{{vvw}~}}x}ywwuyvyvtx{~y~}|z~yzz{y~~~|{|{ytxz{}y~|ow}~yyxw{{xrqzpx~v~pu|z|yy~}}w{{|{v}us}|yxtxy|zw{}|}}ux{}|y||zxq{vtrxxzx}|tzx{wtuzv}}|{z{}zyvxy~y{z}{txy|wy~~wtguu}}svxxywuxuylosw}tptwt{|{~{~z~ysvquvxzz~}}zxyz}|{otms||ypuvsvqtjsuoxvx{|qtz~zy~xz{}u~rtwwzy}~}}zxzysnwtt}{ps|srqwtwwkwyv|{ru}~t{z~vzy|~{t~roz|zw~~y}tusownu~~zu|}rptxryqpxvtwxqy~xv{}v|y}~~{u~xmqy~~~|~z~~~~yrsrquoy~}}wzrttvnvnr{srwtsyxy}}t}y{{|}wz~tmw~|~~z}z~~~tsunstv~zy}stvwpptmvxttyrv}{v}}yx}~z|{}}t|{tt|}z~~{|}~z}wvrmvu|v|zsvuwnxrq{trwvqwyx~{~v|zzxu}zsy}|~{}~~||zzyvsnstu|{wwwwqrwmvystums{}x|}|wx{wzt|{tsz{|}~|~~zy~{tsoowp{~z{vvwwowlqyutupp{~wy|t~y|{yszvqxz|}y~~~zw{usqosox~~txvyqwpo|wqtqq{~xv}t~z}zzty~xrw{{}~{~|~y{}wutotns~|vyy{quso|yqrspy}zv}s{~|y|zwv{ruyzz~~}~}}}y}xvtpoqo|y{{||wqyox|srspt{|v{|ww~z~~yxt{}wsxxy{~~}|~}u{{vuqmqlx|{{||rwtr|wqsrox|zv~~t}~{y{ytu}zuvxxz~}~}|~zu{xwsooqn}x~}~|yrzov|urrps{}xy}yv~~~y|zxryytwxz|~~~~~~ux{vuqnrns|x||}vuxp{|tssow||u{~uw}y~yvs|}vsxy{}~~}~uyxusqornw}z|}uwup}ztrrqy|{u|tz~z|wut}|uuyz|}~~~}~~~sywurpnqnz}~|}svur~zsrqr{||u~}s{~~y|wst|{uuzz|~}~}~~~~|}rvusrpmqo|~}~svvs{tssrz||v}~u{~x|wts{{vvz|}}~~~||{|qtusqpnrq}}~twvr{ttrrz|}v|}u{}x|vss{|ww{|~|}~~~~|zzzputsrpnsr~~sxut{utrt||}v}|u|}~|x{vrt|{vw|}{}}||xzwotsrqpost~szsyyutqw}~zv~xv}}~zzxurv}zvy|~~~}|y{v{rqtrrqorsz~~zuys~wtss{|wy~uy~}}~x}~vtrz}xw{}|}~}}yxvypssrrqosstyuw|vtsw}}|v}~~zv||~|yzvru|{wy|}}}}zxzuzsqtsrqoprz}{vztxutt||wy~}uz}}|~x}~vtsz|xw{{~||{zywwxotssrpors}u{u|{vut{}~{v~~~ww}{~yzxurx|ywy{}~}|~{}}xxuyqstsrpnpq}w{wz}wvtz~~}v}~~yv|~{~|yzurv|zvxz{}|}}{wytzssusrpnop{~z{zzxywux~}vz~{v{}||~x}vst{{wwyz|}}~|yxztxvqutsqonotxx}w|wvv~}zw~~}wy|}zz{xusy|ywyy{|~|z}|xwuzrttsrpmop}z{{|z{zxv{~v{~~zv{}z|~x|vsu{|xwyz|}~~|ywytwvqttsromotwz~y~yww~~|v}~}wy|~~y~{zyusx|zwxy{||~~~{y~vxrysrtssqnnpy}x{}z|ywy}yx~}|wz||zy}wtsz}ywyy||}{~~yzzvvrxrstssqnoq}|z~}}{|yx|}xy~}{w{{zz~x~~vttz|yxyz}}}{w|~xvtrwqsssrqnpr{}~~|||yx}~wy~}zw{{yz~x~~vst{|yxz{}~}{~~wz}wutqwqrsssqoqs~~~}}|}zy}~xy~}{wz{yy~x}~vstz}zyz|~~}{~|~vx|wtsovqrsssrpru~~}|}zy}~xy~}zwz{xy~x}~vst{}{z{|}{~~}}uy|~uurpvprsttrqsw~~}~}zy~wz}}ywz{wz}w~}vsu|}{z|}~|}~}}{tz{}tuppuprsttsqtz}}||z{w{}|xyz{}v||xzusw}}z{|~|}|{wt}||yrtmssqstttrru|~||{}~|w}|{wzz}yw~x{xttz~|z|}}||~z|tw~z|usqnuqrttttrtx~~|{xz}|yxzz~v{}w|usv|}{{|~}}{{}~ws|{zyrtmstqstutrru{~}|~|w}|{xzy~xw~x{xttz~|z{|~~|}}}zzsx}y{stpouqstttsqtz{~~}~w{}|xzz|{v}{yzutx}}{{|~~~||~z{tv}y{utrnurrtttsqsw{~}}x{}|yyz{}u||x|vtw|}zz{}}}{~z~|uu}yzwtsmtsrtttsqru}z~~{y}}zy{zvzx~~wtv{}{z{|}}}{~{{~wsz{yysunqurttutrqs{w~x}}|y{z|yv~zzzvuy}|z{{}|}~|~yzsv}xzuurnusstutsqrwy|{z}}zz{z~v{~x~~wuw|}{z{|}}~{~{{~wrz{xysunpurtuutrqt{wy|}|z{z{zv~{y{vuy}}{{{}|~z~y|{tt{xxvttmttstuutrrv}xy}}|z{z}xwy{yvuz}|{{|}|}}{}x}~yru{wxutrmttsuuutrsx{{}y}}{z{y~vxx|yuv{}|{||}}}{{|x}}wrvzvwtupmuttuvutsuzz}}y}|{z{y~~vyx|yvw{~}||}~}}{{|x|~|wquzuvttpluttuvvutv{|}}y||{z{y}~vx~x|yvw{~}}}~~~}{z|w{}|vqtyuuttqltutvwwvuw|}}~y||{zzx}~ux~x|yvw|~~}}~~}zz{w{}{upuxtustomutuvwwvux~||z||zzzx}|tz~w}yvx|~~~~~x{zw{|ztpuwsusunnutuwxwvvz{zz||z{yy~zt||w}xvy}~~~~~~x|wx|{wqpwustsskqvuvwxwvx|{y{|{z{x{~vv~yz{ww{~~~~~}zy{vy{zuoswststpltuuwxxwwy||z||{{zx~{tz}w~~ywy}~~~~w|xw{zwqpwtrtttlqvuvxxwwx|zz||{{{x{~vw~xz{wx{~~~~~~~xzzvy{ysotwrtsuomuuvwxwwwzz{{||{{yy~xu}{x}xwz}~~~~~}~zy{vx{ytoswrssuqltvvwxwwvy~z~}{}}||zy~{u||w~~ywy}~}}}~}|x|wwzzuprwsrtuslrvvwwwvvx|{z{}}||{x}}uz~x|zxy|~~}}~}}x{yvyzwrpvurtvxrv~}}}}{yx{~u|}}~~}|y{zy~}}|y}|x{~{z|vwyxsptwpnpywpuwwxyxwwx}w{{zyvxyyyxv{ztz~x}{xy|}}}}}|{}|~}}{x{ukkq|xuvqopppoqu{yq~z~}~~~~{||z~|yyy~}yw}}xy|}yvzvuxwsnosnmrxyty}{{{{zy{~||}uuuuuuruxrt{vw}zwwz||{{zywz}~}}~{tmnojls{{x~{ywurqsx{st|y~~|||zvtx{}||{}y}~}~~vwytuwuplprnqv|xu|~}}}|{{~|~xxwwvuswxrv{uy~{xx{}}|{zxx|~~zyyunilnkqx}x}|zxvuw{}tw}z~~~||}zxutz|||{}y}~|~~~vvyttutokorpsy~{u|~}}~~xwwwwuswxsv{vy~|yy|}}|{zyy~|}}wvurkhlpou}~y~}{yxwy}~tz}{}~||~}{xvuw~{}{||z~}~{txxsturmlqsqw}yw~}{}|xxxxwttywtzzv|~{yz|}|{zzy|z}yturnhjpps{{}~|zyz}yuz}~~~~|}~~}{ywuv|}}}{~z~}~}uvytstsnlptru||w~}{|yyyyxvtyxtz{v{|zz|}}|zzx{~y}yttrnijpprz|~~|z{~yuz|~~}~}~|}~~~}{xvuu{~~|~z}~|uwytttrnmrtqv}{y|z|{zzzyvvzwv|zw}~{z{}}|{zyx{{{}wttqmjmpns{|}|~~uz}y}~}|}}~}}}}~|zxvtv|~}|}~~~wvzvtutpmptqry~x~}~|{~~~}||{xvyzvz|wz}{{|}}|{zxx}y}{vutplkpont}}~~{tzy}~}||}||~~~~~|zwutw{}~|~~}vwzvtuspnrtpsz}y{~~zz~~~~~}|yw{zw{|w{}{{}}}|{yxx|~~y|{vutpllpont}}~|uzy|~|{{|{|}~}~~}|zxutv{|}~~wvzwtutqnquqryy~}|~}}~{x|}~}{wz|xy~yy|{|}}}{zxx{~~yz|wtsqmkoqnrz}wyyz}~|{z{}|{~|}}~}}}|zxvtuy}~~{uxzuttrooturu}~{{}}{{}xx}~}zw{|x{}xz~||}}}|{zxy}~{wurnmqrpu}~x{|ty|zyz}{{yw{xwwyyyxwutu{~~z||vx{wttroosuru~}u|~vsrsu{{ssz~~~~}zvxxtz~~}}}~~}|{z{~zyopqljjighnpou~}~uy{x~~|z{~{}yywv}wttsrrqppsz}~}|z}}}}x{{wuutrtyzx}u{{{}xtruytnrz}|}||{ytswwx}~}}zz}}|wxsmkjgfjpopx{~vtz{~|{}~|}ystxx}|zxvusrrsx|~|}y}~z{}yvvusty{y|}sw}{}ywx|yqsz~|}~}}|zvtwyy}~~}~yz}}~z{wqnljilqppx{~wtzz~||}~||xstww|~|zxvusrtx|}~}}z~~z|~ywvutuz{x|zryzy~~{y{~xru|~|~~}}|zuuyxy}~}}~}y{}|~~}~xrpnkkoqor{~z}ty}wz~}}|~~{vsvvw}~|zxvtsvz{|}~~|}}}}|}xwvuuy{xytu|ux~~|}~utz~|{~~~|xuxyx|~|~}~{z}|z}~xtromnrqou}z{yt~ywz~}~|~zuuwvw}|~}{ywutwz{{|}~|}}}~}yxwuvz{xz|svztw|~~vv|}{~~|xvyzy|~{~}~~{z}{z}zvtqnpsrpu}zzzu~zwy~~}~|}zttwvw||}}{xvux{{{|}~~|~~}}~zyxvvz|yy~ts}|uuz~yv{|~~}yvxzy{~|}~~}}{y{|{|}xusppttqs{~y~~uxxx|~~}}~}zvrtwvz~|}zxwwz|||}~~}|~}}zxwvx||y{~yqu{vx}~}ww}}~~}|xux{z~}~{{yx{}|~~|wusqruvsu}~z|uyzz~~}}}}}|zxtqtwx}~}{yxy|~~~~~}~}||~}zxwvx|}z{~xpt~yz~}~xw~~}}|{wux||~~}yyxx|~~~{wusrswwtw{{tz{|~}}|~~{zxvrquy{~}{zyz}~~~|}|}||zxwwz}|z~~|tpx}z}~}}}wz~}||yvv{}~~~yxxwz~~zwusswyvv||}~vv}{~~}}~|{yxvspsx{~~}{zz}~~|}||}~zyxwy}}z|~}vpvz{~}yz~{vvz|}|~}||~~~zyzz}~ysrz{y}}y{~ww|}~|{{~|x|}ywvurpty|yty}xwxyz{{||}z||}}|zxvw}}vrz|}vtz~}zy}}yxxxvtuz|~~|~|z}~~zz{{}wrquz}xz~~wrx~xxz|}|{{{}vx~|{zxuuz}|xuv||zyyxy{}xxyy|~|~{xvuuz}}|ux}|~trx}}zxwwvsrvyz}}z{~|{z}~|}|~zvrqtxz||tr|{vwy{|{zz|zvz}||zxuw|~}zwvw}~}||}yyyz~~~~{~zxvuv{|{{uz{{}|tsz|yxxvsswz{}|y{~|{z}~|}|~zvsrtxy{|us||wwy{{zzz|zvy~||{xvw|~~~{xvx|~}}zyzz~~~{}{xvuvz|{~}vx}z|~}~}usx~zyyxusvz{}}zy~~|{z||||}{vsrsx{{yswywxz{zzyz}}wv}}|{zwvz~~~{yxx{~}~}yzz||z~}yvuuw|}|zu{}{}}||}~zst{~zzywutx|}~}zz~}|{{~~||}~~~xtrru{~~ysyzwyz{zyxy||vw~||{zww{~~~|zyyz~~}~~zzz||z}}xvtux}~{v{~|}~~}{zy{|ysu}~{zyxuuy|~~zz~~}|||}~~||}|~{vsrsx~xtzzxyzzyxxy{~{vx|{{zwx|}~~|zzyz}~}}zz{~~{z}~{wutvz~yw}|}}}{yxxz{wty}{zywuv{~|y{~}|||}~~~}|}~||~}xtrsv||uw~yxyzyxwxz}|wv}}||zxwz~|~|{zz{}~{{{}{y|~|xutuy~~zv}~{{||zywwz{xtw~}~~zuw{~~~~~}~~~}{yy~{yz|~|}}{{|~~}wxzy{||||||z|{zyvtx|~||z}~yxy|~zz{~}|{zyy{}zv{~~wu~}|~}|~}zy}|zzyxvx}}z}~|{}~~~}}}}|~xtu{{y~}xzzv||yyzzywvx}~}{|{z||}~{{||{zzzz{{|}}||~~}|yy~~|{|}}~x{xrruy~yw||~}~}|zwvy|}~}{~~ywvw{~~|xvuvy{yx|~}|uv~~~}|{||yutssu||y{~~~}}}|{|~~~~~}}}~~~|z{~~}|~}{z{~{vvwxwwxz~{}{~}uw|~{{|ywvwz~||~}}}{xy~}{zz{~~|x|}xwxz{{{|{}}{}zvvyxuvy|}}ywz~~~~~}{y{~yvsrswyz}~{{}~{vy}xxyzzzz|||zz~|wuwzzyxwxz}}{zyxwwxz|{z|}~{z}{tqpooorw}}|}~~zxy||ywxz{zy}}xx}~}~}yxyzy{zwuuwyxvx~|yz~~}~{w|}{|{yz|{zy}}{}~}|{{zzyxy{}|{{zzyxwwy}~|z|~~{z{~{wvutssux{yuv|}~{xx{|zz|~~}}zwtsuz|zxttwzyvttuxzzxy~}xx||~}~~|~~}~~zutwz|||||{{zyz{||{{|}}|{|~~||}~|{{vuuutttvz|yuv|~{}~}~}}}zwvw{}{xuuxzxvuvx|~|~{}~|{zzyyxz~zwy|}|}}~~ytux{}~~}~~}||{zz}~}|||z{}~~|}xvvwxxyz}zy}~{{~}}|yxz~~~|zxwvwz}zy|~}zxwx{}}{|{|~zx~~~zwy|}||~~yy|~~~}}~}{zywuuw{~|~}||~~|y{|}}}zyyxwxy|~zy}~~|yvw|~|z}}{zyxxz~|yy|}ywvwyzxvx}{y|}{~{z{{{{|}}{~}yz}~}|{zyxxz|~~~~}||}~}~|||{|~~|zyxvuuvyzxuw~}{||{{|{z|~z{~}{yy{~{wtvzzywwy|}}|{}|z~|zzzzz|~~{|}|}~}wvy|~}~~}{{zyxxy|~|}~}~}~|}~{z{{{{|~{y|~~}zy}}{zzzyz}}}{yxxz}}{|}{}~zwvwy{{yz~zz}{{}}}}}~{xy{{yxz}~}{}~}}||{z{}~}|{zyxxxy}~}~}}~~}yz|~~}{zyxwx{}}zwz~}|zz}~}}}|}|}~}|{{z{{~~zxz}}zwvvxzzxy}||~{}|||||~|{}}}}}~~|y{}~}}|{{z{}~}}|{{{~~~~|{~}zz{~}}~{zyxwwy|~|yy~}{|}}}|{}{y{~}{zxxy|~|yy|~{yyy|~}{|{|{z{yzz{|~{xz}~}|}~~~|z}~}|}~||{zyyz}|{}~}}}~zz|~~}||{zz|~~zz~~~{xz}~}{{{{{}~~|zyxwwx|~{z}|yxxz||z{~}xx{}~}}}|||~}}}ywy}}{|~~}|{{|}|zyyxxxz|~||~|{{}}{|}yy||{zyyy{}~{wx|}|}||~~}~|{~~}~}|{zyyz||yy|~|xvuvy{{z}|}||}{{{{{|~~|~}zy|~zy{}~~}}}}}}}~}|{zz{~~~|{}}||~~}{y{~~{yyxxx{~|{|z{~}|||z|}|zyxxxz~~{{~{zz|~}{|~zz}|{~|||||}}}|yxz~~~~}~}~}|}~~}|{zzyyz}}~~|~}{{|~|{~~zwy|~~}|{{|~{y}~~~{yz~~~}}~~|}~}~}{yxwvvwy~|{~|yxy{|{z{|yz}}}~|||{{}~{}}~}zxx|||~}|{{}~~}{{zzzz|~~||~~{yz{}|z{}zz}~||{z{}}yz~~~|{~~}||~{{~~~}}~}||{zz{~|{}}zxxz}}{||{}|{|{zzzz|||{y{~~~~~~|}~}}~~}|{zz{}||~|z|~||}~}}z{}}}}|{{}}{}}z{~~||zzz~}z|~|{zyxxz}~}}}~{zz|~}{|~{{}}zy}}}}}~}}}yxz~~}|||}~~}}}~}{{zyyy{~{|}~~{{|~~}{}{yz|~}~}||}}z|~|yy}}}||}}}~}}~~}{zyxxxy|~~}~~{yy{||zz~~zz|}|~}||{|~|{~~}zxz~~~}}}~~}|{|}~~}|||{{|~~}~||~~{zz|}}{|}zy|~~~~}|{{}~zz~}|~~}|{}}{|~}}}~}{zyyxy{~~~||~}zzz}~~|~||{{|{{zz{~|||yxz~~}}}~|~~~~}|{{z{}}|~}{|~|z{|~|{||xx{}}}~}}}~}|~|z|~}|{}}{}~}||}~}{zyxwwx{~~~~~|{{|~}{|~{z}|{}||{{|~{{~}}~}{xwz~~}}}~~}|{zzz{}~}}{|~}zyy{|zyz}}zwwz~~}}|}~{{~~}{{}~}|~|z|~~|{z{||{zyxxxy{~}}}{yz|}|{}}z{~|}~|{zzz{}|yz~}|}~{yz~~}~}|}~}|{{{{}}}}{z|~}{yxz||zz}~zy{~~~}|||~}{~|{|~|{||yz}~}||}~}|zzyyyz}}}|z{|~}|}~{{}{{|{zzz|~~{|}}~{yy|~~}}}~~|{{zzz||||{}}zz{||{z|~}yxz}~~}}}}{~~~{z|~}{{|~{}~|||}}|{yyxxxz|~~~|z{|}|{||zz}|}}||{{}}z{~~|}~~}zxy}~~~~~}|~~}|{zzz{}}~~||~~{yy{|{yz}~{xx{~~~}}~||~}{{~}|||{}~}{{|}}{zyyxxy{~~~}}{z|}}|||{~|{~|{{{{}~{z}}}~~|yy{~}|}~~}~~}}|||}|||{||zyz||{z|~}zxy|~|~}}}}~}|}}{z|~{{~~}||}}}{zyxxxy{~}|~~~~}}~}~||~~z{}|{{{|~~{{~~}}~}{xx{~~}|{|~~}||{{|{{~|{|}zyz{|zz{~}ywx{~}{}~}{|~}{z{|{~~}|{{|}|{yxxwwxz}~|~}|}~}}|{}{z~|{{{{}~{z|~~||}~}{yxz}~}}}~~~}||{{|~}{}}{{}}zyyz{zyz|}{xxz}}~~~}~|{}~|{z}}z{~~}|{z{|||{zyyyyz}~}}||~~}~~{|~~z{}{zzz{}}zz|~~|}~~{yy|~}}~~~}||{{||{}|z{}}zyy{|{z{}~{yy{~}~~~~}~|{}~|{z|}z{~~}|{{|}|{yyyyyz}~~~||}~~|}~{{}~{{}|{{{|~~{z|~}||}~}{yy|~}~~}~~}|{{{{|}|~}{{~}zyyz{zyz|}zxx{~~~~~}}~}{{~}||~}{|~~}|zzz{|{zyxxxy{~}{|}~}|}|{{~~{}}{zzz|~|yz|}|{|}}|zyz~~~~~}}~~}||||}}}}{z|~~{yxyzzyy{}{yxz}~~~~}~~|~}|~~|{}~z{}~}|zzz{|{zyxxyy{~~~~~||}~~}~~||~{|}{zzz{}|yy|}|{{}}|zyy}~}~~~~}||||~||}{z|~~{yxyzzyy{|{xwy}~}~~~}|~}{{}}z{}~}|zzz{{{zyxxxyz}~}~}}~~}~~{|~}z|}{zzz|~~|yz|}|{{}}{yxz}~}}~~~~}|||||}{||z{}}zxxyzzyy{|zwxz}}~~~}||~|z{~|z|~~}|{zyz{{zyxxxxy|}~~}~~~|{}~{{~|{zz{}}zy{}|{{|}|zyy|}|}~~}||||}{{~~{z{~~|yxyzzzy{}|yxy|}|~}|}|zz{~{{|~}|{zz{||{zyyyyz}}|~~~~~||~|zz}{{zz|~}{z|~}||}~}zyy|~|{|~}||||}~{z}~{z|~}zyz{{zz{}|yxy||{}|}|zy{|{}~}|{z{||{yyxxyz|}{}~~~~~|{}}zz}{{{|}~{z{}}|{|}}{yx{~~|{|~~~}||||}|{|}{{}~{yyz{zyz||zxxz}~|}~~||~|{z}~{{}~}|{zz{|{zyxxxy{~}|~}~~~~||~}z{~|{zz{}}zy{}|{{|}|zyy|~}||~~}}|||~|{}|z{}}zyxyzzyz||zxx{~~|}~~}}}{{}}z{}~}|{zyy{{{zyxxyy{~~|~~~~~||~~{{~{zzz{}~~}zyz||{{|||zyy|}|}~~}||||~|{}~{zz}~~|zxxyzyxy{{yxx{~}~~~|}|{{~|z{}~~}|zyyyz{zyxxxyz|~}}~~~~}{||z|}{zyz{}~~{yyz{{z{||{zy{~~}~~~}||||}{||zy{}~}{xxxyyxyz|{xxz}~~~~}|~}{z|}zz{~~}|{yyyz{{zyyyyz|}~}~~~~~||~}z{~~~}~|yvvz{yz}~|z{}zxwx{~}|{}~~zwy}{zz{|~}|~~zxywuux{}~~~~}}}~{yy|}{z|~}|}~}wv{zwuvwy{}}|{|~}zxw{|{|~~ywy}zwwy{{{yyz{|||}~~~zww|}|||}{wvx}|{{||||{{{{{|~~~~~~~~|xxz~}}~~~{wvy~|{{||}|zz|}}}~~~}zxxy}~~~ywx{~}~~}|zz{||||}~~}~|yxy~~~}~|ywwz~~}}||{{|}}~~}~~~~~{yy{~~~~}zwwz~}}~}|{{|}}}}~~~}{yxz}}yxx{~~~~|{{{||||}~~~}~|zxz}~|ywwy}~}}|||||}}}}}~~~~|zy{~}zxwy~~~~~}|||}}}}~~~~}{zyz|~zxxz}~}|{{||||}}~~}~~}{yy|}zxwx{~}}||||}}}}}~~~~}{zz|~|ywx{~~~~}||}}}}}~~~~}{zz{~|yxx{~}|||}}}}}~~~~~~}{zz}~{yxxz}~~}}}}}}}~~~~~~~}{z{}~}zyxy|~~~~}}}}~~~~~~~~}{zz|}{yxy|~}}}}}}}}~~~~~}|{z|~~|zyxy|~~~}}}}~~~~~~~~~~|{{|~~}{zyy{~~}}~~~~~~~~~~}|{{|~~}{zyy|~~}}}~~}}~~~~~~~~|{{{}~|{zyy{~~~~~~~~~~~~~~~~~~~|{{|~~}{zyy{~~~~~~~~~~~~~~~}|{{|}~}{zyz|~~~~~~~~}~~~~~~~~}|{{{}~|{zyy{~~~~~~~~~~~~~~~~~~~}|{{|~~|{zyy{~~~~~~~~~~~~~~~~~~~}|{{|}~}|{zyz|~~~~~~~~~~~~~~~~~}|{{|~~}|zyyz{~~~~~~~~~~~~~~~~~~}|{{|~~}|{zyz|~~~~~~~~~~~~~~~}}|{{|~~}|{zyz|~~~~~~~~~~~~~~}|{{|~~}{zyyz{~~~~~~~~~~~~~~~~}|||}~~~}|zzyz{~~~~~~~~~~~~}}||||~~~}|zzyz{}~~~~~~~~~~~}||||~~}|{zzyz{}~~~~~~~~~~~~~}||||~~}|{{zyz{}~~~~~~~~}}||||}~~}}|{zyz{}~~~~~~~~~~~~}||||}~~}|{{zzz{}~~~~~~~~~~~}||||}~~}||{zzz{}~~~~~~~~}}||||}~~~~}||{zzz{|~~~~~~~~~~~~}}|||}~~~}}||{zzz{|~~~~~~~~~~~}}|||}~~~}}||{{zz{|~~~~~~~~~}}}|||}~~~}}||{{zz{|~~~~~~~~~}}}|||}}~~}}||{zzz{|}~~~~~~~~}}}}||}}~~~}}||{{zz{|}~~~~~~~}}}|||}}~~~}}|{{{zz{|}~~~~~~}}}}||}}~~~}||{{zzz{|}~~~~~~~}}}}||}}~~}}||{{{zz{{}~~~~~~}}}||}}~~~}}||{{{zz{{}~~~~~~}}}}}}}~~~}}||{{zzz{{}~~~~~~~}}}}|}}~~~~}}||{{zzz{{}~~~~~~}}}||}}~~~~}}||{{zzzz{}~~~~~~}}}}|}}~~~~}}||{{zzzz{}~~~~~~}}}||}}~~~}}}||{{zzzz{}~~~~~}}}||}}~~~~}}||{{zzzz{}~~~~~}}}||}}~~~~}}}||{{zzz{{}~~~~~}}}||}}~~~~}}}||{{zzz{{|~~~~~}}}||}}~~~~}}}||{{zzz{{|~~~~~}}}}|}}~~~~}}}||{{zzz{{|~~~~~}}}}|}}~~~~}}}||{{zzz{{|~~~~~}}}}|}}~~~~}}}||{{zzzz{|}~~~~}}}}}}}~~~~}}|||{{zzzz{|}~~~~}}}}}}}~~~~}}|||{{zzzz{|}~~~}}}}}}}~~~~}}||{{{zzzz{|}~~~}}}}}}}~~~~~}}||{{{zzzz{|}~~~~}}}}}}}~~~~}}}||{{{zzz{{|}~~~~~}}}}}}~~~~}}}||{{{zzzz{|}~~~~~}}}}}}~~~~}}}||{{{zzzz{|}~~~~~}}}}}}~~~~~}}}||{{{zzzz{|}~~~~}}}}}}~~~~}}}||{{{zzzz{|}~~~~}}}}}}~~~~~}}}||{{{zzzz{|}~~~~}}}}}}}~~~~}}}||{{{zzz{{|}~~~~}}}}}}}~~~~}}}||{{{zzzz{||~~~~}}}}}}}~~~~}}}||{{{zzzz{||~~~~}}}}}}}~~~~}}}||{{{zzz{{||}~~~}}}}}}}~~~~}}}||{{{zzzz{{|}~~~~}}}}}}~~~~}}}||{{{zzzz{{|}~~~~}}}}}}~~~~}}}||{{{zzzz{{|}~~~~~}}}}}}~~~~}}}||{{{zzzz{{|}~~~~}}}}}}~~~~}}}||{{{zzzz{{|}~~~~}}}}}}~~~~~}}|||{{{zzzz{{|}~~~~}}}}}}~~~~~}}|||{{{zzzz{{|}~~~~}}}}}}~~~~~}}|||{{{zzzz{{|}~~~~}}}}}}~~~~~}}|||{{{zzzz{{|}~~~~~}}}}}~~~~~}}|||{{{zzzz{{|}~~~~~}}}}}~~~~~}}|||{{{zzzz{{|}~~~~}}}}}~~~~~}}|||{{{zzzz{{|}~~~~}}}}}~~~~~}}|||{{{zzzz{{|}~~~~}}}}}~~~~~}}|||{{{zzzz{{|}~~~~}}}}}~~~~~}}|||{{{zzzz{{||}~~~}}}}}~~~~}}|||{{{zzzz{{||}~~~~}}}}}~~~~}}|||{{{zzzz{{||}~~~~}}}}}~~~~}}|||{{{zzzz{{||}~~~~~}}}}~~~~}}|||{{{zzzz{{||}~~~~~}}}}~~~~~}}|||{{{zzzz{{||}~~~~~}}}}~~~~~}}|||{{{zzzz{{{|}~~~~}}}}~~~~~}}}||{{{{zzz{{{|}~~~~}}}}~~~~~}}}||{{{{zzz{{{|}~~~~}}}}~~~~~}}}||{{{{zzz{{{|}~~~~~}}}~~~~~}}}||{{{{zzz{{{|}~~~~~}}}~~~~~}}}||{{{{zzz{{{|}~~~~~}}}~~~~~}}}||{{{{zzzz{{|}~~~~~}}}~~~~~}}}||{{{{zzzz{{|}~~~~~}}~~~~~}}}||{{{{zzzz{{|}}~~~~~}}~~~~~}}}||{{{{zzzz{{|}}~~~~~}}~~~~~}}}||{{{{zzzz{{|}}~~~~~}}~~~~~}}}||{{{{zzzz{{||}~~~~~}}~~~~~}}}||{{{{zzzz{{||}~~~~~~~~~~~~~}}||{{{{zzzz{{||}~~~~~~~~~~~~~}}|||{{{zzzz{{||}~~~~~~~~~~~~~}}|||{{{zzzz{{||}~~~~~~~~~~~~~}}|||{{{zzzz{{||}~~~~~~~~~~~~~}}|||{{{zzzz{{||}~~~~~~~~~~~~}}|||{{{zzzz{{||}~~~~~~~~~~~~}}|~{sonrw~|wpnpw~|zzzzqmojgnx}ug\_n~wtysi}W^vzvrolkmt|vw{tnkkovwu~wpqyxiadilqx{xrlnv{sprxzb\tkYW_iorssux~rklvzndcjsywtvqhgjnonmpxtjeiu{zz{|}eT]}|qnqv{~~ulkqupkkqzzqic`afp}~{~xrpu~vqonorx~}snr~q]Z_bkoc_`cfgebbiqk_bovx{wnjks~zxunpssrqqruz~{x{{yy~{nuxxpWVgp{}oe_^_flfZfk]drz{|}xtpij}|o|}qjiikp{zxwvw}~~wuty~hebNXiy}urqj\hj_pnejptuy}yyuom|}u}{zx~wy{vx~|vv~x{zytw}o{xwaiZS`n~~tjquftimyrprz|y||}o{{{yyz{vuxvy~qtytws}|ymqd\eXV\nsxvq{kt{zu~}x|ywxx|}r}~~ypjtymgpzuz{{uvssw{~~~w{ls^_fgknryu{rppv{w|z|w}{q~~}vsqv}{mmxvolov{zzuzv~ysttoeo]URWdw~zv{wx{||{|vvzux~tjoxqdgouvyp|yt}srsyxyzy{t~qvocmkjfacy~~xvxw|}yz}zy~wsw~}xy}ujryrjgkty~{v}}uywxuwqzlpra[W[emkrx|u}z}z|z~zzrw{xov{oehnqpnh{z{w~vtu|wz}wwyus{ogonkd\\o~}z{u}~z{~{wyuss}~{pv|ulgio|r|}x{|uyzy~~txs|npvhb]_dg`cy~y}xzyz{{z~q~z|v~uyylkoqoldt{vvyww}wv|z|tuxyiqrpj`Zdqru}~zyzy{vy|szqyyvtv}tlkmytqw|zxzv}xtszxpzmd``eib\h{z}{x~y}{{|t{ywx|~|onrurmcmtnx|xw}zw|{}sxyzioqpi`Ybmkhtzz||ww~t~|tuy~w~}{|~vpopxln|u|{~xz{w|vxt|sr}piefii]Wdrv{zyyyz{x{sw{~s~}{truvrkeunoxq}~vz|~~xqwnutqia`hh__kz~~{}wxtu}~{yxus{~}yuv{xjumvv}zwz}xyx~p|wnihkng[\ehhlw{}x}{{{|{{{vtw~~|{z|}ztluphvkw||~{z}}wv{ymtwvndajnd[]emty}~|wzv}~|wu{wwxzxy~||{mzpqwlx}zzy}}xw}{}p~|tqqtsh]ajic`eqyy~|~{y}|{~}qw}|wu}}}tquqtgvzxzy}~~|}uypuwvqkkpnc^`ddeeuv}ux~{tpy{v|y{~v{|xozujqx}}|{~x}xuysqsxxmcfljd`bhmez~w|{~tuvx{|yvv~}y~}zwy|mvtopv|~x|yyqwywpknvth`aca]Yfsoz}}zx{}|xv{ztw~|yy~wvyy}{mqtvuty||xy|wyuw{{qgjqpjedeh^eqoy|~|{uy~{~~{urzvuz{ytyvrryy{xyvwxwsoovyofdghd_^jaixu{x}}xtwxprx}~}~xs{y{rssroq{~}v}{zvwzxpkospjhjkdWgfhzyzxw}}xuv~}vvz~~}~v~ytsvzyvz~{xw{}zsmmvzrheeeb^ak_fki{x|~yvzxstwyz|~yuz}||vyxuolt|~|z{{z~~}xmipwunhhkg]fa`rms~zx}~~}urzxvxzzyzy}|y{{vns}z}wwz{xrpuxrjijid_bmb`lbm{z|}xw~~ursusryyrv~vxzysprz}z{~|v}sklrtspnlg[dh]iigrzt|}~z~{x{}xwz}|z{|~}|}}xpnvy~wvz|yusw{ujfgif`_kdbj_hux~|}}{z}{uvxxtsyz}tt}y~{z{{wrt|~wsw~~|{ulpvuqoopi\fc_ldahqy~z|wt}y{}tsvyzx|~}xnpyyyvwxwwz}rkijjhfkk\gi_imos|~zx|ssvvsq|}uzuzz~~~|ywy~xrqv{|~~~vtvvurqrpcci]fg^`ejms~z}s~{w~}xsuxyy}~y~~xsvz||}{{yvtw}qjihfelseeg^hnllqx}~}{xwxwx{p~{x~z~~}y~|}~xxupprtw||vwzuttqgln^gi`_`abfp||tyuz{~~vttttz~|w~~xz~|}}}|~yw||wtqmkjrrfpk_hlkijmomq~{zyusyzu|quz{~x|x~{z}{wuusu}|x{|ywofqqgnf^`b`]^grxz}}{zvvvsw}}||xut{x|wz{|~~{s}~~{|yvurlkttirneknnnnmhccm}|}~}y{zxtrz|}}|txy}xuy{|}~{zwss{zwz}ukqrgsse``baadjljlt}|txxpqsvy|zy{yuv|xzy~vv{~zyzywuvxknujmrnjknng`bks|{zz{}u~sy}}|~{{s}|uyvvyz}||}zz{t|}tmwpjsngea__bhmkfdhpy|w{{rpqqt||zzz|sxxztyv~~|zvwxvw~~nrsfnvuqnmlfacjquy~}{v~v}{z{|}|{}t}{txxxvuy{z{|~v|yzv|pjvphggd`ainlebdinw|}}w|usqoor|}{yvw~{u~vwz{}{vyzxwxzusyljqrrrqlfcdimpsw|}{|}sw}|ztqyxqyzxy{zy~}{~xw}~|u~ossiddedcdkokeacfku{z~vvvsqsy}~}|}|z}|xxr|{z{}|x}wz|wuv~yxokstsstqh^`iqsrtxxvyzx}~zuqr{}t}x{}tsvxx{~|~~}u}{x|s}wkgfeehlmheddddhr}{|~~~tqttsty~|wwzz{~x{wt|xxz{|xwz|~~{zv~ttutsqqpjdbfmqrqpmm}|zypnt{}zxw~trtxy}}{{}{z~t|x{vlgfddiqska_bcdgsvo~zyusuy|}zurrv{}}w}x~~|{|wuvvy|{}w|vv~}xvtnhegklmqsqjfrwq}z~xrrw{|{|~vysppszxxyy~~}v~|uqliilprnga^aeioketyv}{zwsvwqooqy~yu|wx{~ywwy~~~xy{zvrjeinqokmlfdomiv}~}xuvy{|}}}|y{|~yywsty}zvvy~||{x|vtsnkptsnjgc_ajrjcnmht}~~}|~zspops~y~~x~}xvx}|x~|zww}v{}skgintutoiefpibpuqt{vu{|{yto{{}vtvxvw}}yyxv{{}{wy~~{zwvwvvuohghgefmjcnmcjsz|{}~zvrory~xzyzrrz|w}}~|~~~}~}zxmjnpqstpe^kqfiokhipxyw|~xrq{|wzvvx}}zyxxz|~|x{x{{vwxwy~~wqkfegkpnceqibhqvyzwxwvvzzpv}zwtw}|u~z~{x{}y{~wspptusrqjejiclskfhkoy~~zpp|xr}yxvtw{vvxy|{xy~z{||wwy}~tkhgeiprfamkfjnquxz~|uux{}rt|vv~~ywyz|~{xz||}yyy}}|vtwvssqlgenqbdqoieeinwz|ytyzou|zz{z|ztsuzy{{~}xuw~}wnhgimqlfni_fosssutsz|xtyvq|zqw}vuz~~~|xx}z}}yy{|}xuux{}xspickqgflljiihlt{}yvv~pu}xuxxy|}zusvzwy{y{~}}{}|vspjjopeernbclrttrppqz~~}||xy~tou{~xux{wx{~{z~|y{xtv{}||wmfgomemqhbdgikqy}}~}sxzsz|xssx|~{wxzxx~y}{|{}{y}z{|xtssstndjogeijnrrnhhnv}|~vwrsy|}{{{|}vr{~{}xxy{zxux~}yohpobgsqibbglpstutt|{{|wvqppt|{vvzuyy~}|xz~zwxww{|oeoqffmpnknnjfgnuz|w~vwz{}{zzyx}}v{w{|wy|}{~w~|}xrtxpglpmifcagotsomnpvwv|tqrtx}ywy~sz}z~{y}}}yyxz~wnsqfekqroheeehkotwx|~|w||{yttx|zvyux}{{xx{yyy}{}wxwiounhiiigjprojgint}|~yurrw{||||~z}xu||xvw}}w||z|zs|ykhlnprpjbafkmmnpnn{xw~xtpou~yu~z{snqw}}uqlc]_iuwj_cu}taX^nzvsxolvqsyht|tkgjouvm{|ut|{y|{ux~vlbcq|qZQax}sf\[aq}yoprw||vmfcdkzwjtqmt}qv{wzxtwvc[`jw|se[aoxxspmcar{tvskjmoqsuwx~xlryqosy~xvwrq~|}{|{kfdemtxvnbYW_nxyurzvw}yx}snuzwwompuz~xvuuuz}vkpzvwxxxxwtpvvlkqplhiouyyvl`WYdpz}z|y{{xqn{~zvqmnppqrpmmt~{sztrv||nhpskjpxzzzunhec_\^cfir~}{~}{vqmls|xw~|vporw}}w|}wsu|ymm{|z{yxzzvqjb[W[fljeq}|~}uqootxrnz{z}}{zvt{}|zwy}vqkhhhe_XVZemggt}xz~vrtvwurojfjwyrv|~~~~||zsmieb`bd_^hjfhq}~|yz|~}zyxtpkkoqnkr}}xx~}|vommlmlg^W\gd\alpoqyxz}}||ysmlqsrnhdemwtq{}|}xrlijkke[[fka]ckqx~|xvvy~{trqpoorogkzzsu{~~xrppolje`agd^cigeegoy|xusqportsngforjnz}{{~~wrnmpsna]gi`]`eiknrtw|}{{|~yrpruvskemvuqtz~~}|}~|{yvtqlggmnb\fmkecehnx}xttvvtqqokkrsjiu}{yyz{~vrrrspfekibadefhhhkqv{{vruz{rhmtojpw{}}zvvz~~~ysoifltmbbjliebchnt{~{wuvy|zupnpvwlgpwxyyz}}wvxtkgnqe]`fihecccjtz~~}|{zxyxonuumiltz{{zwuw|}}zrllqrieklihggghlpquz|zyxyz{xrlpzzokrusqux{}ztjfosjb`chjhdbbejsz|}}~||~~voqywlhkosxywvwwz~{}}xpnttjelqmecehklopqrw}|{||{zwtswxqnuwurqsy~~}}pimqjddedegfcabflqw{|}||~rlu|umknqrsvwvx{||}~~}}xvxwmehoqnidejnpomnos|}|yusv}}qmu{xspprw|wmotmdcgigdcdddehknptyxswxsnoppqpnosvz~~{z~|~|yyrhlpnkihffkpqommpu}~|xuxsnu||xsrsvy~~yvyvkddfiif`^`dgijklmuztx|pmqttqonmnty{}|yw||{{~rostpkjklkjlnmllntwxw||xy{uxzyxvustwyz}~{}}}}|ywkghiiigc__cghgefjt|wxwpnruuqmjkmrx{{zy{~}{yx|ysnkjlopmjkllmprplrzv}ywxyyxxwvw{{v~{~}~}~|snmlkhfdcaadggecgqurt~zzwuttvtqmklnptvuw~y|{~|tqooprqmhhknpnhdkx}{z~~yyvx|}|ywuuy{x~}}~~~~yxvrolhfefecbccbemqkitzxz{ytsrponnpppsv|xw}~wsrrrsrokhjopkceosoqw||{z}}yvuux}zv|zy}}{|~}{{{xrlheegihdaadkpjchr{|y{}}ytqrrqpnnllt}x{~~}{wutqoonmlllfchpojjqy~~~}zwwyzxvv~yvy~}{|~|}}|}~|yslhfffgfb^akqldfouwz}||}}|xsoqtuspmmpzzv|{}}~~~{xuronopojdbiqngfiow|~|{{{zzyvpnz|yy{}~{{~}{{}~{xsmihhgfeddglkefmsuuw}~~}|zyvsqrtsplnu{yux|}}~ztpnmoqog_bnrlfgkosy}{{|~}yrns}{xz||||}}||~}z}|||zwtqligebbdjojcemssrsv{}zwwwwusssrrwzuos}|}~~~|upooonhcelmifgjmoqty~||zqnu|zwwy|~}{z{}~}}}~{{|zxvusrokgdcfnph`cnttsrux}~{yyzyvqprx}{snt||y{~|z|~~}~}zurqohbemohcehkkmnpsz~xsu{zspty{||zxx{}{|{{xwwvtrmgdflplddhmpqqqsw{~|{{||yusxuosz}~}xy{}|yy|~~|zz}}{wrkddmpjb`dhjihikov}}usy|topsx{{{zyxz}~|y|}zxwyywupllpphcgnommoqrsuwy~~}|zy|wtw{|{{~|~yxzz{{{zyy|~~|{~wlehopkebacghhfgimqw{}yu|~uppqsuxxxyzzz{}}~~y|~zyyywtstvtkdgnqoljmpsttuvy~}vu{}{{}zz~|zzzyyz{zyyz{{~|wmkpqkedddbcdeefhkmqv|z||vsrrsstwxyzzywx}~|{y~~|zyxwy}}shfkprqollnrsrrsvz~vw~}{z{~|~~}~~{xwvwy{|zxy||x~vrsvsjdcefecbcefghiknv~xvvutrqpquyz{{yx|{z{{}}~~|yvw{ymjmpponnnmnrssstwz{{|{{}|}}|z|}~}ywvvxzzywx~y|yux|wmfccdeeb`adhiiikpz}}utvwvsrqqswyzyy{|~z{|yz~~~{xz|sprsrpnnoponnpstvwwtry}}~~~~y{|y{~~{wvwxxyzz}{{xtx~wmfddddedbabfhhils|{vtuwvsqpqruxzz}|{xvyyz|~}}}||~vsvyvrommnqqpnorvwvror~}|~}}}z{{z{}~|zwvvvvy}y}{uyxqlhfecbbccccegimsyzuw|zyvvvvurqqqrux}{y~yy~zyyz{|}|~{tuz~|xsponopponorusmkq|~}~|{|||{{||}}}{ywvw{xv~{y~wrpnjgdcbbcdbabgnvxsos~~|||zvttuutrrqsy~y}|xy}zzzzzzz|}}xvz~zvrqpppppoooolimuzxvy~~~}{yyzz|~}{{|}}}|yvvy{x{~xutsqnhecbcddcbcjstnimv~~~~}yvuvvtrpqt|}z~{vxzvxz{yxwxz}|z~~{vsqpooqpnieeltvropt{|wsw~zxy|}|zyz}~~}{z|}xy|{wvwvtqlgedccccdiopkgjqw{~}{xvwwvtsv|}yyzuw|wuwy{zxwwxz}~~{wtqppqpkdahqsnjilpv{~}wqr{{yyyz|}|zz{|||}~ww}{xwxwurokhfecdfknmgdhotvxz~~{xvvvvw{|vtzzw}|yxwvwxyxwwxyz}}{vsrqpmhbcjppkhgjmpuz}~vru|~{yyzzyyz||{z{|~zux~~{yxxwusqmhedcgmqmdagosttvx}~~}zwvx|zrqx||zwwxxwvwyyxwwwx}}yusplfdgmnkggghjknqv|~wtw}zvwyz{{zyz{|{zy{wx}}~}{xvuutrpkgfhmpmfbdkquuvwy|~}}{xvz~uqu{~~{wuvwxwuuvyzyxxz~|xsngbdlqngcegiklmpsy~yvx~{vrswyzzzyyz|}|{}|{~{{~|}~}zwutsrpmjilpnhceimortvwy|~}}|{|xsuz~{}yuuvwxxwuuwxxxy}}zsjddjonidacgijklorw|ytw~wrqruyz{{zyyz|}~}~zy~}|||zwuttsqppqrmeaelpppqtvxyz}~||}~wux|~~~{}|xvuuuvwwvvwxz|||ulfgmolgdcbcfijklnqty{x{}vsqrrtwyz{{zz{~}||wy~}}}{xutsstwyuldcglpponptvxyz}ztw}}|}{xvvuuvwwutvzzy{tnmpqmgccccbbegijlnqu~}|~yvtrrrsuwyzzzy{~z~}yz~}|}}{xvvx||shfimpqqpooqtuvwz|}~~xx}~~~|~}}|zwuuuvwxwvx~xx}|vruywpidbcdcbaceghhjnu~xvvusrqqsvyzz{}}{|xw|}z{~~}}}|zy{wpmnpppoppooprtuvvvtsy}|y|}|}}}|yvuuuuvwy}yx|~xvz~~vmgdccccbaacfghkpw}~~}wvwwutrqrsvxy|}}~xuy|ywy|~~~~~{tsuvusrppqponoqstrolnwzz~{z|}}|{yvvuuwz~~yx{|wyxpjfedddcbaabdgkpvyvuz~zwvwvusrrrsvz}y||wvz|xwwy{~~}}zvwz|zvsqppqqpooqrplilt}|y||{{|}}}|zwuuvy}vv|{|ytpkhedbbccbabfkquuqns}}{ywvvvtrqqru|z{~zwx||yxwwxy{~{vx|}yurqpppponnmjhjqy|||~~|zy|}|}|{{{|}}|zxwy}xux}~|wsqnkgecbbbbbbfmtvrmmt|~}|zwutuutsrsw}{{{vuz{xxxxxxxy{~zwz~}{wsqpppppokhfiovyxvvz}zvtx~{{|}}{{{|||{zz|ywx|}}wtsqolhecbbbcdgkpspkkqx~}|{yvttttssv{~{{|wuz{vuwxxxwxxz}||~~|yurpooookfbenvxurqsx}~ytrv~~{zz{}|{{{|}}~{vv{}yutsrpnjfcbabdhmonkhkpv{~~}|{ywutuvwz~|ww{|vw}~zwuuwxyxxwxy|~|~}zvsqpnlidbfntspmmosw|}wrrx~|{zz{|}}{z{|}xuw}zwutsrpnjgdcdhmpnhcelswz|~~|{ywuux||vsv}|{~}ywvuuuvwxxwxy|}|yurplgccglpomjjjmosw|}yuuy~|zzzzzzyz|}|{{}xux}~|ywvutrpmigginpngbbhosuvx{~~}|zyz{tru{~||xvvvvuuvwwwwwz}xsnhddinokgffghiknrx|}xvx}zvvwyzzzzz{|||}}yz}}{}~~}|ywvvusqnmmpqmgccgkpsuuvxz}~~}~~wsty}}}yvuuvvuttvwxxz}{tlfdhnqnhcbdfghiknrw{|wx}|wsstwxyyyyz{}|~}yw|~}~~}zxwvutssttqkecfjlnprsuvxz}{vvx|~{|zvuuuvwvutuwx{~}}woihlpplgdaacefgikmqv}}yy}wsrrtvxyzzyy{~~}}xw|~}}}}{ywwvwyzyskdcglooopqstuwy|{vwz}}}~zwuutuvvvuux|{y}ztnlnrqmhecbabeghijlou||}~zvsrqrtvxzz{{|}{}ywz~}}}|ywvwy|~{ridfimopoopruvxy{|}~xux}}}}|zwvuuuuvvvw|}xx}|wrqsusoiecbbbabehikmpv~}}zwutsrrstwyz{||}{xx{~~~}|}}|zy{~~wnihkmoppppoqsuwxyyww{|yy|{|}}|{xvuuuvwxy}|xx}}wssw{yskecbbccbbdfiknryzvuttsrqrtvy{~~}~yuu{|z{~~|||}}~~wqnnoooooppppqsuvvtrrw|||z|}||}}|zwuttvx|~zxz~}wux|~{tlfcbbbbbbbdgjnsy}~|}{vuuutsrrrsvz~~|~{wux~|yxy{}~~~{trsuvtrqppppoopqrqnlnv~|{||{||||{ywvvx||wvz{xzzsmhedcbcbbabejpuxxtsx{xvuuutsrrtw}|z}}zvvy~|ywwxz|~}wuwz{zwtrqpppponmljikry~~|zyy}}|{|}}}|zxwy||vtx~~~|wsokgeccccccdhmstqmlpx~}zxvvvutssuy|z|~yvvz}zxwwvwxz}zx{~~{wtrqpppnkgedhnuyxwx{~~{wuw|}|}|{{||}}}||~|wuw|~~{wtrolhfdcccdfinqplijov}|vyy{{w}||xvqjijmw}qluzxy}~tnuif}x`aoqidegfehp{xuxt|nbhwzqlmvznp}}tpnotyzwutx~x{z|oc]cntocZ[erzzvstyywzykgnzwniilptx~zwprzztmhip|{wyyst}rccmutkdbfihfejq{{w{z}pgfmx~wqmlpw{wwyzyvuw{}{vtv}}w}{rg]Y[bkt{~{tmjmsz{vtvzytrrrqmjjnsvxz~}z~{vpkijpywrrw~wmbZVX]bcb`^_cir}wtvxz{~}ytqooqu|{vuxzxz~|xvtstvy|~}{uojgggfa[WY_hpwytpnnoqu{~{{{z||z{|{xtssuxz}|y{~vnhca_^agottromkihhhjnrw|{xy|}|zyz|yx{|yvtssu{{xtokifedcbcehllid`_aehjklpu}ywz~zvsrsy{uqomlkihhjkmptyzxy{||||||}zsnmpuvtqnnpsux{~zuojilrwxuqnmnoooonnoqsvy}|vrqqstuvxz}}|{{{yuokkosx|~~}yvtsstwy{zvqlkmprux{~~}~~zupib\[_ehhe`]\]^`abdgkqw~}{{|}{yuqnot|{ywvutromlllnpqojc][]acdehknrv{zxzwvqlged`a_]^dqz}sjprhjpvxzwttxuusihlrwwvvuqiaf{wrmck}wmgfjox~pclqx|khoy~vpmnwxu}}}{wuy}rxkmty{|}vkgmywmn{{t{~u|leehjjiimux{yuromns{yoy}rs}z|xj`^bhlmllkifa[UQTaqyuos}ytrru|{uztsyyvvz~unlmqtvxz{zxtnjjoyuoptvurpnnmmnosx}}xrlkoytorw|~~}zwvw{ywwxwtrpomkgdcfkt|}wqpsvx||vrrw}xvuutspmhc`do~~|vojgecbbdhoxzwvvwz~}xtpnrz~ywx}unjiiiijjijmquyzzwutx~}|~|xvvz~~}{yvttx~xsnmot}}}{{|{vnf`]\]_behjmnmkkkllmpv~~{yvsoljjms|}z{}yxx{~{xtniffhkllkkjjihffffeddgnx}zxvvvuqlihkpuy{}}zvsruy|{vpjd_\ZYYYZ]aiqz}}}~}{ywvuwyzzwqmmqvy|}~~|zxvtqonosx|}zvsqqqrtvxz|}|{zzyyxwvtrrv|ysrtwyyyxwxxwvvutsrrsw||xvtttttuvwz~xtpnlkjihfdcdgjljgeflu}}yuromlmnoqty}{}~~xqkjlopqoljhhgggfecaachouy{{|}~}|~~{ywxyxuqljlqvy{|}~|{tlg^[XVXY\dlvtf_cq|~|~wnfbguumwrjo|{xmrrvrrrv|yvtu}yxy}xh\eshT[vxlimtx{}|yx{zwy|}~|ytsxtiuwx{sr{silw|zvmz~phdb_]ZZ]dlqph`^dnvxuqooqu{|tmikt}pkox~y|whixwpmnpru{|}|zzz{}|rdZ[gsyvoigjntz~~}xttywjgo~|wuuvx}~ww|snllkieb^[[_fpy~}vld``dinsx}|yusqooqw{trvunjhhiijkmqw~|{zyxwvtrqpqsuvskc\[_ekorux|{tnlnu{vrppqrtvy}{yyzzyyyz|~}{|}~~~}zvqlgdcbb`]YY]emty}|{z{|}||~{xvvusrrstuvwxxwuuuwz}~|xuuy~~zsmfa]]`gpx||zvrommosw}|vssssqpqrsuvwxyyxwwxz||zuolmquy|}}}||{zwutstx~|{zyyxxz|~~{uolmquxz|}{yxvtsstvuqkggjpvz}~~~~}|zxwx|~||}~xphdcded`][[\]`behlpux}~|yvstx~~zz}}yvuttrqonlkihhggea\XWY\aeikmnpruy|~~{{~{uqnllmmmllmnpruxz{yvuvy|}{ywtqmighlrwyxuqonopqsuy}xttwyxtpmlmoprrssstwz}~zussuwxyzzzzyxxxyxxwx|~~zrrxyxswz~ync]]flu~vz|o|vntzu||s|tms~{utphagxydex~mjx|ps{{uwkan}|f^mvmqvz}jrxokmv}t{sksfmx~~~}}uszifmx|xwt{uuu|fhm{~fSfr|qkedobfsr{tqn}xst|yrkbcq}pnorwuodk~x|{{yimz{kmuslupse\_hgck~}{omlbVZjwyvvy|wxuxt}ogklurlvtpwxqou|}{sidyxlghtllki^QQ]k}}|wvzvyvtmrtfhk{~~}uuxpoosy~|tqbr|}~|vzwyt}{x~x~yt}{y}wrvehhq~swolkkxx|zw{mv~xvnltt~{~{zxr{khgkz~yoqlgmgvvqww{x{rxqx|~~wxpuun{x{vuxotstz~ux|xvwmwmkptxynm~yyqsrrzw{znyuxtswopnlxkigjnhoqrwly|{xosrt{yyrrr||uzsx|||zxjrmkd`bY[^c|}~~nvxtrikhce\f_[_ajlqyxyy}uyxw{z~{|z|w}vvw~xy{sv}xwnqvv}x}ykkjrxzyss|nmjopktzx{mmokg]_YSZZp}}xt}~vuxqsprxmqqwzzu}vxzzzpipnngkmgiglvjjjrsmtttqdonnkkuxxyt{niimsv{zyrvsnc[[UW`izzjmsomefgadajjcfhooouv~uxv{zwqutnpmw{qrt}{xxp}~{vyjfdjnr|}|uzonr~|{z~|yzywuhnmj`_dejtz~~psyvrlmoikirslnpvwy||~zxlw~snttphjicb`fnbaaiidjllict}zyv}rnmtxz{zt}~uy}}xueefc]^hlnuwupwsphghb_^bg]\]ehny}ruxxyzwrrr~unlrtnnuv{uvrnsriiox{yrno}xy}{|}xttrof_hlolswtstz}~~qy|{vrtskkksnilvy{|ztn{|xzxwmotuljmlggennc`ciffqy}}zxytmlotlknwz~y||wuuqmfqxzrturosx~~ykpuslggh`^_hhflz}xy|ww|zvnz~vtz{squuomktvkhmuw{~{ytjikphehqt{~{wx~}sorsommrtutyzzwtw|~~|}xlswumjkmeeivy|ztuwqklpoigx|}xlmrqhhig`]]ge]]hrw||z{vpquvlinuw|~zwswsqotz~zzupstqqtwoutnfddb\`jxxz~|}wsxvqmrrpis{u|ztvtqjinunlu{yxyollqidgqsx{}}~~vvz|snpsptx}}tvusmpw}~~~rvzxpnmqmq{}}xqtupjkppmsz|ytmqvrikkjcadommu{xxwyqkkplfgpprt{~z{ytv{{}rspojls~y{~ruywmkkqqt}{}|ztu{~smqtoklvyy|{~{suywnrqqjhmz}zyrgfjjc`eghmq|}|}sy~wxuxqoqvqklx|ywwwtpr{~vu~~~upvwpmry{wx~yokpjhfqz|y}~unqslhjgf`er~}y{{vwnknsnfelimlry{z}~~~vux{vv}}}xnrqsljnxzw{vtxysx}}~y}|y}|sqw}uklqnsv~~usyzppqrtt}}~vtqsjikme_biejjuz{}vv}~vuvvulmt||~zz}yrvssjgmxyvy~||rs}z}|{rruyqimvv}|{|~yrorsjkqv}||~x}worotliilhcdjeln{~{|{w|{xyvyrvuvzxnqkkecjw{wvy|{|~yrw}{rvruojlsrns}|{t{ztlotst{x{ypqnpj_`ee]Zcekw~z{v}~vmpwwmpmtvx{x|~toomoimv}wry{zu~|{tuwvsijryyzy|w~||wz{ulnv|{|upvzsrrmndeijdaisx}z~|}ztu{|swzy~xqssjdjhnjkqzzx}|~|zxwv~}wruwqruqslp|{{u||w|ww{xux}|zrmsxqliejb`bfeerzy|{tszwpnuxt|~{zqppigmlqigozzuy~{xzxz}spvyqrsswt}|}w|zuu~}~{}vz~xszuoouujjjjmbcjprw|~y|vvqx|uns|}~|ylfhjbgiggahsyvv~{{~uqmxzvznostzvt|}wjjkr}ydatsV`mnllmjoyw~stmotzkaz|~v~{moswywn^\mszma_fko|}~|ugam|}zv||zidt}op||xsusmts~xzqnnehck||ridgztj_\oz}~xmsvwogo~vyyswnt{zurm]PX^orosyxxsmrwvy}ytnrj`_ky~yv|{|rupgftyumy|}{{{sjfaZgqvzpmpsoluvx{ry}~zzysouvklchpv{{smcW]^`gfqyvtpknkcleijfen{|znkqjmgglggo{z{oltu{y{{||yqgba^bry|tu{qtqvzyztnqjilmux{vz|sf_^ZSXQY\`rzpsoopgnqpu~x}w{prvutzttlsrkpy}}t|||xv{~{y~xtipmbYZ``k|}}zr}{~yy{~wy~|txuqwqvrg_ac[U[Wafm{}|yyuupzwwmrwttwyrmvpsqls|~y~z{nntwvtyphmeif^[als|~{yvxumhlkgoecb^ZXWROZYhppy}~y{y|wu~|vrrupuomtou|wz~xx{nrlinttlijdja^c`_gv||uqpoiiqijd]gf_XZ]Yepr|xy}}|y}{z|~|{|vruowukgnuonsp|~}tvnpqkptqhhqige`jmqy}~{{ljlkhgla[`[`]VTW]`ktq{vry|}{{v}ssqnnprmnxu~y{xxqhpkjrlkijkjndchcpyzzz|}zx|~twnloroiibdj_ab`_grpu}tzvw~{~~yvw~|v}|xsxurrcjolhioou~vspktptthipsokoinsv~~{y|}{{oqrjnqmcai`b^W``bhnpmtpmywvx}z}~}}}~|~sqpnupijquy~tlmmhotjmginokgmmm|x||vv}~z}t}}|tvyq~}wyxoyvsqmklph`iafptsuxrpxt}xqv~w{wu{|{yxvx{|}xvx|{wuklshjigho}~xtquttwjqupooqos}{}{x}{xw}x~s|~zstvoytiimmhfb\ebjsqponikyppqow~~|u|zz|x~}}|vzytvilpmr||txsoqtpholprhhnpqzzuz{|yvus~|xwprsqr{upuq{{y~}ywyyvouwskkofryu|}wrsvvzrpzxy}ytztt}x|xsv{|tx}xxvx|yzxsqvvhjgdlrvy~|~~}|ysny{quqpu}|yy|}ytuvyz{vjxytxvwyzwvxv{wistpokihlnp{tvwpmqumjrmvxtwussvzvu{~}|{y~zzw|~zyrktmt~~~y|t||ummqoumisos}{}}xttyzuywhqpkputmmomwoluwz}{xyvvwprtsw~}|z{{|wsnu~u{{rptusy~vyrt}{urxyzxuvow|uzpnuwsknlftuw}}zx|~|uw{yqxwt}|~}xtwntvvywppwwmsppxy{yxyxrjtwnsmmosz}~y~ss{wnjlnpwoqnimptx}}pz{{z}}{yyx~sv{yzxuuwyy{|}z~wrtvtqnjrwt}wu|yutphgoejtojhmpqpfnvr}|uvxy~pt{w}|~xsswvqzthqnlsz{vvuxutzz{xwvqostorlnxqlnrqt||zxyy}}{w{zzuxxz~{roojqtpwolswupsqpz~|x|ulntsmrony|vzqnoppnnjhibikntwss~y{}y|yyzzuly|u{uuy}{{zx{}~sz}xytqstxy}uwrhfeddmoafkmomkntsx}yx{{zus~{}ux|xtsmdkndpurpqx{ys|}vzznrvtnhjlvqhrqqz}w}x|ysrx~{y{~~wsqnjmqorjhwuqpsuw|unwwsnopq~qtxrsqog`a^fhgqkhrz{y|z}{}{yprwvuzsw~{yxxy||}{~wu~}~|~}uxz}y{lhiefiib`lfjmgknrz~~wwz{}|}xzphhfckujimpuxvu|}z~qx~tpkmpokfqqm~}}||pqxwuwwntzp||x{ynpsspjhmxoirrrxwyyrrrqs~|~v}wmgeb^acfk]dpomnsv{uv|{vqrrzz|}}zyronrws}zy|xxnqpinng`dhgjdakls{}xzz~}}npmjknleilkrmkqtx~~}tussuojjlkv|~yr{}tvtkhkikxxuxx{u~{wqlosoqy~}q}}|{sy|vrkrzp}z~vxik|zoltncmoxyq|{td`qwr]_eawgRw`K^orwighj}z}~{q|yurx{likrwuoovuwx}wq~sy~~vaP[\ipq}}}syxuh\imx|~k]kj`\cwtupmzxkinunpmruw}wniity|~y{ecVVZ_lk\_eeX_hjw~~tvps{xstomxqt}vja^t~xu~~}y~~myyvf\]TXcrzwpku~{vnlmx|psrjqo\^_bZkyxxzvy|vmxz~zxtfUJNWnvz~prvkie\]RSZjonw~{twvrplnn{y{rv{xts}|~}|yxsst{wxtqsjXKHYbqznjpv||vwyrmvrsqik`^^hsu~znnphaY^]gsw}x|ztuwzvutv{x~{~}wrh\XUgp}tghknhepcfjupq|trtswljjxtpqjja_Y]efsupzz}zxtrnpwzztvxyx~{~xzsng\WVoytqpnllr`ablhgnknquyqur||}|ynieegdswv{x~y~xyvtx}{tu{oyzwy}{|rus~}mhgb\PNMSmv|{|ilhpjtxspprpggbuy|wsrruvwv{zxooy~|uz~x~}v|yvwz}tkmk|wzxid^\ZQRHVgny{z~~yxvjiiiwo{u{}{vy}~{~~zo~~}}}slkbiuwxtzpuz}|~}rqknxt}slhhh]YWObcqtumwwt|y|srvrts~||rqd]hlkdcdbnq||yw||uvq}|~u|v{||wqmhic]aU`go~~{{vvru~u{qxy{xx~v|tchmijdf\efqsqmllo||u}t{{{|~yzvpvsqfc^a`X^[[idv~ulpkyw}||}ps{zslvuvnkebhfqqsmlgnvmnmhun{}tvupigeabYSZNWX_qonhirjvz~{iqoyqvzstnnelks|{vrojrphsmpql{~v|ywpojigWVVP^[gqssfkmo}{x}{lrm{~xhoommiiepltyyuvqoxtqzqwss}~|{{~zyw|v~yqknkmjZ`^[ecpvxxluty~}z{{z{puuwlvsnlgdagamsspspqyomxr{w{zy}{wt{umrngcgflk\`[Zgix~z|nrqy}~yy{|zzrwvt|xsphcajeotqlnifmfdqmwxzv{umuqg`b_edRWUTbh{tufjls~||sywjmm~t|}wxqmkrkpyunmjfni_kiotw~{uywmbb^`fYXZR_fwtynjmo~}{|tsxkpr~ru{vzvpkqnn|{tstlpoajjksxx{~uvypedbcla\aW`hw}wprr}}xv}vvuvwrsvqsoignkkz}xvskoqclnpzz}vx~zuy~qqtldfcaja_f\ep|{sux~wr{~zwwuw{vvuorqkhnhfuuoonfklbnoqywx{wt|~nnod]\YZd[_e^itwwru~yt}|~xuwkqxptzz}ywxunosggssqrmchd]helty||ypqk^[]Y\bX`_Ycq}x~roq{zy~utsjwtnqzywxtpvvjrzvvvmeh\`hfnu~{~y}|tvjba`[b_ZbX[gy||pmtz}y|qqzrqs}}xtttps|tmvvvzwlkiakhkr|~~tuywuxzsdaa__f^ee\bqytuwv{y|zvo{vrt}wtsomwthnrpwyslmagjhozwvyxr{wtrb]]^[dbch[[h{~vrz|vv{uvkswrty~~zwtlszpmrmrwtiga^gcgsyy|wv{swl^\[YZ`Zd_U[n~|}wpry}~}xzwqjtolow{|zur~|qtrnuuqii]cc_ft}|y{}w~{zzh`]\V\]ah[T^q}~zxqux}{{}{ynmqkmuz{}}tuztyqrz{umi`gb^fx}wvxzxwzxgc`_Z`_fj]Wbr~{wyz~yy{{ortnq|~~|ywns|vtyptyzrnickdal}|wtvus~|rwsec`_X]]ila\iw~}~yuxyx}~xzqsrlp|yzps{uswlnswqlhclfai{yssts}qtqd`Z[TXYfmeakv|y||~|}twwu|z{tvnopil~}~ts{vrwmmsuqjg`hc^dv}wtwtssseaZ\UVValgbjs}w{z}yxyrzzzssqpsjhx|~ys{yu{vosuvlj`fgbbpz~|tvpywrulh_^ZVW\kjhnw}xzy~~zxtxpvy{wtuswojuz{ysz{v{xpstxpnfjlgfry}{zqsmu~wrulj`^\XY^mpou|wxyz|uqumrw{wrttzsmw|zyrwvrxwprqxrpikmijty~|y|stlrztophj_\[X[_nppytvw{{}sophnpwroquzso{}|xswtsyurpounmhkkilwz{|~vumsxqonhk^\WWX^knp}}rru~~|rpngmnwpnovxolz{y{uu{uvnornmjmignuww~}yvpxxrsmlk_]UXYdlmr}{rqw~{|qrkmnsxopqyulq~z|xsz{yzourqoomghsr|~xt~z{ssxtqqkpib\Y\_ilny}~wst{yuqpknkwtqqx|sny{zzst|zurtqrqrlhquu}rz{{votsoqlnofbX[]fjls~}usywzonhmhpvqru|vms{|}uqyy}zpsorrupiluq}vv|{{rttopnjpheYW[`gin|}|vqr|~|sokklfsqpovypmvx|rryzvronqsskfoqr~t{||uwroqkmmieWY\cefp{z~uqu}{spkohjroprywnptx{rw{wtmoqusjirnu{zu||zvvppqlrmmdX[_ddgu|}wsx}~z}snkqhlrrru{xpssyzrvyyulqtyvllsowyvtwyxvsmopmqkodZ]cgfjy}xtz}z{rkkpgloqrv{yswu}xpsvzvkptzwnouqzyutvwyuqjklkohof[]dhfiz~yuy|~{~~|sjhneijmquzzwzv|~yqrtzwhkqxxqpus{xxuvu}wphhkkogni\[bhfgx}{uwyz~vmflfghiptyzy|xy|trr{z|lhowzuptqyyy}svq}{tkfjkqhjnb\`ijhs|~xvyz}~yrhlhgjentyz{z|vrqw|thlu{ytvrx}wsunw~wlefhqkiphbaknkr~|}z{z~~}zytjkgfkdlv{|}}zurpu|wjku||yztyxttlr|}wkcceplipkeckpot~{~~|{z~|{~x}}wsiieejcjv}}}xsrns}yvjit}|{{s{ywukrz}xja`coiinigdlppv}z~z|~|zy~{~x{zvrkjbfhaiu|{|wsrnu{ztjhs{{{{r|}{yultyxia_cnhkljhflopw{{~w|}zxxz{ywrmjajeajx}z}vtsqyy~~rijv{{~ws}y|~||qovztfb_iniokkihlnrx{|x~xx}w~yxxvqpfelddo|}y}turtzyzqjoy|}~sx{y~}}xlru|}nd`bmjmmjmjjmovvwz|x~w}{vvvsrlckgcgv}z{vstsywvoktz|xu|y~}}qqtw~uga^hjkoimmjjlruwyv~yy}zxtvurrdficclz{x}tuswvxyslmuy~tyy{||qutz|od_ajhokimmihltr~uw}v{~z~vsvstncjgddozxxzuvvys|~wrmouyzuxv}|z~wtvu|zmd_ehkphjonhhosq|tw|w}|{}tsvtwlfkfefqywzyuvyys}xtoouzyxwv~{y~vvuuzxmd_ffmpilqqihqsszux~y}{{}{rqutxkhiffhrzy}ytu{xs~zxuqot|z|ww{w}~wwttwvmc_fdnpilrskistv{uw|~|~||yqmqrwlhheghoxz{tsyxqzxwvtoqz|yw}x}~}xxtsttoe_dajoikqvojsvw|tu}}{~}}wsknqwnihcfhltzuqwypvvuvvqnv}}w~~yy|{{vssrqi_c`epkkpxulqwxvuy|~z~v|zlknvrlkcfimqyzrt{rtutvwvoq~w{}yv|z{{yrrppoedbboonpx{sqyyzvx~~}x}vw~pikuuopfgkpqw~trztttswyzsp|yz|yvxz~z{{srnnpifcanpqsy~ytzz~}}xy}v|vv~~rihsvqsiglrsuvqytutqvz|vpz|{|yww}z}y|}trmkojhdbmpsu{|wz{}{~yy~}v|uv|~sigstrtihmssuwsxsvqpv{|vqz}||zxwx||y~vrlkolkcdmotv|}zy{|{yz~{w|uy|}sjhtsuujintst{ouv~wnonghloopqpyqe^U\coy~rrfovzsp}iWjzjdfbkkjq{}y]jXO[npnfY[bijfjqz||sulcgsvv~yxvtutspmz}~{yvspcjprux~l^_xvwolja_frvynORv|]TW^icciprnlrx|slmjgxwmllgsyvpyu{{~yshdaeio|{txs{qeclzokpspvfLMa{i\b`_fqxwstu{sojc\]c^k|xugemq}~odnsv~||smkjq{tvvywhghv}|x|kPITmuig][cnrqolm{ywticb`[Xbcbq}}ucddhs}~}}|xmjvu{|||yy{mfgj{~q^IMYvzpgZV[cecc^colnh_]__[\hfiuxihbfnvww{|qvwvwuv{~|rrxv|{~tedfm~zogVHP_|zm_Y\ba_^V^gfje]]ab]_khnx|pldgouusxwnsruvuxvuvu{|yn_chpvndbUKUdsg`aedcbX^ghmjcbff``ljqzwrjkryyvz|twuy|y|ytqr{~vl]ajsvmd[]WOZizohhlljk`bjlsqifijc`jks}~yopv~|}{zrmmz|~}|{ql]`lwzpf^UWZU_ouoloqqslfmpwxslllh_ejswux{pkgw|{z}|x|xnmb_mzy}vmd[RQ[[fuzuqsuvyvnqrz~xqnli_bgr~y{~yokeuz}{y{|vx{~~}zz~|uklfbp|y~{{}w|yqkcZQO[`m{|xuvwy~|tvt{|upli`afr}~}z||umkeuz{{xy~{uvwz}zvvzysilhfry{}xwzsw}}~vnibYPP\dt}zxyy{yxt||vqlg_bet{wxxpmjfwy|{xz}ztvty|xsswvohlikuy{{}uuvpvx{{skhaYOR]gy~|{{z}}zu~}wqle_cexwuttmmih{z|y{}yuurz}{uprvulhmiox{zy~zsuspuu{xojg`VOW^m}~}}|{xx{vpjaachzussokmfm}}~|{}}xvtr}~~yrorurikklsz}wzvrtorruztlif_SRYat~~|}v|~zvogadbo|vsqqkmifr~~|xwqw}~~upotrljglki}}ysr}zshtrnv|{rojd[TXXgxxy|}snh`iiuzsqhjnbecs~yyxupqy}wsnhqmimjwtoy|x}}|~~wnpqppu{slnbaYYedlv}~w~~rvredlw|yyvrypljhXZr~|zxtkpvzvyymmmh\jmmxz}{|}~|sjoknquzpliWNWUfpw{{}~knmhmer~rgnfklflees~{}|squ}rptmtonmghnus}xuwz~pqqncrtqqikaXT_ho|}~}vlhnggr~zqjkmfhifdu~zt~vvxxzrksnkmlghrrvyz~vvynrorrnotxigf`ST[epy|wijihem|{vmegcc`dfny{xv}rv}~pnokjdeenoxz~vyujmnkepnh`\\Z\[oz}}ywrngdrltrolndekbgiqs|u|xvsqvtoemmjmnqo{xuw|yrwnkedgkokqc^\\X[mx{~yz~ywfeggt|wohj_dcfntzxzvz{{syvv}}vghaekqvy~~~znnx|zpsgfdfehmddbd]]ft~}vsyz{okfo{y}woi]]Whmp{|vywnrwymg_lekr}~~}pqr{wwrkkjj_c`bf[``fc{~}szxqtqkaqt~~riccYYhjyv{|z{vsxnwur^acdlhx~ytyzqsglhab`aZg`a^Xdlz~|{svoujinnrw}}vkW[Z[]ju~~u}}qwmrokohe_ghsz~~y}mrtymmnegVW\_^[_^ijy{~z{pronnlsv}}~wrdd^bcekut{~~t}{tonnsjkfhiqx{wvmisokbb]`\`fc^\ahwu~}|{wsuswsiuu~{zmd_h^`fj~~}~}}|~z{~xrstxq^b\iqtt~~~wrkpuuypg_^\e_`cYd^hm|wujosmrmur}~qo^]\a`cvw~vwmmrorhh]jmt}w{pmo}von_cYZZa`\c[hcj~}~owtuuemov}|{yxcdXXbblqy~x}z}}qxrtvcf`adio|qrirsvnegZ`TVcab]]_kmz~uponlqrn|z~~{n`X^Y_lrx|w|xqollqpdjkimuz}z{jqolulh`YSYX_iebfhm|z}}|}yrprmuvrx{{{ih_`eaijtz~zwxpxvovfXhcnovy~unjvygpjX_^[a[^][idovmuqlomlm{}}}xwh^YWbdgrxysyinpiakgnqv~y~{tirvtyha\VUT]bd`mcjwn}x{xvqlxsumixx}{zwwvqoiYfYYhpzy{|}t~puuqk_celyyzx~|zkrfppfibdW[WSbfeekj|vvkiqkglzvtqtml`YZ]eivz~|zxiqnilmfguvv~uzz|uxok{jjfZ^YSU]afions|{|yu~rhvvnlrq|uvspupsxj`YZapr~yvyrt{pmnfjskw}|s{|{pigglscfgZSQTgdasjxzv~|mvielkyu~|~vvlnwfieW_cmp}y{sywuxjqojlmlhsyrqv{tyvmmnfhe^_RMSX_kpr|~u|vqrwlgguztuvqfmtrjlab`gurzv}y{yx{xqnhpqupix|~{xu{plpgifkg]XRXPZahoz}v{|zusuebpu{yu|~{{mononemgehcdt{{p}}uyqphssdfps}swpot|tyojdetaaVRSLRdio~}wsvzpkfghxz{z{zpjvohfjchqghrvu|~t~{{ytmbuxnqlmy~~wl{tyyhjfiacTS[RS\]ozzw{sz{qrkmpoky{}~|rlwmkiklqlfc`l~x{{q{sorgmjeipzztwupvhmfnhg[[OQYQUalty}{}q{pglhjfw}t}rnp`gemqjjhrs|{~sw{qqmvioppknz{{z|suuykgo]aZ\VUZX]Ylpq~{}pyr{tpo^qu|||wppkmvgwpfh^nnnyyoy||yx{emeipllyy~}ywpwkqkkkZb\Z[UXSZbxyx{ourn`hrl}~{thjmkohqdfoblqvy}~z|nqn^lsosvw~}}~u}vtgdb_\\aWY]UZ_kozr{xp|ztgkfk|y}xxxhpsqrhibmbdnbk}}q}tthbghvqpryuxpr{lhe`ceb\_MRYUck{uvwsmwimikwp~}wltenwfieodaieot|zzxzukbihqy~qn}txwko_Xib_`bUUZY`]rt}vzyttnxoewn|{}ss|onqkqdknim]etywz|wdommtxvsuvzuuyqoug]dhlib[VZWX]icuxvwxtmqsmidpswyssrpkgonjeembfsv{{y|}}upinrjvyux~ynnooi`fjfaW[TO\^cp~xrsvilunlosp||z}{rqk_imghkgakl{|yxw~yrqrkkupyus|v{yuoloa`jb[YWOTWZklvurusjnjkoak~tzyqrpg`fbllaouln{|u{{xwnvmdsrpx|sqwnkmj^]\`hYT^TSZko|{xfkulnu{onq{|tuq{wgni_akngot|}{}w|ol}yytlkltx}yppkpugin\X\d\]\_a`dly~onjmoptuws~~zppyvqokkgfhimo~zvsrw{yyvsljvwy}x{xlkotmmigb`]YYRZb^cuxzsohrojs{lr{~vrnnfflc`llgz~z{|wup{|rrzkgnnpw||zxspjkf`f[V]bR[^\\^g}|~pdmgfgjtxsyynpxhhkc`]_ltnww|vfsuspkmvpeuz|qxukisecj_YRPY]RRc`jrsqvqclnjpw{}{tidlrd[gagkz{zz~yopywktypqrzrqsv}tqh`glaV_Y[]fd\W]fp}|tzrnltw}zszy~~r{tqqxwjaaflusx}yxvt}xww|y{tkpisxktonmsth]Z\`f^^]Wddhuwknelsppzwr}yvuw{roi^hefkwlx|}ynuovyrmsmexx~w~rrqrukkgZ_YXZbQY^\dcssuodsnqngs{zyvzrmmvccf_d_ew|vv~ztwvj{xvpdkpkft|otojgpb^aUYQS``XUabqz~jlqkaiosy{yqqgerqe]eajkz~{jmutmpsrur}yrvqylkd^loeVYU[[ake_iqz|{r{ynitwy|sqmmopzqfiqs|vvxz|w{szzrxw{}puvt~|ojifjfsoa]ddjcYb]dnt|}~qosjtvxw{~txv{xijebmoqv{{}x~wq|ssutpvup}{zmqqqtfbaU]_a^h_epmxvxqnuhtstyoyy}poqqnrnfrlsrm~}xwtrlvrw{qvklz|{{ssnejihfl^ea`cX`hgejr~zpeqvsotu|}~~{~{v{otlgplgemr|qsfjwytwtx|zkvpspnggi||voo|xkg}ehniwuivnux|sy~z~~incqtxrxwdkks||u~vswrkvt{~qm~|u~uggzzoxlkqxvzlt|j^c]ekjnttv|gltqo|}wq{zunsqa_}|tpnt{t|~nlrmtgigctywyuunmyn~xppzxxnhgcdgjjpljpnzrzz~x{|~nfX__p{u~uvxz}~{{pnyebeacaldnq{|q|wummjv~|}~v||shgseoumsofmymp~~wyzx{xusn\\``r|xxy}yzqxxvkkaf[clemssswsx{npqt{~u}|xvoolrokrrifosqsz|{rupmcWQdhq~v~w|t~typki[[adgmpv}typrkgku~ux~tlkmnpptzdqosqx~pu{oxpbV^_h}~|wzzs|truoec`as_fqrspv{qqppv}y{v}mtqmrlyligxoo~|~xvsyyjlZU]\kyxryzx}mmjZ]Zmcfnyzpxztinmny|v{wvnmvoi|skrruuv{}}{rqvxsoh_]]`nvv~xy|ttqhf[kh`hjwppwrrnowtpuytxxrukrlkjstt|~zztt{sqja]^Odu~|}x}z~zuvgbcVfj`iwwn|{~hpqlqv~xtzvv~}qtsvnquoyn~wxzx~|~~ztltxekc_^Zgw~~y~}w|zqmg^]k]kjqzm}xwlsuvp|}qx|n{}}v}kslmtqsv~r~~u~zruxmldXcTUg{tzz~xyx`h^Xd^kjzy{ytpqqurqxujyuutwustqhxvs}ywzv{|~}tyrkpkgf]eb]kzx~~zv}wdibccbnn}wwwy{lnxtrw~~h{txwx{ooolwvxvxww|~}t}wzvvqlff]^]`lsxxz|w|ih_\h[guwy|z|kunpulu{v{lir|{p|uppnmyx|zzwz}}}{zwwpmshXi][jcpz}}xzr`a_ebmuwr~tzottrxvxvy{ymjus~}qvqslzq~{oyvsz}~z{w{onqoj]bZ_bfxv}t~~odfSfbdqzx~rxwtrout~}~y}tvtglru{}}ojrtn~yxqxzx{}uv}{qmperlU\^_fl}y|}z{nifYgiqs~{ypq~pqvqrwq}r|tqst{xy}ztqnuqy}qstv|x}x{}yqmolhso]]`bko{uu~}mpg^cbqvx}~}s|vrslur}q}t~lnsw|uv}pjswpztww~pw|~}~qlqlmoe]`Tfhm|z|hlhYbjrur~pyvtqqt~zuwuwsnpnv{wm|tx|{rn~slx}w|{odmjfp`^_\bhwy~}~uoo[`]lwsz}zzwuno}}syzmxqfpq~~}yuutu|zyssvnp||z{xz~qklhfocbaQ[fqzz|{zknf^^fuy~}|svxin{}~{}ttlwxjmr{}|tu|qr}}qkxrm{{}wxzjchhmlbj_Ubmy{xy}ztmdaelpyx}~yuokny|{~oprlfq{{yzyrpsoznywv}}~z|uoghmtiacXZ]txww}s{f]cbltzyxuuwql{yuzmozndmv|}{ox~{tsmrtpzztjkkamq]h^S\gqu}z{}{txi`]ijr{}w{rt|xy{wxqjvrhgi{wss{yovmlsmu{nlggdkhbd^YWfw{z{{}}{r}wb^emnv~zsrsxyw||svugppljp|ww|t{y{{snfrsnv|zlemf\gjdi^\`iy}yx~~u~l_jruux{}ynozpy|{}vvuoqlqpwxxvut{}}xveqvpu}}~mfklaajhp_V]rw|~{zzyzogdqvqy~z~unt|ttz}{wohsnhswu|}w~|w{~rrklppnzyujed`cbbqaY]k||}s{r}rikprt{{pq|ry|}}w|snilmkqwz~}z{zt{~ssgislhv|~}wj`bbYbajg^Yo|y{xvywsztummtz}}|qw|mqyt|yxnpdnsfl~~w{z{uz|vnojmklvzyy~xf`^__^Yjnd`n||~~qqyw~vvyuq~}v}ysywjpov}strsjntnl~~wzzysr}yrnntiky|~z{}yrjd``^c\hrkhnzvzwqv}ysxzxyuvzsuvnsntxqrwppnis|t||xvz}wijrogyu}{~~~okhe`Y\`nlmgn{zy}zqws|rs{wy|m`UOVUbluyy{wwnu}{yrv{|ysshdW^dej|wZfvwqvuuw}}okjt~{|pguy}{wtccns}tsogW\o{i_Y`w~tov|ws~~yvxrf^Znyz}tt|pyzw}|uwmWM]upjlpw}lksxyww}tkpnkuyqxz}wplsxjb[ekmxwqrsztsntxuu}}z|qfl}umkeg\^ZZY_jjitz{z|xmbWX\healzz|t~xnqnm{}vuuprskmoytmlnkxwlcjhrw~wtih\RPJLQfrv~wxxzv|jc_fiejyy}vrndenkt|uhjoprowyxy|qllemmr|{sueb\\]cnpq{yzthhhgf{uv{yr}|wrv}{y|xrtt{pnmrm`Zdituvtnjdjx||wunipsxzxu}~xunovs}}mgjmhgt|~~zulo{u{wml|rqkjikrea^b[RXl{|}tsohmrqvkoponmkc`hho{}tpkruoku~zyyqov}x}{xoelh^_lv}|qngigkunpr|xlmvztz|sqmlklkddqrx~qwssliqu}~|qigix}~nfgh_cy}t~tjhb]]dmis~wyszy{w}xpf]gd^cbw|wsltvqmqyy{~y|~wt}~yvpaah[ben|khg]aet|qpqlZalr|z{uy}wlrvyx{wrwzuiv|tha_hw{zrktq|}|yzvrrrprtyyurwnooenw}xut||{{umvm`bhe]icm{z}vwzwnu|uhllmsrqsofbbevxsyywz~{smw~~qkpw~y{|kp~phjeejx~{{ufo|mf{qi{sutmmosrvvu~~uZLJWd{mompmljqvxwirwvzuwz}{|riZXarws~|}tw}upq_]fz~h[apu||uqehpzwvntvxy{}~|yootss~wjdbispxnnpsqx}u}xgblehc]LTY\k~~rr{wifox{|l`aamffp|uuxtumb_QU_s}wqtt}tvxwsmsy|rpnw}{||lYKScuqspit~~wlb_lhh_anvww}wpqibdds}~oheelkzoqt|w{srtroo}}{|xspu|x}x}{hlsxvp{pbee]Rbcmhbj|rnakjrpn^bklh`_hfdgipwyyxyxr|~t{}vlvrx{slckyxrhotrryxstrwuqnxtukir}}zwuzruw}qmwr{zj`nkc^eevyrwvrik_bhesn}zsy~~{w{nfn|y~yymemsos|zqllm]`\Y[_gn}ntvlbmnlr|~~twz|uqr|{{tmmoomty}|wpupgsed`lllz~|mtupwoumpzv|vzwpqyuu{~ptyuhbe[luy|{|plvt}pikg]Vbiijy|{vxr}zvimjryqwzx|zu}zzo~}|{tnssyunijpu~wrjamkll|nivrzqtusoqmw~~~l{ljpkelms{||x}ywnmgfktok]_\gy}ypgpql`epxqmtwwzhmnlt~z|jgkvmSUboiZ[dwslrrwhdecmysgdkyvroiw}vvps{~pjbWco~~xu{oolrvuwrnjq~zsyn^^_bisyvpfhjiffhi|yt{vuqvwojbZ]VXWhzz||v{{xuuoiiilmw|yokuzhily|zvs|}{pww|{y~|rn\XTV]aqwrump}~gdotuaX]dw}|{yxx~}v{xtoqfU]\gkwz~yrwzvx}x{}rosiswv}xyyw|{jki`dkopbmy}|rv}x|ysztnqw~t`]cfqxzyhkklqzpprmssnokjlfd_iu||wbgpntpz~~}rccQVekx|yuljx|{qrq|zzzwssksthiYoyvuruqu|sx|uv{{rbVnennp{qwqvx{hu}{~{ym{rkuwjjfaWRWRV^bmomnuunimhci][fn}|}~u~{zw|t|ystccfdmp|y|jgflnptlx~~|swpivxlouqz}{{phrnprw{}oijajcinr}y~tiovlgkthl|mj\dhnpjyxxw}tvvy}wzu|rzsu}}{{u}gkgof^cWZ]dx}zxys|yxroz}wlrzxrv{{u{|yuz|eeaXS[eqjkpzl}~}np|~|||~}v{muvwwxy|{vuxvuyka\bdn}wzurkjltwptoknk||~xiqwsw|smhpswvutuulmhx|w}uhxvqjolzq}}ylorlzwSSel{|no~snkgitq||tu}ylc`ZZtutnk{ufsx}}y}vvzsrmgw}uvoxo]`lvv}x}{z|xxsmg]j|oupqx{mm\OKYcv~~mbfrno}~u{xzxgiml|}~xxvmacov}z|zx~|v}tt|xz~sfYPUijxvws{pd\^ivukWGWemwrwpdot}|qkjaW]lv{|yqlvsnm__di\Yimt~ytywgbmlv{qa^Zdtzlc`kpoqn}~yliphpzqy}}qp|vux~wyy}yvyq~z~rdZae`achqx{re[b`goz}s\aejwplrs~qfhd``lux|v~{u}qq|y~|ru{~twvvyumkq{{~|lc[VQO^h^nz~wtowxccd]XZdgjc\noig]SEQNNaszx|vkrv|yqbac_gs{sooidg}wou}ytzyzswwukZbfforxso`\a_SZ\fnw~z~y}ywmlz{~vwuklhmnu{y{y{uqjd]mjmu{tmkhci|psr~wtxv~}yrwplckv{s~y{{}wo\a`krywpdgbeervrxqmnqw|}qqqztvjfe\WYdltuuz|w}}vxvyxzx~wjsrtyvpmuvswnrqu~~||vqurtt~|~~tibeow|~uu~x|{kosehnroxurvvov|z|wxsjtx}}ld]caimnyzpgjhnm}zstu}}z~mqpokpz|ypoklq{z~v}yxoptqqp{{|txlix~xtxvtpqor~|{y|ww{xtpjv||kqntsy{uqq{ruj]`YXMbopvpvv{rqsyqbjjjuq{w}z{~smnu|xzrzsdeY_ljmlrdadfljsvytof_jhfiZdhpjpz|uotljipzusitv~}zwv~u|zgsw~v|}yxvwsygg_d][[_^`c`q}o}|qusihdoppcdjnqmklvlYb\__[lp~x~|zinncq|voup|mobj{z{vynigmy{wuxuzpn|yuysqrz}yhmkokibhrdcsw{x~wuv~|uz|~{|xwtpe]O]jnvyx|w|}z|kdwt{wmomtho|{svspjkpypckmu|~zzokttrumtvu{{wwzqpaaah}svql~yshuxsoy{|liyvwqmjn_eijl\bh_``jyxvpqsjvzx~~z}usotoh|zx~q|~usqk\hnidosv{iaomuojc_eZ_cmypz~{ror{ospou{v|}vx|u}uv{pkpoow~}~yz|~r}}}jpvoehjim\NWWcfmpsxqpsuqmqytyuujekdhhrxtwgr}wxnmohpxxywwr}|~wfmrxysu}kgtrkltz{|~{ytzyplwwvskktrqxnnhYabjq|{~zzyzuvqt~~xzy{y~~}eefgpldesmi{{{uy~~~rrtttlz|pksyyukhrvx|xuqgwrebfs{wuut}}vzyrphlims`cjpw~~xvwwtwor|ursqpcrvnrmnj]V_npy}z}{~zzz~{vv}x~~|x}rovjkpnojopqwxvyvrqpp{ypthlt{{pmllh\mppts{}uq~try|}sptzvz~|}wmegles}x}rqxox}z~~yztrqmlvniiX[ahf]_bkknwx|~|okpslr}sslpzxzyuu|uyuqqrpu{lesw{~yzuqx|w}tvrnpokoxpxqjuztw}xsnptv~vxob^da]hs}{{~u~~}zrsppsuz|y~zu~~ypqwzqrtjhg^bgik|xr}zu}y{|qqtu}}u|oirxx~|y~pw|yuqojluwtkv|u}|~|ro}xtifjqnoxvvyzz}qv|zxultwz|~vjmjdpo_]`fp~|vztvur}{}}~}~|tuoyrlryuqquph]iqtspy{voy~{xvmmtuzxy{olqkp{vx|vxxww}}{qxthqyxywvrrjw{{ytv}qoy~zz}|}vzztpvt~xjfqvsmjd^T[\eoou~}|{y{xyy~~ojqjes{|xz{xuvtmgpqpffbmupv}wsuvnmps~vjiuyzxzwsq~z~zuvt~|{rojigkqsmppffiu{x~ywxzusuvomz|~|wyzxx|ywv|nihg^\^drnhl{v}xxv~xvnnmqy|{~|zzxytqy|ulutrcaekmp{{{wtxry|qoxolmqoos|wq{y|}~vkpsqofe`hjtvqqwsx~|y~|y~yv~xt||v~|{ognrxqtu|}}xw{ssuufbg|njm|y{c_speoxqokx~sq|nhni{ush\^dffbmoihw~|}votm`XY[TMXemrwzmkmqs}tnzzucW`gtqmgnpoy{f^b}ihjorqwpghot}}~ynqlppzyvma_THJbyl`OFR\j}vwqhhvyxonsqnu}zvaZ`i|zmnmou{||~{haTRRevwwxtiht|}nkly|xkeajhdlyr|sbYIJSa{}x|{~yrpjhfqntu{yxl]UKKN`nwooonfm||rln}qgckhgu{h`ffs|{xpnhpu|}zsrxu}{yv|vk]TRWfq}{oonlfs|zh`W^flojpxv|yu{n_adht{roedfgt~ymcdb_ijrrmikiigv|~nhchmpy}~||pquyvz{sp|~|rtthhcjjswpqv~zry|{nfd_c`bqz~theg_gkqwtrvwywpd]]`cjz|{tturnw|wqjru|{sw|xszvhhgnsx~nkbc_Zdq~ttjhnpywstvuqo~teb`cceouptq|yrtvzv~zy~~zwy{svnddjrrw~|sihgplvzwx}z|uv~uzpligjdalrthcbbagvywoeiglma`dosqutpsfgga[_ejgqpu}pmkptq|zz{|v|{wyljaa_WXbly}{}zsuppvwx|y|{wvgfc[X]djn}~yswtouqpovvpmnhqonka]dlofjlghcggadku~vqosqp{ztlhkgrohgltvxz~vxvqpsxz{{x}||wryz}|vtjie]]dqxr||||xrrvts{~}vsvq}~yvz~zyyliaZVX\^crrzwqjpkgkgjlrtqrrlww||tpw~|{yrquymjmljszvqtltwronqqpvpnrigaZXZ\_`onquv|wojejlhnkpu|u|{}wnpvzvvyrvlfb[X[dw{|}~{w{xnrid]SNPSWZmv{||vztsw{|zxymmoltnechnnnuourjga]^ctx~pghkjmz~|~vv}{}{{yw||u{utpeadfhiy~~zzx|||x|rnptrqvnrpgb\Y]cwuuxumnrqs}|z~|{x{srxuyxwtpttmsllh\W[^acvx|tqw~}~uutqzxpnrww{~{tqru~~vnmonq{~zwuwlmomrsrnkrpotmpl`[]^]]mpw|wxwttprpkpggoxzy|}swrs|{ttvzx{xofdek{~~~|z}~yvzsuslneXUXYW`qv||yopw~}zztvmswtnnopoyuw|xqg`_^hot}z~{zysrpovv~z~y~~xz|yy{|qhhihcpw|yxtyy{wvvrv{tzxrh\XX[hl{wuv{wqomjmon|{|~~~~}y|w|ytxpc]]]X^ko~xx~}tx{zwwwuv}~qiihqs{|y|upkgcdhfrz~yxvqv{|unpmounrqh__`][gju}|{y{|vsw{y~~|{yxs{~z~{qd_^bjk|}{|}yw|zz}xx}vrrltqnqla[ZYTYcgw|~~~|z~~|qstttsronzx~~sigfmns~}~{vonqlryz}}uxzzxw}|tonlfmpv|z|vu|~uxxxzyvpqzv|{ytf[XX`^htv||{uuwqxz|{z}}|ww~x{zsjea^Ybcny{}yz{zy|z||vzvmjkqmw}~|w}~}ytrrkcce`ikq{|{~}wx{~ysnjfaihqz{|ury~yvv||}z{|tv{y~zmb]]daksv~}~utvpvwz}ww|wtljqmpojd_ZVR[\ety~}{}|xw~{xngemiowx|}{~xwxvmhkfkoq~{y||wqyx{~zvtojdjln{yy~wvx|yxtpsyxpjpmtxvuoe^Zacerv|uvrswu~}z~|xy|wmtstxtpnjd\afft{~~~yuw~{t{yxpinonwx~{}~{|{y}v{{vtuwrkkhjpozv{{}~zyvoehmkvzyvxwuvyz{uv}}txtz~}woh`deeoqy~~wvpquqz~~|x{~|{~xnrorvqmmjcZ`dfry}~|z~xqjnjmsry|~|x~x{sjkj]a__bej{z}opqrt}xhbant~off^ksuxzyzxswqogjaamiv{zuhb_uyws}|w__hrz}ghx~ypsnirlv{}woregp|wbn|cdq{y{skbebnvnopnniabfr~zrmiknyy|znkkcge^grvyzzvyz}x~{qjtpwzqkhllv|~|~}}quvs|lg\^dlx~p]RMirj_`\auzwuuxu{}oqrr~}}zvnlkqrz|{rwtnfggclihtyvovxtgeg`oz~||vkihfclrddfgstkcgr{}~~rroo~p^WZWlumqopqlmjeclxpqtqwrfZ\cfqttuoknrx{xwzzyklkikhhkrx|z{}xqy|y|t}}~vneadnilsr|}{w~usxqx|}uypeXTOWtsiinqneZ[XY^ge\a]dhgb_hjrtsssttwuyuvz~yx{{yuzpoplhglnrx|wsux|}~yyy{~}qga]oy}}z~|}}{rqpspupmy}ylbfmaWNRXlyohi]YX^\[_^fe^OOUZehruywrx}~zwyuwwopv}{wvvv|qqu||~}w|smorolpx~tumfhmtqvy}w||xxuzwy~~}~sgek`]UW[j{}{zonbdgmnhhgomkchos||{yy{}~xxu}yv|v}z}zxo`_arpntvzxolmiqs{{zz~}tzw}~zyuqejhmt|}z}{{{{xmrinn`XTd`^VT_qxypommqnnqzxukehhsr~z~~~~|}~x{}snahrskml|wvoih^`]dgfis{wvlqq}xty{|rxuw~~zwyxzsv~~~qisnyykd]haaZ[jv~|{qqrt~|x{|zssw|yyy{y}{x|ynwvrl_flymqrwvwkga`gfrtpr|xtqnqnxu}quutjdkfoy}{~t}}u~w}}|zilmdnb\RV`_cW^l|}ztzty}ww~uvowx~{{|v}wpwpqbboz~svn|wvuhf^c\_hdcouqsllhry}}souuunwtv||uxw|xtuxzyu}}~ohrhsslf^gdg[Zkv~|tysw}zyvyw}yy|}}ujrqte_kp|txs{{lf`fbhurnw}ttmnjqwwy}snutqejghsy|~x{{wzvv{y~|qktgmib\T\^eYWem}v}x|z}zty|yw|s|z}{}xntrtc^jpw~|}ohciacmhcmsnqmkfltz}}rmstqjtrs}~xyzzzwvwv}xxqx{vtkshnpok`egl^Wel{zu|uv~zv{zw}zx}~ykmouj^iltwvjcjfeuvpt~wwsmjlwz{ytlsutiljfpuxy{uz{xwtyx{}}z}~}{krmglfeXV[caT^fmz}~x}~yv~~sx|~vtzv~w|xzrkotubcko~|shhmelslfosnsnjgmt|{{omrtoirov~}w|}{zvxyx|zysvwr{}vkqfehjj__fnd[elv|v{y|xxz}vz|zvyz|qgjrsdhos~xuhjnepxuqz|wzrlioxwvllptnjoipuy{|xu}}xtxxx~~}{s}y{ukpfcegcWT]h`[gnv||}yxzruz}uwyx{|rrtvofhsxiipu~zxiiqinwtlntptpiiks{xwplnroionvy~}}wtwyvz{}vvrxqs~|xzymmf^`dg`[bpofmszw{~xx|{z}xx}}~rorvrhepwllsyx|~mjqinvwrs{w|vlkkt~ytmikrpkpou{|{u|~}xsvwux||x{pt~{wvujjd\^de]U\ljemu}|yy}~ytrx|wz{}~xukjpsmdeuyoqu{zzjnrltxwnprqvmgikv|urnkmsmlpp{~zzttxwvy~zxrqwwluyusvtki_Y^dd][juoot{|~yzy~zy}y}{|tkmsrldnyspry}{{tnuoqwyuqxw|ylmlr}ytpkipqmpow|}zsz}yuxwuv|{vy~nqxvvuvmhbZ]eeaW_nkilt{z{zzvsy|x{z}wqhirrnbixurqvz}xnurrx{xpsrvtgiio{wqomjprmomt{}w}xuxwwv|~}uorxzlnussrvoidZ\decYbrpnmu{{|{}y}}{|vlmtspelxtpmsx}{rxttw{yqtv|zmnmq|ytsnhoropmtyz~|wpw|}~zwyxwt{zuv{~ontsuswqhd[\efeZ^lkifmuz{||~zsy}z{x~wrigpqqfeturnpwxuxwux{trtwxkikkw{srpinsqpmpxyvy~~xyxxvy|pqrzqippsqrukf_Zbdg`Zjqpmmwx{~{zy|{|xrinrspenxurluw~~w{xxy|tuw~znqos}ytupjprrqnuy|yqx{||z{yyt{~{uvyqnrqurvuid\Zceh_^mnmhluw{||~tuz{{wzzzqkcinqncmvtsnxyy|xyz~vuw}vkmlpzwrtnipstrnuz~~v~~{zxys|~yrpszmjnnsosshc\Zbei`^nssps}}z|{~wx}||z{{}|}sngioqqelyywqx|y{zzyxuv}}qpprz{suskorutpt{ux~z~}zzxztw~}sss}ulomrqqvlc]X_djf_muurp{|z{z}xtx|~zy~yyy{{|oibcimqfjwyysy}}{zyzvv}|ommou}yrtrjnrwvqu{x|~~~|{wyrv~|sqoxphkipoptia\Y`dkf`nvxwvyzz~|wv{}}|~{xyvz|yynidglqqfnz{{u}~||{zzzuvzpqptzxsvqmqtyuqx}u}||~||xwxqz|xspt{olllrntsg`[[aflceswytx~~{z|}}uxyx}~~~}vwu|z}ricafkqjgtyzuw~}|z~~xu}sonpt{{stumnpwyru{xz}~~~|xzru~}uroxqjjhooquj`[Z_elgbnuxvvzy|~}wwz|}|}}zwwt{z{ukechlsngqxzwu}}}}z~zt{vqqsw}~vvwopqxzrsyyx~z|~~~|wysu~~|urpyunmjqqrvlb][_dkgbmswsrz{}{}~xyyxz~~|xvs{{|vjdbeipkeouxtryz||u|wroruz|tuwooovzrrw~yw|y{ts~wtpwtlkgnpqvnb[Z^bkhaiqusqy{}z|~zyy~}{~{~yywsx{zylecfjqphmtxvrxy~}|wx{tqswzzuzspqt|vquz~u|{||~~yxwp{~yvptxqojmsqvth^\^`gkcensupux{}{~yzzx{~|||~ytszy}thccejpjgntxssxz}}u}xtptuy{uuxqqov{rrv~zw}{}y{ur~~~yuovvnkflopupc[Z]ajjdhotuqwz{{~}zy{{~yz{{wrrzy}}rfbdflsljqvzuvz}{}u}xtpuwz}wwyrrpx}ttv{x}z|z|xysq}~}xtntuplgmqqvqd\\_ajkfipwxty{|{}{zz{|z{y}xspwx{}uhacdhpminuzxw{}}~uyyvqruvzvtxsrpt}wsv|x|{~z|~yxvpx}|yvoquplegnnsrg\Z]_hmijpw{w{}}}y~~~{{y~z{zw|{wqmuwy|uh`bdhrqmqw}|z}||uwyupruuzwvyusquyuw}y}{}~wy}xwsnw}|{xunpspleioptsh][_ajokkqy}z}~|z~~}{z~x}zx|{wpnuvz{sfaddjrompw}{{}|~tz}xuptuuxtuwsspxwuw~z|z}x{wwqpz}|zxrmrrpicjmpsocZ\_blmkns{{{~z{~}~~}y~~|x}vyzyslqux{xlaadfpropt|}{}~~|wv~zwqrutwwvywuqt~{vv|z|z}ww}ywsnv||{yvnpqqlehnqtsi]\_ajnllpx|z}~}{~~{{}xyy{{wnpux{zpcademqnnpw{y{|}zv}{ytrvtvwuxvuqr}{utyzzy|yx~{xtpv|}{ywoprrnehmpsrh\[__hljkmvyxz{}|~{|~~~yxyz{vmpux{zpcaddmpoopw{yzz}{v}|zuswuvxwzywss}|vuy{zy|xv|{xupt{}|zyqqrspghnrvum_]``hnlllsyxyz~~}}{~y}{z{|zqpvy}|ugbfelrponsyxyx||~w|}{xtwwvxwyyxuqy~xuu~|yxy}|vzzwqry}~{{uqrstlhmpuvrf]`aemmmloxxyx{}}~{~|zyzz}wosv|~|qeeghqrrqqy{{zz}|}{w|||vuxvvxy|{zuu~~xvy|yw{}xt{|xtpty}|{{srrutjjotxxre`ddjpppnszz{y}}~|}zz{{{~wptx~|rfgijrttrqy{{zz}|~{x|{|vvxvvxz|{zuu~~yvy|zw{|vsx}tuyzqj\UWVZaee`eolkgkgguwy{qsu~xhdb^`djmqrlpxskeimnrqtxxxqot|ssxfv}~tt{~vpfjw~ugq|trqvtqw}~}vqszZSldTUdt~yurqf`eksutkcfgkruxwyypsw~zqnnjjmqtuvqu~|zsksvsw|~wrkcbdnpls|}z|{~vwxvtrwxystwoqtxxy|q_PMcynigejiknsxplosyzyogkns|~|wpjqsrvuvw}qmlpv~zqnqry{vmo|z{~tmikd[_cknmg]]acknqqoxyyy|~|tha]VRZt}slh`_^egadjsyzvlhllrvvtmnqpmffbepswzz{{smklntqmqu{||~zuxy{~ypjt}wxy~}z}uruz{qkorx~xvxzrkc]]YUVhznf`_bYVY`ghf\UXY]cdfa`fgidedeqw{~|~xsrqurkjlprorsrsy{}~~~{}yqin~{yxz|x{wvuxyw{x{|~wuvx~ykfa\][VTaywomnd^_cihe\SUV[`beaagjmijijv||}||z}zsqquusttrsz~}}xoflz|~}xwqnkouonry{twx}~|}vsuv{~viea]_]XT\q}z}sllotspg^__bgimigmqtppooy~||zy|~{||zz||xpfgt~|ztnjiokfinxzytllnrwz~{w|}zxt{|vrsuy}{vkcb^__\VWhzvuv|~{vkghinpttnqvzyuvrw|}zxxsjdkz}zsngjmfdgnturkeginquwrrvz{vuqqy}}ysqtw|}}}{vqea`]``\X^q{z|zpnoquw{xsw{{yxu{|~{zutsohdo}}upjmngegnssphcefknrtonswxsrnovz}|{wrquz~~}{z~|zz||}xsnc_^\_`_]cv}|~ursuxz~|wy}~{yv{|~vtonpnjgpxslmpjggmssqkddeilptolptwrqmlrw{{zxsrv|}~}}zx|}{x|xxyzwqnd]][_bdcgw~zuuvz{zz}}{vy}syojpgnlv{vprx{lnopyvsjlentowpiqrrmokowoxpqvrqt}|{uvy}uvoz|~{{vvz~|xuyzvpm\^_acifit{|~xx{|}}||}qrx~}{|~|jongqmlqw~wwtxpnxuxwlcglowrrogqvyspfgtpxvqvqln|{~}z~~sxxx}xqpw|y}yvq|sno[^_\[ckk{~~zzxz}wz~sq{{yy~x}tqjknlrqmp|}qq~xxrnyyzwqqljjttrqirxodggjtvxxuqmvxtq~}}zov|}~z}xv{x{~nosswqph^]Zamollz~{{|t{z|zwvx}{}}~}oihljtllz~xsuqyvytlaillttqrstoptkkmurxvoppry~}zwzttw|{vjq}~~~~|zv|wuqqkg`Y\_``lmx~xwxtzxzy}mnoigilmtzyv}wtq|~{qkntooqslrotqmdfpnrqruyssw}{s{zxqv~wrs}y}|z}vpmxtoie]dbbdklepzz~}}vvusz|xwywlffiknns{{rx{zvplhmrsrjml|xqlnminrsuworv~v{|yx~}tow|~yuqpuuxv~zutvonih`a]`dgdcmu~zy~~yvtwz}{}x{~yvihfiflop}~}zxt|y}}rnptlsnmlrvupmghlrrnzrxru{wzx~|uouyxutr}}|twx|~ytqlqkl_a^`f`dgpr~y|z{sqzw~~}}}{zwnkngmfjms|wxv|}|{wnrmprsjkstykheomusltssvz}~y|{vsnsxzxxrt{{}{vpzwvnjnsln^[\]chfklwx~xvmtw}~~~~xx~~~nkgjlplkuv}v}w~~y|knlstwoelsttkejjtvumuou~z||ztqvquyrmu{}|nptxwuhjlnlj\W][lkrry~|yy{wlvt}ztw{u~pnfhgmpkv}~yzw|zzwnnqvwtmdtlqlhjhoptmruuzuwv{ttwmwvsvrq{~yylrtotimijeg\W]aiuuuy}zwuksy~|~ptzxzxkhlnmttuyz{rtosotprhesonqkgmssrxoyv~~~}{{vx|wuyvrukppwzzuurkuttmeihmkfbZcfipp{}~yztysoy~|~}xvuwxnijhpnswtx|~|wppmu{trnptijfaiovtsyvy|}uvty{|ystu}zprmsxt{sousuolkjmkng^d]cfiwss~y|{}pnrv}|{utz|spjdkgornzx~wpqvwuxpnqmphclilqr{}xx}{}xyz}xpw~{vtmqptztuzsqortninmfff_`^gninvz}|{}qrsry~}uw~xvslldekhnxwx~tw{rtuuvlprjmnifju{{y{wr}|{zzu{|vyusslsus}{uqovxsmnkfjgfc[ddaknsy|z}vysrty}}}{}|wutrsjdfcktqqt~zurqwztoonsshfgnzwxxqszx{zvtuzwpoopz~vssvzttthfghlieb_^dijsx~yz|ysuwz}z~z~~y|{wrptwsnhbhnmkqvztqtyyxrkswoihkqqu{zusqxxwyrqw|zsolsyuvvx{sswqicfkmlje[_dcksz}{~xy}wyxyxq{||{~~xz}z~znpuwvplgikekqz~zttwwxwvrrtlnlppjrxtvswx}w{~|zqtwzzvrprrmux}zstsnmdgjmokhc_`akqyyvzxw}|{tou~~{~{yxx}}yrrrwtqqpmfcjoz|{}}y|vtxyxokpotnklmrqr|{~~~|{vtzw{vvxusmipt~zvvpnjimhmlllhg`_jmvuxyu~|~tqutzxz|~|vv~~|ttztvpptrshbimvw||vtxy{qkorupmkgkks{~{}}zqv{||sszyvkjmqxyxuqngmpomhklhjddgknpx~||z}zvvttsq}z~vzz}|xqyy~slsttrkjfnptyz~z}v{wywsrmtrroijciuw~|{ttyzvtxuzsmmkqqwwrtjlopxjhkgkgkkdhhou{x}}zzwzxvujt{}z}zxww~}tzw~|psrrtlplikoqrzzwzyuswsrqspmmefmr~{}y{~|zzzwx|uvttrmnoqptvmrpqvookdihlnjdcikszu}}ywzx|umts}~}}~yoww{w}xrutspmrlsqhnfrsx|}rsqqutmnpemsm|syvyv~|{z~spsqqtrotrpmoryrjskeofsoiibbbplxz|}|y}y{syt{t}wxqq~u|}{t{uvmpoqrqwfiiauzrywt}zumppnrk}un|ms{zzz}{zwqmuztljgnqr{y|rmtmngpvhmcacdgaopzx}~y~wuqyrz}|}|x~zz|zw}u|zyfnnpntnckejvs~x{{s}}yyimlrppxhmsr|{{zv~txz}xyuxzvonnfs~wnjrlnjqhdijqkhf]mrv{w|w|x~q|ztxw|~}z|~{|ku{}xvuxwzrnnrzrpggijyty|wx|vvst|wqqoyoqloq|yx{zux~|xyxuvoposenpmytw}slkrzkgflekobhjglqt}xz}}y|u|xv||x~~~wzzytz{x{yrwrropuplljifiurz}youvrymvzplohkkwz{}zx~~}vwsqwtuyrjttqtmxrrpnspechecjpjukmmo|yy{}yy{{s|zx|}~~txq|yywvusomvrxotqsugnmnz}w}}squtnxsz{pxprkknqu|}yvz~uyuqjvutunmikuplvpznkkoi`ljmrnmjhxt}~}}uw}zz{vwt|~}|zx{zrwvt~~z}wlprvomqoqvoxklnr|~~x~ovru{qqvmropphkqxy}wrxwrkrrv}rtgqolqpurjilmigphmeqsltopzzx}yuu|t{wv~wv}uozxx~z}vrutpevovvovwssqit|}x{sylswqwoqppiqppwxxyupzlqttxojrjqsumhplvhkmmnonllosrvv}||{~y|{~usqv}}|}}z|tzltx}uuv{rsqnortytzpmqsux}xzzwsrpot|ymnmpmtv~}wtw~wvmj|oyqhjpvupgnojtznrloqomtopyuyt~{~|~}}|yps}v~su}y}~{oowz{}|wrwvtmuu{x|soopwlx|{uxxtmwtxsoplim}u~}pwwyzvtjowtvtnuqpqvjmrtxromoizontpsspz{~{{r{yuxw{{w{vxtryz|wvwszvoxsz~wsyyrmvr}xvhX\WXcacfjltmkmsy}{xm_jlpn|xngjmkru{{}nojenostss||~{nd`f\digvuxrnilyxwv|wzkyxfgmovx~xpmra^_\o~|{yijj|sZTu{dcsuh^_despeinrvoikt|x~~|qhfcem|~rx~}pswxulu~{r|zqv{~uxul|}niwwwrqwxzztlks|tqnmyspedmx~{{wcOMLkx|urom}{piihn{oinpz~xrr|~sxvsmpt{~|zupkh_dnjorr{{tfgsxulpmn}}}~td]XWYhk_cdhojc`jwx|us}tsnaOJU[ztieptvxmgfddlxmjnmxwnbbjo{|zztoqtvvv{~|xromadgbbcfluutptyzwoqvjllp}wrmijw{psty~xr}|{}{zu}~q__e\RMZd~~mhlstpg[ZSRVb_X]]gh`UQ\cqvwxwvu{|~y|}zuwifiiecfejt}zy|~zvrtzxq}wlgknuzv~{zsw~~}x~tvzv{z{{x}weUS`[XU_kwuv}xn_]SRU^_XZ[fhbWU_etx~|w~{}}{~pnornkoimv}xy~z~{p{|rjelst}~qmt|zwunsx|{|~t{}~xpwzy{y~}{~tprmum^QVb]]Ydq}vji]^_ggccgqmh]cgq|~|yvuu{quvqx~tm{togfo{zwglp}|zmohhkqwstx|pnswv|{|prxz}~y{}xxx}||ugmhlrbYP`a_\[kzurifikrmjmxxtkhnpzzz|zx}yv|~viuvqjajtjjmtyzlhb_dfpolo{{voino~~syz{qnvu~~zzxur}wv{vdjhitf^Q]b`_Wdr~xqmpnxvqt~zsnqqzzyz}{}xjstpj^hqnoovwzke^]aammhlxvsmikkzypxy{oour{yzzvr{v|sx}{~uclhisfaT^aa]San{sqsp||ux}wsttyyx|{}}zkosql[dnzvtsux{oe_^b]jmgiwuqnhjhvw~ouxypltpxzx}yr~~tzrvy{{{gklfrieXZabcS\jw}wswqzyx}uvr{|}v{~~z{qhqoq`^ksvyr|{jd^d_dpkfqxrrkjfmwz}tnvwuiqqr~~w}|uvwwurw{w|zsfpfjnfbU^ag^Uen~{swuty}|xuvwwy}z~~|wywifmoo^dovzyu|zkcaf_irlhuxrskjepv~||nnuvpjspv~v}tyvwstv|x|xqfobhifaU]bj_Zkszsyuuy~~ytvv}uz}|~{~|szuuhcjpqagsy|yu~}ncbh`jtnitxrtmjfnw}z{nlsupisrww~~uxxvrsvxx||u~}}sembbfccVYamh_nx}tzyt}{zvtvy~ww}~y|osrrk`fowijv{~{s~secjciuqjqysvpjfku|zyojptrjssxx|xw{vrsvvz{zty{{ueja]abcWWaomer|u{zv|zvt}ux|wyyymnoqj`eqyloy}{t~teeldlvskrwswpigku~xwljotpkts|w}wzytquvw}{zyqz~xzqfi^\_caWYetmkw}wyzxuv{uy~~w}zzsjlopfaixvmu{xwpdlkfsvqlvuuvlhhoy|wrjkqtlptwyy}wvqrvty}}{wztr~~xwwjgcY[`d]W^oskr{x~~x}{vv{ww|yzxvjjnqkadtynqx}}w~wgkngqvtlrutxnghkw~wsljnuonss{wy~xsrvuv{~|vwvnz}wvwnge[Y_d`XZlskow~{{~}vwzyvzzz}yvkinrmbctwnpu{yyinojswvmruuymhilx}vsmjntnoqsxx{ystwtv{|uv}tnyzvuwohdZY`daXZlqjms{~}}wxzzwz{{|~zwlgnroebrxpory~{mnrlswyppuuzohjju~vsojltpopq|~yv~}{tswvuxvuzxmuyvuvtjf]W_dc\Wgqlknx}wzy}yx~{}|~xqgjrqlakxtpov{}wmtppvywnstxwijjn{ytqmipsopov}~}ty~}xsvvuu{zsu{pnvuusvpgcYXbdcY\npmmr{|xzz}wz~}|~zulelqpjapytrqz|~~upvpsw{vostzthkjp}wsrliqspqoy}{t{}~|wtvuut|xrtz|mmsrsqtoeaXXbddZ_qrqov~{yzz}vz~}|yrkcjopkbpzwus|~~}yrvstv|yortzuhljo{xssnhqtssqz~u{}|}yuvuvsz~zrsv|ojporprqfaYWacg__qwvtw~wzx~~wv~{}~{qkbemnodl{{zu}|swutv{|rrtzxkkkmxyrspiouuusyw{~|~}zwwuvrx~{rst|pinmqoqrfaZV`dic`ryzxy~wyw}~wv~z{|}|yoiaelopen|||w}uxvvw}|srt{xklkoyxrtpjquxvt{w|~|}}~ywwuvqz~yrru{njmmrnrqe_XXaejbdvz|y}{wxy~~}uy~~x{y~}}tke`hlqlfu}}{y{xxvwyytrw}rkmls|}usummswyuv|w{|~|xxuvss~tsqyvjmkopotka\V]cih`ny{{xxxx}}xv|~}}xyx{}zxmg`ejooeo{||wzzxxx}vsu}wmmmry~wsvpmruzwt|v~|~}}}zzvvuq}}vspwyllkmqntnc]W\bhj`ivyzvyxx}}yv{||}yywz}zymfadiooemyzzu}|{zyy~xsv~wnnnsz~wswpnrvzusz~v|}~~{{wwuq~~wtpwxmmknrouoc]X\bhi`htwws|zyx}}zw{~{~{|zyvx}z{ngadhnpfjvxysz}}{zyzuu~zponsx~ytwsnqt{wrx}v|}||~|}ywwpz~zuqtzpmklrousf^Y[`fkbdquwsw}}zx|}~wz{{{~}~xwu|z}sicafjqkfqwzvu|}||z}~yszupnqu{}vuxpprx|ttz{w~{|}}}|vxsr~~wtpxvnmioqpvnb[Y]aiiaiswws{}{yy~}|xz}{z{~{xtu|y~|pfabflqiitx{ux}||y~xs|}tpnru{|uvwpqrz|tvzyy~z{||}|vxqs~}vspxumlhopqumaZY^bkicltyxv}~{yy}}|yz~|yz}zwsszx||pe`bflrkjty}xz~|}y}yrz~uqnqty|uvxqqry~vv{}x~z{z{||vwrp|}}wsnuunlglpoupc[Y]ajmfkuz}y}|yw|~|~y{|~y}{y|vsowwz|sf`aeiroksz|{|}z{{rw~vrnpsw{vuxsrrxyw{x~{~|yz|}vvsnz~||wtmrunkfjontqe[Z^akoilv|{|}yw|}}z{|~x|zx{vqovvy{rf`bejsomt{~}|}y|{rx}urmqsvzuuxssryyx{~y}z{xy||uvpo{}|{vrmssnjeknotobZ[_cmnjow~}}~|xy}~}}|{~}w~wz}xunpvv{ymb`cfnsnpv}}||ywr|ztpnstxxtxvsst~~wy}z||{xxz~yutmt}||xuootpmffmnssi]Z^`hpllsz}|~{w|}}||{~w|yx|zvpnuvy{re`cdkspnsz}~}~y~{ry|vrnstvyuwxttr{xxz|{|zywy~{uunq{||ywqnsqnhelnrtl_Z]_enlkpw~|~||x|}~}|{x{zx|zxpnuvz{se`cdjronqx~{|~~z|sz|wsottvxuxxtsr{wwy|{{{ywz{vunr{||yxqosrohfmostl_Z]_emkknu|z|~}~y}~~}zy{{x|{yqmtvz{ug`bdhqompu|{{||~ty|xupsuuxvwyutqxxvw~z{z~|vy~wwppy}}zysorrqjeknrtpcZ]_bllklqyzz|z|~~{|xxz{zvmqux{ynbadenpnnqy|z|||yu|zxsqutvwvywvss}}wvz{{y{xv{{wtos{}|yxpprrpggmotsl^Z^_fmklmt{z||~z~~~~{yy|xzzzrlruy{wj`bdfppoor{|||~~}wv|yxrrutvwwywwrt}xw{|{y|wu{zwsntz|{ywppqrofgmpttl_\``hnmnov||~~}z~~~|}xy}xyy{smruz|xlbdfhqrrrs|}~~~}{xtzyxsrutuwwzyxttzx{}{x{}wsyzwsnqx{{yyqpqrrhglpuupc]bcipprqv~~~{}~}}}}yw|xxx{wnquz|{rfdhhqttut|~}z~ztyyyuruttwx{{zwu~|yz~{xy|xrw~{vsnpv{{yyrpprsjhmqvwsg`dejrstsw~~{}~|~~||~xw|xxx{woruz}{sggjjsvvvu}~|z}~ytxxyuruttwy|{|wv}z|{vsuvz{qow~{yuruy|yvtmmouwoquy|{vkglkquuyx~|~}}{}vvwz|uyzzwz{yz~~|yqttu{{}}}|||}xxy{{tw{~yz{yz}~{uy{}wuvtzyqin~xrkkotwstqmos{wswz}ulnppxy||}~~~~{|y}ztwwux}||zuwzy}zpqssz{~~}~||{||yy~|}xqqmpsplnz~|ustvytsqlmovuorsxywqgikkrtwyy}z{y~~~yyuy|zuw|{||}zvwy~x{{~xoqssz{~|}{~~~{{~}~~~upplpsplnz~}wvwz|wwupqryvqsswxuphjllsuxyy|xyw~}yzvy|zvu~|}~~}xyzyzz~}yqrttz|{|z~}{}~}~xqqmosrnmv~}~ywxy}yyysssyyrsruxvrkjnmsvx{y}~~|wxvz}{{wv|{xu{|}~~{zz~zyyz}{urvuy}~|{z{|||~~{}tqplqsqmp|~~}wxx{}y{xuuv{vrsrwwuqlooqww{{z}|~yvwv}~~z{vx|zwv}}~{{{yyx{~}{uuxw|~z{y||||~~{|srolrsqos}~}xyy|}z}ywvw{vssrwwvrnrqtxy|{{}|~yvwv|~~zzuw{zxw}}~||{yzxz~}|wvyx}~{{y{}|{}z~}~|tqplqssqt~~yyx{}z}{xww{xttrwxwuqsstyy|}{}|~{wwvz~~zzvuz{zy~}}}~}{}zzxy~~}zxzy||zzz}}{}y||}~}|tqplpttsu~yyyz}{}|yxw{xttrwyxwsuuuzz}~|}|~{wwvz}~~yyuuz|{z}}}~}|~{zxy~~}zy{z}|{zz~|{}~~}y|{}}|zsqomrtutwyzy|}{|zxx|xvutyzywtvuw{{~~|||~zwww{}}}yytx{|{|~~~~}|~{{x|~}z{{{{{y|~~~}{|~~zz{z}{|wqrnotuvu||z{{~|~{zy{{xxtw{zyvwwv{{}}}{~}xxvy{~~~yzvvz{|{~~}~||yz~}{{{z~{{y{}~~|{~{y{z|||xrroosuvuz~{|{~}~||z{|yyvw{zzwwxvz{}~}{}}xxwx{}~}zzvvz{{z}}}y{~}{||z~~{zy{}~~|{~~zz{z|||xssoostuuz~|}|~}}{|}zzvx{{zxxxw{{}~}{}}xxwxz|~}zzvvz{{z~~}~zz~~}|||z~~{{yz|}~~|z~~~~{y{z|||zttpostuux~~}}}~}}~{|xw{{{yyyw{||~{|~zxwwyz}|{xuxz{{|}z}~~}|}{|~~{zy{{~~~{}}~~~~~y{zz|{{wtsoqtuvv|~~}}~~}~~||xz|{{y{yy}|~}{~}yxwxy|~~}{{wvyz|{|z~~~|}}{~~~|{zy{{~~~}z~}~~}~|y{yz{{zvtsortvwx}~~~~~~~}|x{||{z|yz}}~{~}yywyy|}~~{{wvy{|||{~~~}}~{~~~}{zyz{}~}}z}}~~}|}|xzyy{zzwtspruvxy~~~~~~~yz}||{}{z~}|~~zyxxy{~~}{{xvy{}}~{~~~}~|~}~{zyzz~~}}}}z{~|~~~~}||{xyyxzzzwttpruwyz~~~~z{}}|{}|z~~|~zyxyyz~~}~~{{xvz{}~~{~~~}~|~~}{zyzz~~}}||z|~|~~}|{{zxzxxzyyvutqtvxz|~z|}}||~{|~|~zyxyy|}}~~~}{zwx{|~||~~~}~|}|{yyy{~}}||{z~}}~~}|{z{xyywyyyxuvrruwy{~~|{~}}}~}{}~{zyyyz~~}~~~~{{xwz{}~~|~~~}|~~|{yyyz~~~~}|||z}}}~~|{z{yxzxyyyyvvsruwyz~~~|~~~}~~{}~|zyyyy~~~}~~~~{{xvy{}~~|~~~~|~~|{yyyz~}}}}||{z}}}~~|{zzxyzxyzzxvvsruvxz}~|~~~}|~~|{zyyz~~}~~~~~||xvyz|~}~~~~|~~}{zyyy}}}~}|||z}}|~~|{zzyx{xyzzywwtrtvxz|~}~~~~}~}}{zyyy}~}~~~||zvxz{}}~~~~}}}~{zyyx{~|~}|||{{~|~~~~}|zzyxzzxyzzxwvrsuwy{~~~~}~|{zzyz~}~~}~~~~||ywyz|~}~~}}}{zyyx|}|~||||z|}|~~~~~|{zyywzyxyyywwvrtvxz|~~~|{zzy{~}~~~~}~}~~~}||ywy{}~~~~}}}{zyyx{}{~||{{z{}{}~~~~}~|{zyxwzzxyyyxwwstvxz}~~}|{zzz~}}~~~}}~}~~~||zwy{}~~~~~~}~{zyyxz}{}|{{{z{}{}~~~~~|~|zyyxwyzxyyyxxxttwy{}~~}|{zzz~}}~~~}|}}}~~}||zxz{}~~~~~~}}{zyyx{}{~~|{{{z{|{}~~~~}|}{zyxwwzyxyyyxxxtvxz|~}|{zz{~}~~~~}}{~}}~~}||yx{|~~~~~}~~|{zyxy|||~}{{{zz|{{~~~}~~|}|zyxxvxzxyyyyxyvuwy{}~}{{zz}}}~~~~}}{|}}~~}|}zxz{}~~}~}{zyyx{|{~~{{{{z||{~~~~~~||}zyxxwxzyyyyyxywtvxz|}|{{z}}}~~}}{}~}~~}|}zxz{}~~~}{zyyx{{{}~}|{{{{}||~~~~{||zyxxwx{yyyyyxyvtvxz|}|{{{}}}~}}||~}~~}||zxy{|~~~~}{zyyx{{z|~~|{{{z||{~~~~~|||{zyxww{zyyyyxywtvxy|~~}|{z|~|~~}}||}~~}|||xyz|~~~~|{zyxy{z{}~|{{{z{}||~~~}{|{zyxxwy{yyyyyxyvuwxz}}||{{}}}~}}{|~}~}}||{xy{|~~~~|{zyxz{z|}~|{{{z|}{}~~~~~|{|{yyxwwz{yyyyxyyvvxy{~}||{{~}}~}}|{|~}}}}|}{xz{}~~~|{zyxz{z{}~|{zzz{}{|~~~~~|z|{yxxwvy{yyzzyyywvxy|~~}|{{}}}~~~}||{{~~}}}}|}|yz{}~~~~|{zyxy{z{}~|{zzz{}{|~~~~~|z{zyxxwvy{zzzzyyzwvxz|~~}|||~~}~~~}||{{~~}}}}|}|yz|}~~~~~|{zyxz{z{}~~|{zzz{}{|~~~~~{z{zyxxwvy{zzzzyyzwwy{}~}|{|~}~~~~~}||{z|~}}}||}{y{|~~~~~}{zyyy{zz|}~}{zzzz||{}~~~~}zzzyxxwvx{zzzzyyzywxz|~~}|||~~}~~~}}|{z{~~}}}}|}|yz|}~~~}{{zyyz{z{}~~{{zzz||{}~~~~}zz{yxxxwwz{zzzzyzywxy{}~}||~~}~~~}}||{{~~}}}}|}{yz{}~}{{zyyzzy{}~}{{zz{}}|}~~~~}zz{yyxxwx{{zzzyyzywxy{}~}||~~}~~~~}||{|~~}}|}|yz{}~~|{zyyz{y{|~~|{zz{}}|}~~~~{z{zyxxwx{{{{{zyzywxy{}~}}|}~}~~~~~}}||{~~~~}}}}zz{|~~}|{zyz{zz|}~|{zzz|}||~~}z{{yyyxxz||{{{zzzywyz|~~}}}~~}~~~~}||||~~}}}|zz|}~~}|{zyz{z{|}~~}|{zyxxwwxyzz{zzyxxwwwyz|}}~~~}|||}~~~}|{zyyzz{}~}|{zz{}~~~~|{zzz{||}~~~}}|{{{{{|~|{{zyyxyyyzz{|}~~~~}||{{{||}~~~}|{{{}~~~}}~~~~}}}}}~~~~~~}}~~~|{zyz|}{xwvuuvvwwxyyyyxwwvvvvvwyz{||{{zyxxxxyz|~~~}}~~}||||}}~~~}}}}~~}}~~}|{zzyxxxz|}{yxxwxxxyyzzyyxwvvuuuvwxz{{{{zyxxwwxyz|~~~}}}}~~}||||}~~}}}}}~~~}}~}|{zyyxxxy{}}{zyxxxyyzzzzyyxwvvuuvwxyz{{{zzyxwwwxy{}~}}}}}}~~}|||}~~~}}}}}~~~}}~}|{zyyxxxyz|~}{zyyyzzz{{{zyxwvvvvvwxyz{|{{zyxwwwxyz|~~~}}}}}~~}}}}~~}}|}}~~~}}~}|{zyyxxxyz{~~|{{z{{{{{{{zzyxwvvvwwyz{||{{zyxwwwxyz|}~~~}}}}}}~~}}~~~}}|}}~~}}}~~}|{zyyyxxyy{}}||{{||||||{zyxwwvvwwxz{|||{zyxwwwxyz{}~~~}}}}}}}~~~~~}}||}~~~}}}~}|{zyyxxxxyz|~~}||||}}}}||{yxwwwwwxy{||||{zyxwwwxy{|}~~~~}}||||}~~~~}}|}}~~}}}}~~|{zyxxxxxxy{}~}}}}}}~~}}|{zyxwwxxyz{|}||{zyxwwxyz{}~~~}||||||}~~}}|}}~~}}}}~}|{yxxwwwxxz|~~}}}}~~~~~}}{zyxxxxyy{||}}|{zyxwwxyz{}~~}||{{{{|}~~}}}}}}~~}}}}~}|{zzyyyyyyyyz{}~}|{{|}~~}{zxxwwxz{}}~~~}{zyxxxy{}~}{yxwwy{}}|||~}{zyyz|~}{yyy{}}||}~}zxvuuuvxy{}~~||{{{{{{||}}}}}|{{zyyxyyz{|}~~~~~~}}}~~~~~}|{zzz|~~|||}~}|{{{|}~}}||}~~~}~~}{yvutstvx{~~~~~}{yxwwxy{}}{ywvuvxz}~}}~}||}~~|{{{{{|}~~~~}}}}}~~}|||}}~~}}}}}}~~}|{zyxwvvwxz|~}}}}~}|{zyyyz|}~~}{zxwxxz}~~~}}}~}{zxxwxyz|~~~~|{zz{|~}{zz{|~~~~~|zyxxwwxxyz{||}~~}|{{|}~~~}|{{{{{|}}}}{zyyz{|~~}}||{{|}~~~~~|{{|}}{zzz|~~}||}}~~}|zywvuuuvxz|}{zz{|~}|{zz{{|}}}}||{{{|}~~~}}~~~|{zyyyyz|}~~~}|{|}~|{zz{|}~}|zyyxxyyzzz{{zzzz{|~~|{{{|~~|{zyyyz{|}~~~~~~~~~}|zyxyz{~~~~~}}||}~~~~}~~~~}|||}}~}{yxvvvvxy{|}}}}}}|{z{{}~~}|{yyxxyz{|~~~~~~~}|||}~~~}|{yyy{}~~~~~}|{{z{{|}~~}||||}~~~}{ywvuuwy{|}~~~~~}}|||{{{{{{{|||}~~}}~~|{{|}}~}|{zyz{}~}|||||}}~~}}~~|{{{|}~~}{ywvuuwxz|~~}}|||}}~~}|{zzz{{}~~}}~}|{{{|}}}}|{{{{}~~}|{zz{{}~~}}}~}|{{{||}}|{zxwvuvwxz|~~~}}}||{{{{||}~~~~~}}~~}{zyyyz{|}~|{z{|~}zyxy|}}~~|zz{|~~}|zyxwwxxy{||}||{zzz{}~|{{|}}|{z{{|}~~}|||}~~~~}||}~}zxvvxz|~~~~}||||}}{yxyz}~}~~{yyz|~|zyyz{{{zywuttuwy|~}{{{}}zxxy{~~}||~~~~|zyxxxyz{}~~~}}~~}|{||}~~~~~~~~}|zyxxwxxz{|}}}|zyxxz|~|{{}~~||{{{||}~~}||}~|zyz{|}}|{zz{}}|||}~~}|{zzz{}~|{{|~}|{{}~~}zxvtttvxz|}~|||}~~~~}|{|~~~~|{zyyyz{|}~~~}}~~}|}}{zz{}}|||}~~~~}{zyxxxxyz||||{ywvvwy|}}}~~}|{{{{|}~~|||}}{zz{|}}|{zz{|~~}}}}~~|{zyyy{|~}|}~~}{{{}~~|zxwvwxyz{|{{{zz{{}~~}||}~~|{z{}~~}||{{||}}}}||}}}~~~~}}}}}~~~~|{{{}~~|{{{|}~~~~~~~}||{zyxxyyz{||{zxwvwx{~~~~}}||||}}~~~~}|{|}~~|zxxxz}~~~~~~}|{zz{{|~~}}~~|zzz|~~|zywwwxyz{|||{{zyyzz|}~~~}|z{|~~}|{{{{|}~~~~~~}||{{{{|}~~|||}~~}{{{{|}~~~~~~|{zzyyzz{{|||{zxwvvwy{}~~~~}}}|||}}~~}}~}{{{|~~}|zyy{|~~~}~~|{{zz{|}~}||}~~~~|||}~~~}|{yyxyyz{|||{{zyxxxxyz{}~~~}|}~~}}~~~}}}}}}}~~~~~~~}|{{zyyz{|~}||}~~}|{{|}~~~~~~~|zyxxyz|}~~}|ywvuuvxz{|}}}~~}}}}~~~~}}~~~}}|{zyxy{}~|{zz{|~}|{zzz|}}||||}~~|{|}|ywvvwxz|~~|{ywvutuvwz|~}}~}{yz|~~~|{{{|}}|zxwvvvxy{~}{|}~}{zyy{|~}{zz{}~}||||}}~~}{ywuttuvxz{|||||}~~~~~|||}}||}~}|{{||||{{{|}~}{zzzz{}~~|{{|}}{zyz{}~|{z{}~}{zxxwxyz|}~~~}{zyxwvvvwxy{}~}|}~}}}}~~~}}}}~~~}{zyxwwxyz|~~}}~~}|{zzz{|}~~~}}}}}}}~~~}{yxwvvwxxyyyz{{|}}}|{z{|~}}~~~}|{zzz|}~|zyxxy{}~}|{{|}~~~}}}||{zzz{|~}|zyyxyyz{|}~~}|zywuuuvxz|~~}}~}||||}}~~~}}}}~~|zyxxy{}~~}|{zz{}~~~~}}|{{{zzzz{{|}}~~}|{{||}~}zxwvwyz|}}|zxvuvxz}~~}||}~~~~~~~}}}~~}|{zz{{|}}}|{{{|~~}|||}}}~~~~~~~~}|{{{|}~~}|zz{|}~~|zyxxyz{{{zywwwxy{}~~~~~~}}}~~}}~~|zyyz{}~~|{zz{|~~}|{zzzzz{}~~}}~}|{{|~~}|zyxyz{||||{zyxxxxyz{}~~~~}}}~~|zyyyz{|~~~~~}}}}}~}|zzz{|~~}|}~}|{{{}~~}{zxxwxxyz{|}}}}}}||{{{{|}~~~~~~}|||}~~~}|{zzyzz{|~~}||}}~~}{zz{|~~}}}}~~~~}}}}|}}~~}|}~~~~|{zyz{}~~~}||}~~~~}}}}~~~}}}}}}}||zyyyyz|}}}|zxwvvwy{~}{|~{yxwvuvwxz}~}}~~~~~~|{z{|~~}|{{||}~~~~|{zyyz|~}|{{|}~~|{z{|~~~~~~~}}}~~}}}~~{xvuvz|zyxwwwxz{{|{zywvuuuwxz|~~}}}}}}||}~}{zz{|~~}}}~}{yxy{~}|{z{{|}~~~}}}}}~~}{{|~}{xvuvx|}zxwvwxyz|}}}|zywvutuvxz}~|{zz{{{||}~|zyyz{}~~~~}|{|~~{zz{|~}{zzz{|~}|||}~~~|ywvvwz~}{zzz{{{{{{zyxxwwwwwxz|~~}|{zzzzz{|~~}}|||}~}zyxy|}{zz{|~~|{zzz|~~}|||~~}}}}~~|zxvvwz~|{{{||}}}|{zyyxxwwwxyz{}~~}{zyxxxyz|~~}||{{||}|zz{}~|{||~~}{zz{|}~~~~~~~~~|zwuuwz~~|{{|}~~~}|zyxwwxxyz{{|}}~~~~~~~}||{zyyyz{}~}}||}}~~|{{|}|||}~}{{{{|}~}|{{|}~}}}~~}|}~~|ywvvx|~|||}~|zxwvvwxy{}~~}|{{zyyyyyyz|~~~}}||}~~|{{{~~~~~~}||||}~~~~~~~~~~~~||||~~|zwvuwz~~}}~~~|{zyxxxyz{|}~~~~~~}}||{zzyyyz{|~~}|||}~~|zz{}~|{zz{|}~~~}||||}~}|{|}~~}{xwvvy|~|{zyxxyz{|~~~~}|{{zzyyyyzz|}~}||||}}{zyz|~}||||}~~~~}}}}~}||||}~~}|{ywwwy|~|{zyyzz{|}~~~~}}|{zzyyyyz{|~~}}||}~~~~|zyy{}~}|{{{|}~~~}}}}}}~~~}||}}~~}|{ywvvwz}}|{zzz{{|}~~~~~~~~}}||{{zyyyyz{}~~}}}}}~~~}{zyz|~}|||}~~~}}}}}}~~~~~~~}|{{{||}}}|{yxwwy{}{zzzz{|}~~}}||{{zzyyyyz{}~~~~~~~~~~}{zyz|~}}||}}~~~}||||}~~~~~~~~}|{{{||}}|{yxwvwy|~}{{z{{|}~~}}|{{zzzzzzz{}~~}}}}}~~}}~~~|{zz{}~}}}}}~~}|||||}~~~~~~~|{zzzz{|||{zyxwxz|}|{{{||}~~}|{{zzzzz{|}~~~~~~~~}}~~~}|{zz|~~}}}}}~~}}||||}~~}}}}~}|{zyyzz{{{{zyxxy{~~}|{{|}~~}|{{zzyyzz{}~~~}}}~~~}}|}}~~}{zz{|~}|||}~~}}||{||}~~~}}}~~}|{zyyzz{{{{zyxxyz|~}}||}}~~}}|{{zzzz{{}~~~}}~~~}||}}~}|{zz{}~}}}}~~~}}||{||}~~}}}}~~~}|{zyyzz{{{{zyxxxy{~~}|||}~~}||{zzyzz{|}~~~~~~~||||}~~|{zz{|~}}}}~~~}||{{||}~~}}}}}~~}|zzyyzz{{{{zyxwwy{}~}}}~~~~}|{{zzzz{|}~~~~~~~}}||}}~~}|{zz{|~}}}}~~}||{{{||}~~}}}}}~~}|zyyyyz{||{{yxwwxz|~~}}~~~}|{{zzz{{|}~~~~~~}}||}}~~~|{zyz{}~~}}~~~}}|{{{{||}~~}}}}}~}|{zyyyyz{{{{zyxwxy{~~~}~~~}|{{zz{{|}~~~~~~~}||||}~~}|{zz{|~~}}}~~}||{{{{|}~~}}}}}~~~|{zyxyyz{{|{{zxxwxz|~~~~~~}|{{zz{{|}~~~~~~}|||||}~~~~}{zzz{}~}}}~~~}|{{{{{|}~~}}|||}}~~}|{yyxxxyz{{{{zyxxyz}~~~~}|{{zz{{|}~~~~~~~}|{{{|}}~~~}|{zz{}~~}}~~~}}|{{{{{|}~~}}|||}}~~~}|{yxxxxyz{{{{zyyyy{}~~~}||{{{{||}~~~~~~}|{{{{|}~~~}|{{{|}~}}}~~}||{{{{{|}~~}|||||}~~~}{zyxwxxyz{{{{zyyyz|~~~}|{{{{{|}~~~~~~}|{{{{||}~~}}|{{{|~~~}}~~~}|{{{{{||}~~}||||||}~~~}|zyxwwxxyz{{{{zyyyz|~}||{{{|}~~~~~~~|{{z{{|}~~~}|{{{|}~}}~~~}||{{z{{|}~~}}|||||}~~~}|{yxxwxxyz{{{{zyyyz|~~}||{{{||}~~~~~~|{{z{{|}~~~}|{zz{}~~~~~~}||{{zz{{|}~~}}||||}}~~~}|{yxxwxxyz{{{zzyxyz{}~}}|{{{||}~~~~~~|{{z{{|}~~~}|{zz{|~~~~~~}||{{zz{{|}~~}}||||}}~~~}|{yxxwxxyz{{{zzyxxyz}~}||{{||}~~~~}{{{{{|}~~~}|{zzz|}~~~~~}|{{zzz{||}~}|||||}}~~}|{zxxwwxyz{{{{zyxxyz|~~}|||||}~~~~~}|{zz{|}}~~}}|{zz{|~~~~~~}||{zzz{{|}~~}}|||||}~~~~~}|zyxwwxxyz{{{{zyxxyz|~~}|||||}~~~~~}|{zz{||}~~}}|{zz{}~~~~~}|{{zzzz{|}~~}||||||}}~~~~}|{zyxwwwxyz{{{{zyyyy{}~}||||}}~~~~~}{zzzz{|}~~~}|{{{|}~~~~~~}||{zzzz{|}~~}||{{{||}~~~~}}{zyxwwwxyz{{|{{zyyz{}~}}|||}~~~~~~|{zzzz{|}~~~}|{{{|}~~~~~~}|{{zzz{{|}~~}|{{{{||}~~~~}|{zxxwwwxyz{||{{zzzz|~~}}||}}~~~~~~}|{zzz{{|}~~}}|{{{|~~~~~~~}|{{zzz{{|}~~}||{{{||}}~~~}}{zyxwwwxyz{{||{zzzz{}~~}}|}}~~~~~~|{zzzz{|}~~~}|{{{|}~~~~~~}|{{zzzz{|}~~}||{{{||}}~~~}}|zyxwwwwxy{{||{{zyzz|~~}}}}}}~~~~~|{zzzz{|}~~~}|{{{{}~~~~~~}|{{zzzz{|}~~}||{{{||}}~~~~}|zyxwwwxyz{{||{zyyyz|}~}}}}}}~~|{zzzz{|}~~~}|{zz{|~~~~~~}|{{zzzz{{|}~~}}||{|||}~~~~~}|{yxwwwxxz{{|{{zyyyz{}~~}}}}}~~}{zzzz{|}~~~}|{{z{|}~~~~}|{zzzzz{|}~~}||{{||}}~~~~}|{zyxwwwxyz{||{{zyyyz|~~}}}}}~~}|{zzz{||}~~}}|{zz{|~~~~~~}||{zzzzz{|}~~}||{{{||}}~~~~}|zyxwwwxxz{{||{zzyyz{}~}}}}}~~}|{zzz{|}}~~}}|{{{{}~~~~~~}|{{zzzz{{|}~~}||{{{||}}~~~}}|zyxwwwxxz{{||{{zzzz{}~~}}}}~~}|{zzzz{|}~~~}|{{{|}~~~~~~~}||{zzzzz{|}~~}||{{{{|}}~~~}}|zyxwwwwxyz{|||{{zzz|}~~}}}~~~}{zzzzz{|}~~~}||{{|}~~}~~~}||{zzzz{{|}~~}|{{{{{|}}~~~}|{zyxwwwwxz{||||{{zz{|~~~}}}~~~}|{zzzz{|}~~~~}||{||~~~}~~~}|{{zzzz{||~~}||{{{{||}}~~}}|{yxwwwwxyz{||||{zzz{}~~}}~~~}{zzzzz{|}~~~}||{{|}~~~~~~}||{zzzz{{|}~~}}|{{{{||}}~~~}|{zyxwwwxyz{||||{zzz{|~~~}}~~~}|{zzzz{|}~~~}||{{{}~~~~~~~}||{zzzzz{|}~~}}|{{{{||}~~~~}|{zyxwwwxyz{|||{{zzzz|~~~}}~~~}|{zzz{|}}~~~}|{{{{|~~~~~~~}|{zzzzz{|}~~~}||{{||}}~~~~}|{zyxwwwxyz{|||{{zyyz{}~~}}~~}|{zzz{{|}~~~}|{{{{|}~~~~~}|{zzzzz{{|}~~}||{{{||}~~~~}}|zyxwwwxyz{||||{zzyz{|~~~~}~~~}{zzzz{|}~~~~}|{{{{|~~~~~~}|{zzzzz{||}~}}|{{{||}}~~~~}|{zyxwwwxyz{||||{zzzz{}~~~~~~~|{zzzz{|}~~~~}|{{{|}~~~~~~~}|{zzzzz{|}~~}}|{{{{||}~~~~}|{zyxwwwxyz{||||{{zzz{}~~~~~~~|{zzzz{|}~~~~}||{{|}~~}}~~~}|{zzzzz{||}~}}|{{{{||}}~~~}|{zyxwwwxyz{||}||{zz{|}~~~~}|{zzzz{|}~~~~}}|{{|}~}}}~~~~}|{zzzzz{|}~~}||{{{{||}}~~}}|{zxxwwwxyz{|}}||{{{{|~~~~~~}|{zzzz{|}~~~~}||{||~~~}}~~~}||{zzzzz{|}~~}|{{{{{|}}~~~}|{zyxwwwxxz{||}}|{{{{{}~~~~~~~|{zzzz{|}~~~~}}|{{|}~~}}}~~~~}|{zzzzz{|}~~}||{{{{|}}~~~}}|zyxwwwwxy{||}||{{zz{|~~~~~~~|{zzzz{|}~~~~}}|{{|}~~~}~~~}|{zzzzz{||}~}||{{{||}}~~~}}|{yxwwwxxz{|||||{zzz{|~~~~~~~|{zzzz{|}~~~~}||{{{|~~~~~~~}|{zzzzz{{|}~~}||{{{||}}~~~~}|{zxxwwxxy{{||||{zzzz{}~~~~~}|{zzz{|}~~~~}}|{{{|}~~~~~}||{zzzzz{|}~~}}|{{{||}}~~~~}|{zyxwwwxyz{||||{{zzz{|~~~~~~~|{zzzz{|}~~~~}|{{{{|~~~~~~~}|{{zzzzz{|}~~}||{{{||}}~~~~}|{zxxwwxxyz{||||{{zzz{}~~~~~}|{zzzz{|}~~~~}|{{{|}~~~}~~~~}|{zzzzz{{|}~~}||{{{||}}~~~~}|{yxxwwxxy{||}}|{{zz{|}~~~}|{zzzz{|}~~~~}}|{{|}~~}}}~~~}|{{zzzz{{|}~~}||{{{{|}}~~~~}|{zxxwwwxyz{|}}||{{{{|}~~~~}|{zzzz{|}~~~~~}||{|}~~}}}}~~~}|{{zzzz{{|}~~}|{{{{{|}}~~~}}|{yxwwwxxy{||}}}|{{{{|~~~~~}{zzzz{{|}~~~}}||||}~}}}}~~~}|{zzzzz{|}~~}||{{{{||}}~~~}|{zyxwwwxyz{|}}}||{{{|}~~~~~}|{zzzz{|}~~~~}||{|}~~}}}}~~~}|{{zzzz{{|}~~~}|{{{{||}}~~~}|{zyxwwwxyz{|}}}||{{{{|~~~~~}|{zzzz{|}~~~~~}|{{|}~~~}}~~~}|{{zzzz{{|}~~~}|{{{{||}~~~~}}{zyxwwwxyz{|}}||{{zz{|}~~~~~|{zzzz{|}~~~~}}|{{{|~~~}}~~~}|{{zzzzz{|}~~~}||{{||}}~~~~~}|{yxxwwxyz{||}||{{zzz|}~~~~}{{zzz{|}~~~~~}|{{{|}~~}~~~~}|{zzzzz{|}~~}||{{{||}~~~~~}|{zyxwwxxyz{|}}||{zzz{|~~~~}|{zzz{||}~~~~}||{{{|~~}}~~~}}|{zzzzz{|}~~~}||{{||}}~~~~~}|{yxxwwxyz{||}}|{{zzz{}~~~~~}|{zzz{|}}~~~~}||{{|}~~~}}}~~~}||{zzzzz{|}~~~}|{{{{||}~~~~~}|{yxxwwxyz{|}}}||{{z{|}~~~~}|{zzz{{|}~~~}|{{|}~~~}}}}~~~}|{zzzzz{|}~~}|{{{{||}}~~~~}|{yxxwwxyz{|}}}}|{{{{|}~~~}|{zzz{{|}~~~}||||}~~}}}}}~~~}}|{zzzzz{|}~~~}|{{{{||}}~~~}}|zyxwwwxyz{|}}}}|{{{{|}~~|{zzzz{|}~~~}}||||}~}}}}}~~}|{{zzzz{||}~~}||{{{{|}}~~~~}|{zyxwwxxyz||}}}||{{{|}~~~~}|{zzz{{|}~~~}||||}~~}}}}}~~~}}|{zzzz{{|}~~}||{{{{|}}~~~~}|{zyxwwwxyz{|}}}||{{{{|~~~}|{zzz{{|}~~~}|{{||~~}}}~~~~}|{zzzzz{|}~~}}|{{{||}}~~~~}|{zyxwwxxyz{|}}}|{{zz{|}~~}|{zzz{|}}~~}}|{{{|}~}}}~~~}|{zzzzz{|}~~}}|{{{||}}~~~~~}|zyxxwxxyz{|}}}|{{zz{|}~~~~~|{{zz{{|}~~~~}|{{{|}~~~}}~~~}|{{zzzz{{|}~~}||{{||}}~~~~}|{zyxwwxyz{||}}||{zzz{|~~~}|{zzz{|}~~~~}||{{{|~~}}}~~~}|{zzzzz{|}~~}}||{{||}}~~~~}|zyxxwxxyz{|}}}||{zz{{}~~~~}|{zzz{|}~~~}||{{||~~}}}}~~}|{zzzzz{|}~~}}|{{{||}}~~~~~}|zyxxwxxyz{|}}}||{{{{|}~~~~}|{zzz{|}~~~~}||{|}~~}}}}~~~}|{{zzzz{|}~~~}|{{{{||}~~~~~}|zyxxwwxyz{|}}}}||{{{|}~~}|{zzz{|}~~~~}||||}~~}}}}}~~}|{{zzzz{|}~~}}|{{{{||}~~~~}|{zyxxwxxyz{|}}}}||{{{|}~|{zzzz{|}~~~}||||}~~}}}}}~~~~}|{zzzz{{|}~~}||{{{||}}~~~~}|{zyxwwxyz{|}}}}}|{{{|}~~}|{zzz{|}~~~~}||||}~~}}}}}~~}|{{zzzz{|}~~}||{{{||}}~~~~}|{zyxwwxxyz||}}}}|{{{{|}~~}|{zzz{|}~~~~}||{||~~}}}}~~~}|{zzzzz{|}~~}||{{{||}~~~~~}|{zyxxwxyz{||}}}||{{z{|}~}|{zzz{|}~~~~}|{{{|}~}}}}~~~}|{{zzzz{|}~~}||{{{||}~~~~}{zyxxxxxyz{|}}}||{zz{{}~~~~|{{zz{|}}~~~}|{{{|}~~}}}~~~~}|{zzzz{{|}~~~}||{{||}~~~~}|{zyxxxxyz{|}}}||{{zz{|~~~}|{zz{{|}~~~}}|{{{|}~}}}}~~~}|{{zzzz{|}~~}}||{{||}~~~~~}|zyxxxxxyz{|}}}||{{z{{}~~}|{{{{||}~~~}}||||}~~~}}}~~~}||{{zz{{|}~~}}||||||}~~~~~}}|{zyxxxyz{||}}}}||{{{|}~~}||{{{||}~~~~}}|||}~~~}}}~~~~~}||{{{{||}~~~~}||||||}}~~~~}}|{zzyyyyz{|}}}}}}||||}}~}}||{||}}~~~~}}}}~~~~}}}~~~~~}|||{{||}}~~~~}}||||}}}~~~~}}|{zzzyzz{{|}}}}}}}||}}~~}}||||}}~~~~~}}}}~~~~~~~~~~~}|||||||}~~~~}}}|||}}}~~~~~}}|{{zzzz{{|}}}~}}}}}}}~~}}|||}}~~~~~}}}~~~~~~~~~}}|||||}}}~~~~}}}}}}}}~~~~~}}||{{zz{{||}}}~~}}}}}}~~~~}}}}}}~~~~~}}~~~~~~~~~~}}}||||}}~~~~~~}}}}}}}~~~~~~}}||{{{{{{||}}}~}}}}}}}~~~~}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}~~~~~~}}}}}}}~~~~~~~}}|||{{{|||}}}~~}}}}}}~~~~~}}~~~~~~~~~~~~~~~~~~}}}}}}}~~~~~~}}}}}}~~~~~~~}}}||||||||}}}~~~}}}}}~~~~~~~~~~~~~~~~~~~~~}}}}}}}~~~~~~~~}}}}~~~~~~~~}}}|||||||}}}~~~~~}}}~~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~~~~~}}}~~~~~~~~~~}}}|||||}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~~~~~~~}}}}||}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m55.bin b/applications/SprPoP/dist/MUS/m55.bin deleted file mode 100644 index 931f6ea..0000000 --- a/applications/SprPoP/dist/MUS/m55.bin +++ /dev/null @@ -1 +0,0 @@ -~~~}}~~}~~~~|}|~~}||{{{{{{{{||}~~~}}||{{{{{zzzzzzz{{{|}~{z}||~~}}|||{{{{{{|||}}~~~}}|||{{{{{zzzzzz{{|}}~{z||{{|}~~~~~~~}}~~~~~~~|zxz}~}{zzzzzzz{{||}}}}||{{zyyyyyyyzz{{||}}}}}~~~~~~~}{zzz}~}|{{{{{{{||||||||||||||||||||||||}}}~~~}|{zz|~~}|{zzzyyyyxxxxxxxxxxxyyyyyyzzz{{{||}}}~~~}||}~~~~~~~}}}}}}}}|||||||||||}}~~~|zyxxy|}|{{{{{|||{{{zyyxxxxxxxyyzz{{{|||{{{{{|||}}~~~|{zzyz{}~}}||||||||||||||||||||||||||}}}}~~~~~|{zzz|~~}|{{zzzyyyyyyxxxxxyyyyyyyzzzz{{{||}}}~~~}|||}~~~~}}}}}}}}|||||||||||}}}~~~}{zyxxz|~~}}|||||||||{zzyyxxxxxyyzz{{{||||||{{|||||}}~~}}|{zyyz{}~}}||||||||||||||||||||||||}}}}}~~~~}{{zz{|~~}||{{zzzyyyyyyyyyyyyyyzzzz{{{|||}}}~~~~}||}}~~~}}}}}|||||||||||||}}}~~}|zyxxyz|~~}}}}}}}}}||{zzyyyxyyyzz{{||||||||||||||}}~~~}|{zzyzz{}~~~}}|||||||||||||||||||||}}}}}~~~~~}|{zzz{|~~}||{{{zzzzzzzzyzzzzzzzz{{{{|||}}}~~~}}||}~~~~~}}}}|||||||||||}}}~~~~}{zyxxyz|~~~~~~~~}}||{zzyyyyzzz{{|||}}}}}||||||}}}}~~~~}|{zzzyzz{}~~~}}||||||||||||||||||}}}}}}~~~~~}|{{zzz{|~~~}|||{{{{zzzzzzzzzz{{{{{||||}}}~~~~}}|||}~~~~}}}}}|||||||||}}}~~~~}|{zyxxyz|~~~~}||{{zzzzz{{{||}}}}}}}}}}}}}}}}~~~~~}||{zzzzz{|}~~~~}}}||||||||||||||}}}}}}}~~~~~}||{zzz{{|~~~}}||||{{{{{{{{{{{{{|||||}}}}~~~~}}||||}~~~~}}}}|||||||}}}}~~~~}|{zyyxyyz|~~}}||{{{{{{||}}}~~~~~~~}}}}}}}}~~~~}}|{{zzzzz{|}~~~}}}}}}|||||||}}}}}}}}}~~~~~~~}||{{zzz{||~~~}}}||||||||||||||||}}}}}~~~~~~}}|||}}~~~~}}}}}}}}}}}}}}~~~~~~~~~}}||{zyyyyyz|~~~}||||||||}}}~~~~~~~~~~}}}}}~~~~~~}||{{zzzz{{|}~~~~}}}}}}}}}}}}}}}}}}}~~~~~~}}||{{zzz{{|}~~~~~}}}}}}|||}|}}}}}}}}~~~~~~~}}||||}}~~~~}}}}}}}}}}}~~~~~}}}}}}}}|{{zyyyyyz|}~~}}|||}}}}~~~~~~~~~~~~~~}}||{{zzzz{{|}~~~~~}}}}}}}}}}}}}}}~~~~~~~~~}}||{{{zzz{{|}~~~~~~~}}}}}}}}}~~~~~~~~~}}|||||}}~~~~}}}}}}}}~~~~~~}}}||||||||{{zyyyyz{|}~~}}}}}}~~~~~~~~~~~~~~}}||{{zzzzz{{|}~~~~~~~}}}}}}}}}}~~~~~~~~~~~}}||{{{{zz{{{|}~~~~~~~~~~~~~~~~~~~~~}}||||||}}~~~~~~~~~~~~~~~}}|||||||||{{zzyyyyz{|}~~~~~~~~~~~~~~~~}}||{{{{{{{{||}~~~~~~~~~~~~~~~~~~~~~~~~~~}}|||{{{zzz{{{|}~~~~~}}}||||||}}~~~~~~~~~~~}}|||{{{{{{{{{{zzzyyzz{|}~~~~~~~~~}}}||{{{{{{{{||}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{{{zz{{{||}~~~~}}}|||||||}}~~~~~~~~~}}||{{{{{{{{{{{{zzzyyzz{|}~~}}}|||{{{{{{||}}~~~~~~~~~~~~~~~~~~~~~~}}}}|||{{{{zzz{{{||}~~~~}}}||||||||}}~~~~~~~~~~~~}}||{{{zz{{{{{{{{zzzzzzz{|}~~~~}}||||{{{{{|||}~~~~~~~~~~~~~~~~~~~~~~~~~}}}}||||{{{{{zz{{{{||}~~~~~}}}}||||||||}}~~~~~~~~~~~~~~~~~}||{{zzzzzz{{{{{{zzzzzzz{|}~~~~}}}}||||||||||}}~~~~~~~~~~~~~~~~~~~~~~}}}}}||||{{{{{zzz{{{{||}~~~~~~}}}||||||||||}}~~~~~~}}}}}~~~~~~~~}||{{zzzzzzzz{{{{{zzzzzz{{|}~~~~~}}}}||||||||||}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}|||||{{{{{{zz{{{{|||}~~~~~~}}}}||||||||||}}~~~~~~}}}}}}}}~~~~~~~~~}}||{{zzzzzzzzz{{{{zzzzzz{{|}~~~~~~~}}}}}||||||||}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}|||||{{{{{{{zzz{{{{||}}~~~~~~}}}}}|||||||||||}}}~~~~~}}}}}}}}}}~~~~~~~~}}||{{zzzyyzzzzzz{{{zzzzzz{{|}~~~~~~~~~~~~~~~~~~~~}}}}}||||||||}}}~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}|||||{{{{{{{zzz{{{{{||}}~~~~~~~}}}}||||||||||||}}}~~~~~}}}}}}}}}}}}~~~~}}}||{{zzzyyyyyzzzz{{{{zzzz{{{|}~~~~~~~~~~~~~~~~~~~}}}}}}|||||||}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}||||||{{{{{{{zzzz{{{{{||}}~~~~~~~}}}}}||||||||||||||}}~~~~~}}}}}}}}}}}}}}~}}}}|||{{zzyyyyyyzzzz{{{{{zzz{{{|}~~~~~~~~~~~~~~~~~}}}}}}}}|||||}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}||||||{{{{{{{{zzz{{{{{|||}}~~~~~~~~}}}}||||||||||||||}}}~~~~~~}}}}||||}}}}}}}}}}}||{{zzzyyyyyyzzzz{{{{{{{{{{{|}~~~~~~~~~~~~~~~}}}}}}}}}}}|||}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}||||||||{{{{{{{{zzzz{{{{{|||}}~~~~~~}}}}}|||||||{{{{|||||}}}~~~~~}}}}}||||}}}}}}}}}}||{{{zzyyyyyyyzzzz{{{{{{{{{{||}~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}||||||||{{{{{{{{zzzzz{{{{{|||}}~~~~~~~~}}}}}||||||||{|||||||}}}~~~~~}}}}||||||}}}}}}}}|||{{zzzyyyyyyyzzzz{{{{{{{{{{||}~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}||||||||{{{{{{{{zzzzz{{{{{{|||}}~~~~~~~}}}}}}|||||||||||||||}}}}~~~~~}}}}||||||||}}}}}}||{{{zzyyyyyyyyzzzz{{{{{{{{{{||}~~~~~~~}}}}}}}}}}}}}|||||}}}}}}}}~~~~~~~~~~~}}}}}}}}}|||||||{{{{{{{{zzzzzz{{{{{{|||}}~~~~~~~}}}}}|||||||{{{{{{|||||}}}~~~~~~}}}}|||||||}}}}}}|||{{{zzyyyyyyyyzzzz{{{{{{{{{{||}~~~~~~~}}}}}}}}}}}}}|||||}}}}}}}}~~~~~~~~~~}}}}}}}}||||||||{{{{{{{{{zzzzzz{{{{{{|||}}~~~~~~~~}}}}}|||||||||{{|||||||}}}~~~~~~}}}||||||||||}}|||||{{zzzyyyyyyyyzzzz{{{{{{{{{{||}}~~~~~~}}}}}}|||||||||||||}}}}}}~~~~~~~~~~~~}}}}}}}|||||||{{{{{{{{{zzzzzzz{{{{{|||}}}~~~~~~~~}}}}}|||||||{{{{{{{|||||}}}}~~~~~}}}}||||||||}}}}||||{{{zzyyyyyyyyyzzzz{{{{{{{{{{||}}~~~~~}}}}}}|||||||||||||}}}}}}~~~~~~~~~~~}}}}}}}|||||||{{{{{{{{{{zzzzzz{{{{{{|||}}}~~~~~~~~}}}}}||||||||{{{{{||||||}}}~~~~~~}}}}||||||||||||||||{{{zzyyyyyyyyyzzzz{{{{{{{{{{||}}~~~~~}}}}}}|||||||||||||}}}}}}~~~~~~~~~~~}}}}}}}|||||||{{{{{{{{{zzzzzzz{{{{{{|||}}}~~~~~~~}}}}}|||||||{{{{{{{{|||||}}}}~~~~~~}}}}}|||||||}|||||||{{zzzyyyyyyyyyzzz{{{{{{{{{{|||}}~~~~}}}}}|||||||||||||||}}}}}~~~~~~~~~~}}}}}}}||||||{{{{{{{{{zzzzzzzz{{{{{{|||}}}~~~~~~~}}}}}|||||||{{{{{{{||||||}}}}~~~~~~}}}}}|||||||||||||||{{zzzyyyyyyyyyzzz{{{{{{{{{{|||}}~~~~}}}}}|||||||||||||||}}}}}~~~~~~~~~~}}}}}}|||||||{{{{{{{{{zzzzzzzz{{{{{{|||}}}~~~~~~~}}}}}|||||||{{{{{{{{{|||||}}}}~~~~~~}}}}}}|||||||||||||{{{zzzyyyyyyyyyzzz{{{{{{{{{{|||}}~~~~}}}}}||||||||||||||||}}}}~~~~~~~~~}}}}}}|||||||{{{{{{{{zzzzzzzzz{{{{{{|||}}}~~~~~~~~}}}}}|||||||{{{{{{{{||||||}}}}~~~~~~}}}}}}|||||||||||||{{{zzzyyyyyyyyyzzz{{{{{{{{{{|||}}~~~~}}}}||||||||||||||||}}}}~~~~~~~~~}}}}}}||||||{{{{{{{{{zzzzzzzzz{{{{{{|||}}}~~~~~~~~}}}}}||||||||{{{{{{|||||||}}}}~~~~~~}}}}}}||||||||||||{{{zzzyyyyyyyyyyzzz{{{{{{{{{||||}}~~~~~}}}}|||||||||||||||||}}}}~~~~~~~~}}}}}}||||||{{{{{{{{zzzzzzzzzz{{{{{{|||}}}~~~~~~~}}}}}}|||||||{{{{{{{{||||||}}}}~~~~~~}}}}}}}}}}}|||||||{{{zzzyyyyyyyyyyzzz{{{{{{{{{||||}}~~~~~}}}}||||||||||||||||}}}}}~~~~~~~~}}}}}}||||||{{{{{{{{zzzzzzzzz{{{{{{{|||}}}~~~~~~~}}}}}}||||||||{{{{{{|||||||}}}}~~~~~~~}}}}}}}||||||||||{{{zzzyyyyyyyyyyzzz{{{{{{{{|||||}}~~~~~}}}|||||||||||||||||}}}}~~~~~~~~~}}}}}|||||{{{{{{{{zzzzzzzzzzz{{{{{{|||}}}~~~~~~~~}}}}}||||||||{{{{{{{|||||||}}}}~~~~~~~}}}}}}}}}}}||||||{{{zzzyyyyyyyyyyzzz{{{{{{{{|||||}}~~~~~}}}}||||||||||||||||}}}}~~~~~~~~}}}}}}|||||{{{{{{{{zzzzzzzzzz{{{{{{{|||}}}~~~~~~~~}}}}}|||||||||{{{{{{|||||||}}}}~~~~~~~}}}}}}}}}}|||||||{{{zzzyyyyyyyyyyzzz{{{{{{{{|||||}}~~~~~}}}}||||||||||||||||}}}}~~~~~~~~}}}}}||||||{{{{{{{{zzzzzzzzzzz{{{{{{|||}}}~~~~~~~~}}}}}||||||||{{{{{{{|||||||}}}}~~~~~~~}}}}}}}}}}}||||||{{{zzzyyyyyyyyyzzzz{{{{{{{{|||||}}~~~~~}}}|||||||||||||||||}}}}~~~~~~~~}}}}}|||||{{{{{{{{zzzzzzzzzz{{{{{{{|||}}}}~~~~~~~}}}}}|||||||||{{{{{||||||||}}}}~~~~~~~}}}}}}}}}}||||||{{{zzzyyyyyyyyyyzzz{{{{{{{||||||}}~~~~~}}}|||||||||||||||||}}}}~~~~~~~~~}}}}||||||{{{{{{{zzzzzzzzzzzz{{{{{{|||}}}}~~~~~~~}}}}}|||||||||{{{{||||||||}}}}~~~~~~~~}}}}}}}}}}}||||{{{zzzzyyyyyyyyyzzzz{{{{{{{||||||}}~~~~~}}}}|||||||||||||||||}}}}~~~~~~~~}}}}||||||{{{{{{{zzzzzzzzzzzz{{{{{{||||}}}~~~~~~~~}}}}}|||||||||||{|||||||||}}}}}~~~~~~~}}}}}}}}}}}||||{{{{zzzyyyyyyyyyzzzz{{{{{{{||||||}}}~~~~}}}}|||||||||||||||||}}}}~~~~~~~}}}}||||||{{{{{{{zzzzzzzzzzzz{{{{{{||||}}}~~~~~~~}}}}}|||||||||||||||||||||}}}}~~~~~~~~~}}}}}}}}}|||||{{{zzzzyyyyyyyyyzzzz{{{{{{{||||||}}~~~~~~}}}}||||||||||||||}}}}}~~~~~~~~~}}}}||||||{{{{{{{{zzzzzzzz{{{{{{{{||||}}}}~~~~~~~~~}}}}}}}||||||||||||}}}}}}~~~~~~~~~~~}}}}}}}|||||{{{{{zzzzzzzzzzzz{{{{{{|||||||}}}~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~}}}}}|||||||{{{{{{{{{{{{{{{{{{{{|||||}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~}}}}}}}}||||||{{{{{{{{{{{{{{{{{|||||||}}}}}~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~~}}}}}}|||||||||{{{{{{{{{{{{|||||||}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}||||||||{{{{{{{{{{|||||||||}}}}}}~~~~~~~~~~~~~~~~}}~~~~~~~~~~~~~~~~~~}}}}}}}}||||||||||||||||||||||||}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}|||||||||||||||||||||}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}|||||||||||||||||}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}||||||||||||||}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{zxtssrrqqssrstssttsuuuuvwxy{|}}{ywutrqpponnnnnnnnoqtx||tx{~|{yxwvvuuvvvvwxz{|}~~}|{zyxwwvvuuuuuwxyz|~sp|wssuwz}~~~|{{{{{{{||}~~~}}}}}}}~~~yx~}{{|}~~}|{zzyyyyyyz{|}~zupnpv|xurponnnoppqrssttttttsrrqpppppqstuwyz|}~|yxxz~~||{{{{{{{|||||||{{{{{||||}}~{xusrruy~{ywusrqponnnmmmmmmmmnnnooppqrstuvwxyz{|}~~|zxwxz~}||{{{zzzzzyyyyyyyzz{{|}~{vronnqv|~{yxwvvvvvutsrponmmmmnopqrtuuvvvwvwwwwwxyyz{|}}{ywusrstw|}|{zyyxxxxxxyyyyyyyyzzzz{{{||}~~~{xvtrrtw{~{ywvtsrqppooonnnoooopppqrrsttuvwxyz{{|}~~|zyxxy|~}|{{{zzzzzyyyyzzz{{|}~}zvspnnosx~~|zzyyxxxwvtsqponnnoopqrtuuvvwwwwwwwwwxyyz{|}~~|{ywusrrtvy}~|{zzyyyyyyyyyyyzzzzz{{{||}}~~|zwusrrsux}~|zywvutsrrqqqpppppqqqrrsstuuvwxyyz{|}}~}|zyxxyz}~}||{{{zzzzzzzzz{{|}~|zvsqonnqty~}|{{{zyxwvusrqppppqrstuvwwxxxxwwwwxxxyz{|}~~}{ywvtsssuw{~~||{zzzzyyyyzzzzzz{{{|||}}~~}{ywusrrrtvz~~|{yxwvutssrrrrqrrrrrssttuuvwwxyzz{|}~~~}{zxxwxy|~~}||{{zzzzzzzz{{|}}~~}{yvtqonnprv{~~~}}|{zxwutsrqqrrstuvwxxyyyyyyyxxyyyz{{|}~~}{yxvtssstvy|~}||{{zzzzzzzzz{{{{|||}}~~}{ywusrrrsux{~}{zyxwvvuuttttssttttuuuvvwxxyzz{||}~~~}{zyxxxyz}~}}|{{{zzzz{{{||}~~~~}|zywtrponoqtx}~~}{zxwutssssttuvwxxyyyyyyyyyyyyyz{{|}~~}|zywvutsstvwz}~}}||{{{{{{{{{{{{|||}}}~~}{zxvutsrrstvy}~|{zyxxwwvvuuuuuuuuvvvwwxxyyzz{||}~~}|{zyxwwxy{~~}}||{{{{{|||}}~~~}}||{zxwusqpooprvz~~}{zxwvuuuvvwxyyzz{{{{{zzzzzzzz{{|}}~~|{zxwvutttuwy{~~}}||||{{{||||||}}}~~~~|{yxvutsrrstux{~~}|{zzyyxxwwwwwwwwwwxxxyyzz{{||}}~~~|{zyxxwwxyz|~~}}||||||||}}~}|{{zzzyxxvusrpoopqtw{~}{zyxwwwwxyyz{{|||||{{{{zzzz{{{|}}~~}|{yxwvutttuvxz|~~}}}||||||||}}}}}~~~~}{zywvutsrrrsuwy|~}||{{zzzyyyyyyyyzzz{{{||}}}~~~}|{zyxxwwwxy{}~}}}|||}}}}~~~|{zyyxxxxwvutrqpppqsuy}}|{zyyyyyz{{|}}}}}}}}||||{{{|||}}~~~}|{zyxwvuuuuvwy{}~~}}}}}}}}}}}}}~~~~~|{zyxwvutsrrrstvxz}~}}||{{{{{zzz{{{{{{|||}}}~~~~}}|zyyxwwwwwxy{}~~}}}}}~~~~}|zyxxwwwwvvutsrqppqrtwz~~}|{{{{||}}~~~~~}}|||||||}}}~~~}|{zyxwvuuuuvwxz|~~~~~}}}}}}}}}}~~~~~~}}|{zyxwvutssrsstuwy{~~~}}}|||||||||}}}}}~~~~~~}|{{zyxxwwwwxyz|~~~~}~~~~~}|zyxwvvvuuuuutsrrqqqrsux|~}}||}}~~~~}}}||||}}}~~~}|{zyxwvvuuuvwxy{}~~~~}}}}}}}}}~~~~~~}||{zyxwvuttssrrssuvxz|~~~~}}}}}~~~~~~~~~~}||{zyyxwwwwwwxyz|~~~~~~~~~|{yxwvuuuuuuuttssrqqqqrtvy}~~~~~~~}}}}}}}}~~~~~}|{{zyxwwvvvvwxy{|~~~~~~~~~~~~~~~~~~~}}||{zzyxwvvutssrrrsstuwy{}~~}}|{{zyyxwwwwwwxyz{}~~~}}}}}}~~~}|zyxwuuttttttttssrrqqqrsuwz}~~~}}}}}~~~~~~}}|{{zyxxwwvvvwxyz{}~~~~~~~~~~~~~~~~~~~~~~~~~}}||{{zzyxxwvuutsssrrrstuvxy{}~~}}|{{zyyxxwwwwwwxyz{}~~}}||||||}}}~~~~~~}|{zxwvutssssssssssrrrqqrstvx{~~~~}}}}}~~~~~~~~~~~}}}|{{zzyxxwwwwxxyz{}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}|||{{zzyyxwwvuutsssrrssttvwyz|~~~}}||{zzyyxwwwwwwwxxy{|}~~}}||{{{{|||}}}}}}||{zxwvutsrrrrrsssssrrrrrrsuwy|~~~}}}}}}~~~~~~~~}}}||{{zzyyxxwwwxxyz{|}~~~~~~~~~~~~~~~~~~~}}}}}}}}||||{{{zzyyxxwwvuuttsssrrssstuvxy{}~~}}}|{{zzyyxxwwwwwwxxyz{|~~~}||{{{{z{{{|||||||{zyxwvussrrqrrrrsssssrrrrstvxz}~~~}}}}}}}}}}}}}||||{{zzyyyxxxxxxyz{|}~~~~~~~~~~~}}}}}}}}}}||||{{{zzzyyxxwwvvuuttsssrrrsstuvwxz|}~~~}}|||{zzyyxxwwwvvvvwwxyz{|~~}||{{zzzzzzz{{{{|{{zzyxvutsrrqqqqrrrsssssrrrstuwy{~~~~}}}}}}}}}||||||{{{zzyyyxxxxxxyyz{|}~~~~~~~}}}}}}}}}|||||{{{{zzzyyxxxwwvvutttsssrrssstuuwxy{|~~~}}}||{{zyyxxwwwvvvvwwxyyz|}~~}}|{{zzzyyyzzzz{{{{zzyxwvtsrrqqqqqqrrssssssssstvwz|~~}}}}}}}|||||||{{{zzyyyxxxxxxyyz{|}~~~~~~}}}}}}||||||{{{{{zzzzyyyxxwwwvvuutttssssrsssttuvwxz{}~~~}}||{{zzyyxxwwvvvvvvwwxyyz{}~~~}||{zzyyyyyyzzzzzzzzyxxvutsrqqpppqqrrssssssssstuvxz}~~}}}||||||{{{{{{zzzyyyyyxxxyyyz{||~~~~~}}}}}||||||{{{{zzzzyyyxxxwwvvvuutttssssrsssstuvwxyz|}~~~}}||{{zzyyxxxwwwvvvvwwxxyz{|}~~}||{zzyyyxxxyyyyyzyyyxwvutsrqqppppqqrrsssssssstuvwy{~~~}}}|||||{{{{{{zzzzyyyyyyyyyyz{{|}~~~~~}}}}|||||{{{{{zzzyyyyxxxwwvvvuutttsssssrssssttuvwxz{|~~~~}}||{{zzyyxxwwwwwwwwwxxyzz{|}~~}|{zzyyxxxxyyyyyyyyxxwvutsrqqpppppqqrrsssssssttuvxz|~~~}}||{{{{{zzzzzyyyyyyyyyyyyz{{|}~~~~~}}}||||{{{{zzzzyyyxxxwwwvvuuutttssssrrrsssttuvwxyz{}~~~}}||{{zzyyyxxwwwvvvvvwwxxyzz{|}~~~}|{{zyyxxxxxxxyyyyxxxwvutsrqqppppppqqrrssssstttuvwyz}~}}|||{{{{zzzzzyyyyyyyyyyyzzz{||}~~~~}}}||||{{{zzzzyyyxxxwwwvvvuuutttssssrrrrsssttuvwxy{|}~~~}}||{{zzyyxxxwwwwvvwwwwxyyz{|}~~}||{zyyxxxxxxxxxxxxxxwwvutsrqppoooppqrrssstttttuuvxy{}~}}|||{{{zzzzzzyyyyyyyyyyyzz{{|}~~~~~}}}|||{{{{zzzyyyxxxwwwvvvuutttsssssrrrrsssttuvwxyz{|~~~}}}||{{zzyyxxwwwwvvvvvwwwxyyz{|}~~}||{zyyxxxxxxxxxxxxxxwvvutsrqppoooppqqrrssttttttuvwxz|~~}}||{{zzzzyyyyyyyyyyyyyyzz{{|}}~~~~}}|||{{{{zzzyyyxxxwwwvvuuuttttsssssrrssssttuuvwxyz|}~~~}}||{{zzyyyxxwwwwwvvwwwwxxyzz{|}~~}|{{zyyxxxwwwwxxxxxwwvvutsrqqppoooppqqrssttttttuuvwyz|~~~}}||{{zzzyyyyyyyyyyyyyyzzz{||}~~~~}}}|||{{{zzzyyyxxxwwvvvuuuttttsssssrrrssssttuvwxyz{|}~~}}||{{zzyyxxxwwwvvvvvvwwwxxyz{{|}~~}|{{zyyxxxxxxxxxxxxwwvvutsrrqppoooppqqrrsstttttuuvwxy{}~}}|{{zzzyyyyxxxxxxxxyyyzz{{||}~~~~}}|||{{zzzyyyxxwwwvvvuuutttssssssrrrssssttuuvwxyz{|~~~}}||{{zzyyxxxwwwwvvvwwwwxxyyz{|}}~~}|{zzyyxxxwwwwwxxwwwwvuutsrqppooooppqqrssttttuuuvvwxz|~~~}||{{zzyyyyyxxxxxxxyyyyzz{{|}}~~~}}|||{{zzzyyyxxxwwvvvuuuttttsssssrrrssssttuuvwwxy{|}~~~}}||{{zzzyyxxxwwwwwwwwwwxxyyzz{|}~~~}}|{zyyxxxwwwwwwwwwwwvvutsrrqppooooppqrrsttttuuuuvwxy{|~~}||{{zzyyyxxxxxxxxxxyyyzz{||}~~~}}}||{{zzzyyxxxwwwvvuuuttttsssssrrrrssssttuvvwxyz{|}~~~~}}||{{zzyyxxxwwwwwvwwwwwxxyyz{{|}~~~}|{zzyyxxxwwwwwwwwwwvvutssrqppooooppqqrsstttuuuuvvwxz{}~}||{zzzyyyxxxxxxxxxyyyzz{{|}}~~~}}||{{{zzyyyxxwwwvvuuuttttsssssrrrrssssttuuvwwxyz{|~~~}}||{{zzzyyxxxwwwwwwwwwwxxyyzz{||}~~}}|{zyyxxxwwwwwwwwwwvvuutsrqqppooooppqrrssttuuuuvvwxyz|}~~}|{{zzyyyyxxxxxxxxyyyzzz{||}~~~~}}}||{{zzyyyxxwwwvvvuuutttssssrrrrrrssssttuuvwxyz{|}~~~}}|{{{zzyyxxxwwwwwwwwwwwxxyyz{{|}~~~}|{zzyyxxxxxwwwwwwwvvuutsrrqppooooppqqrsstttuuuuvvwxy{|~~~}|{{zzyyyxxxxxxxxxxyyzz{{||}~~~}}||{{zzyyxxxwwvvvuuutttsssssrrrrrrsssttuuvwwxyz{|}~~~}}||{{zzzyyxxxwwwwwwwwwwxxyyzz{||}~~}}|{zzyyxxxxwwwwwwwwvvuttsrqqppooooppqrrssttuuuuvvwwxz{}~~}}|{{zzyyxxxxxxxxxxyyyzz{{|}}~~~}}||{{zzzyyxxwwwvvuuutttsssssrrrrrrssssttuuvwxyyz{|}~~}}||{{zzyyyxxxwwwwwwwwwxxxyyz{{|}}~~}|{{zyyyxxxxxxwwwwwvvuutsrqqppooooppqqrssttuuuuuvvwxyz{}~~}|{{zzyyxxxxxxxxxxxyyzz{{|}}~~~}}||{{zzyyyxxwwvvvuuttttsssssrrrrrrsssttuuvvwxyz{|}~~~}}||{{zzzyyxxxxwwwwwwwwxxxyyzz{{|}~~~~}|{{zyyxxxxxxwwwwwwvvuttsrqqppooooppqrrssttuuuuvvwwxyz|~~}}|{{zyyyxxxxxxxxxxyyyzz{||}~~~~}}||{{zzyyxxwwwvvuuutttsssssrrrrrrssssttuuvwxxyz{|}~~~}}||{{zzyyyxxxxwwwwwwwxxxxyyzz{||}~~}}|{zzyyxxxxxwwwwwwvvuutsrrqppoooopppqrrsttuuuuvvvwxyz{|~~~}|{{zyyyxxxxxxxxxxxyyzz{{|}~~~~}}||{{zzyyxxwwvvvuutttsssssrrrrrrrssstttuvvwxyyz{|}~~~}}|{{{zzyyyxxxwwwwwwwwxxxyyzz{{|}}~~}|{{zzyyyxxxxxxwwwvvuutssrqqppooooppqrrssttuuuvvvwwxyz{}~~}}|{zzyyyxxxxxxxxxxyyzz{{||}~~~~}||{{zzyyxxwwwvvuuutttsssssrrrrrrssssttuuvwwxyz{|}~~~}}||{{zzzyyxxxxxwwwwwwxxxyyyzz{||}~~~~}|{{zzyyxxxxxxwwwwvvuutsrrqpppoooppqqrrsttuuuuvvvwwxyz|}~}||{zzyyyxxxxxxxxxyyyzz{{|}}~~}}||{{zzyyxxwwvvuuutttsssssrrrrrrsssstttuvvwxxyz{|}~~~}}||{{zzyyyxxxxxwwwwxxxxyyyzz{{|}}~~}||{zzzyyyxxxxxwwwvvuutssrqqppooopppqqrssttuuuvvvvwxyz{|~~}||{zzyyyxxxxxxxxxxyyzz{{|}}~~~}}||{{zzyxxwwwvvuuutttssssrrrrrrrssssttuuvvwxyzz{|}~~~}}||{{zzyyyxxxxxxwxxxxxxyyzz{{||}~~~~}||{zzyyyyxxxxxwwwvvuutsrrqpppooopppqrrsttuuuuvvvwwxyz{}~~}||{zzyyyxxxxxxxxxyyyzz{||}}~~~}}|{{zzyyxxwwvvvuutttsssssrrrrrrrssssttuuvwwxyz{||}~~}}||{{{zzyyyxxxxxxxxxxxxyyyzz{{|}}~~~}|{{zzyyyxxxxxwwwwvvutssrqqppooopppqqrssttuuuvvvvwxxyz|}~}||{zzyyyxxxxxxxxxyyyzz{{|}}~~~}||{{zzyyxxwwvvuuuttsssssrrrrrrrrssstttuvvwxxyz{|}~~~}}||{{zzzyyyxxxxxxxxxxxyyyzz{{||}~~~}}|{{zzzyyyxxxxwwwvvuutsrrqpppooopppqrrssttuuuvvvwwxyz{|}~~}||{zzyyyxxxxxxxxxyyzz{{||}~~~}}||{{zyyxxwwvvvuutttssssrrrrrrrrssssttuuvvwxyzz{|}~~~}}||{{zzzyyyxxxxxxxxxxyyyzzz{{|}}~~}||{{zzyyyyxxxxwwwvvuttsrrqpppoopppqqrrsttuuuvvvvwwxyz{|~~~}|{{zzyyyxxxxxxxxyyyzz{{|}}~~~~}}||{zzyyxxwwvvuuuttssssrrrrrrrrrssssttuuvwwxyz{||}~~~}|||{{zzyyyyxxxxxxxxxyyyyzz{{||}~~~}}||{{zzzyyyyxxxwwvvuutsrrqqpppoppppqrrssttuuuvvvwwxxyz|}~~~}||{zzyyyxxxxxxxxyyyzz{{||}~~~~}}|{{zzyyxxwwvvuutttssssrrrrrrrrrsssttuuvvwxxyz{|}~~~}}||{{{zzyyyyxxxxxxxxyyyyzz{{||}}~~}}||{{zzzyyyxxxxwwvvuttsrrqppppppppqqrrstttuuuvvvwwxyz{|}~}}|{{zzyyyxxxxxxxyyyzzz{{|}}~~}}||{{zyyxxwwvvuuuttssssrrrrrrrrrssstttuuvwwxyzz{|}~~~}}||{{{zzzyyyyyyyyyyyzzzz{{||}}~~~}}|||{{zzzyyyxxxwwvvuttssrrqqqqqqqqrrssttuuuvvvwwxxyz{|}~~~}}||{{{zzzzyyyyzzzzz{{|||}}~~~}||{{zzyyxxwwwvvuuutttttssssssssstttuuvvwwxxyzz{|}}~~~}}}|||{{{{zzzzzzzzzz{{{{||}}}~~~~}}|||{{zzzyyyxxwwwvvuutttssssssssssttuuuvvvwwxxyyzz{|}~~~~}}}|||{{{{{{{{{{{{{{|||}}~~~~~}}||{{zzzyyxxwwwvvvvuuuuutttttttuuuuuvvwwwxyyzz{||}~~~~}}}||||{{{{{{{{{{{||||}}}~~~~~}}}||{{{zzzyyyxxxwwwvvuuuutttttttuuuuvvvwwwxxyyyz{{|}}~~~}}}}||||||{{{{||||||}}}~~~~~}}}||{{{zzzyyxxxwwwwvvvvvvvuuuuuvvvvvwwwxxxyyzz{||}}~~~~}}}}}||||||||||||||}}}}~~~~~~}}}||{{{{zzzyyyxxxwwwvvvvvvvvvvvvvvwwwwxxxyyyzz{{|}}~~~~~}}}}}|||||||||||}}}}}~~~~~~}}}|||{{{zzzyyyxxxxwwwwwwwvvvvvwwwwwwxxxyyyzz{{||}}~~~~~~}}}}}}}}|||}}}}}}}}~~~~~~~}}}|||{{{{zzzyyyyxxxxwwwwwwwwwwwwwwxxxxyyyyzz{{||}}~~~~~~~}}}}}}}}}}}}}}}}}~~~~~~~}}}|||{{{{zzzyyyyyxxxxxxwwwwwwwxxxxxxyyyyzzz{{||}}~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~}}}}|||{{{{zzzzyyyyxxxxxxxxxxxxxxxxxyyyyzzz{{{||}}}~~~~~~~~}}}}}}}}}}}}~~~~~~~~~}}}}|||{{{{zzzzyyyyyyyxxxxxxxxxxxyyyyyzzzz{{|||}}~~~~~~~~~~}}}}}}}}}~~~~~~~~~~}}}}||||{{{{zzzzzyyyyyyyyyxyyyyyyyyyyzzzz{{{|||}}}~~~~~~~~~~~}}}~~~~~~~~~~~~~~}}}}||||{{{{{zzzzzzyyyyyyyyyyyyyyyzzzzz{{{{|||}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}|||||{{{{{zzzzzzzyyyyyyyyyzzzzzzz{{{{||||}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}|||||{{{{{{zzzzzzzzzzzzzzzzzzzz{{{{{|||}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}|||||{{{{{{{zzzzzzzzzzzzzzzzz{{{{{||||}}}}~~~~~~~~~~~~~~~~~~~~~~~~}}}}}||||||{{{{{{{{zzzzzzzzzzzz{{{{{{|||||}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}}||||||{{{{{{{{{{{{zzz{{{{{{{{{{|||||}}}}~~~~~~~~~~~~~~~~~~~}}}}}|||||||{{{{{{{{{{{{{{{{{{{{{{|||||}}}}~~~~~~~~~~~~~~~~}}}}}}}||||||||{{{{{{{{{{{{{{{{{|||||||}}}}~~~~~~~~~~}}}}}}}|||||||||{{{{{{{{{{{{{||||||||}}}}}~~~~~~~~~~~~}}}}}}}|||||||||||||{{|||||||||||||}}}}}}~~~~~~~~~~~~}}}}}}}}|||||||||||||||||||||||||}}}}}}}~~~~~~~~~~~~~}}}}}}}}}||||||||||||||||||||||}}}}}}}}~~~~~~~~~~~~~}}}}}}}}}}}||||||||||||||||||}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}||||||||||||||}}}}}}}}}}~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}|||||}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}~~~~~~yvtpoqwyvvxz|}{ywussrqrstuwy{}~|zwtqoljgfedcbbbcehlqylmper~|{zyyyz{}~~|zxwutssstvwy|{rw|wx{~|{yxxwwwwxy{}zjaf{xoiggiloqstvvvtsrqqponmmllmmnnprtvx{}~}}}|||}~}ysmilv}|{|~~~~~umebdn}|upkhfeedddddeddeffghhijkmnpqsuvxz{|~{xsomou{}{ywwvvvwwxxyz{|{|}~|umgb_`eo|yrmifdcbbcdeefggggfddddeegikmortwy|}}~}~~~~~~~~}{yvsqnmoszysmhddgny|wrnkhfdba``____``abcdefgiklnpqstvwyz{|}~~{xtqnlmou|~{zyxxxxyyz{|}~wphb^]`hs}xtrpoomljgeba___`bdgikmoppqqqqqrrstvxy{}~{xtpmkkmry~|{zyyyyyyyzzz{{|}}~~xrmhedfks}|wrnkhfdcaa````aabcdefgijlmoqrtvwyz{}~~|yvsqqsx~}|{zzzzzz{{|~}wqje`^`eny}zwvusqoligdba`abdfhjlmnopopooppqrstvxy{}~~|yvspmkjlou{}|{{zzzzzz{{||}}~|wrmifefjpx{wspmkigfeddccddeefghiklnoqrsuvxyz|}~~|ywtqpoqty~}{zzyyyyzz{|~}xsmgb``dku~|{ywuspmjgedddfgikmnppqqqqqqqqrstvwy{|~~|yvspmkjknrx~~|{zzyyyyzzz{{||}~~zvqmigefinu}{xuromljihhgggghhijklmnpqrtuvxyz{|~~|ywtrqpqsw}~}|{{zzz{{|}}{yupkfb__afnx~}{zyvspmkihgghjlnoqrstttsssssttuvxy{|~}zxuromllnrv|~}||{{{{{|||}~~{xtplhfeegkqx}zwtrpnlkjjiiiiijjklmnopqstuvxyz{|}~~|zwusqppruz~}|||||}~~|{ywtqlhda`aekt}}zwtqnljihijkmoprrsttsssrrrstuvwxz{}~}{ywtromllmpty~}}}}}}}}~~{xtpligeegjou|~{xvtrqonnmmmlmmmnoopqrstuvxxzz{|}~~}|zxvsqpoopsv{~}|||||}~}{yxvuspmifcaadipx~{xtqommlmnoqstuuuuuuttttttuvwxz{|~~}{yvtronllmorv{~}}|||||}}}~~|yvspljhffgimsy|zxvusrrqpppppqqrrstuuvwxyz{|}}~~}{zxvtrqpppruy~~}}}~~|ywvtsrpnlifcaabflt|~{xusqpppqstuvwxxxxxwvvvvvvwxyz{|}~}|zxvtqpnnnprvz~~~~~~~|zwtqnkigeeegkou{}{ywvutsrrrrrrssstuuvwwxyz{||}~~~}|{ywvtrqpopqtw{~{yvsrqpomligdbbbeipx}zwussrstuvwxyyyyxxwwvvvvvwwxy{|}~~~}{zxvtrponnoqtx|}{xvspnkigfffgjnsx~~|{yxwvuuuttttuuuvvwxxyzz{||}}~~~~~}||zyxvtsqpoooprux|}zwtrqponmljhfdbcdhmt|}zxwvvwxyz{{|||{zyyxwwwwwxxyz{|}~~~~}|{ywutrponnoprvy~~~~~~}{ywtrpmkihgffhjmqv|~}{zzyxxxwwwxxxxyyyzz{{||}}~~~~~~~~}}|{zywvtsqponnoqsvz}}zwtrpnnmllkihfdccdfjpw}{zyyzz{|}~~~~~}|{zyyxxxxyyz{{|}}}}}}||{yxvusrqpppqsvy}~}|zxvtrpnljhgfeefhkosx}~}|{{zzyyyyzzzz{{{||}}}~~~~~}}|{zyxvusrqpoooprux||yvspnmlkkjjihfedcceimtz~|{zz{|}}~~~}|{zzyyyxyyzz{{||}}}||{zyxvutrqpppqsux{~|{yxvtrpnlkigfffghknrv{~}}|||{{{{{{|||||}}}~~~~~~~~~}}||{zyxwutsqpoooopqtvy}~~~~~}{xurpmljjiiiihgfeddehlqw~~}~~~}|{zzyyyyyzz{{|||||{{zywvusrqpppqrtvy|~~}|{yxwusrpnlkihgffghjmpty~~~}}}}}}}}}}}~~~~~~~~~~}}||{zyxwvutrqponnnoprtwz~~}|||||}}~~~~~}{yvsqnljihhhhhhgfeedegjntz~}|{zzzyzzzz{{{{{zzyyxwutsrrqqrstvy|~~}}|{zyxwvtsqpnmkjhgffffgiknrvz~~~~}}|{{zyxwutsrqpooooprtvy|~}|{{zz{{|}}}}|{yvtqnljhgggggggffeeefhlqv}~}|{{zzzzzzzzz{zzzyyxwvutsrrrrstvx{~~~~~~}}||{{zyxwvusrqonlkjhggfffgikmqtx}~~~~~}}||{zyxwvutsrqpoooopqsuxz}}|{zzyyyyz{{{{{yxvsqnlihgfffggggfffffhkotz}|{{zzzzzzzzzzyyyxwvuutsrrqrrsuwy|~~~~~}}}||{{zyyxwvutsqponlkjihgffggijmosw{~~~}}|{{zyxwvutsqpponnnopqsux{~~}|{yyxxwxxxyyyyyxvtrpmkigfeeeffgggffffhjmqw|~}|{zzzyyyyyyxxwwvuutssrrsstuwy{~~~~~}}}}|||{{zzyyxwwvutsrponlkjihgffffghiknqux|~~}}|{zzxxwutsrqpooooopqsuwz|~}|zyxwwvvvwwwxxwwutromjhfedddeeffgffffgikosy~}|{{zzyyyyyxxxwwvuutssssstuwy{}~~}}}}|||{{zzzyyxwvvutsrqponlkjihggfffghikmpswz~~~}}|{zzyxwvtsrqppoooopqrtvx{~}|zyxwvuuuuvvvvvvusromkhfedcddeefggggggiknqv|~}|{zyyxxxwwwvvuuttsssssstuvxz|~~}}}||{{{zzyxxwwvutsrqqonmlkjihhgfffghijloruy|~~}||{zyxwvutsrqpoonnnopqrtvy{~~|{zxwvuuuuuuuvvuutsqomjhfedccddefggggghijlpty~~}|{zzyxxxwwwvvuuttssssstuvwy{}~~}}||{{zzyyxxwwvuttsrqponmlkjjihggffffghjlnpswz~~}||{zyxwvutsrqppoooopqrtvxz|~}{zxwvuttssttttttsrqomkhfeccbccdefgggghhiknqv{~}|{zzyxxxwwvvvuutttttttuvwy{}~~}}|{{zzyyxxwwvuutsrrqponmlkjiihggfffgghikmorux|~}}|{zyxwvutsrqqppooppqrsuwy{~}|zywvutssssssssssrqomkigedcbccdefggghhhikmptx}}|{zyxwwvvuutttssssssttuwxz|~~}}||{zzyyxwvvuttsrqpponmlkjiihggfffgghikloqtwz}~~}|{zyxwvutsrqppooooopqrtuwy{~~}{zxwutssrrrrssssrrpomkigeccbbccdffghhhiijlorv{~}|{zyxwwvvuuttttsssttuuvxy{}~}||{zzyxxwvvuutsrrqponmmlkjihhggffffghijlnpsux|~~}|{zyxwvutsrqppoooppqrsuwxz}}|zywvtsrrqqqrrrrqqpomkigecbbabbcefgghhiijkmptx|~}|{zyxwwvvuuttttttttuuvwyz|~~}}|{zzyxxwvvutssrqpponmllkjihhggffffgghikmoqtwz}~}|{zyxwvutsrrqppppppqrstvxz|~~|zywvtsrrrqqrrrrqqpnmkigedbbabbcdfgghhiijkmorvz~}{zyxwvvuuttssssssttuvwxz{}~}}|{zyyxwvvuttsrqpponmllkjiihggffffgghiklnpsvx{~~}|{zyxwvutsrqqppoooppqrsuvxz|~~}{yxvutsrqqqqqqqqqpomljhfdcbaabcdefghiijjklnqtx|~|{zyxwvuutttsssssttuuvxy{|~~}}|{zyxxwvuutsrrqponnmlkjjihhgggfffgghijlnprtwz}~}|{zyxwvutsrqqppppppqrstvwy{}}|zxwutsrqqppppppppomljhfecbaaabcdeghhiijklmorvz~~|{zyxwvuuttttsstttuuvwyz|}~}|{zyyxwvuutsrqqponmmlkjjihhggffffgghijkmoqsvx{~~}|{zyxwvutsrrqppppppqqrtuwxz|~}|zxwutsrqqqqqqqppoomljhgecbaaabcdefghiijjklnqtw{}|{yxwvuuttssssssstuvwxy{|~~}|{zyxxwvutssrqponnmlkjjihhgggfffgghhiklnprtwz|~}|{zyxwvutsrqqpppppqqrstvwy{}~~|{ywvtsrqqpppppppoonlkigfdcbaaabcefghijjkklnprvy}}{zyxwvuttssssssttuuvwyz|}~}|{zyxxwvutsrrqponnmlkkjiihgggfffggghijlmoqsvx{~~}{zyxwvuttsrqqqpppqqrstuwxz|}}|zxvutrqqppppoooonnlkjhfdcbaaabcdefhiijjklmoqtw{~}{zywwvutttssssttuuvwxz{|~~}|{zyxwvutsrrqponnmlkjjihhgggffffgghhjklnprtwy|~}|{zyxwvutsrrqqpppqqrstuvxy{}~~|zxwutsrqqppppppoonmkjhfecbaaaabcefghijjklmnpruy}~|{yxwvuttsssssssttuvwyz|}~}{zyxwwvutsrqpoonmlkkjiihgggffffgghhiklnoqsvx{}~|{zyxwvutssrqqqqqqqrrstuwxz{}}{yxvusrqqpppppooonmljigfdcbaaabcdfghijkkllmoqtw{~}|zyxwvuttssssssttuvwxy{|~~}|{zyxwvutsrqpponmlkkjiihhggffffggghijlmopsuwy|~~}|{zyxwvutssrrqqqqqrrstuvxy{|~~|zxwutsrqppoooooonmlkihfdcbaaaabcefgijjkklmnpruy|}{zywvvuttsssssttuvwxyz|}~}{zyxwvutsrrqponmllkjiihhggfffffgghijklnoqsvxz}~}{zyxwvuutsrrqqqqqrrstuvwyz|}~|{ywvtsrqqppppooonmlkjhfecbaaaabcdfghijkklmnoqtwz~~}{zxwvuttssssssttuvwxy{|~~}|{zyxwvutsrqponmmlkjjihhgggffffgghhijlmoqsuwy|~~}|{zyxwvutssrrqqqqrrsstuvxy{|~}|zxwutsrqppppooonnmljigfdcbaaabcdeghijkklmmopsux|~|{yxwvuttsssssttuuvwyz{}~~}{zyxwvutsrqpoonmlkkjiihgggffffggghijkmnprtvxz}~}|{zyxwvutssrrrrrrrsstuvwyz{}~~|{ywvtsrqpppoooonnmlkihfecbaaaabcdfghjjkllmnpqtwz}~|{yxwvutssssssstuvwxyz{}~~}|{zyxwvutsrqponmlkkjiihgggfffffgghhijlmoqsuwy{~}|{zyxwvuttssrrrrrrsstuvwxz{|~}{yxvutsrqqpppooonmlkjhgedbbaaabcdeghijkklmnopsux{}|zywvuutssssssttuvwxz{|~~}|{zxwvutsrqponmmlkjiihhgggffffgghhijkmnprtvxz|~}|{zyxwvuttssrrrrrssttuvxyz|}~|zywvusrrqpppooonnmljigfdcbaaabbdefgijkklmmnprtvy}~}{zxwvuttsssssttuvwxyz{}~}|{zyxwvusrqpponmlkjjihhgggffffggghijklnoqsuwy{}~}{zyxwvvuttssrrrrssttuvwxz{|~}{zxwutsrqqppooonnmlkjhfecbbaaabcdfghijkllmnoqsux{~~}{zxwvuttsssssttuvwxyz|}~~}|{zxwvutsrqponmlkkjiihgggfffffgghhijkmnprtvxz|~~}|{zyxwvuutssssssssttuvwxy{|}}|zywvussrqqpppoonmlkjhgedcbaaabcdefhijkklmnnpqtvy|~|zyxwvuttssssttuvvwyz{|~~}{zyxwvusrqponnmlkjiihhggfffffggghijklnoqsuwy{}~}|zyxxwvuttsssssssstuvvxyz{}~}{zxwutsrrqqppoonnmlkihfedbbaaabcdfghjkklmmnoqsux{~~}{zywvuuttsssttuuvwxyz|}~~}|{zywvutsrqponmlkjjihhgggfffffgghiijlmnprtvxz|~}|{zyxwvvuttssssssstuuvwxy{|~}|zywvutsrrqqppoonmlkihgedcbaaabcdeghijkllmnoprtvy|~}{zywvuuttsssttuuvwxy{|}~}|zyxwvusrqponmllkjiihgggfffffgghhijklnoqstvy{}~}|{zyxwvuutttssssttuuvwxyz|}~~}{yxwutssrqqppponmlkjigfdcbbaabbcefghjkklmmnoqsuwz}~|{yxwvuutttttttuvwxyz{|~}|{zyxvutsrqponmlkjjihhggffffffgghijklmnprtuwy|~~}{zyxxwvuutttsssttuuvwxyz{|~~|zywvutsrrqqppoonmlkihgedcbaabbcdeghijklmmnoprtvy|}|zyxwvutttttttuvvwxy{|}~~}|zyxwvusrqponmlkkjiihgggfffffgghhijklnoqstvxz}~}|{zyxwwvuutttttttuuvwxyz{|}~|{yxwvutsrrqqpponmlkjhgfdcbbbbbcdefgijkklmnnoqsuwz}}|zyxwvutttttttuvvwxy{|}~~|{zyxvutsrqponmlkjiihhggffffffgghijklmnprsuwy{}~}|{zyxwvvuuttttttuuvvwxyz{}~}|zyxvuttsrrqqpoonmljihfedcbbabbcdeghijklmmnoprsvx{~~}{zywvvuutttttuuvwxyz{|~~}|{yxwvusrqponmlkkjihhggffffffgghhijklnoqstvxz|~}|{zyxxwvvuutttttuuuvwxyz{|}~~}{zxwvutssrqqpponmlkjigfdcbbbbbcdefgijkllmnopqrtwz|}|{yxwvuutttttuuvwwxy{|}~~}{zyxwutsrqponmlkjiihgggffffffgghijklmnprsuwy{}~}|{zyxwwvvuuuttuuuvvwxyz{|}~}|zyxwvutssrrqpponmljigfedcbbbbccefghijklmmnoprsvx{~}|{yxwvvuuttttuuvwxyz{|}~~}|{zxwvutrqponmlkjjihhggffffffgghhijklnoqrtvxz|~~}|{zyxwwvvuuuuuuuuvwwxyz{|~~}{zywvuutsrrqqponmlkjhgedcbbbbbcdefhijkllmnopqrtwy|~}{zyxwvuuutttuuvvwxyz{|~~}{zyxwutsrqponmlkjiihggfffffffgghijklmnpqsuwy{}~}|{zyyxwvvuuuuuuuuvvwxyz{|}~~|{yxwvutssrrqpponmljihfedcbbbbccdfghijklmmnoprsuxz}~|{zyxwvuuuttuuuvwwxyz{|~~}|{zxwvutrqponmlkjjihhggffffffgghhijklnoqrtvxz|~~}|{zyxxwvvvuuuuuvvwwxyz{|}~~}{zyxwvuttsrrqponmlkihgedcbbbbbcdefhijkllmnopqrtvy|}|{zyxwvuuuuuuuvvwxyz{|}~~}|zyxwutsrqpommlkjihhggfffffffgghijklmnpqsuwyz|~~}|{zyxxwvvvvvvvvvwwxyyz{|}~~|{zyxwvuttsrqqponmlkihgfedccccdeffhijkllmnopqrtvxz}~}|{zyxxwwvvvvvwwxxyz{|}~~}|{yxwvutsrqponmlkkjiihhhgggghhhijjklmopqsuvxz|}~}}|{zzyxxxwwwwwxxxyyz{{|}~}|{zyxwvvutsrrqponmlkkjihggfffgghhijklmmnopqrsuvxz|~~}|{zyyxxxxxxxxyyzz{|}}~~}|{zyxwvutsrqponnmllkjjjiiiiiijjjkllmnoqrsuvxy{}~~}}|{{zzyyyyyyyyyzz{{|}}~~}|{zzyxwvuutsrrqponmllkjjiiiiiijjkklmnnopqrstuwxz|}~}}|{{zzzyyyyyyzzz{{|}}~~}|{zyyxwvutsrqqpoonmmlllkkkkkkkllmmnnopqrtuvxyz|}~~}||{{{zzzzzzzzz{{||}}~~~}|{zzyxwvvutssrqqponnmmllkkkkklllmmnoopqrstuvwxz{}~~~}||{{{zzzzzzz{{{||}}~~}||{zyxwwvutssrqqpoonnnmmmmmmmmmmnnoppqrstuvxyz{}~~~}}||{{{{{{{{{{|||}}~~~~}|{{zyxxwvuutssrqqpoonnmmmmmmmmnnooppqrrstuvwxz{|~~~}}|||{{{{{{{{|||}}}~~~~}|{{zyxxwvuutssrrqqppoooonnnnnoooppqqrstuvwxyz{|~~~}}}||||{{{{||||}}}~~~~}}|{zzyxxwwvuutssrrqqppooooooooooppqqrsstuvwxyz{|}~~~~}}|||||||||||}}}~~~~~}}|{zzyxxwvvuutssrrqqqppppppppppqqqrrsttuvwxyz{|}~~~}}}}||||||||}}}}~~~~~}||{{zyyxwwvvuutssrrrqqqpppppppqqqrrsstuuvwxyz{|}~~~~}}}}|||||}}}}}~~~~~}||{{zyyxwwvvuuttssrrrrqqqqqqqqrrrssttuvvwxyz{|}~~~~}}}}}}}}}}}}}~~~~~~}}||{{zzyxxwwvvuuttsssrrrrrrrrrrrrssttuuvwwxyz{|}~~~~~}}}}}}}}}}}~~~~~~}}||{zzyyxxwwvvuutttssssssrrrsssssttuuvvwxxyz{||}~~~~}}}}}}}}}}}~~~~~~}}||{{zzyyxxwwvvuuutttssssssssssstttuuvvwxxyz{||}~~~~~~}}}}}}}~~~~~~~~}}||{{zzyyxxwwvvvuuuttttttsttttttuuuvvwwxyyz{||}~~~~~~~}}}}~~~~~~~~~~}}||{{zzyyyxxwwvvvuuuuttttttttttuuuvvvwwxyyz{{|}~~~~~~~~~~~~~~~~~~~}}|||{{zzyyxxxwwwvvvuuuuuuuuuuuuuuvvvwwxxyyz{{|}~~~~~~~~~~~~~~~~~~~}}}||{{zzzyyxxxwwwvvvvuuuuuuuuuuuvvvwwwxxyzz{{|}}~~~~~~~~~~~~~~~~~}}|||{{zzzyyxxxwwwwvvvvvvvuuvvvvvvwwwxxyyzz{{|}}~~~~~~~~~~~~~~~~}}}||{{{zzzyyxxxxwwwwvvvvvvvvvvvvwwwxxxyyzz{{|}}~~~~~~~~~~~~~~~}}}|||{{{zzzyyyxxxwwwwwwwvvvvwwwwwwxxxyyzzz{||}}~~~~~~~~~~~~~~}}}||{{{zzzyyyyxxxxwwwwwwwwwwwwwwxxxyyyzz{{||}}~~~~~~~~~~~}}}|||{{{zzzyyyyxxxxxwwwwwwwwwwxxxxxyyyzz{{||}}~~~~~~}}}|||{{{zzzzyyyyxxxxxxxxwwxxxxxxxyyyzzz{{||}}~~~~~~}}}}|||{{{zzzzyyyyyxxxxxxxxxxxxxyyyyyzzz{{||}}~~~~~}}}}|||{{{{zzzzyyyyyyxxxxxxxxxxyyyyyzzz{{{||}}~~~~~~~}}}}|||{{{{zzzzzyyyyyyyyxxxyyyyyyyzzzz{{{||}}~~~~~~~}}}}||||{{{{zzzzzyyyyyyyyyyyyyyyyzzzz{{{|||}}}~~~~~~~}}}}||||{{{{{zzzzzzyyyyyyyyyyyyzzzzz{{{|||}}}~~~~~~}}}}|||||{{{{{zzzzzzzzzyzyzyz|zy~}|{zyz~{z~||~||{}~v}~|{|~|{yy~z{x~z||wyv~{zw{|}{}~qruztzwurvvuw}zyzzx{||~v}||z|{z|z|{}v||~|y}y}zz}|v~}{xxw~{zu~}{||}y|rqvztzyxrutt{zzzzyy}~}{v~|~{zz}y|z{w|}~w|w}|~y{~~u~~{xxw|zs~y{}|~v}zrtu|t{zzqurtzzzzyw|{z|w||v~|y|}~{~}~x}~|xzw|z|}x|wy|~wzx}{}{sw|}|}{vyyrx~t}v||{pupy~z{{zyt~{xyyyy~t}z~}y}z~z~}zyzwyzz|w}t{}~x|z}{}{r}u{~~}{|s~wyuyt}v}}puoz}z{z{yq|yvxzyyt|{}x}zz}{}}|xzvzwy{~w{wz~}{}}{}{{r}q{~~yp|vwxxu}u}ruqvz{{{{rv|tw~v|}s~{}{x}|z{}{~w|u}szw{z~{|~~}x{wlwzuym~x|xj|}}yxww~vudrxw{sh|~|vt{t~zw~}y{yq}wzu{z}~u||{x{yyysv{{{xz|ut|xx}qoxym~zs~muo~uyrsq{}z~rs~zy~||y|z}y{}yvsy{}{us|t||~||~tu{}z{yyv|yx~}{|{uu~wtz~uyxusw{{pxwmv}w~yy~t{zu|ypz{}uuy}~w{}{|zxx|zw~|s{~t~z~s~zyw|{vwtwssv|x{{xzsou~yw~tv{|vz~utvtt}{|vvv~rz~y~vyz{~~~|yz{}vw|~~r~}ztt{w{|}~sxsx|xrtvz}wo}zt{{|i{|||vsxxrjtv~|~{|}z}|}wv|}{yv~}yzzt{xu~kz}~{{tty}{}z|n~zw~t{yzsxtyoy|pz{yt|{muy~ry}{wv}t~ww}~y|u~}z|}|v~{y|xww}}~yp}x}xnvsxyvwyv}yyys}rtzy|{syt{v{w}wqwzywyww|vuvt{~tqzqvzuxv~q|wx}x}ywx}y~xvxouxt}}~us{x}pwyuqtu}{|tv}zzxyqv|up{nh}wpty}{st}}||svz|}mqzw~~|yltyuoy|x{y}qxz~}{qjy~|}q}oyw}yoqoz{{qmz}}}o|r{{sz~wtuzwoz~ukz{{vx{v|qwi}tz{~~~vyn{l|tzqu~||w|~t|}w{~u}}r}{qq|y|v{ws{tzy}}~yozrusou{}sypz}~|rzs|v}|zv{ttu~xu~ru~u{}~z~}oy}}ylsqyz|twl{xn{}~z|wxl{t~zysr|q~{|~~|x~~~z|u|zt|owxqrzyzyp~~~|zwz~~{x}{z}~zzpr{zyu}|mvtyrvy~uy{|zsuty}zyxw~~{x|uw}~~{}ws~{|}zt{}}qx~}w}~ru}{||v|||vyy~y~tz{v{su|z{~t}w|~z}~{{}{ywu}{}z|smx|~zyv~{{yx}y|{|{vy~xelhn|}x}xhbd`Lk[asdk~|w~vtvls}xyijin}qvsw~{rp{vs^p{zxrdyn|w|x{}{y}hv|nsxyuqwsjilrxlx}vkvsdihfvskyjy~{{iaz}vldifx{l}uovozqlfWovu}fHV[[m_[ttd|rq}|ydefe]Vafntoglqqv~{z}sxm]qv|~{i`hjr~njprfjbi}yiXaPh~}wzagicascuxfm{`juiilhnjnnq}rmgdnZgwzuy}uv|un^_[\kmu{pziphfwvw~~oXVDP^^qehdjh_ssmdbqhluo~}tilibo^\na[nnozyxpreluxo{yvz|fpnm}vk{phl\k}z|o{pox{oj[]lovwyfsu{esm\jccjcdu^cox~uw}qmzluhimu}~~}}zwrudcj\oslzvs|mtvdz~~}}|y|n_c_tr}{u{|~~rymygcqxu{vzwv|kuxo}y~wvwjpk^ir`pgjn~{zor~uqozisyov|{z{~lnej|q~rjZdkjgZXQUWOT^J\TN]`gV`sllywpx{qrhpt|v{|{~tyvvy~z|upvolhsg}vox|{vrjYSMT\\kyx|yog_S_bc`icxlcgeh[ghgo}vm}|z|ty{v|qyqlz}y~p}yoxw{~|ahbh`\mr~xsrfompmmg\b^a]mYgi`kdgY^g`_qj\__ojtssyvq}{u}y~~uz|}oodorg|y|z|wxgjmrs`\V`^`]k^lthwpmY^kdhurdkjupzwrurmnx^ebcisnnxonlpmrlyiwuywq{ripvzsqgtquq{{~~u{vr||yspcmrnjglc|uz{yrk|wyy{~|||w}svjv~}psumksrdpjqckxz}w{xlqo{{tiisospxg|y{~twt{vqwwwhdcdQVZb`\szxqo|rxr~vw}~qr~rt||{~y|{x~~t|ux|yyu~{wv}qyrc_joacdqcmz||uzzkidmm_Z]aX^YaR^bamibUah]cpneffilzx{{~{y}x}}xmmuhcjqvt~zqmmreYUgchcfe_qfloiY\fbctzlommnswswpnexmheaksmgrj__nnlsttfy{~}||s}upq|vn|{xzw~yzwr}|s}~y~tkuuuumxex{~wq}{r|}xvs{y~ztwpt|mvrecpvkpqvjtw~}yytx|rnistpfsvktnym|~yy~z|rzw}uw|vipo]SZ`Z`erhv{snzz|wxm|y|y{xzt}}zyv{|v{~|{|su{sdmn]Wbjchjoev|w~xqkrmb]ggbf[dYfgfnj`_nmagrlmmhnsys}zy}z~uuq{}xx{jgolfkpplzswpgdlflljrhsnppj`ake]ktprmmr|wqvqthsnkkfpqggqk_anmnutlnyt~z|vt{ysuyyz{w}{{yqu{z{|w{}vx{ts{zwqzwv}zsw}y|~~{{{rtyqfjtqsutgoss|}{zxortomvwmvlspr{zzw~|}{xzwqyrwwu{vilreZ_d^]`bZmv{x~y|zrz~tv~w~ypyy}{z}}y{|yx|~z{|pekoc\cifilh\nrt~}tvuokspfqdhbahhkjcalpedoonnlmr{pws{}x~}}v{yqw}z~}r~|}tiimjlnmds}}tostiuotvpvuwsjfmnfdlppsqtzsxvsznvsrqopjdenjadmmmqsioys{~wuzwv{y}zw{y}onwzx{{sw~zvx}}}ytyuw}{~{w~|}sowylflonpqjfvrz|{~z~wprropzunyqxrqx{}xy}}}}~|rznsvmvsuuxxmegk`UW][^cd`qz}{zw~|v{su~}rs|{~~}~zx|}|wu~wrxvwtupfbil_Zafehjgfxxxxxtsxslukqohlmmjeirskirvvusvzyp|tw~t|~vrw~wmpwx{~|w~{zyppuob`gilpro~|{wty|ryzxvuvvslgmtpgmvxzxy{uw{pyonrppnnh`bklcagkmpqmty~~yzyx}{|{|~}}|tqwzpkqwxz|{}z{||yu{xx|xv}w~~~}xnlrvkchnnookn}w}{x{wqrrrv|tt|r{ztvx{yx~|y}yxxpkuipqhoruttoc^cf[RTZ^cggmz|zx{{rpv}vpty{~|}}~|x}yw~wt~rxvlrttrpkcafjb\_dgjkio~x}|zy}vv{pwvnmmnkipwvnmtxzxxz{sr|r{zry|~}wppv{smptvwwt|zzs~ztz}~wonrqe^aehjllzv~{|{zwrpuxtkmu{~}~v}|r|rmrstqogadlnebfknonm{~z|||~z~}}zz||z|{z{zz~}{rmpwtlkortusu}{|{z}}w|}y{{vx}~|xqosxskikmmlilytwz~zwww{xu~ux~wvvwwx}}y~}zrltjlrfhnrsrngegjcYWYZ\^_gxv}{w}}~}wonsxslmpsuusz|{|zz{v{}vu~rvznosusqkdchmha`beggflyt{}y|wsssqpuz{tosy|||}{us~tv}rsx{|zvposxtmlnpqpns{}xt|srzppuz{zunmormc__acdem}|~|z|~wpsy}}y}ywrtwvsngdgmmfdfiklko|{y{|{zzzy|yz~w~u{suxyxtnkmstmijlnopr~~}|{|zx~~w}v~~tvz}|ysnotvqjijkkkktzs|z}{z{wzw}zwwxxz}x{~|ztkjrgipfglpqpkecfhc[XY[^`cm|zxz~xy{{xtmkouuolmprstx~~{zyxzxuy}~|wquyntukknpomgbcilhcbdgikmx{~~xwvvv{~xtv{~~{uv~uu}uruxxvrmmqvtnklnopqx}}~~~~}xpmvpkuojnrtroifhkib]^aeimv~|}|uuzzw~{ppssqmgdglnjggikmot}}|zyyy|xvz|wrxzpvxnmprqnigjorniiloqsx}||~|y|{v|uz}sruwwtoklqtqkijllmpy~w}~~x}z~{zz{}ytuxyxurlfjndiohfjmljeaadfb\Z\`dho|}y|~ytvwvrnjkptrmkmprtx|yxxz~xtvz|||ytotwlpulhjlkiebeimkgegikms}}}zzz|yvy}|wt{}sx|spsutrnlnsurmklmnot}y|}}}~|xz|}{ytnjppgnqiimppmhefije_^_bfks~xwz}~y~trsrplhgjoplhhikmpx~}|~zxxz}{vux|}|{vqszsowrklnomjgglppliijlnr{~|||x|xwyssuuspmnrutokjjklow|uy}|y|{{}{z{}|wvxzyvrlgkoefnhdfijigddgif_[Z\_biu|x{|yxywtollpsqmjjklnt}z|{{}yuuxz{zwrotwnouniijjhedfjmlheefhjpy}w~~yz}}yv{vv}wqqssqnlmqtsnkjjklpx{uy~}~~~ywyzywsmimphgpmhhjlkhffikhc_]^`dkv}z{z|~}}xuttqmjilpplhghjlpx}yz}}{z{~{vtvxzzxtpqxslstmiijjhffjnpnjiikmqy~||}|xy~uz~wrqrrpmmosurnkjklntzyt||}{|}}||ytsttsqlgejlcemidegigeccfhfa]]^afny{}{}}zutsqnkknrspmllnpu|~~|{{}~{vrsuvvtqnntsjmrlgfggfddgknmjiikmqx|{z|~~{wv|~vu|yrpppomlnqttqnmmnpu|~x|||~~{urrsrplheinhdmnhfghhfddfhhdaacglt~|z|}{ztrqoliiloqolkkmpu|~}}~|{{}~~zurrtuuspmourjnsmhgggfefinqpnmmorv}||~}zwy~}tw|wqooonllnrtsomlmnrw|zv~~|}}}}~{upnnnmjfcekjcfnlgeffdbabefecabejqz}|~~wsrqnlkmpsrommoqu{}~~||}~{vrqrttrolnsrjkqmheeeeddhloomllnpu|~||}~~|ywy~|tv{wqoonmllortspnmnosy}zw~~~~{uqooomjgdglichnkgfggedceghfcbcfkr{||~~~}}vsqomklnqrpmllnqv|{|}}}}~|xtrrssrpmnrtmjpqkgfffeegkopnllmosx~|~~~{yzwv|{uqppnmmorutronmoqvzyty{~}|}xsponmkgdejjcckkgegkmpsw|{|zz}||}}|xvwvtrsty|wwymafpkouy~}zvv}{z}xrlifed`\Z[_^VW_`]^clu}|fR^cSXdqx{|xtsvsjhnngddefhkpw|}xnb]g|ynq{}|{y}zv{~|{{zxustxxqry{xvvwyz|zsja[\dw}wtrssux}}y}|y~|z{|~~~~yvtronoqttsrrsuxz{vmjqv{~{z{}~{oiluxrrvskkopolklmnmjgeefhiiijotrlpvtpnnnnnoqtvvtrrrtw|~zw|{uuvutstv{}|z|wsqqqponmqwxsv~zusw}ywurmhb__`aaabekojhnqnjhggggiloqponoptx}{}~}}}|}}|yurqrssrpptxvpsyyustuwx{zwtrx|tmhfdb`][\a`YX^_ZWVWWXZ]aeggffhjnswvrw~z{|tnpsomv~vqnlkifeflnihosqnmnnnnoruwwwx{usvxz~}wtuungikf_[YWUTUWZ[[ZZZ]`flnlmv|yy~|z|{vpmklosvz~|yy{unptrmkjkklnquxyxxy{~|wrlgcbgpz~tmhd`]\\^^]ZXWWY\`b_]djhfinruvvvtuw{}}~~}|xrjc^[YXY[aly~|}wtpmjijlmllkmptzyuqlgc`dkopz~zuoifccdghdaflifimqsttsrrtx|~}yy{z~~|xutttrplijkf]^cdcfltz~}{zz}~ynkkd[VTUY`jv{twxqkkmnnlkhedfilnnoppnklsuppvzyxxxyzyvqnmmmlkijnrojnsqkgda^[Y[^bfkqy{vpjfca^ZWWZaly|yyzzxwutqlhkokfinnmlmnpqpnkklmnonmouvqouwspooooopsutqmjhghlprr|}wrnjhge_X[bdemz|uqsunimpnljiijifb_]]^^]\\_efbdlnlkklllmnpqqomlmptx|yux|wpnqtw|{}xqoponlkjkpx|xutrplgcaaaa`_^aff`ahheccdegilpsutsstvy~~{{xxz}~~~}|~~{upmlljihhkmpv|tnkhea^[\``ZZac`]]^^^^`beedcdfiouz{y}~~}vrv|{}~}~~|tmkmgacimqw~}}|unpqlgdbaa`begiiggghkoturrz~{z~zxzzwttuy~~zwvvuuvy|~}{zzz||wxwrmhecba`_`elt{}yurnkjjigda`acgmqpmqwvqqtwy{}~~{tnjjlorw|~}~~|zxwxz}~~~|yuohb_\[ZYY\dnsu}zxwxzzvrvzwsux{|}|{zy{~|xutsqnkhillghqvwy}}|}~y{{|}wrnliea]\^^WTY]\\_djpw{{||}}|{{|~zwz|}zyzz{zyy}{uxzuokihghlrx~|z{}xpprmfa^\YXX[_dhlqx|ww||wvz}|{z{|}}{xvvwxxwvvy}zuvzytpomljijkkifedfjpx~|{||||{{~}~|xurokiggeb_[ZZ\`fklnx~~{vsrrstttvz{|~{yxwvvwxywtqnmlmnnjfkqpou}~~~~}~}ywutstsmhjlhdfjnsw}~{wsrrrrqoorvtnqvuqoooopqtwxywuttuwyyuqtwqmmooonmllnsz}xuttttsstx}|y}~}}|||}}|}~ztssrpoljijmqvy}~|zxy}{w|{wutsrrsuvwvsrrrtwzzvw}~zy|}zwvvxxwwvvuttz~}{|{y}zzzz{|~{zzzywtrplgejommu~}zusrqpoqsttrpoopruwtqtyxtux{|}|{yxy{}~~~}{xsqtuolorsstw{||~{zzyxwxz{zyxxy|xtuunknpponoqtwxy{~{xutstwxtquzwtvz|~}||}}|{xz|zwtsrqpnkijmqux{}~}yvsqpnje`^_`acehpy}xy~zvuxz{{zxwvwz}|{~{~~|{zzxtojgedba_^afigktz|}|vqnlifcaaefbahmmnrw|~~}z}z|~|xvuuutrpprsnilnjgefhjmrx|{zywurpqsnggif`\ZYYY[_ejnquz~}}{~||||{xtqoopponnquupqvwromkigeeeffeegkpx~~}|{|}z}{urpmjhffeda^\[\_djnot}ywwwxxwvvy}zvx|{wuttsssuwwvsqonnoqrpntzzz~~~}|}{z~~zvtsrpopqqoliecbbbc_Z\ba_agnty~{vtssrrqpswzvuz}zwuutrrrtuutsrstvy|zuswunklnpruwz}~~~~~}}}~}{wtrpppqpjgjkebceefeedeglry~}z|~zxwwwwy{~}|{{|~}z~|yy{|{zxurqrtvxy{}~}{|{yxxxxy{}~}{yxwwy{{vruxsoopqpnlieddegghhijjjmv||~|zxvuvxyzyxxz|~zz|}~~~}}~}zwsnmrust|~|zzz{~~{x{|xy|}}}{yvttuvutrqnjedghbafiklnsx}}xw{ztrtutrqqrtuuuvy}}{{{}{y}|y{}~|zyz|}~}}|zvrqtsljllifcba`_][Z\`dimrx|}{}~{vqmkkkklmpw}}~~}}|~~}z||vwzywtsrqpmieba``_^]_dgdfntux|~}zvtssrpnlmpoihmnljknpsw|~~|~}{yxwvrnigedcb`^`cc^]bc_]]^^`bflquy}|{{zxwvx{zvvzzvsrqonnoqsssrsux}~~zxvvvutrprtrlkomiecba`_acddccceintz{}~~~~}{z|~yx||xtsrqpopqrqnkjiijmonlpwxw{~{xusrssrqqswxsswxtpnmkhgfffeb`^^_adgfcekkhjouy~~}~~{xusrqrrpkjnnjiloqtuwxz}~~~~}|z{~{x{}{wuutsssuvvurpnmmmomheijebdfghiiijmrx~}||{{{|~~|zyxxy{{wsvxtpprssrpnllnqtwy|~~}}|{zy|{|}zxwvtssuuusrqqsuwyvrruqkhiihggeddehkmoopppppw~}}~zy}~ywy{|{{yvtstuvvvuutqoqwxux~~}|||~~|zzz{}}zvy|xttvvvuspmkklmmlkjhfbafheejortw{~}}~|{{|~~}|zvsuwsopsrqpprtuvvvwz}~}~~|||}}|~xuvxxxxxwvwxz{zxusojffjhceikkjklmoonmnquy}{{~xwzzxvutttsqnmnoqstvy~~}{yz|}}|{zyvrqtsmknomjihggfc`^^^`abbejprrw}|zxwwurnkiiiiihilqqnqwxwwxz{}{wuxzutx{{yxxwvtqlhedba`^^`ddacikjjmpsvz~{zyyxwutuwuppsromlmmnptx|~}}{y|}|zyxxxwtqnmllkigfgifabec_\\[[[]`cfhijmpuz}zyzzzyxy||xz}{vsqomkjklmlkkmorv{}||~zwutsrpoortpnqsqmkjihgfghgdb_^^_adecelpory~||{zzz|}|zwutstuvsoputqrvy|~|{zzyxvuvyxsruuqnmlkjijklkhfdbbbcda]_ba^^begikmoqv|~}}~~}|zyyz|~|xy{wqppqqppppqty}~|~{{}{zzyxxxyyxuqomllllhdehd``abcba`_^`cfilnqsuvy||}~}|zxusstvvvvvutrrw{yy~|{zyxxxzzzxusrrsttqmnrokjlmmlkhfeefghhhhhfdchkiimruvy|~}|}|ytqrtpmpuwxy{~~|zzz{xtsvuqoqrrrqomkkkmnmmlkifcdheabfgffgiknopqsvz~zz}|vuxxvusssssqonoqsuvwy~~{}|xyz{{zywuttvwwwvutqmlooigiigecbaa`^\ZZ\^`behmuyx}}}yurpoonllmrussy~~}{yyy{||{{zyvrrvtooqrpnmmllkhecbbbba``cffcflmmnqtw{|ywvvutrpqsrnmpqnllmnoquy|}z|~yuvwvtqponmkgdbbbbbaabefcadec`^^^^_bfjmpsvz~}{zy{}zwy|zwuttsrstuvutsstux|}||~}}{{{{ywtrppponlkmolghkiecbbaaaceffedefhlpsstz~~{ywvuttuvvurponnprsposwvux|~~}{yvsrrssrqqsvuppsqmigedbabcddcbabceghebehfdeimqux||ywvvvvrnprollopqrrrstw{~|{}|xx{yvsrqppopqrpnljiiijjfcfhdabdeffffffimqux{~}z|}yvwxxxwvtrqsuwxyyz{zy{}zxvtsrsttsrpppqstsolnmhddeeeedcbcehjjjjjihfinpot{}~~}{ywvwxyxxwwurprvtqtxzz{}~|zzz{{xttwuqpqqqpomjhhhijihhgeb`ceb`dghhjknqtvwxz~{y{|wtuvusqqqqqponoqtvxz}~}wvvvutsrqpqsuuutrplgeggcadfggghijkjhffgijlmotz}}}}{{zzywtrqqrrrqrtyywy~}{yyyzzyxwvspnpqmjlmljhhgggeb`_______afgdflnnoqtx{~}{xussssrqqruvrruvtrqqrrsvy|~~yvwxtprtutsstttrnkigfeca__bc_^cedcdegiknrvxz{|~}|~{wy{xusrrqqrtvwwwwwy{~|{}}{yxwwvuromlkkjigfgifbcfda_^^]]^`bdddddfhlpsstz{ywusrrsssrqqqruwyxuw{zwx{~~{xvusrommopmjmomjiihhgghihgdba`abdcaafhfgkosvy{}~|zyxyyyvsuwursuwxyyyyz|~}|{zxvwyxsrttpmlkjiiijjjhfdcccdec_adc`acfgijjklosx{}{}{usstttttsstwz|}}}~}|{}|~~|zyyxwwwxwuromkkkkiebeea__aaaa`_^^`beghjlnnnqx{z}~|zwvvwxxwwvvtrrwxuvz}~~|{{{||{yvusssttpmoqnjklllkjhfeeghhhhggeccfheeilmnpruy{}~~|ytruurqtwxxyz|}~~}||}~}}}|xtuwsoopppomkihhijjiihgebbeebadffeefhjklklnqtwy{~~{y{{zxwvvvutrppqrsstux||z}~~~~}{ywuuvwvvtsrokjlkfdffeca`aa`_]\]_abdeglqqquz{{{}{xwutsqoprtrptxxwxz|}~|{||}|{zyxtrsurnoqqomlllljhedccccbabefcbfhgggikmpsx|~}}{yxxzyusvvsqpppppruwxxxxz{~~{xyyxusqppomjgfffffedfihddgfca_^^]^_bdfghjlpty}~~~}}{zzyyyyz{zxvuttuwxvtw{zxz}~{xvttsrqonoqokjmlifeddccdeffeccccegihgjoooquy|~~~~~|ywvvvwxurtwvstvyz{|||}~|zzzzyxwxzzvtvvrnljhfeefffedccdeghfcdfda`bdeghjkmqv{}ywyxtrssssrqpoprtvxy{|}||}}ywvutssstsrpnlkkklkgehhdbcdeeedcbbdfiklnoqqqtz}|~}{yxxyzzzzyywuuyzwwz}~~~}{yxxxyxwuttttuvsnnolgffffeedccceghihhgfcbdhgfjosux{~|{{|||{{zxvstwvssvxxwxy{}}}}}~}}}}yvwxuqqqqqpnlihhhiihhgfdb`cea`cefffgilmnnoqtwz|~}}~{wwxwutsssssqqqrtvxyz}zxwxwwvutrrsttsrpnlhddfeabeffffghihgeddefghikottuz~~}}|zwuuuuutttvyxvx||{{|~|{z{{zyxwuronpokjkljihgggfdb`_``````bfecfjkklnprux|}{{{{zzyxy{zvvxwtrqqpqrux{}~}ywxxtrtvutssssrpligfedb`_`cb_`cdcbcdfgilorsttvwz}}}}zxwvvuuvxxxwwvwxz|~}|~~|{zyywurpnnmlkihijhdegfca```_`acdddddegilonosxxy|}|zyxyyzyxwwwxyzzwuwxusuwy{|}~~|zywtrqrrolnpnkjjiihijjjigedccded``debbehjlmopqtx|~|yy{yvuvwxxwvuuvx{}~~~~zxzzvsrqponnoonljhggghhfcdfebbdefggfffhknprtvz|~|{{z|}~~~~}{ywtrrpppoljjfbbefdfkqw}||wrnlhb[ZYUQPQRSTTUVWZ]`bdefgfddgifegiihiilnopqrsvy|z~|zxvtrporussw{}~}~}yvtssrqrtuvuvwy|wnhen~}qjgghilosuvwwwusqrrnklmljiiijkkkkmptx|zohhpyqjebaaa``__`aabbbdghffikkklmnpqtvyz{zyyz{}~}zz}|xwwwwvtrpmlllmlkkkigfgjigilmmlkigc^YVW]hs}{xz}~~xurlea__ckvzxwqmkjihfca^]\\]\\[ZYWTSVVSRUVWVVWYZ[[[[\^abdefimnmnrsrrstvxz~~~~~zuoid`^^`hu}xrmf`[XUTRPMLMNLIJMMKJKLNPRUY]_acehlpuxyz}wsqrv{|xutsrstvwxxwxxy{~~}zsmje^VOKHFHNWcoy~zzwrooooppppqrtwyyzzyxvuvyywy}}|{zzwqmmkgehmt|~~}{zyzzzyyy{~}z|~~~}yvtqory}zyyvqpqolihgfffghhhgfffgiklkjmppoqsvwxyyyz|~~{vromlmosx}xtqnllmmmlkkklmnnliklkhilnprtuwy}{wsppquy|zvsokhdbaaaaaaa`_^_bdbcfikklnprttttuvxyzzz|~|||{zzzyyz{{{ywvutttsojjic^\[[\^afkqx~}zwuxxvvy|}~}xtnhb__][]dmu}{uttplifdcaaacccbaabcdeebacda_abdeffgghkmnopppomkloomnruvwxz|}~|zxwvtsqprw}~zwspnligfgjpwzupmkigec`^\XVWXUSTVWVVWXZ[\\\^`cegiknsvuw||{{~~|zzzzywvwxyz{{|~|wqkeb_XSSSTUX^gox~}zustspnmmmnoprttttttuwyzywy|{z{}}{yxvspmllkhinsvz~|~}zwusqomlkjhebaacflsy~~}yvvvusrqqqqponnopqrrrtwwuuwxvtsssttvxz{{||~}{{{yxvsqopqtw{~}|ywuttsrpooqqonqrqqqstvwz}ywvtrppqty}|ywuttrpnljjijkkhhkljjlnpqrssstvx{|}~~}~~~~}zxwvuspnkjifaabbbcglquz~}||{yvxzxwx{|~~{ywurokhedcdffglty}|||xttvutsstttsrponnmlkjjlmkjlnnmmnnopqsuvutsssstutqprrommnmlkifdccefhkoty~~{xxwrnnmjhfefgilosy|zxwuspnkihhhhfccfebcefgggggghjmnpqrssrruxxwy}~|||ywwy|~~}{zxwuqmieb`_^][[_dfjpx~|xwwvrqrsrqpqqrrrqqrstuvwx{~~|}zwusrqpoopqstx~|zwtpmmkfcca_\ZYYYZ[\_djry}zzz{{{{{{{|~~|zy{|yx{}~~~}||{zywuuvtpoqpmllmnqtz~}{ywvuvwxxxwwvttwxvvxz{{{{}~~~~}~}zxvtssrnlnppquz~}{yvtsrsrqqqrtsqruutttuvwxz|~~~~~}{xvutrqomkigfgkmnqw|~}|zz{|zwxyxvuuvvwy|}||{xvtrqomjhffghjmpu|}~}zxxxxwvutssstuvvvvstv{ywvnjkjhhjlmmnqrsy~|oggqzuf]\agnty||yxsonllkjhgfghiloty~~{xuqmkie_[[YVTSSTVX\afkrw|}wk^hzurrw~~qluylfgkoswz~~{yxxyz{{yupmkjkmpsuw{}u{xsvzvrvtljlqsrsutqonpqsvx{}~~q`SMOXgy}tkeb_^]]^_aa__^^__`a_]_aa``bdfgikmptx{~}||{x|{xvuuutqmga[WW\dlsx{wqkgdb``bfmuz}zxvsqmjgc`][\`gq}~|{zywusqomg`][XWY^dimnoppnic^[Z\_abdilllnqstuuuuvxz|~~{xvvw{|smknt}z}xpeXOHIMQX`efosrqtvvx|~{vuzv~}hdhoxvp\WUW]hvxqtxppw{vhWLMVcow|{{}wu}~}}ukfisszz~}|}}p`U[osnz}zyyzzyxvx{zlens^RPTY_cdb_ZURONOQSUUVZblru{qc]_jyzuqmie`[VSQPRV\cksy}}xrlkp}tkjuukecefeb[RKGLZiqwytja[Y]ckrx{~{yxwxy{|~ynecdekx}yxz~}}~~o]MEGP^s}pd_`]XVY]`cfhjmpsw|}rhcemsw{xrrsw{}|xsnieb`___begjmpsuvwxwurpwtibaenzyoga\ZZ[\ZXZ_a]XWXZ]`cfhjklmrx~~yurru}zrpqsvxz~zrh\TTTPQ`y|xtrrrtuwz|{tmf`^_fx}{xtojd_[XWZ_hqy~}yvutuvtqotwsty~~|zzzyvuyui`\[^fr}vplihge`XQQRLHHMSZbkrvw}~ytqqswyxtprvu~~}}~~z{wohc]XTPMMNPSU^kqquy{{zxvuw||xz}yuuvxwvtqmifb_]^`dflxzvssux|}vng`\]dpxqljihc^^bda_cjpuy{~}ytokhec^[_gjls}}{xvtrqppppruy~|vrpt}zrq||wqmiggghikmprtvxxxyyxurssnhglquurtzzuuwxxxwwy}{wsokihilligs}|yvrmg`YRMKNYgu|}{z||xtrqpnjgkz|ywuuuvvvwynfc`[VV\`er{vrnjgda^\ZXVUTTTTVY\afhfa`gnnkjmrvy{|}}}|{ywuromkklpu{|vpjedn||{}{ywusu|}ytpmkklmmq{~|{{zzzyxvtrqomkgb_dntx~~~}}}}}}}~~}|zvsqootysolhd`\YW[ds|yuroljihhhd`_debbflrx}~{{|wrnlllloy~|xuqoot~ypigp~|wrlhedccdeefedddehiigfdddefgiknnlikni``iqty~|zwuroljhhilptvy|~{urqqponmkjijlr~}|||~~|{}}{zyyy|zwuw{~{|yvrnic]XRNKIJMSZct}zwtqnjgdb`^\\[[\\]^__`acgjljimoljkosuwy{{zvqnquuty||}~|xx}}}|xsokiggggfglrssx|~~zwtqnljgda^[YWWXZ^cioty||xy{ywwz}zz|}}}|{wsokigecaajuxuroligfglu~}uppnhcdjt~~yrpqsssqnlihfeeeglt{xurolihecclyztoqspkiijloqrrqonmllkkjihgggggfdcjrvy|xsoljiiikmpu{}{zzzzywusqpqrrqngep}xwwxy|~|yxvutssstuwz||zy~}xwwvuspmifb_\XVSPPRZhy~|zywtomptsonoqrsuwy|~~|~}}~~}{yxxy{||}~|ywtqnlkklnqrpjir|xurolifca_]\\]^`acirvvw{zvvuohca```acfjotx|~~yvwzyutvxz{|}~|{zzywuromjgehs}~}zvsoljijlotz}|{zywvtrqpppponmmmkfccfd_]_bdfeb]Z]fow~zvrokheceoz~}zwutuvwxxyzzyvtvzzvrqqqrsux{}yz~|xurnkjls}}z|~|yxxyzzywutrrrrqpnmkjjhgeca^\YURS[gosw|~}|{zzyyz|~|y{~}zyz{{yxvtsqponmmnmlhcaacabit}{yxxyz|}~}{yz~yvy|zwvwyz{}}{yxwwvx|ywtqoljgeca`_^]]]^ckv}xutssrpnkhebaacehihhlswwxz|}|zxtqnljjknsx~|yvtrpnmlkjheb`befkv~||z|}ywwvutromkihhijloqsuvwxz{}|{{|{||xspopqqomkhfdcccb`\Z[^^\[]_bejr}|xwwxzzz}~|zxvtsrsux}}zvsqoooooonmmsxzxxxvuvyyvqnnpv~}xuwxuqpqrssssssttspmkmmjfcccccba_]\\\]_afo~}zyyyxwvtsrrrrrrrrrty~|zxwvutttttqmjjnpprv|}zxvusrqpooooooonpw|yuqnlkjjhfb__cecaacehkqz}{zxwvutsrqnjhhlnnmnqtvz~{xtqnkigecba`aabcdegilr|~|{zyyy{|{xuuwvronnoonmkjjjklnoooqw|}{yxyyxxxyz|}}|}|yvtrpomkihgfd`]\`dfeeeehossty}|||}~~~~~|{{{{}}zyywuspmjgd`]ZXXXYZZY[`giiknqtuuvz}{~{wuuuuuvuusqomkjhgedcbbbcdddccekswxy~}}~~zwwvrmmpqonoqsuvvutuy~{yxwvusqonljihhiihfehnssux}~}}|yvrprttpmlmnnnnljgiqz}{xuspnlkjjjjjkkjjihinv~|zxvtqnkihhikmooosy}{xz}zz}|wsqrstuutpkebbca]YXXZ\]^_^]\]_fox}zy{}}ywy{xtqpppppooopqsvx{}}|{zzzyxvvy||}~zvspmjhfedcccdfgiihfgo|}{ywusqsy}yvvy{{yy|~~~~~}{zxvtrpnmmlie_YVVVSOMNQVZ\]`gmonnqx|xwy{~~~}|zxwusqomljhhgghjlnqtx{}~}{ywvutsplihkljgghjlmnnorw}zywvvtrnifehjihilptvx}~|zxvtrqpnmmlkkjjjjjjigb^_grwvsrrstuvvwxyzyyxy{~|yvspmkigfefgijjjlqy~}{{{{zzz{{{yvqnnonjfdcb`_][ZXWVUUUVXY[]^`adhoz~}|||}~{upqvwupnosw||zwussstuwxxxy~{upmlkjjiiiijkllmmnqv~{xvuvvvvvvusqpsz~|zxwvvwxz}~}}}}|zxuspnkhfb_[XTQOLKKKLOTY`fjlmou~}zz|}zwuvwwvsommnmjfddfilosvz}~zwvwwsnjgffghiijhgfinpomnoqu{~}~|yvsqomlkkklnqty}~~}|{zxwutrqonlkihfedcba_][YY]emqrtvx{}~}{z{~}|zxvqlheeec_[Z[^bflqtx|~|zyxwwvuspnmnolfbaba^[Z[[\\\]]^^_`aaaacfmu~~|{{zzyvsqruwwwx|}{zxwutrqqqrsuvuttwz{zxxyyyxwusqommlmnooomkkloqru|~|zxwutsrsstttroljjklnsx{|}~~~xtstvvsolkihgeb`^\ZYXWWWWXZ[]`cgkrz{zzzwsonmllkjhggfghjlpswz{||}~}}}|{|~|{ywvtrpmkigfffghjlmlkkkmoponrz}}}}~~~~}|{zzzz{|~~}|||||{{yvsommmmiebaaaaa`^]\[ZYXXZajquwxxxxy{~{xurpmjgeb`^\[[\^afkptwxz|zwtromkjihhfd`]\^``^]\\\]`deda_^afmv~}}||||}}}{zy||zywvtqmiffhjjigffegkorqoopqstttttttttsrpommosz}{yxvutrqonlkihgecaaemu{~~zxyyuqmkjjihgfedcba^\YXZ]^]ZXWXY]clw}wspprsqmkjjkkkllmnpqsuwy{|}~~}}||{zyyyyxwttv|}|{{zzyxwvtrqonlkkjjjklnopqrqpos{}}zwvutsrpnligdb`^\[ZYYXXXXWUSRUY\\[[_gqx|~}zyxwvusqomjhgeddddfgjmpty}~{ywvtrpnljhgfedca^[Z\`a`__abcdefiov~~}|{{{||||{}|yvspmjhec`^\[ZZ[]_`aaacgigeeiortuvxz|~~~~}{{|{wtpnnmmmmmmllkkkkjjjmu~~||}}|yurqrqniecbba``_^^^^^______^_bgox}{ywvutsromlnpokfehmruuuvxz}~~}|{zywvutsqpnkkmtz||zxwwvvutrnkiiknnmllnpsvxz{{}{yz{zxwvwy{{zz}|zzzzvqliijjjihfca_\ZXVTRQPPPPQSUWY\^`bbcekv}yvuuuvuutrolihilmmkjknqtx{~~|zxvtsqnjea__aba___`aacglnlihhkpw~}|||}~{vsqomljhfca^\ZXWWWXYZZZYY]bfhiknswz}~|zxusqonmmlllkifddfikkjjjlpy~|{zyyyyyvqjeehjhc^\\]`cegiijihfedehmsy~~ulfehjqy|zpkhgegjmnprttstssw~~wofa`cjrxyuohefhmt{|tld^\\_djouz~}{zz~~yvsssw|}}{wrmknv~xvwz~zwuttrolifc]SICDKV`d`WOMUbmph]TRZgv{pihjnty}~|yuqonkjjjlmortuvx|~xuux~ww|~tjb\YXZZZ\^acfikmlid`\[Z\clw|xuqonry|vrqswz}~z{}|{yvrmhda^YRJDAADJQZahmsy|{vqljjms|~{rliimquxxwtojfb^\[[[]_bdhkns{{yxy{}~{xtnha][[[YWWY\_cefeefjpx{wvw|yurpmifdeinsw{~}{ywusrqppoooonmmllllkjhea\XTRQRTY`gmrvz}}{xtoljjlosx}|xspmlkklmoqsuwy{{{zyy}}zxwx{~~xrnjd\UOLJHGFFEEEEFHIKMOPPOOQTX\`elu|yvuvx{}}|{{}~{xurpoopqsvxz|~{uqmifdb_^]]]^`cfilpruwxyz~{yyz{}|yvussstuwy|}zvrmhd_[WSOLIFDCBBCEHLQV[_acfmu|xutsrokea^\ZZZZ[\^`ceilpswz}|xvtsplgc`__`behkmoppprvyzywuutssstvz~}zxvtsssuwz}|xtokgc`^[ZXWVVUUUUUUUTTTW]elopqqqpqruxyxvttvz|vqlhgfd`]YWXY\`dinrux{}zyz{|zvqliiklkhfdccccccccdddeeeeeffilry}{zyxwuspmlnprqppqrrrstvyzzxusqpponmlkjihhhiijihfefhkkigffghhiijjjiigecaaceggfdccfjpx|}}yvspnljgda]ZWSPNKJIHIJKNQTVYZZ[]dnx~yvuusojfcba`__^^^^^___^]]_cgkmmnoqtwy{|}~}{{zzyyyxvuuwy|}{yxz{{zxy}}zxvutttuvxyyxvutvxxwtrrrrrpnjgdddca]YVTSRRRQQPPONNMLKJKOV]cegghijjjkmpv}~{xvtrqppqrsstuvy|~|zxwvutroljjkjfa^^adffeedddddddfinsy}~~}||{zzxurooprsqonoprsuvvvvvvuutsqnjfcbcdedbaabccccfimonmmnprtvxyz{|}~~~~~}|{{}}|{yxwy~~ytonmlhd_\[[[[ZYWSPLJJLMMLJIIJLOQTVY\`ejpv|~zxvwyywsponnoopppqrrsuvwy{}~|{zyxwvvvwwy{}}{|~~}{z{|~}zxvvxzzywvuvxz|~{yyyxtokhfdca`^\ZXVTSRRQQQPNLJIILOQRRQQSW^gqy}~}{xvtrppoooprux{}zwtqmjhecb`^[WSPPQSSQPOPRUWYZ\]^`cgkpv|~||}~}{xvuvy}}zxx{|zwtromkjiiijlnopomjhhlpstsrrsvz~~|{zzxwvtrqonmkihffhmty|}~}zyxxxwurnjggfd`\WUSRQQPOONNNNOPQSUVXYZ[\^bis~}zyxxwvutssstvy{~}{ywx{|yvuuvvusponoprtuwyz{||||{zz{|zxxyz|||zyz|~}{yxxyz{||||||}}}~}xutsroljiiihhgfdca_^\[ZYXXXXYZ[]_behkmoqtx}|ywvvvtqmkiijkmortwz|}|{ywuqmhda```_\ZXWWXXXWWY\_`_][ZZ[]_chnswz{{{z{{}}{ywtromjhfeddddeeecbbdglnpqsw{}|zywvutsqolifdcdefedba`aabejqy}}|{zywtqmigfffeb^ZXXY[]_acdeffffeddcdfjou{}ywwxxxvttuwz}~}{zyxxwwvtrpmjhhknppnljhgfeddcddfgiknopqpnmmorux{~}{zxwwvwxxyxwtqopswzzxvuvx|~ysnjhhiiifc`^\\\\]^_`abcefghijjjijlqz}xtqonlkihfeddddefgghhgghkptxyzywvvx{~}}}}}}{zxvtqnlifdb`^]]]^_acdeda^\]adeda`bfmu{~zuqlihgfc`\YWVVXY[]^_^^`cgjlmlmnoqsuw{~|{ywtromkhfecba````abdfhjllllou}~|{zywurnlklmonmkjiiiiknpqpnkjjmqw|~}}}}{ywttuvwusqppqrsstsssrqqpoooonmljheba`aa`]ZXY\aegggghhijkklmnopqrrrrrrsuy~~|{zyyyzzzzxwwy|~ytrpomid`][[ZZZ[[\\]]\\[[\_cfhhgfffhlqv{~zyxvspnmmnnooooonnnnmmmmmnnnoprtvxz{{{yxxyyxwwwxxwvutsssssrrqponmmmmmnoqrtttrpnmpty~}yvtqomjgd`]ZWTRONMLLLMNPQRSUX[_behknpppqtx~|ywxwvtqomlkkklllmmnopqrstvxy{|}}~}zxwvvvvvvvutrolklnoonlkkklmmnnpsw|}|zyyyyyyxwutsrsuwyxvsqqrsuwxxxwutrpnljhfdca_]\[ZYXXWVVX[afiihghlpsuvvvvwxz|~{wtsrrstvxy{{{zz{~~zvqmheccdc`\XUTSSSSSSTTUVWXYZZ[\]_bejpw||{{|}{xsomnpsvxxwvuuuvwxyyzzzzzzz{|||{yvttuwxwusrrtvwyyywusqqrtuvutrrrstuwxyz{{}|z{~{tojhgffedb`^[YWTRPONMMNOQSWZ]_abbcfkqvz~}yvuuuuvvutromklmprssrrrsuxz}~}|{{zyxvtqonnpsuvuqnkkmqssqomllnquy~~~~~|{zyyyzz{{zxwwy|}zxvtsstuvvspmjgfeddccba`_^]]\ZYWWY]djnpqrtvxy{|~|zxwutsqpnlklosvxwvtux}}zxvtsrrqolhc^ZYYYXXVTSRRSUWY\]_`````acfjouy}~~~~|ywwwxz|~|ywvwxxxvtrqqrrsrqpoprttrpnmmnopqrrssttuvwxxyyyxwwwx{~}|{zzyxwutuwz|~|zyyxvsoligeb_\ZYYZYXVSQPPQSVY\_behjkllmpv}|wtqpoonnmmmllmmnpqsuwz|}~~}||||}~~|zwusqonlkjihhijlnpstuutssssqonptz~~{wtrqpponmmlkjigeb_\YXYZ[[[YXWWXY[_emtz~}zwtqnljhffeefhjmpsvxz{~}zwtromkigeb_[WUUWXXWWWXZ]_abba``adinsw{~~~}{ywusqomllkllmmmljheccehkmkifefilpsvy{}~~}|{zyz{~~}{zxwvusrpnmnqv{{wutsplgc_]\[ZYYYXYYZ[\^_```__`bfkpty|}yvwyyxtokhghjknpqstuvvwxxxxxxxxxxxxwvutstw{~{xuuuuvuspmkjijjlmnpqsuwyz{{{yvtsux}}|}}|yvsqonlkjhfda_][YWVUTSTTUWY[^`bbbbcglsz~}{yyyywuqnkigggghjkmortwz|~~~~|yxwutqmjgfeeeedcb`abdffdba```abcehlqv{~}}}|{zxvtqoljgecb``____^\[[\_cfhjjklnoqsvy|~~}||}|yxwxxwuspnmmmnnnorw|~zuqnlkifca`_``aabbbbaa`__]\\\]_bgnu}zvsrrsssqonnnnmmnoqtwyyxwvuuvvwxxxxxwvuspnnoruwvusqpooooooppqqqqpoopsuxzzzyyyz|~}{xtpmjihgeb`]\[ZYYYYYXXXXYYZ[\]^___^^_ciqx}zxvuttsrrqqpomkihhilosvxyz{||}~~|yvtrpommllllkjihfeefgijjhea__adiov|}zwsqonmkiea]ZXWXXZ[]_``___`cgjllmmnprtvxz|~|zwutrqpppppqqrrqonmnrx~|{zywuspmkkklkhd`]]^adfggedba``abdgkpv{}zwutuvutronmnoprstvwxyz{{{zxvtqpopqqqokhdbbdimqssrrqqqqqqrrrsstuuvvwwxy{~~}}}}}}|zzz|~~|wsomkjhd`]ZXXXYZZYXVTSSTVY\]^^^^_`bceghjmqx~{yxwwvvuttsrqpooooopqsvx|}|~|zyyzzz{zyxwusqomkjihhiijllmljihhjloppqrtw{|ywusqoljheca_]\ZYXWUTSSTX\`bddcdfkosuvwxz}}{zyxxxxwvvutsrqpooooopqrrsstv{}{zyxwwvvvuutrokhedefgfecbbcdfhijkkkllmpsw}}|{|||zwuttuvutttuvxz{||||zyxvtrpnlkihgfffeedb`_`aehkmoqrrrrrstvxy{|}}~~~{yxxxyyz{|||{ywwxz}~~|yvsoljiihfc_[XUTTTTTUUVVWYZ\^`acdeeeefgkou|}zxwwwwvutssstuvvwwxxxxxyyz{|||{zxvuvxz{{yxwwvvutsrqrsttrpnlkkkmoqstuuuspnmnqv{~}xusrqppnmjheb_\ZWUSRQQRSVX\_bdfgikmprrrrty~}|{ywusqppqrrqomlkkkmoqtvxyyzz||yvspopqsttsroljiijmpqqomkjihijkmptwz}~~}~~~~}|{{{}|xvtuwy{|{ywusrqponmlkihfdca`^]ZYWWZ^chkmoqruwy{{zyxwwx{|xurpooqsuwwvtrqqtx}}ytoiebbcdda\XUSSTVX[]_abcccbbaaabcfjoty}~~~|zxvuuvxz{{{zywvvvwxxxwutsstuvwxxvusstuvuspnllllmnnopqrstuuvvutsqppqsvxz{{{}}zyz}|wqlifeeeeda^[XUTSSSUVXZ]`begiiihhikpu{zvsqonnmmmmmmmnopqrssssrstvy{}}|||~~|ywtrpnlkkjklnprtuutrpnnnpqrqponoquy~~}}}~}|ywtsssttuttrpmjgdbaa``_][YXWWWXYZ\_djpuxyyzzz{{|}~~{xuspnlkjjklnqtvxz{{{|}~{xvtqolifb^\Z[\]]]\[ZZZZ[\^acegggffhjnsy~~~|{zzz{{{zzyxwvuttssssrrqonmmlllkkkkkkjhgghjmpruwxzz{{zzywvuttuwz}{zxvtsstx|~{ywurniea^]\[[[[\\\[[ZYXWXY\`cfhijklmpsy~{wsolkklmoprsstttttttttttuuvwxyz{{{{}}zwtrpoonnnnoopqrsuvxxxwurpppruwy{~}||~~|yvsqpomljigdb`^\ZYXXXXXYZZ[ZZYY[^cipvyzyxxy{~{wtrponnnnnnoprtvwyyzyyz||xtqnmlllkjigdb``abcb`^\[\\]^_acehlquz~~~}{zz{|~~~}{yvtqomjigffffeedcbbbdgjkkjhggimrvy{|{zyxwwwx{~}zwutttttssstvz~ytqpponkhecbaa```__^]]\[ZZZZZ[]`chmsx|}{{||{yuqmkjkmpsuvvvutssttvwxxyzzzzyxvtrqrtwz{{zxwwwwwwwvuspnmlmoqsuuuuuvwxxxxxxy||zzz{zxuqmjgedcccbba`_^][ZYXWWWXYZ[[\\]^aflrw{~~~~}{yvspmkjjlmopqqrsuwy{|}~~|zxwusqoljhghhiihfecccdcba___`cfinrw{~|{zzzzzyvrnjfdba``abcdeedca`_acfhjjjjknpsvy|}~|{zyxwvtsqonnopqsuy}~|yvrolkigecbaaaaa````aabbbbbbbcehmrw}}zwvuuuspnlkklnqtvxz|||{zxvutuvwwwvtroljhhjmquxyxwutsrrqqqppppppqrtuwxy{||}}~}xurqqpomjgdba_^\ZYXXXZ[[[ZYXWXY[]`cefghhhkou|~{yxwvutrqponmmmnoqsux{}~}~~}}~~|zwtroljhgfffghijjjiiijkkjhfdcejpw}|ywvutsrqpnlkhfc`\YWVWY\_abba_^_aejoruvvwxy{}~}{ywusqpnnmmnnopqrsuy}~}|{zywurnjgeddeeecbbbceghhhgecbcdgkosvz}zvsrrtuvvuttuuwxyzzzzyxwvtsrqpoooooonlifca`bglpstsqonnoprtvxyz{{zzzyyyz|~|zyxz|}~}zvtrqpnlhc_\YXWWWWWXYY[\]^_```_^^^_adgjmosx|yxxxxz}{{|ytuvtooonkmqstuvuqqstuz}ume^\\_gnt{vojc\Z[\]_acfinsx~x}{~~zz}~|rljkouz}~{wqlfa][\[\^aeimnmh`YUXbpyzqgcjyzxunkms|{pf_\^cjsz~{wrnmmotwz|}}}~zqnr{yngipy}}vmd^]_dhlnppponnnnmmmlllmmnprv}~|yvtrrtw||skilu{rkhjou{|zvspmkigeddehlqvz{{xtplhffhlrz{vttw}zutv{xpkjmrx~|ywwwy{}~xphc_]^_`abcdeeeeeca_][YWUSRPONNNQSX]dlt}{vrnkihhjmpty~}xrnkjjkloqstuvwy{}~~|xvsrqponmljihggilosvz}~~{vpjebacgnw{wvx{~zqjecdgkpstsplga[VQNLKJLMPSUX[\]]\\\^`cfilosy|xsokigfeeefghjmortvxyz{{{zywtqmkikou~~}||{|{{zxuqmhc`_`befedbaacglrx~}{zyxwuspmkklmnoonmmnprtvxyyzyyyyzyxuqmhd`^[ZXWUTTTWZ_cfhijjjklmoprtw{~|{{{{||||zxuqnmmpty|~|yuplgca_^[XTOKIIKPV]cimooonlkjiijlorvz}~{yyyyxvtqnlkkklmnpqstvxz|~~}|{zywsniebabdfhikloqtwz|~~}|{{{{|}~~~~||}}{x}{wtspoqrqppljf_XSOKKMRZckrutnf_\^fr~rkr~~qfchs|}yuqonnoqty~~wrprx~tnnszxogb`aekqw}~{yxz}}yursy{x~}}~{wuw|yrnosyzupkgc`_][YXWWWXZ\_beffaZTOPVcq|{riglw{urruz~xrlhedefijklnpsx|}||~~uqsy~vmfbabdgjlnoonlifddefilortuvusqnlkjlnrx~zusuy}sljlr{{uoieb_^]]^__`````abeilprtvxyzywsokhghjnrw|~yvwz~wqliilqw}~{z{}{vpjc^YVUUUVWXYZ[[[\[[\\]]^]]\[Z[\`gp|}{yxvtplifdcdfimquy}}|~|xtqmifcbbejpw}|vronnopqrtuwz|~~|{zz|~}}|{zyxwvvvvwxz{|}}}|zyy{~|tnieb`_^_`abcdddccbbbccdfghiklosx|{vqmjgecbbbdglrx}~|{{{{|||||{zywtqoljiikmpsttspnmlmooonlifdba```abcehjmprtvxz{}}{{{{zxurnjfca`_`abdfiklmnmlkjjkmprsttuvxz}}zwtrqqrux|~}|{ywusqpoprtwy{||}~}||||}~~|yusqqsuwwvsnjea]ZXWVVVWXYZ[\^`adhlpv{~}{ywwwxz{}}}|zwtqommmmnnopqrtvwyz{|}~zwsqvy|~~wrokedefnrtv|xx}{vsuw~|z{{{yxkioyztmgb_^^`adfghhhfb]WRNLMS\fottpia]_frznmutgbemw}{xtnjhgghjkmopqstw{~zvvz~zpf^ZY]ckrwzzxsnhd`^[YXWWX[]`ekrz}}{wsrsv|ywvx|}xsnifc`^\[[[Z[]^_^]ZWTQOOSYbnxyqjfiq{|y{|zyz{~{unhcaacfjouz}yxy|yne_]`flrvwtoic^ZWVTSSSSTUVVVVWY]ckt~}{xwvvx|~xqkgfhnt|~xrligghjknopppoooqsvz{|yuqmkhgffghjmpswz~}{ywwwxy|}|{|~~vpmnqw~|wrmhc]YVTTVY]adgjlmmlkifdbaabdgiklllmosy|wqmigffghjlnprtvxz{}~~}{xwuuvwxwvtqonnpqrsrqnligeddegknrvz}~{voha\YXXZ\_ciow~ytqnljiiiijjkkkjjigfedcba_^[XVUUX\`deedcbbehlry~{xxxyzzywurpmlllmoqtwz|~}zwvuvwy{}~~}zxtronmljhd`\YVUTUVWY[^aeimrvzztpmllnpppomkjijkmoqsvwyzzzzzzyxwvutrpmjgedefilmnmkigffhjlopqqomljjkmpu{~~~}zxwwxy{~~wpkg_XWWQMNOMKMPOPSWXY]behnswz||{xwvvx}zqgju{vi^\dpz{uojfefhlpv|vmgcdipwzzuojhkr{wojikov|}xurstx~ztsy~{zyxxxz~~uonosx}}xrkd^XTQOONNNNOPRUX^dkrxzwpg^Z]gt{qhfmz|xsnlloty~|xtqnmmmnpqqux}{wvvy}zpjioxxnheglsy~ysnkihijmptx|{snlnu~{qhddiov{~|xrmha\WSQPQRSUWXXXYZ\`dioswyz{{zxwtqpnopsw||tolmqxyrooqv|}zxvvvy{}}yupjc\UNIEDEGKOSVY[]^`bccdefhikmprtvxy|{wtqomllkllnoqsux{~~{zz{|||zyxwwxyzzzyxuqmiecbbejpw}{yxz}~}~~~|{zzz{}~|{{||||{yxwwwxy{|~zslf_ZUQONNORVY\_abcefiloruwyz|~|zwtplhda_^^`dhnu{|zxwutrpnlifdcbbcdgjmqtwy{||}~}zupkgfgjmquxyzzyyxwxxz|}{yyz|~}xtqnkjhfecb`_][ZXWVUVXZ]`cdefgilquy|~~}|{{{{|~}zxurpnmlmnpsvx{}~|zzyzz{{{zywtrpoopqqpomkigfedca^[XTQONORVZ_djosw{}{zyyxyxxvuspmjgeba``acfimqtvwwwvvwy|~}|zyxxxxyyyxxwuttsrrtvx|~|zzz{}~~|{yyzz{{zxtpkgc_\YWUSQONMMMMNOPQRSTUWY[]`cfkpu{~}||{{zyyxwwwwxxz{|~~{xvtrrsuxz}~}}}~|zyy{}~|{zyxxxxyyzz{{{zzyyxxyyzzzxvspmkjiiiihgec`\YUROLKJIJLNQTWY\^adhlquy|~~|ywuuvwz|}xtpnlkkkjheb^ZWUTUWY\]]\[ZYXXWWWWWWWYZ]`dhlqux{}~~~~~|{{|}~~}|{zyyyxxxxwvusqqqrtwyz{{{{{|}}~~}yurpoopppomkiggilpuz}~}}}}~~|yuqnlkkkkjgc_ZVTRRSTUUVVVVUTSRQQQRTWZ^chmrx~~~~~~}}}|||}~~}||||}~~}|{zywusqoljhgghikmnnmkigghkotx|}|{{|}~|||~~}||||}~~~~}}}~}yvsqpqrsssrpmjgdb`___`abdeghhiiikmquz~~|{zxwusqomkjjjkmpsuwwwvvvxz|~}zvsoligfeeeffgfec`][YY[]_aaa_\ZYYZ\_cglqux{~~{xusrqppponmllkjjiiijlosvxzzzyxxxyz{|}}~}yvspmkjjjklmnonmmlmosx}~|{zyxvsqnljjjlmnmljgdbaabdgikllkigeccceimrx~~|zyyyyz{{{{{{{zyxwusrpnmlkkjjjjklmoppppooopqstuuutsqomkihggghjloqsuvwxz|}||~~}{xuromlkllmmlkigda^]\\]_abcddcba`acgmt|~zwvuuuvvvutsqnljiggghjloruwxyyyyz{|}~~~~~~~}|zxwutrqpnmllkkkjjigeb`_^_acefgggghjmquz~|zxvsqnkihfeeddccb`_]\\]^adhjkjigfefhknrvz~~{ywusrponnmmmllkjiijlpu{~}{yvtsrrrrpnkhfedefghijjjihggghjnsy}yvuuvxyyxuromlmnqsvxyzzzxvtqomjihgfffeedddegilnooonmnprtvwwwutrpnmlllmoqtw{~{xutttsqomkihgggffedcb`_]\\[[\]^__`__^^_chpy|xurrrsuvvutrpnmlklmoqtvxz|||{yxvvwyz|}}||{{{|}}}|zywvuuuuuuttsrrrqqpnmlkklnprstuvx{~{wspnlkjjjihfeb`^\[ZYYYZ[\]]^^_adgknpppnmmnqv{}zxvuttttttssssssttuwz~|zxvuttttsrqomlkkkkkjhgggghikllmnoprtvy}~|ywuttuwy{{{ywtrqqrtvxz{zyvspligfededfkkllllmpruy{}}}wvvwqnqqpnmkkigfiklouy|{xurrqqqonnnlllmpw~vry}~{yvsqooprtusoi`VPMOT]ekmjcZSQSZbjookd^ZY\cmxxz~tlgdcdegikkkjhfeefipx~vnihlt}umhfinu|}ytoligfeddccbabegjpv}|vtxywwz~}zz}xtrqsvxz{{ywurlheb^[XUTSTW[`ege`YSQU]gqvuoga^`flu|wpljjlnrux{|}~~~~}~~}yy}ytnjgecba`_^]]\\\[ZYZ[^bgmty}|zzz}zvtuy~zsnkjjloswz||yvrokifda^[XUSQPQU[bjry~}zvspmlllnquz~zusrtwz}~}|||}~}{zyz{}~~~~|{ywvutrpmkhfeddcddefghikllllkigfeefimsz|zyyz}|yvsqnkihfffghiklnnnmkigeddegiklmnoqtwz}~{wsokhfffhknquy||{yyyyz{{|}~~}}}~{uojfb^\YWUTRQPNMLLLMNPRUWY\^`bfjpv}}{xuqmjfdbbbcehknqtwz}}ytqmjhgfefffgggffffghiklmnoprux|}zvsolheb`^\\[[[[[[[[\^aeinrvy|~|yvrnkigeeefghijklmnoqsvy|~}{yvtqnkifdbaabcegijkkkjjjjiiiiihhiijkmoqtwz}~}{xvtromljihgfedcbbbbdeghijjiiiiijkmnoppponmlkkklnpruy{~~{ywvutsrqqponnnoqux}}{xurpnmlkkkjiihgfedddddfgikmprux{~~{xvuutsrpnljihgggghhiiiiihhgggghiklmnoopqrsuwxyyyxxwvvuuutsrpnljhfddcddefggggffefgilosvz}~}{ywurolifca_\ZYWVUTTTSSSTUVY\_cfilnqsvy|~{xvtsqponmljigedcbbcdfhjmnpqrsuwz}|zxusqpooopponmlkjjjkmoqsuxz}~|zxvutssssssrrqppoonnmllkjjihgfdcbba```__^^^^__`abbbbbcdefgiklmoppqrstuwz}~~~}{ywtroljhggfffedcba`_^^^_`bdfhknqsvy}~{ywvusrpomkjhhgghhijjkkllkkkkjjkklmnopppqqrsuwxyz{||}}}}~~}}}|{{zzzyzz{{|}}}}}}}}~~|{yxvtromkigfdb`_][YWUTSRQQQPPPQQRRSTVX[]aehlptw{~}|{zyyxwvuttssstuvwy{}~}|zywvtsqponnnnmmlkkjjkmoqtwz}~~}|{{zzz{{{{zzxwvutsrqponmlkjhgfdcba`_^^^^^^^^_`abcdfghikmortvy{~}|{yxxwwwwwwxy{}}zwtqnligeca^\[YXXWWWWVVVUVVWXZ\_cfjnrvy|~}{zyxwvutsrrrrrstuvwxxyyyyyyyyyyyyyzzzzzyyyyzz{{||||||||{{{{zzzzzzzzzz{{|}~~~~~~~~~}{yxvusrpmkigdba_^\[ZYXWVUUUUUUVWWXYZ[]_beimrv{~}|{zyxwwvvvvwwxyz{|}~}|zxwvtsrqpoonmlkjiiiiiiiijkmoqtwy|~~~~~~}}{zywvuttsrrqponmkjihggffffgghhiijklnoprstuvxz|~~}|{zyxwvvuuuvvwwz}|{{|}~~|zxusrpppppppnlkjjjheb^\[\^adgjlnooomljiijlotz~~}}}~|zz|~{xwxz}{wuvwtojgfgjnrvxyxuqlgdbbdhlqv{~~}{z{ztpnoquz{xvuuvwxz|~|vrrttqnnpruvvtrnkhedccdfhijkllllllmnprtvx|~{ywxz}yuv|~|ywtqomlkkkklllkjheb_]\\]`dhkklpwzwuuvwxxy{~~~}xtstw|~zwwx|~|zyyz{|}}}|yvrnjgeddfhkorsqmmqtokklnptx|~}zwuuuvwxyz{{yx}~~}}||}~~{ywuttsrqomkihhikmligjoz|tnmmortrw{z}~ysont~}zz{~~ytprw~xqs|whbhrwvoihkptwyyywmhdelvzxz|}{upmnt}vpot|~xw|{{}~ojq}unjgefhihhhgeb_^bisz}uollmnmkikrz~tjefkqw|}xrnlmouxrqwxx~wv}|tpopsvxz{||vpkjlmkksypg`]]`cgijiiknpstutnhdccdgkpw~~rhlzxuw~~|}{ywustvy||tkaXW\gt}{vk\W^ku|~|~wokiihhjqy|yuttuvvy{~||z}~}{zz|{z~thepyqjfegijjkjjnrttu||vtw{vqpollnpppprvz}~}{snryzz}~|{zuvy~wy{{{vqnlkhd_`fffkmny~~}~}~~xrqz}|{|zytpqtwxxxxz}~}wohdbcdddc`[[aebckjjnokehmihlqux|yv|wpopsvyz{|}yrz~|yupjffgeeegikjt~}~~wsstsrqomjjklhlustuvwwrqt{~zxyzzv{rgkolmpppssqrtwyzyxxy{xu|}|~ztqyzuttqpoqssrnheptnquz~}zz{zxz}zwttuvrv|vsu~}tsvy|}~}|{{|~wyz{~|tsyvtuusrtxyy{wy}}|~~zwtrsttrnlijpnnpot{xuuutsssrrqppqstpkpljnqrqonkfckpjedfkqw|}|||{{||}{wpsqhgffimomifdksolkkknrxxuzzuwyyxvutuuvvvvwxutz|{|y}}}|{|}~~~~}||}~{vrprvz}}x}{~}{z}{}|{zz{|~}~~}}||zywutssuwvqstoptvuplprnoqrpmjhgillieloigghilmmlkkjgkqnhfgjoqouvqrsssqppsvurpoosx}}}~~}|~~xy}z{}~|{xuuvvwwwvuutsrqqqsuuv{{}~}~|zz|{wvy||}{y|y||{{|}|zwtrrtvvruvqqssqqqpnrtpopqqrstuutrx}yxyyyz{{{|}~~~~|yywvxyyuoqvsqrrpnkkkkhkpmmonkgddeilnnkgdfnqmkkinvsrrssttsrqqrssrpnmnrwxrv~z~}}~}}}{|{~~~~~{zz{yx}~zz||}|{z||{zyxvsry{vuuuuutsrpnkospmmnnoomjnqmnpqqoliikmnoonopoqy|vrx}z{~||}~xw}ysssuwwuqnkklnoonligjrpkppnqtutsrqqqrrrrrqrstuust|{tw}}}|zyzz{{{{|}}{x|~{~{z~~}}|~}}}~}~||}||||}|xtxxuvxywusrqqrttqkkqsqrsrotuolmnpssrnkhgghjkkklmmjksunorttuvwxxwvtssux{~}z~~{wutvy{}~|vr{vrsvy|~}{xussssssssuvsqvxwy{yx}}zz~{yxz}}z~ywy|zux~}zxxxz}~z{}yxyyyxxxxxwvutuuttz|xw}~{z{}}|{{|||}~|{}~|yvsrrsttqmnttnnvvsttrpnmorssplhffimprronqnnrqoqtuuuttssrqppqsvvtsyyuwz}|yy}{{|~}{|~{|yz}~}z|}{{|}|{}}|zxy||wv||{||zy~zyxyzzywusrrrqqrrsqoqtvvstvwvtsrsuvvtronnpsuwvv|zuv~{z|yxzwxz{|{yvsrrrsrpmjijmmmrwtqonsvrpprtuvutsstuuusqvzvsstwz}}yuv{{y{}}|||}|z~|{{}}z|~~~|{~}||}~yw}~yw|~~}zxwxyyxusrrsuvwtprwxtqruxywtqoopqsrpnmmmkioussuuqpuyvtttttuwz|}|{xv|~~||||~~}zvrv|zwxyywvz|wqpqsuvwwvvvspsxwuwxxvuuwz|{wuy{yz}}y{~{{}|yvwy{~~~|zx|}z~}{~}||{zxwwxz{|{zx|~y}~~{{~~~|xwwxyxwvuvusrxzuruzxtsuxxxuronortvvsnjnqnmoruwwspruspoprtvwxxuruxuuwyzyxxxyz{{yvw~}}|~}~~yxz}~}yxy{~}zwvwz{zwy~}|{|~~~~|xvvy|xuyxz}{ywwwxxwwxxuswzxxxvuy{ywyzzxussuxz||ywz|xttx|}yz~~~}{{yzzy{{vuvwwwvtttvvtsvwropqrtuwxxuqquurqsvxywv{~ytstwz}}|yvuuwz{{yw{~z}|zy{~~{z||z}~}}~}|{{|~|{||}}zxxy{|{xvz}xttvz|{xyzxttvxxwvuvvutx|yutsrrsuxz{zvrpu{|{{{zy|{|}~{xzzwvuvwxxxwwwvusuzzwuutv|}||{yxxy{||{ywvwy|~zvz}|}~~~|{{|}}|~~~||}|}}}}|}|{|~~{y{{{~}||~}|{yw{}{{zyyy{|{xxzyutwz|{yusw|zwwwxxwvuttuvxxwtsw|{vsw{{{}~}{zz{||{zyz|~|~||}}{{{|}||{{|{{~~xz~|z{}}zxwyz|||{{{|{z}~}}~~zx}}zz|~{y{}||}|{z{|}~{wvy|||~~{y}~{{}|ywxz}~{{~~~~}||}~|zzywx|~|zz{ywz}{yyz{{zyxwvtsstvy{{yuux|}yuuw{|xvvwy{||||{zy}{|~}}~zz}~|}~zwwy{~}yy}}~}{z{}}{~{{}}}~zy{~{xy||yxxz{yxz~~|yxz~{yzz{{|}}{yusrsw{~ztv~yxz~~}}}~~}||{{~zz~zyz||xuttuvxy{|{xtw|}zxxyz}{wuvx{~}zy}~{{|}~}~}|}}~}}}|}~|}~}yvvx{||~~~~~~~~}|}|{~~~~~}||}~|zwwy{}|{||{}|zyz|}~~|zyxwvvuvxz{zy|}xw{}zyz|}{z{|~~|{~|~|~~~~|}~}zwwxz|}|z{|vv|}|{{|}~}{{{{yz~}|}~~~|{}~~}}}|~|{~~~}zyy{}zw}}{|~}|}}~}|zyyz{z~{y|~~{}~}~|~|{}{{{|||||}~}yv{}{{|}}}}~}|{yxxyzzywuu{|xvw|zxwx{~}{yw{~}}}~~}~~}||~{{}}~||}~~}|zxvy}z{}{zz|}yy||}|y|~}}~}zyz{}}}}}}|zz}}|~~||}~|zyy{}}yut{}}}~}}}yxz}}{~}|~~|zz|~{{~y{~{zyz{}~~~}zvsw||{|~~|zy|~xuux|}z|~zy{~~}|z{~~||}}~|~}||}}{}{~}{z{~zvx}}zz}}|}{z{}~}}~}zz~}{z{~{{}}}yxy{{y}|xw|}|zzz{|}~}zwx{zxy{}~}{|{wvy}~{yz}~}~~~~}{{~~{~}~~~}|~||}{zz}|y{~{wvx||ww{~{z{|~~~}zz{|~}{{{|}|~~}{{{}|}~~}{z|~}{~~|||}}{{|}~~~|~~zxy{}{y{~}|}~~~|{|}}}|zz}~~~~~|{}~}}}|{}~{zyy{}}z|zvuvz~~zvuz~{{{zz~}|{|}~{z{~~~~|}}|~}{|~~}}|yxyz}~yuv|~{yz~}}~~}|||}~||~|{|~}{~|{z~}}~~~}~zyz}|z~~zxxz|~}{yy}}yx|~~}||~|z~}}z{}{|~{{~}|}~~|z{~|zyxy{}}zwvy}xv{}{z{|}~{y~|}||~|}|z|~~}~~||}z|~}|}}~}}|||}~~}{|{y}~}{{{}}{~~~|z{~}~|{||y|~||}|yw{~~~{z|~~~}|{{{||||||}}|}}|~}{|}{zz|~}~}z{}|}~|~~~}yx{~{z~~|}}zxwy{~~|zz}~~{{|}zxy{~}|}}~}}~|{vwwspqsv~zroqv~umnwxmnv~}xqmpxxrorz~tprw{|xvxunoyvkmzztv~~rlkmpuy|}{wrmkknqtvy|zwwwx{~xutw{~}zvspprspmkmptwyz~ytsy~tnq{{rquvwzzpjlu{{|{snnry~{{wrnmnt||uooqqrvtlmxwrqty~wrprw}|xvtqpt|uompxztqrxyrooqsvwwsolmqrqqqrssv}wqnnqvz~~zwvvvxwvrnjhilquxxww{~xtstyzrnptwwy}}|}xpjilryytpoty{yxxyz}~{zyyuqopu}}vqoorx}}||}~ztomoruz~zy~~{xtqpqty~ztpoprtvy{|{yvtrrtuvurqtwuolow}}wtvxxtqpqrsssuxzwsopsuwvtposy{xvvzzuuxxvrpqx|xtrqqqqrtvxz}~zxxxxuqprw|~}xtrpqruvvtqnllpvzxrmntz{z{~|z~{vtttvwwwvusqqrsttsrrw~}{xtpmntz||}}|{zxwtsrrrrqpqv{|xwzzz~}xsqsuwwvvwy}~{}|wtrsvz}xuw}~}~~|{zzzzzyyz{|}}~|zzzyxwurppsuvxzzz{}|xx~~{ywvvy|ztollorvy{{xutw{{yyz|~~yx{}zutw{zurrsuvvutsux|}zz{~~|}{xx{~}yuroptxwuuvwxyzyxwutux{~{vsw~{tppsx|}}yupnmnprtvvussw{{vsuz}||||{zwuuvy|}{vplnrtrqruxzzxx{~~}~|{{|}}|zvqoqssqqruwyxwuuwxvsrsvyzywwz|ztonpsvxxvsqqqqpqtxzvrpsxywutvxy{{zwsolmotx{zvoknv{ysppsx}~~~{xvwy{|}|xsprvyxurpprvyzzxurqqsuvusprvzxurpprw{|xspoprtvwxxvssvwtpnoquxz{ytpnptvuutsrqsy~}xtrsvz|~}|||~~ywz{xursx}~{wtrqrsuvvtrqqruvvtrrtwyxtqprtxzzywtrpooprtuvttvwuqquwvtstuvvutttuvvurpoty{xvuwy{{}}|}~~~{wsruz{yvuuuvvuuwywspqsvxxvsrv{{wronpsvxyxvtqopsx{ztomqvzyxwvuuuuutsrqqruxzyupou}{vtuy|~~~|{{|~~{vsu|}xuttvxyzyxvtsttuutqopu{|xsnnry}|xsoopswz{{xtpnquwvtsstvxxwtrstuuuwxxvssw|~|ywvwy{|||}~|z{~}{xvx}}zxwvuuuuvvuuuuuuuuwwuuvvtrrtx{}|yuqoopsuwxxvtrrtvvwwvutvwxxwuttuwyzxupnqx|}{ywwx{~zxy|~}~}xssv{|{zyxvuux{|ytpnquy||yvtvxwsppruyzzyvspopuz|{wtqquyzxvvvwxxwvtrqqsvz|{wsqt{~xtsuz|zyz}|yxz}|vsu}ytrsuy|~~|xtppqtwyxustx{ztposy}}zwtrrrtwy{{zvrqruvtsuwxyywuuvwvtrsvxzyxx{}ytstx|}||}}~|z}}|{{zy{zvsrsvxzzywussstux{zvsuxyvsstx{~~{wspoorvy{{yurruy{zwsrsvy{|{yvtttvxxvttw|~}yvuvz}zz|~}|zwuvz~~{xvuttw|}xtqqsvy{{yvvx{zupnpty}~}ytporx||yvrpqv}}yvtstuwxxwuttuxzywuuw{~~zwvwz~}|{|~~|{|}~zuty~yutuwy{}}|yvsrrtvwwvuw{}zsprw|}{xvtstwy{|{yvrpquz{xvuvwxwwxzzxustwz{{xvw||wttw{~|{|~}~}}~}~~}{xx{~zvtstvx{|{yvsrsw||uqrwzywwyz||{xtrqrtvy{|{wspry~~yustvxz{{zxvvvwxxwussyzutw~}yxy}}~~~{wttw~|xtsuz}wrpqty}|vrswywtstwz}~|xspqvz|{zxvssv{~}zxvvvvvwwwvvwxyxxxyyyz{{yvvy~~zxxz}~~}{{}~{xy{~~zvtvy}}xtqprvz||zwvxzzvtvy{{{{zxvuuwz}~}{vrprx||{yyxwvx|~ytqrv{{yz|}zvtvy~~zxx|~}}~|{{}~}~|xutvy|~|yurru|}wsuz}|ywxz}|xtqqtx|~ysqu}zurtw{zvssux{|{ywy~ytuy~|zz{~}}~}|~~|~|~}||}~~}{yz~~yutx}|xutux|}xvwzzwtux|{vtuy||{zyyxxy}~yutuwz}~~|zxwwxy||yy}~|zxz}|xwx{}}~}~}~}~~}}}}}~{yxy{~zvsrtx}}zxyz{{{{{zz{|||{{{||||{yvuvz{ywwwz}xuuwz~~{{}{wtvz}xwz}{|~~~|}~{{}~|}}xuuwz}xtsw~yttx~|{{|||{yxwxz|~{wtv|zwvx{~|ywvwz|||{{~~xvy}{{}|z|||~}}|~{{||~~~~~}}|zz|yuw|~{xwwy}{vtw|~}{yz||yyz||||}}|zyy}}ywvwz|~{xwx{|xx{~}~}zyy|}z{~}}{|||}}}~}~}~~}}~||~~|{|~|xvuwz~|xwy|~|z{}~~}{{|}}zvtv|~ywvz{vuvz{yz}~|zy{~~zxz~||~}|{|~zy}~}~|yy{~~||~}zyy{~|xuv{{xwz}|}~~|zxxz|~{xx{}ywx{~yvuw{~|{|}z{~}||~{y{||~~{|~|{~~|~~}|~{{zy|}zyz|}|~zxz~~|zyy|yvw{~~}|}~~}}~~}{zz|~~}zwvx{|yvvz{xy~~|}~zxx{zx}}z{~||~~z{~||~|}zy{~}|||~{{~}||~{wuuy~yvw}{xxz~~|{{|}}}{zz}}xvx|}ywxz~}{{}~}zy{~}zz}~}~}{{}~~}~|yy|}|}}}|~{z}|yz|}}~zxx{|xwz~~{xx||yxz}~|||}~~}}|}~|zy{~|xuvx}}{|~~~~}{z|~||~~{{~zy{}yy}|z|~~~}}|z{~}zyz|~|{}~~~}}}~~zww{|{|}~}{z{}}}{wvw{{wux}xvy~~~|{|}|yz|{|||}}z|}|}{yz}~~}yx{}zy{~yx{~}{yy{~{vv}}yxz~~}|}}}}|{z|{ww|~zxy{~ywy~~|}}|}~}}~{z{~}||}|~}zz{~~zy~~|}~{z}~yx|zxx{zy{~}||~}zz|~|yz~|zz{}~~~~~~}}~}}~~||}|ywwz~}yy|~|}~~|yx{|zz~yxz~~|~||~zy{|z|~~{z}|xwy|}||~~~}|}{vuy~|z{~|xwy}}}~{yy{zvv{~yxz~}{z{~|z|~zz|~zxy|}|~~}|~|xx}{y{~zxz~~zxx}}yy||yxy|yvyzvw{{yxz}~}}~yx{~|zz{~ywy}~~}}}yy{~{yz}}z|{xx{yx||yz}}{{}~~}xx||yy{~}zz}~}|~}|}~~~~~}}~ywx|~|{{|~|{~~|}}xvvy|wx}~zy{~~~}|||xy~|yy|}}|{|{z|}|}~zz{~~}~~|{~zwwy}{z|~|{|~~~~|zy}|yx{{xx{~}~~{xy{}xwz{xy}~{z|~~~~~~~~{{}~yxx|~~|yy}}{{~{yz~|xwzywx~|xw{}||}}|}~{z|}yxz}zyy{~~~||~~}}|xwz}}}~}||~zxx|zwy{xz}||~}yy~|zz}~|{|~|yz~|z{~~ywy}~~~}|}}}{wwz~|yy{~yx{}{zz}zwx~zy{~~~~}||{xz{zz}~yy|~}}~}|~|zy{~|{}||~zxy{~zwz~{z|~~~}zyzywy|xwz~|yz}~}}~|z|~}zz}{{{}~}~~{xx{{z}~~|yx{|yz~zxy}~|}~|{}~zxz~{yz~}|}~~~~~~}~~{y{|ywx{~||~}}~|xwz}{{~~zyz}}{z||xx|{yz~|{|~|z|{yz~{xx{~~~|}~|{|~|xwz~}}}zz||xxz|xxz{yz~~|zy{~}xx|{xx||zz|~~{z}|z{}~zy{~~~}yxz~~zyz~}z|~~zxx{}xw{|xx{}{z|~~}}}xw{|yxz}}zz}~}~~}}~~|}{xx{~|||~~}~~}~{xwy~yx|}z{}~~|{~~yxz~zxz}~}~}{{}}yy}}zz}|yy{~}}}~|z{~}ywx{}{{}~||}~{yz|yy||yy|~|yz}}yx{|yy~|{|~~~~}}~{z|}ywx|~~~{xx||zz|{yz||xwy~zww|}xwz~}}}}}}|z{zxz~|zz|~}|~~}}~ywz~}~~}|~{wx{zwx}|xx}~|{{}|xx||yy{~{z{}~~|yy}~{yz}~ywx}~~}~}}{xwz~|zy|~zy}}{z{~ywz~zy|~~~||}zxz}zxz~|xy}~~~|}~}{{~zxy||{{~}{}~~{xxy}{xx}}z{}~~{yy}|ww|ywx|~zz}}}~}z{~}zz}|{{}~|}zxwz~{z|}}~|xwz{xy}~yvx}}|}~{z|~ywy~~zxz}|}~~~}~~zy{|xwx|~|}~}~zww||{|}yx{|yz}~ywx~}yx|}{{|}zz}|xx{|xwy|~||~~|{{}}xvx}~}}~~zy{}ywy}}ywy~|yy}|zy{~~yw{|ww{|zz{~~zy||zz|~yxz~~}~}ywy}~zxy}}z{~~~~zwwz~xvz}xxz}{{|~}}~ywz}yxy}~{z|~~}}~~|}}ywy}~|||~}~~}}|xvx}zxz{z{~~~|{|{wx|{xx{||~|z{~{yz~{yz~zyy|}}~~~{z||xwx|}z{~~||~}zy{|yy|{yy|{yz}|xx{{xy~|{|~~~~|}}zy{}ywx|~}~~~~{xx{}zz|}zy|~ywx}|wwzzwx~~}~~}}}zz~|yx{}zz{}~||~||}{wx|}}~~|}~~}xwx|}xwzzwz}{z{~{wy~{xy|}{{|~~~|xy~~{yz}~ywy~~~}}|xwz~}zz|{z}|zy{~ywy{y{~~~~|{|zwy~~zxy}|yy|~~||}}{{~{xy|}{|~~}~~~|yxy||yx|~{{}|yy}}xw{{xx{|{|}}~}zz}}zy|}{{}~||~{xwz~{z|~~~~{xw{{xy}~ywx}~}~}{{|}ywz~yxz}}}~~}}~}zy{{xxz~}}~}}~}xwy}{{}|yz|}zy{~{xw{{xy~~|{|~{y{{xy}zxx{~~}|}}{{|zwx|~}}~|{|~zxy|~zxy~~zy|}zz{~yw{}xwz}zz|~~~{z|~{yz}}yx{~~~}ywy}}zxz~}{|~~~zxx||xw|}yx{}{|}~}~}xx{|yxz~}z{}~~}}~||}{xx{}||}~}~}}}yww{|xy}|z{}~~~|{{|xw{|xxz~}|~}{{}|yz}|yy}|yy|~}~|z{|xwx|}z{~}|~}zy{|yx{|yy{~|yz|}xx{{xx||{{}~~||~~zyz~~ywx{~}~~~~|ywz~{z{~zz{{wx{}xwy|ww|~}}~~}}~zy|~zxy~~{zz|}|}}{|~}ywy~~}}~}}}~~{wwy~{wx|~yx{}{z|ywy{xy|}{|}~~{xz~~zyz}}ywz~}|}{wwz|zz|{z}|zyz~ywy{z{vrw~}{ytlhjnrw{}yssvz{rlmpw~tokjjnrttsrpqu~zpoxuuyjhr}vux|}{wuux~{uqnpw|ngjvzpkms{|rlnzwqwuqv{urpprstsqonosvxxvttv|xoknu~{uqpsx{vvy{{wrlijntyzxusu|~snr|xsv~~xuvxyyxxz{vsqrsvz}yojkr}{zyxvtrqsvx|}~zvrqruz~~xtv||tnnt|~wnijq{xqmlpv}{vqpt|{wuvy}{rkjmruvtrpoooqtvwwvw{zqjimt}|uqruvvtrrstuvtrqsw~|vpos}xpmmptwy{~}yxxz||ytolnt|}yvtrqqrstvy~~|~ysporvxxww{}zvsqpsz|xy|ypjjov|~|{||{|}zvqory|ywwwvsqprtutsstw{}}zuokjlqvz{xtrsy|ywvtrommpuyzwrmkmsy|{vpkikqw}{uolmrz|wqnnquz||zvuuy|vqqtxzyvrrv}}yvvvurollptx{yuollpw}~{wvy}yvssuwyyuqmlnsx{|zvroosy}yvsqpruz~}|~~ztnkmqw|~|xtsu|}wrruy{zvrqt{|zyyxvsqqtvwxvtrqqsw{~zwvy~}||{ywvwz|{wrmlnsx}~|xtqrw}zvspoqu{|{~~ysoosx{|zwutv{zzx}|wvzwlghlrxzqic`chq{~~}{utyrp{mdk}ncbjx|vtv{xrnmnsy|vqpt}~tru}}yuqonry}wzzv|~{xutttsplgcchpx~wqs{vlhhkoty~{sonprttrommqy{uu|yniluxoihkpuwxwvw{~~~|yusrty~}zwusstx|xy|~zwwz}{wtrplgddhq|vqrx~}~|yy|{tolkjknrx|}ywy{zsmlow}{{{{xtnhfhnu{}zvrrv|}ywvvvvtrrw{vssuwxwusrsvyyxzwqnot}zwvxy}~}}}ytnlmqw}zvttrolkkmprssrrtx}yx{xrpsy}wqlknsy|||~~{|{yvtrqqqqruyyqkijpy~wtuxz||yvtsrssromlmpssokhjqz~|{yvspooppoljijmprssqomnrx}}yuuy}xtommnpqrtx~|xurqqqrqpprw~~{z{||ztnjims{~{xutsrrponnoprrpoorx|xtsrssrqpprsttspnnry}|~xsqpppnllou}}~|yvsssttsqppsx~~}~}{ywurpnorv{zurrtvvtrqqsuuromou}}yvuuutrqppsuwwuqopt{|yz~~zvtssrpnpsx}~~}{xtrrrttttux}~{{~~|{zxuqonpty~tjighhigmvy|~|yvz|rkijov}xsmhcdgox~~}}z{|wx~{jeo}xidnuoqyzngegmt{|xtqsz~uqu~ytpmmpxwquwplkljgeddefedeiow}}vuzvlhhlrx}{tnkmqvxvqlkow}uprx}rhdivvpot{}xvtqolkmpv{|xtqoos{{wwz~~{yz}yvrpnkhffipz|vv}zxyzzyyz}~~{y|zsmkjijmpv{{vtux{yqjhjqz{uqqu{}xsppsw{~~~~zusrrqqrv}}vqqtxyxurqrsuvwy|wqqx|vokikpv{}~}}~}}~yussw}ytqonnmlkkmpswxz{~{z{{uqqv}|zxtolkot{}zvrpqtutrqt{~{xussw~wpjghkorqnkjknpponpswyzxtqpsy}xwwyyvsokijklmlklotxyvqmlpw~{yz~~zxvrolloswz|}|yxxwvtrppqrsrsuz{xvuuutrrsw}uppruwvsnkklnnlklpuz~~|yxz}}}~}{wtrqqqppoopsvwvtqqty{yxwvtqonorux{}|wuuwyyvsqqrttsqpsy}zxvuuvvvtssw~|vssvxxvspooppppqsvxzz{z{}~}~|wspprtuusstwyzwsqqv}}yz|}|yuqpqsuwxy{~~yvvwxxvusstvutrsv{~|ywuttuvvvx}|phb_`efjmnoyyy}wlfejr{~zwuohdbfnw}zz|~}zzvjitwjgsww}|{~|tkdcis~}wsru{|xyyqkkt~x}qhkw}qhedfffefhkmmkjjms{}z||qifhnv~}yuronpsvwvsporwzurquzzsmkq{xssy{tpppqrpoorx~yusstvx||usw|}zy{}|xuqmkhhils|zy}zqmotz}~}~z{~{phfhlquvwz~zrpquy~}xqljmt}zurrv{~~|vplkmrx}|xurqqrtvzxsqqtvxwtpmnqv{yssw|{zxtmhehnv|~xsrv|}uommnnmllnquwwwwz}}{toosz~zvsqppqtz~}~{wsqqrssstuz}wrru}|tlfdfjmnnnoprssrrsttuvwwvuux}zuttuusplijlmnmmnquxywtpnnrx~|yz{xwxxvtqpquy{xxwvsqopqsuuttuy~~{xwvvvuuvyxqmmpstsoljknqstuuuwy{|||{}|yy|~~|xtqpqqpnmnqvz|{xusv{yvvvvvtrqrsvy{}}~zurqrstrqoopqstvwy||wuuwxxvtrstwxwvvy|wvy~}yuttvxyzzz||}|zyzzzyyz|~}xvvwyzxurppsvyzz{}|z|zvtw|}voklosvusqopqsttssux|~zxz}xwy}}xsonoprrqqrtwyyxxz|}~~}{}}wuvy|}|yusrrtuvuuvy}|wtuyztqpsx|~|vpkjlostsrqqrsttttuwyz{{zxvvy~{uqpsw{|{wrnklnrtusqprux{|}yvvx{}}{xvtstvwxxwwwxxwuttw{~|xrnlnrw{{wrlijosvvspnnpsuvvuuwyzzxurqsx~zsnmqw|}ysmihlqvxwtrqsvyzz{|zvw{}wrprvz||zwvwxxvsqqtx|~|wqlkouz|ytmiimrwxvspopqsttssuwyzywtrrvz}}ysnmpuz}|xrmiilquwwussuwyz{}~|wwz~xtrsvz{{zxxyzywtrsvy|{ytpnnquz|zvpkikpuwwurqpqsuwwwvttuvwwvuwz|}zupmnrw|}{vplkmrx{zwrprvz}~~~~~~~yvx{|xttvy}~zwvvxyxwvwy|}|ytomnrx|~{uoklqw|{wrnmptx{{ywuuvwxwutux|~ztonrx}}wqmknsy|}zvssux{||}~~~xvy~~xtruy~}zxwyyywutwz~}ysppsx|~{vqmmqw|~|wrppruxzzyxwxxyyxwwxz}}{wsqrv{~ysnmosx|}{xutvy}~}~~}}{wx{zustx|~{yyyzzyxxz{|{zwuttvz}~}xsompu{~}zvrqsvz}}|yvuvyz{zyy{}}zuqpsy~{upnpv||vrrv{~|}}~|~|wvx}|wttx~zvuwz|}|{z{||{yvssux}{upnqw~ztqqty}}zxwxyzzyxy{~{ursw|~xsppty}xttv{~}|~}~|~{wwz}wttw}zwvx{|{zyz|~~|yvtux|~|xtqrv{zurruy|~~}{yxyz{||{{{||{ywvw{}xspqtz}yuux}~}~||~{}}~}~}{}|yy{{vtuy~~zwxz|}}|{{{{{{yxwx{~|xsqrw}~ytrtx~zvuvz}~|{|}}{xuux||vrqtz~xttx}~~}~}{}}}{{~~zxy}~xtuy}xuvz~~|{|}}|ywvwz~~ytrtz|vrrv{{wvwy|~~}||}~~|ywwz~{vttw{zutv{~~}z{}|}}~z{}{~~zy{{vuw|}xvwz}}||}}}|zzz{|~~}{xvvy}|wssv{|xvwz~|zz{||{zz|{wssv{zutw~|yz}~~|z{}{|~}{}}{}~|{}|wuvzyvwz|zyz|}~}}||}~~|yvuw{ytsv|zvuw{}{{|}|zyy{~}xuux}}wtuy|zz|}{|{z}~zz~~{{}}|yz~yuvz~xuv{~{yz{}}}|{|~~{xwx{~yutv{~yvux|}||}}||{|~|zyz|xtvxvuwvsuy~}zzyy{z{{|yxy|{xwuropsvxyytqu~ylkwzt{vu|zvvxz{zwuuywnjkrz~vporx}tljls}|wpnpwzqqzvor{~yxyzwsmkknsxywurpprw||rklt|sqsz~yvuuusrqqrtwy{}~ypnvyux{tprv|~{z{}~{voijp{ytqqrty~zsporuvtqmms{|wttwz{ytpnqx{tqsy|wutuvy~|xurprvwssvz}~}}{wvx{|}~yros||ywtqomnquwvsnihkqx}|{}yronoqsw{|vokjmrvyxsokilrz~vqoqu|~ysmiimv~}yusstvxxy}zxz}}xrnnry~zsomov~yrnns|}{|zsnmnqtvxxy{||~}~}}|yuqpqv{|sljmsy{ysnlnsy~~ztpoprtutttvy}zrmknswyywusstuvvvutssstttux~yqmlpx~wokloswxwutux|{vttttsqqsvz~{yy{~}wpkjlqvyzxtpnorvz|{zz|}vrsw|}xsooquxzzxwwxz|}}}{rmlotz~~|zz{}zz}}vokknruvutsrsvx{}~~{z}|uonpsvvusrsvz{usuz~xtsuz{z}{xwy|~vpnptx{zxussvy|}~~~~|wssw|{uoklotxyvromorvyzzyz|}~}zwwy~{ustx~zrljlqvyxvsrtwz}}}|{}xrpsx|~{uplmqvz{zxwwxyyxwvvy|}xsppu{~}xrljlqvzzwspnnpsuvvuvwyzxussvz}~|xtpoqvz}|xrljkpuxyxvtuwz}ytsvz~ysnmosvwwuttuvvutttvwyyxuroosw|}zuokknswyyvromoruwxvtrrtuttssuxzzxsolloty|zvokjntz}{xtsuy}~xttw{~ysonquxyxurqqstuuttuvxxwsomnrw|}ztnklqw{|xsnklpuyzxurqrstsqpqtwzywrnlmrw{{xsnkmsy~}yvuwz}~xtuy~~wqnosx{{yusrrsttsrstvxxwspnpty{{wqmknsy}}ytommptwyxvtsstttsrsuwxwuronpsx|}zvpmnrx}{xwwz~}xuvy~ztpprwz{zwutttuvvvvvvvvvusrsvy||ytomnsx|~{wronqvz|{wtrrtvwvuuvxyyxtqopsx}}xropu{zvuw|}wtuy~{tpqtz~~{wsqruxzzywvwwxwvsrsv{~~ztpnqv|zupnpuz}~{xtstvwwvuvxz{{xtrqtx|~}yuqptz~yvvx}~~zvvy~{uqquzzvsstwyzzyxxyyyxvuuw{}}{vrprv|~ytporw{~~{wutvwyyyxxxxyxwvuvx{~~|xsqrv}}xvw{}~}~~{yy{}wsqty~}xutux{}}{ywvwyyyxwx{}}ytqqtz|vqpsy~{vrruy|}|zyxyzyxvuuw{~zursx~{vtw|}}~}~}~{xxz~xsrv|{vrsvz~~|yxxyyyywwy|}ytrty~~xsqrw}{vssvz|}{zyzz{zyxwxz|~~{xutv{{vtv{~||~}|~}|}||yz|}wttx~}wtsvz}}zyxyz{{{z{{}}{yvuw{ztqrv{}xttvz~~|yxxy{{{zz{|~}zvsty|wtv{{yz~~|~}{}}}}{}~|{}zusuzzusuy~zwuvy}~~}{{|}}{wuux}}xsru{~xssu{~{yyz{{zyyz|~|xuvy~|wtv{}yy{~~{z}~~{|{z}|||zz|~xuuy|vrtx~~zvvxz}~}|{|~}{wvw{zvsty~~xtsv{~{zyz{|||||||}|{yxy||wtvz}yy|~~~||zz~{|}{}}z{}|~}|}~|xvw{|wstx}{xwx{~~|{{||{zyy||wssw}ytsv|~yvwy|}||}}}|zxxz~xuv{{xx|~}}}z{{{~~}~~zy|}{}~~~}yvvyxttx}zvux||||}}|yxx{}xtuy}wstx~~zwwy|~~}|}~~~|zyz||xvx}|xx{~}zz~|{~~~}{|~zz~}zz~~~~}}~}ywx||wuv{zwwy|~}}}}}|{z|~~zxwy}}xuvy~~{yyz}~|{{|}}|||~|xwy}|yy}~|}~|{||{}~}}~|{~|z|~~|zz|zwvx|}zxx{}{z{|~~}}~}zxxz~{wvy}{xxz}}||}}}|{{}~zyz}{yz~}}~~|~~{|~}{}~{|~~}}~~}{z{~|xwy}|xwy|~|{{|}}}}}}{yz|~{xxz~|zyz|~~}}}}}}~~~}{{||zz}}}~~|}~||~}}}|~|{}~}~~~}||~}{yz||yyz|~|{|}~~~~~~~~~}|||~~{yy{~|zy{~}{{|}}|{|~|{{~~|}~}}|~}}~~}|}}}~~~~~~~~~|zz|}zyz}}{{{}~~~}|{|}~|zz|~{zz|}|{|}~~||}~||}}}~~}}~}~~~~}}~|}~~~~~~~~}{{|}{z{}}||}~~~}}}~~}{{}}{z{}}||}~~~~~~}~~||}~}}~}}~}~~}}}}~~~~~~}|}~}{{|}}||}~}}~~~~~~}||}}{{|~~|||~~}}~~}}~~}}~~~~~}~}}~~}~~}~~~}||}}|{|~~|{|~~~~~~~~~~}||~}{|}~}||}~~~~~}~~}~~~~~~~~}~}}~~~~~~}}}~~}||}~}||~~~~~~~}}}~}||}~}}}~~~~~~}~~}~~~~~~~~~}~~~~~~~~~~}||}~}|}~~}~~~}}}~}||~}||}~~~~~~~~~~~~~~~~~~~~}~~~~~~~}}}~}}}~~~~~~~~~~}}}~~}}}~~~~~~~~~~~~~~~~~~~~~~~}~~~}}~~~~~~~~~}}~~}}~~~~~~~~~~~~~~~~~~~~~~~~~}~~}~~~~~~}~~}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/m56.bin b/applications/SprPoP/dist/MUS/m56.bin deleted file mode 100644 index 60c47cb..0000000 --- a/applications/SprPoP/dist/MUS/m56.bin +++ /dev/null @@ -1 +0,0 @@ -}}|zyyyz}~zyxxxxyyzzz{||}~~}||{{|}}~ztt}xvw{|w~v~~~~~ztqv~}wssuvxy{|}~~~~~~~}}}}~~}zvtv~}}~~|wsvwsstvwyz{|}~~|yurrv|}zwuuuvwyyz{||}}}~~}{{~|wsqrw|~}yurqqqrsuvwyz|}~~|{xurqsv{|zxwwxxxyzz{||}}~~{wttux{~~|zwvtsssttuvwxyz{||}~~~~~~~}|{zywusrrsw{~}|{zzzzz{{||}~~~}~|ywwxz|~~}{yxwvvvvvwwxxyz{{|||}}}}}}}}}|{{yxvusrrtvz~}|{{zz{{{||}}~~}}~}zxxxz{}~|{yxwvvvvwwxxyzz{{|||}}}}}}}||{zyxvusrrsvy}~}|{{{{{{||}}~~}}}}{yxxy{}}|zyxwwvvwwxxyyzz{{|||}}}}}|||{zyxwusrrsux|}||{{{{|||}}~~~}|}~~|zyyz{}~}{zyxwwwwwxxyyzz{{||||}}}||||{zyxwutrrsux{~~}|||{||||}}~~~}||}|{yyz{}~~|{zyxxwwwxxyyzz{{{||||||||||{zyxwutsrstwz}~}|||||||}}~~~}|||~}{zzz{}~~}|{zyxxxxxxyyyzz{{{||||||||{{zyxwutsrstvy}~}||||||}}~~~}|{|}~|{zz{}~~}|{zyyxxxxyyyzz{{{|||||||{{{zyxwutsrstvy|~}}|||||}}~~~}{{{|~~}{{{{|~~|{zzyyyyyyyyzz{{{{{|||||{{zzyxwvtsrssux{~~}}}||}}}~~~~|{z{|}~}|{{||~}|{{zyyyyyyyzzz{{{{{|||{{{zzyxwvussssux{~~}}}}}}}~~~~|{zz{|~~}|{|}~~}|{{zzyyyyzzzz{{{{{{{{{{{zzyxwvutsssuwz}~~}}}}}}~~~~|{zzz{}~~}|||}~~}|{{zzzzzzzzz{{{{{{|{{{{zzyxwvutsssuwy|~~}}}}}~~~~|{zyz{|}~}}||}~~}|{{zzzzzz{{{{{{{{{{{{{zzyxwvutssstvy|~~~}}}}~~~~|{yyyz{}~~}}}}~~}|||{{{{{{{{{{{{{{{{{{zzyxwvutssstvx{~~~~~~~~~~|zyyyy{|}~~~}}~~~}}||{{{{{{{{{{{{{{{{{zzyxxwvutsstvxz}~~~~~~~~}|zyxxyz{}~~~~~~~~~}}|||{{{{{{{{{{{{{{{zzyyxwvutsstuwz|~~~~~~~}|zyxxxy{|}~~~~~~}}|||||{{{|||||{{{{zzyyxwvuttstuwy|~~~~~~~}{zyxxxxz{}~~~~~~}}}|||||||||||{{{{zzyyxwvuttttuwy{~~~~~~~}{yxwwwxy{|}~~}}}|||||||||||{{{{zzyxwvuutttuvx{}~~~|{yxwwwwyz{}~~~~}}}|||||||||{{{{zzyxwvvutttuvxz}~|{yxwvvwxy{|}~~~}}}}||||||||{{{zzyxxwvutttuvxz|~|{yxwvvvwyz|}~~~}}}}}||||||{{{zzyxxwvutttuvwy|~}|zyxwvvvwxz{}~~~~}}}}}|||||{{zzyyxwvuuttuuwy{}~}|zyxwvvvwxy{|}~~~~}}}}}||||{{{zyyxwvuuttuuwy{}~}|zyxwvuvvwyz|}~~~~}}}}}||||{{zyyxwvvuutuuwxz|~}{zyxvvuuvwxz{}~~~~~}}}|||{{{zzyxwwvuuuuuvxz|~~}{zywvvuuuvxy{|}~~~}}}}|||{{zzyxxwvvuuuuvxz|~~|{zywvuuuuvwyz|}~~~~}}}}||{{zzyyxwvvuuuuvxy{}~}|{zywvuuuuvwxz{}~~~~}}}}||{{zzyxwwvvuuuvwy{}~}|{zxwvuutuuvxy{|~~~~}}|||{zzyxxwvvuuvvwyz|~~}|{zxwvuuttuvwyz|}~~~~}}||{{zyyxwwvvuvvwxz|~~~}|{yxwvutttuvwxz{}~~~}}||{{zzyxwwvvvvvwxz|~~}}|{yxwvuttttuvxy{}~~~~}}||{zzyxxwvvvvvwxz{}~}|{zyxwvuttttuvwy{|~~~}}||{{zyyxwwvvvvwxy{}~~}|{zyxwvuttttuvwxz|}~~}}||{zzyxwwvvvvwxy{|~~}}|{zyxwvuttsttuvxz{}~~~}}||{{zyxxwwvvvwxyz|~~~}||{zyxvuutssstuvxy{|~~~}}|{{zyyxwwvvvwxyz|}~~}}|{zyxwvuutssstuvwyz|~~~}}||{zzyxxwwvwwxyz{}~~}}|{zyxwvuutsssttuwxz|}~~}||{{zyxxwwwwwxxz{}~~~~}||{zyxwvuutsssstuvxz{}~~~}}|{{zyyxxwwwwxxz{}~~~~~~}{{xusrty~}xtrqrstuvwyz|~xvvy|~~}|{{{{{|||||{{yyyyz{}}~}}}}~wmo~ujggikmoqrtvxz|}}}~}||{{{{||}}~~xy~yvvvvwwxxxyzz{|}}~~~~~~}}||zxuqptz~~{wutttuvxy{}~yx~{wutsssssstttttuuuvvvwwxy{|}}}|{{~~xrmlnv~}xrnlkklmoqsuwxz|}~~}|}}zwvy~|zxwvvvvvwwxyyzz{{{{{{{zywusqqruy|~}{yxxwxyz|}~zxx{|ywvutsssssstttuuvwxyz{{||{{{|}zuqnlmqw}}yvsqppqrstvwyz|}~~}|}|zwvvz|zyxwwwwwxxyyzzz{{{{zzyxvtrqpruy|~}|zyyyyz{}~|ywx|~{ywvuttttttttuuvwwxyz{{{{{z{|~{wspmkmqw}~{xvtssstuvwxy{|}~~}||~~{ywvwz}{zxxwwwwxxyyyzzzzzzyyxvusqpqrvy|~~}|{zz{{}~~zxwx||zxwvuuuttuuuvvwwxyzz{{{{zz{||xuqnllnrx}}{xwvuuuvwxyz{|}~~}||~~|zxvvw{|{yyxxxxxxyyyzzzzzyyxwvtrqpqsvz|~~~}|{{|}~|ywwy}}{yxwvvuuuuuvvwwxyyz{{{{{zz{|~|yvspmklnsx~}{yxwwwxxyz{|}}~~}|{|~}{ywuuw{~|{zyyxxyyyyyyyyyyyxwvusrppqsvz}~~~}}}}~}{xwwy}}{yxwwvvvvvvwwxxyzz{{{{zzz{}}zxuromklosy~~|{zyyyyzz{{|}~~~}|{{|~}|zxvuvx{~~~~}|{yxxxy{~|yxz~ysyyupmlllmmnopqsuvyz{||}}~|~|zywutrqonnorw|vpyroquwyz{||}}~~~~}z~~~}}|{{zzz{~{twwpnnopqrrsssssssssrrqrsuwz|~~|zwvy~~{tt|zsomllmnopqstuvwwxxxyz|}{{~}|zxwvvvx|}vqqw|xuttttuvvwxxxyyyxyyz|}{z|}xvx}|xtrpoooooooooonnnnopsuxz{|{zxwvx{zwwz}yvsrqqqqrrstuuvvwxy{~~|{|~}|{zxxwxy|{vssu{~{yxwwwwwwxxxxxxyy{}}{{}zwwz}ywtsrqppppooooooopqtvxz{{{yxwwy|}ywx||yvusssssttuuvvwxxz|~}{{}~}}|{yyxxyz|~}zvttw|}{zyxxxxxxxxxxxyz{}~|{{}}zxx{{xvtsrqqpppppooopqrtvxyzzzyxwwy}{ywy}~{xwuutttuuvvwwwxyz|~~|{{}~~}|{zyyxxyz|~~|xvtuy~}{zyyyxxxxyyyyyz{}}{{{}|yxy|}zxvtsrrqqqpppppqrsuvxyzzyxxwxz}~zxxz~}{ywvvvvvvvwwwxxyz|}~}{z{}~~}||{zyyxyyz|}~}zwuuvz}|{zzyyyyyyyyz{|}~~|{z{~}zxxy}|ywvutsrrrqqqqqrrtuvxyyyyxwwx{~|yxxz}{yxwwwwwwxxxxyz{|}~~}|{z{}~}}|{zzyyyyyz{}~~~}{ywuuw{}|{{zzzzzzzzz{|}~~}{zz{~~|yxxz}}{yxyyzz{||}}}~~~}{yxxz||sov}smklmpqrsuuwyz|}~}}~}x}}{{{|{{yxwvvvwxyzzyyxxy|uzuooqtuvwwwxxy{|}~~~}}~}z|~~}~ypq|~skhghhijklmoprtuvwxxxyz|}}zsry|tpmmmmnoprsuvxyyyyyyyz||yxz~|{zyyyyz{|}~xvz|vsqpoopqrsuvwxxxxxxyz|}zy{~{usty~ytpmkjjjklmopqrsttuvwz}~}}~yuux~{wusrrrrstuvvwwwwwxy{}~|yxx{~|{zzzz{|}~~zxy}|yvtsssstuvvwwwwwxyz|~~|zz{~yutv{}yurpnmmmnopqrsttuvwxz}}|}}xvwz}zwuttsstuuvwwwwwxxy{}}zyxy{}|{zzz{|}~}zyz~{xvutttuuvwwwwxxxyz|~}{zz||xuuw|{wtrpoooopqrsttuuvwy{}~}|}{xwx|}zxvuuuuuvvwwwwwxyz{}~}{yxxy{}|{{z{{|}}~|zy|}zxwvvvvvwwwxxxxyy{|}}|zyz|~zwuvy}}zwtsqqqqqrsstuuvwxy{}~~}|{|~zxwy}~~}{{zxxxvwxyz|~~~|zyyxyz{}||}}}~~~v{|||{zywvvuvwxy{|}}~~~~~~ztt~}}|}}|pn|wx{}zwusrqrrstuvwwwwwxxy{|}|zww~~z}|soosx}~|zxwvvvvvvwwwwxxwvussv~}{zz{~{ut{|vronpsw|~|{zyyxxxxwvtrru{~}}~|wstz{ywtrpooqty}|zxvtsqpnmnrw|~yutv~}{wvtqonnorw{|xupljlu|{{||}~~|}srstuuvwwxyyyyxxyz|}zy{~~z~}zz{|}}~~|~uyukijlnpstvxyyyyxxxxz|zvw~}||}~}}~}rknzyqlklnprsuvwwvutsrsux~|yvv{|xuuw~}vojltxsqoopqrsssrpooqu{}zxx{}{xvuvz~ytolms{~yusqpoooprux{}}}~}{{||yusrty~zvronqx~ytqpqtx{}~}{zyyz}~|{|~yursv{}}~|xtqoptz~}}~~~}|{zyyxyz|~zwuuvwxy{~~}||{{|}{vrnmnsz~|{zzzzzyyz{|}wrmkms|}{yyyyzz{{|}~zvqnmpv~|{{{{{{{{{|}~}yvssuy}}zxwwxxyz{|}~~|{zyxxz~}|||||||{{{{||}~}}|}~}{{}~|zyxxyzz{|}~~}yvvx}~~}||{zxvusrrtw|}xustvz}~~|{zzz{{|}}}}}}}}~}yvuvz}{ywusqnmnqv|}yvsrrux|~||{{{{{{{{|}{xvvx}|yurpoopsw{~|ywusstw{~}|{zyyz{}}zwvwz{wsrsux|~~|{zz{}~}|zwuttvy}}|{{|~|yvuux|~}|~}{ywvuuvxz|~~|zwvuuwz}|wspnorw}~{ywvuttttuwy{}}zxutstx||wrnkjlr{}zxvuttttttuwxz}~~|zxwvwy}|yvtssuw{|zxwvuuuuuuvvxy{|~~}}~~}zxwx{~{yxwvvuuuuuuvwyz}}||}~}|||~~zvsqqty~}{zxwvvuttttuxz~}|{|~~}|{z{|~{wtqooquz}{ywvuuuvwy{}~}||}}{zzz{}}zwtrpopsx}~|zxxxyz{|}}}~~|{|}~|{{}}{xvsqooqty~~~}}|||||}~|zyxy{}~|zwtrpooquz~}||{{{|}~~|ywutuw{}{xurpnnpsy~}}||||||}~}{ywuuvx|~}{xvtrqqsvz~}||{{{{{|||}}}||{{{{|}~~~}|{zywvuuvx|~}|{zyyxxxxyz{}~}~~~~~}zxuttux}}{zxwvuuvwy||{zz{~~~~~~~~}zxvtstvz~}{ywvvvwxz}}{zyyz|~~~~~}{yvusstw{~|zyyz{|~~|{yyxyz|~|ywusrrtwz~~~}~~~~}{zyxxy{}~|zwusqppruy}~}||{{||}~~~}|{yxxxy{~}{yvtqommorw|~||{{z{{||}~~~~~}|{yxxxy{~}{ywurponoquz}|{{zzz{{||}}~~}}||{{{{|}~}|{zyxwvuutuvwz}~}|{{{zz{{{|}}~~~}}}}~~~}|||||||}||{yxvuuvy|}|{{zzzz{{|~~}||}~~}}|||}~~{ywusssux|~}|{{{{|}~~||{|}~~~}}~~~|zxvtrqqsuy}~~}}~~~~}|{{{}~}{zxvtrqqqsvz~~}{zzzz|}~}|{yxvtrqppqtw{~~~~~}|{yxxxyz}~~}|{yxvtsqppprux}~~~~~~~~}|{yxwwwxz}~~}}|{zxwusrqpprtx|~~~}}}}}}}||{zzyyyz{~~~~}||{zywvussrrsuw{~}}||{{{{{||||}}}}}~~~}}||{{zyxwvutsstvx|~}||{{{{||}~~~}}}~~~~}}}}}||zywvtssstvy|~}||||}~}||{||~~}|{yxvussrstvz}~~}|{zz{|~~}|{yxvutrrrstwz}~}|{{zzz{|~~~}|{zxwutrqqqrtwz}~~}|{{zyyz{}~}}|{yxwusrppopqtw{~~~~}|{{zyyyz|~~~}|{zyxvtsqpoopqtw{~~~~~~}}||{zzzz{|~~~}|{zyxwvtsrqqqrsux{~~~~~}}}}|||{{{||~~}||{zyyxwvvuuttttvwz}~~~~~~~~~~~}}||||}~~}}||{{{{zzzyxwvutttuwz}~~}|{{{|}~~~~}}}}}}}}|{zywvutsssuwz}~}}|{{z{{|~~}|{zywvtsrrrsuwz~~}}|{zzzz{|~~}|{zxwvtsrrrrsux{~~}||{zyyyzz|~~}|{zyxwvtsrqqqrtvy|~~}}|{zzyxxyz{}~}|{zyxwvusrqqqrsux{~~~}}||{zzyyyyz|~~~}|{zzxwvutsrqqrstwz}~~~}}||{{{zz{{|}~~~}|{zzyxwvutsrrrstvx{~~~~}}}}}}}}||||}~~~}||{zyyxwvutsssssuvy|~~}}||||}~~}}|{{zyxwvutsrrrsuwz|~}||{{{|}~~}}|{{zywvutsrrrrsuwz}~}||{{{{|}~}||{zyxvutsrrqqrsuxz}}|{zxyyyz{}~~~~}}|{zxwwvuvuuvwwxz|~~y{~~}}}}}~~~~~~~~~}|zywutsrrrrrstuwy{~yxyz{{{{{{zzzzz{{{|||}}~}~}}|{zywvuuttuuvwxz|~{wvvvvvvwwwwwxxyyyzz{|}~~|~~}|{{zyxxwwwxxyz{}}zwutttttuuuvvwwxxyzz{}~~|}}}|{{{zzz{{{||}~|~~zwvuutuuuuvvvwwwxyyz|}~~}||~~}}}}}}}}}}}~~~||}zxwvuuuuuvvvvwwxyz{|}~}|{|~~~~~}}}}}}}}~~|{}|zxwvvvvvvvvvwwxyz{|}~~|{{|~~~}}}}||}}}~}{{~{yxwvvuuuvvvvwwxyz{|}~~}|||~~}}||||||}}~~|{}~{yxwvvuuuuvvvwwxyz{|~~~~}}~~}}}|||||||}~~}{{~}{ywvvuuuuuvvvwxyz{|~~}}~~}}|||||||}}~~|{||zywvvuuuuvvwwxyz{}~~}|}~}}|||||||}}~~|zz|~{yxwvvvvvwwwxyzz{|}~~}||}~~}}|||||}}}~~}{yz}~{zxxwwwwxxxyyzz{|}~~~}|{|~~~}}}}}}}}}~~}{yyz~~|{zyyxxxxyyyzz{{|}}~~~~}|{{|~~~}}}}}}}}~~}{yxy{}{zzyyxxxxyyyz{{|}}~~~}|{{{|~~}}}}}}}}}~~~|zxxy}}|zzyyxxxxyyyzz{||}}}}|{{z{}~~}}}}|||}}}}~~~}{yyy{}{zyyxxxxxyyyzz{||}}}}|{{{|~~}}}|||||}}}~~~|{yyz}~|{zyyyxxxxyyzz{{|}}}}||{{{}~}}}||||||}}~~~|zyy{~|{zyyxxxxyyyzz{||}}}||{zz{~~~}}|||||}}}~~~}{zyy|}|{zyyyyyyyyzz{{||}}||{zzz|~~~}}}}}}}~~~}{zyxz}}|{zzyyyyyzzz{{||||||{zzzz|~~~~~~~~~~}|zyxxz~~}|{zzyyyyyzzz{{|||||{zzyy{}~~~~~~~}|zyxwx{}|{zzzzyyzzzz{{{||{{zzyyy{~~~~~~~~~~~~~~}|zyxwwy|~}|{zzzyyyyzzz{{{{{{{zyyyz|~~~~~~~~~~~~~~}|{yxwwxz~~}|{zzzyyyzzzz{{{{{{{zzzz|~~~~~~~~~~~~~~}}|{zywwwx{~|{{zzyyyyzzzz{{{||{{zzz{}~~~~~}}}}~~~~}}||{yxwwwy}}|{{zzzzzzzz{{|||||{{zzz|~~~~~~~~~~~~~~}}|{zywvvwz~}||{{zzzz{{{|||||||{{zz{|~~~~~~~~~~~~~}}}|{zyxvvvx{~}||{{{{{{{|||||||{{zzz{|~~~~~~~}}}}}}||{zyxwvvvy|~}}||||||||||||||{{zyyz{}~~~~~~~}}}}}|{zyxwvuuwz}~}}||||||||||||{{zzyyz|~~~~~}}}}}}||{zyxwvuuvx{~~}||||||||||||{{zzyyyz|~~~}}}}}||||{zyxwvuuuwy}~~}||||{{{{{{{{{zzyyyz{}~~~}}}}||||{{zzyxwvuuvx{~}}||||{{{{{{{{{zzyyyz|~~~~}}}}}}||||{zzyxwvvvwy|~}}|||||||||{{{zzyyyy{|~~~}}}}}}||||{zzyxwvvvwz}~}}||||||||{{{{zzyyyz{}~~~}}}}}}}||{{zyxwvvuvx{~}}}|||||||{{{zzyyxyz{~~~~~~}}}}||{zzywvvuuwy|~~}}||||||{{{zzyyxxyz|~~~~~}}}||{{zyxwvuuuwz}~}}}|||||{{{zzyyxxxy{}~~~}}}||{zzyxwvuuuvx{~~}}}||||{{{{zzyyxxyz|~~~}}}||{zzyxwvuutuvy|~}}|||||{{{zzyyyxyy{}~~}}}||{{zyxwvuuttuwz}~}}}||||{{{zzyyyyyz|~~~}}}||{{zyyxwvuttuvx{~~}}|||||{{{zzzyyyz{}~~}}}||{{zzyxwvuuttuvy|~~}}}}||||{{{zzyyyz{}~~}}}||{{zyyxwvutttuwz}~~~}}}|||{{zzyyyyz|~~~~}}||{{zzyxwvuttttvx{~~~~}}}|||{{zyyyyz{|~~}}|||{zzyxwvuutttuvy|~~}}}||{{zzyyyyz{}~~}}||{{zyxwwvutsstuwz}~~}}|||{{zyyyyyz|~~~}}||{{zyyxwvuttsstvx{~~}}||{{zzyyyyy{|~}}||{{zzyxwvvutsstuwy}~~}}|||{zzyyyyyz{}~~}}||{zzyxxwvutttttvx{~~~}}||{{zzyyyyyz|~~~}}||{zzyxxwvuttttuvy|~~}}||{{zzyyyyz{}~~}}||{{zzyxwwvuttttuwz}~~}}||{{zzyyxyyz{}~~}}||{{zyyxwvuutttuvx{~~~}}||{{zyyyxyyz|~~}}||{{zyxwwvuttttuwy|~~}}||{zzyyxxyz{|~~~}}||{zyyxwvuttsstuwz}~}}}|{{zyyyxxyz{}~~}||{zzyxwvuutssttvx{~~}}||{zzyyxxyy{|~~}}|{{zyxwwvutssstuwy|~~}}|{{zzyyyyyz{}~~}||{zyyxwvuttstrutv{}~z{xwyx{~}}ztuxz|||{~|rort{~~~{vtmlry~wswwtqnnonrw~~{ywrprsy|}{pipqqrruyz|}}wotwplorty~zy}ty}ustpmnommhoqiu|yy}~|x~u|}ttuy|wqt|xzszvy{vy{{puopvurjnzwsuuy~|v~ywxuzovuuww|tspu~usmsyuowvwqo{|}yvsu{}z|zzpu~vs|~xwx||~z|yxt|~~~uy|}}snkuztu{zyy}}}|}yvwuqzqoltwv{xrw{~rqvtz}y{~~~vz}}|}{{swusswxvw~w~}kc{||sjhkjptu}zovsz|~x}~uq~~~y|zrt|{|{|}}t}{~tyw{}{ti}xsnorms{yvlpztxsv{{|wxy}{|w}|v{tutusyyw|z}}|yy}|}{mkiuzq}szv}yyjapptvjuqsw~}twuzxtzx||}xv{rutvmyxxwxzxmlietvtvwy|zxpnpmnxlurvxyxxqy}wmw}~|syzouuzo{{|vwx|{wmwvfhv{{s|xyyruknp~nvzuxvyvztt{ys}}~xz}x|swwrsyt|{vyzs|~|rvxofj{~wxzzzwotwknvopzxtry~uq{}xx{|yy}t{zuvnvy}|u|x}zz{~~|sqyvmiw~vyz{xtiuukoyymz{wwrs{}ru}}{zx{}}|t~}wtpr{u~|}v~~w~~~x~ko|uqkzytz|ysmm{mjsztmzwxwuw}{w~y|{}~||wyvwwqs{xy~}y~yy}}{tjtztrnzwv{xslq{jmw}umxyyrw}yy~{}{x~xuvtvupu}}z{}~|{{~~~}x|~ljwxspw~zux}wtmvuky{xosx|ws}{{~y~y~zv|utuzwot|x~|}{~z|zs}yiltswr~xtxor}ppyvntw~ysww}~x}wy~}|xtvzutzy}{y}z{|sy|kiqsvvxu||sp{xlzyvtnrzwr}yy}w|}y~{x}~~ztv{xty~}{{x~{{~~st|qhntwxtyv}uqxxltsumnv}xrw|}wzyx}}}sp|}svzz~~v~~{wsyumoruzw{ryqvxoprtolwyrxz|yz~xv}z|}tpz{swx~u~y~vsyxooptzw~r{qvxssstuku}{tv}|wyz~vx{y|~toy|tyz}}w}z}vtxwpmnvxt|~uzt{yusstukx~{tx|tx{|vw{w}umz~wzw~||s}~y||wuvtpnmuvu{x}v|xwvtsuhxyrvyrx~z{xw|zuoz}y{w{zt{y}x~|wttsroksut{zx|wwyuowkv}ztuzrxw{|z|y~wox}xzw{~}|xz~|{u{{wustvpisxu{|yw{xxz|ymvnr}zuu~tvy|~z||yz|przy|x||~~{}{w{yzzxzvwurutloxt|z~{{{w|{{qtum{|zu}vs~{}{|{}yxx~rowy|zy|{~w|{y|rywxvrsvplxtx{{~wz|ypwqw}{wy|qxx{|}}zxw|yosy|w}~z~xyxvzwswvwrntvlpvt~y~~|xzurvs{{xv~tr~zz}z~|z{xy~}vqvy|}|}y|w}z~v~}~pvtutopvrlutz~|~~yz|xrswvyzwy~rvz|}yz{zvz|trv|{~y{z~zzz~}x{x~orqtrmpvqoxv}}~~y|xxsswxyyw|~}ry~|~z{w|zx|zsrv{~zy}~y|yz}{wwnqruplrxrvxy}{~{wvxqu{y{yxwt}}~v}u{wz}|wrsy}zy~{zyw|}|{qoqtsmnwwtyv~}~y|}vwp{~xzw}ty}{}vtx{x{|ztst}zz{{x~y|||}{}y}xoortolrytxv|~|{{uytuzzx{~wv}|{t{r{xz|{xtrz~x}x{x|x~{~~|{{{zsnosqlnwvxxw~~~~{yvyq}wyx}{u|}}yxyryzz}|yvsvzxzxzy}}~|~xz|xunnsrlluwxxu~{}tzq~wxw}|v{~x|xsw{{}|zwtuzzzxz{z}yxx}vvomsrmksxzxt}|szs|xxv}x|~y~~wvu{|}{yxus~{{zx{|x|xxwtwqnrrmlsw{xt}|}s{t~ywx~}z~~x~}xvu{|}zyxts~||{zw|}xzwwvswrosrmlsw|vu{|t{w{wy}{~~~~w}|ywsz}}zyxtt}|||zw|~v{wxusvuosrmkqv|vs~z|uzz}}wx||~~w{}z~ysy~~{xyvt|z|}zv|}v}|v{rusvorqmkot|wr}z|xw|}xv~||~~xz}|}{uv~|yzxtzy|{wz|z{~v{q}{oxqpqnkmrz{r|{}~}}u{~|u~~{}}zy{}~{yt{}yyztxx~}~xy}}x}xw~tuptupqpllpv~uy}{{xwwz~|||xzz{{vu~~{xzwvyx}|w}{{}}|u{~|p}zovsqqnlnr}|v~{||~w{~~v}z}~xyx~x{sy|yyzvyv~|yy}{y|xv}vs~spvrqomlovx|~}{}|v}zxzy~|wyw{xzr{~{xzzyyv|~x{{|{|}vw|~sv|qpvspnlnqzy}z}{w}x{}xz~zwxxxxys|~{y{z{yy}}~}x}y}~~{}|uy~}zqxyoqvrpmmos~}}}{}yy~w{w}}wyu|u{vu~}zz|{|vz~{{|y~zxv|{}tsyunsvqomnpy}{{~~w{|{}xxywxu{u{ty~|y{{}yx|~}~z~y{~xz{|yruwrnxwrppsv|}{|~|}}vkoprsvz|{u~{}{wx{x~xw{~zxxqttuovzurqtv}||vv{{w|~|vz~vwqq|sz}~{z|}v~wsvz|zxrmlquyvsvorpsnoxtomoqy~~}{}~{rmwpsxsrrtkqzyz~~x~|~{~||~}vqr|~{z|~}y|y{zvzxz|~tps|{xtzxyz||{ysu~{rrnm{wr~y}unmw{x}urrsusqslqlmnittmjmnvy{||}utlkvrn{}qvyyxy|y{||y}}~|uqr}}w~}}}~~~yxz|wywyz~}|~||vx{yw{}zvz~|{wsv{sunqzupy~x~upnxyq}zxvwsstmtllpjrvnklnvv{|}~~wvmmurlrvyyyyv|{z|{w{}}vrr{s~~}}}~zzv~x||wxz}z||}{z~|z|z~wu}{wst}~tvpoxwrq~~|~yrot{pv~}~ywtsvotripomutllmsuv|~~~}{wslssqlvz}w~~wx}{}~y}ww{ysquyt}|~}{|}wx~y~{xx||~{x|}}}~~}~|xtw}zurv~{uvmswwsr~}vpovyqv|zwsuvpwrjprnuunnoutx~~|}}}}ywplsqrnv~~x~xxx|}y|vvzysqu~yvz~}{z{~xv~}{}yy|z}ywz}~~vxs{uru||uvnrwvwr}~}~uomsxuvz}xstxqwvmmtrrwsppvuu~{{}{~{vripqrtsy||x{{vy|xuw||tqrz|zy|{w}~{~wy}}|z{z|yvww{s{~wrsywwonvuywx~~}{}vomqwwzxzsv{}~{zxt~|yzvtqos{x{|z~}z|~zwovxz}to{qpps|{q}wyuqsyxvw~}~{y}uv{|{{}v|~~{xuw{v}wvlqwnt}{snpw|~z{ru{y~~}yw~|~xwzxtt{}{ok{vjekqlnqnmqnp|z|}{yvv{|~|{xv~}wnyxpmrlgwsoqkknv}|uqpuxw}uy~~|xtz}~~|}|~xx}uxsnrxrkjotxyvxrnvuw|{||}~~v}||{|zy|~zsnsztzvnqqnpmkvvw{ywtru}z}}|~x~~{qv||zpxytsljls{|vqptvt|ur|||}}yru~}|}{~|{|}zw|xomptwxvzrpwtvz|y{~~~uz~|z||z~~||{}vmqz}|~ssvsrlmwtx{zwsru{~wx~}|{}~|ru{~x{yyumlnt{|wrqvts{sp~yx{~~yqtz{{}}{}~|{{}{spruwvv{srxuuy}y{{{~vx||z{~|}z}wyopuwztyxvnnwsw||xtrty}yt}~~|~}utw}~z}{rnnsz}zusvuqywov{uw||trwv}~z{{{{{}~zttvvvv{yrxwuw|}x|{}~zwy}{z~~~y~yz}{~uosr{zw||vnuvt{}|wssvz}ww~|}}~}uux}z|sopv{}zvuwsrzuqv{sv{~zstuv}||{||{{~~|wvwwvv}ytzwux}yz|||yxy||{~x~zz}x||tptr|x}ypwwt{~~yusvz}yw|~~~}}}vvx}xvrrvz}{ywyuqywtt{}trx}~{tuvt}{|{~}{{}{yyxwu||v{zww{}x}|}z|{xy{}~{|z{|{{wuz}{wruux|zuvzux}}xuux|}y~}}~~~~}~~{xxz|~~yszwx|wuy~~zsxwzxz{z{u~|y{x}wqwvw{||}|}}~|zx~{~|z}~z}~{{{}}||}~|woysmryzxtxtu{z|x|}xvuux{{}}|}yrs{|spywrssuwz|~zuy{~zy|ysxv{z{{}{wzsnlpywvwyyurtz}uwuv{}|zxy}{{vtx{}}}~xmopt~~ztttmu~~}{|zx|x~yw{~{urqrvz~}yqmpx~zxvttuw{|ztvv}{yz~zrvvw~~~w~|tnkkmt}wux~|}wtzyrx}}zxw~}z}wuw||~~~}~wmonoy~|{you|}~|{y}}zzw{~|xtsrsw~}~{soptz{~}zwttvy{zuwy{~}{|~zswst||~w~~wqnkkp||zwz|y~~vvzzrx~{wy~z|}xvwz}|{~}{{~xprlmuwz{rx||}|{{|{~yx{~}yvtrru|{~~wrqsvwx~{vtuyzzvx}{|~}}|uxsryzx~x~zuqmkmyxw~z{|x|wvy~~uu~|xx~z{{xwy{|y}|z|}stokquuzvx|||~~{{{y|}xz}~~{xvsqr|~v~}{urrtvvx{vuwzzxw~z}xxwquzyw|{}xtpljr{uz}}zx|wxy~}uv|x{}x{xxy{|z~{z|yssllsvuyx|}|}~{|{v|yz|}}zxvsqt}w~~~|zurrtwxz|xvwzzxy~|z{~xyvqv{zw}|||wsoljs|wz{y}xyy|~xu}~y|}w~{yxy{}}~}z{~ystmkrxxxz}}~|{u{yz{||zxvsqs}z~~~y{vsrtx|~z|~x{~~{{}sp}|y|~{tp}zlfjvvqw|~z{{zvtw{zz~x{ssxtw|x~{y~x{wswzwqx|}zrp||rnvzsqu{yvy~~~}zy~~yyrs}}uqtqnxwory}}|{}}~xwyyus~}}|uppxxqqyvrtz~{{{w}|vtrqu~{xvtsv}~v|yu{~{{}zvxqnquwv{}}~upory}wuvzyvx}~~~}x}zxxvy}|xuuuw{vv~yqu|~|{~{ztuxyww~~|wrpqv}~{zzy}~}~xy}wttrry~{~rpqsuz~|yx~|}|wzvnpsvvwx~vqoqv~~{xy}zy{~~~xzzxxvv{{xttuvy~{wwt{~|~||uuwxwu{~}|ytqpsz||yz~xy}wuusqv}}|spqqsv{}{x}~|y|vppstttz{}wtqqszzy}}z{}}~|vz}xwwusx}|}wuuuuvz}{zv|}}}{uvwwvtx}~zvrpqv}~zyx{|vvvsqt}|ztppqqrw}~y}~~{y}wqqrsssv~|yvsqrv~~x|}{||~|vy{vvwurt}~|xystutux}}yz~|}wvwwwuv}}|yxtqpszy{zy}xuvurqxw~zpnopqty~{}y}|tqrsstuz{wwusrtz}y~~{{}yvzxtuvsrvwxuqrtuw{{||~{vvvwwxy}~wvvsqrv}z{y{{vtutrrzu}xnmnpsw|{{y}{srrstvx|xsuustw}y}|{|~~yvzvrttsrvxuuppsvz||}{vvvwy||~||tqttsuy}zzz{vstuttzuv~ypkmqv{~~||w{|urrsvy}|z{pptvw{|y|{}{uxwqqsttv}{sx}xqosx~~~~|{}wvvxz~{|x{tmptvx|{}zy{vrsuvvzvsyxqmmrx~~}|wz|urstw{~yw|ontx{~~z}{}~zuxvpqsuvx}yswyvrqtz~{{{vvwy|z~yt{~rkpux{|}~yzztrtvwx||ttvtqnotz~}yw|ztstvy~}{txwlovz}~{||}|vwxroqsuvz|uuwvurrv|}z~xwwxz~z}~zsuxlkrwz}|zz|ursuvwz~vvusspnqw|~{w{{ustuw{~|tv{omsy|}||{vwyroprsux}|vwustsqsx~}{~xwxxy|{~~~~yruymkqvx{}}y{|urstuvy~|xyuqsqnoty~~zy}zutttvy~zsx{plrwz}}|zvyyropqrsv{zwyupstqquz~|~yxxwx{}~~~~~}xqu~zpjntwy|}y||vrsttuw||y{voqspnqv{~|y}{vuutuw}~{tu~}umnty{~}zvxztpopqrtx}|wyxpotsqrv||zxxxxy}}}}}}zspx|vnkouy{}yy}ysrsstvy~yz|rmqtqpsx}~z{~yvuuuvz~xrv}zsmnuz~|xuyyroopqsvz~~~yvzwnntustx~}yxyyy{~}|||xqpwyuokov{~|xy}xsrstuw{~~xy|skottsuy~~{{}yuvvwx{~~~|wqsyyupnt{}xtwysooprtw{~~}~ytyypkqwxxz|zxyz{|~|{zzxqmrvurnnu|~xv{ztrstvy|~~wv{wmksxxx{}|z}zvvwxz}~}|{wppvwutqs{ysuytppqsux|~z}xswzrkox||}}~zxy{|~}{zyyvplqtssqpu}wuzytrsuwz}||uu{wmksz|{}|~{{}yuvxz{~~}{{ytnqvuuusu|wsvxspprtvz}~}z~{usyyqkpy~~~~}~yxz|}~|zyxwsmmsrrtsrw|uwzvsstvx{~{~xty{slmv|}|~|}|{~|wvwy{}}|{zvooutsvvtw{tuxuqpqsux{}~z~|vsx{tmlt|~~~}zxy{|~~}{yxwsmmsrpstrsz~vwzwsrstvz}}~xtz|tmlrz|{|}|{||wvwxz|}|{yuoqvsquvtu{|uvxtppqqswz|~{|utzzsmkrz}|}~}yxyz{}~}{zywrmotportrqw~wyzvsrssux|~~wv{|tnkpx{{{}~{z|}wvwxy{~}|{ytoqvspsvuswwwytqppqruy{}~|vtz{unknv||{}}{~zxyz{|~~}{zywsmotqmpttqszzxzwsrsstwz}~yuz}wqmmsz|{|~|xzzwwxxz}}~}|{zvpoutppuwuty|wyvrqqqqtwz|~~ytv{xrnlpx}}}{z~zyyz|~}~}|{yxuplpsolpuusu|zzyursru|xxvuvtrrqrromorx|~{sosv{{wwz|zsichpsqrxy}ywz~vqnkr~}~|tsyystyyx}zzzvtz|roppmoprwz}onts{~ursuxvs}|wqiu}zwy|vux}zpuopy|zrruyxqmys~z{z|}~x|}plvtongmchov{zy{|~z|sxootx~}v}zyv~~{xvrtrmqkmvrnxz~~z~yxootyzsz{zvzy~stpdjrw{y{xwprrqimrnuzpopny~{{xw~|~}{pjonr~||{y{xr{~{{x|s{}vtpnpswz~|}{y{~~{}}yvtqppov{rr}~~y||wsqlu{}{x|z~|}|tjptt}|uvpsxutvslstpvwoz{|}y}z}zvsimww~yyxw|u}w{~~|~~zx}{ytuwuow~}~}z|~~|z{y|yz}~yxzwnnusx~xz|y~y}w|umyxty|{vyzw~wztruontv{}rz{qqqoqvkluqktxx{~~~z}{~y|wtnlrkkz~y~}|y||x{x}~}xzstzvuw}x~|wvx~{}~|||}{{zzxv{rs{wy{}|}|{zszymmrpsv{{zyzuyrowrv{usxtsstprsvwomvy{|{{{}~}~}zxqooqtpq{x}|~~y{{wyrrrrx}y}v|}y}}t~}|~xwustxns{ysz||}~|zprxwvsv|z}}z|oltpiox|{y~}xvyxwnpqrwvrwrx}~~yyy}{}ywlilnw|}}|}}{}t|yrwuttwsu|z~|||}v~~~{}ux{}||zvyz|yrv~wyxxzupskrzw|{|{zukiryzztr|uus{wszwt}}yzz{}{|u}~x~|r{urnsypxxy{yw~zrxy}~u|~{tz{wqt{{|||~yx{{zzy}ywzw}}{tv||vz|}uw{qvwouyxxu}~}|sqvopqpsv}puwqsypnzxtp|yzzw|}||}}~v{vtniqnwnw{xx~|}|}v~~y{wysv|{v~|{{{{~|yz~spvwrswuyz|zu|{~z}|{qy}yxtrouryy}~||{}xyyvzrq~z{tsvwy{{{w{|r{wvv~utv}~|~{wxuuqlrojvw~y{z~yz~|~{{z|{zty{}x}~}~~}z|}zu}tsuosy}szxytyww}y}syzzutqu~y|~~zvywurix}vw~v||x~uxuzzw{o|vzu~{||~~||vyxtvmlqnq~z}~~|v}{yzz}|wzuvyw|y{{~zy}~|txusuy~wxwxy{~w{zqvuwytyuw}}}}|{{yznp}y|yu~~}z|w{{{zx}wzwv|xtu{zx|y}{~wwwlkpguvrxy}zx~||y|}{{t{z{sx}~{}|~z~xwxtsuxtux~|zy{yzzw|vz{xxsxsrzz~|~~xr|{uupt~}~y~}|vz~vxwp~sss~{u~y|}z{|tssjstjjlx~|~{~{{zx|||vszxu}w}z|{~|x{yz{y|utu|wx}|y~{xzz}uyuyrptuvt}~|~{||t~{ztswruv~}y}}wwxtusty}~s{}}{wuwosxkjfoupz~|~~|{~yv|wtwrv|zz}z|~~z||~z{vxu{vqwy|{w{xywvzxsxuyrvx{}}}|y~|zrwtnpryu|}~}x{vyvts{{{xv~~}z}z{xs|unlmspow}~y~{~t{{wwroqnv~w{|{zy~wzxzyz~~yoxsuz|y{~}{~ysvvtnuuzzx~|}s~~wxxqvpqumtxxy~|y{w~u}tyyy}w~vyzz{~~~~~wx{zytsqsppx|sxy~x}~}{wxorp|tpsz}~uyv~wtw|zvnw}wwrpz~}{}}{|}~yt|vwqwus|~wy}|xy{uospumuq{z}w{wxq||wtw}xzw|~{yywqoyriv|qsor}~~{y|xyxwry{uq}~|{|{}uz~xttwpswqpxwzxz|rvwwsx{u}}yx}z~vu~vrwurtmqytx}y|}x{{|vt~zuw~~}~|w~t{|sxvv|zs{wns{y~{|{~~}rxzzvxsm{uy}}~|y|~ww|wunuurnmv}}}{}~}~{uw}}yt{zz}|}}z}v{|rn|wknnlsxt||z~}zy}wwzx{~v|~}|~v|~x}}wu{uvumsvsx|{}uz{svqvyu||uu}}}~y~wx|vztnuplr|rr~}zy}}xx~yxvw}w}z~yz}yw~~z||xms|rtwtt{y|w{y~xrz}qsrq{}z~~{v~}yxu|wtqrxvpux|zzw~|wxvvxx}zu}~{~|rytqqkptpr{zx~yx|}{zz}z~uu|yux|t|}~|x{vxsxuoqtqvrv{{~{wzx~}z~xs|x}}|{}}{{zvyursvkmxumqp{~|}{~y~{y|w|wy{yy~{}x||~|txxwxvrrvoutw|}~}~yzvutpr|zuvzz}|~y~zyxvzxptvuunvywv||}{{y{x{xtwxx}y~{{~w}zvupx|wty|wyqlw}z}~{|{splmlhookhlkr}~|||zuw{{~ylnq|~jfmrvy}{}yv|tu~s|xsnpsuyxqt{|sxwwwu}p|yuvxupz|pj|rlqsuqu~|{}~~|wz|wyuwrvtqywyrw}v{srvwqluz|~vrwzvvyy{xt}}~x|v|zxz}fnzvmwxysqyx~}zx~xqrumnrjnmcordkdqtnosvvr~z|~|}{st{lxz~v{{uy|z|{{{|~}y{{osnkrmhjhzsjnqsvsz~wu{x|rv}ymlkuylvz}~y|~{|y|zvw||x~~x~txvknhfjjecewidooquv}}{uzyqur~{y}z~}|{{x{|x~{y{}{|xv}~v|vssnltrlgr{fmvqtvx{|~y~xyxowxposp|r~~r~~||}{{zpt}|x~uqwoplfiujeevmbssquvz~zwu}yuyv|{{{~yw{}~y|{~|y~ur~vzvwvolywjktzfnwqvwv|~{~|}~v{xouurtts}v~~t~{~zww~vmu|yusrrtmgnxfemviesouwt|~}|vwzxyyv|~x{}z|~w~}~wu|ot~yvx}uor~mitxqgrquytx{z}{yw|tqstvury|{|z|}~~~xs{{mo{|~z}|wqszrkkylbqsoemnsysu}~~{{wvy|xv||{}|}{~|||z{vr||oq{|}~tzxqs~ohzurjmovyru|yyyz~|xsptytsw|}~}{~~}vr{zloww|w{oy}uloxkexpnihmvxqt}|}}~xsy~xwz|~|z}~~~~~~yy~upy{opwx|t||st|si}vppinxzrr}~}xz|z{}||umsztsvz|}{~}{wqwzpnrtyx{rv{povrfvvjpgiuysp}~~z|~}rt~{w{|~}x|~~||~{|x{xprytort{~||wzsyxososoht{wqw~|wz~wy}z}|plvyswv~~|}~{{~z{{sqvupppt{xq{{ptupkzsjrflyxrqzy|~~{ouzz|~~||w~|~|z~xtyuprusqqr|}|y{tyvsuprrix{xrw{u{~|x~z|x|}qjuyuzx}}|~|}z|~uxyspssrqot|{ty}}sttrpwyjrljyxuq}yw~z~|~qp|}|~}~z~v{~~u||pvtporrttq{xvyvww|znvmr|yvs~uv|z~w|zwx}xknyx|{z~~|~{~vzqvrppqrusr}}yxz}xsvruvzrmtkvywrx|u|~~~z~vnw}~~y|~x}x~}}xtoqspoppuxsz|~~xywv|z|qttr|yws~ut}y|y}|{ww|znmwy}y{~{|{|s{ntppporwut~}{{y}yuvrxyxtmtqxxwsz{u~}}z~vpw|~|w|{x}wy|ut~mppopnpwyt~~y{vzzzrtvxzyuw~sw~yz}y|{wy}wnpv|}{}|z|~{~|{|stppoqnnuysz}|z|wxtvxwsovywwtv~vx}{{~{}|ssx~}~zyx{x}{|~~~~u{p|tmonpnlszuz}}|}yz~xyqx}xxvv}uu}vxy~xy|yqorz|}~zzy|zw}t}wooornkrxtxy{|yzvy{twow~vwsw}vxx{||~|tsu}}{xxyx|~~{~}tytyvomnpmjrxuyx}~z~v{rzwxty}~ux~~twx~y{|xrppz}||zyzz|wzxzyrnornjpwtxv~||z|x|syrvvvrw|}xyv}x}~|vtr{}|{xxx{~x|uuxvvrlmpminvvyv~}|wzxu{xtw}|yxzu~s|{{|ytrot|~zxyy}{|{vyzvwonqpjktvyvx}y}{{ytzqvut}{|y~{}zxz~~}yvst{yxw|~{zv~ztszruqknpkiqvyzw|~}v|x{zxs{}}~y{{zu|tz{|{xusox}|~}zxz}x{zrzyrxomqojltx}v||x}{~ztzuxtu}{~|{y}zyx}~|yxtu{{yw}y~yt|zto{qrtknolipv|}y{|~yy|{ysz}|~{}{w~x{}uuz||xvvqv||}{xy{yv{sr}pusmpplkrx||wz|}ux{|}vs|||~}|yz{~~ww}~zzyu}{~{wz}{}suyxmtyltpmpoknu||y~w|~~uw~|}||}|u{zzzzsv|}zwxuu|}}{}zx|}zt{zvm|sovooqnlqw}wx|~yu|~zr{}||{}xy|}||zt{{{{w}}z|wz~z|~}ss}xxmqylqrnpolou~y}x{ux}||z{yuyz|wzst|}zxyvxz~{~~zz}}xt|{{tn{rouqqpmmryvy|yv{xu}||zz}vzyz{zs|~z|zz}}}|~|x~|~}|{xrvzxukvulrrppmmqwz{~~x{}u}||yy~{txwzvzqw}|xyyxzy}{~~z~}}||uw|zxnvxnqurqnmpuzx{~w|z~u|||yx~}vyw~v}ux~z{{{}z~|~~z~{~{xzsu|yxvnsvnntrpmlot}{}z{x}||xx~ztxu|t{tu}{xyyzyw|}}|||y{t}z}vqoos}z}|x~~y}~xqy}xz}x{{zwx}~~|q}}zz}|vx}y}~}{wxqy~{~xyustx~}lrwtpuvyzvzyyuv~~z~zz|~|xwux{z|xy~y}wotwqstuow{upopt}|y{}}||}zprz{{~||~~{z~xuttvoutrzqovqvuuxx{~|xy~~~s|}vsv~|}{xz{{zppw}v|zqtruttqns~{txuz|x{z}zwvwzs|}{~}}uqs|{zxqzzrmmnuxyyy||y}zu|z}wrs{~}~{}}xutxtyw}zu{rtuqvqtwy|}zwxz~r{{uuz}}}{yz}{y|yyrsx~~xsvxutpns~zuyt|~x~~y{yxwzzt|}~~}~~x{trv~~{}ty}uonouwxzy||~}{z|rzz}ytuz~{{|{zvutzwzx{vyvvwqvrswz~~{ww|~xtv}~{vv{}|~z{|}s||w{uvy~}us|xvroqz~uyvwzx|wxwwy~x~z}}vx{zvswy}{u||sppsxuz}|}|}~z{ur~z|}wwyy|{{vtttz|z|x{uwyzrvtqvz}xwyw}yty|{ww|}|{|qw{v{xx{|yp}|wsoqxwyvv}v}vuvwzz{}}~xvxyutxz|~x{~vrquwu{}}}{{~|yvr|y||xyzy{{~|{srsu|{}}y~uv}{sxsrv{|ww{xzxvw{yvx~}}|~}|pxww{wy|{xr}xros{}xztz}v~~usvw|}z|{~|}{xuwwst{|~z|}vssxsz}~}}|z{zx}swyy~yxy}}w}z}~|~vprty{x}}|s|vwvqtyywx|ywzwxyvv{~}}~{~vvvuzvvx~}s||upryy{ux~yyyrtw{|x~y{~~x|wuvrrx~{zvuxsy}~}{xy~}wzwxxy}xww{}y}{rrtxur}{||nv|rwyz|~xwy~yt}yxzyzws}zxywxtw{vwy~yznn{|{|u|}yw~xorw}zy}}ww}xxzyqzvrrprw|~~}}yqrz{~~}zxy~~vwz{vz{z{qo~yxpvzy{xtywllpvsr{x|w{uuuw}{yz}s~~yyy{}tt}{vrrwyqmrtlntoopu|{x|zqnuw{}{xw|~{wvwzyw{ywxjpvnkjlpuwwu~|vxutxw~{z}~x|}{zy|upv~zyyztrvphkqrmuvs{~u}zxxux~~|ywzz{wvwuz|}xx{yolvsippmnqw||x|yrqxz~}yy~wx{wzx|~y|xjrvnijmrwwuu}uxxxyw{z~v|{{}yvyurx{z{wrurjkrrnxvt{~}x|z{yvyzvzx{vrwx||~yxzzqjwvioqopsx}|~xy{utz|}~y}~~yvww|z}yz{mnwrjhlsxyxu}~uuz{}x}z}ywy}y{tuxuy~|yz{tquojosox{vz~{|zzx|z{~}v|xy~~}{psz~zwxzwln{olqpqtz~z||vyzx{~~|{{xxwuu~~{x}rlswnhkrx{zw|~}zsy~{{|~v{{tzurwx{}yz|xqssmospx}wz~zy||v{}|}yzyw}~z}|qqz{xxyxnnzrnqprv{~wz~vwzz}}|~yuvzsszz}snswohlsy{yw}{~}~zsz{|}}x}~wszuquy|~{|~yqsuoqqq|zx{~}xx|t}}~{|wx|z|ypr}|{yzzvnuxqqpqty~}}|u~}uvz|}~~~}|vr{znx}zysqvvljpwzyvzzx}wu~{~~z}~{qxzqpuy}~xrywprsuwy{}|yx{|{|uz}|~usy|}zv~|swsoporv{}|}~|{x}~~zwx|}|}}}~{{zvr~wiciqttswtt~|~yvuvz~wx{~||ytw|qhitporsuvx{~}yy{}}wvyw~}vz~{zz{uppt}rnoruurqu|~{zu}~x|}z|zvw|zz}vqtryyvwvspsnmy||}}|}{|z|zussux}|~~~ysrv}{w~ystssrtvyzwt~||~}xtyvz~|}}x~vqoorx{z{}|xtty|y}|twyv~~{|}~}xwyz~~~~ypsqs|~}}}{vwunt{{|}}|}|~}{~~yvttuy~}wssx}z}ywwusstwyyuu|}vwxu|~~z{|uqoor{||~~yuvz{x~ztw~yu}}z}{}xwzz~}}~wpspt~~~|xzwov{{|}~|{}~~}z}~yvttuy}}~wssx~}}{yyxustvyyvu}~wwyu|}z{uqooq{{~|xwz{x}}uv{}ux{{{|~zxz{~}~ypsrs}}|z{{rt{{|~~{|}|}z{}~zwutux~~~~z~xssw~{}yzzxutuxywu{|~yvzw{z~zuqoorzzvw{{rowzxsrvqqyvqopu{}}~}ynr||yyzy{|rsxxy{}~||{}yz|~~|xrnr~wlszyzz~y~xvxwurqtwxwv}~|{~yx}w|~|rr}xlfdfq{x}xz~}zy{yv|zuvx|}wsxzxx|{}|szzvsjfjgku{yx{z{zty{z{||zuty}zy~~~}{zxxxvqqw}xwnournlnrx}{||zvrpponox~~z{ytxszxttutsrqpprz|vrnm}}yxzysu{|wvxru~~||~}wvvs}yz}|~{y}yzzy{zzwty{qy}xyz{{|||{{qx~snqsprz|yz|uy|z{||smmsy{}zu}xy|{}{rs~|{~wmxzwwxwvtspmkq||||xqluz{}|z{}xy{~~zqkmt~wu{~{wvwunmokpz}~~zuxurt{w~}|{zz{}zrnx~}wyyutqiowxyyyyz|~~|}~{vqoptx}~z~~y{xuw~uw|vtvxxxvtqnlqzyy|}vqxzz~}|}zuxzsoorx|}zzysqsmkqvxvwz}|wuw{~~}~~~~~}{|tlpuv}}{zqqxxvuttux}}ysmjkou}~vvuppu{~ywutrrqqpnpy}{~|{xru|{}~z{}{||}zwsqruz|~}zy{ytwvpquxyuu}z~~~~yx{~{~~}tntwy|}}|sv|{yxvutvy|zw{uqoprw~~z}xyxsru|}{yvsqpomkmx||~yqsxvz~|~z}}|xsnlmqw}}xvvuqvytuy||zx|}}zx|{ww{~~}}}~~|zsnuy{zvx{{tu}~}|{zyz}yz}z||zvsqruy~z~~vvyvtw}~}zwussrpqx}z~~|urxxw|}|z~~}zzwsollns|~{zytuysquy{|zz}{vzzvuw|~~zxvqkqy{yz~vsy{zzzzz{~|x~xwwvsqrtw||zxqsxxx{}z{zxutsttssz~z~||xw|{{}~}{~z|zzxuqoorw}|zuxzsrtvxxxx~{y~}zy{~~}|zxrnuxyz{vv{zyyxxxz||{zzxurrrtz~}wquwutx}{z|zvsqqstty~~~~}yswyw{~{z}}}yvuspnmnqx~~~}yz{vwyz{{yy~zu{~{yz|{|~{zytnruty~z}{~~}}{{}|w|zzywtrqru~xqvxussv{~~zwuuutx}{y}|~~~ysuvru{~~|{wtroljikr~xy}xuvwxyzzx{xtz|yxxz~}}yxwqnrsrx~~}}~~~}||}yv~zxwwwvtssx~{{vsz{xvvwz~{xwwvvz~xx~~zx~{wzxtvz}}~~~|{xwtqnllq|y{~xuuuuvxyxy|x~}{yyz}}~~~~~|ztprqmqw{~~~~}||~|z{yxwvtrpt|||zswzwussuy~~}yxxxy}}z{|~~{vxxrswz{|~~{wtrpnljmu|{}zyyyxy{zwxuy~~|zyyz}}~~~{zvpqrmlpuy|~}y|~}~zxwvtrppu|zz|y}urwxusqqsx}}{yyzz{{|~}|wtxuqswz|~~}~ztqomljjnx}|~~}yyyxxy{zvv~|tx}}{{z{}~}~~ywtprtposx|~|y||y|~{xwvvussx}}~}|wyxsxywuuuvz~{zyy~{y~~|}yvzystwz|}{~~xspomlknv}{|~yyyyy{}~{x|xw|}|{zz{~}}~}~~|xtoosqnqw{~}~{z~yvtssrrv}~|zw|{qsvutttvy~~~}|||~~~{wtxytsvy{|}~wy|xrnllkkmu|z}|{|{z{|~|wxywz|{zzyz}~}}|vrmnrpmpuz}{zw|~yurqqqqv}}|~~~~yuzxqrtsssuwz~|{|~|~}}yutyxtux{}~}~~{tvzvpmlllmqz}{|||{zz|}zvyyz||||||}~~~{}|wqnstppsw{~{|wx~zvtsssu|~z}~}}~|xx~wuvuutuuw{~|||}wtxyusuwy{}}wuz{uplkkknuyw{}}~}|}~~zy|{||{{zzz||~~~|~xpmqrnmosvy}}~xv|{vsrrrtz{w{~~}yx|{wwvutssstw{~}}~~}vtxxsrstvx{}~~vsxzvplkjjnttrx~||~~}zz~~|}}{zzyxxz}~|{|}xomqpkkmorv{|xuz~{vrqqquzyvz~~~~}zxzywywusssstvy|{|}~wtyytsstvxz}}~wruzxrnlkloutqv}~}|}~}{{|||}|{{zz{|}{z}|}torsnlmnpsw{}}|vw}{vsrruzzuv}~}~}{{yx{{xutssstvxxw~}uwzvrrssuwz|~~~~vsvzxqmkjlqtqpw}~~|z~|zzzyyz{{xz}{~{qnrplklnosw{~~|vw|~{urqruzxtw|~~~~}|zuu{{wtsrrrsvwww~|uwxtqqrstwz|~}~vrswwrljikptppw}{w{|zyyyyz||y{}yz}|snqplkmoqtx|~xux||wsqqtz{vw}~}}}}||zuqw|yussstuxzyx~}ywywtsttuwz}~{ysqswvpljkpvtqv}~wx|zzzz{||zy~}{x{}wpqqnlmoqsw{~{ww{}zurqty{vv|~~~~}}}}vpu}{vsrssuwyyx}~~zwyvsrstuwz|}|ytpquuplikpusqv|xx~}~~~}yy}|~~yru{y|{x{zwvxxwx{|~|}{xy~}{|{{~~~~~uy~~~~~~||}{sox|{{|}~}||rz}}zy}|~~|zzzyxyzz{}zx{}~~yusvzzxuvz~vv}wuuuvwxy||vry~~~{y}~{{}{urw||}~|{}~|xy~zuuz{xusrtxzxvvy~~zz}~~~~~|vu~|v{|vrv~~|{{||zxwz{z{{xvwwwwwxxxy{|xwxy~~|z{~|||vt~~|zyyzzuqu}~{{{||}|yw{~{xxyyvrtz}|}~zyxx}~zwussvyxvux|{xy|}}}}}}~~|vsz|y}}||zvrs{~|ywxyz{yvvvwwwxyyyz|{yyyxz~|zz|}||vsy}|}|vru{~}{zz{{|{xx|~{xxyzxtsw}~~}|}~~{zzxy|ywutuxxwvx{zxy{|}}}}}~~}xtw~~z|}||yurv}}{xwwy{zwvvwxxyyzz{||yz{yx{~|z{}~}~~ytu|~~~zuux|}|{zz{||zy{~}zyyzywttx~~}}}~|zzywy|zxvuvyyxxz|zy{|}}}}}|}}{vtx}}{~|{zywtsv}~|yxx{}{xwxxxyyzzz{|zxyzyxz~||~~}{wtu|{wwz|~}|{{{|{zy{}|yxxxxwttx~~~~~~~~~}ywxxwy~~|zxwwyzyy{{zy{||||||{{{zwux}~}~~{yxwvutw~}{yxz}|yxxxyyyyyzz{ywxzzyz~}|}}{zyxuw|}zyy{}}|{{{|{zyz}|zxxxxxwvy~}~~~~~}}|yvvxyz~}|zxwxzzz{|{yz||||||{zzzxwy}}~~~{xwvvvvx}~|zxz}}{yyyzzzzzzz{ywwyzz{~~}}~}|zxwwvw|~{zz{}~~}||||{z{||zxwwwwwwy}~~~~~}|{zwtuwxz~~}|zyy{{||}{z{|||||{zyyxxxy|~~~|ywuuuuvy~~{z{}}{zzzzzzzzzzzxvvxyz{~~}~}|zxwvuvx|}|||}~~}|||{zz{|{yxwwwwwxz~~~~~~~~}}|{zxutuwxz~}{zz{||}|{{|||||{zzyxxxxz}~}~~|zxvutuuwz}{{|~|{zzzzzzzzzzywwxyzz{~}~~~~}|zywvuuwy}~}}}~~}||||{z{|{zyxwwwwwx{~~~~~~~~}||{ywuuvwxz}|{|}}}|{{||||||{zzyxwxyz|~~~~}{ywvuttuw{}|}~}|{{{{{{{{{{zxxxyyyy|~~~~~}|{yxvutuvx|~~~~}}||{{||{zyyxwwwwwy}~~~~}}||{ywuuvvvwz~}}~~~}|||}}|||{zzyxwxxxz}~}~~|{yxvutttuw|~~}||{{{{{{{{zyxxyyxxy|~~~~~}|{yxvuttuvx}~}}|{{|||{zyyxxwwwxz~~~~}}}||{{ywvuuuuvw{~~~|||}||||{{zyxxxxxxz}~~}}}|{zxwvutttux}~}|||||{{{|{zyyyyyxxz}~~~~~~}|{zywvttuuvy}~}}|||||{{zyyxxxwwy{~~~}}}}||{zywvuuuuvx{~}}}}}}||{{zyxxxxxy{~~}}}}|zywvuttttvy}~}}||||||{{yyxxxxxyz|~~~~}|{zyxvutttuvy}~~}||}||{zyxxwwwxy{}~}}}|{{zyxvutttuvx{~}}}}}||{{zyxwwxyy{~~~}|||{yxvutssstvy}~}}||||{{{zxxxxxyy{}~}}}|{zyxwutsstvxz~~}}||||{zyxxwwxxz{~~~}||{{zyxwuttttuwy|~}}}}|||{zzyxwxxyz{~~}||{{zywvutsssuwz}~}}}|||{{zyxxxxyz{}~}}}||{zyxvutsstvxz~~}}}||{zyxxwwxyz|~~~~}||{zzyxvutsstuwy|~~}}}||{zzyxwxyyz|~~~}|{{zyxwutssssuwz~~~}}|||{zyxxxxxyz{}~}||{{zyxvutssstvx{~~}}}|{{zyxxwwxyz|~~~}||{zzyxwuttsstuwy|~~}}||{zyxxxxyyz|~~~}|{{zyxwvutssstuxz~~}}}||{zyyxxxxyz{}~}||{{zyxwvutsstuvx{~~}}|{{zyxxxxxy{|~~~}}|{{zyxwvuttsstuwy}~~}}||{zyxxxxyy{|~}||{{zyxwuttssstvxz~~~}}}|{zzyyxxyyz|~~}}||{zyyxvutsssttvx{~~}|{{zyxxxxyyz|~~~}||{zzyxvuutssstuwz}~~}|{{zyyxxxxy{}~~}||{zyxwvutsssstvxz~~~}||{zzyyxxyyz|~~~}}|{zzyxwvutsssstvx|~}}|{{zyxxxxyy{|~~}}|{zzyxwvuttssstuwz}~~}|{yyxxxxzy{z}|~zywwvwxy{uspnqvx}~|w{{ss~yyxsuz~zytw|vsptkmptmr|wvq}~}~z{{{{{~xy~}z~u}}~~}z|yrxpmsslhlwwqw{}{x|uv||zwutxzxw~w}ssu|}zxwvusyzlv|zwt~~{~wusu{twxz{t{rwmpmwvsoqnvz}zz{{}x{~}{|{sx|w~z}{{{~}|zzsvozloolxxvv}y{~wnxtvxu{~yqz}z~|}o{|w{xzznywx~w|}wy}xn{qhmmmyyw{y|z}w~{r{}~|y}|z|}|{~swuvvsz}pn}wneinsmvs{yv|t{}qzzr{|~yzqzynt||{}v{ruomwrnxnoxrsxsvzz{yzyzyxos}|{q|z}~pouv|yqvtwzknsgukkr}y~{}|{ux}zyt{u|}~}}mnq}z|u{tsgvfxgjhszy~z}}vvw{yr~|y~z|}}}|nposyv~v|nqllkpms|zzs~wr~{{{ryxxzy~~~wrnspv}||yv}urtmrtrny~~~wt}}zp{xv|xww{~x}wskskrxxzuumztsurx|z|~zo{y}~s}|u{|v~~w{zy~~}~~~xqktqpvzyyxvq{trupuyx{}m}~z~v}ysy|x}~zvz~||||qkpopw|zutnzsotswwwyp|{|v~|svzwyzw|{}}zwmrqqx~|zutp{vmsrwyzz~ux}}~wy}srxxy|w|~}|z||}oonov||usow|orswz|yzyt~|}yvvsvww~}}zy~x{roplsxzvrusqruy{|z~|p~~~~r}ztvyx}{v~xz{~{|vmpknu}~xsp}{qttxx|z{~rx||~}wywuwvxz{|{{z}y|{nnnjsx|vqwqrrww{|{}xo}~r{yuwyv~{yz~{yy|tjpkqv~~ysr}yqtvwwz|{sx||}yv}utxww}|}}|||x{|mlomuy|wtvsquxyy}|~}o~}|}~sywtu|w|~y|||xx|vhmmpy}yts|qswzxzz{~xr}||{~{uzvsw|x~z~|}|z|uw|shnkt|xuv|psyzzz{z~tv|~||}xuzvsz}|~{z}~|yxsvzninny~usxzpvyyzzzz}r|~|}||}vx{vs}|}x|{~{z~trwxkkmp}|su{tqvyy|{z{xs|~~}}ytzyuwz~~y~{}w}zqsxrhnowwrv~rsyxz{{{|szz~}}|vuyxt|}~}{{|~|v}tquxmkoq|ttzxqxzx|}{{xqz|}|}yswyvxz~y~{|uzyprwqinpxxsysv{xz|}{{qz~y||||uuxxu~}~y}}~zt{rpvvkkot}ux}ws{{x||{|vsy{}||xtxxvz|}z~|~szuotxninr{wv}|ry}xz||{xq{y~||ztwxwx{x}{vvworyrilpwzu|sv}yz||zzq|}x}}{{vvxwv|y}~}ytyppwukkou}u|vu~yy|}zzsx~x}~{|vuxwu}}~z{~~}~{tzqpwwkjnr|v|yt}zy|~zysww{||wuwxu||||{|}|tzrovxmimqzw|{t~zy|~{ztu~xz||xuwxu||z~z{|}uytouyoilpvy{~u}|x{|yvs~zx}{{uvxvy~xy{|~vwworyrikps~}yv{}xz~~ywq~~|w~|{vuwww~xz~||yuxpqxukinq|~yzxyy|zxsx}xz||xuvwv}{z}{{z}sxsouyoimpv|}w|y{~yus}{w~{{vuwwz~xyz||wuvoqxtjjpr~~zzzyz}zwr{~|w{{|xvvwwz|||~{y|swqouxninqx|w|z{~xut|{w~{{wuwwzyyz~{{wtunpwukjqrz|zyz|{wsx~{xy|{yvuxx~||~zyx{suqmsxrjnrw}}y}}y{~yvs}{{v|{{wuvxzz{}|{~}wyruonuxojqs{|y|z{xutz{u~~zzwuwy}{z~z}}~yxwrtmovvnltu{zz{|}wtvzzv}zzvuxz}yx}}vyssqlqxtmovx{|}y{~zvs|{{vyz{xvvy|}||~|y}~}~{uxqsnmtxrmtw~|z{z{xtty{t~~zzwuwz}z}x|{~{~vwtqrlpwwopxx{~|z{}{vsy{{ww{{yvvy|~}||x||||ztwqsnmtysnvx}|{{{xtux{t~~zzwux{~z}w}{~{|uwtrplqxvprxy}~z{~zut{y{vxz{yvvz~~{}}zy{}{{wuvqrmnvxrpxw}~z||}vtx{zzt|{zwvy}~~{}w~|||zztvrsnmtxtovw~~~{{{xtvx|s~~z{xuw|~z}w|}z}zzuvtspmrxvptwy}{{yuuw}t|z|yuw{}z}w{}z}zzuuuspmqwvprww~|{zuu~w}uzz|yvvz}{|x{~z}zyvuutqmqwvpqvu~||{vu}w}vx{|zvuy~}{}yz~~z}{ywuuuqnpvwqpvs||~}vuzy{zu||{wux~}~}}{y}~{||xxuuurnouwsour}|}|wux|x}s~{|xuv}|~|~~w|~{{}xxvtvtonswuptsx}}}{yuww~u|{}zuu{}~~{}x{}|z}zwwtuvqnqvwrrus}|~}vv{y|zu|}|wux~}~~}{x|}z{|wxvtwsootwtpur|~}}|yvx~w~t~|}yuv||~~|}wz|{z}yvwsvvpnqwwrsut~~|}vv{x|zu|}|wux~~~~}{w{{zz|vwurwsnotwvrvs|~|wuxzzwy|x{xtu{xu~}yttknpqqutqurtwqoqvxuuvo||uuuptqqspmpyzy|}|t~~wzzzz~yy}x~yz~|sl|{}|}{~{ros~|z~~}yw{tp~vwoknpoqtoqrnusnoswvqrnvxljsz}utqmlszvzy~|}}|yytzzz|}|z}|}zz}}vonj~{~||zyw}ypmr|~{|}usr}wnppppsonsmrtnnpuuqqqu}||riku~{xspt{xzz||{|zyyvz|y{||~y}}x{utuvrmon~~~}zzw|zqnr~}{{}|zsts|qrrrqtqnunswporvvssswyyzqilv|vsv}y{{}|{{yyyw|{yz{{~~x}|wyttuuqnoo~|zyw}ypot}{}{|xttu{rsrrsuppvnvwppswvstrz~~wzxnjny|vtz}x}yz{yz~xyvyzz{|~{wxzwttvuponr}~y{wy}voqx~}{}z~utrywtsrsutmurozspruxvtsrzx{tlkq}{vw~y|{z}}zzxzyvx|zzz{}~w}}v{vuvwtpnk|{{yx~yqpt|~|}~zxvrs|wvstuvntvmzwqruxwuso{}y}xokow|yx~zz|w|~yzw|{wv{zy{}x{uwvwxvsoit~~{{w}|tprx||}|~~z}{wspyzxuuvxrpzov{sstwxwunt~y||slmqx{}y}~y~x|~{{yxy{zuz|{yz|~|vzxzwwxxusklz|{}xz~yrruz{~}~|yywqr~|xvwxxpvxn|ystvxxwsny|}x}zqmnsy|~{|{v~}}yyw|}|xu||{yz|~zwv|ywwxxurjo{{z}w{~xrrvz|}}{y~xvqt|yvxyxoy|w}x|}y}|}uruzytj~{mifnvpqonv}|z|~~ur{zzzzwskq|~vwzmplptqlmqw|~xzxvt}|utqt|ret}vlklpqqlit{y~}~|v{ztpsyutm~tlhjtponls~}|{xwwwts{zz{zyrtvt{yuolrpkjntz|~|yy}z}ytzxxwwwrommsyspqsrqip{zz~{~yy|ytu{zwotx{xrnuvpqoq{~~{wtqrtxv~~|{|||yt}vyvx~~{ruuqmotz~~~z{zv}zvv{yzy}wpklnrt|ywxwwpp|~{~y|~zw{zvu{{uupt|{}wrw{tuss{~~}wuqqtxx||z{|}{u{sxxz~uvxtpqv{~yzzv~zvu~{y{z|voklptv}zxyyxqq}|~~x{~{w{zww|zuupv~|~xsy{uvtu}}}{vtpqtxw}~|{|}~{vysxx{~uxxtqsw|}}yzxw}zuw{{{{zunkmquw{z{{yqu~~{|~~w|~~yx|zwx}xutpy|}~xu}zvwtw~zyvrpquwv}}|}~xyuvyx|}wzytrux~|{yzu{z~yv}z}{}ysmlnrs}||||vq{~|{{yx~}x||ywz~}xvqs{w}x|yzwv{~}{|wuqqsuuy}}{vzuywy}z||xttw{z}zzvw~w|x|{}|{{wpmnppuzry}{yz}|x}|}~z|}{xy|{xrrys{}||ywz}~y}xvrqstst~}u}~wzyxz~~{wvwyz{{{wu|wxz|}}}z~~zsoopoo}uw}{yz|z~~w|}|}~~|~}zxz}~zvqwur~~yy~{x}zxtqrtsqz|yxyyzxxz{xxx~w}{yty{s|~{}||}xroppns|u|}{x|y{}{x}|||}~}zz{}zsrxru~z{{w~ywsqrtsr|}w{wzzxyz~~ywsoutny}x~|usxuv~~{{~~}~wyvqnuzsvx{vv|{||yxtry||z|tu}xs|zr{zvurqsvuv~}zy{|~|{}}{tpw~|vlxzw{|zxwuryzs|}{wzvu|}zvz{srvooruzsqvwxz|~tqru{~~|wws{{|}qvxsromorss{|}xwz~~zoot|wyxz{zwutptztw|{~xruxw~|xtw~y}yqstzwosvwyz~~|sosx|~}~toux|{v|wtrnnqttx~||wv{~}~yoow~z{}|zwvrqzxu~x{yrryz}{|xtx}}~utuyzqrwxz{~||}vosy~|z{tot{}v{ytronqtuw{|xw{~}}|ypowy{}|{xvsqzzt|xyyrrz{}z{~~xux~}vsuy{rrwxz|~{{}vptz|~~~xztou{~v{xtrooqtux~~{|xx}}|z|xpqyz|}}{xvrr{yt|~vyxrt{{}z{~~~wvz~~utv{zqtxy{|~y{}uqu{~{|{xxrqx{}x|wuropsuuz~|{{xy~}~|z|uosz}~}zxuqw{uv~{vyvsx{{~{z|~}~{ww|~|uuy}vrwxy{{|y~zrrw|~|~{xywquyzz|zvuqpqttw{|{~zx{}~}y~zqpu{}~~}zwrt{wsz}vxytwzx|z{}}~ywyxvyyswxyzz~~x~~trux}}|}zyyztuxv~~}|xvsqqttvz{|}|yz~}~}}y~~tprw}~|yss{xrw}wvzww{w{}{{}}}|xw{~}xy}tvyyz{{w{xssvz}~{z{}wzwuxvw~zxurrstuxy{|~{z|~||~~|zzqqsx}~{wqx{tsz{txzw{yw~|z{}|zxx|}|y|zuy~|}z|}~~~}r{~vr~x{vuvtusqrttvyvux}wqytxyos|vsvy|{z~{vs{~y{~sz|uwxvyxyyxxz{{}zwypht~utxqmyzuuxz||}{q{{pu{{wvussqpprsttqu|zov~~}qs{vrtw{~xz~xu}z{}|{tr~{|xzxxxvvwxwxvu|}wvvmly}zqz~wvxz{{z{{r|~}y{wutqz~zyutrppqqqrqw~wtwwryyt~|vvx{}~~|y{uz{x}ywyuvuu}|{yywuuvuuvu~|rpsrpzvw|xxz{{zy}w|~z}{vturtuz|zwutqpppprsv~}xqrxwytyyvx{}~~~z}wv~xy|tuttwx}|yyxutttsvv}~~|rmqus{{uyxz{{zy{{{z~~z|vqsqtx|~{xuurpppprtw~z}xopwy{uw{wx{}~}}}~xv~yx}|srssx{~}|yxxussssvw}}|{qlqvv}}uzyz{{zx{}|y}}y{~uoqquz~|yvusppppsux|x{vnpwy|uy{xy{}}}}}~xw~xx}zqrsuz|~~|yxwussrsvw{{zplrww{wzyz{{yx|{}}x{wz|rnqrwz|xvusqppqtuzzxzsmrxx~~u}zyz{}}|}|vzzvy}vprtwy|~zxxvtsssuvw~y|vnouuxw|}zzz{{yz}yy{~xx|xopruxy{wvurqqqsuu}wyxoovvwxy}yyz|}}~{~xx}ww|zqqtvxw|xxwutttuvs}z{zqotss}{z{z{{{zz~}v|zy~xw{{qpsuvt|}xwvtrrrturz~wxzrpttr{|yzyz|}}}z|zw~xvz|tquwxty~yxxvtttuvrw|z}vpttpwz}{{{|{z}~wv}x~zwy|vosuwtu{yxvtsstvsszvzwprtqt~z~}zz{|}}|x~w{zvw|yqswyvt~{xxwuttuwtq|y{{srurqz~}|{{||{||uyzz~xvz{rotwwrxzyxutstuvqw~xvytpsspvz}zz{}}}{y|x}yvw|wpswyuu{xxwuttuwsr~~xzzrruqs|}}{{||{|{u{yz|wvyzqotwwszzyxvtttvvqx}wtxtossqx{}{z|}}}{z|w}yuv{woqvyvv{xxvutttwtr~~wxyrqurt}~}|{||{||w{yy}wtwyqmrwxu{{yxvtssuwsw}xsvtoqtrx||{{|}|~{}wzzutxxontyyx~xxwusssuvs{~xtwtptuu}||{{{{z~{z{w|~xttwulmtyy{}yxvtsrswvu|ysqtpouvy{}{{{||}~yw}|vsuxrlpx|{zwvusrrtwuy~ystupsxx}~}{{{zz}~||vy~ysruumkrz}}yxvtsrsvxw~|ysosqouxz{~|{{||}yv||vrswsknw~~~{wvusrqtxwx}yrruqsyz~~}{zzzz}}|vx}xsqtumjr{~ywvtrrswyw~~{xrnrqpvy{|~|{{||}xv|{ursvrkoy~zwvtsrruxwz~|wqruquyz}{zzzz~~~zuy|vrqttlku}~~ywvtsrtxxx}zwposqrwx|~~|{{|||vy}ysrtvoks|~~~xwutrrswwu}ztpttrwxz}{{{{||}ww|zsqrupjoy}|{xwutsswxv|~{ytnqsqtvw}{{||~~yw}{ursvslox}{{xwutssvwtx}|wqsusuvv}~|||{|{|yw|{urruslmv{y}~zxwuttwxtw|zvoptrsssz~|||}~|}{x}}wssvvnmv{y{~ywvuttvwtt~|ysqvtttsx}|||}{z~{w{}wsrtvokszxx{yxwuuvyvs}~{xrostrsqt}~|}}}}{~~x{~zustwslqzzw~{xwvutuxvry}{vqsvturs{}}}}~}y{yx}{urrutmmvzwz{yxwvuxxtu|zwpottsrqv}}}}~|||x|~xtrtwqms{yx{xwvutvxur{|zuptvutrt}}}}}|y|~xy}ztqruslnwzw{{yxwvvxxtv|zvpnttsrqw~}}}~|||x|}xsrtvqls{zy{xwvutuxvr{|zuorvuusu}}}}}~~{}~xx|ztqqstmmv{y|{ywvuuxyuv~|yvpmruttrx~||}~~}|wz}ysqrusmpz|{}xvutstwxtx~|zvpouvwvv}}|||}}}~yvy{uqoqspkr{}}|ywvutvyxv}}}ywslntuvvx~|{||~xw{ztqprtomv~~yvutssvyww}|ywqmrwxyx|~|{{|~~zuwzvqoorrloz~ywuttuyzw||}ywsmlswxxy|{{|~~xvzzupoqtqmuyvttssvzxw}~|ywrmpwyzz}~{{{|~zuvzvqnnqrmo{xvuttuy{x||}xvsmlsxyxy~|{{|}wvzztpoqspnvyvtssswzxx}{xvqmqxzzz~}{{{}}xtwzupnnrrmq|}xvuttwzzx~}}{xvqlmuyyxz~|{|}{vw{xroorsopz~xutssuyyv{}~ywtnmuzzyy}{{|~{vvzxronpspnv~{wvuuvyzwy|~ywtokqxyww}}||}}xw{ztpoqsrnu}~zwuttuwyvw}{xvqmryzwv|~}|}~}~}wvzztpoprrnr{|~}ywvvvxzvv~{xvplowxusx~}~}|zxxturqpw~}|~{zxvw~{u}xz~vpp{{|{uu}}~~|~wtyofgqwvsz}sxtvvoloqv|y~{{xt{xn||zytrwxy{{zuqtnjlmlty}|v|{wwxzz{{{vy|wuwv{rr|}~{y}yv{~~vsprnq}||}uqqttruwwr{}z|w{~yv{}zxw{ztw|z{zqx{~{|wvzypqy}|xuyz||t|uqststtw}}wwuw|y{~|}xutssuw}~}zvztrqqptxx~~y{|y{vuwyzzy|y}~w|yuz}{yxt}}w|v~z|y}}y~{yrtmux|{|x{y~~{zrtqv}{zswrunwz}yz|zvyz{sxx|zz~wxy{xxswzusxv|~z~{z|~{xy{{{x~|~vzvuttrp}z||}y~wyz}{uusuq}}}wxylmnx~}~z}~~~yz~zv~tsv{|~~xuxs{}x|u}}|z~}|{{{zv~{~tuwwso|~{zx}}}~y~~ytxxwq}~}yxqtyx|~}}z}~~}}zyw}zzvw~|~~}}~u|~z~yywu~wz}}|{w|{y~w|~zwx}nmmsz~~z}~z}{{xqmou}ywxqtrvy}{~z{{~|{}||x{vyz|~{{v|uuvz{z}}|{}{z~|{w}~}{v}|}~}zsxxuu{~||}~|}~xwz}twzxx~~yz{up{~{{|}y{~~~yz|{z~}w}|{}vwwyxx{~~zz|z}yxz{|tvw{}}~{|wwusrw|{{xxx|~{yxwuuzz~wqnvzqw|}~}z{}zts{x{||xzxvz|~||}|~}xvwx{{~}|x|}|~wr{}zw~}zy|}}|}~}}{xzx~}~ywvvt{zosz|xxyzyonvz~}z}}y|z}{xxy~uz}~~{}~{z}|xwyr}~}rz|uonty}~x~z|}|{y|yv|wzzx{~uttvvv|~y{}}|{~ux|zz}uzw~yywwxur|{}|yx~~|xy}{vx}v}||~yv}}v~~}~zzrsy{yxz}xwyvvzpxz}|||y|}uz}wu{zy~twvuwyy|{~wx|}z{~~x}~ux~z|y{|vv|}{~{~{~{}yyy~~}yv~~y~zzvsmpyz~|{{wy~}~}y|~}}{}zwxxs|xy~~y{||ux}}}|zy~~~~|vyxxw{y||y|}~}z~{{~zx|}zyvnsv{}|zxwy}{w|}~z{|~}}z{ytwzz}~~zy|w|~y~{zz~}~~vxyvrs}}~z{|~{~z||xty}~|tsstz~}~}}}|y~vz{zzzy}}{~~}~~zsz{y|~~|zyzt{|w}{||~~z}~{}wspxz}|~}}|zyyu}{x{~w}{|uvxw{}|{z~|~||{{xzww|}|}z~yw}yx~}~~zzvyyx~y|}}x{~vrs|wt}~~||}}w|yv~zwyx|z||{xzz{zzz}}{|y}}~z~{yzvvyz{||}|{x|x{}|~~zxxv{w}~wy}~~yz{wu}xr{{~~{~~{~zw{vtt~y{~}{|~}zy}xz}y|}|~|}wuyyz~}~zzz{||}xz{w}~|zyww|{~ux~~zz}~}{zwqzzw~~{|x}vsr}v{~y}~wy}x~}~}~~{}}xx{xwyz~}}~{yy~y|~xvzzy}}yw{zu|}{y|~||~~~}~{{}x~v~~zv{|sps}ps|{{t}{}z|{y~{x~||z{v}}wxy{{s~|xw}{upw}y~zyw}u~}xvz{~z{~xx~|~xu~~{|wsn{mpyzyz~|wxz~|}{}{~x}yzut~tz}z{}}|y{}yw}x}zz|~|}xvytxvt||{{~z}yx{~~yz{~~v}qvy~{||~}}}|~|{|ox|ywx~{}~z~yz}{vyyv}{|yvy|~~{{~x}z}}||v|ztwqxxv}~yz}}~|z{vz~~zwwyxxsyzt|}|z~~~}||{v~~||vzzqwpxzr{{}{{{z{~}zzy||}v|~|{qzy{|}|~{~|{xytt{~}~yy~y|~yx{ww{y}}vvy|y~}|v}|z}zw|wtt{uy~zxt||}{zv~z{|||{|x{uuvvv||}}{|~y~|~|u~}zy|{s{ps}tst~x}}~z|~|||yw|yx{|xuxsxz|z~~~xvvvz|}~|~~zz|utyzuxx~y|zwv~{z|{x|}{{zzzvty|w}|uu}|}~}~x|wzv|{|~zz}wwwvsvzxz|||}y}}yy~}|zwwsr|soq||y|}}}y}}zz{}yzt{wuvywx|}zyv{x}}~~~}{~yuywtxxtyt|zv{~|xy|}}}{|{vv|v~|rv{}y|}}~~~~zyxu|xz}}}}yxwyxyvvz{v|}|{}z{z|~z~|}{}wxrzzror{uxx}{z}y~~y~x{zuvyusuxz}~}~{}{w{~~ywvuzzwytvwyxuywu}|y|zu{~{wy}z}uy~u~}yqt{|j{|xxxu~~{zw|ww}~{}|mtwrolv|utxk|}}{ozzwrhltzywz{y{y}vn|ovrq|ywuz~w{|vx}~zrriuzwvyx~vw{y}tur|zyx{|vsrz}u|zo}yyxy}|{npov~~}tqxvloqvqrtpsupxzqr{~}|z||t|}{}||~{~z~wvqkdksx}vvw}sxxry~ux}r~y{~zt|owuqyxx{xzv~}}urkoux{zxrsmwqpslswporwmx||}}}}yx|{x{z~}|}{{vmlkgpu{|ys|~rztt}{vv|ux|{~ytwtyqt|~xzuz~vyqqkqw~|xrpm{opqlrwtquvm{}~{|xy}|x~}x|}}~{{ukmfgpv~zwr{|tztw}zwzsv||zwsswru}~~xztw{x~unrlqv}~ytqm|pmpotutuxxlz~}~wy{|z{x|~}~{y|{tgligov{wt{pwvx|{z|wu|}{tvrvut~}zxxv}u}tjqmqvzssn{vkpotwvuvznt~~zzzx}z}|{~z|zywyvifkhswwwuxrsu{|{}~}q}}sxzuvyv|{v|~t~z|~z{~zknppx~~wrns}lmnruwxwztm~}~w|y|||}yy||x{rvvmdlirzyxu}qtty{|~~rx~~~~}uvztu{z{~|wzv||{||ywzzmmqp{~ytrq}oknquvxy{xm~~x}z}~{yy}}}v}{psvnemlt{{xv~osu{{~}rz~}|uvzwu|z{}}w{v|z|}|wuzyknpr|}ysqu~omosuwzx{vp~|w~|{~~|}|z|}u{umrvlglmx}{xx{orv{|~}q~|~~{ztwxxxx~zx}vx|~ru|vjoow~~ysr{{lmpuvz|y{nw}|~~yy|}{}y~|~{w|nmuthjkqyzx~trvzz}}vu|}|zxuwxv{~x}||w|}{}zuqy{nnos{wuyqkptvx}z|sp}~|}~|{y|}|{|~|}u|rmtxkijnuz|y}xpvz{|~xs}|~zyuwyxy~u}}~{yyz{xqx}qloqw|zuzvkpvvw}{zvn~|z{|xz|}|y|{~|wywmryqhjlqx{z~rsz{{~|q~y{{wvwzwvz}~|}w~z}|ru}ymnorx|vxrnvwvz~zyov}y|}|{yy}{~{|~u|rnuxlhknrz|~|}qx}z|~xs}zzzvvxyzt~{~~{xz}||{rv}vmnqsz}~u{~ppxwv|zxoy}~x}|}zyy}{|}{~~~}uzpovvkilos{||}|rz}z}}vt}yzzvuxyz~t|~|x~z}~{{zqu|vmmrs|~v|spywv{{xox}~x{}|zxx}|}||}}|}sxpmtwnilps}}~{ty~{|}ws~w{{wuwyyu~~~}w}{}~~w{qryxnmqs{wzypxyvz}xrs~{x{|xw{|zz~}{~||ustkovrjkqr}zzu~{{~zszyx}zyuuyy|y~~{y~}~~}vxuotysmpux{wst{wx}zup|}}w|||ywy}~}{~}|}~xxpullsumkqt{{~u}}z}{uu}|vzzvtxz~y~~}x~}}|}}wuunryvopwx~vws{xx|{vpy}}xy}|zwx}~||~}{~~~wyptmlsvokqu{|~w}}{}{uu}|uzzvtwzy~~}x~}||{|wutnsyvopwx}wwv|wx|zuq{|}wz||zwx}~{}}{{}~}|vwpslmtuolsu~{x|{~ytx|{u}zzvtx{}{~{y}{y{uvqouytnsw{z{v{{xyxssz|t{|yvy}}z|y{|wxsrpkpvsmnuu}|}{||vt{}vzz{xtuy}yy}|{~|yxuunrxwpovuz|z}yy}{tr{z|wx|}zww{|{~|z|{~|~yvvsrlnutnlsr}~||~wt||}xw||zutx|y}z{}|}~}yzvvpqwwqnus~|{~zy{}urx|{zu}||xvy}}~~|}}y|}~||uwtsnmsuqkqqx}~}}yuxz~t{|wtvz~{z~y|z~~yyxwrptxuoqtu}~}zzystx~u|{}zvvz|{|y|{||xvvtqmoutnmsp~}}wv|||zv}|{vtw|~z||{~}{}}}xxxvqpuxtotsy}|{zwsvw}t~|}yuv{}}{|x}}z~{{wuuupmputnosq}}}~wv}z}yv||zvtw}}z}|{~|z}~||wwxuppuxtotsz}|{zxtvw~t|{}zvv{~}||x{~}y}zywtuupmouupotq~}~wv{|z|t}|{wtv|~{}~z}~x}||{{xuwvqotxvqtuw}{z~{tu|w|xv|}{wvy~~}zx||yz{wwtsurmmsvtpus{~~|zvwv~ty{|yuuz}}{}xy}}z|{xxuuwsoqwyusyv{{{vtw|w}r~|}yvx~{|~vy{yxzvvuqstnmqvvrtww~|}vv|w|yu||{wuy}||zw{|z{{wxuswtopuywtyw{|zxtwu~s{|}zww}||uxzywyvtupsuomqvxtuxw{|xqpu|rvsnzutsopy{nlxshihhokhjpuutzx||{{z~}zklxtyy{wyy{{||ppvwtvquvpptz|z~|}{|w|ystsx}wrt{u|wv{{vop|ojoz{ongglfehorqrut}~~wmjt~|~vtvv|||yy|}|qnrx}z{xs{wsuz~~z{vtss{yrqu}yu{q{{xwrot|qims{}yntpiimrrptqy{}}z~~xuojn}yyxz~|{wz}|unqsx~~|~t{{tuy}~||zz}yyu{|uqt{|u|tx}yytop{|tjlnt}vzyomostrtqv{~~|~||zxrkju|{yy||w}}}yoppty|xzvvx|~}~|}~y|{||vz~xrty~vzxu}zzwqot~~}znkknvxvppsuttsq~z~}~}}z}zwojmy}|{x}}yy||}tpppu{|v}vwz~~}~yz{z}}{w~}vsv{|v{vy}{{vqpx~~}vlkkow|}ursuvuvrt|{~}}~}~{|zvnkp{|}{y}~y{|}||rpopu|{w}wx{}zy{x~{x}vtw|{w{vz|{{vqqz}||tkkkpx|~wttwxwwru|{}~}|~}~z~{yvokq|~}|y~z{||{|rpopu||vyy|z}wzx}}x~~xtw{|xzwy|{{wrqz~zzukkkpx{zvvxzyyuuz{~||~}}~y{zxvqmp|~|y~|y~~{y{uoppu{~|x}zz{{|~}wx{zy{{vvy}zyywz{zytrw~}yvvmjlpxxxwwz{{zt}|~||~z~~~|~}|zwywvsoq{}zz~~z}~{xyxppqt{zw{|~~|~yz{xv|{|y}wvx|{xywyzzzvtw}~wtvojmpx{~zxx{}}|v{~|~z~~z}}|}}{zwwvutqt|~{uss~|~{}|}|qpu|}zrz|ryz}z}|ww~|{zyw|z}}}|~yt}{}|z}mkx~tjhshoyrrty|~~x|}|xpu~srwrxvsrt~|z|zppv||t}{t{}{v||zxuvtt{{wwz}}{{||}|zvw}~}znjpztsvhsvpqswz|yu}~}{}{y{wsrz|{vztsqx|}|z~xvysorv|}zw|||ytz}~~wxsx{xuwz||xyzzzzxvy~~vwrknr{{wr~wttvz||uy{y{|y}~~zywtrt|~|wtv~}~{}}|uzwpqrv~z}w~{zyuxzz{|uw|zwwz|}yy}z{{yvu|}vxullms~u}wvwz|}uxz~yy|y|}z~}{zvrpwyvu}~{||uyzrpory|w~~yzvwyu~yv{{xwy{~|w}|z|{xuw~~}ywyokkmvz~{xwy{~yt~|z}w{zz~x|~}{{yupqy~~yu{}}{~zu{uqpos{~}|}yyxvyuu~wy|zxxz{yy{{|zwuy|}uyvmkjnxz|yy{}~ww{zzwzy|}w~|zzxtpsz}yw}}|~wv{tqoot||}xxvuytw~w{|zxyz|wzz{|zvuz|}txtmlkoy|}{z|~wy~{zzvyx||wz~yywtqt{~zw}}|~~wuzsqpot|~~~~wuutyuvyz}{yyz|yxzz{zwvz}}sutmlloy||{}yx|y|uxwz~x}~y}wwvtsu||x}~}||ryuqqpt{~{~|ystrwyv{x||zyz{}u}}x{zyxz~}~votnlnpx}~||~w~~z{wuvw~|{}v~ytvuuv|~~yy~~~|~stwrrrtz~|~z}{qqqu{yy{}~{v{w{tiomlps{ytuxqlqjehjrutz{u}{tvvwz~{s{utwrrynlrnoqtz~|yz{qopt}|vptvsulmwqrtuvx}}}}wy{zntsqux|stuokqmhkmuwu~xv|v|wz{|wptw}sruiinjlmpv|~||~|z|z||rrtzwmqz}oqwpqrstvzy}~{z|wtz}yqxutwz{tszwsvnjlowtt~}ux~}z|z|}~~wopwzz|wkonkmmpv{|{z{z~z~yrtv~~{qov}u~ztvvuuxxz{y{{uv~~suwsuvy~wsv}z{yonpvwryyvzxy{{|}~{spvw|~rprmmmnry{{|~}z||}}tsu{~yuorw~ywwwvuvvvzzz|xv||~xuxsttu{{vuy}}spqvxqv|~{ux{{|{||{}~xswuwysupnmlpuyz~~}{{}}xsuz~x||rrty}yyxvvus{{{~y|zw{|z~uyusssv}yvw{}{rruztry}}wvx{{{||||}{}vwxt{vwtpommrwx{~}{|}}~utw~{wwrsuz~|{zywvst~y}zy|yx}y{{wyttstx~~yvx|{zstyzruz}~~}vwy{~~|}||||z~{vxwt}wxtqpnoswx|~~}{|~}~uuyzxvrsv{}|{zxwsv}x~yy|xz}z~{yttrrtxz|~}z{{ww|}xz|{ru~tlnty}xy}~z|s~xwvu~~vo{{qnpuuuz}~wvz}z~}uqsv|wmpvrjmklqor|~{}yyvqr{{z~|{vioxxxnlurlmqu{{zxx|~{{}z~tprzzurooqprrw{xwy}~zuxtv}{}tw~xrlrunnuuosvuz{y~{{z{~}|~|xwwzun~ymqunkoty}y}yy~|}}uxvv{{}phvymnprtv{zwx|{~~ww|~tw~w~{tuzzps~qimrkkpotxw~}|{vsqx~~|~}|skyxsvvknvqnquz~z||{yx{|~vxyywnjnz{wsqqpuwuuy~}}y{}uquyvyw~yy~|vrsy~tsuminorwu{~{xuqqtytpsrp|~{npwrnquy|~wy}xwwy}wz{|{vnkmr{}zyvyyvvz}{}}wyyqouyvzxz{zussw{}|||rosswxv}}}ywspruywnotrs~~rw{sqsvz}~v}|wwxz|||vz{ysmlnt|~{z}{xy|}zzw{wpqxwu}z{}z|xtstwy~~{rsuvzvx~}|xvqqrtztostovyt|xrrtx{}zxywwxyy|yy|~}wpmlou~|~~zy{|y}}wyzspvxrvzz{}}wutttxvtwx{xv}|}{wrppqu{xruunqzv~|ustvy{}|w{xwwwvv|y}~{somlpw}{{|yz~yx|vquysszz|z{~zwusrr}~vyz|zv{||}ztqpoqv}vuwqnt|{}zutuxz|w|yxxwusyy|}xqnlmqy~||{x~~|wzzssxvpu{}|{|~ywuspt|x|}~xx}{}}xrqporw}|uwwoov}{yvuwyz|}w~xwwwtr|y}}vpnlmsz}}zy~{vzzstytqv{~~||}}xvurpv{z}xy~z}}wrpoorw}ztwunpw~|zwvxz{}}w~}xwwwts}y}~~upnmnt{~~{y~{vyzssxtqv|}}|}}wuusqv}y}zyy{}xrppprw}}{suvoqx||xwxz|}~w{xvwwus{y{~vonmot{}z~|wwzurvvqu|{~}}~~wuuutvy{}y}{x}{sppqsw}{||tqurpx|zxxz|}ywzvvwxvx}x}~}~ypmnqu||}~}yuxwrtwtt||~}}}}wttvww{y~z||wz|uppqsx|{y{upttrx{|yyz|}{v||vvxyxxx|~z}yqmoqv|}|}ytvxrswut{}}~~|{xstwxx|y~||}vy|vpprtx|zwyupturx|}zz{}~zv{|vvxyxyx||y}ypmorw}|}~}ytwxrtwtu|}~~~{{wsuwxx|z|~{uz|upprtx}xwytqussz~}{{|}~xw}zvvxxvz}y~yz}vnnpsx~|}|wtxvsvvsw}|~~z}|utuwvy{~|ww}zropruy~|vxxrstqt|}{{}~|wzxuwwuuz{|x}{qmnpsy}~~}ytvyuuvrtz~~{|xttutt~}zu}}tooprvz~xxzutupqx~}|}~~xyzvvvtr{}{~x}~tnmnptz~~~{vuzwvwrrw{~~|{{uttsqy}vzxqoopsw|zvzyuuqoty~~zy}~xvvuqu~{yyzpmmnqv|}~}ytxzwwuqtx|~{}xutspr}yv~~tpopqtx~~wwzwuuopvz~yz|wvvsqx~{w{xommorw}~}xtyzwwsquy|~}{}wttsptxv}spopqty|vvzwvtoqv{~zz|wvvsqy~{~vzwommorw~~}xtxzwwtquy|~}z{wttsqtyu~upopruy|vtxwutopv|zy}~wvvtrx~{vv~ypmmosx~}ztuzxwuqty}~~~yy}ytttss}|tyyqoprux}}xruxuurpu||yzzvvvtu~~{yry|smmpsx}~|vswyxwssx}~ywy~|utuut{vu~|spprux}~~}yrrvvuspu|}yy}|wvwuu}~{zrv|unnptx}~|wsvyxwtsx}~~yww||vtuvu{wt}}tppruy}~~|xrqvvusqu|}yy||wvwuv~~{yqv|unnpty~~|wrvzywssy}~~~xvw||vtuuu|~uu~|spqsvy~~}{wqqvwurqv}{yy~{wvvtw~~|~uqy{rmnquz~{usxzyvruz~~~{wvy}zttttv{tzxqpqswz~~~|ztptxwtpry~{z{yvvtt{~~}zqt|womnrv{}xsv{{xrrw{~~}xwx||vssrr{~uw|spprtx}~~|{vprxxuoouz|{{{wvsrx}~|ss|yplmosw~~zuv{|ysquy|~~yxw{}xtrqpvxv~vqoqsuz}~}|yrqvywqnrw{~|{}~yvtqt|~||~~wrx|unlmpty}xty~|vqswz}{yxx}|vsqoqz}vz|tppqsw|}~|{vqrxyunntx|}}|}xusqw}}{|}trz{smlnquz}wu{~|uqtx{}zyxy}{urqnr{~|v{{spprtw}~}~~|zupsyytnoty}}}|}xurqx~}{||trzzrmlnqu{}wuz|uqtx{~}xuroqz|yz{|}y}}x|zxy|}u{vzzwwvsory}zuuy}~z||z}|{zzwx|yy{|{xspw|wrqsux|{}yvz}z|ty{vvyxtstsx{yvutu{~zzzz~z{}yw|{{vtsrnmsyxtquz}}|~~~xsz|{ywsnqwxspqsvy}|~~~z{~~}zyx}~}{wssstyyvtssw}~|{{{}||~{}|}~zz|z{~ywurorxyurtx{}~||~~~~zwvww}~|wrrwyurrtvy|~z{|{~|~zwvy||xwvvyzxvuux}~}{z{|}}{~}{|}zvwz{}}zxursxzwtuy{}~|{~~}}{xvuwy~}yttxyvssuwz|}z{~|{~}}yvvz||yxvvzzxvvvy}}|{zz|}~~|}||}~}xuwz{}}{yvsuyzwuvy|}~~}{{~~}~}zwuvwy|xuuyyvtuvxz}~|z{~~|}}}~{wuwz{|zxwxzzwvvw{}|{{z{|}~}}|}~~{wvxyz~~{yvuxzyvuxz|}~}|{|~~~}}~{yvuvwz|xvxzxuuvwy{}|z{}~~~~}~}~~|yvvxx{|zyxzzxwvwy||{{{{|||}}||}~~~|ywwxxy~|xvx{zwvwy{|}}|{|}||~}}~|zxwvvv{~zxyzywvvwyz||{{|~}|}~}~~}zxwwww|}{yz{zxvvx{{{{{{||{|~}}}}~~}zxxxwvy|yx{{yvwyz{|||{{}|{|}}}~}{ywwvuw|~{yz{ywvwxy{|{z{}}{|~~~}~~}|ywwwuw}~{z|{yxwxz{{{{{{|{{}~}}}}~}{yxxxvvz|yz|{xwxyz{||{{{|{z}~}}~~~|zxwvvuw}~|{{{ywwxyz{|{{|}|{|~}}~~~}|zxvwvux~~|{|{zxxxz{{{{{{{{{}~}}}}}|zxwwwvv{}{{|{yxyz{{||{{{|{{~~}}}~}{yxvvuux}|{|{yxxxyz{{{z{||{|}}~~}|{ywvvvvy}|||zyxy{{{{{zz{{{~~}}}}||zwvvvvw{}{{}|zyyz{{|{{z{|{{~~}|}}|zxwuuuvx~}|||{yyyz{{{{zz{|{|~}}}}|zxvuuvwy}|||{zyyz{{{zzzz{|~~}|||{{ywuuuvx||||}{zzz{{|{{zz{||~}|||{zxvttuwy~~||||zyyz{{|{zzz{|}~}}||{yxvttvx{~}|}|{zzz{{{zzyz{|~}||{{zyvttuwy}}||}|{z{{||{{zyz|}}|{{{ywutsuwz}|}|{zzz{{{{zyz{|~~}||{zywuttux|}}}}|{z{{{{zyyy{}}|{{zyxvtstwz}~}}}}{{{{{{{zzyz|}}|{{zxwutsuw{~}}}|{{{{{{{zyz{|~~~||{zyxvustvy|~}}}|{{{{{{zyyy{}~}|{zzxwutsuwz~}}~}|{{||{{zyyz|}~}{{zyxvtssux{~~}}|{{{{{{zyyz{|~}|{{zywvtstvy|~~}|{{{|{zzyyz{}~||{zyxvtssuwz~~~~}|{||{{{zyyz|}}|{zzxwutstux|~~}||{{{{{zyyz{|~~}|{zyxvutstvy}~}|{{|{{zzyyz{}~}|{zyxwutstuwz~}||||{{zzyz{{}~}|{zyxvutstvx|~}||||{{zzzzz{|~~}|{{zywvtstuvy~~}||||{{zzyzz{}~}|{zyxvutttuw{~}||||{{zzzzz{}~}|{{zxwvtsstvx}}}||||{zzzzz{|~}|{zyxwutstuvy~~}}}||{{zzzzz{}~}|{zzywvttttuw{~}}}||{{zzzzz{~~}|{zyxvutsstvy}~}}|||{zzzzz{|}||{zywvutstuwz~}}||{{zzyzz{}~}|{zyxwutsstux|~~}}||{{zzzzz|~~|{{zywvutsstvy~~}}||{{zzzzz{}~}|{zyxwvtsstuw{~}}}|{{zyyzz{~~}|{zyxvutsstvx|~}}||{zzyyz{|~}|{zyxwutsssuwz~~}}||{zzyzz{}~}|{zyxvutsstux{~}}}|{zzyyz{|~~}|{zyxwvtssstvy}~~}||{zzyyz{|~}|{zyxvutsssuwz~}}|{{zyyyz|}~~}|zyxwvutsstvx|~}}|{zzyyz{|~~}|{zyxwutssstwz}~~}||{zzyyz{}~~}|{yxwvutsssux{~}}||{zyyyz|~~}}{zyxwvtssstvy|~}}|{zzyyz{}~~}|{zxwvutsssuwz~~}}|{zzyyz{}~}}|zyxwvtssssux|~}||{zyyz{|~~}|{zywvutssstvy}~}}|{zzyyz{}~}}|zyxwvutsssuw{~~}}|{zzyyz|}~}|{zyxvutssstvy|~}|{{zyyz{|~~}|{yxwvutssstwz}~~}|{zzyyz{}~}|{zyxwutssssux{~}}|{zyyzz|~~}|{zxwvutssstvy|~}|{{zyyz{|~~}}{zyxwvtssssuwz~~~}|{zzyyz{}~}|{zxwvutssstvx{~}}|{zzyz{|~~}|zyxwvttssstvy}~}|{zzyyz{|~}|{zywvutssstuwz~~}|{zzyzz|}~}|zyxwvutssstvx|~}||{zzyz{|~~}|{zyxvutssssuwz}~}|{zzzzz{}~}|{zxwvutssstux{~}|{zzzz{|}~}{zyxwutsssstvy|~}}|{zzzz{|~~}|{zywvutssssuwz~~}|{{zy~|zzytqqmkkhiihimkptx{||}|||wxxwx{z}~~}yttuuyzyyzvwutrnuxry}xuw{|~xttrsuww}~z}xvnlntvwrqssuxy{~wljy|sooqvw{}{yqnryywqtuvy}|xty~zvzyyyzvwx|~x}|ztkfr{vxvvy}}~zu}{xx|yxz|z}|ys|~z|~trpno{vuusupv~wxzvtrssnkopmhrnkotuqqw{}{}y{{wr~yz{~{pkmppp|}{}~vuy}z|}}}~{}{||tghjtw|z|{tomhkojadfmklpjsuxvqs||}|}rtqvtv{x|{~zvz{pglitt|~|v}~trxw~}~~z{xvzugimmx|z}srpgfpkchkjnnqmrrrurq}~z~{~svuost{z{x|sv|riqoqv}uztt{zz}|}~yrxuhnspv~}|~|rushdrkflqlnrqoponrqq~}~z|}tzwnru|{|}wzrv}qlvrswt|~sw~}y~}}}~{vrysgqsqv~|xrvrehujipsmrtqooomqnt}~y{zt}sorx|}|wztqy|lquru{}|vxtz{}{~|||tuzkktqsy}stumdsrilsppvspmqmnomy}{~uzzoqs}z||s~vptrmursx}v~ty~{~~{~{|wu|ojtrru~vsvpeswllrqpxtqlsoloktz~}wy}prr|{{r{xqrwmsqrv~||y~vx{z~z~|~}zuzvhrtrr{|tuvjlzrmotnwwsmotkplnv{}}vvrsw~{|~xt{rpxooqos{~zz|w|~z}xy|}xu|olussv~zuwuiszqmqqo|vslutnqlqx}}zxtusy}|{uvyroz~mqpot~~z}{x~~x~~~{wy{{wv|nnvuux~{vwuitzqmqqp}wtmwupsmqx~|~{ytvsy}|~z~uuxqny~npoot{||y}~x}~|u~x|z}zwu{qnwwwz~|xwxkr{tnprn}zvovyquoqv~z~wwvvv~z~|{xswrnsropor~}{yz~}z}~|tz{}{||yzvtxxmuyy{|wzslyyqmsnvyuq~vvuqt}|~y|~tyt{|}|x|zrtupm{{oqqry|{z~y|~{}vt}~y~|x|wwutv{qtz{|}}xxwlv{tmqqrzxq}zwxrtz{}|zuyvy}{}~yy~zssuqmx~qqqrw}}|z}}z~}~||ws|~x~{w{wuttv|rtz|}~~zyxmv{vnrrs|zr~{xystz|}|{~uzvy}{|~xy~ytttqmy}rqrry~{|{~~y~{}z{ts{~|x{~}zvzwstsx{rvz|~y{voy{vnupx|xtyzytt~|~y}{u{v{|}{~~wz}vtttpo}yrqsr~~z|{~~{~{yxqv|~x}y~||wwztrtt|vsx{|z}ru||srvr}t{y{vtwz~z~vyyw}{||zv}yuvttnx}tqqrw}z~|}z}z~zyrs{~zzx~}|yu{wqtszxsvyz~|vu|xsxswyy|xutz{~wx{w|}}~{}}v|{uxtvou}vqprs|~~~|{|}z|ytqw}{xy{~{{zvw{qrrw{svwzy~}uz~tyvy|u|{{wr{}|}{v{zx~|{{zv~vxvutpzzuorpz}~z}~{|y~zwrsz}y{v}{{yzt{xpsr{wtvxy}~zw||v{uzx{}{ws}~{}yw|yy|{{~wx}uytvrq{ztosp~}}{}~~{|}z~yuqr{|x{~v}zzxyr|wpss{vuvyy~yx|}w{vzy|}{ws}~{}xw|zy|{z}vw}uzturq{zvptq~{}~~{|~z~zuqqz|xyuz~zzwyryzptrzxuvy{}~{x|x|w}x~~|ys}~|}ywz{w}{y}|xt~uyustpy{yrts{{}}~||~z~~|vqpu|xy{vv{{ywwur}rrsw|vxx}{~xz~{{zzz|~|vw}|{xx|xz|}zy|{sy{uyrupt||wsvu|}}~}|||{}~~yqoqzyxyzsx||xxuxp{xqtt|xyx}}yy|{|z}{}yu~~|{xw{zx|~zw}|{tv}tzstrs{}ztwu}||}}|{}z|~~~~~yropyywx|{sv{}xytxpyzqut|yyy}~zz||}{~{}yv~|{xwzzx|}yv|zzsw|tzrtqt{~{uxv}}}}}|{{{{~|~}~xpopzwxx}xru{{xxtxo{ysuu|yzx~}~yz~~}}~|~xx|yxw|xz||xw|yyq{ywyrtpw|xvwz|~|}{}z}}|~{~{}tont{vzwssv|xytwtr~tuty{yyz~|{|}}}~}v~}}{wwy|v~zu{zztt~u{tsqsy~|vxv}~}|}||{{~z}{}{~xoop{vyw|vrs{yxvtwo|wttw{yyx}|}|||~w|~|wxw~v~{uyzyvr~wzwsrrx|~vxu~{|}|w||~|yxzx~{z|{~}~~~{njiyx~ztt{~z{tyouxpomrpqnstz{{xz~wr~~}|}}yzy~|x}xzpwytxqrpv|}xtr~xzy|yz}~{}|}zsigw}~yxssjikustqpskxtqosuvvuzz~~{~wz|}{vxwz~{xqjqzrt}~}|}x|vsuvzwz|~}x}z~~z{~}y~{}z~znihq}|tprzwytuup|xutyz{z{~}xu|~rsw|zxttsq~wwxzzusnppxrx{~{uwywxr}{z{trptvzsrqy~~~xzwtjefo}unkrnojhkdookimoppost}}{{zx|~{||}{}|~{vrsrvqnou~y~{{wu|wpsrvuvxz~}zwxyz}xyz}|w}{yxvvywsjln~}{tqxzyytztx{yz~~}zxu{{{y}~vx|{ztwrvwyx{xuqnpqyq||}ytxwxvs~y{ytppswzuvw~xwrtrqiimz~slmrnpjllgrqnmqqtrsuw}}wxu|{}|~}{~wqnoqvrtvz{}{{wv}tqsswvxx{z}uzx|{xyz~{{v||~uvrvvwpmox}xss{y{wwzu|{vurv{||~xy{}}wwwr{zyzyvtnppysx|}ztvwwwr|{z{uqqrv{xxy~~~usoqqrkko{unotqsmnnjstposruttux}}~vwu|||}~|~|tnontutuy{||vz|rtrvwxyy}wyvyy|xyx|z{vzyyvstuvunor~}vryz{{v{xz|~yursy|~|}}}w|t}{{zzwvoppxvt~~~{uuwvxsy}z{xqqru{xxw|~vspqqrjjly{suxuy}}zwvppnsyvv|zu{}||}|y}}{wqpqqtqv}|~~xxy{}y|}|z}|yxvz{z{{~|{z|~zzurv|v{zw{}xsswstsy~|zx{{~~|~{zxyvwuvrops{uy~}{xsvuxxv~~y{z}||z}~~{}{x{|~{{z~}|}~~{qqnmnq|zvqpqoontxut~zx}}z{|||}z~{xw{}}~ywv|z|z||}{tuv{|{|z{wwxzynxqmorsqqqusvru}y~}zxyy}{ust}{z~zx}{uninotvv|~}||wsquxz~~}zqomrsnv{vvzwuonppsqs{|y}xvwx|~y~wtvz|{|~~}}umehjotw~}|{wtrvxvr}yu{{}xyy}~~~zy{yy{}xutx}~{yw||~{}}}woqsz}~}}{xzz}quwnoqtqrpttutq|{{~|xyy{}wusz}y|}wx{upijnrxy||{{{xqopwz}yrpousqz|uxzwtopprsru}|z~wxwz|~~w}|{ssv|~|~{}~~}wzvogdinvz~}~|zvvy{wuxx{||xyy}}}}{y|wwxztssz~zyx}}}}x|yxpmpv~}{{|}{qxuoostrrqusvru}z|~{xyx|{vtu|{y}{txwrmhnqy|~~xuvvpoov{}urpuvrwxvzwvppprtsu|~yzxxy}~vzzzsruz|{|}~~{svsmgdjox|~|xx{|yvyx{|{xzx}}|}{x|vuxxtst{~{zz~}~}}uxvtmoqz}~wtzsoqustrttuuqz}{}~zyxz|xvtz}zz~wuxtpikouz|~~{ttvronrx}zwtwzvv}v{xwrrtv|}~|}xtxlnopkikpy}~~}|z{{}xosokeeiow|~|vwwxsr}x{}uop{nm{{~|zx}}~zrrxvutw}}zw{~zw}{tqqqmiknv{~{skiuxorsqwxuvu{zwtw|}{}uyyvrnqs{~~|xz}~~uostppotx~|slfkq~|~{ptomjnsvurvx{swumpqqlnnuy~~yy{xyy~z}yvpqry~ytpnikmu~zwsnjmqxvwwzzrrpihnllkmqtyx{}{~}~|{xx{~}xyzwstu}}~wllkjmnuzyvpssqnmsuusyqospnhjkrvyy~}{yyzw{{~utyvvsvz~utonkmpuzuptuwstuwwpupkmomklmtx}z~||tvwwz{z}zxrtu|{uommkorx|zsoqu{xyx{zsqqjhnlmkmquzy{}{~~}|{zyy|}wwyxtvv}{zxmjlkorw{~ytsvsqnsuwswwnrpokklpvy{|z~xvyxz}}~zrvwvwv{~yzqqoppuw~zuruwxtvvyuqvnlmplmmqw{|~}z}yrvwz}~|{|zxtwyxyzqmmoqwy~{zsqrw{z{z}ysqqiinlnmpsx|z~}|{{z{z{{}~xmiidcdhpt{zxyxwwttqussyxt|yz~|xrrqvxzx}y{{}z}{~y}sx||sptqrpsv|~}~||zzxwxtrt{x|{~ztruqqqsy}}vqz|x}xwtyzzwx{}|x}vssrpnprz~~|utssy|}zz}wxz}z{ssxwyx{~}~v{}yvsuwvyxtswy{xzz}yuxsoproqpuz~yx||}z{~}|}}~~|ytw{|xwx{}{rqmhgiqruuwyvrssmrtwww{~~zuwtwy{w||z}~}yorsotzy|yzwpkmzuhfks{{~z}}~|}}}}|yzz|y}|xw~|}~}{wvuxuuy}{|xohfpyyrjpvpqvwsvy|||~~{z{xsutrrqv{|yxw{wtrqptysmjhou{{zplplkmtxz{z{xtmpjiorxz~~~~~~{|||}zvz~~{xzvlkinry}}{{vplmlljnqxyz~{zzvvtv{}~}|{}|~~|ussz}}~|zz|{}uonnqvy~wwtnpsy{}}zzyrtttutx||{~yzz~~|y~}zxyvvuz~{z{slmlqsy~|~zxz~|uuvorrvuww{~zsqnopqx}}{||uwxyzy~|svphghns{xvvvwuyy{{{wvxuvuy~{sqtpkor{{|{wvx|y}vwx|}}~w|}xstwzyxx|||}|}zz}vvxxzx|~x}zxz{z||~y{zy}~}ywyy|{~|~||{}}~}srsw}|{yx~x|xuvwywxw{{yw{}sw|}z|~}vqrswvz}~}~{|z{}{}|~}}{tyropu|{|{|{|~~}~~ytrpmlinqtuy~~ztswqmoqwy}x}|zz~~~yyy||}~y}}xsvy||{~|}{zzvqpqijkortz|y}|z~}~~zumtrmnqwz~~|||}~~yv{xrvy~~|wtuxxzwxvwrrxuvw{{zy{~{{{{x~|xyvvuxy{zty{wqtuz{|}z{|{sxvssx}|xqrruwz~wutrqnsuxvy~{ux{{z{{z}|}zusyuqrt{|x|}|}~x{|vqtv{{~~{vwvoonqopsxzxz||qu{zz{~~|tpvrpotx{|~~xpptkjjqv{~|}{wpsqnloqpqrwwxxx|{|yxwy{{}vvzyqqsx{}~xshlolkqy{ywwwwxy}ywyyqopoomsuyxz~wy||{|z{|{}zvt|ytuy|zxy{{zx}xuwy}~~|z{{|}{|{xsxtooprpsu{zz{}ypwyzy}~wx|ytuz|{}}vzyqopvx}}{}}}vrvsonrqrptwwwz|y}}~~|}}uuvzz}}zy|zz|wosrmjnrvy}}|z{{}|~{y{yqpopnotwzz{yxytsw{}{|}yux{|}~~~}xsuypklosu{~~}~}ztxwqprvtwzwz~wuzztrwz}}~}~~~|xppvokkqtx}~}vyzwsvxxwx||}}~}}}|~z~wttwxy}}{~|zpmrqkjpsxy~~~vutvsux|}|y~vv|wrpuwy{~~{}~~}~}{z{{{}xrtyqlmptv|z}}wtuxvxy~~wx|~~~uosuplqsxx}}{z|zz~~|}|{{xrlsrmlpvz{x{{utwwxvz{||||~}zxwwzy|zut{zssu{|{y|||~y|~~~~|zvmnttorx||vuuurvx{z{~{uz{{zzzz}|~}|uyzww}|}x||}{}~~~zzx{{|}xtx}usty|y~|vrtusuw{yzz|zrtx||wuz|vuy|{zy{xz}|}}}|yqqyvrry}wwyvprssrtwxz{}}}|}x{xttxz{}|xz|{}|||zpntwqrx|}zzsrsuswz~~{~~}{su{xssyz~~|~~y}~}~zzx{z||wsw|trsy{~z}yvyzz{~~ww|~~~zqqwxqtw{}~~}|}|{|~{}|{{yumtwsosx{|yxzzyx|{}||{}}zyyz||}xv~ztrtxy}||~}|xz~~~}zypkotpmruz{yvvwtwy|{}wz|}}||~}~}{rtzwrqvx|~~z}}~||~{xwwywzxtnuvomntu{}}wuxvxx}}}}~yuu{|~~~wprxuptvz{~{z|y{}{z|~}|zyzxywvnlstomsw||z~|uwwxvxzz||y|}}|yyy|{|vxxtrwx{~y{{||~wtx{{}{~}}{zwomswqruy|~xuvutwy|{~~}wy|}~||sw|ystxz}}~{yz{zy||}}}x||}zyxz{{}xsz{tprvw}}~xuxwxy}}}}}{vu{~{tszysuvz{~}xvxvwy|}}~z|}}}}|votwtnruy|{|xwwxwx{{}}~{||{~~{v~|vrtvx|~~}xwzy{}yw}||unmutoqsxy~~yvxvwz|~~~z{~zsw{xrtwy}~~|}|z|}}|{}}~zyxyyy|zvpvxroptv{~{x{z{{~xu{~{sqwzstuxy}~~z{zz|~yx{~||y{zyzyvnouuoptw|}yzz{y||}~z{}~}zz{}||uw}ysquuy|~z{{{|~~~zttz{}{}}|zyxqknvspsvz|~yxyvyz}}~|xy~~}||~|}}}uqvzvquw{~}{zz{yz||}}{wyzzvvuwvxzxspxxtqtwzzyzy{|~~~{vsy|~}}wqqyyuvx|~zwyvxy||}~{vuyzyxwyxxxysmrwvrux}~~|yyzyy{{}}|wy{|yyy{|}{v}zvxz|~yy~{{~}}|zvqwxroqrkknqnqqstt}tt~||~~y|yqquttx|~}xw~}~wwx{ztq{so|urv{{z{qtvvrpsv||t}spv||~~~~}ssuz}vx~{ys{|yxxuy|}xvu~w{y{tjqzopux|vxvzv}ww}|}xxu~{{zx{vzxu|w~~x|vz}}~y~|}{}umnx{ztnpnkgpnlwtmqrryxy}|y|rwysz~~~x}yqzxx}|z}}v~~~z~}|}rt|z{zw~{mpninmnoqltwpuxru|uuwwt|~wx|~yzwtyzx|}zwwux~|}zzsoqpq|}xutkmpkksos{wwvvoutkpwps~||{z{x{ww~y~yxzztu{x~~{vzrqupnw|~ruqkrtppqoswpnxqnwvsqqnruy{~zuwytpzyzuvrvzxrrwuwy}zxqtnfmmgltqtsrqtuv{rq{{}~z||tvytzwwww||ssz{}y}|z~yrssmv{z}wxripoonnoqvu|xnowttrqyzzt|{|~wvrt}|}|~xy~x{~v{~~}~zszrlpsx}wtpppswqisxvxtvy{snqptxusxr~uxyvx|u~yzxwv~vw~z~|wvqvqisyvyzswxusnpvwqmtswzusokwyyzy~y~v}|wvxx{~suztktwz~|~yrxxmmroqt{|z|{ynkpouyvttnyvtsuwoxzx|xxvwvxps{vqvwwxyxx}~|x|yywvrqttxus}|wxzrqqjvuvty{tyzvy|pywpruxywz}stutuuwt{{|rt~|~|}}}{ojqyy{yy}wtvxnswsz|v}yrsrpsumr|zsy{|y|yuyz~wqtyz||~u|zt{{{vrvuxppv|z~zwtrosvsttv|umsqrpw}x~}}{z|xuptsyuv}w|wy~}~wzpuxulrvux}{{ynsuurqtyquvx|oruwtqtxswu}{{~|nxuzurxx|w{~~{{{zzywxwxsmy|y~y{ywnozzytvwvssotsosut{|~u~{vuxyuxvxqtz}~}{~~z}zvxzvskzy|yuwxsqyyytwvuwlrwpstsz}}vy||uzzsu{t~yt|x{|}|}~}yy{|zwxpwvrtoy|z}}wqzx{xryzx|rt}tsstuqy~z}|ywystzpyws}wvx|}|~||}|xtyrvtqvouz{zt~zywrz{w}vwxqtrswxy|{{~zvuvuwzq}{txtyvtz{~}~~|zvwnvyuvnptt{~y||zx|zx}tw{ponrwxy~w{}wtwvvys|wrwuwy}~|{~}|~y{tvyuxopplrus{~wz~w{xz}tuutqttww}yw~z||trumv}tvry{yz|}~y~~}us}wztottuxs|||wz|uywuvsruvsszz||v}wz}vvtv{uywzy{xvw{zz~}||~|{pvwxrovrnsovzy|}{yxutrtsvuxwz~~v{{ttsqvqru}zz~zv~}|rx{}vruqoopwt{|{u{uyxuqx{vu}zyst}~yxuyvrwvxw}||y}yv}|{}~uwwxtrwqopku}ttx|~v{yy{|zzw|vxu|vw}~~z}wxso}wqquzw~vp|}z}~tw}vuuputoqrwrvx~~{|yy}yuw}wu{{|yt||xwwyvrwvyvzyzz||w}{~~}xtxysqwtnqnmxqnox}{{z}{{|{yxyqw{~x|||{u|{ponuyu{sy~}~{|}~{t|zwwrpvmnowttyy}{{uxxwy~|qx~{~zyy{|tvtyuwzy{{vvz~x{~~~|}u{|ynmoprsgyxjsqx|}z||uqsuv}{zv|z|vw|ykk{|{||vxy{{~~y|~{}u|~}r|}rnuoojrnowutu{yysx|}~|r{}yx}uu~}sysvpsut{uz~s}|}~y~}{|{xyzppuklqjhynnou|{|~||}{{s{{v{~}zxx|vvos{sr|yt|~|v}~z~{~zz~}|prpmjnovsowzszz|z|w~xzzz~w~svzsqyvz{yz|w~vzz~|}{~~z~~xzztvxpntls}qnsstvx{~~~~{~qu|tr{tz{}}y|uw~{s{|||vwry~y}w|z{{~{suxnsoqtqmovkt}|x||~}z}wuu}}~z}u|tuz|vt{uw}}sxrty~~|yyzvliljnuqkuqpv{x{~~|{zww|~~w{~xruus~ys{yy}{w|~z}}v}~x~~}{{{vnzynoonttqrvvrtns|v}ux}~xz}v|vuryysu}yvt}}zz{|x{{~yvxsvupmtzrtllw{u|{ury~xuzpszzu~|wz{|ts|zvv{v|x{||tyxuzxmnztsxsnpzqvusvwu~w}wvuu}~ys{zvv}y{}xxyty|uzy~}}~|}{y{|v~|xpoutmttrtoosnopu~z|y~vy}|uuyvzytx{zzu||yy{~~x}~~|xz|swpsrllrorpqytvrx|xx}zz~}~|||~yruwwxt{|zx|zuvy|{|xz|}v}{|}wuxrvloqnrntwtptt{vvwy{~}{|}|uzwzyw|zvz|}u||zuz|v{|~{|y~z}{|{{uwpqtokruwqqxsontns{xzx{z~|tvyxzrz{wuzyryyuwvxuikmrxpwz{vxuq~|yy|{yx|v|~wrtqrpfmponntmmzzpuzw}zwxrovytw}{}ympokipumsyorokyyvz}w|vvwoqpjsxxyyw}yq}~~|}tzy||||~~~|oqwpkusu}uqyqjotmix|rrwnu{~{~{{||}}ws{zuy~w}uorj_hd^dli`iofafbeprrz~x{yqs{}~z}|qxzsv|~~zvz|urwtwqw}|||zww{}yz~|}rrzqv|~zzuppngblrjflikstpzzstqemxrs}~yyusxrt}xxz~vyxyru}}y|zw{wnxwswxwmjsnbhop}}}}qpskksrpxw{y|xrytplkk__mi_gidnqinxqu|sswyz~}|w||yzyxx|y}~|r{|jipkwz}}{~v|{|{truwv{xxyzysuynjxtdghbouqtwtvictvpwystu{w|}|}{usututw|~zw{{tvywzxszyuovrfp{njpilyxy|{xvsojlmmov~}syxpkupgqxicd\akjkuzx}xnt~{z{}{|~}|}}}y}{~{~{ux|vr|wkmnly|||}{|~~~}}y||}xswrpqzzxw|pkuwnmmjpqnnrwvxtmqyytvrroz}w|wyvnqvswv|{{{u}{}zyyipurouvmoxtkjikvw|{w{yqqrijroruxxt|vrrwqnsujba`hmooqzxxytw~}vr|}x~y~{~{}}}|~~x~|wwzts|}tpqqx{}}}~~y}~z~|{~tptoryy~{xsvvswzonlntspnyzwyrlv|ux|utv|rxwtz{tv{uyxs{wxx|}{zvunmvsuorojoytossw~}z|}wwtlrmotpkqrqyw~y|}zumqljsxmjkiqtqlrzw||u{}wvu~~}||{}y{zx~|}uswqr~|qrrt~~~~~~~|v~~wz}y~zzutwuqtxy}|{s{wrztptpyxrqwrttmq}{xutvzuxux{xuvwuztz~x}yy}ytx{wzsvwvoinghwzruus~||}xtmtmnnoqoklqu~{{wotolvzqnnitwrrt}|}zru~~{{}{|}|~|y|{}{wrrilyytwvy|~z~~~~|~v||{}||zytvutvwrtzzyzru}|vwuwzvppwvwuqt|}{|x{w}wwrrusqxyz}x}xzzx~|uvzxzvpyusmomjrwsopo{||ywqytoqkknihquz}{ytptwsnopz~ywr|}{{vt}}z}{~y|~z~|y|~|}vnnkpy|vtsw|}|{||}|}||{}{{~|y~z|vtuojqtx{}z~xusw~zys|{|unt|z}z|zwvspvwsuxtzyu|zspttvsx|wwlmkjrxrnqq}~}|}}w}~vwqwqommqpllnnvxx{yuqx}wsus{}xvwvu|}|{~~|}z||}|~|~}w~|}xrvru}{qqpu|~}wwv{~|||~yz{y~{~}{{z{z{xtrrpnotw~}||yxv{xx~|}xsv~vytx}wyy{|ytsrtxt~~|souvvwryvvomqov}xpnjq}{}}|}~||ypponyrqpopokjoqxy~z|x{}xtr}zyzz~vu{}{|~{{w{w~~z~}}}v~|xrsot|{wvuz~||uwxz|zu}}}}|}zy{zw{}yvqztrtswyusww{~~|x~|wytzvzzyxwyyzx{{utxv{zw|x|{}zwyzxxoqvpsopqtz{vonp}}}|~~}ywzv{{vunolptlnlnuuruwu}w|||z}}xx|~~xx}x{|}x~}|{xx~x{u|tusnrt{yww}}~}~{x|~}vx|{xyz{xq{~{x~~}vrsqxtuttyxuuyz~}z|{yw}wx|{vuytww|~zxvu{ty|x~~}yuvxwuqyvvtkllpy|wwy}}{~}}|{~~w}}{zrpv{z{sppjokjknuwwvwv|y{wwx{~||zxz|}~}}|}}|{tyuvtosrvz|~|{zy~~{{{}~~vttu|wvry|~{tty}xyvqwolmqwz{{~|z~}yw}z{rx{vyz}}{vwy}{zyzwyxtw|{{usxrtnlnox}{xxw~ywty|{vsz{zursxzxsmpipsmoopsroru{}{{}~~}~{wy~|~~|~|||z}|||v~|zrqntz||}~}|~z{~|{~}||~x||zxwz~ywy}{|u||ytoov}~xyrqtlnorxyuwz}{~}y|}|}z|{xzzvuyz|xyyz{wvxyxxqx|x|solnrvvtx~|~}vuv|~{z|y{{woiluvyursmrokkkqsqqtt||{|}y{|xy{|{}zwyzyqstyzxy~|z~}||{{}wux{|z~ysttz~yyz}xxqxzzysqwzyxpqqoxutsuwurrty}x{|z|{{{~}zxvy|u~|{}wsuutru~z}ypnlpwxuwy~|}{||~wvv~~|~x|{xrjjptvvnpnktrqrturolmr|{~~|}{z||zzxvtt~}~vurv}}yz{~xuxyzvxy|yvwx|vrovxtzvx|zsiegoywtqkuusux|~|}zyz~z|v}{{lv|~|~xvtw~zvss{wy}~x{~{}w}~yuxyz{x~xutx}~{xwzz}~{{negv}hbY_kjortvuqquz}~~~~wsv}~~}w~}|zzyzwy~z{~xqwwzvqqpstrpssxvszuwz||yxz||zsv{uzyvwz~~zropxusvlproliku~}uspnxwxz|zy|~{~{wsvwtpquspomlnejmlswzxtqqqqmlwtuxqpquz|ywz}zw~}|{z}}~xwkkkgdacn{vysmjimmllnoupqyty{~|xrz|~}{~xvywvsu{yy|{vqnkpmjuqrwxwtqpstspv{uzuopqv{yuvv{vpnehjlmkpy~~uxvs|zyx|~wxusopz{wwzzxtpnpkfplmsttrootuuruwprnknr{|{z|x{vz~}y|zvmccccaenyy{~vsssrpljmrrp|yz{|{z}z|tw|}yvy~}{suspnr|}|zwrrtwtqwnnooqpptwwvptwqurpptz|yursyrqsknqnighow|}|||zwwy}yuywtqrx|zuopqtqqwpqqqpllqtwwqvvoromntz}|yy~~uxyvssw~xwjehfcaagszzvqrtqqtuytrytuz~}}zz~}yurt}~wuywuqot~|wuvw{wrxqmprqpoprtrow|v{wpnnquvsuy}wtrhkmlkhirx{|y}|z~}}}z|wsminz}zwuxulusntwvtropqplqysyzsssuyyvw|xvxwvss{~}~wuskmljd^`hqy~yzyxuztpqvxx}~|~}~~yu{vzytx~zzy{{tsqrvuqsw~{~~|wvy}{z~v}wunjzz~~}|{{xw|}~|z{~}woklmoolsvq{|wvwustz|}|qmuvuwpstttrquxxwv}{xyutty~{|||~~zy|xuvy~||zyxy~zwz~~~z~~~ytposy{}{zwrruvwwy}~~|{~zxy~~{zwwx|}{w{ytpqv{|xvsrmjlllkgmoimmjjouwvqnosmntosyxsnkmqrpouqnwtsuw|{{zwuuz}|{zzvqomkicdolnurqruwwusv}~y{w}}{ywy}}~uy{vvw|}}|{{x{}yrvqljkry}zzyyxtpxws{{z}zy~{x~}~zwytw~}}|~~}~~wyxspqx{~~ztpkjjgenqkrpkklossoqtxsrtlnqqqnouyxvprnjrssuzzyzsoouzz|~}zwrpnielpkqqnoqv|}z{|}wtvrv|||~~|zv{zv~~zwy||}~|}~}xwkkolkouzzvwwut{~x|{vvv{~|}vvz~}}wzxs||xxz}}~{|vuvnlpu{|xsrsromqohjihjnvywtrqtnnrnpuvrooqstrqwrqyvrrsuy|tz{uttux{~~wrrrqppvuopmjjow{}|{|}tuwqu{|yxy|~}wuzurux}~}|{~~~{zujopkklotx|}zuzztyzxx|}{~z}z|~{xxz~}~|zzvtuz~}~uz{usprtuy}wuvvwupttjmmijnrturrwztuwmlommmouzzwtvomttstvz{{~{zzsnkntx~}zwurmqworvrqsvxxutx|yyyxzxvsrw}}uxyvtuy{|~uzyuw|}{x|wuzwqjhhkw}|xsszrorkfefjoprw{zuwsmpqrqoqttronqlozwtyz|xz}}}yzyoqtpmlnpy~|yspwxrwvrpoqtsru{}}~wuuvuqsw{}~}ukgtyzxw|}}~zyzz{zyysxskghltyxzyx{|xzussv}}{~tmkmwvvtrrppsutty|~}zz|~~xwxlkpkhkqx{zxvv}~zy|ywukegnxxwvvurrsqootwtx|||}~~|wvwyzvruqhoqmnqu{}~}}}{~~}|{x|uouqklov~}zsonllmq{{y~}yvy}z{~}zzz{|yrqqffmkmrw||z{z}|}xllmhinv|{||xusqnmkryu{}{|~~~}}~~}||xvvsnmqmjqqoprx}|z|xxwlkomnry|vqonoqpv}ww{xuw{~|{w|}}|zz{wqmmkekpqsu{~|xv~}zxogjjjmu|wsoonlpyut{zy{}}{x~|{ututrprrklonmov}}yz~xsrmgmppqv}|wtrqssqu|vsxutwz}x{zw{~}yxwtqnmohioonrz~{|}vumdimmow}{xvvrnpvpqxy{|~|{|~}{{{|{vstuuurstjjoljnu{||~{}pkkeejpqmorqpprxuu}{zxxwvz}}~~x{~ywxwurlnkcjqrty||~}}|~xqmou~~~wvz}}~||zz}x~z}zx|{{{xtuwy|{x|yosvtvz}vx~srnimqsuz~zzysv{ww||yxwxyywzxw~|yxwx{|}~vv|z~|y}uxzuqpppwvopqnlqwz||{{zqrvpptutttvzzwx{souuuvwz{zz}}~|xyy{zx{~~zsooqv{qnnojhoxyqlrxty||{tsxqnqpnlkmrssv{xqturrt{{{|wzu~{yqr}|wtrolrvollkfelv~}wx~wu||{~z}xpppoppsxyxx||xrx~y|{zvsnjlsy}uqqqqsuutmlspkmonnsx|~zw~}w}~~{~yvvuvv{{tptx|{z|}{xpggfdjr|}xutwqpvvronnmpsvzxrwxnjkkmrv{~~~{zz}{ywzwqkhhgdgnuz~{uqlfcfilnnqrjjqqprvvwxwwwomusmortwz{~~||~~|}uuw{}ommnqw|wrkimjghigbbceilnvvpuyusttuvwwzwqu{uqqqrstw|}~ysonpsu{~xrpkhhjllhkkcadedfjmpqosumnwxwyzz{zxxwoltsopruwxzvpoe^_fr~zwvtonnlhfefigiqmikjgfhimooqxtqz~||zwusqtvspw{vtuy~}zywrr{}{{}|wwrkhjjkmnoqlinkehjjkmorsqqurlsxy{{z{xvxzzw|~zxvmhjquy~|}|w|{vrqonmkmolipoknpoomklljlsurx~~~~|~wvy}}|{tmoslghkmqw{{|ysrrrttrstnjnkfhjlnmoqpopvyw}~~~yxyz}{yyy{}wpfhmhfkorwz|~~zzyyxtoomhgnomopqpoquwy|~|z|uw{~{x{~~|zytlrsifgimsw~{xwwyzvutqlkpqquy|zxyxvww|}x|}ux~||}~~~|}zxojpohhjmrw{~}}zwxyywx}|wuvurruvy{|z}yzywz|}z}~~~~}upurifehmqw|zurwwrsvwvxz{|{y}{u{|vz|wussrpruw|zw{~}rkmmgfhmruy~}|~{w|}utusrtvy}~~xzxomqojilquzz{~~~xqpqkfflsy}uwxrquwy{{||ywz|wx|zwsstsppx}{yz}}}}~~{z|~~~yrpvvrquyz}||yrrrrtux|{xxysmptuuux{|}}{srvxz~|}}|~}ttwpknsvzytwwrsstsqoomklqtru}|w|}tnnopty|}|zz|~~zspwxrrvxz~wtuojhhjjkptwz~~~}~~~}zqntrlnruy||}}~}utzunoqu{zxvplnsssuyzxxzyxy{~y}zxwuvxy{yrtyrmopswz|~}zysow{utvvy}~{y{|{}~}|wvuqqtvxzz||sqwywy{{{|zyvnlttprux|}|}}|{rrvqlmpv|}yz~|wwvronnortx~zuz}yxxvwxwxytpw{vsssuvw{}ywwuoousmkkosv{|y|xpnonnqsuwvvzvotzz|}|||yxwqlpwvuvx||{}~~||zxxskfjkhhmu|~{yywu|}vtspnnnpssu{{uy~|xwutvxwtw}{vsuy{~{xvrrsqmkrxwux}zvzxqpqqrstvwtuxvpsz}~~{xxwuru~|{|~}~~}}z|~~}~~ztoqvsptz~}}}xv|{vvvutqpqpptz{y{|{{{}~yx~|utwz|~|zzzy|}{xuqnuzuprssvz}ywyvrrsvvvwwtsuxxv{|||zxtv~|xy|}~~{yzzz}}wrjfnrnosuwzz|~zvx}}{yxwtrtuvz}{}~~~}}uw}xrtwz}{}ywuttolstmklmqux}ywz{yy}~|{{xwxy}}wz|zvot}{yz{{{yy{{~|x}zvsqjfnqllnqvy{{zyvvwxz|}zz~~{rrwursuy||||zz}}}}}yxwuvvplpsnjikptx~{~}y{{z{|||{y{|uu}{yyvpqxzwsrstsuxz}||~}~wz}}|yxvogglmklqwz{~{zxvvwx|}}{twz{}~}|xqlotspptx{~}~}{z|||~||xwvuwxvploqlikqw||~yxz{|}|{zvvzyuv|~{zxvwwust{~{wxzz{~}||||}}~}vuy|}}~~{ywsniiqtrrvz{~xuttvvy|{y{|wqsx{|{}~~}ww|ztty|}}|zxy{{}{{||yxyyy{{xsmnsrmotx|y{}zyxyywvvusuy|zy}}|~~}{x||utuvz}|ywy{|~~xvz}yxvspljrxutwy{~~~|wtsuvvy{{~}z~~~{sqxxtuy{|zzwx|~zy}}zzywwwwvsnqwtooru{}{}}}~~{{{yz|}~|zxz|}~yuz}wstux{|~~z|~~}~~xw~}xvtqpojmwyuuuuxy{{zywy|}vw|}}~|ztnsyxvxy}}}~|~}||yy{{|~~~y}~zxvssuuwvolqrnmnsy}}yxwvxz}||}|zyzzz{xtw~|xutvwx{}~~}|zy{~yv}~yuronpoklsvsporuw{~yyxx{|}~}}}uv|~~{zysnry{zyz}|{{{||yxwwy{|}wxxwvtstttuqkhlpompv{}zyxwxyy|{}}zywwz{ywy~}wstwx|{~}yxwuv{}yx{}|yusqopqnllsxwtvyy{}~}{z{||}~|{}~zvx~{zyvsrw{z{zz{{{~~zwvuvyzz}}wqxzu{w|~~nntlcbdhy~~yjw}~z|y|pwt||x~y|v|vnuvprnsqhmnrp~{}xqzbZhqwm}w}zu||xsxzs|{}~ujitjnwoz|{xtlpu{|yo|qz~mcfwmob_jhd_hpz{wph|utuzpsx}svoinxsu}{tz}n~|rxmr}~ys__wj`fkhohpmemllqy~xwlposz{yzvq{qsr|z||gekmzw{~yt~xw{owvgdcbdz}mag]bxiiin{npzpr`bvq~}tvsl|yu||}}wzluq`hqq}zm|pzyoop]cnq}{|orhfqhdkuvpp{nkegigwyp|noo}xu}wnhnfns}u}{y{pvxv}pjkb[c}twgazhadgyroywe_wnsxp{}ywnzzxu~xx}y}qxyeeh]}n|yz~|xzrozibqnspzyxsgpmicsspnxwklgj_m|tz~nrqm|pq~mrmejk~}{}zwwuurx|qxmc\hqu}fxwbbatvl|zs]qvkroj~nyy{v~~|{wwzvspfl[dy}y|x}zyw~zvp|sfsrqic|ovpqgornlpzjoqrdhtkq|n}tsnzt{p~rwufieon~}{wwtwtu~vtkbff`ts~hfcwxm}~varvnqlfxxyrzx~v~{||uy}vt~qlo_hzjoty}~zv~|uutn}vue[x}z{|pxomjouiqwxelnaksjtrqtmu~ufhjjbv||{r}wsq~z}trgn_Zhw|~okpvvlfzqpsjjvgovz|~|uy{yv~p{vnsndyn^`hz}}v~{z{vt}zqZgqow}{|oqoylow{nkre`e_oxvrw{ov{|mnmqchluy~|yy|qn{~||svl^`feb~~vzzug|ztvngtiflvsuwyzzx}~o|zprrh}xaZ\pr}|}xxx}|sv{miiebionyn|ujsunmkjrgi`fyvy{lw~w~v|umudljiesqzy}~uw}zzn[flfcg~~zrv|nhauu^gpmj|mvxyyyr|y{|u{weqf]Xf{{|{{{|}yz}zjvneie_foq}vntokx}q}n_susip|zxwy{{|xyonavn]_nkrz~uusrw|~tnrcbh__lnw{{ss{xhtsonngpvq~|~sxqqksout||q\mqvu|w~n}lo~w|}quuklo[ebanp{x|zxrlslfnjfsvv|z|oulmy{}nyyjmqimopyx}ptyrv|p{mg^WiUTdz}~yvryrpwrlxmfugsjvxvpwwxukjkagnlyx~szyv|~|yzoqhdlpdoneZv~}zxvrzhj[ll\czyuy~wxzpsuloqgluix~y~}vzyuzztp{ywanmh_`pnz|qsgwyggusm{ylhvw{tx}vz~tw~mqo`bYsnk{}z~yx~}p~upxkhg^\ehpuop~zrgxsrgszx{~yx}t|xljdxj]etitw}}yusly{~}}~{}|qlmebf_cmisvs~r|yotnfkqq}~xzqloogtq{t`ksrqsxvn~pp}{z|uwypttfik^hf_cl|}yqspkqnlwvu{wp}rqxvi||ymoujnojp}|wu~q{svwwowf]VajYYnwu|zzy{rsxkrtijlt}vv}r}{u{sijjcjlm||s||{~{}smizqaftghrn]bx|~~wqhbnxgfz~ww~}uquqrvptuikofswps||wxxs{|~{uxvu~n`pqladmgpww~zstylvswyho{z|{w}{z{t|rpvf]Yiqdj{}yvzsqu{~~vrwojthecea[fov{yrjilzrk~~xq{t{wqnrqoiwqlopujzvyp{xz{|~lqs``dTer[cbqw}x~{tuvpco{s{w}mutusvot{qt\k{q|yr~||uzr{ro~y|xprxorifbYffbi}wrtk|xkz}wj|xunwvoivwmpipajwnz~xs{zv{y{xr~kdkZ[t^Y[asgu{zvniwosw{yxlpur{t{}ia|qpuil~zst{{g|lttuyp}xrtjj]bda\gstwzuurwjw~yws|uqo|rprsm]mievww|yvxvwyt~v|~mnmXjqY\Vek_t}xxrpwlru{yxwxqptx}jk}oonelwpz{tw~{i|gjkyzswrrpoagdcZdggnwy~wkwz~u}{v~tuq|vrvyp^mf_nmk}~yvuvtp||}~x~qrr\luZ[Wck`mwwzk}v~pnu|vq{~qoq|ztj~tqpfgujps|wz}m~igdv{~s}{pupuhkih_]f^di|z~|vo~vw|vyuty|qwwyfjoaci_xy{yyu{mtw|z~x|psgbxfZW`nectsuiiqtvwixyywrqwjyuuncppipt{s|{~xswrieq}v|yxorqwlknji]f_a`psr~~oyq|z}yxy~oswxkougbdZk}osx|~y}pstyyv}tsiezj^Yaqgaqtpe`|wyxvjv{ux}x}vuxn{{woepoinpvlwz~vzr|xniy}pt~}|stsymmnmibj_balhivsyqxx}}}orvvkwwicbYnsfgr}ovszuz|}~vujs~h_\kocdtso]dyy}~rp|upss{|w{|t~wlivllnrlios|y{vtswptxlx}u}}|xwxpuwqqqmrssyzxxx|}xxzy}{yw{vy|}}sopyrpnpwz{|zvosz{st{ipoqwvzz{xxvsx~}~|{xqt{ysmsytreigbeffqwu|mVjxgZ\filqr||wp{{vuwrutv}zx~}|sgp|prsovzx{v}z|copbdbfmkwyq~tynt}z{yz{{xidS_{wrpcZei_modlpu}untyqxwgz{tfilrvleppt{~{wrqpvf~z{vpksuf~uvxzuowp{}oqsvtyr~z|vwz}~ha\P_Ww{jprokbegguwbegl}mvdr}wlajqturcioz}ztzjrihom|~}{hmcmspzw|qvv{wxzrvvtqywwwtyv`daQZ\{||npvnehliewsajklyqqewvr|j`mqopwggrz|tulynhlpzz{}{zufmdjsuy~~uu{z~uusyxvvxy{vqsr]dgVY_zxzrnypelqmcuwbilhvtrguzr|o^jpnkxpht}uxm}yllm}t~||z{twbiefux}||t{}~{swuww|yu}vulr_^k_\at|wxkvvghsqgl~ibkepwsllvzyaanokszlr~zxnuqolxu}~zxz|pwd_d^q}}xxw{s{{v{}v}z}~wileWfhaiox|orzpeptlg{u`hfhxuphz}w~k\inkk~ws}xuqzsppyuvxs|pm~iYa^k{~x~~|zv|~}x}~~~y}~|}||tjhgWdlfpq~sp|sgpxphyzaddcuwrixzo[enmj}{u|zwq|uto|tuxq}{nk{iW_]k{y~}}|v~z~{||{{||z~xxphhdWgmittru|rhsxoj|x_dacwvqi{{l[fnml{x}|vsywvq{u~stq}xklzcU][oz{~}v~xx{z|||~||{|xtqlgh^[limvxr}{om|yoqm_e_kzumo~c^kohrzws}w}rz}t{}~zwyow}rhutWXZ^z|}{}}{yxx|x~xx~w}|pqljieZekfuqyzunx~toz`dbdyyrl~n]hokk|{wzwyz}ys|yw}{s{qr}vjpy^U[Xo|x||~w~ww}x|vy}y~x~{poklkh\`jcpqvz|suzr{iae`t|umu{`cnniv{ytz~wzqzuzt{~toy{okymTXW`zyzz~y}~z}zyw}wy}wz|xzlmjlkd\fgetlzuyu}cfdg{zsmq^ipkkyzww{|{{{~s{~}w|yrxqpzwlozdSYVh}~vyyy|{|{|wwy{~yzwv|~utghgkjb]hfhwm~zwywzcgdi}zsnn_jpjmy{xyz{}|}z}ty{x{yq~vopzvknyaSZXj~vxyz|{~zz{~vw{zxzvvz}t|tcdcjic^ghi{p|z}xfgfh|{uos_hollx}}x}x{}|r|{w|wp{voovvljxhSZZgxvw||xz}x~wtxuzuztz~utxba`fie_dlh{y~{}ypdidt}xpudcnpmw}{xz||yw|vz|~xzurynlounfpt[W^`zuwz|}vxx|ztx{zvvu}pw{x~zmwk]]`ggcbmmuz||}{gifl~zunm_krptz}~}{tz~~yzw{mxpjjpnfjwfW`auxwy}{{zwyy~ztutvs}qr||w}|zlro\\^fhecmqt|~}|mihj~|wn|r`istw~{}}|r}|xzv~{kwoihmmfiwjY`buzwy}{{ywyx~ztutur{npz{w|{wjpm\\_fhfdnrv}~}~~ljhmzwn~oaktuz{}~|t{~y~yuvkvmhfkjelwh[bd|zy{|{u~wwz}xswzzusvy}ks{yx|ythsg[\aghefqq}~~~}klhxzuq~gdpww~~{v~yz}|vxmqtjgglhfrt`]bjyz||r{{}v}zur|yuuq}yolvzx{ywklq`\_ehgcjpryrmkp|zo~pblvx|}}~v{~{}xv~qlvlifjifnwf[`c}|{~|~|ryv||vrxvyvr|wsjsyxzzwohrd]^dhgcenm|y~{onn|uvzehsxz}~t{{zvyxkupjggjgjuo]^_o|}}zzvy~zx}xss|wzvus{zllvyx{wvimo_^`fhfbilp~tvqnw~|rqemwz}~w}|}xv|~qmvljeghgnwj\_by|}~{u|wyz|vrt}tutvw{ziluxyzusfnl^^bghfbjls}uuro{~|qnfox{~~}y}}|wu|}nmujidgggowi]ad}~}}~zt|wyz|vrt|uutvxyzhkswxytsfll^^agigdjntvysqy~s}rfmw|~~u}}wty}|pkukiddfgmwo`bez~~{~ywux|zx|vrrzw|utr}zs~jfouvxsriena]`fijfipr{tss}wsziju|~v{}xtuz{uhqogfbefjtwicgq~{|{zpv}w{ztrv~~w{tqx~}rxrdhptvupoajh]^cjlkiqt}~xtr~}}q}phq{}}w}}ztswzxkkrggacehrzqfjo~|y{wswu|yy{urt|w~srt{uquddlrttoocfk_^bjmnkqv||us{~~sxtioz}u~}{truywkirggbbeiqzuikp~}y{w~svu{yy{trt}~w~jggr}vmpow{~xkqy|fll`feouuknv|yytgihv~nonv}||tpk`lbdhfqwum|}}~lonrzz|}viklfplnxxlltk{{~rkojuuxyu}q{ryr{yxvouvxp{sr~}~yvhmkk}~vurt~utsr}uq{xjjseqzyurvox}xs~pulanwr{kdostywmelievxs~ru}{swxip|{}z~|sztfirdnxv{yz|t}}~wodmb_mxrjqrv{xkekdcw~rpxksyxv~xnsrcq{~}|ysxobjpcpxtzx}|t}|~zibm`amxslrrx~zkfkccx}pouhsxwu}wosqbty}~~ztxobkodovty}w|{s}}{{ibmaakvwmrow}lekbbw~plqfrxwu}|ruqbr|}~{txobjodnttw{}w}{s~{}z{jcne`is{nroupemdatripenxvu{uuscn}~}uxreisflrxv{yzz~sz~~ww~}nbki`inrsqqwelg_nvhlehxvuwzvufg~}xxugfrikqz|u|yxvu{~uv}sdgnbhk|wruo|}ijmag||khhatwvvxwmau~~~|wxmdoqiqtwz||w|s|~yy|w}xhblgelruxrvufofarseh`jyvvz~yuch|yxsfhskpr}~yyxvwz{st}yoaemeln|x{q}~kjobf{kee_rxvv{yn_p||ywndmsluu}~{x}t|}zxtr~zuh_ijhrr}~{rzgolblzfe_bxwwx{xh`w~}zxuifqprxyvzuxyr{vvzvn`bmiqt}~uz~pisfexodd\mzwwzr_jz}{ywoeltpzw}|z}v{}xurpzwre^illwvr~nrzwqtyygozwmnuoruuttw{|w|tpcx~y|zw~~}qv~or|qdsyfdu~ssowqon_Shw{ppupy~|zv{wtx|{ennb~pv}xvvuz{tzz|~w}}ryq[qwlmuy}}uylkgj}z|r~{`icuuebZliehy|{{yh\wvw}|~xy~yso~vuemqbk|ru~sxcox~ub\ox~tncmhqz{nq|lzxndjfj_\jgzwy}rp|srogoowtmzyrzy}ntw||mcj^Zn~mosfp|psoqxkrzvuZkmYdlpp}nsxyovodygeqitqux}}tzncmemet}zs{mo~zjstehnloaY_Plxuv~t~|uk|t{sqwpoittywsw{|v|}wyou~{|ypwbZY_kzqq}mtrqyjrysx_rx\_]\jxpxuvx|tyuk}tmwj`to|pw|}~k}||oznm\RxuslnsvgfhoqnceUZg_ryt|uhryywz{vtsk[yz}|x{z~}{x}nxwth`a`aaj~{vsxstozsvgemffb\eityv}|~zxownwow{fqlanrpyxzr{qt{z{}toXam`j}uyq}ujcknqkefZicVjnp}xz||uzsx|v|{{zj^tx~s{u}{~~rw|~}ylekffY[cn}|w{wv}wtdh|oimjbibjr~wzt}|z{orj|pyzmynfhi{y}tysjn~|xp_er`^lut|ohnnqidf_qiZbbcyy|t{uxzt~}r_wvwoyxxq~|{x~vuqyqhkkk_ZZ]{|uumdzthnogmiilpnh{vrxxujvss{svwimksxu}~u~x~pjr|w~si^sk`glnyxwqputpdd_ltfag`gqn|}zz|u~}wv|{~|blyxtivw|~x}tno~vnkpmf[ZYmwvu{||zwh^[US^llj{}}rwvkmxtrowtn_oq]epuupnrll~z~}xpzytotlbitrrls}np`ymU^flkjpuii|tkv}zyry~n}mdzqrpomnutemrpr{|zll}xyws]n{nga]]cmhWi}u~}w{gkk~~}irw~s~|rpitf}upsytc{~{rjmotwpdbhjthdfikZUpnrstujps\a^o~|yzlo~zt|tkncpj~xz{}|hsxtmtoz|jic^hkytsnodUlokoxxfj~oYb^dyysxlm{xq{okrflk~}}{}mowuqzq{vhmi_ej|wpogXkqhq}hi~l\gccvxo|~lpw}vo|~oqvijh}|z|lp}wsv}s}ulriabizrqgYmogtzem~g^iaeusl~kv~v~sn}|otvijf~~}}jszxqz|ttnshb`i~vuh[omhuzeo~e^jbetqjkx~v~rm}}|ouvjjd{js{xqy{tunrgc`e|zn]qoju{eng]iaesqi|lwv~~sl|}{}ptuilcylr{~wqx|tvmrhcba~}u^orks~iik]gcbqrjwqsvzvlx~wqrujleqsl}|wut}t}~zloiaf^y}bkumpne|s\cd`otlqxnzyyosvsotjkii|j~{zy{ty{qwyjz~|vgkpipku|{l\qmvm_p}eepmztxydsxgn|xgoifrrtvngsxvxpqoq|sxwyyy~}~}rnyx{|wwx{bptnm|{kjvfporto{pl{tsxw~tilymyrff~srpsnaxzw|zw~y}}x~yrrsef`nxn}x{}mvkaf`pmvpXZftxffomjoyxrq~y{lw~jr}vwylpjjoqu~{x{|sv{omyz{x{|~kYaVZp~t}~}lfmsifkqnz~|szvxosnptpcqvneqopywjw~si}rov{mjwyvylmpohgwybcvmopkop~tru}ou}}jnptvztv~zs`gjk{p}rrvwvs|rortiv|uzt}sjalwrjlbdmmnbm~|u~{v`rrkysmywv}yylljf]yvos|{zmrwwnnlmf{{rdOZqjgg\Yhoq\bjn~{rddkqim~y~u{zqmw|~sx}uphZi~|uzzszz}rq|t]]Y`f{rts`ioZ`dkhc|{uowtkts~sz}rthnkv}omosfm~xpzuscmzx{n}uqcRPc}pikvyjjtnglpvlvpiXVdjsl|{ty{nq{~sxz~vswmkuxy~u~~z|u~}rmu}x~hgl]`axvpebhxu~~|kcge_i{ntj|tvzy~orvvtjUYhswyx~woxz}rUSXgou}xbjjg^\xznii`Sljljuwhxuwl~fac\d{~sxyouz|sxfo|phk}z}ylgkwxjmhhwkre`fu|x~tww{l{|mxuq`fwwsc]pouuwv{yx}ehj^[Yt|rjyoibswdXh}sruw|s{~vz|sq}}|yqo_Vaus|nxzsrvrq{zyylfY_jm{}posyunwwqpjmUdtmrcw{wje{su}ws~ymvmZextu|uw|u{{x|uupv]UVORqu}tpxswk{vdhfjdmsqu~}xr|uou~|xz|yroZTnlzy||{|vn{~tiry}wq\ed]fitkvrggyvl]ewn\]difs|utr}xszyxtmhjhmt}wrvl}uu|}v{tiVQn}wukqqks}|mn__ukttuv~wy||~kvuyi}rfyxdV[ipypzsp}~xy{rqjc_f`cuvoqjcn}zto}xpktpdl|z{yuw~~~ywzurv~vmjZdxvm{}klxuy{xpmXkbWkwttprnpx}yhcfelo}kw}r}{rm}polhcy~ztssjmtss}wz|~|sw{t{ptzlpyj\J\pmn||fi~{sszgi[g~kz~wty}r~qsoop||pefkqzvtmrrpu{wspt{tomng_bnj|t`z|yvlsmaUb}usxtns}yumhxzw|ric_x}w{}{nu{wmmkgj|ri]epoywykspvz}vgeifjmw|z}{uuyw~bw}tyismbY`|txxw~vxulstrlkkaW]owtnflohlvlf\n|~zr~|ttillqv~sheefgsuzowwvp}kgvvhad^chq~|ntswvmehijfqoy|v{os}gciifjujc_sx{v~xou~|{rb|qjdXccciposjhtn{t}xwxv|{w|wxyc{{q|uohjmgk}xn~zz{vm}|xxzp}jpnqpqjZX`iu~~w{qu}jk^hprno~pprmqx}vkvzlckmr~y~~|wqx}pn|rfhd[[e`_`qquvsonpjfityz}zv|{~upvx~~jtxddZcort}tzzz|ws{omlhf[Y_kstwptymfaZ_totp{}r~wuf{nrlliie_jyy{kz{qzs|~ytu~~l]dliegy~~|}ytmjklnvzyq~tvrlch}xid^V]oowxy}xtsv~{oisx{{r\`h[cco~yyqbev|z}uzy{{xqttsv~zl}o`edgt}w~|x{piehm}qujpjb^Y]dmqv~z{wf^xouvq{xtr~ztstrutgwt_Yitzy}px~mw{y~xikgfak{tbROQTmsyhdelt~{|{{~|uwytonvxuqfVfts}w{}~phsxnfinmmqiVfd]mxzsyyrov}tcbdjrwxtzvzsvrj|n`bfr|~}|{xun|zxrninxjleh{xtog^[qz||twgb}sy{{~o}psfhlkgfsuh`aktz~nwquzvvuokksqkfefdficw~ysv~s}{wytnxx}|zytvsmkmsqlmi[p|{}y~yzxnir}xx|}|rkqytsstmkbYljfs~~{mklpwzywvxxtt~{qhddpzptijwssvuu{{~}|zurqzp}~rctlei^b_YU\nutx~}twrla|{{|}uxnnmnqw{qhdgntvqqrpn|uulhghnkgedd`doi{{tunhc_m{zwwx{}kelz~~zokf_x{~}vlnxywxts|qd]Z_mvjh\[mt{tmmkpvx|}ujcyuqtec^Y\jwuqttr|~~}}foscjkmnmj_\bm}ywy{rqbpyzyxwoknrvxngcbfjy{tshefir|{yv{tf\cpywqnd_hcwz{yxrpq{w}wxwrvvhffis{mha]q{~yqjouz{vvqojd^ougi\SVW_r{}zvwv}||xxjh}srxrqnmjgpz}z}ywvzpuqq~w{yertijimmkf[Z^gs}zuuhhkpt~}xyzzu~xulirz{uqqjszwy~{wtx}{|}|xztpleistoec`^hj^dci}~yxsqpv}~{}wpqpssmjwugg`\dksy|ts|~}zvtokjldbwndhcbhmsu~}{xonsv{~vy~tvfnvtqnliifgqy~|~}ov}mvtpuux|zxzyubiprtpi]]ejoqy}}{tvvz~xslfdes~}ujd`csqr~z}}zvsptzy{|w}wsnc`jmvrr{w{yzyx~tpyusrvzpnkhio}~nvwqrxuqrys||ujdj|zi_nsuvtuu{z{}ut|cadhkr{tp}sdknv{todbbcdn~ty|ssnp{swuv}uphpqm}y}ufq_fyrscTYf`bhis{}tr{vlz{ou||wvdnz|roupokpv{zvruu{{nssjmnxrtulgty{ogfbb_bfifjeXzxr|~zuntmqsuvlkjbdt|yz|v~lm_xq{~om{w~zy~seV[etymhWiwhlVktgostij{x~u~xqsss}zssv|wmr}piir{ymzwqozxsbn|yy~x~}}one`htxd`erviafebmtxl{|ob`oyt~uglqjn|uewx{qtkozu}}xwv~}{os|rwzxsdTnqw~xewsnjpvc`^urw}znmmgr~sorzlfmt}um{zyltxjdj~st{rmzlyz~qoUR\s}etyqebqjgS[}smora\vx|x~|}lh}snz|uux}{tgpqvz~y~nop{xuwy~~vh^qxim}z}wqukcdpxxnslx{pkZ\f~yruvykugmz|wizpiki]gos}yu}zy~~~|wz{wiVninroqttajsg^d}wu}taexovr~p}}xuvlkz~|l~plg_l}vw~zj|wpzy~zo~xz}lpfibo~~v{|oc`bnmgrvtwsx^dvpusqdxotynarwvo~~j[n}~{{zpt~~z}{uumhW]cagzo}tealvpsmrjmkmkzyztvzwlfes~z~vvqt\j~}u}u}|shz}yyxp~{yse_pcjoxmr{ig_v}xic{~f]dkljx}~xhvton~|}mpcnkty|ummvqx}}}tl{wpnqbXs|wvtjmnemw~~stjawlswz|uv}ji{qwoo_tzh]gv|qzvuyr}yvu{wloiggrjnsolmpjdmmgzwlhpsff||y}{{xuitvww}qowleb{pztyonzxjj~y{~~|zvkgj]VbcswswtzseYZasycUYegn{qp{~pqntxpg_rnaemytzqwpotzuxzm|wz|{vici[NXixxym{lggjjqmj`\fboqzk|{pzyot{sdivd[_jyjlt}tt~s}}|svp]csiijj~tzy\lytoukkkmg]}tysln{s||yisv_gzrjhupu{|~x{n~w|{v|l}k`hjerr{sijtz}viitvrg{vtcluunk{zrpf]vwjqn|~ty|zzt|{|i|uYWpmhoos{~kmgotsnrin}kxxwluq~po|xn}`gwlp}|v|vt~v~|puu}~wuyx|mZnm_ozzz}lpmotvhiikynfxg|vq~vlxsmw}x`b}o`ny~xs|zt{}q|}|{on}wxn]gm]cv|z{vkgrupmepuk~jsowzqoyzf]tk_q~}{~uyv{zztqy|qkz|q\js\^uwzurkktzsrmakqg}}hk|q||jw|oz{hc{tbn|zp|xytqzq}uxsi|~nV_nadvu{|wtrqytqrgimg~ku~pusm~}kr~~f_yualw{}~{~zy{wtwryvrzyo^gn]^pu~yxrotokjcnog}lu~y}vp~zoxhf{sfpsw~~|wuw{yvzn|}}~vlqkXgqdisry{{uu}~urndlffxyjzzu}so{ukx~heumaqvzx~zv{}yx~l}w~{r{~rs~z{kZfmagqmr~{yvytnnhqmi|znx|vt|tlz}edztdoqrv|~{v{ztpyn}~}xrpzzp^hrdetoqy|yuvoofnpf|w{kzwvqp~zproetseqxvx}}xy{tzowu|{v~riw|zq_crh^mlrx}y~|tumkrhytry}uuwso{rehhgYUj~wrtvlvyykxvqy__bon]Ycdjxvyyqwxo`wtjw}x}}zu~qxx~~xxq_drsyi|y|~~vtt~{sgo~xxuxw~mb{se]fmbbdyyoilprgqst}trpi}qjtxp`wogVfq~~rpzpt|~}znloqohwm]Pacidbquvnnxxsp}}{oviw~sv]WYiqjn~uwuv|ulzqgv|rfwmmpqogd`Y[q}{~|tysibpvzpoyx~{itvrrxxcpsdcg}||q|dhlz{wu}kWj|scahdL\kgipw~x|xqukhu{|lsvwptppwxtcqkdZh{z{{{vtxpswv{{whpejtbdSRYkmnxpvtg_wwxsyo}z~pnhunqoidjkmx}y~{ptrnp{txzmtdo}jnvvfScyifn{zusuk`fsrzzp|yud]zpb`faUs~x}|qz~wzpu{yw{~rqpuodiomppqVXnfnpxydq{sx|w|}}sorz|tvpevdgebl||pjjmrkd}|qrkkrhkbvm\^^bbq|x}hh|syx{p}qzxixxohvxt{wdXi{z}z~|qqsrpytysnapso`Xqu]RXbce~|u}lrmqq~y{~ypknutmuvusprVl{z~y{skd~zy{|yrujdhouphlVak`npx~zmxkbkpjvwvsmm|vvlp{_cdkpyywz~|~qc}m~usum^dplfk|ujhobWc{~yh{ws|{}|{nqshp~xj\jxiY_ioo~z}}uhqqx{u~spebgpshficgdpaetsguwz{xprvmovyxkke[vrw|pug{rfkuvu{zgl|ulhrwhiVkhYinzz{}yxxpvzwsqljdatzwnp|vpqxnhx|zywvpi}|xw|y}uvwzo_itz||~~~x~{pahkmkjilwuuwt{|~tTekmpx|}t}||kv~}}z|yul}~~{pZWbionrquywvnu}WI{qjjhollfi{pqoo{zzx}yzutx|{pYamxy{}xpny~plloVm||{sqn{nt{mtrwz~zutukiq}obr|m]W^gWxynd`blqywymWbp`^^_mqzsls}kemja[jux}_^p|zrtxy~zwxwwkb]lpg~{vt{hvppjz}xx|ujow{nqppb_ZpoxvqpsprroujTgi]YXsz}qitichqicWu|s~rzoZk~|mlnv|srxyvkjd{nyzyxw{{y||rjwqjz}xx}urxt~yimorifbl{u~~{tstooqkpoVbma\Xtvhnibfqkh[o{r~sxu^i~olnrsmtwxmng{txyz{y||y}|zthztlw{wv}ws{v|}tfjnrkifo|y~}vvwppqlqmVgnc_Zt~~rfog_dnhe[u}sv}r`mpnpv}}|nlrwvmmi~ry|~|z~|z}z|mmxvm||{ttzszxz}yzlcimrijdvu~xzwprpmteXqkf]a{}{kcwtb_glea_{x}yidwzoop~~{}ujotyrnhqwv}}~z{|yyf~xunwyrxyvqwyzsefjqmhehwo~}}vttotpYjrgf[p}|}ocl}g^bkeb[u~zs~scosppxz}zllryvnigtk{z{x}~gxxylu}|svywoxyzvgejpofbaog{wwrut\kvjk\j{zynbi~i_aida[p|u~qwfmuppv}{}|nkqwwmicpe|}z}ksy{ntvxts|yuq}{}{yzldiorhe]idvzzutzgcznnd_w{zsfcvqb^fga_c}s|s|mhx|sqr~{~ujmsypkciik}~|zvjzxkzuxqu{tsrz~yytedipmea`lb|{uzxcpzmp`eyxwmdi~k`_gca^n~r}o~jl{zusw||{|ohmswnjbmgt}~z{rn|{ulwvupvxsrvz{wwpabfnid`cle~}x}wdtznq`gyxvlfl~j`_fa`_rs~pkm{|xvz|{z{nejpuljdpju~{|to}|xj}wwvpswqrvyyutp`^clkeccqh~~x|{ir~ptecwxwogk{mb^db``nut{okw{z{|}~zxxpcejrmjgnto~{{my}mryv~ypnuprt{}~~wsppdZ]dkefdsr{|zrk~xrq`lwvsiiswf__d_cg|rzkr~}{x{~xvtsd`emqkll|q~{qx{~tl}v{~rlqqpt~y}z|{uqnmfXZ_igghszw{vk||rudgvutkjq{h`^d_ch{s}lp|~y}~}wussf^bjpkmm}u|rv|}xk{|uzsmqrpuxzxzysolkeWZ_jhiiu|y|wn|swehtsskkszia_d`el|s{lr}w{~|zutrqc_bjnjlpt}q|z~ul{~~yu|{qkqppv{y}{~x}wvomjkaVZ`ifhjzxssywtcnttqjoxtg`bdags|}vtnvxz||wtsrl_aenkkkvzwxt{~nr|{{uxvnmqms}w}vvzxurmljhZY\efdgp{s{rvzlhtstlmt}ld`eaelzs}ns}vz~}{yttrqc_bjkghnzo}szrmzz{uv}xplqmqwx|vxyyvtnmkj]Y[cfbdivmuy}qisstnnupgbfbelwt}ps|w||}zztussgabgkefjvlt|xlwyzvu|{rkomnu{zyx}wzuunnllcY[^e`acrlxy}~}{jqutrns|wkdeeciwwztwqx}wy~{{vtssobbcjedeqopyy|pqxwxtyvompkr|{x}tvxusqlmkk^Z[ab^ahriy|vlttupqxsjdhdfnvuyst{~xy~z|xystrskaadjdeftlww}~~nswwwsztmmnls{x}|rtvrrnlkji\ZZa`^alqk|}vntsupsyskehehqxv{tu|~xy~y{wxrsqskaacicehunxx}~prvvvszunmols{w{~}|q|stqpmijii]YZ`a_cktl|~zntturryvmfighqxzxvty~{uwyuvqqqqmb``gdeiuts{z{voutvsw~wpkolq{}wx}{}srwpqlmhhgibYZ\c`djxs}~sqtstqw}}qjhkhp{w~sw~~t{ywvtsnpnoe``dhejq|sx}}ostutt{{slnnox|yuyy|vmzznokkgfgge[ZZbbdkxyx~xptsurv|tmhkipz~z~sv{~uwzuuqrmonog``cifjq~vx~}pqsstsz}tlmooy{ztxwzukxymnjjfeffe[Z[bcely{yyqusurw|unimkr~z~tw|~~swxtspplnmofa`chflt~uy|}orqsst{zrknnq{|vtx}vzrkyvllhhdefgc[Z[cbgn|xvsututz}slkmmvy{ux|{q}ttqqnmnnmca`ffgmyzw{}{xprrtsy~vokonv{~sv{vxwkqznkigfdgfg^[Z_abit{ttvtwuyxqknms}~|}vw|}~svxsrpolonogb_bedjs{r|{}prqtrv}xqjmlr|}|{tuywwxmnzqkjggdgfh`[Y\`_foxpwxvxwz{slnmr||~{xx|~~uu{tsppmpoqic`aechpxottssrv}{rjiglzztlt}qrxzowswxopqtrkjjpnrxx}yzqzwu|poqsvx~stsldpzw|wy}wwxxvmjgkikpyusy~u}|}wyy}}wqpksqr}xrw{utit|mheha_^cfgnvypywx~}rtuyz|pmfj{{qx~suzwq~|y|~}ztrrwuzxuujrxnmujijlpr{~ulihiuqutpu}|}ufnws{|zpmjonpv}{uqdb_j{}}vocejtx|}ssuietsqttwtvuwrljimkpv|sz}{}{wxy}xtuu}vun_fut}}qjefeciooezx{z{wvx}~tutxx}{qggfoztty{r}{y~|}|{}|y{{qwrjkimnuz~skdffp|upxyorvvuthfx}}{sqptqw}w}od`dj}|~xmehmvx~}}xorpdhxvvutusvvxqmkmpnu{|q|~zzy{~~uuw}~|tnog`p|qlhjhiotmjx{}|}tzw}uutww}ulfjn}z|svz~vv~|||y~zz{xwsyplkloqx|xogehm|{~pr}|roryvpsmesxurutv}}y~{}{~|ofagl}}zpgimux}|{vnqpegyywvtvtxwztpnoqpw|{p}~~|{y|~~vvx{y{{olnfbtvqmoknsvln~xz~}~~~{{t~}xzuttww~}rjgkq~}wsw}}}s|~{~|}}w}{~|xz{~v|xpolpqw{~skfhiv}uqy}tmpt}xptqfk}}yuvwv}~v~z{z}ribehvujknuy}}{worrgfvxvutwwzy|vqooqpw{yo~|}~}}|y|~~vwv~xxzynoncbsz||wstov~~}wtx|{rr}}qptouwwvy|tlkkrz~{hj}|ws{wuunqysz|x{{|zytrw|}~sryxnw~~xpppu~pq{}~vnz}~}|zpovyz~{w{~~x{tytsx{}|zqnoq~~ial~xpouphq}z{z|wwrry|{}}{twz{~qtrpx{snoo{|}sms|xz{ungnx|{zuurqklot|~{su|pqvqtu{~xoonu{}yofku~wp}~~|xyuz}{riompxynhghs}t|rqzzshfmv}zyou~}{}wpolmv~}}}wxz{x|{~{|{|rjbjv~rkvyutsxz|uqmnkqwyr{|{~~~|ytwyzu~sltsuzwrst|ury|}vnkiju~~ywonpty|zwor|xmntpsty|~wopov}xywphmvzr}~~yzw}}ty|rgnsu}vmmlt~xwwps}~{tmfls}y~tp~}wrnonw}}y|}~z}w{}~xz}{uojkv|oo||yvvy{}~xsnmnnw|zrz{}}}ywz||ss|ynnyy~~utuz||}ux~{uoiklt~zxqsswz}}wsmu{tjqwuxw{|}vpss}}uv~zupmv~xu{y{{{rowvkivz}tppr}v}~rou}||wrkkryz|zqw}wsmopw{|}yw}|}~~tt{zyvqnmwtq}|ywz{}~ysonppy}{szz}}}ywyz~yqr{ynnz|xwx}|z}vy~{|snimnx}zsutxz}|vqnx{rjswwxx|}|vrtv|ysw}}xtnoxvzz{{~{umsxqhox{}uus|yywpr|}}ysmhot}}{~ts~|{upoqs~}|{|~yxy}~}tryzyvpliqzzt~~{x{|{uqoppw{yr|{~}}xvwx}}xrt}ynmw}wmmms~}xxx}vov{{wq}~~txrwz{}{|}~x{xswx}zwx{zrrsx|}}|llxwxxzxyt~z~y}{~~}yvxy~}svv}}vvw{}}zwmow~||t|}srzsotuvxwvruu{~~zy|sxz|~~xuvx||vkjt{}|sw~||y{}|~~nosv~xrlnnx~~uxztv}vnjpu}~vt~yvts{~|~~~}xwxz~zyw~~tmflv~umu}zxtx{zxturx{zs{}y~}~~}zvxw|}yxvmtx{{wxz|{}tsx}{trpr{}}}ywppprvy|wtqyynpwwzy~|vxw~~|yx}voipwzt~~}~||zx}riqv|xttt||szvqu}}~yskjou}|~~tu~~{vutv~}|~yxyz~zxx}y~}zsmhowxov}{vy{zwtusx|{txy}~~}{vxwz}{xu~wmrz~z{{~||}wsy}|ytqrs|~~~|yuqrquw{xtqs}|slty{|{zxyzz}vw~}}xsnow~xw~~~}~xsu|wmlx~xvvx{t~~uqv|{{vqknsy~~~||ty~{xtvv|~}yzy|}{zv{wtyyxvroox~wq{|x|}}yvtvv~zs~|y}|~}|ywxx{}{wsu}{rny}||~|z}xzyz}~~~ww~zzy{~wnj}mowxyvy{{{zxxvnt~ysmpqw{~zz||v~|wy}xtx~ur{}~~{z}unryz|wsmnpty}}}}{qt}~~|zwx~zywrrt~w}~}y|yrs{}~zuty|{}{y{}~}{|}~|tq}uw~xxwx{~|||}{xz}vw{uu~~xrttxz}x}~|{|z|xqn~xx}{uqw~{rowyzz}|z}|x~yslpu|xy~~~}{}~yz~}}zutz{{z}xusw|ts~|~|xpmsy|}vvwz~x}zr|}urz|ztpqz|v}}}~|}{yw~~zrtv{wvz}yxvyx||vpy|~zuqtv|{x{ysz~zvu}vx||~zwpqtw}}su|~zuqov{yts||tlrxzyx|~}ywz{|}vw~~~ztruw|~}ut~~~vu{}~xyz~~~}ywtz{|vpjllpsxy{~tox}~~|tq~{|xupquy~|wsz|}~~}~{ux{wlq{}yzy}}wov{uxwxxvtpsty||wuwyt{|xxx|z}vsuv{}yx|z}}zzvsnqtz~~usyzzwy{}xyw|}xz|x~zvprsx{~wot|zs|}~|xuxyzytss|~{volnpuw{yyz~ypny~}vt|yywxvvsux|}|xv~}~xyy|}~xnr|~}~|umq|vyzzzwtpstx{~}xwy}tw}|~xyx|{uprrx|{u}~xzzvvtrqqwz~xouz}}|zwxyzxv{{}{xtruv{|}|}{rozyu|}}|yz}~}}||vuv{}~|wonoquwzxy{xoq}{svyyxwwutrwy}ww~~{uxw{|~{|}zpjt~{tny{uvtutsrswx||zxx}zsz~}xwy}}|~ztsuvz|}~xywvwuutututy|~utz|}y{}~yzz|}{w{}~~zuprrvx||{}uox~ws~|~||zz}}~~x~|vvx|zunnprvy{xz|xor}~{txxwvuvtuty{}}~vy|zvxw{{~{zz~xomyzrpxvvuvutrtuwy{|wwywv}~}xyy~|wrtuy{~{u}yt|uursqqpsw{ztz}}~~yxx|||y{}}~zvprsux{|y{}~ums~yt}{|z{{zyy|}x~|wvx{}xqqqsvxywy|xpt}wqx~wxvwvusrvx}~v{{utuvy{}z|}vmoz~vpwxxvvvtspsswy}{yz~zrw||zzz~}~yssuw{}~ww|vyywussqpnrty~xz}z{z~{w{y||~|ytqttwy|z{}{rox~}vv||{}{zvwxy}~{z|wwv{}|z~xstsvx{yy|~uou||tr}xwtutroprtz}x}~~ztuuwy{|y{}~tlnx~xrxxwuuvtsqsrvy}|y{|tw}|z{z|{|vtuvy{~|~yv|utzvustrqnpru{}y|}{~{x{z}}}}ytpqqtvzzy}vos{yt}}}{{{zxvxw{}~wzzxxx}|{z}wtuuxy|z{~}tpx~{ts|wvtusspqsuz~}y}~~ztuuvxyzwz|~tloxzsyzxvvvuspsrux{|y|~uv|}y{z~~|{}xtvuxz}}}zty~vr|}utpqppootu{y|}||||y~{}|}{wrprruwywy|zrox}vu{{y{zzwxyz}~|v{}yyy|~|uuuuwy{y|{rr{wqtyvustssruvz~yx~~~~zvqsrtuxwvy}zrls|xt~yxuvuussuuxz}zz|zsw}{{|}~{vuvvy{~|~wu~yrswsqoqprptwz~}z}~}}z}{}}}}yuprrtuxxw{}tnt}xu{{xyyxwvyy|~~~|ux}|xxz{|vvvvxy{y|zrr{~vquxusrsrsrvwz~yy~~~}ytprqssvtuy}xqmu~}vu~yxuwvvstuvy{}z|wsy~~{||}}ytvuwy|||{tyzspz{tsoqpqpquw|y~~}z{}|}||yvpqrsuwxw{~woqzzu}}{xxyxwuxwz{~}}tw|}yyzz~}|}~xxxxz{}{~{sr{~vqwxvsrsrrpstx{y{|{~|xrqrrtuwux{~ummv}|w|}zwvwutrtsuw{|{~uv{}{{{~|zz}wvwwyz}{~vt|vqtwsqopoonqquy~~}~~zx}}~|xtqrrtuxwy}{snu|}ww|zxyxxuuuvx{~|yuz~|yzy{{xw}}xyxz{}}}yrv}ztr}~xvrsrrpprrvy~~y}}z{~~zwqrrrtuwuy}~ummv|}x~~|xwxvurtrtvz{{~vv|~||{}zxw~ywxwyz}|~xsyxrrztropoomopqvz}zw{~~~~zvrsrtuwxx|~vnqx~zv~}{xxxwusutvx||}~uv{~{yzx{ywx{yzy{|~}wrx~xss|wuqsrrpqrswz~z~|y{}}yvqqqqstuty}}tmnv}|x~|ywxwususuw{|{vu{~|}{}|yx~yvxvxy||}zsw~yrpy}usoooonorrvz}}zy~|}{xsqsrtuxvy~zrnu|}ww|zvwvvtuuuxz}{~zsw{}zzzz|zy|zzzy{|~}}us{~vquzvsqrqrpstvy}|z{z}|{wsopoqqtsty~zrlpy{x~{wwwvutvuxy}|}|tv{}}}|~|z{|wvwvxy|{~~vsy|toq}xspnpoportvz~||z~}||xuqrqstvvw|}vorzxv}zvvvvttvuwy|{|~xuuusux{}{|{~~y{|}||}}zyyz}{{~|z}~|~}|y|~}}}zxywuxx~|vsuw}~|{}z|z~x||yuy~}~{||}yyz|z|~~yx{}{z}~~{|}~z~|yz|xy{}}}}|{y{~||}~~~~~|yw}wy~{x{}~|{zxww|}~{{|~zy}~}~|ywuvwurw|x}}~}~||{~~}zywzy}~}z|yzzz~z}}z~z~}}}zzywvtywy||}{}}}}|z~ux{{y|~}|~{}|xy|~|~|z}|}~zz~~|zutxvxxx~||xx~~|yzyx||~~}|{}|}zy|~~|~|~|xwy~{xwzuuvwzy}}{~~}}~~~~xttxy~{}~}~zwx}~|~~}||{~}z{vtxuxyyy{~{}~}||~}~~{xz{x}{z{~|~zz~~}||zvy~}z~}~~zvz{~|zvyuwvu||{~}{wz}y}|{vx{}}{}~~}~|yy|}~~}{{~}{{xw}}x{ywut{{yz~~{|{}z~~}~||zyw|~}}~~~|z|||{||~|zz~~~}}}}~yyyuyw{~}~~~{~~}~}~zxwwx|||~}{|w}~x|~}yy{~~|{}y~~|{}|z}xxwwux{~zzx{||~}~}}zvu{~}||}|yz{|}{{{~~}|zzz}|~}~}y{uv{|}{{x{~~{{y}{}~~}yww}~}~~}}}~{}|~|}~~v~~}z{y}~zwwyz~|}||{}|}~~|~}|yy{{{|}}y}~~}~||y}}}}~}{{y|~~~~~|{~~}xywzyzz~}}|z{}}~{z{{zx{}}}}|~}|~y~||~|}}}|xyv{|~z}x{|x{z}|~|~|~|{|~~}}x||}|{zz{~}}~~{}|}{~~~}|{~|~z|}~~~yw|yx{|||{y||{{}~{}}y|~x|~|zz}}~{~}~}~~~|yy}|{z}yy~~~|{|{|}}|xz|xz}~zyz|{y{|}~|~}}|~|}}~y}{~{~{}}y|svy|}|zy{|}x|}{|~{{z}|}||{|{z|~}}~~|y{|||~{~}{|{~{~{|~|}||~|z|}~z{{~}}}{|{xxz~|{|}{{x|{~~|~{{yzvz|{~~~~zz{|xx}}~}zxzy|~y{z||~~xz|xy|~}{yz~}|}}}y{y{~}|{~~~}y|||}~~{zwwvt{|{y{||z}{{zy{{~~~zvzz{}|~}z}~}}y}|z|z{||z|||~zz~}yxz}y{y{y}}||{{{xv|~~z|yz{}}}~~~z|vxz~|~}|{~|~}~{w{~|||~~|}{y{x|~~|y{|~}}~{wvvzz{~~y{|{{~|{z{{zz}|~}{|{|~~{{||y|{{xxzyx{|~z|y|~|||x{}{||x{x}|~z||zyz||~~}yz{}}|y|~|z}}{y|}~}}{~|{|}xz}~}{~|{{yzz{yx}~}{v{yz{~~{~||{~~|zz{z{{||y~z}yx~|||~~{}}{zz}~~z}|yy{}{{|{~~~|}~~y||{y}}~}~|~}w|{~||zvyz{|}~}}~~~}|}x{}{{{}}{zzx|~~~~z{~}|||~~|{~{}|yw{yxxz}|z{|{}~}{}~~~|{|}|}~|yz}|||~}}|u|z}~~~{~~|}~~|~}|zvw{zyyz{}{yz~~~}~||~~z~y{{~~y{{y|{~|y|z~{{z}~~~~~~~~}|~zyvz|z{z}~x{y~|}}~~{zxy~~~~xz|w{x}~}x~}~~~|}}}~{yz|~yy|wvy~}~|}{~~|y{z|{z}~|{~{}|z{w}~~}}yxw~}}~~|}}zuxxxwxz{zz|~{|}{}~{{y{~}}y{x}}y||z|}zyz~~~|}~}{|x|{ywyz|~{z{}x{w{|z|~}~}z~}z|y}{zzyzzz|}y|}||}}{~}|yx~~}zvz~|wvzy~||z}z~|}{~{yz}}~|}{|{zx~~|z{xwxy~|~}|~}~{}|~~|wwzzz~}{{}yz{{z||~{~~{xu{~}|~yz}|{~~~}z~}xw|}}|}~}~~|~y~~{z{zyw{|{z|zvuy}~}z{w{}|~xz}z}}~|zz|}{~~}|~}|xz||}~}ywyy{yxy~{~|y}|x|~~~|~|}}z{}{|y{yy{~|}zyvxz~z{~}{~|y~{vwwy|z}{~zx|{z|||~~}{wz||y}~}yz{~{{||{xw}|z~~~~|}~{~~|}|z|zy{z~{{wwtvx}}|zx{{~~~}x{z|~{|{xzz|{~~}}}yxz|~~~yz{|zv}}}|{{~~}{vy~~}|{~}{{}|}|z|yz}~~}{zyv{||~}z{~~~~}{wuu{x}{{~{y{{|}{~||{x{|~}zxzz~||}~}~zx~z{|}}~|}}~|}zyyzxzz{}z|{yxvw{~}}|~~}}~yuxz~~{{}}{xz~z~}}~~|xy{}~z|{uw{{~~}~{vwwy{}|}}|}|{{urvuquvw|{}z{|}~~|}|xyxx{vwyvt{|~vuw{||~|z~}|}~}vxzww{z}}{||||}{|{~}}~z{zx{yvyxty}~|x{~~}~||}xvzyw||~{{}w}||xzysvwsv|}}~||xswzz|}{~zyxv{zz~}}~{vzzx||}{zwvz{}}~~|~{~}~{||~}}yxuyxvxy{|{twwuxxw|{|~|||xuvstyxz|}}z}}~}}{}|}}~z{}{}~}}~|}~~~zwwv{}{||~}{y|~}}~~}~}||z~y}}}}xy{y{~|~|zy{xvvwy~~~}|wuvw}~|~|}}z}x{|||yx{zz~|}}}}}|~{~~z}~~|~yxwx}}{{yy|~|wzzzyutwww}~|zx{{wy|zz|z~{{}z~}y{||{}~~~z|~}|~~xwxz~~|~{{zz~~~~~~~z|x~|~~}{|~}~}{}||~}}xy|zxvz}}{{}wvwx}~~{}{~y|~~|xuyyw{~~~|~}}}}~y|}}~zxxx{~{{|x}|}z{}}{wtvxvy}~~~{{{zy}|zzyz~|{~}{}}yz|{}~}~}|~~~|z}}|xxy{}~}~~~}~z{~~~}}~~~y|y|~~}~}|}||~}|x{}{zxz}{z{}xvwx|~~{}{z|~}yvxywz~}|~~~}~|~~z{}}}{xvvx||z|yz~~{}~zvuxxw|}~}~|{~~y}}|zzy}~{}~~{~}~}x{{|~~~~||}{~~}{}}}{yxy|||~~~{|}}||}|}~~~z~z~|~~}|}{|~}{x|}|{y{}~zz||ywwy}}~|}|~{|~}xvxyw|~}|~}~}}~y|}~~||~ywtvx|{z|y}}|}zxwzxy~}~~~~}{~{}|zz{~||~}z~|zy|{~}}{}~{}~||}}}{xy{~~~}~~~}{~~~|{{~}{||~~{}{|~~}}||}{y}~|}{z|~{{}|zwwy|}~~|}|{|}|ywxyx|~}}}~}~~z}}}|}|xvtvx{y{{z~}}~~zxyzy}~}~|}}~~}|z{}~{}}~}|}~}~y{{}~~~}|{}{{~|{~|yxz|~}~}~~|}~}}{|}}z{{}~}~|{|~~~}|}~{z}~|}|z{||}}zwwy{~|~}|}|{|||ywxxx}~~}~~|~{}}~}}~{xuuvyzxzzz~~~{y{z{~~|~}~}|{|~}{}}}~{~~}z||~~~~}||}z|~~{|~|yyz}~}~~~~~}}{}}}~}||}}zz{}~}}}{{{~~|~{{~~|~}z||}~}zwwy|}{}}}}}{||~|zxyxy~~~~~}~{}}~}~}|}}xvtuwzxyzz~}~}{{|z}|~}}||}||}~}~{}{{|}}~}~}{}{{~|{~}{yz{~|}~}}}}|~|~}||{}|yz{}~|}|{|{}{|}~}z|}~~}zxxy||{}}}}|{{{}|zxyyy~~~~~~}||~}}|}||~~yvtuvyxyzz}}|}{~~}~~}}}}|}~}~{~~z||~}}}}~}||}z}||~|zyz|~|~~~~}~~~}}~|}~}|{{{}{yy{||~||~|}{}}}{|~~}zxyy||{}}~}~}~z{{}|zy{y{~~~}~|}~~}}~~{{~|||~}xvuuwzwz{|}}~|~}~~}}~}~|}~}z~}z|||}}~~}|~|{~|~~{zz{~||~~~~~}~~~}~|}~}~}{{{z||yyz|~|~}|~|~|}}~{|~~{yyy||{}}}|~z{z||{y{z{~~~}~|}~~|}}~~{{}|||~{wuuuxyx{{}~}~~~~~~}~~}}~}~|}~{{}}}|}}~~}}~{~|}~}{zz|}{~}~}|}}~|~}}}|~|}{{{{{}yyz{|}}}|}}}~}~||{zyy{|z}|}|~z{z||{y|z|~~||~~}|~|~}z{~}|}|~zwuuuxxxz{~~~~~~~~~}}~~~}{{}}~|}}}~~|}}{~|~~~}{{z}||~}}}|}}}|~||||}|{{{{||yyz{|{|{{~|}}|||zyy||z}}}~|~z{z||{z|{}~}~}~~}|}~~|}~zz|~~}|}|~|xvuuwywzz}~~~~~~}~~~}~~|}|}}~|}|~~~||}~}}~~~}{{{~{}}}~}|}}~}|~|~}{|~{}{zz|{}{yzz{}~{|{||~~|||zzy||{}}}|~~z{z||{z||}~}}{|uy{{||||||}}}~~~~~~~{~}||}|}}~~|~~~y}zyxyz|~}||{}~~}y}~}~}~~}}{~}}~|x~}~~~}{||}}}~||y|xxwyz~}~||||~{wyyvzx~~}z|xx|{}}~}~~~|~~~}z||~~|}|z||yyyyz}~}}}zx{wyyy}{yyzuz{|}~{{z|~}}{~}~~~~~}~|}~}~}~~|~{||{}{}}~~}~|}}~{~z~}{xzwy}}|{x{~|}|}}|{}z~}{{|{y|}~|}~}~~~~|~|{z{y{~}}}|yyzx~~zy|}}}~~}|}z}z|||zz{|wzz~~}||~}}}|{{|zy|~|}~}}zx|y~~~}y{}~|}}~}}~{z}{}{|}~yyx~~~~{|}|}}}|}||~|y{|~~}{{}}~{y}{~}||}}~{z||}~}|z}{}|}~{zw|}~|||z|~|}|~}}~{|{~}{{}|~{y}{~|||||~|z{||}~}z|~{}}~|{w{~}}}|{y|}{}}~~~|}{}~}{{|}~{z~|~|{|z|}|z|~|{}~{{|~~||w|}}|~|~zy||z~|~~~}}{}~}{||}{{}}{{|y|||y|~|z~}{{|~|}w{}|{|~zx||z~}~~~}}{}~||}~|{~}|{|y|||y|~|{~~zz~}~|}w{}|||~zx||z~}~~}~|~z|~}|}~|z~~|{{z{}|yz{{}{z}}~}}yy}{}~}{y{}y~}~~}{~z~}~~|~~~z~~}|{|z~|zy{}}{y|~}~}{w~~{~|~~|yy~y~|}}}~{~{}}~}~~{}~}{~y~}{x~}}|}zz}}|}w{|||~z{w|z}{zu~|z}}~}}}z~||}xw|}{{{}}{}||{{z}{}||z}}}}~zwx~|~}~z|}}y}}}|~~|}}||{|}}}}~~|~}}~|~~~||}~~}~{}}vvw|{{~~}|{}}~}z{~y||~~{~{{y~~}~z{|{}~}zyurxwz}zx}}w|}{|}}{{{||~}~{y{{}|||~{~}~|~}w}zz~|{{xuvsxy|{}{|~z}|}|~}}}{{}~}~|}wz~~~{}}}~~x{{z||}}}yyw|yyy{z|u}}yz}~{~|}|{~z}}|{z}~~}}{}~}{y~|z{w~~yvzxyz{z{z|~}~yz}x|~{|~}z~}}z|}|||}|yzxz|~|}}}|yt|yzz|}{yzz~}~~{{|{z~|~|z|}z{~~z|z|w|zy}}~}}zzxwzu{~{}yw~}~zy~|{y~||zz~~||{}}|}}}}~}~y}{zzv{~|xvyz{}x~z{|~~}|}}z{z{~}~~}{}}|~}}~}}}~{|xy}{{}~}|~zyyz~}|}z~z|~~y}x|~{}}zz~}yy{||{||~{~~y|}yz{|{zzw}}}}|xw{}}||w}|{}yz~~{|}{|yzx~{~~~}{|yxvy}zx~z{}y~~||xz~}~}||{y~~||}~|zy}x}|~~~~~}}yx{||{|{}}||{y}}}|||z}~}z}}y{|~}}~}{y}zz~}}~}~~}~|yzy|~|~~|z{~}|z|||}y~z}xzz|~}}}~~}|~|}}~}}xu~~z}}{{w{|~}~~{}|}|{}{}~|}~||~|~}~|xz{v|zzyx|}}~}y{wxz{|~|{}~|~~|{}|}}}~z|{~~{wu|}{{{{xzz}|~|~|wwuvux|}}~}}|~~{{||}{}~}|}y}}}}}}{uvz}}}||}|}{~~~vvwttuy}}|~}~~~||}|}|~}{{{~~{|}{vs|}y~|{z{|~}~~}zwyvuuw{}{~}~{~}{~}|}|~~|~{|~|yvxz~}{yy{|~|~~}xxxvuvx{{~|||}}}~~~{}{~~{{|}{wv||}zz}~~|~~ywyxvwxy{|~{z}{~}}}z}|~|||~{wu~~{~z|}}~}~~yvxywwwy{{}~}|{|{~}~~|z~{||~~zxt}~{~{{|}}}~zvyywwxyzz~}~~z||~|~~~}{}~}~||~~~zwt}|z{}~~}zxyyvwyz{{~~~~|z||~|}~~~z~}{||}||z{}{|{}{|~~||yzx~z}||{~~~~{}~~}|~}~}zxz}~}~~{{}}x~|wy~~~~~~~|yz{}|{||~|}|xzz{z}}z|~~~|v|}{|}~}yz~~z{~}~~}~~~~{w|xy~}z}}~|}}y||{|}~|{xz|{}zz|}~~yy|}z{}||~}{|}~~|{|}~z|}|{~~}z{~|{z}{}|}~~zz~~|y~}y~|z|}}~zyx}|{z}{{|z~~{{~|zz}}||~|}}~|wv~z}|}|{wz{~}}{~}{}~yvzzx{ywzyy~~yy}{z~}vx}y{}}~}}}}~{}|}|~}~|~{|{{|~}wyxutx}}{zy~{yyzw{|~{|~~{{~~z}{}zz{}{~zz{{}~~x{{uy{wy}}~{yyx{z{~}~}z}}~{~~~~~|~~|~~}|}|}|{xtruxwzz||{}yxxwx{{z|y|~}w|}}|~|}~}y|~z~~~z}{}|yy}z~~}v{~~~~z}z}~z}y{yyz|||}}}~|~~|~~{}wv{}ywuxz|~}||~{{}}~zy{x{|}}|~}w}|~{||}~{|~}}||}|}|||{z{zy{~|~{}~yvz~{{zz||~}~}{}~|{}z}~|~}{|}{|{}|zx|}|||~zwyyzyz~}{x|}}|}}|yz{|}{}}{|~{y~}{|~{|{~xx|~yz~zu{~y{yzzyy||}{{yz~|~{|{}~|}~}}|~wz}~|~{|}~~~|}{|x|zywz~}}}zy}~}}~}x{|~{}{}~yzxxw{||}|~~|z}}~}|w|z}~||}|zzzy|{|{zyv{}~z~||~~~}}~z~}{~~~yx{{y}|}|~{xz{~|yz|~~~{{{y|{v{~}~z|~~~{zz}}}z||z{~~}~}~}}{~}zvyxxxz}||}}z}~|}~}zy{z~|~|y{~}|}||z}~}{~~}~~z{}~|}z}{~{|{{~y|}}~|{{yyywy{~|}|z~~~|}}{z~|{}~}~}~{y}~~}||~yxxz~~}}~|||~~~~~z{z{z|}}~}{z|y~zy{{z~}~z||}|||}{zv{|y{|{}~|{zvx{yw|~{{~}|{~~|}||~}}~|~{y{}}~}}~}w|z{~z~}yy|}}|}wv~~{|~|{~||~~~~{{yy}{|}}|}~~~{}~{z|}y}}yzxx{~~~||}}~}|{{|zzz|{||zz|}~~~z}~~|z~|}|{|~|||}{zz{~}}~~}~}{{wxzxy|}~{}}{||}{z}}~~~}~~{z{z}~~{xzx||}y~}}}yy{~}|}}{|zx}||}~}~}~~~{y{xz|~|zwz}|~~~}}|{~~z||yyy{|{}~}~{yz}|~~}~}~|}~~|||}{||z||zx}~|{z}|~~~}{xy~~~yz{z|~~~}{{}~}~|~}~~~|{|{}|xx}z|~}{~~|w{|z~}~~~xx|~~{z{|}}||{}~{~~x{{xx}|{~~~|||}|{{{{|}}~}~}|{{{|}|{~}~~|z}}|~{|zz{{{~|{{{}~}~~}|}{z{}~~}}|z|}|{~}~~~|~}~}zyxz~~|}zxz|}}zyz|~~{|}~~|{x~|{{}~~~~{{~~}|yz~}}}{z|{{}}~~~|zz|~|~}~|{~}}|z{z|~}}|~~|z~~}|}~|}||zz}{y{~~}~yx}~~~|zxy{~|{}|~|~}~~~{{z{~{{}}zy}|yz|~~~~{{{{~~}~~~{{{}}|}~}}~~}|~|z}~|}~~}{}|x|}|y{{z}}}}}~|z{~}}~~~~~~}~~}~~~}|~|~|{~~~}}}~~{z|}~}|zww{}~}~~}~~~|{{{~}}}~}~{{~{z}~|}~~~~|~~}}{{z}}{}|zz{{|}|}}}~}~}}zy|}~}~}}|z|}}~~~}}z~}~~}z{xx||}}||}{{|~~~~}~~}|}|}|}}}|~~~|{|z|~}~~}~~|zyyxy|}}{y|}yz}}~~y{~}}}|~~}|{y|~~{~~{zz}|zxx|}zz|{yy|~~~~{}|z|~|z{z{}}~~~|}|~~}~{z||wwz|}~|||zz{}}}~}}}}}~}zzz{}}}}}}~~~~~~}|zyzxxz{}}{z{z|~}}|z{~~}}}~}||{~~{{}zz~}{~~|~}{zz{{z{yyyyz}~~~~~~}}~~~}}|}{yy||~~}}}~|~}{|}z{|}~}zzyyzz{~||~~||}~~{zzz{}}~~}~~~}~}}|}~~}~}{{zyyxvxz}|yz}{z}~}~}~z{~~~~||~}|{y{}~|~}||}~{xy}|xz|{yxx|}}~~~~~~}}}}~||{z}~~}}~{|{|}}~{z{|zyyyyyz{~~~~}~~~~}~{{}~~~}~~|z~}~}|ywwwyy}yxy{}~}~}}~}{}}|~wxvy}}}}zzzuy{{|~~~~|{}|xzywxvy{~~|~~}~|zwxx{~~}x{|ywx}|~|~}||~}~}y{|~}|yy||z{zyw{z{~~}}{{|zz|~z}|yvw||{y|y{}~}{~~~{}yvzzy{|uvxzx|{|}zy~~}~}~|z{||{~yz{zy||~||~}}{~~|xwywy{~~}yz|}~{|~{z}|z~}}|~{z~{}}~}xxx|y{{|z|{{}|}~~}|~~|z||}~y~|wy}||z|}~}}~~||~}||{twxz{|}}~z||}}|zy|}}{|~y|{yz}}||y|}y||~~~{}}~||{~}|{{z|w{|}y{|z}~z}}{y}}~~{~~}~z{y{z||}~|~{}~~|~|~yz|vv{}z~{}||}}|||z}~~~zz~z||~~{z~|}vx}~|{|{~~~~~||y|}~~yx{y}yzz||}}~}|~|wz~}|}|}}~~~~|z|{|~|~~{~{z}|||}{zxxxzxz|~|z{~~||}||{x{{}|}~}|{|~|}}~~|{|z|~~|}~}||y~zzxv{|~|~|{y|}{|z|}}}~~{|{~~~~~~||{w|}~~~~{|~~~~}~|~|yxzzzz~|}|||}~~~zy|{}~|yxwwx{{~~~}{|}|~xy{|}{z~}{}||}}~{}z~~}}||zz}}~{|z{|~{~|z{zxy||~~~{~~~}xywv{{z{~~|w|}|}~~~|~z|z{z}{}}}~~}y|{~~~~z}|z~zyvuwz|~||~}}~y{|||~|}{}}}zz}{z||{}}}~~y~}z~{}~|~{xuvuzz~~}~~~~y||z|z}~~y~{{z~{y|z}|}}~|~}{~~}~~}}~|zzxxwxz}}~~|}{{{|~~|~~}z~}{zx~y}}z|z~~~{{}y{}~~z~}|zwwuxz}}}}}{~||{}|}z{{{xzz}{}~{}}~~}}~{{}z|~}}}~{~~{yxtwxy|}~}~~{}||}}|~~z~|{zx~{~}z|z~~~~z||{|}|}}~~}~}||wwvx{~~~|}|}~~~z~|z{{zz}{|{{{~}~~|z}zy{~|}~~||{vwxy{}~~~}}~|}}{}{|{|}z{y|~z~x{}|{{{{{|}z~~~~|{xvyxz}}~}~~~}|~~{z~|z~}zyy~{~{z{{|}y}yzy|{{~~~}}}|vwyyz~}~~~~|y}}z|~{zy}~|~{z~~~{~z{{xy{|y}~~}~|xuyxy|~}~~~}z||||}}yxz|}}}{z~~~}}{y|zyy|z}~~~~||uyx{}}~~~}}zy|}}|}~~yxz}~||z~}~~||y}{yx}y{~~}vx{{}~~~~~|}{x}|~|}~{yy~~{|y}~}}|}x}zzy}z{~~}}wxzz|~~~~~~}~{x~|~|||yx}~{}y~~~~~||w|zzw|{z~}~~}xyzz}~~}~{}yz~|~}}}~y~|{{|}|{}~}|}}~||~x{zuz~~|{~~zxx}zx}}~||}|z{y|~||{{y{|z|}~}xy~~{}|w|vzzz|}}|{z~~}|~~}~}~{x|}}~~|}zz{|y}}}~~~z}||~}}~|z||{}wx{|z~~zyz|}|x~|~|yx||{{z~||w{}||}{}}~}|}}~}~~uuzy{}~{}~{~~{|z}~~|}{}}{zz|}|{x}{uz~|~z|}|}{~~~~yx{uuytw|~}z~~~{}|~}{~~||yzy{x|~~}{|z{|z}|~~||~zyy}wyywz}|y}}~}|}}}}~}~|~|}}{~~}w|}~}~y}z}~|{~~~~~{{|tw||}}~|}}{{zx~~~{{|{~{{~}}}zw|{z{{~}}y{}y|~}z~}~zxyytxutw}~|}~||}{{~}~~}w}}||{{z|zz|~}z}|}zx|}}z~yw}xxvy~~~~~~}~}}}|}x~z~|x}~|||y}~y{~}y~~~{~~}}y~~~zwvtvwyw|~~~}y~z}z|{z{w{zz{}}~~z}{|}~~z}|~~{~~~}yzsu~z~|{~}|~~|{zz~~~~~}|}}z}}||}|z{||yz}}}~|}zx{zxxtx|}|}}}}~{~~~}|xw|~{{}}~|z}}}{|~}}}{|}~wy}vvvsswxx|}}z}~~~~|}z|~~~{{~|x~~}}{~}zy||{|~}yz}yzxwwy~{~|}}|{{{}~~~}~|z|}~y|~~{}~{{}zx~~}}yytzz{yzwyx|{{ywzz}{{{{}{}~{xzw{}}{y{{}~zz|~{~{{{zzyvyz~{{x}z{~~|{|{}z||xvx{~~{||{}|}~}~}~~~~}}}~{|xzz~z{~}}|}|y}}||~}|{z{~|}|}}~|xw~}y|}xwttvz}zyuw{xyyxvxxzy}~y}}}~z~|~{y{}}z{|{~~~~~|{{|}{z}|{}}~}|~}||}|y{~~}|{zz}~zxvz||{{yxyy{z{{{{|zwz~|{x~~|||}|}~~zz|}|z~|~~~}~{{~}}~}}z{||x|}~}{xxyyy~|xxvxyywxxvwy{z|y{}}~~{~~}{}{|}z{~}|z~}~~~~}}{|{{z||y}|~~||{|w}~~}~{|||zw{{z|}zzzzzx{xz}||z{~||x|{|~z|~~}~~|yz||{~}|~~~~}~~~|~||}{}~{z{|}z|}~~{|xxx||zyx{zy{zzzy|{z{z}}~{~~z|}|}~}}zzz~}}}}}}}~~}}{~|{y{|z|||~~~|}|{~~|{{}}{{x|||~|zyy{{z{y{}~}{|}{}}z{~|||}{|}~}~zvy~}|}~~}~}|}}~}~}|~~~~|}yz~}{~~}~{yyz|||zwzz{|{yz{|}{|{}~~|}~{~}|}}}||{y|~~~~||}~~~}~|}{{{}}}|{}}}~~z|||~}|}|}~|z}}|}|z{yzzz|z}~zz~|}~|{~~||{~~zz|~~}}xx{~~|}}~}~~}}}|~|~~~}~{z}{y}~~~~}|z{|}~~}z{}}}}{{z{||}{~|z}~}|~}|~}{}}zx{~~|}|}|{{~}|{z||}{}~~~~}}{~}{}~}~}{{~~|}|||}}{~{x{~|~|{~}{{{}z|{~|{xvy~~||}~~~~}}~}|}}}{|~~}{yz~}yz~~~}||}~~{z}~~}|{{{|}}|{~~zz~}~|||}}}}}|xy~}~~~~~~}||~|{{||{z~~|~|{~|z}||~}}||{|}}{}~|x|~{}|{|~|zz{}|||}~||yvz~}|~~~~|}~~}}}~~~}|yy{|zx}~}}}~}{|}~~}}||{|}}|~~{z~}}}~{|}~~~~~~{xz~~}~~~}|~~|{{{|||~~|~~}{{}~|{~|}~}}}}}}|~}{y}|~}|}~}zz|||{}~~~~~}|{ywy}~}~~~~~}}}}}}~~}}}~~}|zy{}|z{~~~~}|~~}}|||}}}}}{|~}}~}{|~}||~~}|zy}~~~}||~~}|||||}~~|~~}~~~~}|}}|}~~~~}}}}}~}zz~|~}||}}{yz}}|z{|}~}~}|zzxx{~}}~}~~~~~~}||}~||{z{}}|{~~~|~~~}}|}}}~~{{~~}}~~}{|}||}~~~~||yy}~~~}|~}|||||~~|~~~}}}}~}}|{}}}}~~~}}}~~~|z|~}}||~~|{z{~|{z{||}}}}{zzxz~~~~~~~~~~~~~}|{|}~~~~~}|{zz|}|z}~~~}}}}~~~~{{~~}~}{|~}||}}~~~~}|{yy}~~~||~~}|||}~~|~~~~|||}~}}||~}|}~~~~~~{{}~}||~}|{{}~|zz||}|}|{zyxy|}}~~~~~~~~}~~}|{{|}~~~~~}||{z{}}{{~~~~~~}{~~~}}~~}|~~}||}~~}~}|{zz{}~}~}}~}|}}~~~||~~~~}|{|}~~}|{|~~|{}~{{}~}||~~|{z{}~|{{{|||||{zyyy|~}~}~~}~~~~}|{{{|}~~~~}|{zz{||{z~~~~~~|}~~|}~}|}~}||}}}}}}|{zz{~~~~~}}~~}}~~~||~~~~~~}|{{{}}~~~~}||{|~~}{~~||~~}||}~}|{|}~|{{{{||||{zyyz}~}~}~~}~~~}}~~}|zzz{|}~}}}||{z{|}|{|}|}}~~}||}~}|||}}}||{{zz|~~~~~}}~~~~~}~|}~~~~~}}~~|{zz{|}~~~~}||{|}~}}}|~~}||}~}|{{|~~|{{{{{{{{zzyz{~~~~~~}}~~~~}}~~~|{zyz{{|}}}||{{{|~~|{~}~}}~~}||}~}||||||||{{{{}~~}}~~~|||}~~||~}|}~~~~~~~}}||||}~~|ywy}|{|}~~~~}}}}|}{~{{~~}~}|||}~}}}~~~~}|{{|~}~~~~~~~}~}|~~zvvz}zyxxxxxyz|}{z}~{{~~||}~~}|{{|~~}}}}~~~~~}}~~}{yyz|~~}}}}}~}}}{}~}~~~~}}}}}|{yxxy{~~}|{{|~~}|}~|zy||zzz{{{zzyxxxy{}~~~|{zzzz{{{{zzz{|~~~}}~~~}|zxvvwz}~|zyyz{ywx{~~||~~|{{~}~~}||}~~~~~~}}}~~}|||}~~|{|~}{z}}}}~~~~}}|||}}|zyxxy{}~}||}~~}|~~|zy{}||||{{zyxxxy{}~~~|{zzzzz{{{zzz{}~}~|}~~}}|{ywvvx{}~}{{||{yx{~~}|}}{{}~~~~~}}}~~}~~~~}}}}~}{{|~|zz}~}}}~~~~~~}||||||{zyxxy{}~}|}~}|~}{zz|~}}}||{zyyxxy{}~~~}{zzzz{{{{{zz{}~}~||~~}||{{zyxwwy{}~|||}}{yz~~}}~~~|{}~~~~~~}}}}~||}~~~~~}}~~~}{{}~|z{~~}}}}~~~}}||{{{||{zyyy{}~~}}~}}~}}|{z|~}}}||{zzyyyz|~~}{{zzz{{{{{{{|}~}{|}~~}|{{zzzyyxxy{}}}}~~|{|~||}}~~~|{|~}|~}|}~~~}}|||}~~}{{|}~~~|{}~~}}~~}z{~~}}}~~~}}}}}|{zyyz|}~~~}}~~~~~}||}|yy{{wvvwy{|}|{zzyyzzzzzzz{}~}||~~}}~~~~}}}}}}||||~~|{{{|}|z{|||||||}~~~}}~~}||}|{{|}~~|{|}~}{ywwyzxvwy||{zyzz{{{{{zzzz{{{{zyyyz{}~~~}|{{|}~}}~|zzz{|}~}{yxwwwwwwwwwxy|~~}{{}~}zz{}~}}}~~~}|}}~}|{||{{}~}}}}~~~}||||}~~~~~~~}|{|~||{||}~~~~}}}~}zyz{zwvx{|{zyyyzzzzzzzzz{{||{zyyyz{|}~~~}|||}~}|||{zzz{|~}{zyxxxwwwwxxz|~~}{{}~|zz{}~}}}~~~}}|}~~}||||{{|~~~~~~}|||}}~~~~~~~|{|~}|{{||}~~~~~~~~~}|}~}{{||zxwy|||zyyzzzzzzzzz{{|||{zzyzz{|}}}}||||}~}|}~|{zz{|}~}{zyyxxxxxxyz|~~}{{}~}{z{}~~}||}~~}~~~~}}}}~~}||||}}|}~}}||}}~~~}||}}|{{{|}~~~~~~~~~~~|{{}~}||}|zxx{}}|{zzzzzzzzzzz{{||||{zzz{||}}}||{|}~~}~~|zz{|}~}{zzyyyyyyyz|}~}{|}~}{z{}}|{|}}}~~}}}~~~~~~~}|||||~~~}}|{zyxxxyz{|}}}}~~~}}|zzz}~|}~~}|z{{|~~~~~}|{{{|~~~|yy{}}|{zzz{{{|||}}~~~}}}}~~|{}}~}}~}{yxx{~~}|{zzzzyzz|}}}~~}|~~|yxy|~}||}~~|z|~~~~~~~~~~~~~~~~}|{{{|}~~~}|||}~}zxvwx{~}|{zzzzzzz{|}}}|{|~}{yxyz|~|{}~}~~~~|{zyz{||~~~~~~~}||||}~~~}||~~}{yxxyz|~~}}||||||}~~~~~|zxxxz|~}}~~|zz{~~}||||}}}}}}~~}}}}}}}}}|{{{|||||~}}}~|ywwwxz|~|{{zzzzzz{|~}|}}{{|~~|{yyy{|~|}~~~~~~}|{{{{{zz|~~}||{{|}}~~~~~~}|{|~~}~}{yxxxy{}~~}}}|}}~~~~|zyxyz|~~~}{z{}~}||||||||}}~~~~}||||}}}||{{{|||{{}~~~~}zxwwwyz}~|{{zzzzz{|}}||~~|{{}~~|{zyyz{}~}~~~~}|{{zzyyxwwwxyyyz|~|zz}}}~}||~|zyz{zyxvvvwxz|~~||}~~|zyyz{}~{yz~~}}|}}}~~~~~~~}||{{{{{{{zzz{|||{}}|||||}~~~~|zxwwwy{}~||{{{{{|}}}~~}|}~}{{{|~~|zz|~~}}|}}}~~~~~}}|{zyyyyzzzyz|}zyxxyz{|}~~~}|}~~~~~|zyxxyz{|~}}~~}}~~|zxwwwy{}}}{wx|~~~~~~~~~~~~~~}}}}}||{{{{|}}||}~}}~}}}~}|zyyz{}~|||ywwwxz|}~}|{|}~}{zz|}~}|zyxxyz|~~~~~}}}}}~~}}{yxxxxyzz{{||||||{zyz{}~|{{|||{yyyyz{}~}|||}|{|~~|zyyyyyyyz{{|||||{zzzzzyyyyyyz{|}|||~}}}~~~~}}}~~|zyy{|{yxxy{|}~~~~}}||{yxxxxy{}~~~~~~}|{{{{{{{{{{{{{zyxy{}~}{zz{||zyyxyyz|}~}||}~}|}~|{{zzzzzz{{||||{{zzzyyyyyyzz{|||{z|~}}}}~~}~}{z{||zyxxz{|}}}~}}}|{zyxxxyz|}~~~}}||||||||{{zyxxy{}~|zzz|||{zyyyyz{|}~~~}|}~}|}~}|{{{{{{{|||||{{zzzzzzzzzz{{|||{z{~~~}}}}~~~~~~}{{|}|zyxyz|}}}}}}}||{zyxxyz{}~~~~~~}}}}}||||{zyxxz|~~|zz{|||{zyyyzz{|}~~~}}|}}~}|~~}|||{{||||}}||{{zzzzzzzzz{|}}}|{{}~~~~}}}~~~|||}}|zyyz{|}}}~}}}|{zyyxyy{|~~~~~~~~~~~~~~~~~}}}}}}||{zyyy{}~}|zz{|}|{{zzz{{|}~~~~}||}~~}|}~}}||||||}}}}||{{zzzzzzzz{|}}~}|{|~~~~~~~}}}}~~~~~||}~}{zyz{|}}~~~}}||{zyyyz{|~~~~~~~~~~}~~}}}~~~~~~~}}}|{zyyy{}~~|zz{|}}|{{{{{||}}~~~~}}||}~~}||~~~}}}}}}}}}}}|{{{zzzz{{{|}~~}|{{}~~~~~~~~~~}}}}}}~~~}~~|{zz{|}}~~~~}}|{zzyyz{|}~~~~~~~~~~}|}}}}}~~~~~}|{zzyz|~~|z{|}}}|{{{{{{||}~~~~}|||}~~||}~~~~~~~~~~}||{{{{{{{{|}}~~|{z{~~}~~~~}~~~~}}|||}}}~~~}{{{|}~~~~~~}}|{zzyzz{|}~~}~~~~~~}||~~}|}}~~~}|{zz|}}|{{|}}}|{{{{{{||}~~~~~}|||}~~~}|||~~~}|||{{{{|||}~~~}{z{}~}}~~~~}~~~~~}}|||||}}~~~~~}|||}~~~~~~}||{zzzz{|}~~}}~~~~~~~~}||~}|||}}~~}|{{{|~~|{|}~~}||{{{{{||}~~~~~}|||||}~~}||||~~~}}|||||||}}~~~}|{{}~}}}~~~~}}~}}~~~}|||||||}}~~}||}~~~~}|{{zzz{|}~~~}}}~~~~~~~}|{|~}|{{|}}~~}|{{|}}|{|}~~~}|||{|||}}~~~~~~}}|{{||}~~~~}||||}~~}}}||||}}~~~}|{|~}}|}~~~}}~~}}~~}|||{{{||}}~~||}~~~}}|{{zz{|}}~~}}}~~~~~~~~~}{{{}}|{{{|}~~}||{|~~|||~~~~}}||||||}}~~~~~~}|{{{{|}}~~~~~~}}|{{|~~~}}}}}}}~~~|{{}~}||}~~~}}}~~}}~~}}||{{{{||}}~}}}~~~}||{{{{||}~~~}}}~~~~~~~~~}{z{}}{{{{|}~~}||}~||}~~~}||||||}}~~~~~~}}|{{{||}}~~~~~~}}|{{{|~~~~~~~~~~}|{|~}|||}~~~}}}~}}}~~}|||{{{{||}~~~~~~~~~}||{{{||}~~~~}}}}~~~~~~~~}|zz|~~|{zz{||}~~}}}~}}~~~}}|||||}}~~~~~~}||{{{{|}}}~~~~}}|{{{{|~~~~~~~~}|{|}}|||}}~~~~}}~~}}~~}}||{{{{||}}~~~~~~~~~}||{{||}}~~~~}}|}~~~~~~~~~~|{zz|~~}|zzz{||}~~~}~~}}~~}}}|||}}~~~~~~~~}|{{{{{|}~~~}}~}}||~~}~~}}~~}}}~~~~~~~~~~~~~~~~~}|{|}~~|{|~~{{}wxz{|}~~~}|zz|~~~~}}}}~~}}~~{z}{yxyzzyyxyyz{||}~~~~~}}~~}}}}~|ywvx{}{yyxwwwwwxz{|}|||}~~}}~}|||}}~~}|{zzz{}}|{zzzzzzzz{{{||}~~}|{zz{|~~~}}}}~~}|zzzz|}~}{zxxxxxyz{||}~~}|||}}~~~|{yxwwxz|~}|zyyxxyz{||{{|}~~}}}}~~~}}}}~~~|{zzzzz|}~}}||{{{{{{{{||}~~}|{{z{|}~~~}}~~~}|{zzz{|}~}|zyyyyzz{{||}~~~~}}}}}}~~~|{yxwwxy{}}|{zzzz{{{{{{{}~~~}}}}~~~~~~~~~}|{{zzzz{|}~}}|||{{{{||}~~~}}|{{{|}~~~~~~}|{zzzzz{|}}|{{{{{{{{||}}~~~~}}}}}}}~~~|{zyxxxyz|~~}||{{||||{{|}~~~~~}}}}~~~~~~|{{zzzzz{|~~}}}|||||}}~~~}||{{||}~}|{zzzzz{|}~~}}||||||||}}}~~~~}}}}}}}~~}|{zyxxxxz{}~}}||}}}||||}~~~~}}}~~~~}{zzzzzz{|}~~~}}}}}}}~~~~}|||||}~~~}|{zzzzz{||}~}}}||}}}}}~~~~~}}}}}}~~~}}|{zyyxxyz|}~}}}}}}}}}}}~~~~}}}}~~~}|{zyyyzz{|~~~~~~~~~~~~~}}||||}~~~}|{{zzzzz{|}~~~~}}}}}~~~~~~~}}}}}~~~~}}|{zyyyyyz{|~~~~~~~~~}}~~~~~}}}}~~~~}|{zzyyzz{|}~~~~~~~~}}||||}~~~}|{{zzzzz{{|}~~~~~~~~~~~}}}}}~~~~~}}|{zzyyyyyz{}~~~~~~~~~}}}}~~~~~}|{zzzzzzz{|}~~~}}}|||}}~~}}||{{zzzzz{{|}~~~}}}}}~~~~~~}}|{{zyyyyyz{|}~~~~}}}}~~~~~}|{{zzzzzz{{|}~~~}}}}}}}~~~~}}||{{zzzzzz{|}~~~}}}}}~~~~~}}||{{zzyyyyz{|}~~~~~}}}}~~~~~~~}}|{zzzzzz{{|}~~~~}}}}}}}~~~}}||{{zzzzzz{||}~~~}}}}}~~~~~}}}||{{zzzyyyzz{|}~~~~~}}}~~~~~~}}}||{{zzzzzz{||}~~~~}}}}}}}~~~~~}}||{{{zzzzz{{|}~~~}}}}}~~~~}}}||{{{zzzzzzz{||}~~~~}}}}~~~~~~}}}}|||{{zzzzz{{|}~~~~~}}}}}}~~~~}}}||{{{zzzzz{{||}~~~~}}}}~~~~~}}}|||{{zzzzzzz{{|}~~~~~}}}~~~~~~~}}}}|||{{zzzzzz{{|}~~~~~}}}}}}~~~~~}}}||{{{{zzzzz{{|}}~~~~}}}}~~~~~~~}}}|||{{{zzzzzzz{||}~~~~~~}}}~~~~~~~}|~utzuqsruux|qnr~vyhhgeinp|qxu{|{qwzqj|rky}|zp{su~}kt}orumq|rlpwlmmnpj{tn~~of\qeXkukmv}yr}n|~|x}~~|xrhqukoyvknj|tyz}qvuw}uxxuwxvmps}}vwx{}}sulo~~}y}rmnMXftxzpiXndg][dejpoixvkqz|{xopnzqktvq|rz}ztisss{oqpuyzw~~|yohsuuu~vqt|vw}qt}{yzz}|w}{xv~bb[^gjy|qqio^jn[amjfxosoux}vl}tmj}mimejksvw~sxznznxusluuxrt|}uuxoplt}xwr|}w~m{|y|x{{{t|rqdggVlqvwolmf_drchlknxqt{m||oxtpumjfiZfns~q|trrsvsz}wttoy{oxqpzqvtzp~{v{}v{~xz{slk]\Uuustq|lmqpgkfiotryygwzlmwmjjctn_cvkkyq{ypu~glqnz{x{zzunzpesyjizs||yz|uxp|~}q~{wrZg__enp|yrxsbk^ouf^sog|{uj~}jv}tqtacghi}|}xyrwoovzwo}|w}x{pntnpqsqz|~urw|x}~snyz{uyrw{~t|setfRabzz}k{vrpukjvnj{zvpouqivm^akq^ZZtmty}xyurrrwu|xw|z~qjkyznqwwmw}~~yyw{rputtz~xzwj`ca\ivu}}tvpltzicmulq}mhw{rtwowpmjdjty}|wzzvtc|yjn}tz~}nsqjqsafoqy|xz{wwnq{w|}y{~ytx}}zylpb_mZ^Vqxw{ws}sillvowpu|yutqn{vnf^prXTkljt}uv{nx}~y{~qxv{mrfzfdwvg}om~ws}}wztvuvxm|~xx{rx{ozmagnckou|}pynxi_vrntvzit}dhm`jlqk}~w|}suo||ld{s}xvw}{v|ohnhknyqv|zvzyv~~}}v|{yy{~wtgvcZeXbgb}vts|yqnymv|onvkq|rkxqkjdogbgrxry{plzxy|x~{wtxfnygdhkad{w|}xx{~ux~{w|~vu|q}v{pul||`cmljaaljut~vnhrogv{x~jp~snwdpifel|yzx{{}{sjwswurvswytuxlnurnnfuxym|~ztw||~r|pulx|z~zz~yxvwwu^bkanobpp}~|~wy~r|swzqjntnsg`[`rscfy~lzvv~vtxxqyqjpksZamsdz~{}{vysy||lp{wuyzxmyzixxh]diafiwou}{tsnrhypo{wkzwqzgfxtwx|}}{tv|tzm|}vwvz~|pujhupecicyu~}xvxwt}~z{x|}t}~x|~}ssttuwdfej_ce[wwyzy~w~}x{{t~}pu|twjkqo`[^t{oo{ztt}~u|~}zxsyqvknlk^h}pdfqt{~~{~z~xtt|}iw{v|}pry}~xrohokr|kqufXftsihssqyqr{vvsj}spcsviwoz|}yx~upm{|lrt~xxss~ylmldlldpxu~v}z~{y{y}xo~}|u}xy~|x{mmpjicbWfi`npr~y~xzrut}zuuypjollgoxzm}zyzv}zyzvzuuzepplj`epbgoxqw|~~~wzt~{|xsy~l{u{{xfmzyoqpf\po_Zevprjwyyu||}v|mr|{jhrnxxnov~zupr}r}ztykqwrl}quncarquzy|}|zrv~vssxtun}x|~}yvzrvrswzllfiqocZh[rvoqvxp|u~zklmnrthcm|rqy~v|{yw}{|w~ykttr|jk^cuk_ew{tz}|u}ysvyo~ru~zy}z~z{ttoz|rlnvdZdha_gjenq}}{z}svw|xxvs|nxuzww}y}|{yyz~qz}rm{vwtnrfjrfgmkv~wzv~y}o{urpwy|z~zt~~svzs~ylfkhlihfjkej^h~}|v}uzwrldlyvlamv~vs|vsr~zxtzrnsbuxqdXeytns||wy{y~{okzszvvtyqz}~ysrovwheyrfjjgbj_^\pvkor}{}yy|wkv~yxkr}{xy~z{{~yovv|uysmswlpdwmfhgmwwrv|xzw~|~v|w|tkz{}{}y}ztvvppuhki`[ihnid_mpnw|u|rnvqosxslx~}|yw|yuwvz{xtnksjemgl_cxtt~|~xw~}}st|qrnsz|z}}vsyfntsqdfookhkdWene_iy}z}~|vunhr|zzmx|}|txmxysrysoeq{qn`joouol{||q|vo}~|rms|l{|~z}}{wlqwtsg|uslc_igbeelkshno}|zy~u~stmlxztm~zx|uxxwztrts}yldo]nrfc_r~wyzvzy{zor}yq}}|svz}{x|xlmpmwjqd`rodcnkeYcfih{{|xmwvquwxyp~{}{~w~}|wjjupklqhpehwhrxyx~zu{{w{|y}uzy|tytnrn~|wwuzrkttuujslopbec_cphiknqxuzww|v|yywujtwy}ut|~trzutytsupke\loof_x}}~|yrs~oo}u}ryv{t}~}x}v~qoraqxqi[dqqffkc[dg`bs|rw}jr~usp~{rx}y|xvllphdwmfefysprt{~}{pthffr~yst{njhkkreswstwqifnlovwsouvssqpcmnc`vtlqo{{}}}yhpoj|{~|w}vz{{|xw|}{|sjSnmkpsr~zow}|}vssvms|z~|{^cpjlfpqohxquz|yxyymvsrfa}{|nf_foievzy}}v~uyvqvyqp}}yvidyaX`_^[Vqhlxrow|~|~pwu}|s|ux{mdnjesx~zw|}~xwkxzuvz~ng\mrwxrrnsp~{yqy|tnose~yvkigy|wx~yvihrt}z{qwzzuvv{vrhkehnvzoiorgkudoihmkqyumnjuu}~}xhsprvnkshnsrnmtrfaidfu}~ytklttvuolqoueowwquzpj[af_c}wsxq{sqwyxz|}xswmutvryzsqxyz}}|vv}osn`WYXchrzsifrgenqeqpr~z{x|}{x}}w}~vu~mjimrv}zv}}|phkr||{ulpxeqzkppohcYdqlqyw||~tqspyqt~~xw{xnfdsqzyqhuysrk||{{{y~uuu|qjaprmsvnopyzxvx}puqn}zzysytnyx~uuusigondfay}~zzwustj`aiy}zpo_aj^]ngdd_oibaekmsx{u{}wpsu~~zkqt}vz|~~ztpifYdxxwzswy~|z~smtpnookysx}zwjwzsuuzmnryrnohkmox}x~uyyvw|w~~||}xwxkuv}}zqrxvzwz|y|vjidrnr~{~w{y~zwqnclv{~yszsn{uztjffYWaps}|q|ss}prskixrrlemyvvyuopndclmmlh}suzrmolfpfjx||~z{ttmtzwyu|qsotrjqoh`_bun|wy~urw|ty|{p|~upmqzwxtxw{zz{zzyyzxvjf`hgZ^^ir~|~~xzx~nv|vw|}}z{x~uwpsnp~{zo{tsqzx~|vxy}tn`zn^c`u~y{kosrhgdqwssjhvcfsjnmmnqnz|xua]^c{}{|~~jepyjqiw~upadnuxzvsjlzruwwtzx{~s_i{oejmkjvxuxxy~~ywg__e{wy}}y{srx~oswosz{xt^\apynujmy|{{rxsqsu{v{|nzvy~zw~vj^giwx{}~}vojv||}qc\`rz~zmhvzptpt{|yzqyonijv}y~~|~{jyved`l~~|ty~z~ymfjbX_`u|{|uxsnkkjvs{wn{}xv}|u}yx{sonc\]ft~z}|~~wricUWgt{}}{xwqj{smtzminovnysuyww{{t}qrkf_a|{{v|ytwz~yqzhig_bmz~~xvzxmyzywsuoh}qlqx}}{~vwtowsemhZis~z}xuuf`^WTgxzqrnilrorrtzsyuqtxx}pz{{}~~}qf_a[\j}|~}~}{y}|zxudc[^ij~y|{{rtqfnrggqntp|}xu||xrrq}vq{leonjqu{vzyxzvtsnqrW_kfkwrqs~trvsvy}skuljsyx{}|~zpzrqqp`Xbldo~}|}z{qqjaTW[`h~{wwusqmkgr}qmpux}tr~zs}yv~~zvrxiajbcgpz}{y}~|zx|xxnsxici`hoz|yxtkwsnxnfjil~~{rrtposstshxrfjhhqws{|x}}zxytjegj`\edm{w{x}rp~zwt{yngswox}x}z~twrhijg]`dibr~y~||qfwo[\`]^g~|tqolnpmxzgs{xuy}w~yom{}sxvwyxx|fag]`epyv}xz}{z}quxvvh]yylctmhkmv{z}{vxqtif{{}tyy{zxyyw_rruw}tzsjnynlrx~qy|{~x|yu|~kc~iny||z~z{|z|~woV_wvxz}}v{}{}zquv|ts|}xSVmutjm|{sp|zum|wz|{usznowv|zwzz{zu^Y|~zx~yw|{{|po}yqbbu|upojbqpcqvwuuvymo|thfrx{{{z}uvsrsrmrk_guy|wv}w~}yofaXRkz{sikh_aghkpfoururfmuu~{}|{stgedV\q~~}~|{~{|unabeYksytpjkmceecdhmm`hwy{rrs|sytpqvoeh{{y{~}qu~|yzhcnladztgo|mgonpsutpfmn^]it{~|}p~tusogacrd^s~wv~z|p}~shaY\_Xhqxyqjhgdcirhikmtnn}}~}|z|~{yxnengebap}|{|z|~}zxulgdacg|x|zpohgtkfhe`aefnsrw|squuzujsrkimjkt}xv{yu|tlfigXbigy{sms{ndrusonmk_dvgfw}{{~vvznjgc`^_niez~xy~}opraZY_aaqvrzpgdeghiodalqtmq{|tqvyyz{ytoabdddh{{~}yuvww{tmd_`ecs{vwtqjnha`\\fkkuxzz{~vpnlouhffbkwu}~}x{z~zurlhe`b[Wnuz}ontwsippoqqnjdmpepz|~~~}ymszlhfaZYcsryzy|}~|~vihaTTZeiw|lmkkiebfodamrqw||{{wr~ru|zupmj_Xlpip~~zywxxw{~v~}oplgedfokp~|tsqflrebabged{|xyyvmknmmpbaijpqq}z~zz}w}sqme^Whztg`fjuwjoplighjir|yxgf~ukhw~xxuz}tqoljinpewfeky{wvuohvtmv|qfg||rrolf`mykryrq}z^Tqud\_dfhemznly}y}n`f|yos}{vsy}yls~nfefb_gc]]_d]Uj||}qkwqneZUi{w~vypqusxmikcb_\YUWhqrv|volcby|yvvyrquz||yri^XT[{|tsmqriffgqzxy{wolv{sptmpskly}smbUMOezvwwmjf^X_gnko|{vrqqoky{lrzysov~}|zwwzzwwiXR]vwiiz~{}|zxv{w|vuunhurffiaQSkxsspooonxwk\Zvtq{uifntwyzsp{rjf`WPRkz}zvts}xumd^c{{tjkkb^\^c`]q|xz{rkdah~~ynuwok_X\du|wpec_[ZY_loy|uz~wwri]]bj|~~}yy|smf]Zbrqioj`]]gpkrwsrvwqnvxrsxtzsosskjvvyzhb\TPUk~vtohcky~{zyqt~mpvtqw|w}uolhfgnqhjd[_wnivupnjijn|tx{tssjjvmcbcd_cyttw|xrkebgyrgelkh`_js}|{uzofb]XXc|{skr|{xzsqokhl}uuvlfb_dowyzznok_X]kxkoqca`UTeus{te`\Z[`lyty{|ztpgaoy|vipl\UPNQb}lhljfhu~stzwtt{|nh}solijtzv|wootwu|yq|i_\[`n~uqljhlxxzvroolcfwthbgowwrmjkozyzoaWXahtyswvtqoqv}zultwjdbcjs}~}zoikr}}~vpmkmv~umpnjj`\gu~xsrsyob_][]h}}uq|}wz}mgkjjo|onpe^ZWZev{{}olpjbet~{~{y{}{shon`^b^Yf~xogcbdjwx~}zuple`j|vxtryvouj^YVTXjtgbgjgkyxs|}zvx~|kp~xsqomr|vssw{y}mrwl_WRR[o~zvtrpz|xwtjhvund^ahswqnllnv|wka[_hrzvwussu}|uldnoc^\[_gr{sruxsuznjfbahvzvyrpqiagswq{shfc_^g{|tns~zwrlwxe`egfjy}~|pjebchv~sx|u~zhekh_^l~{~}rmvocbebY^x{pgb``cnpn}}w~z~ywslej{vosy|rpsf^[WSVd~rjmspqyvy||}pszusrokmy}w{yqmlpvuxvyysmc[X\l~|xsqpnqx|}ywtkhutpmc^cn~vspopt{zxle\V]iwtruuuw|ymqxnfc`_dq}xw{wpou~~tkxtmjgegq~usuphiw~qiy~lb^[X[fx~{vv~zqkxzf[^bchx~{pkfdeix{y}w~neglhgr}|{|ysiclg[XZ^__rulhilv{u}}wzvw}romifgv|{~umqy||wrtj^[YXZdzyofgpuzysz}yuuvrrunknonpz~{sompysnqfbd`Y[g||wttvz~}~zxtjjtnjlh`dv|{ojghlsvw~ulhd_dzzmmpsw{~yjjri`^^_fy|rsxxv|~wdiungefhoxyzpjkopnwzlrvjdbcix|xvuu~zvrjaksi]X^hu}rkgfio}yr{{mb]bls}~||z{~uibjh\XZalwzxulgjq}x{zsor~{pmmnry|z}zxolu~ulgkpbVTVYap}unlv{|xwsovyichmovz{}tnjjozxmpp`\dgekz}xtsv{zzxtqibkmccijgn~}rmmqwyyzrmushddcdoujkryz~rlrlb``agv~pimux{~x~~ywyvgdmjggjmvvompvwzwjmtqkdabjy{xwxzyupf`kmgaZ\fvzoiefiq~xx|pidfryvwwy}zmadj`XXZan}xxxqnpy{{ymky}rmlllr}|x}{vvtnmxuilzqa[[[_j}xqln{{~zwrlu~obdjlq}wpljmt{ulrq`X]caco{z{~}wnjtqgfjlgi{}|rkkms}}yzxwpqyrhfeb`i{omsyz{{qqrh`^]\aoxpqz~|}~{{vkklfeffgm|xrprxz{~}wotspnhb`guzwvvw{|~}wslabliec[X`pyronpvv{xnib`ixxtuwy|~ocikaZYY]gxzwyvpov{vy~njy~uponns}yxxuorvgeutd[YXZbr~yspu~zumqyhadhltxpkhimv{{qtyn`\bgio|~{{}|zqintkdeikjn}|rlknu|xxyrnkswlfdcbcl~~rms{{yw|xkdcbbguvmkr|xzzwuploi`adfhpzspqwzvpuogjjeaft{xxz}{{voebljdee^^l|rnmpuwv{qliccqrmotxzx{ufagbYWXZaqzuy|{||njvvpnnnr{}|yrpsvtu|rcanoe^\\bo{wuux~yvogkxvlcagqzqljlpz|ukmtla[\er}{zy||qfjqibaelu~~{vvsmkow~yu}{oeblumghknuzrqzvnmyzkbabem{vojmx}x~~zuplfeom]Zahnyvplnu~xryugfnrquz}wuvzxzvpi_\fe^`gjjstnot{~zyxqs|wnkkjjt{~tijr{{ttsigjc[[_clrhdcheaY]cUS]rztnnmkopzjj_Xdilhjs|mh|tlghkxxt~pirvtv{x~~zrhpyqwpz|yskuxllyz|li|}vw~ltlfsjdbm{x~sqfv{ussmbVq{jhy~jxx|vpu{vn{tlqlo^YZv~~qprjwtpix}wqvorz~ww|ic`|xvc[`dbolv~~vfmqojr~}ytgorkclmzilkx~wwpnjluk|osloorzt^XX[aadjgwy}ryxvyjk~uuxn{z{z}ukgTijatzzv|r}t{z{zmmat~or^Y[Rhe_j^Xkhquwtx|vjltz~wmtscbqwv{r}{zpxx|~~pwjn~txglqpm\v|ruf^flnlgja{~~~|}{nrop~vyiorvryuopmiq~}vgmiiX[}~thzsn_jtmmplqoek\jvkxzgsuqvny{tw~{touust{qprqsekirzvnp|thWffhvq|tk{prupsfdttughxs}utd_yy|qyx|~xkpg}zwsotxu}z}}rmzjjsfs{kp|jmsqv}do|udwxssxz}xugxugtsi|vsshpmk{|~z}wk~tu}qxtx~{ddgbgvu{k^Y_cgdkohytozrx}wv}kjvpxzx~tsb]uqvluvz~~wxqgbijljxvsnmsggvrr\Zobalrty{owujfkv|}yweoteotkt}q}uswt~}njjmwot|uwy~jlgjf]Z]gkoqzq}y~~syu~}uxlYrtfl}zxo}ssry{y|zrz}qp_`em^jyomaoqfutzmckjtsnxeyxoszsy}|sqfqfdex|putz|~}ryxszygZWRfgg{qiwmszjkilv}to|x{rplsokdfao|t|yvuirs{}~wymyxy|yz{zorgd]a}{~soy{spu|tibeaszr|s|uxsvvlqy~tWgms|n{yout|x||xoopns^Yy}vnhjvzbmw}spolnq}x}sx|~|j}{eemdR^eryrz|~|}tx|zng\\d`hw{mlgwtrvv[okooc~z~vnm|~nunlwyxxywcd{kt}swp{|z|o{ssthZojQgz~voxnp}n|ydijeiqrjprs}vuimwiidlvsv}y~ttlzqwrww|yzngdVjg^olpuq|uutkivml|{xxuuq`{njgutwiXwsxwy~lsz{|xvvvqvscbc`dlrz~rq}o`qssotu}x|{yuvuj}mn{ni\_ootz~~x}{w{v|tz~ogfa^notup{ziktsxtuiwz{z~wvzvpttwhpi^l]~vmuxs}wpr|txuetdmgbmltz}vy|nmzffguoy~|qqqnrvqs{wlmkemymt}}yz}~zwqwf{ignh\otrc|xw{wobsoomcvru{}|k~|qpz}}ockmkrup|xwotyt||wxuq~igTZq]Ze}wwuwslrupw}x~u~rsks}mykwy`jljqvpx|uzvjy{zyvxrr{{vxk\bf_mjiwty|xtphmvrs|}{vtkjd~sfh~{rwrkawyvqqs~y}{twtutrsla`ffmwt{{~y}xxwmq|uv}~z}}{ttwlp}oumls^[dum{|x}s|zpq|qpl`bd`cnwzyqus~kv|ymu|}ywsqxvowyqxtadk_hg|~}vuwzsidpxk{v]`miphv{wvnarxhfwus{z{ystrklttrulgppiv{wxzyw~{rzwhf`wxdZbbYo{}u~|rqnjvunssyt{lxzvlxwsri]alqv{wtuzn~xvyokrkip_SRYsqm}x|~uqspvy{yqr{~in|pnwno~upejtsxuwqmty}~mmiwnbjvnob\fpelymmw{tq~|~~z~y{hhkx~aodbnoekpsoo{o~qjonbjfmbTSYlrywxwsvw~vy}yv{{|y{iilinvusglhhmt}|~xuv|v}tx{pth|{rk_\_\k|qztv}m|u_ouzw~wxutkook~vuqhh]itiwwz~v}tyq|wvtkzrclnmjfuc]jktwx~xqdh}mwv|nwwyllnnvm`lcism{y|{|qxz{~z|inocb[`qqf^gqay~pzx}vw|zz{r~w~{sxsuwxwfik`folsvwtxl|zstxmggatn``V^cl}|y~{yzxzx}~u{rmpstppyynqbkutuv|~{s{|wzs|yikwszljojffXigcv{~zyopw~uv~zyw}wvgltlpmkdfovr{{{~zsm~sstwurrytll\[Ycrlkzvx~zu}w|qz~}}|}xvln{pksa`mdhcrz|~~}xrv{zyxqk|pkdgwtjkcd_[y}xxuvn~tr|xw}uy|xqd{m_kipurwm~~{yjtqspzxgqznpmue_a\X]uwxz~|w~|{wt}{psorkxzysyp`gipmzxz~{y|n{x|~~nljpgisf\\K^vfcg~utsvt~npgaykflzk]pyxoj}||pxy|x~x}qwm|xip}yuam{lje[gpq}kohp}vs}yu{ro{qxvqc_kkqm{|r~wq}zp|s~}yuelilfmric_k`dpztwz|||vszugoxzjrm]^ccuz|{vixyy~vowlddZd|g\QZd^f~wo|x~zyzxrhj{thwzalomvzr~yt{ppwqzxuy~njzroqgeuga]c_hs{wufi}uy~prrqgqq{qqj`hm}w~vo~~juutjvljemywokqo~liwivw{wuilgkyk~j\ol~d\gdi{|zsuy|x~susrcmuoaivtsqx|ys{uskx~uvyj{l}kyws}jsryzmonpyrwyr|qtuqlalfftzxvzu}krxmtvi{oj}rshs{hxx{pxt{q}vx}zdv|qnzwlvsww~x}~zomsmo{ir{gjfnf]ogdqliii`nywxjforzjlzgosht{z~wy|wzzmrrmwoo}vz~skjzszqyz|{wox|kqxyy~{o}x}|z}so^hdsv~xvnvucjfV[g^Nelqc^aisjaym}tyvq}kpw~wwsw|q}y}v}v}vmdoenv{~qyvipqj^u|xwzzxuuqtphVa]huwyuomoadd][[[Lcu{okjo}ixwjtoo}u{irudez|o|}qvp{sqvusflgr~~|z~ryuyvowyyi|xy~~~myzsy}takgh{yur|kqnsthhnpZa{{uijovwehshjmgnx}nu~ui}t{~tpvyurzv|}jmmlz~qy}}zy|~slz{~x|{{}w{e`iems{}}}srppplnofOmwvnhdkvugsvsu|svziruhbywo{rsvo}tvytelgot|{~|uv{}yw{yxcq}}}q~xupoxt_bccpz|{qfipoiimo`Twzzqqu|~ylwwtx}myy|oypdlsefupyx}r{ylyzpysfpqz~|~zxnqwzyyoy{{y|xoqo{{egopx~|z}|knsuqtywlWuyytoouvshprqp{lzz|twzjrsg|zswwuk||wt~sx|hppvxzz}xu{pl~{|zp{wfozkajisw~kiptoovtrYe|srkmsxuqrxzx}swymzidrxfsszzyszw~l~rhxiflnw{nrz~y||d~tzw{~qjwcetp^gku||selronwwxh_~z{usx}vtrwzwy~izptipk_oop}}|v~r|{|~kwwettlux~~}}nox~}m~|z{~{}z~}~xj{hdvygjnw}|jrxvu|zxj_zxrpv{rrpqvwzk}sxqtrerrq{{x~w{~p{|r{xfutlqsz{|~}ps}}h~~}w{}~{~{}shv`cvtdhlvy~vjtvst{xxeb}uuqqz{ruux|l~mxmphatms}w~|~z~x{|lylbu|kkowz}mt{{zwk}|}q~z|}uygnm[jxleksx}mpyxu~{taxyyrxvuvtw~xntrlsipcj~wo}}{}u|z~z}soyemynqvz|}~y~ur}n}|ux~y|z~zzriuagxughosx}}}tz~~wdoxyrsysuqr}yvq{n{prhhzxnwxt}~~~wz~{~{p}km}{lkrtvy{vu}ow~|}ws{~|wwgtebrugdkpo|x|~u{|yih}yxow{ws|~m~nvtnjcowlrtsu}x{z~owrft|pilsp~{|~xw|~}un|z{ypwz}~{x~}vwlkn_iuoefply}{y}wix|~uv}{{ts}|srxkxomffusqwvuy{z}{xylymlz|pkrtsy~}sxtuy}x||wq{{~wu~|uwkonbnvnfhqk|zz~|tj|{~rw{|zrt|}pxyp|rmgivqtvusy~yz|zzp{mnz{oiqoru}|s{~qsw~z}zvpxz~{yw|tuklnajtnefpiz{yuky}~wtvt}~tt{nztkfeqptxuuw}x~{vznvmjvzpiorpx~t}trz}xzztos}y|xytxyzuonfocgrsjfpnuy}{pr|~p~ysx||m{psznjfnuuz|w~u}v~}xwrqvjr|yomvo}yyyzq{w{yxwrp{|}~}q~zrzw{orinkdnuogkrm{}sn|tx}uu|pxxp|tkhkut|{x{ywy|~}uwowlnxyqjsouv}x{quv|wy|vpnvz~z~qz{qxuynnijkcktqiirl~~up~~yvwu{puxnzujgjst}|z|zvxz}}{sunulmwyrktpww~~y{sxvz~}vw{tnnvyz}}oxvowsvlmhljenvrjlsm|~us}vz~uuz}owup|tkimtw}~zvwv|{}xttrvlqzyqmum}w}yws~v|y~x{wqnpwx}{vo{|qsvutlmjoghquohpluw{sy|q{uy~vt{sw}oiiprz|wt~{x{}turvpmtyulppnux{sv|uz{}x~yyullrs{~w|~ow|upwstmkjniemsqilokxvx|vvw|ysyvt}siimo{y}wv||w~{{vrrvqlrxwnorkx{}vuw}yzy~}w|zxxlloq|z~vquzvpwtrpjkokfkrskjpg{y}zy~xyuz}txxt{zkilnvz|x{zw~x|}yztrwunpwysmsiys{w~yv}~t}wzx{{y{pklor}zyvypwxrsxqrnioohhnsohnhory}}vxwxyyyw~tijlnx|uv{|y{xyrtyrnrwwonphxwywww}~x{w{~x{zx{}mjllvyzvxuqwuqtvoqlhqmfhoroiofvr{wwxwyxxv~rijlpz}wv{}zyywxpuyqnrxwpqokwyyxwy}~xzvz{wyxwy|ljllwz{wxtotsorumolgqngiospkogts~|~yxw~wyxyv}wklmp{yux{yw}zuyqr{rorxxrpqi{uy|y{{igp{nntf||xwldj|~|zrkmkqxuto~oi]fsgjeqyrsz}|}~v{u{yxtz{swzrwdjq{porr~~~~ttwvwv{}x{t~~ymms{{x{swt{v|~||okz~|y||nmoozyhWUeldXUe`_enttswp|ry}}{|{ofoy~ulnnu}~xyipx|yeXftrvoac`grjbgr}pxtnzxsjjv}|z~plmlzxztv~x~okYVcr{o}yrtz~|~u~xuofdqxgdadvpikeqxv{owtpj\dlu}~yrxxwz~~yw~|uspmdbs|yzv{|ty}z{|rrdaPSbsttkjmpmkjauykzunrqoerx}|zzytxunmxttrzz{qysoidZ`p|~pogorgbgrujtimxrrkehx{{|xyylkjm~vxqz|ttpjdV]gtz|zwzzz~|xwrkdh|xokhxznoimzvyurtoo_`emvwyr|wy~zsvypqmg`nq}}xxuw|zw~|spfcRQYew}ursuspkcwxjwukoplepo{~~{zyzx{vliryxt~}wsvzpnjcZcds{tnwrgfktqmpisvsqhbfut{zx|wkkhvwwsr{~utnn^XZ_l||}|}~t~{~ytjbewyqwzurmw~v{vspsj_``gs~{{s~zyz~~r{{xtoqmdhji}v{uzxv|~tmjbRRS\n|||ywjk}ulxrjooijpix~}w|x{|}wmho~~~{x~xttu~~pmkd]d_i}~u}zojnttoplqutskccnotw|z|olhsyxur|z}zqlocYYYcrv|{}|}yundbmz~|y{zru|x}uorqba_alw}wt{{|wst{|mmmgelep~yyw}~wx~z{|}ylfh\RSUas~{|oljbpuw{}stlqzyyvpv{otxy}rooq{{~~zspxorzmjlhemgm}hg~b`fouyyuop|zv{zv~wptnaa`fr~}yor}quwknrps{u~x|wv|}{w{}xestnla_ha\_bly~{yy{~v~xlookqto}lfikvztttvwupnu|vpjw}yyzuruxwidgwyyh\_][fehy|wuipvw|}{~~|ww|ndduw~sywlmns~|~}tj]Wdozzurokd_bliqykvplod]Wdqnxz{y{{mkridfhp{}|txtlhkusuus|}zy~~ue[Zowoqn`_^ajsun{xh~{pljpxyzvv{ljnlpxq{rmc_lu|~}}{vsw}xqlz}y{{uyne`ernnjhpkjyz|||{qndltvzzz~||xxz|th`ey{v~zpppu~~kfY[hs}|wuohdilhwtkvlklb[[lopzz~x~xioqgggir{~wnqkioy}uxuzx~yzyk_Yhswzvylfcagptou~jutokms{xu}u|timmpyqsxph^gnt~{vv|y~}|snyzyw|xwtjcdlyywsqxnkxy{|}{upekoqu~z~}|xx}zz|vkckws{zsqos|{pgY]cisxokmhlylnwknlc\dohnsw|tjunggdgqxwwyu~tmkovyv~{{z|v{}zxk`bnjr~rmfekqqo|xmvpmptuly|yrknnxvlv|~xtebgimw|wxwtz}|tt}|zvty}t~ukefkro|{~oryy|}~~zxnglmmqzy}xxytyy|wlgqtoz|wsos{}~ysi[]adkxyrqjmzolxnlog`ephlqs}}}ktqiheemtut|rxplnsvq}~|v{~vwpeamkkx|voil||vvxjg}~xqwwhijquc_jv~y~wsstndhlot~qk{vv~yt}zujbk~swqguxohdhnrl}|oohkhlopfcilou~|wpmrw{}qttkhtur|sdfwx~~tkx~trsmbekpwpa`{tm]RXUQYgdjtvqvvke^^jz{~}px}vsu{y}~yfl~~xqv{~|x{|ytxqmwk]U^wxeikb[Y^eg`m{wqksm`aaif\ixzuvwmhotx~yrnr{}~vt}wts{~skfjcd|xnleVUY\`ekpyncupijgdchjo{owujonhlzy|vtthego{{y~w}wpjgmxznmc[bhbkopv~{pwo_f`ZYZanwvu{unyzuu{|}yjhhrqpyv}{|~x{zon`^`j{yynggkoghyvpgim^YZbh_fu|~}}qsyrjouy}{}{vtqqw~|x~wvv|zhcclikuvpa]acfimqzoburiigeejjq{owwknpjp|y~rpoffhq|wyxyyrmjkrz|tsifnkhqrtzxqwiaf]ZZ\eqvvy{ts}zwx~}~}zpfflsltxz~z}{ppf__co}snpsmhwysmgog[[aja`nx}uqzwlmrvz~vvtqpqwy{~wurv}kdfngew}mhhhjlns}pdxvkkhfefcnzrzzmornu|v}srnedciwzx{yn|ltrfelrv|zyrq}~qvzusvqw}x|ufrgbirv{xtngqrtlbksx}wxux{erfeimil~}t}{xnlhejrx}vpqqmchuwmrz}|{yvsx{~xwvuuqrt`eykjd^dbdeejqvz~y~~w}vjkuzor|wzkbp}po{}|v~zqsw~rjjxhonge_gkbcmyqksvwvtt}xwyxxyz|u}{xrffrkfw|{{yuqpsu|wsvkWT`u}ohmsvwvu}|gm{rkheecae~~{~omkm{zusvzxnqpjnklmil~z}w|zmn}}yuvzhWhmisw}jmummrkny|lkotvldihpxz}{y|{{tnhjz|ynp}ww|~xv}tvpvyohbft~|sohfsjZdgdd]af]]iwsqzx}s{zxvvuuhkmyz~v|yllv{qwx\M]fo}xmedgWPgmfinsuslvwnswkl|~{xtnnnryx|}~|{}~yx|yyzytqzsunZYcn~{yzxmilhhebbacjt{{}zvtxmdhsqpoxmuztxu}~zwpolquvy||yvkeecpz|zph^dhchyxv|~xqsh|}{}~z|}rmxyvy}|~}~{xlnyxpx~|vvq^ez||}{z|qswwz|{trwu|{otzytqklorx~{utntz{}z|vt~||q~qzuvz|zt}ykjj]Yjzvyxwtrsrrtxy{yqvw}{sntmdivsmu{||xtxviw}}~mu~wvuuw}~zuoopprvwy{tuyw{rvyooz}rrx{ztogvvicdgjjmqnx{uyzwrqrtw}~ywzpis}y|uhz|n|{x|~u|zqtzpjv}zpdeijikqtw|~uytt~wurljvws|{vxy~xvzthqztsrpqsvyypvvs|}u}||xrwtijtqjry}~|hjxvtvwxyz~qv}}wz|{ty{uuuvz~y{|u}wz{qs}zqtwwwvstr`aruxon|xlhjlkfk|{rotqabo|yt{~yuvwwy||yxw|tr{x{uutpebooffpwmnzujlne]\\\`fmv|vyxy~|yrszvt~y{~|}{us}~tjr~uswqgdfhlqw{yuzw~wribfbZahbdov{|krzuvz{p{~z{vvutwz~w~v|}wpv|v{~wz|||{xyyihusruy}~{{whjxznijkibhyzssy{lis~}||}}~~w}|rtz}~reospt{}{|yoitxmgjog\dp{zz{miql`]^_bhnv~zu~w}x|~~xwvqhmzuljppdeuzwnljhhikooejtor{~wsiekfbjjcgpu{u|{|~x~ykeccdhnv}w|~tuxpqvojortwwuvvigtutw{{ljvynhjmmip{zup{|{|~vossmtyst|{ms}|~uy{xzyrioxpggmmacr~sjrwmfghjntzz~}w}|yzxqmx~unntodj{~xtmlkjklosogqvpv{ukkpkjrplt{{v}ywwsv|uo|{yylhhhilosvmjvus~|tx{ux{rotuuvtrutjq~zmp|~spsurnwww~uvx}tssrsuyxuotpkpngkqtwyxwztmyz{yzwokwzqkmtpgp~okvuifhkouz~y~yqu{x|}|rr|~snpqnhozulkpsjj{x~ussqqrsxukr|xzyulippnusou{zt~|{~||xz{s~sq|vieefgjntwnlz{x|~wuyzz{ywxwos~ypr}~soqsnkwxrrwwln~zyyxyz}{szomrnmqjhnsuxywwxqr~ypu}vmkptkiyuhowlbbdfimszypy}y~x{~~~|yxszzpnoleiwvnlqxtmzyutrrrtyynq~|xyulchkgkjeksy~}|~|tuxtw}ypxrp|vhddehlqxzpr{~yz~~~~{xxunrxyz{~~qkozypnqrmp{x{~swzyyuuuvwy~uu}zpqvstqilrtx{{z{{vxvr||rllqpefw}{tn{}nhhilot{yvyyxx}~zy~|{{rqoifqysnouypm|xusrrruxphr{sqy}smstrrkipux|~~|x|zsz}usw|woztwwieeegjpvtlt{~{twzyzzxvwvsty{|zuw~rnnkeiwysru||ru~z{tttuvywvspwvtoghnquxzyz{xwz~~ws}ysrvyqkxxkr{odbcfjnu|xr}}~w~|x~~{|}z|tpojdjusmkoutlq{zyyz{}rq~ww|vljrsrngjqux|~}}}{}yu|utx|vq|qvtfbabehmsohq|w}{~{xvvsquwy{wtu|~tnnlgkyztru{|tu|wuuvxz~ur}|pjqsplccimoststvxwz{syypmotqip|mmzwiddfjns{{sw|{wy~|{||y|{trv|ulihcbnuqmntzws|wuuuux{ukq~}vz}yrikstsmiovy}|uuvqquxrp~rhszndbdfjnuyrn{|x|}}}{xuuvtty|}~}usv~~urqnks~{utw~~w|~}|ussstv{xmo}z{}wmksvtngjpsvyywwz||~vs|}tmlptplxwls~vjghkosy~ys}x|wswzz{zwuuxxx|xuv}}tnlhdkusmkovxsyyvuuuwzwln|zz|vljtxwpjouy|}}~{xppz{snotyusujszpd`adgkqunkx{x|~}~|yvvyxwz~}vuv~~vrqmku~ztsw}}w~z}ysrrtvy}wou}~zrhirtpgcinqtwvuv{}||uy|soptvpq~pn{}phgimpu{zqu{z~wy}~|ywy|yx}xusw|vnjgbfrsmjlsxvu~yxxxy}{pny}w~~zpjrzyqilrvy||zy|}|tt~~uporvtoytkrzpd_`bfjqskjx}||~yuvzxtw{}|ywx}uplgiuytprv||xztstuwzzqlvwz~zqhmvvmccimpstsru|}y}zxysqswvpu~zos}xkeehlpw{uq||xuz||}|yttyzstypj]_]]YRZY_gb]j|n[n}y~~}sz|wwqrxpqhhlo{~oblz|~~w}hayjlmxxzuq~}wqogonmkdgem{~uwlWizy~wyxrrqs~z|uqplrysjpy~VKp~rkprsrjsslee_gmkohga_ells{}{||x~sXd{yqptwxrqrrv{}|tWFj}vuioqg]\[`kgkghe^X[bnu{~|r}{sunlhlx|e_tnuzyrr|}unbaheN]}sqzpaY\\fb`_]aa`YWcuvusyzttrmf_buzdt{qs|rzx}{tfYWf`T{}xwpzzk]]_bd[[XXX`b[Vpz~~qx}wyvyupd^c{vjwo{~{wy||pxy~|tdXUh`bvrpnm}xi\`ac]UUSTVacZ]|}~qzwwxxyunc]ewwtqxssrx{v~s{znaVYmcvypmjgp~ue]cb`TQPRNT`fZk|zr}uzxyusic]my{pyxrppm{vuw|vjbVdmj~pnjffx}o_ae`WPQRQMXdd\zsuxwxwvsmebauq}spqlqxxr{|ymf]\pj~unlgdn~wf_ec\RRSVNUahZr}~yqz}tvuuspec]s~yp~toomk}vzqw~|zqgbYllsznoiek||m`ef_URSXPR^h^g{~puutstqrfc]i|qyvnmnhwz{ru|{ythfZhmlqrlhjzsbdhaWTTZUTaf]`{oqvrrsqsgb]f}rwxomoiyyzst{~{ytge[gjguuojlzuddic[XW]XVcf\\{oovqqsqthb]b}~rvxnkngwxyst{|zuhf[efdxxqlo|wefkd[YX_XXdeX\~|nouoqssvhc[c}y}rzukklgztwtv|zzrif[f_b|}sorvmzwf_]a^apoYcxsxovzvq^hpgtwyotu|wlpqmcdwwt|}qlnoqm^ZUbcexvsouoxw]`wrp}rk^cw}qickzph`okjqj{|xuvvxp{wlwz{wqqgl^ovru|ynuuekl\VVnymU^qs}~ztiw{mpv~rfadtox}s|~tpush~~wv}ifxrddhbQU][y{{wsizvttodnulmwd`t}wy}|wupvohivwaztuzrwhktw~whv}}yvv}zy~~l_]kwm|{zhmyldhnhoz|Xwrix{}mmu~yqeqpeocbdlrh{|o}|v|}{}yzywyyzsrqxrg]juaw|zw{r{xbgY]a`wtgosks~wcoo{r}nnwzvgft{krytn|y|rxz{}tvlej]hY`jn{q{zr|vu{lghmulo}xdllw|yuo~tm|xrty~ukhmu|}tzwrmhtnumzxhchkrrc^lxkf}ycquootuuv{sghyoytkz}trtblbfh_uw|p}v|tsu|{v|~|u{~jsyqlmnrmgat{rx{{vzzcgWdbnz~rzsxamlylcelqxpfl}ykwyom{y{~wt|zswaa_Wg[lwxv|wsgegoqnrtgnoyvprmrzzhmztpmpqpjgwv~ywoueowxmpqz~i[Y^eliaexxntylb}okjllowmzvu}|ykszs{ede\pdrzv{rot|}|~v|~vwsw{yfmvogacba\^ux||z|~glYinn~w}v{gmkz|g^^dmrnfm|xnw}xmkutrtsyz~r~vsu[bYYk]oz}~z{mecjqqptojpt|vlsrxysduxqgegfc`g~yzx~{uqgvynqrzp\XX`ihc^ovrm{xewthhfeeowqyr}qp{u}mciZqlgx{xrmoy||zty~}wwxzzxhkwoe[_]ZVYp}uu~~~or_sunzy~yznplzvb]\ampmdkuslu|ms~pppky|i{yspyvfWcvf[W[Pfxqgl~~|~toodh|rk{vzyyxrt{qmsk_]ebZXuqi||}rlwzx~zfrsdi`\]_Vamgbesysc~}supkuy~xywsljU_juegqklj|suqmswpk~ufjss_dWc`gcYct{k{{mlpaqydmyx}yz{rqtnim__m~{mlr~wsmwvldzmxxygqktk`[ea_gpolqzy}wtwij}{pfqxrqusmvxskYomflzvof|{oktzqzu}xurr}xaokcVkl\fjf\v}~xpnxq|jfl}xiroawwe^gaTlqmh|z~~zyxltxqzwszsrtbY\fcRLksgdiox~upnpyvsus~giyqsiklp\cnfdlxqoz~zlys|wpuvu{liUW]ibYcclsvd~t}rs{~wl{|}qy{yx{kjbcoah_fqyx~lxnrpbu~vyop{plg``bY[cokppiwtzl]vznmqjfv~xzkh_ri^ehmu{ny{~~qh|uvv}ulx~kt{jR^sf^]jflk~zz{qwz|oqv{tju}fjxotynajxkmqk]ov{{k}trsq{~z{}mtyvfn{dSTYMUdcbt}~}{~qpt|||m{|xoxl_lmaR_vjcmvfv||r}iqqx~vx~rmp|vspjfj]Mf^^\s~vfpxtq|v|wq{ujxpccikacglszkzzz}t||wh|x}~~zssmvzwhc`Sg]\]\ixt~{osqdpxts|~l{v{pjik_chlgosrw~{r}maxplwsh{nyvtyyhcWnZU[Z`lwswnr}rqqoh~zzp_qwiajjfiors~~zuywnsw{l{z{rhfaj}uf`okdjgUVv{ss~rvotyt{{piywa]_U]kgak|{tqvysv}tzw{r|}uzn}n_`bxy{zzthzupqxypsrpoiotidyzjw|fy|x}zflzxtthfzeRc}{}zpt{|utwvhvmeo}x~{{|~|x|~xc^_YaW_hglsxqwyzxwsihltrjy}ywt|kds|mjfoz{s_brgjwrmuu}{x{|ruzu|{sidjRo~|mXMQWP[^jvdr|~}|}u{~pruicWUXi~vx|mssspokpwzrzzzcacjg|zqkxus}mt~w|t{poyv|z{~u~~tdew|hnpk[]pyrplnrqwxfewjjhmqkpzzuqrmalduqvygnuxirulu}{|~}}wsdohko{z{pismqv{tn}y{mfr~~}~nunhhj}uzvzeikoihutxghyn`gvyhYln|swo]cnrmj}|su}}uzvtwjx|r{zvjonprzy~p}yjhrzynznw}|}rt{}y~|ynajf]iyuwzzuq|qbdclbu~{rgomrb^w}qnsqbZoilgwyw|tixovr~||zywsjz~qdm|{xxcozyng{z{vzlr}ocdilWjkqkruq{nmfyxfhpulfxy|{ziphwovxtnbmqhpyrpytnu|rtvsx~n|riz|ojppij|p}|z~t}{x{|u}z~{~}znkmmdYdtp{rfjvxlaii|wxvihn{ynosd`_hdWtt~ryui{yzzvp}}xtww|pkn~iruzyy~v}nlu{v|{}t}vzo{xw~{{~vuyvn^b^gqtg~~{pgztjYfrlmlimynjcv}rjki^]vvwq~vrq}~u|{vztkrmfimxh|}xquxqq|p~{u{|t|zp}tcqmfUVnqghusjzzr|osjsv|ypvqcetxregaiwt~z|~z{vvyp{ul~qtxsv|rsukbovsluqjoq}~}}s|{owrnuyupoy{i\iaY]To_gy}{}w|zsqpqjyqm`Yof^OTokgioomynxvyxv{ood_gloe`d_yz~u~tszx{xvvworzv{}hrkifkvnf\^_huzvhouz}vhcfx}wz^a~~d[Wu}mysmnnys{z~ofwvuhqufW\vz}ynpzp|pu}zqs~v{vgnrc]plnc^js|x|n{~tlglohsqugnqmr{{dXk~}v{}j{rmruw|ukt{xw{uunqsru~obon}mmwpsuo|pytit~}f~ok^[dhg_^hrpx}nv|vrzoocpylkw}ndfn~q][x}tz|{||zxxwwrzvc`bbnvokqqtyu~zw~ppyvm|svusw~}kutpbRWbco]Xhmhpu~sm|xlvof}slqsxoX[|ultxy{x~~yt~q~oqvp^XZ]hwofsmzqiyx~thvy}~qj}~~z~y}duuc[RQasqem~wzpt}wt~sfvxuf}slqo|q__svkkuu}x~}{~||~vvqtg\`blqphosx}}yh|zuvxnx|ym{zqfzx}}|{gnlnb\PX^eohcv~vg~ypu{vih}xhntquvofdvuwzxy~}{|zxyyx}oosh`\]fitnm|v{lkvy}~yuzz}|~~pl}||~yrayjfaX[\Zagi`pu~ytu}}rfxvniqnrt|k_m}x|ytt}zvu~wq|}vyp|oligb`fmojnpiqvyqrzzy~oix~y~|sywisoi^YT]djphs~tzqx}}qe|yrh|pjr~j^izx}xrw}~vy~~{}i|jngc^\hksqmrm|luzmq}z}x~~pl~vw|{zvmi}klh^XV\fnnhspstv~{vx}xoo}upoyiauwu{z~vvr}yrsxx{yqiketqe`ahuxyytxsy|~~nnyd~ifyt|zzoos~|oxnnvjZV[\afmf^gyiy|otu{w}{muuytku}wto}zp|v~}ix~}yt}^~fypaRTYfpv}v{||x{phuowtbwmxyspprz||plfsoabjghoxvggo{{wgkkno|uwt~rnimqiiquur~vuyvxy|qgz}\vtg\Zaionyulsw{u{lkkf~|~~sx~|jhmstsz{orssp]Vaebhpsb[bvnytxyxxtpyupptqqy|{pz~xp{qqcrkygYOS[dfqzu|}|yol{pyq_yl|vsmnrvu|mjgnocekhdnxvhgl|}yyljmmo{|xw~vqptsljqryw{tw}v{uzzmusp^i}pe\akqouvkk}vz}ui{ye|uy|pozshmsuquvqhvsodUZeggptjY[h|}|x}}~ntsosopu~owvwudklkubWOS\ber|y~}{qlxtw||~zm]yo}tpmjnqnykiegqfbegaluukjp~|riompyyv~zutzwqprqy{trwzvsv}vkh}j~f\|ylb_kruw{rmwq{~z{lrpp{z}vjw}y~x{nmqysttocgyli[U\jknxug[`v}z|{unuynmqks{w{x{sx|\rhjp^YQW`cix|{~plsustx{sh^uz|pmzjeijhv|yei^gqe_aeanttor{}tkrns~wuyvwwvwupqlyyrpv|zocjrow^cvmcdqv|}trs{|~|}rlv}o|u}ooywzxv}tontwpvndYrredWXbqoz{se_l~y|np~qkqko|v|y{vt|^hpdpb^XYbckvymnvnmcx|oqmjjoyymdedX^_w]p|xnfwxx_R]Ycfnut~{ssv~yu_`{lxtv}t|ljqzmlfs~wyyv}mnpjirpcxz~liz|otx_xklijmk{yxuwtwzyokxgfqv{|wkdbSm{}~urvo||vt}youq~r}ytyqikjdogwu{sqrjw}pgoc~s{sibnz}plhp[q|pqav}|{pxy__ehagXzsr|~tlw~ouq{hm}{}xs~vq[my|zly}z{ykhP_wyownmv|{blz|ypmx~tpc{mbqyz~lr|qsZyz~rxypmvy_ew~tl}hjkrz|ulfVVotrrncl[r|uqohm}xtp}~zt~ntwpfqwtvopimzzq|}kzyso`ibj|~qkxyvuhVzqsfav{wwlf{m]^db`\V|zp{rhvyuwnvxlwqYixyxqbs~}tlYexzvsquvaj|~zsmypt`ysamy{}rqzps]n|zus|ooz{e_t~ynvtqrt{{upbWfuvsrehcb{nngjszsqvkxi{zqgaxxuqnixxvnx}vqh`j_w|}tvzy]`}ov`bryrqcf~o_^a_^Ybun}vo|zwy~uy|i~umWdswtocw}~{ulbozxtutch|wrymu_rwdp{{|}tozzosfjqzuuj}~{|{j[g|{yswz}}xum__lwwtmgmavztoihmtxsxns{jjw}tocm|utqpnz|~okrwqlcdlh}}~xtWnyqs]eqvpl_m~n^^```bw}}nm}{v}y}x|zdpo`Ucrvwpmxzwrlnwyty{ocn{y~xmo\wslyz{wpow{tqth|uv{mt{y||{{z~{n_\t~|{{vtyqikxzuk}}t[[lwuwu}s~zjxreywv{vlw|icbwyd|}xsz}purswt}|~k^f\akWlszzsmcXiosq|faqpp~}p~{{n_q{{}vxwl|~~hgrreo{{vrkXosy|q{svwx\Zb}i]\h\Rgaqs{xuw|~ri`wkprxvsts|}npynqlvnatxzfonvXbzgT`Z\g|yjcwulipbqvyd[rvz|}yuzljtdttw~{]di~m`mt||~|r~wyc[_ZsqkqcUahz~qhs||xj_tlYwpsmrrz}{otwiruzeqxmu~p|ozy|{uox}}rtk]_ql^gqv^iutvponguxeuhfswlotjevsw}}}}|oYbykhjyxrzxwv~xzy|rguq_hvslbozcYt~xbiuu}dmiel{o}~qnrfgjy~t~vjoi`{xqjv~}r}}}ssv\WXkpn{nhnkf{likjjqxh^mlexu|w|vtrk}yxkv~z|esrpzsu~qvwo|t||xv~z~rbYUrxzhwwqw}hrtghsbcerym{ppqtqqzzqux~qovwfXjtm{vtoq~n}}{uupoc^dmn~sw{tgswioxv{ipprqfd]o{mqkysxhuv|odj]_r|w|zv{}v}r||ytjtegY`yxipldttjjv}zlnjjc`fvwt~{ehtyotvhrkvopo}~r~q}xrw|q{b[\`_gnwu|vooodwypcmrnpbxxdvqq}ksxovy~rjfehhp}xoszzwwxy|}|bcusd^psztwuyqkkqpwtcorr]^dlhs}}sgtsvxqoxk^lp{}oxxt}xx{z~||nlTaez||mt}zmvyy}q]ivrhlt}nstumov{{}~xmhnr|fck{v|~zsuxv}x~kbee\Xcr}|}prvzryutu{ntfdqsy~~gn|vu}ys}}{__r}tgsu`coqii|pgbkumbj|v~~xpnwda}ut{|pv}zknv|k`totl}~~zhxlkvyzvrvyyjp|gy|wlpvw~~i\w{youxvtu}gem~~vtxtgpv|srlwxs}}zv{{wkZSlTgwa]a[yswyry|}{nvxwny{~olmZfzjum{mpq|}xkqgs~wc[X[wtvcazso|eZvuxqfvsnk\_kquumw}wzq{wv[Uqsjo}~w{~utgvtQNOQ}qgmuwjruclosvnftre[_}zwrsvp~ieh]ifVnvyv}xnzuz|vwq\cocqv{wrtkdjieptntjreaZVbelt{w{ve{|v|nwy~wtuZ_z}z|}}ytzzv||}|igs^Ykzuyun`swpnhwymhden\cyutzyyhrnyvrtplmj`Ypvw{|~}s{rx{~p^ge\ffxrpfjqlmurwl^omeefSRuu}zv{p}xmj_`hip{{}sz{zfxkkt|~~ztreWajlv~xst{hqylcoq`iwaUXUfrlswvyxr}|}wjlskiZew~{z{t|wztx~soywukbeV\^q}wrud_y~igbapogkwoqrfu~{{vx}vyxstkb]imo~m{yqrp~rr}{otp`W[`x}~jkfq}pgr~vwi\nxghlmnuq~}zt|z}vjlklg]uzyy}uin}~u|~{}w[^mmabhbw{}wzzk~}mk|gjdhi`alhmipz{tdu{l[^SWs{py|sy~{}yx|vq~ukb[LZj`^horlo|jkbecXYc`jirspsuih|}lljtuyyxqs|{xuswqjtww~w{|whr}m]YQ_e_b~uvmnlbakgnkn}v~{|~}{{sigoc[`yzzzusotnforrzq~t|miaprjetxqox}j_\kpcfpd`{ppyy{z~stx~vv`mt]ep}~pejflyzxtprx~ywg`mwlns~}d}f\`]`Z[gz~u||}ogz{uw}hd{hdcgny}{zlr}wuwyy||wvqs{vyZeuwvj\]aik`s{iw{xromgduw|ry~wxw{ncfXzplw~uf~~w{}vkihsne^drvyhbelyfZtorked]ovae^knpy~xuuwsqu|lrj||~txwv~sr}w|qy|~mjd\hlxv{wyjZqnebln`q}cd\Wltt|qm}ulsz~wmlvkmhx~~|ruzxxktkyvkliakk~{xqws`u|omvu^_{s_c]Wsyq~ie|vwx||uov|khj|~~w}}qwynw{pktqcad}vconeiuo[lo_gb_nmez~svu}t}~y~yqtwjjew~s{xv{szwueq~z|opuj`^c~y}pcxthlxran|dX_W[tsopx}xxty|{~tnuvjmlv|}}tls|~p||or{skff~wi{pdepiZnmah^\pqlmuu}~zuu}x|~x||lh`tpvuu|uvy|oxsmqjc`^y{}sbxxmn{u`ko_e_\nng|qss{}zw}y{votvjkfp|zzrzwzlt}oksocc^xkw|qkuude~s^baYgmiuvo~uyw{{x~{pqwpmfe||v{~o~qunq~~~vlnl__Whqrshnxi^syc_aWanjpo{x}yw~y{{zmhnlfkg|s~xutrsqlxvsfgpjdee}lzwmiuo_g|mafaarrmytq{~v{qyww}ymptklip{uzy|rvwms|~|{pejlcgbtqq}tkp|nex~e^^Xdroqpyu}zvyyy{qiooioi|v}xsurpk}}zxugbifahhs~uipsddys`a^[nwswznzw|z|wyvhemiinpwy}y{pvqw{yyvd_jdYktnhyekwtju{`Y`amvkqf{yrw|r{xpxYcrlzx|~uqq~mijlyrykVR[\fxxxsao|pq}]`cUuylmstyw{zqlw~}o^dnx}x|xrvnlkzu}wutkrziSYbb_s{|{iwlcwpb^_Sgu}ytwzxvkx}wnlpdmou}n|s}tqhzuz~sft]_a[ujo~qvpowo\mmY]_opq~ynr{ty}}uyto[ilmmjv||~utvuyl{ry|q|sa\aob`z~rwlottxquddZe~jlw}z{|qz{v}yjike`du{tx}yuxyylts~myxzwep{e]dfnhq}sqoougygcjkhq~t}{wmowt{r_qiigp}||tyxzmiytqqxlpec_Xflows~njrsehjjdywnsuwy{v{|{ptrwxyy`cjqwi|szv~twytx||w~xzr{zu]Xg`\apmnipxtykmXjtrmk|~ymyntwuieuuklt{uvxn{qnny}~zzjzunmif[diiiwzzmxwo{ecjmilw|}~z~{v~qomwhcsjl_ev||yzxut{zttpjmitflfX_lv}v|lgz}xsqj]uysmqyvwuny{t{srebqgi{|{}su{vpvqyzztwppqcSZppnoz{uhrs~tumems|~{uyjm}tlcuvdbgc_px~x{s{rx|v}}yzssvjg_a]Ulings|{n|y~ra_u{{{}w~uqszxlnajqlngdw{szvgvvopsuy{~{lkdxcdmijep~z~|swyupjp[s~wufurz}yrowlu~yroobXlzr}~voy~yzz{x|glri^XjpcYZcbksvp|jut|uv}z|zx}opw~~rmrgcbifa}~z~vz{s|mq~z{}tpzyqbiki\\Niipwu|p_mxzniojluwry|attylxnolh}{|~~}}{|~wyzrhn~{xyoijjhWoremutmqynbo|}gmk}yuzz}up|yt`z{i\mjg\`nisqp{uwos}yzx}ouvv]U_hinc[eloqmvzqtwws}nyvyaqj|wwzrg^krgpss~|nqkatglvqyyvf[qzhfrsc^Mwxv{ylrzv~|~tz{onxnpr[Wghmrimw|xzzz{{z}zsw~{jzv~{psxlrevhjN`dYbc~~|tusuvlqp|zt{xpy}ubqtednyp[^p|qwz~xx~{owwprk{r_U[`ce`ammos}zr~sxoog\vq~t{xziz}wyrztxvkzrsnqrv~tnnpzwx{xw~rk|yxshlyq^V]ph\Lh|x~x|rrjwu|z|y}j}pylYbiktd_flgn~uuq~uo~xwxjq{lqix~hlZjhcnhltx|zylikqqrrt{q~yq_ttggmqbPcxouk~{{xnprwvt}{e~szk]SUftreguimpu|xv~{pewtvlvozklump^mskpimlx{zrruzzouux{twyuabz~o`Y`l]RQhdjawpw}|z~xwyppuuti^ijqvmcpttr|}tixoswqfo{vl|y}loj~rke_yc_c`djoy}vtx{qxrprxzvpo\vrffggd^pyuvl{~}~|trssz||xfu~ilffe`bfg\[nknry|zu{~tgouslyw}vrxpven}ejglhimy~~~ywy~{xr~{pyzfZrsaZZgbTThlmovwlqr{zwzu{yqidjmqthenqst}uy|prwzpcqywjtowkllukr`gzZT^ikqw}x}~qy|}qyr^qyd_jnh[Vhppvr}olqssuxx~}oqwdZgqyxosvyttwz}|mpdgua_mo|mqikossxzox}xyom}z~}{spwnrdjgrxptp|}rsPSjydbszoloxyr~~||rsqqleio~x|~sohirvjpsw|vy{nsurlsmtqZuyWS]ms\Yu{gdhr|uv~jq}iqdfgp|xsw~yrjqpcgsyzz{xy{f~h[^g_L\oqe`_rtswvxiup~zyz~nwnlri}nhxsroy{{|rt{||z~sw~~jivwhb{vz}yfZfg[Xansdz{~yqxuuim|{}yumtpy|kmr{uzzyultwqv|}~sy{v~uzwvxwiuwyzkVg}qejiwvrthqn~~{}u|}udmkm{ytfm|x~}plmh{{xhpyor|ukemvyptpsywictuaZ]hqi|utfirkmhswwrs}mr`szryzy{syzzwzpz|ur~w||{~~~s{~mjpnik{scYiwnmuuxhrnx~t~z{uu~uw{nrg~|nlvqyznmnw|zif}ytx|uu~xz}{nkswsh~~}~vlrtgadhthtvnrcoprpl}yvunh|uysssty{pj}vr|}qyvln{yu}yv}|y|ss~rhilkkzlctvimpnmii~sxsuvv{tz}lpfszvtomxz}pjilywpgvty}xy~w|qhjqqqjx~}}phqlilflcjvxdioljdvtx{vhw}yvl}odvxv|w}ysuyvzswrw{ni~{i`a`^owklspr~jma~zqww{wv~{rqop{xrjks~wpxujhjxt|inzrxysl~yutjhluwxtx~xsriikor[qxqs`komid{ztxulg{xslu~{}olyom{|vwr|{vxqyidyr`Y[]`wwejwokzypugvhnnuz{~uwqpzxvqpu||totrpswu|ltylpssn}~{z{wxwidfnqv{|}yptrsuvy]kzprboywofxzmpnie{{rp}kdvwliy}yw~|}m~~}~}xztnookmfUcbr{~lv{zlikgss_pvgwsu{|zzoy~{kotho{zlkfbrtyzwmsumyu{~w}~sz{i[hkiu{y}}oefrpcitunocbyxqyvmxs}~nq{zw{rcg|tnsy|{xz{y|xs~}xtzjlgeltcbhusoyxrptjh~gawlp|yx~tuqwunwoiuupopcnt~t}svznq}u}www{~qqxk_dkjw}z~xvgfqrbhwqlof^u~u{ym}r}~uo{vvxtedvvtw}|wv|vxtzv{ynxvhjd`juhjm|tqzvrtxieg_njs|zyowotxnumjrpospgqu~q}u{{jsyututvzyymprgbhgnwzywhivn^pzlin`dx|uswzs|suwt|txncivrzw|vtyvpt~tw}|}qmtlhh^`ppknrp}yss|vbqv`del}ynv~sm}pspjnnlsvinoszy{qi{usl{rzvxpmpidifgrxuqkvtajodndasyuxvt}|vyynyyuvpfispwt}ys{zrrmxr~x~yzskokjh^_mnknmt}uuzfqwbc`i|}xov~vl}psnmoljsvjni~r}zpkvupi~{usvuoonffkcfksyup{qbrldlbettrvv~}|uukvtvwmgmomvnzzsuvmorq}|}v|mlkjlf]bminil{~wywhzrdc]k|yvo|~yl}oslpqjjtskoe}r|poruoh{{wmsqnqlehkbggm{|vsetlcjdgurqxx}|tyhspvvnhmmjtk{uutomnn|~~xuqljgmh^_iglmg~|{x}lvxgf^eyzvwv~}pxntnprlgotlqgq~z|~~vmurriu}stkrmpnecjfcjd~{y}{jmydfeftvpwy~~|xxmnoqwrhinhpppz~{yo{vxzvx{j~wlcmb[aejeqw~yx|q~qoks{evszw~z}st}{spsleevlopyyu}x|w}yu{wz}vzgpqagiViqWbcy|tz~worwsjpx}|zrntoohzkps|cd|oy~w}s}zm|p{kv~vhngae`d]jtuuwszrsnrtvs{x}z~{u}yspyrldpmiqu{j}|{s~u}x}z}{~{rftfdm]Zva[_a|zx|voxspn}{xyzljpjyzjzyxr^xuoxuv|~ywtrzmw~|}qx{}xlqudgae_^hklrrqtp{qtk~|~t}|wnvvrlqrfllvirwru~w~yy~~}}xkyl`i`YtdY[^sot}zr|xrmyzw~x{}pnnjy{o}zr^uysvmj}vwwrxirxxyv|ykrvkoeh^]ddafyz{ust|vsk}t~{zq|wrnwujmloboyovvtx~y{w~s}|tr}ofiZbu`YVcofo}zzxt}ssy}w{yvtptws~lczvvthixs|ustth}mnuxvozronll\c`b[gmou|vyky{z}|}vzxwt{nopqfeskfply{|~w|ssz{|||tzklf`vh\W_ldasz|uvz{qvxysz|}wyuxtwgz|xvkesnnqx{|wrujokk{{t}zrqnpbhce]chehnzt~qy{s~|||}zqprtjjsj`d`nywy}|sruwwvz~poji}n`Ydod^mrphdvzywp}vuxvx}z{k|ynfrojlmqjw|uuntml}|ry{{wwsufhgjddjdffoelv{xuvz~zsuuolwqc`_aysml|}rvsxr}|~}ypoj}{kbcqj_drmhZj{~~~}rx~woqq}{~ssxmlukljofflt|}xuwtlwwn|}{ywx~rnnwnkvvqiww|}nfox~wpxy|kdpr{prvw|uzxUrjs{{}|klxx~kSednpjchfibXkwy|u^Nznjojtjk|{tyqxmdszx|or{{xnrqirdpzsu}|yv|ogwzvs{v|shewVs{|}yzvvznxyyryv{v~|~tcRZq`wwpcfbe[Q[n]YRefRomkafyxu~xmxszsyvlrrtpfxzw|}ntto_Wgvq~}sywxrbw~ouiws~wyq}{vu|{u~~tsppog^]ryzrunqdSndacXu[mtivexyxpyy|slk{r~{|wimr}jkpozifuyzu|}w|qdkfh]]czt}nssp~s~}|zv}jghgd[_o}~{kqiofMd`W`NmZdscueyuyzt|~zttprwsnvtti|}{|}zfljnbbhzwyzzilmxhwm~}{wv|||u}vwkhjlgaeytyrzmYrfdcYw_qxlxg}rww}}jstznkk~uy|xioxkmqs~kjs}r|v{xyuzl\ceh]`gy~n{t~lz~|}y||v^ach\]a~yusrqv]^o[cRcmXxkomiwqyzwnw|}ppwxyr}~owu~jzs~hfjofaby}z}zrjrukpmwv}wuv|wyynfjoh`as~}y~qcyljdb|atzowh|~quv{zhnsxigh~pz|mv~lpptzhst{lw|wyvyi^dicZZkysr|vtv{}~zyz}{}l`dicWWeuo|~wypaumfa[x_iyjrds}qswz~nqx~rmkxx}wwxx}ps{syxfkpn`^dzm~|l|ttiyr~w}|w~|rp{}~{~iimo`]ayopvrqbxxcvtnkxrvv{sipvsfdnykyzt}xpsqtqg}}wspy|x~yyvxfafk`XXnns|zt}tz||wy~z|~}yyo_cheWUash~|wjzzng`{id|opfm|urvv}qlszsii{zv~|yzx{urv}z|}}jglob\_wm}ty|yl~l}{tuyrouu~y~}~teimi]]lzq}sxpjqkuukqytvvzoioundctuoxyxustssol~zrvu}t}vvstd_ei_Y[toy}uuy||ytz~|{zy{wtrn]^ccXWcxl}yozundztbzvpkj|{uuwzwkpvvjhus~z|{zys}xy}}}{yxpbfli__r}uzu}usxr|tz{rx~xon|}||zyvvi`eje_e~~|z|oulruriu~ytvvylhnrich~s|zy{~tutuspv{pp|wo}wqron^]cg^]jt}wt|zx{wu}}x~tu|romleX]cb[_v~z|wtymomk~tqhq~{uwwzrkqwqjm}|}|}zx{yy}s}uvstfagkdbmy|x{rnztr|qsxypn|y}}zvxvtrrk_djhch~~tzqrwrjt~ztvuxnhmqkekt}{{}~vxvwtrz{rp}ysmy|tnolm_^dg``pv|vy{wz}uu}{zy~ukszulmik`Y^c_\gv}yxo~iw}rlix|wtxxymmstljyv~~~}z{|~w{svvussmaejgbg~{|urxzto{y~|tpuxplvw||}v{sr}~uqrpnachhbe{}yutyrlu~{twvypinqkelq}}xyxytuyuo~vnlxzropmm__dc]_tzs~z{u}~vz}wqx}}}zr~wnkvxplmjj\\``ZZnwox|vpzqjq{{txxzulptohmv~|{{w}vq|ztwstgdhib_n|o}|~qvor~xz||{orvskm{x}{zyx~tmu|yptprh`dgb_i}qtxpny~xvzxzmloogeuzn{}z|zuyv{xp|rjnxwnpooj_ada\avov|}yt}}{uy{upxx}~{q{tlirvnjmik^[_`ZZktk~}q~smoy|uxyzymorpjlyw}|{xuou}{rwstmcfhd`i{n|tzypnx~zv}y~sorsmjwq{~}}r}ulluxpoqnpcaefabuzswwor{~uxzyxlmqohj|vt}|zzw}vuqynhmutkpmni^aeb^fzo|||wrz{~{swytp{w}~}zou}qgdlqjglfj^[_a]^nyms|vnmw|vv{x|porsnn}u~~tvnnvypqspqddhhdgzzv~|stmpy{tyzyznorqlozy~|~zywo|xmfiqqhnlmk`chhfn{}yunt{{t{x{vlorokst~}~zyz|wqq}tidkqmhoine_cgeessy|sr{{{|wswxtty~z~x{wktwjaailcfhei]]cdch|{y~utmqy{t{z{{oqutqw|}xu{ohntrksnqlcgkjjvwuwxnksyut{u|rmqrop~s|{}y}w|xluymcckngkmjocdikjqyvos{{t|y{ynpsqowv~zz{|x~pp{sgciokgoingaeihjyuyzrqz~x|{uswwtwz~{zu}~ritre^ahhaiehh]`eegruzz}qnu|xw~xwqtvtuw}}r{riipskqqnrffjkjqz}yt|pjowur{uzvmpqoozq}|}zz|xyympyncahmginipfchjjoz{xqt{|vz||prtrqyu~||~zz}pq|rfcioijpipgbfggk||syxpsz|w|}stvtt{w{}}ywylkvma]cidekdkc^bcchxyozyprz|v}}stvut{xwtymhmsomvntnehjiky~oyootwt{|ysonllvwtt~zwzurzt~~xjkhkxxskfrovxnruuvv|{xxuv}sr{xq{khz~x}y|~w{y~vtvtsyvy}|qpszxyuwsyzyuooys|z{|syzxxig]ez~ibf`jdbgjkp~|o{w~y}vy{umjxy|z}xv~wyoy|wx{gekvrntnddqndbjrqu~w~xonlkqvu~~zzyqyuv~zx{{o__kjmw~v~upssty~t~|ukgek|ztskhogpqpxvpyvkbnsmnu}|w{}uvwwxt~y~zushlw{yutqw~{zyyvssfYWhmpzy~tjjihkvqb|xlx}{wvslxxz}wv}~wnmmr}wz~tz|vnkeew{~~siotiacknlwurxlhiinzyxwt}~zxysrzqz|zwnsnc\jvuw~~~wvxxyw}{xigjq~|xvloqjtrt{}qqwpgdtxrqw|yyyvxxx{psz}yrpnjyxx}sry}yz{xjli`Xcw{|zsnnllr{mhsnx{wstpq|y|uw}~{~z}pmpvz|zzvytvxtlhigvzpkuqfchnlqxnvqhikpyppw|{xxuoyst|x{wlonf_hz{z}~}yzz||y~~|yuvihmu{ypstlvuu|}qqvphfvysrv{y{zxyyy}npvyvpnmk|xy|ssx}{yz}~|~qgjf_[iz{}wsrpry}hqnowxurrlu~|}yxtw}z}x}xnorzy}}~v{}yzputnhhdl|~yovwlegmmnypsujiimuqru{|xwvovupzxzznophabswvz||{}u{~}|{zuxlijp{uxxoyxx~~sswqiertopw}z{yzyz|npuxupnihwwx|zrsy||~zz{|x~|nkme]Yhppv~{xwzwg|lrxwsrnjvszy||tty|zzvpopyysklpgqttz}sqwqicr{z{zyz~pyxfgpyzwv{~{z{sogfxzq{pvyvrqklumv~~tjfi|ndixtxyyz|xsu{|wyw}{niyty~}}yw|~ykhqty~~|}~i^bmxlkrolgqvolpwx|}wqkjr{yw{|yvtnwwrsdXaisv~|vww{t{|tnyzsg`hzxvqlkfhqhqww~yw|uk`fppt|}{{|t|z~xog]eov~xr|}urv{|xtru{yqbURbktzxomlntykdvjptsnkdcpo|y|~wx}}vmjjuy{xxnuz{xof_Wbpwxognofbejkpyorsiecfrtwuyzwusoxvq}}~}ssqf]Yirw}||t{~~{usz}rgdgszunltknvuy~~vpuph_bqstx~}|||wu~qtuqje`fwz~vyztty|~~|vuy~slkbZUcv}xsqqtzyepoinqnigbmxwx{wx||yxzolnx~xy{vrxtookd_]`vxojspgdgkksujqnffir~urtxywurq{ss}{}~tlnic^eyzyy}zxu}~xptjglwzvosuksww{~zporkfbnzyx{~}~~}n~rlnnieefx|vxuw{{{yy||}qghc^\dzywux|}kiugjmlhgem|yy}}wvyz{xtyqnr|~zy|xttnhhgb__ez{rmurifhkkttjqnhhlv{vrtxyvuqrzqs|z|~{qkmhdaj|uw|~}yxv}|}z}pqrjkq~|vrzrnxxy||uosojefuyxy}}~sr{klmkfdcm}}x{{uvz{~~|zz|w}~|sihf`]]o|}z{pg{iimnjgdhvu|zz}wuxyz{tztopv{y}xs}rmiigb_\asvwr{}ussu~ntympssqldipp|}xz}{tvyrnis}}yzuu{~|zwxxotx{vqppzumupkjmw}|rjvkqsprwrngnwvw|yw~utpsurnkittusty{|}utyt~vtwrnhlxz}sqiZ[_`^]\gkgv|~xqglrswzzx~v|ywv{{~tnts|vtuuwwtrow}}jelq|njngcbdlsocwrknpokfbjkiz~x{{uqktywyzuy|xtu{}|wzpefnm~~xvme\]dcbfknt}sqysnlms{zm|xmorqomjtvqzsjpy|~{x}~ywzsotzwslf_`h`dqtx{ztrupkcajllqvwz|v}yxyw|{|ztoiowu~wy{{}}zxvwndjty}~uolmrvmbwngjljfb`jiiy{w{zuqluzxy}|xv|}zvx|}|w~zrkdhts~}unebjjghlor|voxupnnu}~pu~onqrpnlrzszxskn{~~zv}}y~ztrw{wvqkfcjkdnuwyzwqrslhbhqrruww|wx|{{~uuuurmjjx~}x~~xz|{yxz~wpjhv|~wsru{zlg~}kfhigdaeqnq~yw{yvrqz|z{}y}xx|~~{yw}}xzyusojfgt}uphjqnlmoqw|qqwsppu~|n~wmnqqpoozzuz{yspmy||}{yoy~wsusrqpy~}vzyztprsqpov}vzwpnnxywx{~vwgpyskhisvox~~|w~zp{zssvy~{q|~}|wsow|z{~{totlmppppv~vxwqw|~x~wg[f|wqpnkqusspu|{yz}typpsrqqu}{rrt}xttuy{yysou~}xppz~{yvk]l}pfinpoopxvlruuyztx}zy{~~}}~zki}wrszsr}|w|{x{~{vlc\bhi~xtssyzoptolkqzti{lb`bgptrvnkuyz|zwuxvttxy|xz|~}}}zusqnrtiptzwoeizu`]ioprrrvz~~yvwy}w}lm{os{~|{|~|spsyyzwusuz{|pdlu~{uonw|m[\utdadhhiipwnoz|~~yvwvzzz~~}~{solozqqx~}~vwz||}|ncbdr}phdenjdottuw|~xgkveaekry~{{~|zzzxx|}{yyzotxy}wopc^]_gs{xuporpmidikjlrw||rzwyzwz|{yx}{xwz{}y}~ytsstx~zvrtqbdpxwqecegilrztmuxyzyvtxy{~~||~}yxskjmpv~}zzz||||~xtz}}}ww|zwsmghlpz~z}wjxzmjlnptyv|}~|{z}|nmv{{xyxvtw~yrzzxzz{~vn}g[Y\bm{||~{yurwyvuuux~wnxwxytwyyxx}uqwurokhmw~z}mr~omqvy}ytrptzqo}wrssqoouujnwz|}{xz|}}{{|}{~~xo~~njlqv}~|z}}ywrt{}||}}y{y|{}~||{~{wrnmu}z{tk}rggkosw|zqvxvvutw~{xutxuluxrsmpuvvvzvs{}}}|z|ppygbgpz~{uswwtrrrv{qnywky{xz{yww}{oqusqolkqw|~}zo|posx|zvsqvvmwtrsronqwngnpnljgipu|~~{xggxra[`is|}}~zy~~|zz{ttwtqppr||{|zyytf_s~rijotwz~|sx{{{{yz}{yuoqtqnnnqwtmx{xzssuuttzxwnsvfbhqz{wwvsrorxywvvv{|rstj{vkjlmnot{ulqsqomkmuz~xz|uw{zurprvnn}ywuwwvtu{wlotsrqnmrtuxuwzxttuy{~{kew{j`agmt|~}|rptqnkgfkpu~{lcs|m`^adglu~zsy|||{zz~|zwruxurqprxrkuzz|{{|{|~nanwg[Zair}~zvoprpmlmpwsks|prtnnpppqwynmstssrquxz~uzwxz{|uvxusssu{xntxtspmlqtkhpsssqopqprst{~|{z{~yylu{kefims}{xvxxxy~|txzwtqmmpquz~vgj}zjbbcegmx~vu|}}}}}zx{|yxww|~tq|~~~}|x|~}~}~}piy|k_\^`em{}ystvtrrrv|vmtz}uy{ttutsu{}sqxyxvtrtuuy||p|rmmopu}{yzxtqonsqfhszzxvsruzskpttsrpqttuww}~|mnoffhkpw|x{|{z{}~{yywuuzxljoomljilnpuyrhv{mhhijmux|~|wvzzxxxy~}rs}{wvvtuy}w}yievyh]\^bhq~xz|zxxx{zryvy{sstttu{}souvusqprttxz}u}yrqstv|~xtuuqommqskit~~|ywy~|qrwxwwuuwxxzy{}oeq~sd_aeip{~~~{|{{vvurqtxrgimljihjptypk|vjgilot}y}~}{wrrtrponqwtnw~{{yxx}xsx{{{{{~{oaewtf`bhnu}yz{yxxy~yu~}q|uonnmloutjkqssssty}wosoqsvz~}zsrtrpnmoutmt~}zyy|yquxwvutvyyz{y|~}}}zxjl~~oggkou}~{}~}||~~{y~wvusrsxtihllkklnty}qjyvjegikpx|uy|vvxvtssuzwpw}zyxwx}wsx{{{{{~{n`hxtfaciov|xzywuuv{|ss~}wyttsqquysiloonnnqvx|~qszqprtx~|wqrrpnmmrvonz}{xvx}zopuvvvwx{|}}vhev}qfdgjov{|~~~~{|xutqpquuifkllkkmqtw}vo~qllmos{x|~{xssutsrruzunu|{yy{yu{~~~}~wi`mypb^_bgnz~~vzvyurrppqwxnjnqppoprtuxz}zqwqpqqu|~}ysqrpnmlospglw~|uy|{zyxyywxwv{~}pen~zkdcdglu~||{|usqnnqumdfjiiiiknpswz~qtwmjjjlqzyx~~|wstutttuz{rox~{{~vw|~}}}~~}}{}{rdbszod`bdgo{~~v{v|wsrqprxxokpssrrrtttvxzzq}wpnnnpwy{|ytqssrqqsxvmnxvx||{zzz{xxxvyuik|rgdefip{~~x~wyvtrqswvkhlmmmlmopqtvzyq}umkkknu~~wz|wtvvuuuw|zpqz~|zz~|suzzzzz{|{{{z}|qcctymb_`bfn{w|w~yusqqsxxojnqqqqqtuuwy{xnxxpoopry~}|ytoopnnnotumku~xv{|||{{}|{|z{xkesxkeefiox|~||}y{tqolmpsmdeijjjknqsvz|ussnnnot|y}}ytrtssssw{vot~~|{|{ux||||}~~~~}|{ui^duvibadgmw}y~wvsqoorwtiglnnnoqtvx{|up~uonopt|z||xuolmlllmpuslq|~vx{|{{{|~}~~|~yncgy|phfhkpy}}~}}~}y~v}tpmkjmqndafhijlosux}vrvpoopt|y{~{xsqrrrstx}yrw~{z{yrvyzzz{}}{}zti`iz{oggikox~}~ywxyz{}wqy|{|zxxz}wmlpqqqrtwxy{{~}tlp|voilxrt}{{|{{|~~{|xvssvzxpouxyzzxtsyzomz{qmmnpu}~~~tpwuls}vtsrruyyrnrttuvxz{{}}~}tp{~rjfhny|{||||}z}ytpnqz~yuz~~}{yskgq|ypkjkmpv}}z|~{y{||||}~yrsz}{zy{~yuxz{|}~z~qgcdir}|zwurnmnppqrtwuprw{~|wz~zyxwy|}xtvwvutttuvy}zu{ywwx{|{vnlry~wspnnpqnijlmnopqrrrrruy}ztu~wplkmqx}}}}{wtqonnqv{|}}|zxtohirwsmiiiknswwvz~}|}}}||||wrty~~||}{y{}}~~~y{zqiffioy{xuqooppqqsutqquy{|~~|y{~}|{|}~{wwxwvuttttvy|}x{|zzz|~{zwqmotz~xurqqrqmlmoopqrtttttuy||wty}ztommot{~~{zwtqnnnosw}~~}|zxuqmnuxtpnnnpsvyyy|~~||||{zzzyurty}~~}{|~~{wwzvojhils{|yvttttttuvvtsvy{|}}|zz~||{{|}{xvvvussstvx{~{{}|}~}zwuspmntz{xvvvusppqqqrrtuuvvvx{~}yttxyvqoprw|~|xvtqonnpsw|~|zxuqoqvwtqppqsvyzzz}~~|zzzzyxwwvttx}~~}|}~~|vsturnmnrx~~{xvttuutuuvvtuwz{}}}|{|}|{{{{{yvuutsrsuwz}~|}~~~|zxusrrqrv}~{ywwvurqrrrrstuvwwwx{}~|yutvwvsqruy~{wusqonoquy~~|{xvsqsvwusrrsuwyzz{}~}|zzzyxwvvutuy~}}~}xtrsspnnptz|ywvvvvvvvvvuvxz{|}|{{~~}|{{{{zwvutssstvxz~~~~|zxvtrqppsx~}{zyxvttttttuvwwwwxz|}~|yvuvwvsrrtw{~{xusqpoopsvz~}{xutvxwvtsstuwxyyz|}~~~}{zzzyxwvvutuw{~|xuttspnnorw}}{zyyxxwwwvvwxyzzzzz{~~}||{{{{yxwvuttttuvx{~}|zxvtsqppruz~~|zxwwvvvvvwwvvwxy{|}~~}{ywwxxvtsstwz~}zxvtrqpopqsvz}zxwxyxvuttuvwxxyz{|}}~~~}|{{{zyyxwvuvwz}{xvuusqonoquy~~}|{zyyyxwwwxyyzzzz{}~}}}||||{yyxwvvuuvwxz}~}|zxvtsqppqsw{~|zyyxwwwxxxwwxy{|}~~|{yxxxxvttuvy|~}{xvusqpopqrux|~|zyzzyxwwwwxyzz{|}}~~~}|{{{zyxwwvvvxz}~|yvtssrpoopsv{~~}|{{zzzzzz{{|||||~~~}}|||{yxxwvuuuvwy{}~|zywvtsqqppqtw{~}||{zzzzzzzzz{|}~~~~~}|zywwwwvuuvwy|}|zywtsqpoopqsvy}}||||{zzyyzz{|||}}~~~~~~}}|{zzyxwwvvvwx{}}|zwusqqqppprtx|~}}||{{{|||}}}}}~~}}||{{zyxwvvuuuwxz|~~|zywvtsrqqqqsvy}~}}||{{{{{{{||}~~~~~~}}|{zxwvvvvvvwx{}~}|zywutrqpppqsvx|~}~}|{{{{{{|||}}}~~~~~}}|{zyyxxwvvvvwy{~~}|zxvtrrqqqqrtwz~~~}|||||}}}}}}~~~~}||{{zyxwwvvuuvwyz|~|zywvutsrqqrsuw{~~}}}|||||||}}~~~~~~}}}|{zyxwwvvvvvwy{~~}{zywvtsrqqqqrtvy|~}||||||||}}}~~~~~}}|{zzyyxwwvvvwxz|~~}|{zxvtsrrqqqqsux{~}}}}}}}}}}~~~~}}|||{zzyxwwvvvvwxy{}~}{zywvutsrqqqrsvx{~~~}}|}}}~~~~~~~~}}||{zyxwwwvvuvwxz|~}|{zywvtsrqqqqrsuwz}~~}}}}}}}}}}~~~~~}}|{{zzyxxwwvvwwy{}~}|{zyxvtsrrqqqqrtvy|~~~~~}}}}~~~~}}}||{{zyyxwwvvvwwyz|~~~}|{zxwvutsrqqqqrtvy|~~}}}~~~~~~~~~}}}||{zyxxxwwvwwxz|~~}|{zyxwvusrrqqqqssvxz}~|}{zyz{}}xrtutyyzyzxyzz~}}{~}yuvy}}}usrqtz{sru|yqouv|~}~~}|ty~}{wz{|wqoy|vxvrowyy||}|}~ystxvtptrlgltmhoz|tmu|~zwz{z~~|}}zzwporpsxwo{yo~~}||xux}u|{rzv|z}~~pmqkqsyzvpn}|~z}|~wuuyrtvwutwqtxzxt~}zzy}|uw{qv~ztv|uuv{|v}|zww}{zvz~zwyz}|zstyyu|yz}~{w~wwt~}wxwvrt{xvw~|xsxztxsor{~po|psv{xuu|z||{}}zzr|ugo{|~~x{xuvy~|~v}~yoqurzuq{zv{}y{lo~zxsonslisiutnt{wy|{|{y{w||~x||~vx}}xsmrz{vvv|~~vt~yllqxwtsqpojn|xvgamnjzsmt}~w~|yt|{xzuw~{srutunrm}{||z}zyvzx}pkndd{vwputtzx~qremss|qsukvw{{xwx{lu|r~tq}}p{ts}vupsvu~||qpnqggq|~~~st{v~zvrtxkkk|qu}xuuwwqox~up}w}}w|n~w{zpxr}~~{u~}{{p~~}~qoxsggt}~}rx|uusrn|xoqosm~uxtps}{rz~}v{}}~y||s~}yyyqs}s~yzuy~}~~}y~{{{lpxrmm{~}}t||tpmkztfnvzlp~{{ppv~pu}}y~~wzzz|}y{uwopyxtxs|||~}||{lu}rrn|zrzxzsofnysjxvwlqyvyssywx|{|x|xxyyvwqnx}yx}yy||~w}~}~ylq{ysso~zuv}wnhrwmi}tzpp|y}wr~svz|z~yz||wxyyomt}~wv|}|zzz~}vz}xpswvupp~yoxzpltuvh~{wuourwr~yu~|yz}x{z{zvx~|pqv}y{~z}tzz~wzxzomutrrp~qx}wlwvufyrxqtr{vqz~vxzv}~xyz~~txunszv{~}}uu||}zrzyrossswtyxm|mtxwonqrvmpw~yss}xyuy|~v{~~|tq}{twu~}|z~z{u{|rvwqorsr|x|o{}oqytsn{xlwsov~{uq|{{wx~}~w|{}}~unx~~uvu~}{}|u~{{}swtrsqru~{py|~rqxvutyzlrtkv}~yr}}vw}yy}~y{{xlr|~y{x~|wv~~~~}~w|vvsrspqu~|u||~suwsuyy{orxlxyu{}uz|y~|zz|xy|xms}{y|x~~}v}u{}|y|u}{vvptuoot||v}{wxwuv~{ypqujy}yy~~xs{zy}~~||ww}uov|{~||}yzwzuz~}x}yy{zxtrnuuomwy|}{~x{trxwxpvrn}{{x|{usw}{|{zvyurw|z~x}~vx|~xyx|z~}syyxvunqxulpyw{}z{~zts}rwnuotzzy{||oxzz{}z~xw{ytsy{{{|v|x|w}{xz{vyxwvpkuypmxu}~{~}~vsztutsvryzyx|~ss{~v}yzyy}wruy}~v}|zx~~y~xw}}uwvxzvlpxwosvu{x|}wvovrtvwzzx}|~qx|}~}xvz~zy|~yvrty|zy~x|{xz{}~}vuwxyqjqwuows{{|{}{vz{oxst{zyzx||zs}{}|x|p||zz~|yvqs{xx{z|{~~vyzuruxypkpwtpvq}|{{}|{wzowrrzxyz|~yu}}{}zxp{|z|~zzwrr|zy{{|{zw~z}~}|zvqtwxokpvtsur}~~}{zw|pxsqywyz||yw~}}xz}sx{z|}z{ysp{{~z~{{yy}z{}{w{vyoquwolouuvup~}y{yruwo~vxz~}{y}~w}|wu{{|}zzzvqx|z||yx}~~x~y~us{qosvplmrwyyp~x}}x}wqzrzxww}|zx{~||tw|{}zwzyst|{}}xx~z{yzxxvruvoqtrmmpvz~szzx{||ruxu~wt}{~}{y{~zysz}}zvz{wt~|~{~yw|y|~v}}v}rwpyrosrmlnuzwz|v~|}}uryyzs}~|{|{yy}w{sw}~zvy{yu}{{~~xv{xyw}~v~rynvuorrmmosyy{~v|~}}wrx}~}s|}z|~|zzx}t{ut}~zwy{zw}z{}~yv}xxx}~w|u}xotuorqmmnrzy~v{}}~xrx|~t~|x}~}y{v~tzut}zwy|zy|y}|}xv~~wyy~{y~yx{wosvqronnor}z~|xz}}xrzz|w~yw~~|xzt~s{tu~~yxz|y{x{z}}~wx|wz~{~~y{~xzwvntuqpnnnpt|~}zyz}}xrzx{|vy}~yzvxxu{sw|wx{{{{t}|}}{w}yw}~{}|y~}yzxvsowtqnmnoq{{}{z|~|tyz~y~xu{yytszyt|~yxz|{}tzz~~|y{zvy~xz}~|y{txpsxsolmnou~y|}zy~}zuy{~{ty~}x{tyzt{wv~|xx{{}ys{}{~}z}xvz{z||~}x}wswouxrmlnnoy{|{}yz}yy|}xt~zyyswu|ww}zxy{|uxz~~y}}{vw{w|~}ystvnvyqllnnqzyz}y{~z}}~}vu~xzwtwt|xx|zxy{}t{{}y~~zvw{~w~|}{zqsvouzqllnnrzx|~y|~{|}|uv~w{uv~ys{ywzyyy{t}}{y}~zvvz|x~}|~|yzrpvprzrllnosxw~y{~}z{uv~vzuv}{sy}wwyyz{v}~~|x{~zvvyz{|||}~{~yytnusoyumlnosxv{y|vutvtxwo~~}{uu}vxut~|xsvu{ywy|}xz|{xy}wsvsu|}|~z}zv~wnpvrv|tqrtw|x~{~}~{w|zx~|omu{vqqxstzv}~yxz~|wvv|ytv}{~{|z{|u~|rnuws{zttwz~|{xsoq{}yxy}~vxt|wsx|wxpxvrtnn}~{|}y{~vpo|xxyvpolorkttlfjqmpsnknqwty}~~uyunomjy}|y{rw~{yyw{}y|utuyztrqv|yxr}~{x|~xytu~{{|y{~z~~zx}~{xwx~|qzxs~xsqw}{xuowsosso{z}}xtrvustl|~wqlrpmvrmghrpmrpmnsxr}~}~wsxolooly|}v{}|zy~s~z|xssu{xsrtzwzvn~xv~y}}ytw|z}{|{{|~}zyy}vuxww~|tqtz~zyovvpqtsq|}x~~{vtvvqwlnwnrsnwuplgnunnspnrvqz}|~~~yv{rnoqmpzxy{~~}|y|s{||wsstzytsv{uzwkzxw}x}zvvw|~||}||{w}||wwtyz{|tquz~}}yqyvqquuqvyty~~|xw{vrxkjz~}{qvsr{vrnimvpkqrpstoz}~|||z~~zx|tppsqmsus{~~zy~wy~~wsrsy{vtx{tyyms~{~yw|x~|xv|yw~~||}||~uw{zsv~y~wqty|~}~}rxxsqsxvqvwsz~}{x{yqwrio{{y~xwwr{{urolqwnkstsuou}z}{}x{}}x}yrpptrlqus{zx{z}}~urru{zvv{wszupt~}wzxzyx~|zw|zv~y~{{}~sv}}rv{z~xsuz|||z|tzwrqrxyrsyvx}|~{y{uoupmo{x~wsqmntqinuutmtyu|}usssuxuzz||}}}{tjuso}}|}}zvu~zzusty{xw|yrunnv}uz~|zv~~}||}}~vplt~}}{{}{~wq|}{~{y{~}|yz~~wropv|{}ojdes|yrxwsrkryuttsrswvot|~ut}xy{usrrv{|wmfemx|}|yvuutuwwtnn}zq}|tqquz~~|yvvtjlmnz}|x~~smjimtzwtv|xzxnpz~vtxxy{{yvyyz{snlnv|yupcdfgv}|xvkowtssssqqvsowwt|ww}zsqqruz~|phegoy~}~yvttvxxunl{|ry{wrruz||{zxxsjmln{~wzqljjlr|zvx~~x}wpsxxqv{||zxuz}x~|sompv~}~zwldgciw~}}umvwuuuvuqpvuqy~uz{uy}xtrrrtw~xnhgjqy|{zvtvxyxsmt}xvxyvsty{{zzz{ynmnir}~|z|wpljjks{zz~{ttv|}sqz~}{xuy|xxropsy}|z}qhibdpxy|{ryywwwxxtptxtv|w|{ux|{vtsrqs|}smjjnrvu|~xvxz{ytovzyxuwwuvz{zyz|~zoqmhqyzw{{ztpmjhp~z||{vwwz}upz{wtzzxzurrtwyyw~tllcclsts|y}|yyz{{vqryxvxx|ux{~|yvuspq|yxrnmmorst}yy|}{wsxxx}vtxxxz{zxy|}sspinuyvs{~}ytplhkxzu}yzyxz{ts}zuy{v}yuttuvyxw~|qpibgnsqpz~|z{|}|vrt{{vt}xvy{}~}{ywurot~z{xronnnrtu~zz|}{ww{uxwrw{{}vmnsvrmr}~|~~{{xxy~|zxuqmp{xtw|{|~{~~|}yptt{zywwwwwxzx{}}||{}|~~~yztmquyxrsuq}tqpomkhkwwuwz|||~|~{{{z}yvuutvyzzwwwzzookcflqtqmv~~~}~~z|us~z}zwrnny}{~}utxxy{tpmlklpttvy|}{}ywy{}{{v|zqt|}{qpuz~~y|~ujw{yzuoigotrwuu{|z~x{z{~yuwzvu|}{yxwwzz{~ynlv|xmjmpstpox~zy~}}{{}~{xzyoz}~|wrqy~|wtwtotz|{wtqprvywtz}w|~zwwy{zzvy~|tqy}srvy}~|{{z}ulty{{unlstrzxsx~}}w{}y{~ytsyyux}}|zxx{}zz{yplu{pnprstrpwyw~{{z}~{xz}wsy{}}xsu}|}~|tswuprx|{wsrty{up~yw||ywxxyx~{wz{{tr{{tvxz|}||~{{zrrtu{~vquxrw|ury~yy}xy}~|vru{ytvz~~~{yz~|}}|{zwooyyttttttrr{{v{{~}{|~~|zy{ywxvy}|wx~{{}~~wswzsorw~}xuvy|vny{vz~|zxxwwx~{xzz{xsw~xyzzz{|{~|yvwqqw{}x|ztz|vqv~~yz{vy}~{vrt||tsw}y~}{{~y{~~|zztpw}~~{zxvuutsw~{v{}y}{|~~|{zyw|zrv{}~{|{{}zxtw|xoou|zy~{z|~yow|wx|~~|zyxwvw~{y{xz{vv}~}||{zz|||}xx}tmsy{}x{{ts{}|~zz|vw{}|xsrx}yqrzxy~}~}x~yzzusy||}~{xwxvtx}zy{w{}||}~~}}|ytyvpw|}}}~~z|{vu}xy|w||vv~zwwvusrv|x|||yo|spuxx}~|v}ujv}}z}z|khxoggmvwpp{~wy~~||~wwzrqqnnsuvsnt}~{zyyzz{{||yrwvoz|{}}~yousllqslhqxsv~yuvuy}{zxusw|~zywrx|{{xtv|~|vz~yonzxpvspnjp}ztqswumpyvu{|{{~~|}~{x||}~ww||}}{tptyxonx|{yxxxxxyzzxokwzy~}yvywyyutwwpnvwv}~~yuyxx~}|{xvv~~zppwy~}wyyv~}}}}zqjt|{y}~{{xpnsru~~yvuwyvrvwv}~||}~}~~zx}}}~|tu}{srz~tow||{{zyxxxyzxpku~~~}~~}vv{z|zvtuwurttw}wv~|x~}}|zwx}~woqxzxwzw}|{|{xnku|~}~~yxtnqvuy}zwuvxxvutx}|}~}}{vy|}~~~}wrx~|tt}tsz||||{yxwxyyumoy}~~~~|vtzzz{xuuwwvrs|zu|zz~}~}|yx}}}~}{qovx|}vzx|}{{{|zrlsx{|xuoqutv}~|ywx{|wqv}~~~{uwz~}~~{|xrwz{yu|wvz|||}|zxwxyzumpwy}~}~ztsxwv||ywy|zqq}yv~yw~~~}zz{~~|{rqutxx{~z}}{{{|yqouvw}~~~ysortqsz|yy}ypv~~~ztx}wz~}~{z~wrvww~~y}{z|||}}}{yxxzzunrwuz~}~zsrwvty|yy~uozzw~{w{~|{{|{{}trust|}{~~}~~|{{|zsouvty~|unptrrw~|yzuq||uu}zx|~}~yy|zstwvy}~|}~||}~}{yxyzwqpvvt{~}}uoswtu{|xytr|xx}{}~~~}|~z}~{y{ystvtv~}{~~~~{zzzxqpwvsz}slpusu{}yzxs{|uu~}|}~|~~~{vwwsvyx{}y}}|}~~|zyxxvppwxuz}|~umqwwx~}yx}{sxyw}}}|~{{~~wuustyxyxz}~~|zyywqovyvw}z~xnnuwx}~{x{xw}}ws{}}}~~{~}}}~~xsrru|}~zw}|}~}{yxxvrov{yx~~~xz~zqovz{~{wyyx|{w~|{{}}z~~}}}}~{tqpt|}}{u}}~~|zyxvqov|yx}~~xx|{rovy{|yz{{|~~}xt|}{{}}z~~}||{z{|ztoov|v||}}{zxwupox}zy~}}wvzzrqxz|~{y{}|}}~zx|z{~|{~~}}}|z{}{unnvzu}}}|{zywtopy|yy}~|~wvzxrtxy||{~}~}~~~{ux|z{z|~}||{zyy{zslozyy}}}|{zywrnt|{y{~|zuvyvrvxx}~{|~}{}{x{z}}z~~}}}|zy{}ypkr|~}~w|~||||{zxuoox{xw{|}zvwxtuwvy~}|}~{ux}z{z|~}||||zxx{{ukmx}}{{~}}||{zwqnu{ywy~|{vuvttvtuz~{|~~zy|{~|{~~}|}}|zyz}zqjqz{{y}}|||{zytnpxzvvy~~||wuuuwvtv|{~~{vy}{{{}~}||||{xwy|wmktzz}}}||||{xrnt{xvx{~}~ytrsuxurv|}z}~{y|}~{~}||}}{xxz|wmluyz}}||{{{{zvpnvzwuw{}|{ursxzvtx~|{~~}ww~}}{}|{~~|{{|{ywvxzummuy{~||||zvppw{xwy|~{}}ysopw{vtx}{z~}~yz}~|{~~}{{|{zwvwzwonty{}{z{{yvoowzxwy|{||vppx~zvy~|z}|ww~~~}}{z}}{zyzzxvttwwqotx||{{{zwqov|zyz}}z}~}{wpls}~yy}}x|~}~zy~}y|~|zyzzywttuwtrux{}zyyywrot{{yz|~zz}ztnq}~|y{|}}yv||~|x{}{yxxxxvsrsuutwx{{zzzxtpu|}{{}{y}~}|{ytmo{}~xy}|{y~~|x{}|zxxxxvtsstuvyyz{yyywrou||z{}~|x|~}{voq|y{}{}}xw~||zwz|{xwwwvusrrstx{zz|zyywrqx~|{|~|x}~~|{{ytnq|}x{~{}}yz}}yx{|{yxxxwusrrrtz}zz~{yxxuprz}zy{}~{y~}}ztou{z}{}~zv{{{|vx{{ywwwvusrqqqu||x{{zyxsqv}|z{}~{{~~}{{zvpow~y||{~~yy|~yx{}{yxxxwutsqpry~yw|}zyxtprz{xxz}~~~z~~}|ztpt{||{|~yv||{{vy|{yxxwwutsqoou|{vw}{ywrqx|zxy|~}|}|{zuppuz}{~}{{{~z{wz|~~}{{xxxwtsrlnnnnqqt|~xxy||~xvwwvvww|~smqvwunomlkmnotv~~|}{~zwrqx~xtty}{~{suuutvvuz{x|zzx|x|zyvvssuuyvz}yz{~wssvuwsrxskq|y{zlmy|urqvz~|~x~ystv{xttz{|zsxyns|vtvvwwvs{||y{}yplvqovmhhgklkfhmtvt{|~zlknuz}yuwtwkqrp~x{zznmnow}yqrrv|szrx{}{tyu~wqtpuut}w}tmnl||yuvxtx{uyxtxztorspvoxsxyuyxrr|~z|{zxqqz||}||xzoqsqjqsptzv~|}{~{rvysz|z{~~{wstqltvyx}{wt|uuwtlu~zz~ztwz}|wy}ssw~y|uuyuy~z|~to{vty{~~wx|suu{{~utz}vvzsqttqxwrmssnm~|~~~}umt|~~{}|vlnpkfqyssz{y{wutxu}}~}{}uwzsstlrwx{|xv{x{}yquvoszzswy}|~{~vsvyx|ytwv{vx}vrtlmv~{~{{wxzrz{yquxrmx}trurpxvsrzvvyyt~~}|~~xnnusp}{}wrxvsutmr}~~y}{vttyxzv{{~|}~uq}{ttztv{zyw}{ztrw{uuxvtzyw}x~wu}ssvryzu}|}~|vxyqnstw|yxvxrv{yvx~zzztnz~yv}wsryxu{yw|{|~|{zsszvx~}z|w{ztzvtuztx}}}|~zz{pxwvpz|z|~|~yxzqquvqttw~}zqwyqnz}srvtv~||rz{v~~|}~}xy{|y|{qjtwmnuw~yz|xuz|tzwuvyqsxxsvqwyvwsw~~}|~|~z}~{{tqtupxxuw}y|x{{urtxxzzzs~}~|xnr~}wr}}~|}y}x}{}zws{{zx{u}|}~xv~}{{x|z}yzs{vsuvpmv}oxy{{}}zsio{yu~}|zz~rx}szyxwxvw}twp}wrqtypy|~}}v|yyumvnuuxyxx}vq}tv~v}vt|}~|{~v|}~}~~~v~{xq}uotmfpvz{|{yvv}xyrw~{w}}~xwqsvwu{u~y{|}}|{uzzsxtjyw}|xx|vzxztwyqzsvvop|ss|{z|y}{z~|yyvputmgtpn~z}|~yx~{{|y}}ty~s}}y~||~~}~{xz|~vtru|tuv{~|xyyxzz|z}}{}u~vwov}twwpx|~z}~|{}~{xvow}w}}|w{zvw}}uyy~ywxyx|yt{qvwq|{y~}|~|xwultxmmpmu|}z~}|}{{y~~yv}{wxxz~z|~{|}{y|~}wxyrtxooqxz}tzuttyz{{~us{}}xx~wvuw{|~~}~{~z~{vhsyr{|}|zxzxz|sy|wv{rn~zqvov{yy{}~|{vx}uqrpft~rx{~{}{~|y~~w{~ywzzswty~w~y||~}|{{xrxwwpqywyz{yyys{}~~y~~ww|wuwsyvqz~~|z{z~zqpzyx{yv|}{zw|||xx}zu{zq{~stqsz{u}}z|~yw}tnqqfrvopqw}|y}yz}|{}~{}ty}{w~|y}{|y}}|}|yxvxqvpqwuurz{{~|x~}w}{|{w~xtwuzvtwu}|}~~||~~ux~txvsqzxz~{}u}~xvxsr~zuuovzuz~}~x~~}y}~ytupmxsgkkt{x|y|~}yy~~rwyuv|x{~|zz{y~{z|xxxvztvqsyvy{wz||zovt{zv|z~{y}x|}v{xst|}}yqhonknqx|w}}}rr}wnrpu|y~}vzvx~y}{||{~~}v{ljjuwsm{u{yruyzyrsvsmx{qx}}~~}}y|}y}u{xyyy|ws}~}o}}}zy~|yv}yxuyztsq}z~|y{y|x~}{|uyqwuiklw{vy|}w||uuw|vz|uzt{{~{w|~}{z}z}}qq}wxprwms}twzz}{y}|xvytrulqlw~zvsz}z|z~zx~{w}v{{rmvxzyxs~|~~zx}~|}{x~tuxrxzxvxz~w|{y|yyzrzuymwxt{vns~}|||~vwyytt{ytzxvv~||~}{wyyzzxqoxzoqpzwzuuy|z|y}||z}{owvy~zut|~x|{}zy}ytyztkp|qltvov~~yx|yyw|{zssu|wyz}~~y}z~|~}wzuz{rptvpstru~y}{y|r|}tu{sz}xu~y|}}|w~y|xtuxsuxsqxvoxuv{y{~{}y}yutywyz}u{y}|~t}|u|yrz|xu|xuytkr~wszz}{}}|xv|zvu~zrr}w|zz{{~{||tx|xsspvqsppu|~|z}{w}~q}}x|{wu}|v~|{uy}ux|wms{pnrqirzy}||{{zytsyvvxy{xx~{xx{zyvs|sqsrptrpu~}}xwvvzrv~yw|{|xu~y~xz|zwxxvxwjtssnw|ts~~{{{y|xx||wzsy}{}y|~}xzvyvv}wx}z{vnt|uowzxw||~w|}{u{wnvvrtsu|~}|zx}}wytuxsuoosupu{||}}wz|~{yxtyyxw~{z{~zyzruvppnjrvqpy|{zzzuvtvosxtplnlkuzwszvvzzv}}}~|z}ywxthnqxuru||wuwzshuy}t}}~{rpust}twotqv~{~{|y}|tpv{tprzyqm{zo~sxx{vpqzzpv~qv{|~{y~ztw~}{yjp|{|~z~yvtt}{yx{zytxtsz~tpy}||{|}{kxvnp}sfr}uwwp{t{~{~x~}unrl}|{|rrtkptrjpgyxlmmplrktrlvuxjq}~z|x{uy|y}{ny|~tvs|x||}~zv{}tqrstjjls}{zzqqv{osj}{kowunvpvtzwuzpy~{{z{wzrtuv{zzvwhv}xxswt{~wrvxynjl{|~{wxunlt|omhzwclupjqlnrynny~unx{xx{z~w~oy~z|}vw||z{}sjouxmjh{}|~~}upvwnoz~jozxqwsqwtpzzt~v~{||}||ty|ux~~sv~}kq{{v{{zt}{{lpw{qpizx|xz{rlp}xhltzoestlrqkn~ujszxq|{~z}~zz{w~wr}~|||s}{}xqw{jfmuqonp{}ztq{si{zylpxqxyro~tu|}v}{y~|}|y}}}vty~{~zsuymwx{x{|y~uvrimwvrtq{tvyslw{sbwwroiqmsuojvspvxryx}~{|y}ywu{{~y~x||}~{zpmxpegqqqtqxxyp{|zhzsvmspy{uny|vzzu}z~||}sy~vux~}vr~{txwyw|u||}srzrklrsuvvqv|tnxwtexzlrlklvwpkvysutq|y{}u}{{}|x}~{{{z|t|z~~{wmntmhinnttxsvt}zvn{oxpmt}ztp}~yyuvy}}y|wpz|uxx~}|uv|x~vxs|wq}ypvtpmnorwv~{q}owwupp~pltkiuytnp|}zvsnw}v{xvv}|}y|zyw}tv~}~w~~xpopnmjjjsrx}wzw}yxv}pvvkt~{vq{}xru{y|||~~~yow}wyx{wxyzwwq{|vp}|~~xsuqrpmkqtu}{y~tzwttw{slvljxxunsz}}xpmy|uxyvu~|~{}z|vwztv}z{t}|yqqnlplhhqpwz}}{}xx}}}urzmt~{wsz~yps}xz~{}~{ps||yzw|{{wz|xox{vq}}}{|y~zuunqtpimsr}z|~w{xuu}xwqovktxvrqxy}tkszz|z}|tw~~~|~x{r||wt||xw~u{xurphnqmelpr|y|zx{|zwryr{z{uw|}wnx{~}y}{~|wrszy}z{||}swoyxut}{y{y|zxvrjpvriorv|~}z~xuxysvntswuwrtyxvlt~{}y{y}|vuz|w|rzywwy{}zyvutsrofjqqhmpu{}|x{u{tt{}xyvx}z|qv}|vy|{}zurtv~~|~~uz~puvvv{}z}xuuutjirwpost{}x~{vwtswnu~utts{wy~rp{~}~ztv~}~zwuw|zxvxwxy}~zz}zzwppprjdjsqnsqz{z~t{vs}wwu~{{yt{~yyzv|}{~{wvsry~~}zvxtutv}|~~z}z}sqrungluvtvs}vz}yw~quwn}stq{zx{xsy}~v|xzz}}z{xtz~z}wwux~~zz|szt{omnqjehpttvrx}}|vw|sxvu|{~{x}v~|y{x}}|~ywxvqx}~}yz~}zwtsw}z{t{u~soqtlhltvzxv~tz|z{~tsys}vqt}yyz|xy|~}{u}{||x}zz{xs~{|zuu{|y}wyqtsyolpohfjqtzsy|x~uy{{yr|||}|{~xx|}|||v|}{wxytr}}}|z{~zwsu}}{}z{|vzsyzqpsojjos{{uux||}yrxz}{rr|{zyyx|~~}{tyzy~yw~yy{zt{}{}|vu{{}x|}v{qxqxsmonhfint{sy}~~x~xx~{s}~}|{z}vyx|y~uy~zwxysty{z~{||xtv{z~zz|v{vuzsqrmjjmq|yv|wx{~|uw~|st|{{xww~}xuwxw||t{yxzzuzy|~~xv{{z|~w}{szzrsvpomighkr{tz{|y|~~x|~t|~~|zx|vxv}u}zt{|wwyvuxv{|{|||wv}}yz}~~z{x||utysrnkjkmw}w{xy||u{y~~sz||zvt{~~{txs~t|zt{|xx{xzyy|~}~yy~z{{~{y|wwwxspvrojhhikvzxz{{~y~|x~~~|xv}{uys~uz|txzwwywyvvy|y|}yx~|xyz|}|~z{xxxpwwsmkklmw|||zxz~yy}zw{{|zvry~ytxq{wv|vv~|xx{z}zx|~z}}{~zy~|x|{}{~wyvrwnpvskhhjkszz~~{{z{}~~}ytx|uysw|uyztz{xwyy||u}||zx|}{~~zxz~u}}{z|qwvnvyqkkmms||{x{{}{z~}{{yss}}twts~~yt{xw{zyz{~x}~}x|~{xy~|w{~{}~~{|yv{ppumoxrjiklpyyz|~z~}{vt}uwus}}{sx{vxxxyz}x}}|~|vy|~zxx{z{y}|~{xtpvsoywnkmos{z~yz}u|{yss{~twtr{{|tv|xwyyz|{~}wz}zxx|z{z|{~|}~x|ww|qmsrmvuljlmsyy}~u~xvrmnw}uzuy{|}~s{~{~~}}|tysosx}~zxy~{|}z}zv|z}xuuv{{}}~uzsoy{zv|xtpu}}z}x~tw}}xss|wyzxwyrsv|}yyz}}~~|}}|zy~|~vtuw|wvmn||xzsr{ls~{wsoov~xzww}y|z|~}}tzy~||{|y~zux|||zy~{rrxsuxrmmntqs}}~|}|yx{u~yurqu{w}yw{}~ttwuz{~vpv|rrrllou}}wstv{w~}zx|~||}|~zxx|~z~uqnox}su~}vplmtz|vxrv|y}y||vy||{v{ux~|z{zwx|zz|{xxz|~wywpnotor}z}~{{zwv}~{y}~zwtrt|}z~yv{}|uvvpzsvwttmlms|~xssu~s}|~{y}~zxw~~zy{~yxtqos{}x{|tnmrx|xzts|yy~yxuy|w|xs{x}wwy||z|}{wtvxyxspssnyz{~|zxuw}}y{~}zxvttw|}wx~~z}~|tzsqzuzyysmmnt~~xttx{r|}{{yz~~{vv~}~}z~}y|wtqruz~~{vootx{~}z|tu|xx|}{{tv{}w~ytz|~}xwy~}~|zy{}{xst}vzxtusnxxy~{wtv{xvv~~xvuvy|}}zy~zwz~|tzwrx~y}}wonnr{|vuw~z{xu{wvx|~yt~{}x{z{~wtrsw{}||~upswz{|z~zt|zwx|~z}{su{~w|~wy}{xx{~yuux{|{yt||uz{xxptzv|{vuw|uv|v|xvuwz~~}{~z|xtx|tw{ux}{tpoqw{ww}|w}xyxrtz~|v{z~y~~ywzvsrsx}~~~||~{ttxzzzx~~v||wwy|x|xsw}w{yz}~~~yx{zqqvz||zu|xx}}zqvzu{|wuwz|{{z~|~~yy~zruxryzuy||ysqsyst|vpxtor{|w|z}z}}z{}}yvsssuz|{y{}||~}xnospmioqp{xtxy|~~zz~xzwnrx||zuu{x~}xqmo{vpihjlqvwsru|x~{wx}wxsw}vx{}~~{riqtsjmsutqloon|~y~y|zvvx}}vuux|}z{}~tmjnxzuqytoqqrtvy}~xvx}yx|~uy{|ww||vwzuvnklotz{wvvwywqmov}{u{zy~~|yvw||v~xz{{|~{yokpnyz|{tprkly|}~~{y{zzzwvwy|xwx{~|ww|~volms||{zuvtrrruy}}xu{~zy|uxwv~z~}wxyr|{sommos{|{{}~{uons|}{|vy~vz}z~|wvx}~x~{{||{{}~ulplo|{uwqkuyz|~|z~z~|z|~ywvvwyxy|{wx~~~uomou{}{zzzwtqquz}~{uy~}~~{{~zvysw||y}vtztpnmnq~~}|}wrpu|z{yxz}uw{z~~{wvx|~w{}~}|z{snpjo{}y{snvxy{~|y{}|}y{}ywvutv~zx|}yy~~~|vqnpty|}}}{wsqsx|}{wy~~y}~}~{vyrt~~~}{zv{uronmoz|~|wtu{yy|x{{}{tz|x}}y~yvwz}z{|}|{}~~yoqmlv{}zqvzxy|{x{|z}z|~~{ywutszyx}~|~~x}{tppsw}}~|xsruy||yx~{}|w|~|}wyvqy|~~~}{x}}zwsqonnu}{xxzzv~~x{|z{~zv}~y{}v~{xwy{~||||{yyrjntwtkjtwtuww}x}~~x|~yzxvuwrot|}}~~}|ulw~~~~~~~zy~{z~z}}{|~zx{~x|}spy|xvwxz{{~|{ustuvuspv|xwvzwty~|~~xx~}}z|z||w|wrw~}~wsunlrvyvnmw{wtux{x|}}|zp|~zy}{|z~vtz~~yvutstuvsrstysqw{}|yvvx|}}|x~yqu{|~wx~||so|}}ytopyxuvpqusrw{|zz{~zw~~{~ywzzvy||z|uouwrtvwvojpywqps{wu}}yy|}z}|qz}|z{~}|~xtw{{|xwwywrou{roty{zwttvyzz}x~}tt}{}xxz{~uszyzzuw}xxyqpuutwyzyz|}wv}zy~}xvz}xw{|}zzy{{qt|}wxyywrmoxysnszuu}|yxz~}{|~uy|{}~|{|}{~wuxx}}zz{|vms~unrwyywtstvwx{x|ysy~~|x||y~|vxvx||{|y~xprxxwxxyz|}ytzzw{~~zuv|~xvy{}vy}~yrv~~}|{zxtprzzrovwsz~{xx{~~|{}~zzzwz~~z|zy~wsuy||~~vlwuprwyxvtttuvv}|y||~zux~{{}z}{{wrw||{{sqy|zwwxz}~xt{{zz{}~y|z{~{owzstwz|~}~|{yrqy~~|zywv|~~tjumqw|~|{zwv{yrx|~~|uqsywpovtks}zommnopppoou|xtwvwzyw|zxy~~~||x~xxuo}{rtuqmnqtwtnrrmpuxvspry}xru}{~~~{tqnjkpmlminwxvtuvxyzzzzwrwwpw~~{z||||wwyuliryty}}vqwyxutvyzzzwspt|}}z{~{vw|}~|}|||{srpilvphq|zux~z}|{ws{{tnlow|yxzqsqjgejswtty|}}{{~|y}~w|~xtty}~||}|w|~ru|~}~{unju|rkilld`jrpw~}~|yywsokhip~}vuz|yy~xuv|rfjqi`dntwwy{~~yzzsrvyvuzwsx~zvtuz|vs}~zuqos{rprsonsx||zz|ztosz~vu~{wsstw{wszty~trrmox~zvvwummttx~|||xy}|yusqpnljjp}~~{}{{}xtrru{yy|qmqxzyyz{|{z~~|~z~xuwxspsokpw}~}}{~{yvtqmknz}{ttxz{yuy|{}}yst}~sowzyvutuw{~|ww|~}~~wwunnu}~}zqnnlr{~~y|vt{|xttttsqnmt~~}}|y~}~{xuuw|z|{soqx{zxxz{{}~|}{}~{||wvysmotxzzzzz||zxupkksyx~}wy|~{vz}||z{yv{u{}|ywurpqu|~y{~~vvsjgjqx~ytot|}||~vz~xsqppnkinx{||{~}{xwvuux}~wqquwvvwy{}}}zwxvuzyrpruwy{}~~~zw~{zzyupmpvvx{vz{y}}{|~~zts}zz~~}{ywtstx}{{{{vxtkjlpuy}wqy}yyz{urrrpnlpvuv}{xvtqqsxyuwyyxwwwx~|ytrwunmorux|~v~~zusrpmkovuwzw|zw}}~~{wz~zy{zyxxwvuvy~~~xx}xsvunlnquy}|~|ps|}zuw}~ysqrrporxxv|~zxwutrsv{}xy|}|{zz~}~xruxrnnprtwz~~y}wronkikrtry}{|~}{~}}~}{|z{~|wvuutstw|{{wnormjmquy}zs{~}yuw{ztomnoosz|x|z|}xvvvvuvx|~yvy|}|{{}z~zsvzuqrsuvx|}~y~{|{tpomlouusz}~~|~~|{|}~~~|y~zxxxwvvx{}~~{~xprsmkmoqtx~}x}}|xx|}xroonosyvt{~y~wtsssrsv{{vx{|zy{|urvuppqrsux}~}|{{~}vpmkjlruqs|}}}~~vv}xwxxwwwy|~{|~||z~}srtnklmnosx}}}ywy}{vqpoptyuqv|~~w~zvtsrqqruxxy|{~}}}xtwtooooopsx}|z}~yrmkilqpkow|~}~uv~xvvvuuuxzzy~{}~|ssrmjklmmpv{~}zvvz{vqooqwzurw|~~}tz}xvvutssuvuv{}~}||yzwrrrrpprvz}~}z{~~xrnmotslmsx{~~~~~zv{yyxwwwxwtw~~{~{xvplllkjkoty}}yy|}xspptyvqswz|}{w|wusrrqrsrqw{|~{zxrpqqpoorvz|}~ywz{wplkotpkntwy}}yv}{xxwwwwwurx}|~|}~~}zyunkkkkjjnrwz}~yvx|zupptyuprwxz}~{w{xtrqpqqrpov~~}}|}zsooppooquy|~{vtwwslilqpkmswz~}wy~~ywvwwyzxuw{|~zxwzxqjjkllmosx|~~|xtvzzuqrxytsx{|~~xxz~~xtrrrtutrs~|~~{{zrpqrqqruxz|~ztsvwrmlqrmmswy|||~zwwwyyxtu~~~~z{}wuy|ulijkklnrvz}|wuxzxsswxsswz{}zzz{}~ztqpqstsps}}|xw~zropqpqrtwy{~~~}ysprusnnqpkmsvx{}~~~zwvwxxvru~~|}||{srz|skiijjkmqtx{{vtwyxtvxtptxyz||{|zy|}yspoqrronw|uxvpooppqsuxz}~~~~{vqortqoqpklquvy|}}~~ywxyyvru}}|}~z}|tqx}vnkjjjkmpruy}wtuyzyzzuruyyz{~|}~{y||wsqrrqmmu|{z|}vzytrqppqrtuw{~}~}ysqsuutupklqssux}|yxyxtpt||yy{}~{}vrx}xqljiiikmoqu{~{vux{||{tqtxxxy|}~}~zz}~zurqqokkrxwvx|~xx|wsqpppqrstw}}z~{vqoruwwskjnqrsux|~~{yxxupqx{xwx{|~}{styyvplihhiklnqv}}~zutw{~~xqquwwxy|}~|}yx{|zurqpmjluxvvy|yy}~|xtqooprstuy||~~~}zvqnotyyrkkorstvz}}~zyxuqsz|ywy{}~~{utvwvsokiijlnorv}}}}xtuz|trvxyyz|~y~|yvwz|{wvutpry}ywxz|}~z||z|}|zyz{{zyz}zw|~uzzy{~~}}|zwsru}yvy|||||~~{}~|{~}~tq~|xxyz|~|xuvvvxyvtrstuvvx|~|z}~{yz~~yswyusssuy{wsuz{zwuvz||yxvsrw|zwwxz{zyyy{}}zyz}~xy{xtrrrssrqollqy{uqruwxyz|}~~~{xz~zvrpqtz~|{zyxxwurqrpnpsrpooprsuwz|zxz}~~}wrrvxyz~{zzzurtwwvtrqtwxwusqrx{ywwxyyyxwx{~|{zxvvx{~|xutssrqpolmqxxtrtvwxz{|}~~~|yx{}zwspoqu{~|zyxvsrtspoqttrqqstuvy{{xx{}}}~zurtuuvy~~}|xttwxxvtrsuxywurrvyywwxxyyxwwx{}||zwuuwy{}zxwvutsqpoqvwuttuvwxy{{|||{yxz|}|}~|yvrqqswz|~|zxututqoqstsrrrstvxzywx{|||}~{wuutsstx~{xxyyxwusstwxvtrrvxwvvwxxxxwwxz}~~~|ywvvvwz~|zxwutsqqruvutuuvvwxyz{{{{yxz||{{{~~{xvsrsvwwz|ywvvurpprtsrrsstvxyywx{||||}}~~~zwwusrrtw}|{{zyxwustuwwussuxxwwwxyyyxxxy{~~}}{xvuuuvy~~|zxwutsstuuuvvvwwxyzz{{{zyy{{zzz{}}zxusrtuvvy}zxxxvsrrtuutttuvwyyxxy{{{{{{|}}|ywvtrrstw|~}}|{zxwuuvwxvttwxxwwwxxxxxwwxz}}|~~}|ywuttuvy|~|zyxwvvvuuvwwwwwxyzzzzzyxyzyyyz{}~|ywtrqruvvy|zzzxvtstuvvuuuvwxyxwxzzzzzz{|~~{xvusrqsux|~}|{zywvvwxxvvwxxwwxxxxxxwwwy|~~~}|{yvtsstwz}~|zyxwwwvuuwxxxxxyyzzzzzyxyyyyz|~}|zwusqrtvxz~~|zzzxvutuvwwwvwwyzyxxzzzzyyz{}~{xvusrqsuy|~}|{zyxwwwxxwxyyxxxyyyxxxwwxz|~~|{zxvtsrsvy}~|{zyyyxwvxyzyyyyzzzzzyxxyyxxy{~~}{ywusqqsuwz~~}|{zxwvvwxxxxxxyzyxyyzzyyyyz|~|zwvusqqrux|}|{zyxxxxxxyyyxxxyyxxxxwwxz{}~~}{{ywusrrsux{~~}|{{zywwyzzyyyyzzzzyyxxyxwxy{}~}{zxvtsrrstvy~~|zyxwwxxyxxxyyyxxyzzyyyxyz|~}{ywvtrqqsux{}|{zyyyyyyzyyxxyyyyxxxxxyz{|~}||zywutsstuxz}~}}|zyyyzzzzzzzzzzzyyyyxxxxy{}~}{yxvtsssstvy}~|{zyyyyyyyyyzyyyzzzzyyyyyz{|~}|zywvtrqrsuwz~~}|{zzzzzzzyyyyyyyyxxxxyyyz|~~}|{yxvtssssuvy}~}{z{{{{{zzzzzzzyyyyyxwxxyz|~~}|zywvttsrrtvy|~}|{zz{zzzzzzyyzzzzyyyxxyzz{}~|{yxwusrqrstwz}}|{{{{{{zzyyyyyyyyxxxxxyz|}~}|{zywvtsrrstvy}~||||||{{zzzzzyyyyyxxwwxyz|~~}|{yxwutssrrtvy|~}|{{{{{{{zzyyzzzzyyxxxyyz{}~}{zyxvtsrrrsuwz}}||{||{{zzzzzyyyyyyyxxyz|}}}|{zxwutsrrstvy|~}}|||||{{{{zzzyzyyxxxxyz|~~}|{yxvutssrrtvy{~}}||||||{{zzzzzzyyyxxxyz{}~~|{zxwvtsrrrstwz}~}||}||{{zzzzzyyyxxxxxy{}~}|{zyxvutsrrstvy|~~}}}}}||{{{zzyyyyxxxxyz|}~}|{zywvutsrrrsux{~~}}}}|||{{zzzzzyyyxxxyz{}~}|zyxwvtsrqrstwy|~}}}}}|{{{zzzyyyxxxxxy{|~~|{zyxwvussrrstvx{~~}}}}||{{{zzzyyxxxxyz{}~}|{zyxwvutsrrrsux{~~~}}}}||{{{zzzyyyxxxyz{}~}|{zxwvutsrrrstwy|~~~~}||{{{zzzyyyyxxxy{|~~}|{zyxwvtssrrstvx{~~~~}}||{{zzzyyxxxxyz{}~}|{zyxwvutsrrrtuxz}~~~~}||{{{{zzyyxxxyy{|~~~}|zyxwvutsrrrstvy{~}}||{{{zzzyyyxxyy{|}~}|{{zywvutssrrstvx{~~~}{|y|{|}zzvwvvxx}~~}vzvswywxwrmjupx{x}}~z~|wotzxztuzrv{vu~vqvuqqlssrmxxuou~zxzy}~}xv|}~|xtzz{|{}}~||ptzrlmpomkqstoq{~~y{zvvqzxwvz~}~{}z{}y}wz{xpnwtjqrsspu~}|{|}z~{~sv|o|twt}y{v{z{rt~}~qnqpwqmysw}}{~s}wuxzvssy}|qz~~~ysx||{xmt}zzz}{rqlr}vwy||rp}|q~fhqvyuw}x}~wz{vuuwt~s}y|~qw}msyr||~zv{z~|hozupntx}kvw|vq}}yy}wvwwu|}{uzx~~z{|rv}~xzz}yw~v~y{pxlqu}{owwmv}hrutux}{y|z}}{yv~}vztz~x}w{}~|{|xrsw{~yuynwyqyqhw{i}wgppp{{|z|~|~uyyu~y~y~xw{~sept{sm|xzxpjxqklkuo}yyv}}u~~w{vx{}{|s~y~~|}xnkxs}|mtxwtxroylwlrns~zy}v{{}{uyw{~x~ur~y{}ystitqv~zrzuxyvqrju~pussu}{x~}r~~|~r|vw{}||}uz{x|{}}}}trovwr|rzrrvuvsop|swwnr{~}}q~}~|trwuz}}~~v{v~x|{}voorww{~vzxrxqwuphzut|rrw|}~~vu}v~}nwqz|{|}}{yx{}}plq|vzvz}qtruxujxvo|rvx{|yow~{n{vrw}z~zxz{~~}tikxtz}{xvvstuuj{ylzuwwyx|~nyy|rzxpv}~|||{}{|}}~~xklwpx|wwyxstuozyjutxx{x~|n|y|~rz|quz}}|}v|{z|~{}zkjsow~w{|vsqro{{jvsty{yy{}qz|s{stx{}|v|{{{zz}{nkqmsw~|}yvqsmy|kspvx|{z{}q}~~r||tsww~zy|~y}y{y{|pitop}y~{zwrpnxotqvw{{z}}~q}~{~~uy{uus|wx}ywy|{|z{qkqnny|~}~|zvpouqpswvx}|||}p|{z~~tt|uwt|x~yxw|~|~zxzripqnv}yyprs~vqvuxu{{||~tv}}z|}{r|vwsy~y|}u}|~~{yxzvlkpos|~yzurs|znuu{uz}z|xo|}{}~rzxtvwx|zzz~|zxvyzngnpr|}ywrswsrryzvzz{|rxz~}|{yu{tus~|~~u}}}zzvv{whkoqw~zzupu{nru|wz{{zxp|{{{}syxsvw{~}w{}|{~vtv{ninns~{wprywpsx{vz}{y~twz}}}y}zq{wvt}}w{}~~x~{tqvylkoqw~|{vpu|rruzxy}}zzzq~z}|~z}tuzwwv}{v~{}~{xvqpxtinqu}}~}ysrw|rsxzvz}}y|wr{y}}|{zqxyxv~|zz|~t{zrpszmjqtx}|xqu{vrv{wy{}|zyp|w||}z|vtxwyx~}w||yu|rqpxsiosy~~}ztsz}qt|{vz}}{{styz|{z{yrwvxw~x|~}~qyupovwkmrwz}}vrz~ury}wy|~{zvr~w||zy{tuwyx|~{w}z~uvxoorypknuz|~ysxyqw~wx|}{zyq{w{|{z{uuxwxyu}{wszonpzrjmsy}{tw|rv}yx{}{yyrywy~|x{wuxxyxu~}~|zrypnnytklqy|~}uu~st}ywz~|zyqxyx~~yzwtwvyyw}~}~{{ryqnoxtkmpw{}vvtu~zwz}yxrx~yx~~y{xtvwyx~w~}~{{qyrooxtklqwy~vv~uv~xwz}ywry~zv}z{wtuwxy}w~|{{ryrpoxuklovy~xwvvyxy~yxrw~zv|z{xtvwwy~~x}~}~z}ryronxvkknvv{wxu~zxx~zxru}{v{zzzvuvxx}y{{~x~swroovxlkluu|w{t~{xx|zxst||wy{zzwuuxx~{|yyx}utunnrzokltt~~w~t{~xyz}ytt|~|yw}y{xutxx{|w~z~zzyrwoooytlkqvxzzwxxyyzvsxz|u|z{yvuvxz~~y|z}v|rvrnnuyolnwu~xu~|yy||xsu}|{xv}zzwuuxy}~x~y~xywrvnnpytmltwz{{yzyzyzurxy{t|yzywtvy|y{~z|u{suqmntypnoxvyw~}yy{}xrt{|yzs}z{yvty{x~z}|~yvyqtnmowwpotzy~x~xzzx~{vrvx|vv{zzyuvz}|x}y|~{~uxtrrmmqztqpyz{}y}~xzy~yttyx|s}z{ywux{x}~{z{|}ytwqsnmnwyrqu|{~yyyzx~|wrw~x{wt{zzyvvz~|y}x~{}z|tvtrqlns{vrs|z{{~}yzz~ystz|w|q~~yzzxuy|x~~~y{|z{{vtursmmpyxsry|}~|~|y{xztsxu}sxz{{yux|y{|{y~y|zxsvssnmovzusv}z{|y{x~|vrw}v{xs{{{zvv{{z{}x~~y}~yysussomntzvsu|y|}zzy|}wrv{xzzr}{{{wv{|z|x}~z|~xyttstpmnszwst|y}~{zz{~xrv{yx|q~{{{xuy~}y}x|~~z|~yyutttpmnszwst{x~|{z{~xrv{yx|q}{|{wty~}z}x{~~z|~yxuttupmnsywstzw~{|z|xrw|xy|q}{{{wty~}z|~x{}}z|~xwvtuvpmosxwstyv||z{xsv{yx}q~~{|{xty}~~z~x{}}z}~ywysopsyytt{x|{{z|w|~~{uv}|zyyxz{{~~{~y{{xrxxwssxsnnqvvsrxv~yvzzvtvs~~wqsyvuy|}|zsst{~}xnp~umfkidehorpnstu{~yyx~~zsptz}stuvvy{ww{zttsrt}~}uxzrqrw|{y{~||}xproq{wqlqv~vu{p{}zzysmpvwsonoxyrumggjoqonspv}|~~|{{~}xmkozyxwvw}yzuvy}urqrsw~~v{|tsuz~~|~~}{{{vwrt|xrmrv}vv{q|}zzysnqy|pnlor{|u{slkmrtrqurx|}}{|{~{}zuljq}|yxxwy{vv~~{}|vppqtw|wyvtuz~{x{zwvtr{{tnqu{zs|sw~zzyvorz}{nkkot|szwnmnsvvtwuv~{~~}{}zy{xunkr~yxxvz|wv}~}{{}vopqtyzxxxvw{ywz{wwus||uorv{zt|tw}zzyvps|{ymkkpvs|ypopuxxwyuxz}~{|{~y}zwtmmv}yxwwz{vw~}||z}sopquz}{v{xwx}~vyy}wytv}{tptw|xv{tz|{zytqv~{{skklqx~|vwqqtwyyyyt~~{|~}~|{|}{{~{ywrmp{|zxv}||yvz}z~{|ypporv~zv~xy|}xwzx{zxt{}xrrvz{vzww|{{{wrs{~|{xmkjns|w~uttxz{yztz}|}{~z||{~}z~|zxtnnv|zw|~}{wy|||{{{qqopsz{vzz{|zwyu|{u{~yssvz|wzxw||||ysrx}||ynljkpxzyvvy{|zzsz|{||~x|||~|~}|zunmt||~|y~~{xz|}~}~|}{zyyz}~|zsx|x~|{t|wnt}sokkryw~|}}{zonllov}~~}{vrzomrms{|}~}|y{zros{z~{usymfdcflsvsxv~}~|~}zz|wyrv~uoq~{pijppr}vz|{yx{~}~y~~zrolmr{~xotuy{yqmuvx{}|{{{|}~}ywy|uty||}|xnux~yskijostp|}r{z|~~v~{vzz~~|u}|{yvrnqy{tuwsy}t}z~}xwv|z{xz~~yuqopw~xv|vpnxy|xw}|}~|z|}zz{{{xv||vv|}{wvzuoy}|wqnotwvr~|s|{}}v~zuzz~|u~{xvusrnt}}xxzv|~u}z~}ywv}zy}xz}yurprxwtzupnz}~yw~~~}z{}yyyyzxu}}xw}{utwwpy|xrppuxxs|s~||~~u{~zvw}~}~~v}{wtsqsqtyx{x{xy{{zxvyywz{x}{vtqsywqwtroy|v~}{y}yxxxyzu|~zy~~zsrvxs{}ytqqvyyt{u}}}~~uz}yww}}x~zusqqtsvzz|x|xy{z~zwvz~ywy{x|zvustzuqwtsq||x~zy~|xwvvyzu~{{~~xrrwxt~}ytrsxzyu}u}}~{t{{xvx}}}~x~xtrprvsz|||xv|y{~ywv~}|xw{y{{ywusv}}qtwsrsy}~x{~zwvvw{wv~{}~}|urtyvv}ytsvzzvxxy~}~ux}xxt}{~z}zvsqqvtr}~y}yx|y~{xv{~~yxz~{wz|ywtuztsxsspy~|yz~~{xwvvzyr}}}~xtuyws~~}wuwzzwx{w}}vw}ywsyz|{||xusrvto~z}|~}}}wuv{xr{}u{zwsoloty~yz|{w|~zvwy|}~xy||zxwxy~{t|||wy}~~}}|}zqrt{~{zz|~uv}xwsw|wx|}xw|zx|~~~|{{|xyuwwwwupns{xy|xwy~}v|w|~{ytrstux}}|~ytv~yxpptutrrrsxxnw||yxrt|{~~wspor|{xxz|{sqyvtpowuswz}xrvvwz|zqnwxz}vt{zw{xuvz|vu~xt~}ywrnnqtw{~|uutsu|~uvwwtsrrtzrn||{}ywspy}zyrmmprw~}zrvyutorxssxz~~ssvwy}yytmqzy}y{zz|yvx||uxvv{yvrnorux~{~{rtstx~wxxwutrrv{pp|{|xvqq|~|}wplmptz~zrwyutprytswy~}rsvwzy|wrls||z{zz}ywx|}wxwv~zwuqnnrvz{}~zprsu{xxxxvtssv|so~{~|xvrq|~zunklqw}}~|svzvurqywsuw|}squx|{{uqmq{z|y}{xx{~zw{u|{wuqnnsx|~|~{}~ynpsw}xwxxvutsv|vo}|}~{xvrq}|xsmjlry~|tv{vvsqyxstv{|spvy~{~ytqmt||{z}|yy|{x{u|zvtqnnty~|}zz|vmptyxxxxwuttw}up{}~}{xurs}yvqkjnu{{ty{vwsszwrtw|zqrwzy|wsomy|z|~{z{~yzxw{wusompu{{zxy}|qnru{~yyyxwvtu{}pu~{}~|zwtpx|{wsnjkqw}ww|xwvqvztru{}tqux|||}yupls}|~{{~|x~{vzxutqnosw|{}{wx|~tnqtx~|{zzywvv{~sr{|}|{xupt~~|vvvwy|~zt}xvqtzutxqttwyzzz{|{|tkzy|~znv|nlkjmqsuy}~zz||x{}sxqppponpvyop{|{tn{|||{tz|rkffilouz|{sz}ystzwtxzp~zxwvuv|vt}xqkt~~tpor|tnlnpquxx}z~|~z|~|s~yvtqptyqlyyy|}{xtov~xtvw|tpopqtxy~|vwzv{zuv}}}{qy~{y|{s|}~~|xsmo~~smpqzytsttvyx{}{}{y~}}}~vv~zwtuzxnr~}yz{{zvqp}|}stv|{trqrtxyz~zv}xw}yuy~}|~wq}{{wt~|}|zvqluzpmrv~yuuuvxzx}{~|zy{}}{}}uz~{wux}xnu~yz{{yuqs|yruyztrrsuyz{~zxyx}zvz~~{y|wt}{{xt~{{{yuqox}xpnu|~yuuvvy{y}}{~{y}{|~~~~{x{~y}~{ywx~{qtzyzzxvsu|yru}ytrstvz{z~}|{{w|}xy||xvz|x~|{|t|{zyxvtry~}{xrow~yvuvwy|y{||}y|{z}}|{yvx~~|zxz}ts|yyyxwvx~}}}ytw~yussuw{|z}~~~|x|~yy{~~}|zvu{|~|||u{zxxwvut{~}~{zxtr{}yvvvxz}y{~{{|x|yz{{zywuy~}{y{}tu{yyyyxwz~~|}~}{xty}yuttvy}|{~}{x~~yz|{}}{zxuv}~~}~yv~~ywwwvuv~}|zywsu}ywwxy}|x~z}zx~|xzzzyxvv|}{{ytzzxxyyxw~{}|zvu}}xvuvx|}y~~~y{{y~~z|}|zyvty|~~}vz{xwwvut{~|}{zxury|yxxy|}y|~~{{|x|}xyzzyxvuy~~}|ux{xxyxwu{~{}~|{vrx{xwwx|}y}~~}zz|y}~z{||{ywtw|z}~wy|xxxvtqx|~}{yupu~{zz{}~x|~}||{}y||xzzzyxvtw|y}|vy|yyyxury{|}{vpt}zyxz}}y|}{|z}y}}y{|{{zwuvzwyxy}yxxwspu}}~}{voq}}|{{~y{~}z~{~y{}wyzyyxwtvzxyxx~zyyyuqu}z~|xqp{|zzz}~zz}{}z}z{~yz|{zzxuuxxvzx~zxxwupq}{~|yrnw}||~|y}{{{||y~~xwyyxxwutwzw~|x{|yyywsr{z|~}zuos}{{|~x}|{}z~z}{xz{zyyvtuywyyzyxxwspu~{~|{wqp|~}}{y~}z~z|{z~|wwyxwwvttxzy{y||xxywst~z|~~}|zupv|{{}~x~~||}z~z}zwyzyxwutuzz|zz~xwwwtqw~z~~}|zxsr~}}}{y~}|z{|z}|vvwwvvuttx}}}y{}xxxxuu~zz~|}{{zwsw|{{|y|~~~y|||~{vwxwvvutu{~{y}yvvwvtx~z}}}}|{{zwu~}|}|x}~~~{y~}z||vtvvutttty~z{~xwxyxw~~{x~{~~|{z{zvx~|{{}y{~~y{|{}yuvwvuuttw}{y|yvvxwvz~}y|||~|{{{{yw}|}|x|~zx}}z{ztsuuttttv{}z{}xwyzxy}~zx|z}~|{zz{zw|~|||~~y}}}x||||wtvvutttuxzz~xvwxww~}~}y~{}~}|{{||xy}}~yz}|~xy~|z{vrtutstttw~|{}zwxyyw|~}~x{y{}}{zz{{xw~}|}zy}zz~~|}ytuvuttttv{|z|{vvwwuz~}y{{|~~|{{{{yv~{y|~~}zx}}{{xssutttttuz}||}xxyxvz}yyz{}}|{{{zwt}~}~|y}~}{y~}}ytuvvuutstx}}|||wvwvtx~z{{|}|{{zwt}|y|}{{y}~||wstuutttstw|~}~~yxxvtx~yzz{~~}|{zyurz|z~}||z~~}ytuwvuutssv{~}}~xwwurv~{z||~}|{zvrx}z}~|z~}y}}|xssuutttssuz~~zxxvsv~|y{{~~}|{{yvqu~y~~{~z|~zutvvuuutstx|~}{wwuss{~z~~{~~}||{xssz|}~{{zz}zurtutttsrsv|~~yxwusz~z{~z|~}||{zytq{|{~}{}z~}wsuvvuutssuz~}ywvurv~~}z|{~~~}|{{xswz~~|}{}yz}ysrtutttsrtx~~}yxwuu|~~y{}y{}}|{{zyus~{{}~}||y}|vrtuutttssv{~}ywwusw~}~}y~{z}}}|||{yuxy}{~~|~yy~}yrqstsssssuy~~~yxxvv}~}~zy}xz{|{{{{{xv}|y}|~}x{}wrrttssttuw|~}}{wwwvx}}~y{{y{|||||}|yzy{{|}yw|}yrprsrrstuvz~}zxxxw}~}}zx}}xxz{{{{|}{x~{y~}{}wy|vqqsssstuvy~~||{xxwvy~}|~~xzzxz{{{|}~~{|y{z|~xv{}wqoqrrrstvx|~}zyyxx}|}yx~{wxzzz{|}}{zzy|{{wz{spqsssttuwz~|}zxxww|}|||x|}xx{{}}|xyywuonmoqv}|x|}|~}urtsnomnppv{|}xy~vqo{~yz~|~yz{|~||y|}wqz}wvxuuusvzz|y}z}}uwwwvxyoz||zmt|{ts{~|y~yty{vx{||v}u||vxy{uu}}}}||woywuukmmrmjqvtz}~rmp}|}~}xxtxwzxxqnou|trwzwvu~yuwzxvyx{}{spsx~yywvxzwyu{zrstvyz|}{{pxz|~}suyv||qtutwu}|{}z}|~{tvstuu}~||tsuqtxzxuwnw{z|styzyzyztv{}z}x~{ww|uxsv~x|ww{~~~x}{y|x}{rtqn{vx|{|z}w{~{xozwrvu{|zxwwxuzuu{}|zzuutnt~z}{|{rxqptqsx}}}{zw}~||{{{zukpns|}y~vwxr}wyx}zuyxwtm{z~~|}|tumx{zpu}{zx{zx|xwwzyvy}~~~y~}vmrz||x}wuxvxuz~wxzuxvu}}}}|{~|qsptx~}|x}z{~~vw|uyzwyrwuw}|~{~x|zyyw~yvxqwwyz~z~||wwzrpvt~~|usrpps|||tvrq~vsqv}zz|v~{z~|}||xxmuumpu}~y~}{}x}vxyxwwzxvv|w}}|{y}}zwztxy~z}{uz{~z~{y{uvtqyxrx~~vtttrr~z|{x~}wr~~~z{|}{|ysxx}|~}z}yvkt{|{|{{~||{xy{|xuzuptqw||}zztxuy|ywvr|uuw{u}|{vs{|~~yzruznlkw{{}vxw|wwyz}{|}{xv{~{yw{y|uqpsvu|y|~ywys|tspyysw~~{}~}}~wswut{}~|zw}|xyz{yxwwv{~}~~z~osy|zzx|w}xv~{yt}srluyrx~~z}{}z|~}}xtl}wx}~{u}~{|ts|{{}rwwz~vupljqpnx~w~zv{w}}}||xzxvt|z|~~|wxxxkry~zy|{vst~txr}~}~z}yt}}{{~rttnopz~~{x|uxuvxyqswzs|~zxz{vy|xmu{{}|{zyv}xs|}tpu}zw{|~~~|yurpxswyw}|wwtvz|{{wz{w|}{~{vqvvuu|{|t}~~{{~{|vwy{}ys{ppou}{}}zw{yunqsyz|~|~~tzy~}{~}s{{{tz~}~}vtnvvtprv}yxuvy~y}|{x}zsvy}v|z{{|y{tpw{~|~x{|tz~vuxrrpx~{}|y~}xqtpwrxwz}y}y~x{~{w||vpy}~~}|yvsxsouuw~wzw{~||{|x|spqxy{~y|zy{~|yxptuwz~wzxx{{vvsuz}|}~y{}vvrsrurty{u}yx~x~}ryyw|{yu~zrpvtpvy{{|{}|}~~~}{{zz~xrts~vv}~z}}}|}|ystsppyz~~|{{}|{|uuuvyz{|~|z}zywvvuusot{}|y~yw{z}}us|ty~~zzu}trtypvxyu|}~y|~{~~usqyzst}~|}{{}~}wrvtoszuzy}{}|~zxxxxzyx~|y~|{}}wyxussootw~}|wyx~~xt{}zu|~}~}{yv~rqywouwws{~z~z|{tuq|yqs~|~~~}~||wuyuottsux|}{~~xvxy{{xx}yy||~y|y{wtvtoptvz}|~xy}}}u|yv|xx|ywzyqt|tsxytp}}}||xwtv}tpu|x~~~~}}|uwxqquorq|~{}|{{|zyyw{xu}~~{|~y{yssyopsru|{}wz|{szyp{|z|}|~wysysp{}viywz}~zxyznq{}vqwzyw}~~y|vyxvwznkpyu|w|yzy}uruyy~{~{vyurttsqqwy|w~yz{{~}|twywtwy{|wz{xsx~pmsvvxzxxz{|zt~|wp||~{}|~yxxtrqhkzsrr|x}yzy|{ywxzw|~|{|vxxywpstryw|~vwyvvq{xu}~~|~{}w{{yy~srzvxnu|v~~xx}wyzxyt||uz~}xu{tw|svtsyltz}~|{~|v}xsyru~sz||}y}utxsrruvtt}{~~}xz|zsy|x||~y|~yzsnvmmmvuxy}}{{}~w{|yzwz~}|}}v~wrvtvrmvruy~x}yxty~u|{zp|~~}z}wxxzupxrmwrr~{z|{wzvxu{{|}|yyxux~xmxxuwtv~{zy{xx}sx|susz~|~|~y{xwsvzsrrxps|~}}{}xz{rvw~yyzyzyq}ynonurqz{~w~~|}wy{}uw|y{w~~{~v}}vvqwoprttv|{~wwzz{~xu}}~|wzz{uvuprqix{qty}y{z}xwxx{y~z~~zv}z{us~yvvqrxtxx|z|zr{qqq||y~z~y|z|yvuvwnsuwsx|}{}}vzx|uw{||y|ytxsqpxuqwyzwo~x}~}uw|xtwt~|~}~~z|}wxvzrprqrrxvz|{y|{|r~~~{xz{}wxysppksxmno{}{|~}x}z{x}xx}~|y}~|zs~|zytovsqsy~{}{{uurq||uz{||{}{z}xzwzrrsurqxvv}}zx|~||~}~~~}}{vs{}lqywzv~~~|tu~vyzx~wmpz~ywuqw{vt{z{|||}~{{{q{wx{ru|y|r~~z|zpyzvy~zqxy~x}w{vtytp}zyx|z}~y}yz~u}~zwxv}zx}rv}zzz}|{zmtsrntnvomnoziklyvw}||z~}{{{xtqz{x||y{yxwy{t{xzmw|zw{~oqqxnrmuyuzuuxuw~~t~zxxz{oz{nkoos~|zqtnkuz{wq}qllrlmhqxoyzwy{~x{~s|uqwtu||x|{{}{~vzupz~vvuvwqtot{r{xsu~zwu|q~ww|ov|pornnx}xouniuy}oztrrsmnhqxnzwrw}z|t}|t}tw{us~|w}y||~v|uqx|zyyyxstks|nxusu{xxz|{m~xw{qw}pprqnx}}~~wpwrhpvt~vstsmninyjtvru{|z~r}w{vuyzs}zyzw}|z}|}v|ypvy{~|{wttqo}mqtrtwyvy|py~}x}}wttrruou}~|~zptwkqr{y{~wwrpmmiypksovy}{ywv|wwywu|yv|w{z|us{rsw~~zwssmy|ktpsuyzzz}zo~|{~pyyqstwnz|~|||}tntpksvy~zzytromm|lmqovz~u}~|xxyx}ww||vzz}x{zstxpuzyyvtnz{ksotvy|}{xn~~~}{||~qxwrtuyo|||||}}{~slrnltx||{yuuqon}olqqxx}v{~|wxyv}|y}|v}yzy{|{{~pqwrv~{zyuqwkppsvx}}{~~n~||~z|w|ruyrus|t{}}}}~{wy~vjnonw{~}||uvqpn{wjqpxy}zw~~u|yyx|xyw~x}y~}zvw|pnusy|{zurwnnoswx}~{}}n~|{~z|vzrsxtvsv}}}~}}vsu}tinoqy|}}vyspp}wkpqyx}xx}}{u{y{yz|~xz|{}y{su||opst{|~yts{~mopvwx~~{xn}~z}|zvxquuuuvu~|~zrqv{njopu{|{yzrrvqlpuyxs~y}wvzz|x|{}yy|y{|zsrvunrsx~~}utxvlptyw{}|}py{y{|}vxurvuxty{|ytqt|timnsw}z{trvxlotzx|uy~{|zvzy|x~{}~~x|}|{|wvru~xmpqvzxty{notyvy||qv}zy}~xxvruuxu~zx||xuqt|vjmnrt}~~}|trx~ymouzx|~tz}|}{wyy|x{z~~~w{||{wuru~wmopuvxu||opvyvy|zqv~{x}xxxsuuxu}yv{|wvrszxkmmqr|}~wrz~|pnuzwz~ux{~|}xxy|y~w}y|~|~~~}~u~xrt{{ooott}|u|tnuzwx|{sr~~w{{xzuttwvy|s|~}~~y{{rrv|oklopv||swxnszxx}~yt}||~zwwz{{w~~x{{~z~|v|rrt|vnnquvyx~rqzzwz{wqx~{v}yzxtsuxw}xw|{~v|vqrxymllrp{~~{s|xow{xy|vu{||~ywwzz}w|w|{z}zv{qqt|tnnruwyyts|yxz{vqy}{u|yzxtrtxx~xy~{|uzupqvynllsq}}|t|{px|yy~|vu|y|}zwvz{~y~~w}{z}~{|szqprzwnnqwv{vxr|{xy|xrv|}vxzyyursxx~}y}{~|z~wvvnoryqmlsuz|}vy~sv}yy|}wt~~v|}zwuy{~|~~w}|}{|{x|rxpopxxpoqyw}v{s|{xx|wqu{|vv{yyvssxy~z}z~{y|vuumoryqnmuu||}vzux~yz||uu~}~u{}zwuy{|~~w~|}y}{}{wysvnoqywpptzz|yzv~yyy{uqxy|sxyyxurtx{|}{}~|}v{svqmouxpnpxt|v~u~|zyytx}~zw~||zvvz|~zxzx{}uyuusnotzsqqywyw}|yy|~xqu}{zyr}yywtrwy~|~z~{~~xyvusmorxsonut}~yx}~zz|{tv~|~t|}{wty{}}w{~x~|}|wwvttnoryupowv}|z~~zz{yrt||y|q~~yzxuqvx}{z~z~~xxwvtnorxsomts{}z{~~z{|{tw~{~t|}{wtx{~}}x|x}{}|~wwvvsnosytpovt|~}~zz{yru||x}q~z{yuqux~|{~z{~~}ywxvtnoqxtomsrx|}~z{{|uv}yt~}}|ytwz~|y|x{|{||yuvvuooqxvqnttwz|y|tszv~sxz{zxrsw}}z}~{~}|||||vwwvpnouwqnptq}{|y~xtzy~{u~}}{vux}||{{}w|~}z}~zzvuvwqnotxtppvr~}|{|ysv|{x}q~~z{zvqux{|}{~z}|}|{yvvxuooqwvqossv~{}y}vu{wxw}~|{uuy|{|{{x|}|y}}xyvtvwpnotxuqrws||{|ysw|zx}q}~{{zvruy{~}|~y|~~{|{xyutxtnnqvyzy~|}~yrut|w{v}yt}}}}xy~yw{uqs}y}}y}|~ywwsuyrmilzwrwtyoryiioxvnxms{wyyxsv{~|y|z}{|}~pfdswpmjzyv~tuu|uy~x{}s}|yswupu{wzttz|xsvxywz|wuwrqulghq}~{|xsnszwrswtjtjnwtvvupsx|~}wwz~~~|rtnddlzzu}x}}}|ysxyz~z~~w~urt{{{}uv~y|urtvvuyztrvmptjimvz}~xosw}}}wrxluxvxxuqu{z}~~{vw|~~~wzrlridhr{}~~~|vuyz||xy|}{rrw|}}t}|z{rttvuwzxquskuqjkq{}|~}rpux~sxrpywxywrsx||z~wvx{~xyxkrofgmy~}|}~|ytxw}z~z~}~~sqsx~x{}z}tstutvyyrtwktukkow~~~~uosswzvryxyyxsrw~{{x~xuvx~~~~~xyzltrhgku}~|}}ytwt|~{~}~~~tqqs|z||{|sssttvyxquxlvvmlnu|~vprp|{|yt{zzzytsw~{zw~}xuuv{~~~~yy}ntvjhjqy}~}|uws{|}}|wqpqv|~z~vstssuyysr{nszpmnsx~~~{prov}~w{|{|{wsu|}xx{z{vtuv}~|}}|u~tozpiimt}{}~|vwuu~}~|}|{sppq}~~{}|tussswywpxxlyxonpv{~uqro{}z||}}{vtx~z{v~~z~yuttz~}}{{}wv~nqynjkqx|}|uxtx~}~~{zzyroor~{{tusstxyvoyvlzwporx~~}yrxyvppty{xz{{~{z|~vx~}|{~}up{|flzuttwx{{|ww~~{{|xz~p{wswtrsr||~{y}~yzwurrw}|q~zyy|~yxxvrtotzytsvz}|{{}~|xxzvy~|{}vojpzymxsnmorvwxuq}~||y~~|~|usts}}wrpnvyzyvy|}z~~vtqnmsz|w~||~vv{~~wwpsxxsqtwzzxx{{|~}xstyx}~|}uqjhlv~x}ussuxyzur}}zz~}|v~~}~zrorvw~xus{z|ywz{{y~|tsomnu|{w}}~~~{qw}|xrwzxssvy{{xy}|}|wtuzx}{wrmiioy}~}wuvwz{zsw~|{~z~}}wy|~wqptu}}xtv}{|xx}|xz~wtqmmqx}|~~{~}~trz}~}uvzzvtuxz|zv}}|~~yutxw{~|~zsojhlt{~}|yxx{||ut}yy{~~|yv}~~yspsrv{vv{}yx|{xx~ytrnlouz{|{~}wqxzxw{{xuvxz|{v|~{~~zvuwuw}~~{uqkhjqvz~|{z|}}ut~|y~yz}}}yv}}~~zursos~yz}~zy}{wx~xusomotwxz}~~wrxx{{y|}yvvxy}|u|{~{wuwtv}~}~}|vsmijosvz}|}~wt~~|x{yx{||{v{|}~~|vrsop||}|{}|z|{yv~zutpmnruvw}}~~{rvwy}{}~|xwxy|vx||}yvwus}~z}{wtojilptv~~|t{|{x}wy{{ux||~~}ytrqmu}z|||{uy}wurnmosuu{~~|~}~urwwz~~}~{yxyz~}t}{}~{xvwsu{|~~z{urmiimqtx~|~y~x{|z~~zxyxw{{xuxwysowsjqsnnmklqvyy{xxwyzqqwy~wtzpouvmx}wy{{ywvwtx}}|{~|x~ytrnlntz~|v{|ztqoltsmortzwpvz|wx}|~{xxxvyuwz|xyuovvjnsnnmjjntwx}~{{~y{{}xv}xps}yxxkoyssvwusrsqs~z}|vu~x~}wvsqsyxtx{vortlknpvvmqwy}}vy|~|{{zvos}uxzmmsnllihmrvw{}|{}yz{~zw~~tqyrs}wtvwvtrsqt~y~{~zuvx~|wvsqsz{ww}|tvwomoqxvnsx{~|v{{}{{{z{spwx|{npsnnmjjotwx}}||}xyzxy{st|qy~vuwwvtsspx{y{z|xty|y~ywusru{zwz{uyvnnptyspvy|yw}}~}|{y}}}xqs}z~vntqnnljlquwy}~|}}ywy||w}wrxxu|vwxwvttqs~|x|~x|zvvx}{wvtrsx}}yx~xyzroptzvotx{}~{vz{~|{xy}~{tsz~|{pttpomklptuw}~~|{~{wy|}w{ysv|~wxxyxwutpq|{vyx{{ww~y}{xwursw{|zx|{{}tpqu{vpuxz|}|vx||~|{vx}}~|vu{{~|~zxyxuvxvvvz}zwvy{z~y~|~zzvpruvutspq|{uw~vxzvv}~|~zzxvw{y{rptoijovtnsxz|}}x{{spqy{wkmxzzzprspqqopsvww{{|{z|yy{}ro}{vtuy|~rzzwyzzyxtrz|tpsvqv}|}wtu{uqswyx~x{{sqsz|sruutux}}zwy}|skq}xvsnospu}wxquyvrqrstvztz{y|vlm{ulpsuxzz~x~|{tz~wspt|pjq{smprwytsxzrkiloqtxy{~roywwz||ztqzyrxwrty~}|tw|voktysqv}yv{}}xz~{vuutonot~~}~zxy}|yyojp|yrtuvxzz~x~|vtxqilx~xvwtu{z{|yqrvxzwrpqrtvzy|~xy}|xussw~y~usuy|st|yuy|vqw}wtw~|vy}}~~wsqonrwy~|~|tpsqmr{~wyz{|}}x{|~~|snuuopy|x||{||vwtuyy{zvstuvx{{{~}zy~|wwutuwzz}yy|~spz|x{~yrw~ytv}}uw|}ztpooptyy~{tossos|~xy{{|}}~xy}|zqnuvqrz}x|}}}ztvuwzz{zvttuvx{zz~}|{zzvvuuwy{z}z{}qp{}z|~xsy~xtw}|uw|~~xspoprwz{|yrnsspu|xz{{|}}v|~{xoqxurt}{y~~wtvvxyz{yvtuvwy{x}~{~z{yxwvvxz}}||z{}ynt}{z~|uu}|vty~yuz~|vrppquyy~~{voptqrzyz{{|}}}zw}}spwwrryy|~{ttvwxxzzxuuvwy{xz~}||y|zywwxz|{||}~}qr{|y{xt|}wtw}{uw}~~yurrrtxw{|xqptqpw{{}|}}}}{v~wrxxrqvy|~}utvwvvyzywwwwy|xz~}{x}}||zxyyz{}}}~}rs||wx}xv}}vtw}{tw}~}~~{xutsuxvz}yrptqnt{|~~~~}}{v~}~yuzzsqu}y{~wtvvttwzyxxxxz|yx~|}x{~~~|zzzz~z~~us{}xvzzw}xuv|}vu{~{|~}zwvuvxww}{upssnq{{|~~~}~v|}}}vy|vrsz{yztuxvstxyyxxxy|{w}~}~zx~~|{{z{|~~zsv}|wx|~xz|vux}ztw~|z|}{ywvvxyv{}yrptrou}}z~~}{w}~zw|{uru|zzwsvyusvyzyyyy{}yw~{}~yz~}}|{{{}|xrw~|wy}}x||vuy~ztx~~{z||zxwvvyyv|~|xqptsqw~}{~~~{w~~~~yw|{usv}{{wrvzwtvyzyyyy{}yw~y{~zz~}~}||||~}xru}~yz~}x{|wux}|uw~}{}z||zxvuvz{x|~{xrosusw|~~~x}~{yuz|wtv|zxqt{{wwzzzyyy{}{w}yx}|z~{}|{|}}~yrt||{~~xz}xuw}}wv}}y~{{{zwvuvz|y|~{xsnswux|}~y}}zyu{}xvw}{xqt}|xxz{zyyy{~{w~~xw||{}{}|||}~wru}}|}x|}wvx}}vw~zy}z{{ywvvx||y~|zwqotwv{~|~~}y{z}ww}|wvy~{~upx~{wy{{zyyz}~yx{vx}{}{|}}|}~}usx}}{yzww{~zuz~w{zyzzxwvw{}zz{yuoqwwx~|}{|}z}yv|~yvw|{~yqt}}wwz{{zz{}{w}vv{|{||~~}~~wtv}}||y|xwy~}vxwyzxyzyxwx{~{x|{wqpvww}~{}|{~~}z|~zw}zvw{|~zrt}}vvxz{{{|~{x}wv{{z~~{|~~~xuw}|z}|z}xxz~}vxwyywxyyyyy|~zx~|xrpvww{}z}}|~~~{||~zx}{wwz|}|ss|}vtwyz{||}x~}wuy{z}}z{~zww|}z{~|zzxz~~xw~xvzvwxyyyy|~|w~}ztptxwz|z{|~~~~{{~||~|x|}xwy~~{wqyytuxy{||~y{zuv{{{}{y}~xwy|z}~{}}yy|}wy}uy~wuwxyyy{~zy~|ysqvyy{{y}~}~~~|zz||~}yx~|xxz}|~tr|xtvxz||}~y}}xsv{{||yy}|xwy|{~}{~|yy||wz{ty}vuwxyyy{~yz~|xrqwz{}{z~~~~~~{zz~||~}yy~|xx{~}}tr|zvwy{|}}~y}~~|xstz||}yy}}xwy~|~~{~}zy|~xy{svxuvxxyy{~{y~|ytqu{|~}{}~~~~~{yy{~|~~zx}}yy{}vqy~xxy{|}}y{~}|xsry|}}zy|}yxx~~|}}zy{yx|suzvvxxyy{~|z~|zuqu|~~{}~~~}}zyx{~}~}zx}~zy{~~vrzyxz{|}~y{}~|{wrry}~}yy||yxy|}zz|yy~yrvyvvwxyz|{{}{ytqw~}{}~~}}~}yxx|}}}|xy|zz|~|tt~}yy{|}}}y~~~}{zuqt{~~{xz~zyy{}||zz}}y}|urz|wuvxyz{~}z}{wrt{~{}~}~~}}~zxxy~|||yx~~zz{wszzxz{}~z|~}}{zvqqx}~|wx||zzz}|}{{}z{~~vqw}wtuwxz{~~z}~|xssz~z|}~~}}~zxxx}~{{{xx}~{z{~xszywx{}~z}~}}{zvqqw}}zvw|}{zz~~~||}{|~{|}vqw|ustvxy{~y}}ytsz}y{}~~~}~zyxx|~zzyww}{z{~}|z|||~|vy}}zvz}}~vsx{|yxvmlsz|zyzxx~~|~}zsw|xxz|~xuykekswvssx}}}~~~~}{y}}z|~~~}{v}pkpspmmpsvy|{z~{{}{{|vw~}|ytvystusqqrru{}{zyz}~{|}}}|{{~z~|{|zxvw}yrsvuqnquyz|}~}|}}z{|{yxwwz}yvv{}z{ysooqsuwz|{xy~}|~}yvw{|~|}|wsru|}yvuttw{|zyyy{~}z{}zzzyz}}~}~{urux{{}~|wtuxz{|}}}||}zz{zxwvx|~zwuux~~~yuttvwy{}|yy~~{}~|xvw{}}}~|xtrrv~|ywvvx||{zyz{}~{{}}zyyyz~~~ytqty~||~}yvvyz||}}}||~~zyzywvvx}|wttuy~~{wvvwxy{||zy|~{{|{xuv{~~}~|zvsqsx~{yxwy|}{zzz{}~|z|{yxxx{~{wrquz}}~{xwy{||}}|||}}zyyxwvvy~~}yussv{|ywwxyz{}|zz|~{{{zwuw{|zwtrqtz~{zyxz||{zzz|}}{z{zxwxy|}{yuqrv|~~{yy{||}}}|||~|yxxwvvw{}{wtstw||zyyyz{|}|z{}|zzzyvvy|}zxvsqrv}~|zyz{}|{{{{|}}{zzyxwxy}}{yvsqtx}~{z{||}}}}||}~}zyyxwvvx|}{xussuy~|zzzz{|}|{{|~}zzzywvwz}{ywtrrtx~|zz{}||{{{|}}|{zzxxwxz~~|zxtrsuy~}{||}}}}}||}~~{yyyxwvwz~~|yvtssvz~|{{{||}|{{}~}{zzzxwwy{}|zxvsrsv{}|{|}}||{||}}|{{zyxxxy{}{yusrtvz~}|}}}}}}||}~~{zzyyxwwy{~|zwutstw|~}|||}}}||}~~{{{zywwxy{~}{ywtsrtw|~}}~}}||||}~}{{zyyxxxy|}|ywtsstw|~~~}}}}}|}~~|zzzyxwwxz|}{xvtssuy~~}}}}}}||}~~|{{zyxwwxz|~{zxusrsuy~~~~~}}|||}}}|{zzyxxxxz}~|zxusssuy~~~~~}}}}~~|zzyyxwwxz}}{ywutssvz~~~~~}|}~~~|{zzywwxyz}|zxvtsrsv{~~~~}}}}}}}|{zyyxwxy{~|zxvtrstw{~~~~}}}~}{zyyxwwwy{~|zxvtsstw|~~~~}}}}~}|zzyxwwxy|~{xwusrrtx|~~}}}}}}|zyyxxwxz|~{xwusrsux}~~}}}}}|zyxxwwxz|}{xvtsrsuy~~~}}}}}}|zyxxwwxz}}zwutsrsuy~~}}}}}}{zyxxwwxz}~|zwutrrsvz~~~}}}}}{zxxwwwxz}~}|ywusrstvz~~}}}}}{zyxwwwy{~~}{xvtsrrtv{~~}}}}|{zyxwwxy{~~}{xvtsrrtw{~~}}}}|{yxxwwxy{~~~|zxvtsrsux|~~}}}}|{yyxwwxz|~}|zwutsrsux|~~}}}}|zyxxwwxz|~}{ywusrrsuy}~}}}}|zyxxwwxz|~~}{yvussrsvy}~~}}}}{zyxxwxyz}~}|zxvtsrrsvy~~~}}}|{zyxxxxy{}~}|zxvtsrstvz~~~}}|{zyxxwxy{}~}{ywutsrstw{~~~}}|{zyxxxxy{~~}}{ywutsrstw{~~}}|zyyxxxxy{~~}|zxvutsrsux{~~~}{zzyxxxxz|~~}|zxvussssux|~~~}{zyyxxxxz|~~~}{ywvtsrrsux|~~}|{zyyxxxyz|~}|{ywvtsrstvy}~~}|{zyyxxxyz|~}|zywutssstvy}~~}|{zyyxxxy{}~~}|zxvutsrstvz~~~}|{zyyxxxy{}~}|{zxvutsrstwz~~~}|{zyxxxxy{}~}|{yxvussrsuw{~~}|{zyxxxxz{~~~}|{ywutssrsux{~~}|{zyxxxyz|~~}|{zywutsrssux|~~}|zzyxxxyz|~~}|{zxwutssstvx|~}}|zyyxxxyz|~}|{zxvutsrstvy|~}}|zyyxxxyz|~~}|{yxvussrstvy}~}}|zyyxxxy{}~}|{zywvtssrstwz}~}|{zyyxxxy{}~}|{zywvtsrrsuwz~~}|{zyyxxyz{}~}|{zxwutsrrsuwz~~}|{zyxxxyz{~~}|{zyxwutsrssux{~}|{zyxxxyz|~~}|{zyxvussrstux{~}|{zyxxxyz|~~}|{zywvussrstvx|~~}|{zyxxxyz|~~}|{zxwvtsrrstvy|~}|{zyxxyy{|~}|zyxwutsrrstvy}~~}|{zyxxyy{}~}|{zyxwutsrrstwz}~~}|{zyxxyz{}~}|{zyxvutsrssuwz}~~}|{zyyxyz{}~}|{zywvutsrssuwz~~~||z{~|ywvurqpnomknlkjnjjnpquz~}~}}{}yssqpoqrsxz{}yuqvtwz{yxwsqqnsx}uynwy|}z{}wsrpnnpqt{~~~|yqppvvvpjlkkkmnpvz}ztsx{urpnprsx}wtu}zwomnmmmpqt{~}xv~{xtxvursrprsvyy~vloy{|vtqnmotv|~||zz{wtruwx}{xz|}}yxuumpwsuuwzuw|vussvuqjkorsjsohmmokgdjoszzv|vyx~wwv~{{~z}qqnqslzwz}vpu{}}~vuzz|}vx|}|nlmhsp{}}|vurllupiflknrnsinnkljecqsv|{{wwzpytz|y|uptmnsr|u|xr~~wyvzz~}v}}}|vmqqios}~yxzukhvpiltslsrrlmgeehedssuy~x}x|psty|{|~~{ttytnrw|x|{}zzt}v|wx|~{}{{~x}~|~}}|vlqunms~yv{woeutknszoqurnkifdfgdrvv|xzzwotu~y~z~wuqxwqrwz|~y}w}ywyv{y{z{{}~|}xwkpuqns~}yryupdtxmnrzprvsojkidgheqxz~xzzwpsuz~vzt}tpvvrszzz~w~zyyw|yzy{}||}~{wsjsvspv~}vqwslfyvnnswmuwrnjolgjhhsx~x}y}tssxz}{vzwsprusrwz~}~wxvz~~|w{|}y}w|}lovvrs|{|ruwrhp}qopwqpztqkmpiikgmv|~|~{{{sut{~|r|vrztpsrpt}{~y~|}zy~|wx}}~zz}~zyonvwusz|~vtzukn~upotrn|wslotlkmhkt|}}|~|{}uwt~|~|~s~}wsxuprqor~z~{~}~zz~~~vx~}}|~}yz}~{{npvwutz}~yv{wmp~wqouro~xumpwnmnikr||}z~}{~vyt}}}|}|u|~wtvwoqoop}~z|}}~~|y~~}wv}~}{~w{}~~||ysnvwwtz}~~x{{qo|{sorum||wrnysnqlkoy|{xzwywyz}}{vyvqpqopmx{z{|y|}}ty}{{}xxx|~||{x{}nsvxvw}~~~~x~zos~yrovrqzxotzprrmlr~|y}~||w{v|~{{{xy}xtrzppopn}{{}~~z|{{uz}z}}}uw~xz|{|yyw|{nuvywz~~yzpu~zsovrr{zpu|qssoms}y}~}|x{w{~|{|}xx{xsqzqpoqn}|}}~}}{|{}vz|z|~~}vu}xzz|xwwvy|ptwyy{~z}}rs|}vouup~|ur~utvroq~~x~{}xzzx{}~~z}ywyxuoz~sqosoy~~|~||{~~vz{|z||vtyy{|~z|}vuuuw}rtwy{{~||tsz~xpuvq}wrwvwtqq~y}{|xz{w|}~~x|xxxxunz~tqotpz||{}|~}wzz|{{zusy}xy|}}}x{|tsuty{rvw{{~|~tu{wqwuu~vvvxxuqtz}zx{zx||~zyxywwxrp}{tpqtq}|y~}|z{yz|x~|zvss{~zyv~|~{{v}wqssu|vswx|y~|yux~suyt||s~{wywtp||~||yy}x}|~|}u{wzvxuox}wrouq{~{z|~|~}~yzzz{zvtrx~{y~v{~z~y|uz{qrss{yswv|y~}{wy~wvzv|~t|}xzxvpz~}|{yx}y{||}u|wzvxvpw}xrotpy~}|z||~}~yzzz{zusry~zz~u{~z}y|tz|qsst{xsvv{w}~|xzyx{x}v|z{yxpy|}{yx}{y}|{u~~w{uwvpv|ztnspw~|{{||~yzy{y}{usqu~zz{ww~z}x{wutrtsyytuuyx}{yz}y|z|zx|}zzst|~}yyz~w~}{}yww{xuxrsz|xpqrq~~~~z~}}}{zy}x}~~xrrp{|yz|~ty}|z|v{rz|qtsu{wuuvzw}{zz|{|zx|}}{yqz|}{zx|}w~|z~}t{v|vuvqu{|wpsqu~}{}}}{{y~~x~}}wqqq|{xz{{tx|zzzvzpz|qtsvzvuuw{y{{{~}|{z{~}|zrz|~~zzx|}w|y}|~tyu|uuvquz|xqsrv}{~~|{{z~x}}~~~xpqo{{w{x}svz{y{tzqv~stsuzxwvw|z}|z{~~~|}z}}tv~~{yyyv}}y{|}wtwzysvqsx||tttt~~~z}||{z}y|~{~~}yppow|v{w~ttx~|x{sysrutstzxxww|{~|z{~~|z~}~vw~zyyyv}}xz~{|vtvzyruqtx}}uuuv~~~{~}{|{z}y|~~z|~~|wopnx{v{v{ssw~zyzsyqt~tusvzyyvy|{{{{~~z~~}uz~}xyw{~v{w{}z{rx~v|vtsquy{uvu{||~}}}~tpor~xz{xupqwzuxssvmxvkgfsyunkqty~}~}z~}zzrox}~~|{~y{xv{sprx{|{~|}xy}z{vsy||wyw|~smry{uwv{{y||z~z|}|z~}}wwv}zztrw|y{yu|sy}romv{|zy~wty}~}|~}xxvvw}~wrpnwqty|vw{y{wt{|~xyw{~|{}|sw|v{|y|xrqqusrnnyrwyswvx~uqqq~{zxztrx|yyuqujosjhgnsxzx{yz}z}xwrxsr{~vtvstojurptmmjmqw{uutu|~w|{qosu}{vvxvzzwxy~}x{{y}|yvxtquyz|yzyx}}wwtuu|{}~wzz}xuzwyz~z{x{}t|}xtyyyztz{}zxy}xyz}w~}v|{||zy{}yxqt{sywruswz{ypmlp{wy{vwrovywxspsjnrljipsyzy}|~}{xwtzvs~xvxvvsmuvrvqnknqv|xwtu}}{~~ww}tprt{{|uywy{xxx|}~w}}{y~|~|}ty|vrrwx|{yzx{|z~}uuuwx~{x{~x}sxxzz~}zzx|w}vvyx{yu}}~{y|~~wyy}ww}|~|{z|}yyru}t{ztvswy{yqnmp{wx|v|~wqmtwvwroqkkspmntw|~|}}{|vvsx{t|vywwvqszvwwroprt}|ywv|}{z}{zrzyqtv{|~vxxy|yyx{}~zz|z|y~}xzuryxtswx|}z{y{}yuvv}{y~|xy|vzwr~}|}{zx}w~yuyxz{wzz|~x~vxx{zy}}}|z|~z{uu~w{|vvtwx{zromq{xw|wz}}vqlsuuvqnplkstprxy~~}zurx{}{x|zsu{{~yxzwsqqxuq}}z||{yrzyxzx~}ysw|~||y}}}}z}}u{u{}|swxuq||||}xvt{y}}y{~zvprz{|}||~}|zzxy~~{~|~}v|||xx{{|{}rnks~}ttoikmpyyxuu{z}{u|~}vx}}}~{~{z||~zwrux{|zx{{}vtrw~z{zz~}ysry{}}~ywxvtpsx|~yz|xyxmrtxwrsu||}{}wwv|{|uprqw{|yyxz|{|~~}{{xrplpsw~~~yxuxslifmrnosonsvrnhhnptusrvvu}}xzyy}{{yz}|{{||z~~yrkmlrwz}{wv{zxspux{tvxnsxwwqqvz~|x~z{y{~}}zyy{{vuqtvw|{~}}|xusw~}{{~|xtz|zvvu{~}|}}v{pnquvrrrw|}|y}|ywvy~{|yqqqrwx{||z||}|{z{uromqrw{{}{y{uokjpspqupnsuqnhinputsrvvt}}xzxz}{|y{|}{zy|}{|}xrkllqvy~}z~~}xtxy}wu{pqwwxrqtw~z{y|{yz}~z}wwxzvurruv{{{~|~{xtv}}~}{~{uw{~~~{uutx~~zyyoorwusssy|~zz|~~}xwv{}|}zupqquy{~~}{}~}~xtuvqpnmrtz~~|z}wrnmsutrwtntvroijnpuuuswzt|z{yz~|}z||}~~yzy{}{}{}zvrlmnsz}~z}}vxz}|t{vpvwyursu|{w~~|zx|}z}wstxttsrvw|}}|~zwv|}{}wvz|}zzzytuux~y{uopvwttrw{||z~}~~yxvz~~{~{~{yvqrrv|}~{||}y|}~wrrtppnosv}xzzqqy~}|tstvywwsvxowzvwux{{|y|x~wvuusoomqux}{~wvz|~~zzusmklmuy}ywyz{{yvuyz{uttv||~zw}wtyyxuxz~}xvzzxywz~}vsxvvxwxyy~|xuxy{}vvuvqqqry}~vuzwxvontvttruyz~zx{~|}~~|wsr|v{unqsuqpqsz|~~zx|{|~z|}vprtrtrsvy~|{|u{zxuprsw|{|zz~yuopw~uotursprvw{ywy~|{}~~zyvz~}zx{xwtpqpty||y|{tuwyussu{}{z}}~xwv|z}zxsnppuxxyv~|v}{~~}{~w~vuuvssrqvy~~~xw{~}upuwuvsuyz}{}}~}~xuryzxxzurrqwy||w{}vvz|xwz|zx|xprustrrtuz{{|~z||zxrssw||~{{}zxrosyuuyuurquuzyvwz~w}|{}ywuv{|~y}{yywvqqppuw{{z||xxzyuvtx}~}z}}~yvu|}|ztpppuwxxu~zu}~~zy|{}{z}}|v}}vtuvstsrvx}}|~xy{zswywxuwy{}|}}|}}}}zvrx}{z|xutrvy|}xy}xsw{xvxy~{vxzrosrttsttx{z|~|vzqnqpplllmuwxxw|x{xtttqspptw}}z{|x}}zzvwwz~{z{|}{xz|||v|uusttsww}w|}}||wwzxvwuz~}~|ww}vsyy{{vspw{~}}yxxy{tngku~xsx}x{~zy{~yyxpswwywwz{~~|~}~{{y{zyxx}~|wuyz}||xrpt}~yusnmjmqoru{~{wz{{zw{}|z|{{xuuty|}~{{~~z{yyvu{~{yzwyywz|{|~vy~~}vuxy~}zwroopnpoopjksy|vu{||vsutrsptx|{z}{sqyzy~}wt{wsxyys~w}{~~}yu}~|}zzwxtmptqlnqrvusqnqu{{rvzvx{|~{suvuvrrqtyx{~|wtv{y}}uqqlkiirz|xww{yyzz|yvprxqqttxvtpnrty~y{~~}zwuvwvz{{zy~}{{~zyuqieicgpu||||{|xvw}ts{{ytstqrpswy}yz|yyxttonqtz|{|}~~|}~}~|zw}{urmlijot~{wtrljfglnpnowsnrxxtvvxyw{|~|z}|wywwxsttw|{~|wuv~}y||~}~|usuvz{}wz}ztqpjehffgdhkpsprtvnmwvuwx~{y|wxwyxvspsssv|~|}{~}zvnkhilmvzyusmgkeaddhgghjpqqrt}yu~{{|yzy{~zwvrpkjmpvz|vtoponoouz}zyttqopotsnrqmiijhhghoqutu||ww~||}~}~yx}|tuvx|z{{||}~yrrprtu{zxzxwwuwvsopqjjlknlmqtzywy|{rt|yyx{~}|~||~zyutwvy{}wvsmljmoou}}~||~~{ztnrnjklpooopuvuuv~zty}~yy|{~~}zzwwsqtuyz~~w~~ytppplkghklpu||{yzy|~yxzxrnpmlkhlorsrw}|y}}~yzxzywxw|}~~|wvuxvuuv}vrpllhfikqsy~|}{xrsskjjjnlotyvx}~}{|~{xy|z{xx|||~~zw{}~}z}trnoolmmrwx~{zwortpqrvwvvtyzz{z{}~}~}~||yyxuxy}~}{~}~~|{|x~zupoqopmnrtx|~|{}|~|ttsqrorvx{y||y|zxxyvwux{z|y{}||~|yx{{zzy~yrqmmkilntwzyx|urrrussvy}}~xx|y|wqqrpplmpqvy~xywwwuy|}yzqnkllkmovz~}uxwtqrvuvuv{{}}}~|zz~{qnmmporw{z~xwwy{y{}|~tvyupmqrstswy|xuvtussxz~|}x}~~|||y|{{}{ztz{yxz}{{{}~~zz}zxvz|||{~~vtqrpnqsy{||yutuwuvwz~{{z||qpuvvvz|~~~|vxxwvsuvvy{}z{y{yx|~~~~|uqqsqrruyz~|y{xtpstsrostttsy|wvz}usqrpnpquvx~~}yxy|yz|~{y~|vprrrrpstvz~~}~vpommjjnqvvy}xxztppsqrrtwvy{}z}|xtxzz{z~uspqpnqrvx{xtytonpttvy}{w{~}wsxzvpqsssprttwx{||~}|~zxrpsrrsswwy|~y{~|wx{{{xz}|}|~|z||zyyy}}|zvsqqroqqsvv{}|~~yy}|toqqqonpprtx~ywvvrsvwzyz}~~vw}|}|}}}vxzuprtrtsuwvxy}~y|~~~|}||rnkkjgjjmorz}xtsvttuw{zzzz~ystx||~{tvzyqoqqrpqssvx}|{|}z|}stytmhikkmoswx}~z||wruuuuswxyzz~{z}}zzz{~}zy}tqprrquvy{}{z~~~|{~yppvwpnqrttuxx{~|vtsuqruwzy{}xw|}~}}}|xw~yttwvxxz}|~~y{}}}{~|~~{{{{{yspqqorsvy{|}{vsvvuww||{|z}|uru{|}yw{xrttvvuxxz|zxwyvvyz}|~}zzv|{unmporsuyy|~{z}{ttvvvsvxxzy}z~~zytqssruvy{||~|wtwwwyz}}tquxpkmmpopttxz|vtturtwz}}}{xw~}wqsttww{|||yz~zvyz}}}~~wu|ypljmlmqrvx||}wx{z{{~}xv{{uu|{srstutwwwz{~}|}|~wpuxtljmmpqtww{}zz{{|y{}}~~{~}}}}~x~xropppssuwx|~~|~~|xzyqosvnklmonqsswx~{xyxvyz}~~z{}|tw{yqmppqrsvvxy{|{||}{}~}~}|{|~}zsxzsmikkknosuw}{x{z{{}~yuw}~yspvyqnoprqtuuyz{xzyxz{|{}~~~~xprvunjmmoqswwz||z{{|y{||~}zz}}~zzzz}||vzytprsswwz{|{x{z{|}|vv}~zrnrxrnoorqsutwx}}xyyvyy|}|~zwx|~}~|~xx}ztvvwyz}||~~~}}~yzz|zz}|~~~z}~}{{{~~xv}ztnlnmoqsvvz}yyzyzy|~}~||}{vrw|~xw~wvuvwvywxy{~ywywwyz~}~{z|zstxxpmnnqrtwvyz|~|yyyzwyzz}|~}{}~wrrrruuwxx|~~}wyyy{{zv|}vpovuonmoopssuwyywyxy{}}}wx||trssuvxzy{{}}}{~|zz{|z}~~~{}||{{~}|tuyvqkklloqsuv{}|}|xqpyw{||}}||ztsx|ywv{|}~~vw}yxy{upv|uv|{sywqrswxux~|xvztuv{}}}~}qstsuv}||~z|}~}xty{~x}}tux~}~{}}{}zss|x{|~|~|||rotlopw}wsvw}u{vt|{|xu|w}w~y|z{w|u~uytyr}}|~|x~~{t~{{}||yqios}~zuwjrolqpkr{|~ztyuwqpurr}{vu|y~w|~{|xtw~|}~zxz~|{vrzsnryy~v~wxwnnlomovtqx}vqyxovszv}|~~z~}y|}z~z~wuzxz|wwzz~z}~{{}vtrwsy{zxutvunmvnntxpsvxu{xrw{zu~|~wz}ws}~~w{qqvvr~zywzwy~|yzump{wzx{smmtlmqty||tw}ypxurqwux}|x{~}w{}}yv}z|{~|{tw}{zy|zznmost{}|{ynsusluvps{uz{ztqvqmv~{y{{z}}u{{|{{w|ztr~~z}~x}~}|ywrwomt}w|vpptorxxprwtns}urutszz{zxx|z|}{~wqryqqv|}z{~xw~}xz|tpnntu~|{y|wrlusmt{uuwqvvrquxvy{su}~}vz{u{~}}}~~tsy{y{txz||}|vprvonxy}|xrqrlpuust|||vqy~yvxuw~}}~yy|~xzx|}vzurryv|yxuw~}}~~ztymqvwuy{~wt{|xyyqs{||tsr{sz{yxy}|xy{}xz}wuzvskvvt|xt|z~{nsrjiv}y|xnv~~xyxy~vzwrswoqw{vyy}~~|~|zy{szx{ztyvprrv~~|{}~}}}|vposmv|w~~oxyz|w{{zt~v{ysrttvwq{z}y{~}y|y|v|}tvy}}mxv}|~z|}|~|||vstz}rqu|twq~vuptxuwuyyuzyzxrwy{~u{|yxy}}tuwy{zx|z}wpvxyzz~~~|z~}y}z~}pqqrttv~xzrp}s{wz}}ywttxorz|yw{{z~|}{{xw{y~~x~|y}}xuyz}~{z~}{~{}|yurlrutz{r}{mstrvw|}v}ys{uy|swyz~}z}yv{zyw|}~{vzys{~|zx~y{~|{zwzyy~upsuy{y}}q{{vzutzyx|~z}uq{w{~sx}}{~y~xyzw}}w{ysxxqzzx~||{~|vwxvxrpstvxv}s{xs}|{|}|~~z{ts~wxzsy||}|}~}|~{{zv||w~~vxzv}}v~}~}|{~{y{zxxwqpsttrr}~}zuyuvwy{u|{|{wuwz~|||}{}z}|{z}y}~~wuytpxsv}y~~|{y|tqstsuxtt|yvxs{y~}zyy|vy{y|sy}yu|}yxzvz|wv~x}{vwssvqz|~z~|~~{~xz|w{{uustrsquxv{|y{x}xyxzzwv|zv~{~y{{yx{}vw}{y}zvyqn}}uwy~}z}~|y}ytwyvw}vpvxwwlq~~y|}z~~|wvyusxzsvzwz~z}}xw}{|svsvxtx}~~{}{vz~|}{|{tu|z|vzzwusvqoy}}xz|x}|vt|tvy|}{|y|~yyv}~zy|tsuos}utwu~z}~~}}}xwzrvsy|vx~{zwlp{{~~}}}~u{yutxurwzvuu{||yxvzwswy|}||z~~z|zyzx{~y|}z|xutqusmuy}|}{~z}xutvw~||zz~y}~w{w~{zzrr{tmqr|~|{~|~|~}wvysqgpxxz~wynvyvyv|{xy}yz{rs~twu||y{xkz|{v}tvyr{nv{{}u|yz~wx~~{wvxxwz|{rsyptyus}pu{wuz~|||}~{o}}|y{z~~{{woyuxyuu|||~|vwtuz}}z}{vtvv{svq}x{u|rp{{v{}~{}~wwtrx|xuq}z}yv}z|wyz}zuwwtvy|{v}|}}}u{~ux~st{vwu{v{{sqvytl}~zut|{vwyw}zr{v~}}zus{ykqwvz~~|~~|{|v}{{~tvss{xur|{tpvxvv}rx}}y}zrsw{~vx~~{{}|rwvwt}{}vxzyuww}}|{}|zy|}{psvvpyxx{xuvrrpynu|}w{yz|}t~|z}}|}yryww{|u{zxyvruxst|~x}~|{|~|{~t~|trxrussv{zsv|tt{wzy{yv}{x|z{{yxw~v{|zx{|xw}yysux~}~~{}|x}wz{~{vv~syyrr}zy~srotvu}{x|v|xsty|z}~}zur|y|z~z~yyzsu}yz||zwz||y}~umz{{~{zy|vy~wqqtkqzyx}}|~|}||rw}|z~vzvx~{}x{|rvyxssv}xy~w|}~z}zwzxvxvqxxptzyqrrwuutt|~~~}yw~yw|{w{{vyzztz{yvpw{z~}yzy}{|u~~{|yzzsqyztwqy|{zptwsqsvv|{x}|z}z~{wzry|}}w|xy|vvyp||~{|}}~|z}|z||xwpvzyzvz}}uwxrrujqrtptyz}{}}|z}~|}}xwz}v{}{~xx|yspxtpt}||{y~~x{}~~~z|}ttuuruuq}{xputnkywqrtv||~wqu~~~z|~u~zxwzvx{}vytpktfnttqq|wrkwxmkrwwx}~w|}swsv~{~zzyxx|}|}zrzzy}|n{~umxz}~vzvqp~wu{|||~|{uqy|v{}rmuvou{{lzoy|txx||{~t{y~xzuyt}}{sx|x}yl{}~zy~~}{~~~y|}|}~vvu}~}~v|xputqnx|tot~pjlnhivsovwy{|fmyspt|~~|yx{|yx}{~tv{|yzqxxovvqq}w~tvzur~w{y{{y}}}~}trwuzw{{}zzzsyyt}|ytxzmegugelnfgtmjo}svw{gisnhjxv{{~||~~~~|}w~tiqpovrq|yz|{yxx|{}}~}||~~|~~~{~vvmqu{|uzxp|{yuqlszgjnrhsytlv{qtzzkx~ztyzxzuz{{}zz}|~{~tw}{}{{|xolpmgr|}py~yvy~{x|~}}~~y}|}ytlrrzx~zstwqpp~tmk|pgjmedqljk|wyy|liprlnuvxxw}y~}}y~~}sxupwxtz}}~{zw|zvx||{~}~|y}~|x|{|z|z{rvwz|wwyxrvqo{tuwpkunkgwvw{usz~xxzzyyuy{||~}~v}~|~z}||z~zw{}x|ulmlgn}y|~y||{quyv{~x||zxvyrux|~}zw~utn|uql{tjjmjhxroissrtwomrzvxwzwvp|ww|~{{|yuytx~uwwsu~~}|{x{{}{~||~zz}y|}y|wyy{w|uss{{zx|{rzztp|ywvujtsnkpyyy~wrr}}~yz~putyv|}~~{{{|}z}w{|qpw~qrpsl}}|{{~yqx|t{~||~x}uuvrtxtsx{|~{~{puoifvvpnromytpmpvsuvpnq|z{x|wyr~}w|||{{~~|zrzyttuusvty~zw}||{|{~~}z|~}~{}}{{xvvooronv~~~}w}zuq}|uqsqq}zvtu}z{zspr~u~{qtwxy{{{|~|vx~}~}rwtomw{rroto~}z{zw}}~{}{|~~uz~ttxtsutmqx{z}trplgryvppoktwrtr|}~uojsvxx~|~z|yz{~y}|{zwztrvwupwu{{}|~~}sux}x}~~|~~}}|~{y{}xstomqsou}}}{{xry|utsrywys{}|~zws~||}x{z{}~z~yw~|z~}xv~|v{{utwuswvqjnlz|y}|z|}}}~}~}}|xxtzvuwrrutnty||{z~}wuvsqy}xqommuwsxt|vmlvy{zz~vy|~}z{{~|~uwtsq{|xwxy}~y~}wut|x}xz~}y||~|w{}xur{ypvttu{tuy~{}zzyw}zuss{qvry{s{}}|zz}z}|z{y~|||}~~}~{r~xyusuusx}yqlmnz}yvwz}{~~~~}~}|zvvsy}qtrrs{wwz~{~y{xwwsv|wtqstvxrs|{}zwqx~~|ywt|x|}{x}|}xzxtqruu|zvuv~}~{}~~}|z{}z|yw}~}}||yu||{~}vvrw|qvstsztrt{z{y{zxwt}vxuv{tz}v|{rxy|}~}z~}x}|}}~~~u~zyqnomrz}vsrtz~}}zyu~}~~|~~|~}}~|y{xws|pqppqyyvx{{yw}rspty~xyv||vxpz~~}}uu{{{z||{}z~{~~}}{xxupssu{~wwx~{}}}{{|y}z|}|ywxw|yr{~|xvx|vz|ppllmvsux~}~}{|}y|z|x~ztw~yz~tyy{~z||{}~}ywy}y|wxslonqw~zusuw||}~~~z~~z~|{y~y}xx~swpuzstsssxsrsxwztstv}{vxw}}y}voox}}y|z|~}~~~z~y}}~{rtrtw~{ww{~}~}ztvx{~~}}|}~yy{{{yxz}w}~ztowz|{~wyxv}sqnnnvuvv||~}z|~|~|~x{{y}~|z~}~xxwz{~~{wwy}}~wu}yx|rtstu{zrpqv~~{}}~~}|{}}||x|~|}~z}{sxyywyrtwt}xusrqwuuuyz~||{|}}vwu}~}|tpv~|y|zwww{~{|{z~z}qrqtx~{|~~~}|vwwy|{}}|zuw|{{y}yxwz{~uy}{{xsqy|~{{psqowuttux|zyxzy|{x~}|zw}}~{}}~||{~w}}z||{wrrztzysvuvxxtqvx~y|{|{{y||}y|{~|{||z{v}tuwxwxsovpuxtrtu{}}z{y{}{||{||~|~}zv{x}}x}u{wwxz~ytzvuzrvvz|~y{{|~~~~}z||v}{~{zux}wywy~}|z~{sumvy}~|tuxzyxtmtpqywtttxxyvyz{~~~z~z{t{}|{}zyy~yz~}wyz|zyu}zwzponqt~|y|{|||{xzz~{|z}{}}z{||v{|}{yrty}}~yruqnuqopsy|{{xz|~x~xz{~{~w~yz{{|}}~}}}||uyyt{tssvww|sy|yxy|}~zxuxz{}z{}}xuoox~}}{yzyvx{{{ypqphlldefmqxxzz}|{|uw~~~|v~~|~}}{yyuuz}}{xu~w{zsonn|~yxz~z|x{{}zxnehxzvqxrnpjmsvwtoqswvvpkonkwtrrtvxywxx~uut|{ux~}{|~}tjegm}xyppuvxttt{}}v~{zzwwvyxyw|~}z}~{yx{wqqsy~~|}z|vw||ztoosy|{y}zt~xtsw{{qsu{~svy|yvopswyyvqxts}}wxwzyzuuv{~|}}{|wy{~|{uu|uxwrpst}~~}yzxvmlms|vwrotusmebbgimkjnrjssmkmprwvxw}}|}~~xw|}yrwqmnkjqv}|v}~{wruvzyxsw}v~yyvxwxuwy}{|~yzxxuur}|y}wutw}~{|xrwz{yttv{|~zuv{r|~~~y~y|}{}{womoty{{uxysx}vvvz{}zyz|~yyz~}zzzwupmvutyxwyz~|{vpwqoqpqu||{svtovxwpieehkmkhlogstqqvx|~|xvxy}x|}|}}}yukjoklsrvz|z}{wssw|~{u{us{|vyy|~|zzz|yt{~z|{yxutos{twwutx~~wtysv~zwtwx|ywv~x|~~yz|syy{~zz}{vvv|zsrslr|x{||xuwx|yz~ywwzyzurywqurqsw~{|}yvomwqqtsqty{}tottrmlinqvrpkpjgpporvy}xzx|zx~~wzzyz~||ypkopipoopv|ux~~~|z{~}yrvvrzz|z||}ywwy|z}|~~~yzz}||vv}uqspmpw~v|tu{z{}urlohdklikouz||y{z~}y~xxyw~~~~|zwzz~{v|ztvy|}~xswzqvwsrty}~~twrsw}~}x|}|yzwomnv{srpo}zwrux~|{w{|w~}{zx{{yvyz~}{yx}upsy}~{xxypz{zy{{~{wwwx|vz~~|z{{}}yz|{wswzx|xy~~}z{}~|~zzyslusx~vpptvtrnrlhmmikjosurrtvz}y|}zx}~~}~}~xz{xw}{}zvvtoxxvuuvxwrqqstxqy~zz}~}xuuux{}z}yurvy}~{wuxyvs}y|}vsty||}{|x|~xyx|zx|}||wzxs{|zvtrtqonrsy|wzyvxyzwxx{{|xy{y~|yy}xxx}yx~}}~||}~{urtttnwwx|{|{vsmookovssqsuuqoppuzyz||}yvy}|}~|zsssyyw}{wvv{vy~{wsrsuqssvw}vu|ztwz|{z{z|||vv~~v~~zy{}~ztoqrur|~|utx{z{wz|yz~}}xy{{x~zutxsx~{zwyuspqquyus}|w{~~~{zwxurr~{|}{}}|vwy~z|~}~~~ypllnony}{xzyxvsuvqsxtrortvtvwy{}vw|z}{}~yvz|~|y}}z~|||~{vuvxuv~|yuyxry{zvwx|zzwwtvslrzvw~}|wwvxttz|~}xwwx|zxsvw{vx}zz|{zyzws}~ytuwzy}}xv{zvuuyxywywzzuoywrv|~}{{wtr}|x~zz{}}||~{wz~z|~}}}zz|}{rqnqnnvyz{wvtjptusvz}{yxuuwwp{}||}~}}~}}|~}|z}}}~~~{{}}{vuvlmpplmpvwzyzx{yqr|wty|}z{xxz}{z|~{}xusyzyy}{{{y{|t{~}yz|~~}}{t}kmszxy||xrz~}|}||z}}v{z}{ywtsswvww|wqqy}~}yx~z~|y~yy{zroz~vnnljousw|}}|~rrvwttwzzzzy{~z{~uwwwvxxz~|~x}zwsvy~{zytz}}}rljq{{vlomkov{}|{~}}zwqvuorzywuwwvuttuzyux|z{|yyvuw{~~zyvopqy||}{|}}xspqu~|}ztuxyyvwy|z{vuxyqw{xsqqpqqtu{~~v{xswxz{|{||zvz}ytyvuux}~y}~yvrmnoy|uxxtx|~|y{zzxuqvvou{{wwwwssqru|{x}~yzyywxuyz{xxz{y|zxvwz}}~|}z|vspqv~x}~{{xuutvvwt{}vu}xtqttuwxyz~|vuyvx||~}{{|{xvv|xtvyvyy~~~{~~|rrppptzxy|yx}|{yyvwsqsztu|xxuususwz|vvz|}zvwtsqu{~x{|ywwz|}|zx}zsrusvw}~~z|}ytuuxzywy}tryzuuvxx{zzz}|vt||vy{~~||zztrqwxsw{||~~~|zyzxnopqrv~wz~|~{xwwvurwytr||{y}|}|zwwxvqr~~}~{xuvuutw}|wwyvwv}|xwwywupuxqs~{usqxvsw}}}}{rq|}|z{tv{zuwx{|~}}|tw}{w|}}xwtrpqvxutqrxwu}{}}|}|~~}}}{qsv~{zxz|{y}{zu}~}{{sty}yqmqsuuxw{{usyurv|~|z{{{|z||xttw~{yx{zzyyz{||yusprzznooonpuxxvtpprpluxvw}~}zzz{~~|{}yvwzytnorjfgnt}~{~~|{v|wsv}vquyv{~wurpoopuxppqrnorxz|{zwyumnsomty|~u}olwz{~|v||||z||}||yz~wqqpqoqqspqnqvtpw|||~~{vsqyywnkikqx~xy|usswxywyzz{}~|}}vz{|yvz|tpnoonnptvzz|{|zwsrttvy~~~zvtttrnmvwusvwyuttuuwx|{xroknnrz{yyywz{}ytpwtporwyzz|{{z{|}ypjedirz||{ywwyywsu{{tsrtsssxy}{}{spsrtuxyzwttrrtyz|wz}wuvxyx{|~~zx||{ywvwzz}|xxwwurt~}{y}}|xxxxz}~{xy{yz{vnllpru{|}~|xxvy{|{~~~{vw~~yz}~~ysoljotx|}yyy~~{~zxxzzz|}~|~tvwvruy~~yvxurnjghklpv~|z{zz}||~}{z{}}zyxttqqty{~~}|||yz}||xuqpllkjluyv}}{z~{w{~xrrnkggflqwy~~{y~||zxzz}}|yvrqqwy{uyxqjiijjlqty|~~~z~|{~{xvxtqs{wuvwsqlklquw}~}}||yx}}z{yyxxw||wsrnnhfmtsty|ytrnntztv|}zyywxxxw{}~x{~yssqponosz}~}{yv{ytsu|xwttsx{}yxsrtssrspqomlssmowzz}|{z}yvy~xvvvvuttxz|{~~~}y|z{xwtwxyy}~{vmkrsonrssppkjikjpyzuy~|zz{|{~xtsrqporv{{~~z{{xspjgcfkrw}{|zyrklqlghmpqttsqrnnsztsy}|}wwxupqrttsruy{}wvvqlklmopuz{yusokhsvrpstsqqonppqv~y{}|{||}yzzsrqtvyx|~~{~~}zyuroqux{|zsmqvqmprssustuwux}vtwwwx{~z}||||zutvxx{}}ztponnlpswwz|}|yzvrr|{vtvussqporqsx{wy{xyy|~wusuutsvy{~~~~{zvw}~wqmjhdeiosy}{u{~xopnnoqqruuuv}}xw~~{|}{yzyxttuwx||vvwvxxyz|yyrnklmmlmqrtvz}{{tt{wtsspqppswy{~~~|}|zxuurqrx{|ts|~zz}~xqmkjhgknru{~yw}vsvvxz{z{{xwz{xz}|~{|zwrstvx}||}}||}xxxtpooqnnossvx|}{zwvuusuuvv}}xussppsvxz}}}y~~zyxwx~z{}yuqrolhjknrw}|zxvvxx{zzyzwwz~y||z}yvtsmjjmnsv|~~~}{zyzx{|~~|vuutnmnpomprtv{}|x{}|||zwuustx~~~}zwyupkmnppuz~~wu}}{yzyy}~|zwtuyzusrsonosv|}y}|wyy|}||zxtwz}xy}|zxyxxuw|~{y{}|z{{|{}}|||{{|}|}|{vtxxvsvwvtuvwy}yv}~~||xursqrv}zxz{zyw{~xttvtuw{||xx~}zx{~|~~z{}|urppmmptx~{w}}yvxx{{zvuronrxywx}|wwzyvuxxvtutuvy{~~{zyz||}~~~~xttxuqorrrsxz}{uxzxvuttqrsuv{|xwxsnnqssvy{{}|~}vz~}y{}}|~~{x|{voonnlqtx}wrx|ywwy{~~~|xz~~|xtszzwtvvsrrstxz~|{}~}|vuwrlloqquwyz||~|~~}|yyzyz}{z{}zz|zuxxtoqqrqtux{~}~z~}z{~{y}yrlljijnqvz}~{xyz}|~}}yzz}{wuz||yvx~{vuuspppqswx~|z}~~xqrtojlmnmoopruu{}yxxuuvxx{}yy{}{{~}y}|voomlmpsw|}{~|{y{~~}~}~}xuxulhhhgjlqtz}~zwy{}|||yxwy|~yx{}|{zux}~wvutpppruz~}{|zz{}}z{}}|~~|yuoputppssttvvxz|~}yxyxvxwwwz}{{{{{}||ztqsrsuxz}~{}~zywuvy~}}{z|{}}~~|x{|vusqnonquz}~{z|~}}{{wwvy|}xx~}~~~zzxvsqqrswy|{xwxxxz{zz|}zv|~yttzvsrussrrqsuv|}~|zvvsstx}~~zz}ysrssswwy{~|}xz}||yxvwuxz{{zzux}|tqolkknpvz~{|{||zwxy~}}~|xx~}ywxwvvutuww{}|xz{~}{|{xunntwssssqrqrswx|~}}{yzxz|}~~ywzztqrstvyy|||z|}{|yzz}}{zzyzxutyxrnnklnpsw|~}}zxxy}~{}}|xssy|xxwwttqqruv{||~~~}~~zux~{yvtmosupoppqsuvyzz|~~{{{zz{}z}~~|{trvtpoqruvwwzyy{}{~ywvwy|~}|z{zzy{wssxtolkjloqv{ss{y{z~}~{qowuohljm{vupl}r|}z~~|qkvu|y{vtrzzuypr}lmounlqtzruv~}qx~qhbUdxw{y}|{r{|xjkotqw{}xxmlwwkhku{x}y|qplx~s|y{or|gejyqkffWdggccjmxyty{r~no}zu~yyvrytwwqnkfh|{wopytkz}x|~xu{rw~yzfXhtpeggelcbnek|snrxxsepsflmi|}w|xj{osss|{|ujmpvvy}r}zw}}qqv|xgcdcbbyxqdmm]n~iijerpftwqzhd|tn}zu}z~uvzqr{x~|tlvhZepkv~yrn}xw|u|xcejfsv|wxshkfdtqmiszwljyqfi`dgg|osuiphwwsspynpur{uv|xzv|xpulyqkqe[Wi}yrymbzzdh`etkf{zxzfnrorkiz~~v}{up|xt|ywnbgcVvppy}|x~}wkrsgldd}vuiftmleqxrplxwjjfgfZtzuvtzimguv~qyqumoktuz|{y}~q||uuvnt~~pwld\]nov}}hzjhebqoet|x~lhzmtndl~tvz}vzwsv~vzklkV`}ty~m~~|z}{xzsxwo|wpn\j{nrropfxsroq{rgokoa`rorsrljg||tt|vunnllsoy~{tx{wzwxn{y}qygd]_kiqpnmfkyqiz|x}he}vmqqio~q{zsy{wz|v}t}uxmnmWczlq}o~~}xz|}uumw~pp|vuoYn|s{ruslxotos|pirpq\gnip}vonhn~wux{~qx{fjionk}{}|{w~~qzwssvwwtfd\gh`qzyomi{~qt}}q`pynsrngzpqy}zx{{{y}vuxu}roo`]sibmtu}~~~w}|w}{vnxrwwrnmfahp{wvrozwopzxo{wreppllijx|}zu{yq{{y|swwmrth_mnaz|y~{s}}xqt}r{{|}ogjgRVivky}vy{zw{{ozqjkw{xdgy|sqorxqvwxvo}v|yxrllk]jrq{pq~{s}r|s}~znjqm^f`ecbowryuwryl|djl[Zaztl{}pzxxxx~|yqxrgzqwqc}{}~t~zwzyu||xww~~vrracmZ_ausvtqxhkczrblcjnkowq{~~{zvyytyup~hbm]ec~{~z~|}sw}ywuvqymjtc[afgtztvm}jvrrzusuiaudggux}zwr~voolfpx_ffgrs{}~zy{nvtvz}}xvtk`j^_oeyry{~{}zizzqprwlwtghlnmxrz{protpyrzwrsp]iogoy|{|~svoyp|w}szr~{{bmpp`\phuzz|zuz{|ttrhhqbqwvuxwto|nxyy{{vl~tplrpiupks|w~vu{vy}{}|n~~wjnm[ZXn]\kq~{zwnzzq}{guyyil{ouut}{}svwuux{vqolafmax}xu}}{tzy~{tynkqddeckfcjjd{ztmpsbbbzkgswv}|z}{xwvumvyk|zzwbnzx{}y|{||ysvxxurtuw}tpri]jga`lppu||mlomfjksrnuxo{}~~}{s}|poy|jdo__b~w}}|wzvz|}|{oyyoipnekibefil{wz|ptv}{rusgkuoltz~xvu|vo{mfdur\`cmxw~}y~zq|{ruy{vzwmlgd^ib]ru~xtwwptynruprtvuzz{vsmn~jlntvkjkbbsvwv{po|onu~~{vq{mixuxi^lpkbozu|{}|ttqnjtqh}z~}wrunruxyynlvpgipgjtt{}|vxlmjkw~~r{vx}kmhieX^dafix}xsqlpuj|~yilwxwwt{z{otrmmfk`evpr|zwu~uz{y|z|{rpejo]e{e\cWeo`r~vwxseavtnxw}{rr|p{q}u{w|s^vzktvlkyy~w|~ytx}lgym}xt~{t~yvzooh_ik`Z`nppt|v|ptssz}|evt|{yzstyum{stolo\fnbj{{vyv|u{vzzu|uzmjsk\ux_c][l_]tr{z}|uiqwmnqtropvst|ydpppwoersmwwx{zwe~emlr}|r~~s{uvqgmld^Zgdfds{xv}xkvstzvqztxwnwvvzxzdio^bni`}|zt|wzzu}~|w}||spxq^u{aeb\me\nnmvftxx}snutor}~svvvrynlvqvsjquemjovs~y{n|njiexzuw{szvwymmrjg[fd_abqssxz|ptywoqv{wxq|{sz|xeum`cjZbvr}}}|xyx~wu|~vyyzyvu|ic~r_b]fpeftpplYo}}vrxx|hvvx}suxszlrvtysiwvgnjonjx~z}|x}ppmgixupy~t{xzynpqkl]ge`aapmiw}~r}|tqsx|{{s~}rx|}zjwqechZ^ypiqsz}w}yr}|}ytx{x}~x}sg{wa`Ydricsvoo[_xx~u{x}np~ytz~r|wsxynxzvmkxjlknsinzy}zyxsy~okgxxor{xw{xunuomg`k_c_fpgjt{xuutxryy}}y{ry{~qp{odbcUfxhfjv~~}~tqzt|twy{~y}x|nnta^ZjpgcrvqkYbxv~w}yzlv}wrxxt}zt}|ytujjugkknriovwyzwzq{rkl}wmozx{wzx~totnqeel`c_hi`hpx{yt|~yvsjqoossrsrpuyw~~||z{|wyyzzuzyoop||yliijnstxzy}}uqhruy}}|zky}{{{~~}z~vussv{z}}{}zu}yztmswnmacgc`dcelqz{qzqX[~gedcjpoqrq~vt~}v|wsurrtno{{~}}wsfk|xsouspuxwyuozx~vgzysfafhovstxwzyzms~{~v|rj_Yd[}sssrsj_`ngfnf_gmq|{lhmo~n}do~|yhdjlttmfpvw{vwqx~rjmhjvez|ojic{vy}nksu}}~psst}wtuvsvu}sxwyy~~xtcbbO[bpzrugjplgafhiewwecnjpowdq}r}|kapqrpvsfpvqu|mjurcpi{y~}~mknbpwu{vry{yyorvwz{o~~ry{s|ywz}vupeloZV]j}vjrnhtpcdpnm`uxdfqolzssfqznw|n`prmkoygjt~wt~nn|~kkjwtt~y}{nmriius{ytzwqrwz{qyrzwyw||wr}rt|udjrfW[`zrpvhtuk`ppqdk}kbingsxslj~vv~vbgvnlhzqho|sxmzymmm|mx~zzzxtfih_otxyy~}z{st}z{qysxyzz|syxss{ldnqa\^i}sxpkyshcvrobt|faiigxvsiqx{p^ktmjl}llqvsp~upmqyo|z|~ztkaea^qw||wz{{vuz{twv{x|w~|s{vu}~{pwg`kna`bnw|qjzsidyupct}f_fecvwsjs}~q^irlgi~pqtwurxtpp{oy~xvq{i\_^[q|y~|yxu~|~~uzu~|xw}~}}x|}sz{}{lrh\gpifmn~|{gyvpctysimo^ce`oztnl|za`omjdz{uvy{p{}uwm~suttronXY]Zm~y~u{w~v{x~uvyx~}|~||tz|~y~yvukmgZdolkwsixxqfvwmlr_bf`lzuom||c^nlhcx~yy~{|q|~x|n}vvs~r~|pl~kUX]\py~v~xt~y~{~vvyy|z{vx{}x~~rwnpilc[gplq{w~l~wri{~wlsn_dear{unp~x^aolhg}}y}~}yu{|{p}tyu~yqyjq{`TZ^`z{{zv~tzw}}yyww|xy|~~w|yzxzvpolljj]`knk{ytwuor}un}cafbh|xsl}~j\knket}|~}~u|yr}zyv~szqzzoj|lUW]\n|yzryu|}~wu~yxzwzvvz{vy}x{knhljl`^hlhvzx{vwtpxoh`ece{ztlyp]inkep~||z}|}~x|yuyx|w|t~xsx{qj{nUU[Zj||qwt{~}xu|zyzvztvyzvw|~}wzhlfkkla_gjfryrxywt}s~kaggez{vowt^gokfm{{}wy||{|y{uy~x}{}wyvts|sjxtYSZZc||tssv~y~|y~y~y}}uxsxw|w}u~yz{hidhkle^eifj|oxzvyyydejerytqfaomigw{wv|z||~rx{z~{qytpzwmn{gSV[[q}ptr{}z|sz{zwtztuxyvux||txdfbikkb`fifs{q|}{xxsdhig{xrtzaepmjm|}ww||~{r~zz||{|rusmyzsjqybSW\^x|rvu}|{|s{xyuty|wuvwusv{|q|vbc`gjjb`fkiv~t}~|wzwghih|ysszbeqpnqzy}|}}rz{||z|ptqkswpimxfUW^`x~tvu|~z}rwyx}vswwvsvr{vp}~{yrrzcb_cgie`fmoty|}z||}kfiguywni`mrrs||~}u~x~{~y|myvnjjsnihvo[X_buzvvzz}|u}vzvz~xrv~xyqtq}xxly|zw~{slye_]`figbfosv}}~{z~~ohjhtzxn|k`nsuv~~~}v~y|~x{~lwuligolhhvq^Zaex{ww|z|~|s~v~yv{}vrvxxrsqwslx{zx{ynlvc^]bfigcirt{}~{~~niki{zwn|gcquwz~~s{}}{y{vlzolehmiflxl]^diyxxz}zpw~vw|zssx|}trqt{y~koyzy{~xvhrn_^^ehieemst{yllkr}{rvqbluxz}y{{|~xy~|kutkidkigiusb\bexyy}{{|{q}v~xvz|tru}wxrrq~umkvyyy}wwjmsa^]cgifdjqr}{}~omlq|ustdjty{~}{y~}~xy}{kuukjdjigiutc\`dvzz~{{~|{q}v~yw{{tqt|{uxrrr~smjtxzy~wvjksc_^cgifcgnoxwsoor|yqwfisx|~~v~xx{|mqwkkdhhhhrvg]_bp|{}}z}}yuux||wz|uqrx}stsr~u|qgpvyy|xuofsi`^afhiceknrvzrqp}r|lfpw|v}zxy~~tjwokhdhghmwqb^ag~|{|{}z|ov~yx{yrqs{xuzsrt}~r}khqvyy{tuihrd_^cgihcgmnwvxrrt}}q{ihszt}xwy~{plxlkecffhoxob`dk}|{z|x{}nv~xx{xqqt|vwyrrt|}pzigotxxyrthgqd_^cgjiehoqzxztsu}~q{kiszt}xvw|~|}z}q~}khcfs|yolmxyxfstckZkb]c_ckqogkr{}topjhr~|sqqt~ko|t\j]kdakehpzzs{y|~uy~nusoxnqdkaligvpot}yoswro}yyv{kusmt}yhpqvnyvy|}yncs|vscy}r{okvrvcpoko~~}yyz|dnrrjsov{yvjoroi|u}~x~~o~|wy}~p|yikkhbwzzh{{qfojfolk]mkfjywxz|rx~{}tlnt`pzwpxt|w|mckpok}zz~}}q|y}}ryye]ekih|wtjsikpmf\nfdh{svxxnz~{v}|ripo^vywpyt{wxfdmsno~z{xszy~}s}yt__gmfo||}rntfqnm_`laclzqtupn|yu{qktfazut|yxyrbhptjx~|~yoy~uyv{yyi\dmmfwyyosojumjYig`durqrsivztyxooq^l|r{z}wygcmvpn}~|||yoxv{}q~zs_bjpepz}~vtvjvqn^`k`alwonphmzvtuptcawx|xznbktuky||~}nz{twwr~{ydaisjiv|~yuzptypg\ld`e{|nlmjd{{xqzstl]r~u|{yvdhrzmtz}}|urut|}x{{r}{p_enrett|{zp~vq`dlaamulhl`myutyttba~y||y{mdmxvm{x}~~o{}tt}{~qrzzyfaitmjwu}wuun_mjbbvpihi]v|xr{~wuo^jv}yzhhp|rs|yz~}p{tvzzypp~zvcdjujqwxuytmaoibd|}nfgd^{zxq~yvk]pw~~{zvfjr}pyz|}z~}{r{twx{vq}p|ysbekvjuwzv{umcrkce}~negc]{zxr}xvj]rw}}}zzwgjr}qz||}y~}|s|uwwyup}p}zxsbdjtkv{uxmuj_efnoyywz|~uwqmjyx}wuwosx{soz}utwxv}wyww|rs}z}p|ywwqgykfnv|wosvozvlru_Un{|~}pq{{{rsoqvw{vvwwx|w{|hjmwyz}mthuw}x}t}~le[jxtx|rmTf{ghnxl^sgc\_]d~iij~{u|lZxw}mv}}}ywsnRez|rqoy{o}nwuspow}t_TK\dj~ymeq]\baigeusqmuxqf]iddw|pt{mqu|t~|o~jhhac{qgx}x~x|rc~zmpm\UVm|wyhfkbQhs]ce`trk~wmoV[|xt|vyxz}v}yukm[Wkiw~wzyqm|~|vvo}~~vddOU_Ur}|nnhbgbeuppgoxokcjg\cm}~y{lj|q~s{ypmoj]\nozpw|szwxkuh}~}z}rgWUa_nyxv|fVsjXe]cvjqwzsVeobq||uyvv}}}}v~~}wx__g[e~ix||xowxsrc^gXUjozzolhakqnjhvoqinve`caqjzyg~luqx~|u~zzo^ge_qx|~uu{{u}|tikguvl_dYYXkygys^`\esmk|~|{ffufakoo}}yn{~{}v}}~lnp][faauu~|xtzyty|z{yrpwj[fhdngvxfmqkogtrnkpzrhjegYbvw|snlmsyws|lnn^ealwwx{zvyzvxoih{~mrj]XVejm|oeb`tthsyqf{xile^f|{tu|xv}|wv}|s{qb^cS_|twz~|z|xxnu~~yzrngqhfaSjuwqrmlyorlu~pnskiXaifj~|ypogx|zpwsuuefafggy|sy}u~{t|ymr}sq~u|qi_Ze^\ozthiyslr|}phzwuk_Zolwx}w{v~zu{|}szjbcS[pihpyvt{}th_`b^ahgXTlv}{x}t{{roz~zupsqbinemY_my~xxy|ml{zvtmyysyvjer{yvnkxp`knax\U`jqqpuxqlowwx|vkxyowvdq|qkzunsvxssxoynnzw}ujhh}~~{lx~}r~vsrinkih``fs|xcdvedlks{|maa\v{~ksuylt~{vwqrtx{~utywksmosthjby{umnkh~w||{zkgjmsuhVhbZYesxqycZ_Ufylo|vnxznnuvq{x|zy}~jzxnl{umtiwsefosm~|vrtorroYffZXkz|osz`[cYa|{mn|vunyuiny}usy{|~z~lyzmnuszow~~mbitwsy}vvtoqqmYheY\s}}lus^^fZf|{{~iuxt}snoiq||yu{z~||k}xmrrtzn~}~{hbjuuv{uytrqtiZkcZay{i|k\adYm{y~yh~vtyqq}zjjt~x}~x~z|to~smz}zqxvo}}scfnup}}w{uxux^ck^[k}}qk|a\d]]x{omzs{~toy{~~shox{u{|~}jzzporryov}|{jdjrpr}|}~|{x|o\ne]ay~yh{k\bcXi}{}yi~ttwrq{}zjmt{t{z}qq~rn{}xqxrn}|qdhoqmys~}{]lja]p}zkpu^_cY`v}zluxrytnx|~okrytw~{zzu{tn{j}yoy{{wuoi~ts||}zzebxzwwusogvidi}rys{zuqz~wbVmvi_cndeiv~}vmuvvz~q~gfturz}t~xt}zzp|uj^fmmm{vxpr}{syhenq{uryrzxgm}s|upsrjXPhgni]`Z`nyux{sx}z}ntjjiunirzxwzm}x|w{nrrrly}~{l}xhWced|~{iajqmnnbunknjrp}qssm}vz~wqrpz~uttqbijjhhq}ty{|tsqlkozucozvfpfo}wz~m|}}uwtf\o~y|vwivx{zlmt~ql{u`_{Z_nut~rqhrtnv}{kiupusswofnuwsgi~s~wk_xpr{x|~sklq{qtzgj|zmnousfmbeW]lkqhcov}}uu\dvo{y_jzxnu{}|hhnz}~|r{x{zu|{rlrvntpqyux{sjlsixvpocSZgpqg[rg{|~pc`emnbl|u}r}|vkvuztnu}fwl_cbrq~|v{wzttce}~pyeaRXp}onhlYcwigmoskZj~zpmo{{v}{z}qwqszm``i}~tuu||fuuyzlns~~~p__dcoy~e[\fplbitqksstsfsgbU[r}|rxvqy~~usvwkxtafywmwypurlw|~obi~}}ofca\V|{id~xrttulmf~tyzvsg\grq|jjgeztrw{{tsmnc_vn{tzjxqy|oxizyzyeshdgZbryfwpkaZlmsj_jsa^`]Yay{|{ynx|y~stmolhp{|qikyvw_q|yz~~uqhZLQy{vogopgju}ik_Zn`lnjusxwprxyqv{mv~n`q}pdakw|zyqkxyxy{}|sjheh]fvufhjbZgumnd~}ytthWWtsvm}yqx{w{wng]tpx}{zne{x}s}qhuvxwapk\clx|wdvtntkhjt{uovkcc`gh^p|~yncjzquz|rtwxyomrrlh|zx~wyg{uv}oYQkphahzrbgyoqxrqd_v_bku~}xqt}twx~xuuw}omlifnt~p}rqlwyvr}yz|{utwph^^`]~vvtxo^~vuqix{q_NRlqjkzvs}zt}vlwytx|chYhp|zo||~zz~qvw{~zyv{}tm|{ng\qsnt~|lujqppqnxuknpsqjgqnqusoz|{~~|{~pk{tmywpcXj{}~wo~v{ls{pmokfZamwzhatxhjrrohu|nf]ntsx{svuxxzlt}}}wichnotw|t}ss|yrmzwzrvqc\bk`rm}ywmqrvvqlfikg_gtknuy|thytmsulagogg`w|}pw|tpqy~}fquabS\jruszyq{~}miyuc\\jlr~yzzzww}{x|{zynnpiaZajv{}{zyqsswywmgxwv|}~~ytwimh`_^ca_m}||pouz}{~hd{fidbwxqioutoz}|o{oalxrkhqzwz}||v}{xrux{xkkwc]`YSShw~xss|vyvo}nqojehliiszm|w}}ysy|~wr|}gWpgjsw|zru~~{~otz}|zy|}{yz|`inV]bgo}{{tx~vmkud`_\WZv~sv}w{xxosac^^en}}y~}kkuy|zt|zyn{~ystlVOav~tsv||xy~ydsm^dhos||qwwnv||rimzxcSV[_hx{|~{xllllx~{uvz}ylloljfebSo{wsmlxxo{{wj[^xyqu{ywyz|xorxyvqmr_\wjqsyxxu~wr~ngltx{txrv}tsm[qhW]cnzxzwvszyuw{wsrpbj}{pm~ymnkefuz{qy}vqvqgj~yxxw~wg~z}vnyuwosvn_W]kk^]iqy~|qzz|fqxbajpzxz}}~~qryyvw|shghknqztqvsttq}~{zt{yuvw{}tmnlic]bhYqxp{tw{vnaapumghsut{|v{kn|~|~ymm_\uplrz{{}|ypsvw~}{}riiffixzmhY`kllt}y|}y{{|ysutmihomh|~|nkzuvggg_WWd|{y|~|qztyhrjwlrsoljkk`Y]er|~wpqqnuxtcrz{{||q~pootvz}ujfehikty|t|vurnfh{yz~zlhp}vnkjebjdwu}z{zvnegs{uzyv||tmq}|pmmjijxpee\mu}}unpru{yyyz{yxhh|jdaRV\^bj|}rz|vwqlxp{~{}~~{w{|jwssrlhejomnt~zru~uutpt{qrhwz{wgv~qljkgef`ZZ`ny~yrpoc_dlns|utqu~}oxuiemuqlot}}~~~tx|yqqu{zyurqjdbjy{mc[VSQ]ohsyyzvzzwvvttoipyy{}}ypopuyxxo^ircjkjv}twwqx}|wpllnkmsm`oo_`aeivyxyur|wokcbl{y|~|tw}uwrlphgeelrwz~z|mo~rsyv|~wvstuqridu|yxth\SZmx}~o|lnppw{w{~pjkihhm{}pdZX\bu~|uztopqvx|{wsuvojhhkifd[Rdvsyzwgbjzv~~xrmlqspqri[jnfo{}wrtvqs|ykfdhs}}ts||yxvrimb]mg\^dlz{|}||wqid|{utyxqnlsuqugn{yvrlgbiyxz|~{|~lluv|}ykv|kni`a_^[[fmjebgqy~q|toow~}~~}~yvvvvu{}xrkijhimkj{}xts{{w}{slo|~vxtqnpqpmgfgdaac^Zs~{~xyupqu}}soqt|}tvlp|tln]auppw}|}}wurrrruqz{ttstw~rgdcetyjmeWbhjnpssr}zzuptqex{nk}{sxphb_[W`py|yw{|~uwwwmev|z{z{~nn{pjnrpprle\X]dkuzz|~z~}yrttr}~npportv}v{y}wihgt}{}z}~yw|{~roz|qwrx~uuqxwspid]]hoouqj_iw|{zw{|og{bNMZemv|y~uswjdgsw~wulnuz}|z{w}rqvvgdis|y|~|wldvzquwzcYd{uageYVhngfjfq~~yuu{t|lfu~jbn{wytnikv{fhqr|zz~y}xmijhjzzpouzz|o`intyomkpstlijkonl~uktpylglnvofkt|~wfq}|zorrwtu}uxqgvin[bvetx{su|yy~meSX_kvtzosvph}rq\vxw~iarxhikv{txl|ynqt}~yt|yvzgZZcp|{{}o~su{~|~}tg}z|yndbc\foy}{hcfjwzkqztruqrl[rjd[_x}vcfsrxvlyzaiwfbl|wvyndgp~~pdff^P]~tmjghsuulwprjpjcgolq}{oxnlijwzxwnqZhpkktsmxn}ln}{oat_`YT_tzx{zltnal}rajvb\`gj_f}zizst~|mz{~r|kphkmlv{urmpw~~|}hxy}vpooaNY~y|{~}sltoltnk__qcmfeimw~zov|{{upxtao}r_Ygryvx~tz~wuy{xzmbc`bgYps|ortsf_|{xnwqggnmdZmvplqu}ty~wikXivo~yl|rgzpv||yrc_sX^grwwptjgikv}ykljfchkvfnyzhxvjro|wzozkhivrkr{~~{zws|}waNYok`g~|lcz}onjwwgiZukbnuyzwyzqt~{m|x{|~mhmhinsvu}wq{y|x|z{}wrsspkacg_{yva{zsl}{ugSZxvjepzwkh}w|{~u}omqxim[pygyxhpzz|ywz~ynr}yt}wpyokzh^dnw|uaYYhqrphemqvvp_hwtyzwvqs{}}lo{ekqf\bsx~qmrpx~yzvr~dz|u|rf_Zqy]Vds{d]gniomheejjghmidz~}}}{|qn{yktwi[t}ek||dis|~u~sk~xvsvzmZehUkzfiovywoxztou{jTbthmneorwkv|sws~nxzvocsuVm|mp~x|zxv{}hq{{z}vxwyd_eXsragupt}y{xuu~^`oblpspnszhp|v~wq}j}tqkgeXvwjoxx||svll~wtywwvlnqj`Qnom}{w}xllwv}xuxk`leim}qflqziyzt}xu{kkwrk]rpo{}{u|rg|vsxvytn|ze^eb[Smun{xpgqy~trrpl`tocer}ntyszkrxnsogjlkcmrq|{im~|xj{w|}{v}c`kj]Tcrlxqgmvvu}xxqkWglfhnvyzyoxwz{ynrhlngcr~rzshp}|fvw}wwtrldge\Ym~vvdhv~wslm`noiextxpqyt}rwzxvo~mkroc\i{r~}or}{~hy}~{zw|ztqvc^hg_Wh}z{rho}vyxwup\lmnnpyurz{rx~rvqjphddv}u~{qq~zx|yl~{ytszlswc\lga[s{}qilzx~smnf^song{k|su{q~pvy}qxxomwkhayx~xu{}~pl}}{xxu~weynaah`aa|r~zqrzyywon\kwsmqxqyw~ysp}tjwkmmcahszpp{|yz}k}u~}zqiyldnia[hyvxnr{z{vqrh\sswi{vm|xw{tuy|~qx}qowec`}{{~zw{|~|kn|zyt~}}|}w~ufzsdfja`a}ryv}t~uko`\fn}}}||{xqdz~vmsf`bcc]^yy|zy}}{y{}vsjijyvxtjba]cVcjiee|vs}{}z^d{yluyx|~zvmrsvibt^l][\kypyu|mmuv{l~vv~oqpzoercbdbjhop~w|vmtxzrompndytnwzq|~z}||gl{lskkoseTm~yx~ytxi{{qklsxs}vmis^Yir\VZhnliv{ur}mqor}v}~u|{s|lddkzrrpgcgglhi}w~ovrysypl~{wyx{tow~yf]fQkjitmlxz}xz}fd{lfoxwiw{glztuszoabschls|{yjnwz{ytrwwxyxxsmozufnspZmr^[cksib|xoxc~~wsprv{zjx{uk|zylWUmyofmyrt{||wqmty{{iqssgdchpm\\dbamt}uw}zr~nrulq~p|}w{}~qrsx{{{|tjlptwreslr}vtu~n_p{ztxzux{|m`x{ga]bi^dWpzmwz}utuz|tksvy|u~xtfzzkjxs`mvic[xvlr~wz|~utw{xxwy{}xquxzwaxqg]YhjodWcu{pmm}ruoz{~qznquyqe__ckmcnz}n|tulptsi~t~}vwb{vemonjdefY\clv|{nwshz}zknwxytzx|sv{hl{h\_dtozv|v}|zqkirx{u~vy{ydXnyh_gg]MVhddcx}|vxvnowx~|umxyyvZtkdc`s~|t}~qvqklu|}~}vtvwwqnwenx`d\UV]iuzxvoxtwvi`syxxxwz}wwsvrpumpkheamigq|y{nkpvjs|pzt~|qxtncywwv{oZRerk`dmxvtmkjxvwn{~|qeqk\`a_X]wx{z~zvwyytqpszuwu|slnqtnissppkk`Ijfelf}wvtprullr|~{wxyzvor~~||}~{worwf^]]WORVekem{~mzuu}ttvtxy{|{xuvtysp{}puvttcn|fekdVKPUfojx{jziUcnmptz{z|wzygm~x|vqq|xxxzxvvpy~suegllraSKTYendr~pxcmtp{|~||in~{uyxuv}rqs~stoox}{|j{|fhlbjrtm___mq_rZSpepxtkcwibfrzy~qcdy{{vdyzutrtrxc|vecimtyph}xx~}omynu~xkljrt}xaxvrvrx|vl}}|yzpyo}tq{wrd_]XppcnPdghxjcyt^^i}|vf`|y{ji}xrqmmq|wue~zausuzlm{|vxxmv|p||sjlfptqhuvzqwqw~~x{jz|zwzr}wswvxrc`^ZqnenSgfjvhcys]]h}{ve`}y|ki{vonlmt}xue}ewwx}mlzzw~}}wxpwu|shjgpv}whuwqsrx~}viw{v|{wr|{vxzuf``\mtes\ame~vjasyc\av{|wh]yz{scwunikl{y~u~e{jt|zrizy{{||wvsyz|tgigny|{iwx}rrry|whuztzyxs|}z}{vgabartht^dmf}shcuxc]avz|td`|w|rbvtlhko{|x}f~kv|}nkwxw|}wwv}|rhjjvwwlu~v~rq~pv{~x|{ufztu~yt}{w~}|uedcixnor\nilymei{ra\fxw~yl]m~ufiwpghlxv~om}|n||zgt}xz}r}}xyz|nklp~ylx|xxzwpvs{y{zxw|goyq|{}wwp|mggiywmxagngz~pggxxf]`uy{{pae~u~ncupgfkrwwjpy}}ipv|ypyyzumoswor}uyy{qqtxvzx~wx{glzoyz{}vv~mtlmp}zozchpgx}}phiyzi]^rwyxm`g~t}nctofekss~wmr{}zhp~vzzmzz{vprvyls|u~x{zop}s}vyy~zzxfl~xoxz{~{tu{j~{ssv}r}fhrhv{}pjjy{k_]ovwwlbh~t|qbsqgekrqy|muz}{imu~zxm{}|||tuyxqo}rzy}ql}~uxtyv|x}}{igx{nsyx|zus|lsyy{urdukpy|tmkvse\fttvnedyxw{dlukekpvpo}}~w|pgwxw|w~un}}}zx{|iywu|yxlpxszwvy}~~zrcm{~tmwxz|xswwh|z|xiltjuz|rnn|~ob]lsssjdl~u{ucqtjhovrto|z}|lk{uy~~zxor|{|z|j|uw|zvkq}uvwx}v{}zz~~}obmz|snwwz{vsyvj~~zjnuktx~{spq~pc]krrridn}tzucqtiipxtuo}z}|liyux~zzpp{~}|jzwt}|{ymm|~|tu{tx|~}yz{{{pahvzultvwzvtyzkyyrixnqv|}urr|vh]epoqjel|vwzelvnkryyso}z||mgvvu~}~yytn{~~kyys|}|zmly|~{st|ry}~{x~zyyynahtxtlsvwzuu{zlz~{rmzorv{|utt~vi^fonpheo|uxyfmwonu}ytp~z{ykiv~~tv}||x{qpy|l|vtz~}}wkmxy{vqwwt~y|t~{xyz|uh_ktwpmuuxxtv|ul~}muxotw}yvvyse`jmnlfjw{s}rftwpt{tzwv}}|||rfny~zsy{|}xx}yky}yrt}sw{}}~qirwxxps}p|{}}~|u~|yx}}vm`eqvslruuxstzykwzuq|qsv||wxxxj`gmmnght}u{ygpxqsyvu}t}{|uhlx|{sw{z|ww|zls~vxquvx~|~tiouuvpr~~qx|}{z~u~xw{|umaepuslptsvrrxwjt~xytuuw||yzzzmbhllmgjv}t{zhqzsuzss}v}|{sgmx|zsxzz{vv{xjt{uvt~uvx~~|~siosstos|qy|~xx}wyv{ztl`eptslpsrtpqxuhr|v|vxvw|}z|||odhllmhkw~v{}kpzvv{sqv~|{thkw{{tvzxzutzxjp{t}rxvw||~vjlrqsop|}su}|ysu}zwxztncbnrtnnrpspovvjl{u{v~xxz|}~wiflkmjit~ywslyzw|xoxv~}||vmhsy|wtywxvrwzpixwut{vvx~}}~pinoqpmu{~p{|svxz|~yvyvph_gpsqkpqqrnqwrgszsz|yy~|thilkmimz~w{op|{{up|x|}ytjjuy{utxvxtsyyllztwt}~wwx~|~|oionpomw~z{p{}}rwu|z}wtxtnf_hprplpqqqmrwphtyt{}zy~}uiilkminz~v{op}{|wq|w||~xsjityzutxvwtsyymkzvuu|yvw||}~rimlmnlu}x~ox~tuvvz|~wrtsmg_enqqloqprnqxtiq|t}y{y|~zmiljljlx~wxtn|~{rzv|{y|wrjhrwyvswvvurxzokxwswzyvv||}silllmks~}w~ovus~tuyz~vprpke_enqqlopoqnrytjr|u}{}y|~zmjljljnz~vyto}{s{v|zxzupiiswyuswtvsszymlzutu}yvv|{~|phljkkkw~ywyny|ru~pzu~zsornic^hoqolpopqnuypjwzt}zvklkklkt~{v~ouvuz|~wywrngnvxxsuvzyt~no|slpssx{}shjlmrr|ugmu~vw~~|vqinvyyqmmt|utytjs}tzqd~wabjow{ynnqquu}}qv{|ro|xyz{wupvvmkr}tip}w{ysk~|ttwvz}xmkoosu~zuqrsquurii|xu~xxytqkowyxpops{}{ntw{x}lg}~{yuxz~ujjllpqy~y||y{|uieflzuvznq~}z{wxy{ws|lfvzyvxyx|~vkjlkop{|||qr}z{{|xvpt|~~wvwx~{wowibv|qc`a_cclyyt{uq}{tnuz|rp~y{{|wunowwumjklu|{||z~|o}zvxwz{y~~}}{|qixwnz}wpmokjeeorsmikinryqq}ytt{udo|}qjlkmpt|{~{~ztntutnfggmw{~|vxw{~svyfguwwuyyy}~tjkkloq~yz}yz|qw{|z{{wuqv~xxxx~}vpwibs~viddadenzysywny~yrmsy{tpw|xzvtonuvvpjllsz}zqxwxwxz}vw~|}}~{ymt~rt|umnmjhdkqsqkkkmsvypxvywt|mgx|plmloqz{{|~~yunkpqpkgjlu||wxx|{qwygjw{yvyyzvlmllps~}wt}~|z|sw}zzzzwvqu}zy{|~}slvqgv|ohhefhmz{uwzmt{|~woqw||uo}w}vxtrmjpstqlprx||~vwxwvuwy}tnvwz~~{qsws~ypnnkjeiprsmmppwz|qwzw~uq|tkztnompszy{~{yyspjfmnoljos{~|wyx|~wpxxhnz}zvyy{umnlmpu}|rs{|{|yt}}zvtqvz}{tvwx|xqz~qq{ztvwyyrx~om{z}~xx|sgfmr~zysx|zqn{|wxwwsrnsz|~yz{|~|yjjos|{|yr~zxtnqs|{{s|{w}|qm|}xwyvwtx~x{zr{{xyy||}sutw~~slhmusou~vmw}xrv{}uqw}yzwvsu~}}||znu|{zw|yxymx}~~yxzvxy}}tljt|trmlhksvxtstrvz|qt|umw}|yzx~yywlw}|~|~~{wsw{y{{{~t}ytz{v|~wx}rjsy|vopykaltwrsvvz|ztmnqort{}y}t}~wrw|xplt~}xxtw~~{{~xt|}~{}|~xsosz|}|ouz||tmglsz}}yqkn|yyvvsv~~~z~wxzou{~zy{ls}}|ywzz}~uw|}xnik{xsrxz|yuuqsw{|pqzzory{~|xzy{{x~moz}y|{}xrpvx{yxzss{xv{u{}pmux~|vpuyhcnuuprtv{}yrlnootw{z{u~{souz~wokv{yvz~{yysz}z|y||~ytoqy{sv{}~tngjpw~||yqln~{zzwwuu}|}vt}}qqy||ypoz{~~x{{|uqvy~~zyqkjx|yux}~yxvswztmt{tnuz}~zyzy~}y{xkt|zxzy|}}xstyz~w{yrt{|xyspvxlluz}wsy{jepwxrstuz|zupqrqvx{y}u|{tnrv|~{{upoy|{xz~x{x~ru}|vvtvxz}yww}~wu{}xqiinszzzyrno~~|zxwwu|~}tov{qox~}zuoy|}~|}}|~}tmoqvxuvqno{}|wx~}zzvy|vlpyvmsy~{xyx{~}zz|mqz{vwvy{~}zy}|zzzrsx}|y|~yv|}|wqqpsvx~{xwrr}zxplor{zrs|spyzvz{|}z~}ywtspv{~~~}}z|~ypopqux}~~|~wy|~z~}xqtw~yw}sy{v{yvzzvustsx~{~y|yvwuz{~yx{ynv}|{uwy~{}vtu|umktx}{uursqs{~~y}y{{vwuy|~|}zjoz~yxyuvx}|w}vy{uw}}||wyvuxz~|ztoportw|z|zutvwinx|z||ywywy{}y~wx}yuqv}xuz}|yzxz|}yyteju~vrsprtx|tpvznqw|wqkot{|usy}|yzxy~}y}{{}}z|z}~}}}qvzz}|tpmt|zvyz}vtqrpqy{}{|y|zrwvx|~}z|z}~{y{xilv~~}zvyw{~~zyz{tv|}~}|{vx}{}yuvsuvx}{{ytsu}whjt|{wz{~xtwvy~z~yw|xsmpw|xy||{z{}~zy~{ifnx}{}uuttyz{rt|uotw~ysljot|~~wtt~}{{x}|z~zz}{|}|}}yqx~~yz|yrmjrz|z~|ywttqtz|~||y|wqvvx{}{z|{}zy{xilu}~}|xzx|{|z~{tty~}~{}|xx}z}|wxvvxx||{{vtu|}mhpy}xyz}{uwxx~{{vy{{upmsyy{}|z}z{zx{z}~|||nq{zuusuz~}y}z~xrjgiku|}yqpr}tt|xxwxyyxo|tpw{|xz}ux|yxvz~}}}~vuzvttx~~~z{||}{{|~vmq{~vvz|~|yv{|zrr}zz{wpqrw}~xywx}}y~{sptw~|}wtq{|uu~~wwxwyy}ynt|~}~|vwvvslqwpjdgkrxx{xuv|wz{z{}}rls{}|vty{qpxz{{{tsqu}z{~yrpuy|z{~|yvv|qqzzuy{}x}~|{lhq{||}~~{z{yqlnpvxx{z{~xvv~~qow|vvxx{|~~{orz{y|~xz~ulf`dkt{{|yvw}y}~}zzz}yuz||~~yx{z{{{}|{kmw}~~{{x{}z}vvy}ztsty~qimwzssvx|ywsqtsuwx{yz{z~srx~z{|||y{~wqu}z~|ullot{{}|y{{{|x|{vwsrqrwx~wsx~}xxx|yutqrvxzsqy|{~xsllpu||~}y{{{~}{zy{yrw}|~~zvty{~ww~}}y|xsw~x{~wnlnrwvvtopu~vz|{wvuwwqvyy{}{xywxxxzxz|~wv|}~}||y|ttvz}zutsvtloy~xwyz|yuqnqqsuvzxyzy~qou|}y{|||xz}xsxzy}vnlqv~~~z{}~{{vxvvwvywwyzzv|~~yturtx{~wrw~xtw{|xqnqtz{{{wy}~|~{xwu|su}{|}}zvyzz}}}~}y}~|zpqw}xtzvopqvxwxtsw~{uz}zxuvuzsqy{|}|zttusuuxzy||wx~||y|y{ztux}~{|}|xxvxyrv~xvwwyusomqruwy|yyyx}rpv~}~zzy{ztxzx|{sprtzzzzwx}~yz~}vvvttsvvuxy~y|~~zyxuy{}zqsz~vwzxqklntxx{zz~{}~|xxw{vrx~~|xuyxz|}yv{~zz}}}uot{zx~{tpsu{{zyuvyyuz~|zxyw}tu|||{}~zxsstsutwxwz{~vx}~{yy~}vuw{|{{{yuuuw|su}|xxwzxurnqqqssvvvyzwpt}~||zx||}vos{||uprsy|{}zz{zy~{wqopnpqtxx}~zzzwvswy{{ssz{z{xtmmptyyzyw{xx~~{{{}{tx{{{~|zxvyyz||{v{}}zz|||tns{|~vqrsxxwwtv{{w{|{yzy|qry{||~~{xrrsrsswxy}~vx~|wz}wqsu{~~}xy~~yxvxy~wu{}{yyzwtpoqprsuxwyz{}oov||x{}}zqqx}}~zssux}|}|z~|}}x}}|~zwrnpoopqutw{}}{}yzwx{|{rpw~zxy}{vonptyyzyx{yx}|{xxy{|uy}}|}zwswvwxy}}~{w|||xxzyxols|~}~wtuv{zzywz~xuy}}{{||yrv~|{y{yvrnqqqstxxy|}|x|~~|}{x|usuw}|||y|yw{}|yxyy~wz{zxzxvspsstuvywxyy~uqv|}z|~~}tqw|z||ustv{zzzy}~}|vz~}|~{xsoqppqqtsuwy}{}}z{~}|trx|xxzzuomorxxyzy}zx|~{zzz}w{~|~|zwtwwxzz~||}}zvz}}zy|{yqnt{zwx{zxy{~}}z}{x{}{{y~{sw}{y{zwtprrrssvuuvw}}y}~~zv{||}~xtuw{|{|z||vx~}{ywwv{{|~z{{wtpqsrttvwuww|{st|~}|y||{voszzz{{uvwz}{|{|z|xw}}zyzvsnmpopqsusuwz{|}}z|~~{tu||yxzzvpoqtxwxxw{xx|||z{}w||z}{yvtwxxz{~||}}{vz}}{z|{yrnsz|xxz|wxy}}{}{}y|}}{|st{~|yz{wupqsqssuvtwx|xy~}vy}|}ysstx{z{{z~wvz{{yyxy{~|}zwsqtstuvxwwxx~vqv~}{z}|zsou|yyzzvwx|}|}{}z}ww}}yxxtqmlnmnopsrtw{}}|}}zssz~zy{{xrprtxwxyx|yvy}}{{{{x{~{|{xurtuuww{{{}~~vx|{yvyxwqlqy||}}xxy}~|}|}xy~}utz|xwxusonqprrtwvxz~zy~~~ww~~~}ysqsuxwwxw{vsv}||zz{|z}~{|zwuqtttuuxxwz{yrt|~~|z}|zsot|}}}~ytuvz{z||~z|~uv||xwwspllnmnoqtswz{{}~yru}~{z|~yuppruywyyz~vvz}~|||~{y}{|ywsruuvwx{y{|wuz{{wxywtmmu}~||~{yz|~~~~|y|~~xty~zwxvspmpopqqutvy|}y}}vt{~}~ztrstxwwxw{vrtz}z{zz{{||}zwtqsttuuyxx{|ysv~}}zz|zxqov~}||~yvwx|{{||}y{{tx~}zuutpnjmmmoosstwzz}~wtz}{z~|xspqswwwxx|zuw~~}}|y}{|{xusuvvwwzyy{|ytw||yyzxvnmt{}zyz{yz|{}~yty|xyvtqnqppqquttwy{wz}vu{~{z|ytrstxxxyy}wstz~||{zzy~}~~|yvsuvuvvyxwyzxsv~|{}{yrou|zyx}zwxy}}|~~z{|ux{vvuqnklmmnnqqqtv}~y|~xty}{y|}zvrsswxwzy}|uuz}}{{}}|{xvrsutvvxyxz{~uuz}|{xyyvrlpw~{ywzzz|~|{~wv{~zwxtronqpqrsusuwzxw||uv}}zz~{xsqrtwvwxx}vrt{~|}{{{z}}zwtwwvwwzyxz{ysu|~|~|ysou{{zx{~zvxx|}|~~zz~uv|}wuuqokkmlnnorpsv{yy|uw~}zz~zxsrsuyxyz{xuw}~~~}|{||zwurtuuvvxxwz{ysu}||zxywuokqx{yx|~z{|}{wywuwx{y|~}|z{x{xzy~||~}}|{{zx{~{yz|{}z|z~}|wvvtwxyz|~}}yyxwuw|z}~z{{w|||{y|yy}~||~}}{{}{uy|xz~~}{{xxtvwz}}~~yywvytrsvz}~~|zx{}{yxxyuwwz|~~{{~{wxzxxz}}}}~~y|~|{}}yvxyyxuvurqtwz}~|~z}}xyy}~}zvxvy{~}~~}|z{~{yyx{sz||||{|~~~y~ww{~~zwtwxyzy{}}~{~{zyx{|{{yy||{xw|~~~~zuu}~{yxxw{~~~~yxxz}}{z}|z~}zx|{xuvyy||}}~x{wtuz{~zyyxw||z}{|{{{{{~v{{v}z|z{~~~~~|{~}|vwut~~yy}zwuvxw|~~|}~}y{}~~~|xx~|}zzzy|{}}~|{xw{||}}}~~}}|{{vy{z|zxx{}~|y{~v{yxwvz|}|zzyy{xx|~|~{{}|zwy{~}w~zzxy}}~|~|}~{}y}~z~}||}zww{xz||}}{|{{{wvv}{xw{yz|x~z{}z{}}}zz~|~}|||~~{|xwsx}}{|yz}yyv~|z||x{z{}{z|~~{x||w{y|y{}}|}}~~zyuzy|~|~|z}z{~}~yx|{zx~}xyzz~~{~|xyzx}|||~}zy{w{~{}}}~||}zxwz|~{~{}|yz~|z|{}~z|w}~y{~}}y}{}}}{wyvxxy{x~zw}yz{y|~|}yxz}~}~}y|}zzz{}~~}~~zyvttwx~~~|y}|xz~~|~}zvt}}~}|w{{yzy|xx~}z{}}|}~{{}|xz|~~z~|wuuw|xz}{{}zxwxxxwv~{~~x}~~|{~|ywxxuy}|yx|~~~{zzz~}|~~~yvyzyy{}}~|~vyyqrrx}|}~}{|z{z|{z{{{ws|{}}~~~~{||{~|}}{z}~{zyttstv{~}z{|~|~{~}vzyzzy~}|||~}{~z}xwwvzxx|~~}|~}{z{{|{z{yyxxxtw{}z}}}|}zvwvzzx}|~|}}|z{|}}||z{|xsu{|z}}}~}~}}}{w}z}}zvvvvuv|}~~}~}zxwuvxyy~}}z{y~~{{||zywxzy{~|}{}}yx{~~~~yxzsusv||~~|}~xyzyuuz|}}{zz{~|~xyywyv{|}~{~|y|z~|~||~{}|vuvx|{|}}z{|zxy}}|~|y~}~~}yuuwuwy}{}~||}y|~|~}|}xvsv|yy}~~~~{{|uuv{||~y{}|{~|z{{{vw}z~}~||}}}~z|}|}|yytvuvwuz~~~{zvx}{|{~~}~}{||zyvxy{|{z|}~|{z{yz~|z~~y{zzxuvy{}~zwww|w{~||{}{}}~}~{{xtsw}|{}~||{~|}}}}}}~~}|{uvzxxvx|{{}}}}|zyzyyyy}|{}|~}~|zyxxwu||~~~~|{z}~{}~~~{vwttwuzz{~~{w|}|xx{{||}}}~}}}wyu{{x}~}}~}zxyz|y||~}~~||{ywwwwwy~}{{{yyyy}{~}||~|~{wuvywvwy}~z{y|~}~z|~wuuvvvvv{~~}{}wvzw{|yz~~}~~zvuttv~|~~zyy|{y}{}|~~}~xvtstxzyz{}{z~xy{ywz{~{}yzz|~}wxyyz{yx{}~~~zy|{|yz{{~~}~z~}|{zvt|w{~~y|yzxv~{{}~}~{|~zvxvuyyvy|~~~}xy|}|ywy{}|yuustvxy{|{uz{xxy|{{yy|~z{{xwwz}||}~~zyz||z~z~}|}~~{xupwxu|w}~|{}yzz{|~~~~{xrssuy{|{||zyyz}}|~{{{|}{{{xuwyyuww|}|{~vz|xxy}~|}~}}~}~|xtrvz{z{y}~}}ywy{}~|}~~}~}xvsyuvz~~{xz{z{~}z}~zw~}~{|z{{xtt}~|zz{~{y{{{}}~}{yxtuvtwwz~~{}zvx|~}{{{}~}||xvuxzy|}}~}~|zzzy}}zzw{~~ztxyuwsw~}~|~{yz|z~|~}||~||zzytvyy{z~~{}}{yy}||}}|}~}zyyxtvvu{{z~~}|xwwx|}~x|}~|uw{{yvwz|~~{xw{y}~~{|}{ztxwrvwxv{~}zxy}z{}~}~~yvtw~}|{{}}zyx|z|~~~}{{zv{xvwtv||}~~{x||~~~~~|{~~||{xwuvy{~}|~}~|~zv~~{xz~|xyyuvuvz{|z}~~~}|z}yuvz{{}}~~{yyy{|~}~||~}~}|~~|~~~}}~}zvxytuwvvzxz~x{|}}~}}|{}{||xu{|wuz{}~z|trsvz{~~~}{w|}{|}~zuyzvwz{z|}}~~}~{|~{v|~{{{}}|~||~}y|}xy~}|~yyz||}}||z{~y}~x{|{~~|~|}~z~|{}||}|x}{}{y{ztvxutz~~|}~{|ytwyzz{~{~}~}yyvuzvvyz||{{|{{}|}||xwxtvyy|~}}~}|{~~}|~~~{}~zzxx|xuwtu|utxwuxwtwyu{~~{wvxytwxwy~}~~~{}{{zx{}~|~~}||~~}|}~xvwxz}}}|~|~~z}~{}{{xw}|yy{zz~wy{zz~}{{~~{xz{xwvwz~~}xxxx{|}}{y|}{z|~~~yvywtxz~~{z~}{|{~{~z~~y{|{~~}|~xyyy|}yzzww~}{vy{yyvrrwuvx|~~}z{{}{vy{zy||}~}}|x{}}{{~}~~|{{{|~z~}|~wwyz||~|}|z}~{}~}||zw~z~y|zz|}z|~}~}{{}{||}~zw|{{ww|~~}}|||xzzz~}{{{y}zvvyvuwz~{~|}{|~{~|}}}~yz{z|}}yyyyz~{zzxu|}yz|}}yusvwuw{~|~}||{~~xz|{yyzz~{~x}}~|{y}|~~}}zz{z|yz~||zwxyz~}}}{~|{}|~}|{wx~z{{|}{|}}~|{|{z{|||w{}{{wy|{{z~{zz{{~~~z}~|}z~~~~yvvyuux{~}~||{}~|}{}~}~}xzz{}}~~zxwxx|{yyxu{}{{}{wuvxvw{~|~}~}}{}zz}{zxyy}{~y|}|~|x||}}||}yxz{{{z~}{}yxxz{~{~}}|}}}~~}~}~||{w{{{}}}~~|{|~{z|}|{x}}||yz|{z{|{y{{}~~z}~}~{~~~yvuwutx|~~}|{}~}~~}|~}}}wy{{~}~}zxuvvxzxxywz~|}~zxvyxwz~}}~|}{{}y}||{z{}~}|}y~~}~zz}||{y{ywy||||}}~{zxyz}~~}}}|~~|~}|}}|}{||}~||}{z~}|~|~}|{~|z|~}{y{~|||yz|~{}}|yz{}}~~|||{|}~~yvtvuty~~|}}~}~}~{}|}xy{{~}~|}~}xvstuxywyzy|~|~~~zxwzxx|}~}||{{}{~|}|{}}}|{}}~z{||~}{yy{xy{}}|~}~}{yxz{~}~|~|}~|}~||||}~zzz{}}||}{{~}}~~~|~}{}~~{y|~}{}zy{|}}|yz{}}~~|}||{}|}ywtwuvz~}~~~}~~~}}{}|}yz{|}~}~~|}~zwustuxwwyyz~~~~}{xzzx|~~}}|{}|}~|}{}~~~}z}~|z}|}}~|zy{xx{|}{||zxzz}~|~}}~}~|~}|~}|||~zyzz||}{|zz|}}~~{~}z|~~{}|yz}|}~}zz{|}}~}}~|~{||}~zxuwvuz}~}~~~}z{}{x{{~}}~}|~|}}}xvsttvxvxxy{~~~}{z|z{}~|{~|~}|}|~~~~~~z}{z||}~}}|zy{wy{|}z}|zxzz}}|~}}|}|~}|~~~}|{|{~zyyy{{~|z|zz|~~~~|~~z}~~||~yy{~}}|z|{~}}}}}|}z|{}{yvvwuy}}~~}~}{}|}yz{|~|~~~||}~|}|zwusttwwvyx{~~~|{}z}}}|{~|~}}}}~~~{}{z||~|~|~}{zy{xy|}~{~|{yzz}}{~}||{~~}}~~}~~||z{z|{wxxzz}~{||{}~~~}|{~~}{~{yz}~}{{{}|~~}}|~y{z{{yxvxwx}}~~}}y~|}|y||~}||}z{~~}{|{}}wusssvxvyz{~~{}||}||}}~}~~~~z~~}z|{}{}{|}{{y|zz}~}~}|yzz|}z}||{z}~}}~}|~}}}{{z{y{zwxxzz}~{}||~}~{|~~|{~zyz~~}{|{~~|~}~}|{xzy{zywwyw{~~}}}{{~~|~z{}}~~{~~|}~zz|~}{{|{}xvtstuxwx{{~}~}|}}||~}~~~}~{~z||}~z|{|}|{z|zz~~}~}|zzz|}z||}{~z|~~}~}|~|}|zzzzy|ywxxzz~|{|{|~}|~~~{}}zz{~|||}|}~}}|}~xzyz{yxwyxy~~}|}}y}}~{z|}~|}~~}|~{zz~~}|{|{}zvusttwwvyy|~}~~}||~}~~|~{|}}}}}~~~}~}|}||}}|}}~~~~}{y~~~~~~{||y|{}~}}~}~}}{y~|z||z}~|}}}~|}}{~~~~}{~{||{zy}z||{}}}{~{~{~~|~}z|y{~~}}{~}|z|~~}|{z~~~|~{y|{~|{zxy}z||{|||}~|{~{y~}z{||{yyxwy~~}|}z|~}}|~}||~}y{~}}x~~}~}~|{}~|yy}{}}}~|}|{~~{|}y}|}|z|{z{~|~{|wx{yx|~{~}~~z{~~}z~~z|y{yvuyxy}||~~~|~~~~|v}~z~~{~~}|~|}yx||z{}~}|z|{}~|~yx|~}z~|~z||yvyzy}}|}}z}|yz}~w|z}|wz|zy}~~|~{y}}{}~{}}~~{x{~}{x{|~~~{{~~zx{|z~}~|~~{{~}y}|~y|}{{}}}}z{xvzzyz~~~~|~~~|x}{y|}~~{}x{{xvxzx}~}~|}}~~|w~~{{~~|}~~~}{}yx{{z|}~}}y{z|}|~yw|~~{y~~}}y}{xwzzy~}}}|}{z|~{x|{|y|{z|~~}}}zz}||~}{}|~}~~wx|~|xx~}|~}}~}~|{~{yy|{|~~~~{{~}~z}{{|~{{~}}}|z|ywyzy{~~|}~}~}x|~{x|~~~}~~xz{xwy{y~}}|~~~}|{y}||}|~}~~|}|yy{z|~~}z|z|}|}~}wy}}z{{z}zyy|{|}}|{y{~~~|y~z{||z{|~~}}~{z{||~{}|||}yw|}~yx}~}}}|~~~}~z}|zz|}|~}{{}~~{||~|||yw}}~{~|}}{}~||{{}{~~{~{{|}||{~~~~}{}~{{{|}~~|z{}{}~|~~}}|}}||xz||~}|z}zw{~~}}}|z||{zy}}|~|}{{||}|zyw{}z}~|xwz||yw{{xzy{|}~|{|~xx|~|{|}~~}}}}~}zyx}~~}~{y{yxzwtx|uw}xz}|~{|{{}||~}}|{{{{|}|~z~zv|~~~{~~v{~x}~}|zxzyxwy{yyxy{~}|~~}zyz|}}|||z}}{}|}||zz}{}|{}~~~||}y|y{~~~~~{{wx}xyyt{~}|{|~~yyz{}|{~~}~}|yz~z|{|~||~}~|~y|ww~{|~}y{~{vywzyy}z~~||z{|yzz{}~~||}zy}~~~||zz{{~~~}}xz{t|yw}xywz{|}|zzyx{}}|~z}~~|yy~~}}{x~}z}}}~}z{y{|{{|~~}~}|{zx|xzxuz}}~|{}|~zzx~{{}{}}|z|||~||~}}~~}~||xx~}yy~z{vw}z{x{}~}~{}|y{||~y|{|~~}~{|yyxz~~|~~~~}}}x}{{|}||z|yzzzx}{~~{}|x}y}z~{{{}z~~~{~~~~z}~}{~|~}}|}{{{{~~zzzy{xvwxy}~~|}~|}yx~|~|{}~{z||~~}z~|~~~}||}~|zxvz{~|}|}vx}{~}|zy~~|x|}{||~}|~~{{~}}~~~}~~~|~}{}}~{}~~~|{~~|{zw{yy~{~{~~|{{xy||{x~{|~|}~{}}~xs}~|~|{}|~~~|{z}}}{|||y||z~|~}~}~|~}z~}{x~{~~zrx~~{|}}~zzzy|}}|}z|}~~}}~~y{|}}|~~~}~}{|z{|{~}~|}}|}}xy{yu|~{}~~{wy{yyzxxxvz}}~}}}~{z||~}~}{~~}~{~z}~~{|{{}|~}~~{uuyyv|}}|z{zvyywvwvy|}~{~~}|yy{~~}}z~}}z|}{~{}|{||~}~}|usxyv}}|{|yyyx{yvwxvx{z~|}||zyz}}{~~{}~|~}z}}||{}|}~~wtxyv~~~|~}}|zz{z{zwxzxxzz}~}}}}y{}~~~|}~~}|~~~|z|}|~}{}|}~zux{w{~~|}{yzzzzwxzzyyz{}~}xy|~~}~}|}}~}~~|z~}z|}|}~|}}~~zvy|wz}~~|~zyzzyzwxz{zzz|{}|yz}}~}~}|~}~~|}~|z~}{|}{}|~|~}yuz|w|~|~}~}zz{z{zwxz{z{||}{~}yz|~~~|~~~~}~|z~|||}~}}}|{{|{w{y|~}~~zw}|x~{{||~||}}}}}~|}~~{y{~~~~}}{}|~~~~{{|z{~|}}~}~||~|~~wx}|z|~}|~~|{{}}~~|}~}||~{zv~||ywz{z|}|y{~~{~||}~y{|x{~|||~~}}~~~~|}||}yx~{~~w{|{zuvzz}}}{{|z|}{~}{|x||wx{zy{~~||||~|z~}}~}|}~z~{~~~~zy}~{y{~|yz}{}{}}{|~}~||}|~}z||~}}}|w{wz~~~~|{{y~}||~~}|yyy|||}zzvuzwy{{y~}zzz|ytux|~}~|~y{x{}}|||{~~||}{~}~ywz~{{~|y|{xuywsy|z|zy~|~}~}}}|yuwy{{{|{y~}|~|~|wwxwz}{}{}}vsyx{x|}}~}~|y||~{|~|z|z}|{~|~|z{}}}{|}~~~}|~~|}~yzw|wutssx|~|y|~y{}{||}}}|~~{vt|}~}~z}~}|~~~y~~}~}}{x}xz~{wzywvqw}~{~y}xyxvwy{|}~~}||{|~~z~wy{||{}|~{|}{wvtwuu{wy{~}~|}}|y{~}{{{{|~~||z|~|z}~~}||~|~~|~x{uux}{{~{x|||~~|z{||zy{z}{}z|}z~~}~|x~~}}~~{{{yy{|{|{{x|{vzxsx~}~~~}}~~{~{}}x~}~z~z}|~}|~z|}z}}}}||yw|}~}{{|wuutvtyxwy||}{~~~yx~}}}}z{~y}|~|{~}~||zxyxzux}~}}{}{{xyzx|{}|y}}{zxy|wy{w}z|{|~~}|}}~~~~{yz}yx||y}~{|yxvw}}|}zywz{y{y|~~|}{yz}{|}||}~}}}||zz{~}}~~||{wxz{~{|z{{z|~}}{w|~}{~}}}~{~~yxwx}||{|~{}{|}~}~}z|zwwxxuyy|~{z|y|zt|z|~zx~~}}xyy{}{{~~~~{||~}{~~}{}}~~zzzy|{{{|~{y|~yww||x{yzx{|z}~}~||}}|zy|~}||~~}~|}{}{~zy~}z{~xvwvz|}||}z}zyzy}|}|{}z|~~|}~{wy~{{|~{{~}|~~}~~~y~~}zz}~y{xv}}}||{z|{}|wzxz}}{z{}~}|~|zzyyy~{}|~~}}{~~~}}}yv||y{||}}}|}~}zzxw|~~z{{|zwwxxy}~}}{~}~}}~}y~~~~}{zy||vy{xz}~|zy|{x|~~|x}~}}~|wwx~}~z||~~|}~~}}~}}||}}}}|~z~|{||y{|y{~~zy~|wy~}|}}}}|~~~|{~~{{zz||}~}~{~}}~~}{}}}}~~|{|~||~}{ywv}zy~|xy{~~~~~~}~~}{|z{}}~{~~~~~||~|~~}}~|z{~|zxyz{}{xxzxz|~{}|}~|}~~|}~~}y{|yw}}|}|~~{~~~}}~}~{~~{|{|{z{}}z{|xwyy~|{z~{}}|{}|zz{yz}}}~}}|}~~}}}|}~}}||~|{|}{xy~|}~{||}{||||~}||}~}{zzwy~~~~|}||}}}}{z{yy|~|zy|zxy}}~~}|~||~|z~~}}{~~||{yx|~}~~|}zz|~|y}~}~}|~||zzzzz{~~~~}}{|}|z|~{}~||{y{||}~~~~~~~~~~~}}}{{}}|ywx{~|}}zz}~~{{~~}}|~|~}zyzwwy||~|z}}~~}~~}|{{y||}{{}}|{{yz{{|}~~~}|z{~~}}~zz~}|y{{z{|~~{~~}}|~}|||}}|}}~}|{xz|}}{~~}{|}~~~}~{}yxy{}~}|}~|~|}}}}|~}~|{z{{{{z}{ww~}|}~}{|}}~}}z{zxxxy|~~}~~}~}z}}}~~~|{|zz}}~|{{~}z{|{|}}{z|||}~~~~~}||{||{yz{z}~~~}}~~zy|{}~|{~~~}|xxyx{}|~}|~|}}}}~}{yxxzy{~}}~}~|}}~{~|}}|}{{{{{{}|~~}|zxz}|}~~}{{~|}~~~|~{yxwwz~}~~~~{|}~}~|}~~}~|{~|||}~}{{zz|}|}~~~}{||{}}~{zz|}zx{~|z|~~~|}~~}|~~{~~{xyz|}z{{{{}~}}{}~~}~~~~}}}~~|||{{yyxzzy}~~|{}~}~~|zyx{|{|||{|{z~~{|}z{{~~~}~|}~~~~}~}z{zyy{|}~||||}~~~~|||{{~~{yz~||}zyz{|~}}}z{}~}~{xwwz||}}~~}{~~~|}}|xx~~|{|~~|{|{z{|{}~~~~|{}~~~{z{|z|{zz}|~}~}{{{zz}w{y||~~}}}|yyz{y~~{y{yxzz}~{v{{yz|~y{y{~~}|{x}|||}zywwxw||}zzz{{|zw{|z|||~|zxz|}~~~z|xy}yuux|{yz}}{z|||}|~}}}~|{x{{z|~}z}z}||y}~~}|{{w}{}~xwx|}xxtyxxz{~||}}{yz~~{}z~}wx{~~~|}~}}{{zzz|~~}~}}|w}}~{}zxvuvwz~zy|}~{}|~{{{|~}~}~zzzvyxxy}~}~|}}~||z{zzzyxx{~zytwxuvyxx{~|~}}}~~{}}|}z{||||{{|yy~|~}|{{|x|}}||wtx}|{zzw|~~}~||~~z{|z}}}}~}~{}||~}~|w{~~~y{|z~~}y}y}|}|zyyxtwtuty~{x{zw~~|~}|}~}~~}|{z}yyxww|~}~~~}~~|~~{yz}}xxyurx{~x{z|~}~{{{}}}~|{{}~~}||~}{{{{x}|~||{{|~{|}{vt{{}zy{z~~}|~|~z}~}~~x|~~|}~}~|~}yxvzzz|y}~~|~yy~~}|~}z{zxyuwuttw}{z|x~~~|~~|~}|~}||~}xy}~~~~|}{~~~}{{yzuy|~}}{|wz|wxuu{}{|{z~|}|}}~{{{}~}~~~z{|z}{x}}||~~z|~{|}|~}|{{~{wvstvuwuyy~|}|}||~~}~||}~{z}~yzyy{z}{}~~{vt|zz|}~}~}}~wyxy~}~|~}|||{}}{w|{z}y}}}~y}~y~~|}{~|zyvw{wyz{~~{{{z{}zz}~}{}~{~~~{zw|z||~~|}~}{}}yy{}}~~||}zxx{v{}~}~|{}wx|{y}}~~|~~{~z||z{xx}{}~~~}{~}}z}|}{|}~z}{wxwyz}~}|z}xxz|z|{z}y}~~|z~yzzyx|y|||~}}{z{z}~~{}~{wvwwxz~{{zxx|~~}{}z{|~|}w~|{zzyz|{~{~}{}|z{}~}ywwxxz{|~}}|xxy{{{z|z{|}{{xzz{|{{z{~}}z|}y}y~}~~|zxuux{z~|y{xzwy|{~zx||y~{ywyyzz}}|}~{}{}}w}~}|{}|zvuvx|{{~zyzz}z{z}~{~}{y}{{}xwwz}|~}~}~z~}}~~{~~}~~}}{xvutx|}zwyy{yy{~~|~}|z~xz~}{{z{{z~}~}}}{|~~z}~}{}||}xvtuxy{|zzz{yy|}}{~}~~~{z|~|z~}|yz||}}~}||}}{y|}}y|~{~zxuvxy{}~~~yzx|zx{}~{z~~~~~~y~~~y~~{y{}}|}}|{~|}}}{~~{xytux{}~~~}~x{x{{y|{z}~~y~}}|w}~{z|}}}~}|~}}}~||}~~{~y{zttxz|~~{{xz{z{~||~~~~~||x|||w{{}~}}|}}~{|~~|{||}z~{y}{{x||}z{||~}yy~|}~}{xvx}~y}{|}{~|}|}}{zw|}zz|z~~z|}yxuuvwv}}{yyz|~z{}}}}|}}~y}zz}{|{{}}{~{}|}|}}{y|}}{xzy~}~{{{{{|{{}{x{}}~vu||}}{{z|||w}~}xx{~|zz{}~|||yzwvz|~~|~~{}{~~z|x|{uvzz{~~|{{{z{~||~{|~~~}}}{z~|{}}wz}{wyv}~}~~{}|||~~|}yy~{x{~|{}}|zz|z}~~}~~|yz||{~~}}z~~}~}|{yy{z}~}}u{~}~|~||z~~{y{zzz}{{}{{||~}~zx|~x}z{~~|y{u}~zz|{{|zww{yz|}~}|}}}||{~{~z~}z||}x}~|~{~|~~~|}zzy}zz~}~|}z~{~~}}}xx}~~|~|||z{{|}|}}|~|{y~~}}|zz~}}{~z}|~}~~~}y{||zzz~||yyy|wwx{{~|~~zv}}|{z||wy{v{{zy|~~}~||}~|y{|~~xx||~~~~y{zz{yu{~}}||z{w{yz}}{|{|{||~y}~z~xyzzzy|~~|zy{{z}y{|}~}~{zy||}|~~z{~}}{{~}|~}}~~xxx}z}|}|{{x}}~~~}|}~}z|~}xzx{z}{vzyzwwxz}~~zw{~~}|yz{y}}}|}|{~~}||}{z|~~~|w|}zx{z}z}~~z~||z{yz}|}yxwzyz}~}}{}|}{w}}~~}~~}~~~~yzx~|~~|~{z|}}~}z}|~}{yswzy~}}~~||~~}~~y||z~|~}z|~~}}~|{|~~~y}~|~~~|~z}|~}{yxvvy}~|{uuz|~}~y}{{{{xwy~y~~~~}yyx|~|~}y|~~|yz~~}{~y~|{|}|~~}{z~~|}}wz}{xyz{|~}}~~}~~{yxuwzy{z{yyy~~}{z~{{{|}~~z||{zxz~~~}{{|~}}}}}|~~|||}wwz}{}z|~~|~~~~~y}}|~zzyyz}}z~~}~z{y{{{zwwsuxy|}xx}~z{~|{{~~{zz}y|{x|}|}|{|zyzy|z|~}}}}~}}~}}{}~|wy~~}}yz}}|}}|z}}}}~|{z{zz}xwvvz|~{x{~xx|y|~~{z|~~}xxz~~~|~yz~~|~}~}||{x|}~~{z~~}~~}|{}~~~~~~}|||}zywz}~~}~{}~~|~}}}y|z|xuutww||yx{~|y~{|{}||{{}{z{|xv~}|~{|y{{z~{~~z}|~~~}~}{{}w|~~}{{}|~|{{{|{z|~}z||z}zyvwy|~}|{~|x~{wyz{}{zz|~|z}yx|{~}~~{y{z}~}{~~}|yy}}~~~~~||~{~zz{}~|}|{}}}z{|y{{{|zwuwy{}{|~{{z{~|}}|{||zy}}xw||{|~~|yz{|~}|{~|{}|~~~~~}~{z{{}~{{}~{{z}~~z{}~~}zz|z|{xwx{|}~}x~zzz}~~}}}{{{}{zz}|y~~}|}~~}{|zz{|~~}|~~}|z|}~~}~}{z{|~zz||~||{}{}~z{}~~|{|yzxwvxz|~|}~{|||~~~~|{z~~~{zz|zy~}|~~{{{}~~|||{~}~~~}~{yz{~{~{}~~}{{|||}{}~~~~|{|{zzyxy{}~}{|}yy{~~}}{||yy{~~z~~}~~~}}|{|~~}~~}{z|}~|~}~}~{z|{~~{|}~|{{}}}{}~}|}|z{zxy{|}~}|{|~~~}||~}zy{}~z{~~~|~}||}~{z|~|{}}}~|}}}~|zy{~~{}~}}~|||}~~}~~~}{}}{|{z{}}}}}|z{~~}||~~~zyy|~{}}|}||}|~|{}~~{z{||~|}}}~}{z|{|~}}}~|||~~~~|}}{{{zz|~}~~||}}|{}~~{zz|{{~~}{}~}~}~~|{|~~||}}~|~~|~~}{z{|~{z~~}~}~~}}~~~~}}}|{{{{}~~~|{z{}~}{{}~|zy{}}{~}}~}}~~~||}~||||}~|~|}~~}|zz|}|y{~~}~~~}||~~~~|{{z{|~}~}{|~}}||}~~{zy|~}|}}~~}||}}|}}}}||~~}}~}{yy{|{y{~~~~}~}}}~~~~~||||}~~}|{|~~}}|}~~{zy|~}~~~}}~|||~}{{||~~||}~~~~}~~~~~{zy{}|zz~~~~~}}~}||||~~~~||}~}|||}}{zy{}~}~}~|{|~~|||}~}{~|}}}}~~|}~~~}{yy{||yz~~~~~~~|||}~~~||~~}||}~~|zyz}~}~~~}}~}|||}|||}~}}|~~}}~}}}~~}}~}}{zyz|}{y|~~~~}|||~~}||}}|{|~~}{zz|}}~~|||~}||}~~|~}}~}}}}||}~~}~~~|zyy{|{yz}~~~~~}|}~~~}||~~}||}~~}{zz{~~~~~~~~}|}}}|}~~|~}|}|}}~~||}~~}}}~}|zyy|}{yz~~~~~}}}~~||~~}|||~}{zz{~~|}}~~}}}~}}|}~}{}}|~||}~}||}~~}}}}}|zyy{|{yz}~~}}}~~||~~}|||~}{yz{~}}}}~~~}~~}|}~~|}~|~~|||}~||}}~~}||}||zyy{}|zy|~~~~~}|}}|{||~~|zzz|}|~~|~~~}}}}}}~||~|}}|||~~}{||}~}|}}}{zzz}}|yz~~~~~}|}~}|{|}~~}{yyz}}|~~}~~~~~}}~~||~|}~|{{|}~}||}}~~~}||||{zzz{}~|z{~~~}|~}|{{|~}{zzy~~}~}||}}~~~~}|{{{}~~~|zy{}|zz}~~~~~||||||{zyyyyyxxxy|}|z{~~~~}~~~}~}{zzz{}~}zxx{}|}~~}}~~|z{~|zxwwxz{|||{zyzz{{{{zzzzzzzz{~~||}}~~}~~|ywvx|~~~zyz}~|{{{|~~||}~~}}~~|}~||}||}~~}{{|}~~}|{{|}}}}}|||}}|}~}|{|~}|}~}||~|{}~|zz|~~~~~~~~}|{{||}~~}|{yxy{~}~~|}~|zz{|~~{zzz{{{zyxwwxyyyxxwxxxxxyz}~|z{~}}}~~~~~~~~}|zxwwy}}|~~|{{{|~~}||}~~}}~~}|~}{}~|{{|}~}||}~~}|||}}}}}||||}|}~}|z{|~~~||~}{}~~|zz{}~~~}}}}}~}|{{||}~~~}{zxxy|~~~|}}{yyz{~~}||}}|{zyxxxyyyxxxxxxxxy{}~|z{~~~}||}~~~~~~}|zxwwx{~~~}||}~~}|||~~~}~~}|}}||}{{{{}~~}}}~~}|||}}}}}|||||}~~|zz{}}|}~||}~{zz|}~~}}}}}}~~}{{{|}}~~}|{yxyz}~}|}}{yyy{}~~~~~~}{zyyyyyyyxxxxxxxy{}~|{{~~}~}|{|~~}}}}}}}|zywwxz~~}}}~~}||}~~~~~|}~|{}~|{z{{}~~~~}|{yxwwvvwwxy{~~|{|~~~~}||}~~}|{{{{|}~}|{||}~~~~}|zyy{}~~||~~||}~}~||{{|}~~|{z{~~||||}}}}}}|||{||~~||~}~}}}{|~}{{}}|{|}~~~}||{{{|}~}|||}}~~}|||}~}~}{{~}zxwx{}{zyxxwwwvvvwwy{|}{zz}}||~~~~~~~~~|{{}|zy{~}{{{{}}~~}|||||}~}|}}}}}||}~~~}|{zyz{~~}}}|||{{{{{{|~~~~~~||~~|{zz{{||{{zzyzz{|}~|{{{{|}~~}||}}{{}~}{{}~~}|zyxxy|~}|{zyxxwwwxy{}}{yz}~|{|}~~~~~~~~~~{{|}|zy{~}{{{|}}~}}||||}~}||~~}~~}||||}}}|{zzzz|~~~}}||{{{{{|~~~}~~~~|{{{|||{{zzyyyz{}~~|{{{{|}~~~~~}|}}}{{}~}{{|~~~~~}|{yxxy{}~}|{zyyxxyz|}}{zz|~~|z{|~~~}~~~~~~~{{|}|zyz}~}|||}~~~}}||}}~}||~~}}~}|{|||}}}|{zzz{}~}}||{{{{|~~~~~~}||||||{{zzyyz{|}~~}|{{{{|}~~~~}}}}~~|{|~~}{{|~}}}}}|{zyyxy{}~}|{zzyyz{}~}|zz}~~|zz|}~~~}~~~}|||}~|z{||zxxz||{|}~|zyz|}}{z|}|{{{||||{zyyyy{}~~zzyx{~~}}}}~|~~|||}}}}}||{{||}~~|{zzz{|}~~~~~}|}~~||~~}{z{|}~~~}}||||{{zyyy{|}||}~~~}{{|}~}{{}}{z{|~~~{{~}}|{zzyyyz{|~~}}||}~~~~~{yz|}|{{|~}}~~~~~~}~~~~}~}{zzz|}~|{}~|zzz|}~~}|}}~~~~~}}|{{{{{|}~~}}}~~~~~~}}}}~~}||}~}~}|{zzzzzzzzzz{}~~}}}}~~~~~~}|}}{{|~}~~||~~}|{zzz{|}~~~~}|{{||}}~~~~}}~~~{zz|}}|{|~~~~~~}{zzz{}~}|~|zzz|}~~}|||}~~~~~}||{{|}~~}}~~~~~~~~~~~~}|{|~~||~~|{zzzzzzzzzz{|}~}}}}}~~}}~|{|}~}~~|{|~}|{{{|}~~~~}||{{{{|}}}}}}}}~~|zz|~~|{{}~}|{{{{|}~~}}~~~~~}}}|{{zzzz{}~~~}}}}}||{{}|}~~~~~}}}~~~}}}}~~~}~~||~}{{{}~}}~~}|||||||||}~|{|~{zz{|~}|}~|zz{}~~|{{}~{z{~~}||||}~~}}}~~~~}zwwy}~}}}~~}}}}}}~~~~~~}~~~~~~~}}}~}|{{{{{|}}zyyz|}~~~~}}}}~~{ywuvxyzywvvx}~|}~~~~}|{{{|}~~~}~~~~~~~}~}}~~}|{}~~|{{{{||||{zzyyyxwvux{~~~~~~~zwy~~~~~~}{ywuuuvx{~|zyy{}|zyyz|}~~}}||{{{{}~||~}{yyy|}~~~{xutttvx{}~}{zyxwwxyz|}}~~~~~~~~}|{zyyz|{zxx{~}||{{|}~~}}}~}{zyz{|~~|zyyz{zxwwxz{{zyxwwxyyz{{||}~~}{zyyyz{}}~~}||||zyy|~}{zzzyyyxwvuutttuvvwxxyyz|}~}|{{|~~}}~}}|||}~~{xvtstux|}|}}{yvutuvwxyzz{{{{{{{zzyyyz{}~~|zyxxyzyxx{~~}}~~~}{yxxy{}}||{zzzzzyywwwxz||zyy{}~~}|{zz{{||}}}}}~~~~~}||{{{|}~}||||{ywxz~~}{zyxxxyyzz{zzyxxxxxyyzz{{{|}~~|zz|~~}}~~}}}}~~~|ywuttuvxz}~~}zyxxz{||}}~~~}}}|{zyyyyz{|~~|{z{|}|zyy|~}{zxxwwxy{}~~~}|zzyz|}}{yyz|}~~}}||}}}~~~~~~}|{zzz{|}~|zxwwxyxvtuw{~}||||}}||{zyxxxxyz||}}~~}||~~~~~~~~}}}}{zxwvuvvxy|}zyyy{|~~}|{{{{{|~~}||~}|z{~}||{|}~~~~~~}|{zzzzz{|~~}||}}{{}~}||||}}~~~}|{zzzz{|~~~~}{yyyzzzxvvwz~~}|{{zz{{{||}}~}}~~~~}|{{{{{|}~~~~~|zywvvvvwy|}|||}~~}}|||}~~}{yxwxy{{zyz|~~}~~~~~~~~~}||}}||{{{{{|}~}}}~}}~}||{{|||||||}}~~~}||||}}}||}~}{zyyz{{zyxz}~}||||||||||}}~~~}{zy{}~~~}}}~~~~~~}|zyyxyyz|~}}}~~~}|{zyyyz{|~~}{zyz{||{yyz}~~~~~~~~~~}}}}}}||{{{||~~}|{{|}~}{z{}~~~~~~~~~~~}|{zzyyzz|}~~}}~~|zyxxy{{{zyz|~~~~~~~~}}||||}}~~~}{|}}||||}}}}||||}}~~~}|{zyyyyz|}}}}~~}|{{{||}~~~|{zyyz|}|{zy{}~~~~}}}}}}|{{{{{{|}}~}|}~~}|{{{|||}}~~~}|{{zzzzz{}~~|zyxxxyzywvvxz}~}}||||||}}}~~|{|}~}||}}~}}}|||||}}~~~~}|{zyxwwwxyz|~~~~}||{{{|}}~~}|{zyyz|}}|zz{}~}}~}|zzz{{{{{{{||}}~~~~~}}~~}||||||}}}~~~}}|{{{{|}~~~~}}}}~~~}|{zzz{|}{zyy{}~}}}}}}}}~~~}|{|~~}~~~~~~~~~~~~~~~~~}}}~~~}{zyyxxyz{|~~~}||{{||}~}|{{{|}~~|{zz|~~~}}}}~~~~}|||||{{{zz{{{||}~~~~}~~~}}~~~~~}}|{{{{|}~~}|}}~~|{zxxxxz{{zxxy{}~~}|}~}}}}~~~~}}}}}~~~~~~~~}}~~~~~~~~~~~}|{{zyyyyzz|}~}}}}}}~~}|{zz{|}}|{zz{}~}||~~}{zzzz{{zzzz{{||}~~~~~}}}}~~~~~}||{zzzz{|}~~}}}}}~~~~|{zyxwwxy{{zyxy{~~||}~}|{{|||||||||}~~~~}}}}}}}}}}}}}}}}||{zzzzz{{}~~~}||||}}~~~}|{zyzz|~~~}||}~~~|{{|~~~}{zzzz{{{{{{||}}~~~~~}||||}}}~~~}}}}}}~~~~~~~}||||}}~~~~}|zyxxyz{||{yyz|~~~~~~~}|}~~}~~~~~~~~~~~~~~~~~~}}}}~~~~~~~~~~}}|{{zzzzz{|~~~}}}}~~~}|{{{}~~~|{z{|~~~~~~}||}~|{zz{{{{{{{{{{||}}~~~}}||{{||}~~~~~~}}}}}~~~}|{zyxxxy{{{zyxyz}~~~}}~~~~~~|{z{|~~~~~}|||||}}~}~~~~}|{{{{||}~~~}||{{{{|}}~~zz}~~~~~~~|{|}~~}}~}|{|}~~~~}}}|{|}~}}~~}}}}}~~~~~}}~~~~~~~~}}}}}~~~~~~|{|~~}|{{{{{{|||}}~~}|{zz{}~~}}}}~~~~~}}}~~~~~~~}|{|~~}}|{{zzzyyzz{{||}}~~~}}}||||}~~}~~}|||}~~~~~~~~~}}|||}~}|{{{|}}}|{{{{{{||}}~~~}|||~~~~}}}}}~~~}|{{|~~}}|||{||||}}}}|{{{{}~~}}~~~~~~~~}}}||||}~~~~}{zz|~~}}||{{{zzz{{{||}}~~~~~~}}}}}}~~}}~~~}||||}~~}||{{{|}~||||}}}}}||||||}}}~~~~~~~~~}|{{{|~~}|{{{|}~~}}}}}}}}}}}|{{{|}~~~~}}}}}~~~~~~~~~~~~~}}}|||||||}~}|{zz|}~}}||{{{{{{{|||}}}}}}}}||||}~~~}}}}~~}}}}}~~}}||{{{|}~}}}}}}}}||||||||}}}~~~~~~~~~~~}|}}~~}||{{|}~~}}}}}}}}||{{{|}}}}}}|}}}}~~~~~~~~~}}}}}~~~}|{{{|}~}}||{{{{{{{|||}}}}}}}}}}}~~~~~~}}}}~~~}||{{{{||~~~}}}}}}}||||||}}}~~~~}}||}~~}|{{{{|}~~~~~~~~}}}||}}~~~~~~~~~~~~~~}}}}||}}~~~}|{zz{{}~~~~}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}~~~}}|{{{zz{{|}~~~~~}~~~~~~~~~}||||}~~~}||{{{{|}~~~}}}}~~~~~~~~~~~~~~~~~~~~~~~}}}}|||||}}~~~~}}|{{z{{|~~~~~}}}~~~~~~~~~~}}}~~~~~~~~~~~~}}}}}}~~~}}|||{{{{|}~~~~~~~~~~~~~~~~~}}|||}~~~~}}|||||}~~~}}}}~~~~~~~~~~~~~~~~~~~~~}}}}||||}}~~~~}}|{{{||~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}~~~~}}|||{{{{||}~~~~~~~~~~~~~~~~~~}}|||||}~~~~~~}}|||{||}~~~~~~~~~~~~~~~~~~~~~~~~}}}}|||||||}}~~~~}}||{{{{{|}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}|||}}~~~}}}||{{{{{{{|}~~~~~~~~~~~~~~}}|||||}}~~~~~~~}}}|||{{{{||}~~~~~~~~~~~~~~~~~~~~~~}}}}}}||||}}}~~~~~~}}}||{{{{{{|}~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}}}}}~~~~}}}|||{{{{{{{|}~~~~~~~~}}}}}}~~~~~~~}}}}|||{{{{||}~~~~~~~~~~~~~}}}}}}}}}~~~~~}}}|||{{{{{{|}}~~~~~~~~~~}}}}}~~~~~~}}}|||{{{{{{{{||}~~~~~~}}}}}}}~~~~~}}}}|||||{{{{{||}}~~~~~~}}}}}}}}}}~~~~~~}}}}|||{{{{{{{{|}~~~~~~~~~}}}}}}}~~~~~~~~~}}}}|||{{{{{{{{{||}~~~~~~~}}}}}}}}~~~~~~~~~}}}||||||{{{{{{||}~~~~~~~~}}}}}}}}}}}~~~~~~~}}}}}||||{{{{{{{||}~~~~~~~~~~}}}}}}}~~~~~~~~~~~}}}}}||||{{{{{{{||}}~}|}}strtoqwwsiloutywttq{}po~noxvia]_edlr|qes~{znlw{tkah|tywlwuzzrlvypx|~wuvsvkiqwpjfogjulhwwoxet{wtvwnj_eja_fwtvxryy{{ntu||xws~l|ttqhfp}svp{||w~x{scg~ypwnhnjuu|||wugp~t|z}wz~ZWYb|y~|hpylghaknllrtdnovk^srutrkjiff{pjcrrqw~q{~~zlov{q~xrsqo|}vr~wzzmyhhmsv}|tmx~uvrozw|tv}x~||{rsmhkegmv~zqnlmoYoeYXcfaoymsnornxgsxphgy|jxqomfjfzxsywr~{qyprin~}zv|~}spibltu}psogw~vm}~zx|{s{xqw}~{tw}`bgahttxrvkg\kwjjpkkirglsdr~rgpqo}mn_jbXcoox~zvs{lswwwzq~vw}uu|xrxn{fhnw|}~zwnxqwzot|{w}{|x}~x|i`ZRtrl|~nl|qosph]l_dhupryvcrqegtwbidcwve`ozcl}zp~rtgtwoyzy~~v}mgptcYz|zqlwwxy~yvyxzso{~pzw~vbnmckksousugcj^pvm]gyib{uuctzydr{ymsk\gdhe~{yz}o|nplvyxsu}txwpvmpnoknwz~{u}por}uu~~zmy{z}}vt~~w~tepuWUcfq}uhwuqopnflsneuwqt~ilonhnsa_\ns][\j{ov}~yq|uz|}}tnjtljtsvlytvzywww|q}yyxztunw|}u~}|x~u{tfefgin|syuqkisg`lsmmxwhh{zsqluojsmfjldoy~~yw~vpzilvgq~t~{msrmaql_cusw{~{rsytnzx}|~{zu||~xpnf`ddZc]|~}zq|tiyyjfojpotomxxnsxliyvgn``uk[Tnwj}ww~wuv}{w}ytwsxregsubs~hvxw~syvwsyy~}ws||y{{kx~}vsv}v{p^jtdn}z|xxnxl}n^jxp|koqwsdswdcke^lorp~xzqypvuoiu~|ry{yzq|igicmkzv~}xp{}{|s}{w{x{~{omuoVgaXcll}|ykzwuqmzkowtnnsdnmlprlifnp`djzvu~owy{~|{|wh~y`bln``qvq~~wqx{}t}pzy~vzvus}w||w}~xt}syp`spipbfmlt}uyngw{okk}tv~se||yumsehqcfiky{w}|x~~ujxuxwxr}ym|vthnslitck|~vylvrzpu~utv{wsw~{zyxu~vuzuwjbffeooliuxyy}uq|}yxsss{oimqhqud__Vnyjfl~}{t|u~t|xyzvjpnx~kWempgs}t~u|zysr{pmq~rw{~}{~z{z|mpzvu}qafimffi~v||rup}|hg}ok|{vnqvzwkzsbkxvw|wzq}zpwsx{{y|wyzuuoginmd`ial~xr}{wzs|zy}z}}~z~{|~ostpm}jddf]dcj^p|xv{}pzuzr}wwrs~wr{nbukm^^fxyyy~{{|y||z}}yxomvxig^j}lemsrxwyx|x{sqs|hu{qp{ow{t~ypwmklkoryrjyxdVjyyzvq{mpxo|rxy{ldxxlgcqulywvztutloxmy}|}~y|rkv~osigfetjl~z}zss{uzz|tx~{xu~zy|{u|}~yuxxvxxasfikbaY`mluy{{xuzyx}zxopvwyx{ktwqmuhjlmw|{}|~}||tzzs}jkrheedhmd\t}y}wzu}~wvxyyx||vzums}ztytpesymjvtf^_lq[akv|z}v~zr|~|rq|qnzstaqpkxyty|ux{u|||{~zqvpjx{}vloqqorpg`mxww{zsxzy~trpzy{zpy{~{{vo{~to|v|uxnjvtksf`kpkggejs{}z{zz|p{qu|tikfc|oqie{|~t|yu}u}~xorox|vf^chzhamvzwuvwwvohs~zo~{v{}wx{xw}rzyzpjnw}emskkYdf]]muqntu{t}}tzsxz~wr~rnst}s{y{~y{v{y~xxyrxpttxmlkrscdehmewqw{~s}x{}u{zzurx{}{~}z}|q~y~vyrvslpurgcfYjl`nfgkmuox~{wzyx|ulvibppuwop~~utyw{yxyvtumpdstqibboyvzzpyzliq|~yxyqpl}zxy{v{|~{tpgnzymkdovnpkadcnpa^tx~~{t{x}}vvxuhfpuruj|yzxrwnr|||yxsejy~umbiomnhkin~y~{zqv{wrs|wsvx~{t|x|wz}swrtxsnkuuqhncbl[[e_mupijrx|~~y}}{zu}zlkurxv|zu||{{m{usmeflhlkdd`mp~y}tyy}xxuxsv{wx{xqq~~~usuwppseqjkjhhlkceuch__usqw}vnxuwjkuutx~}zz~~zpy}{rnesxdhjnrnofokp{z{|x{zqwrnzsxzplst||{zvtmvrfiqxrkkjqqppi]\hdjrdpzr|{|{|w~xtwywrxz|mutky|wvxu|tv|wqvzvmpp|{|uxrsy~{oiicu}p}jlx{{|||v~ztv|xp~wrmkxqmv}z}tlaspab\`^V\ZYgz{tkn{yxqo}rz{{~}y~r|zzumxl{prlfkqpefgnyv|zwwz|}{}~uzrifVfwtppkqkfdhukwxo}upxstpwlginpcph^rjnwz}|v}r~vw|zqmg]or~sda^Xocdqflosplwv~xzz}y~}r}ryyxvv~uqqamskv|v}rimuv{}}{{uzqw}z|sui^_Pgqoyw}yupywrrkrlid_prpxp|yxtwrkx~{~xwy|hrpx~~}oeeqsbpbdztvtmkq|uuwpzt}}~qlsxovlgmeimhkyskfhkqprqpuzjnojxu|~|zzgif^~~|qmxlou}tttxwpwmovx}znghhmnnjchr}{y}zjy~{zz|~|z{xz|~r^qefwuwvx{v{qijghftjgwrqnbkstuzwu}vw{|ppvuv~yz~|xs}}yvwxsx{smzvstxyzywuotm{{vzsjzxt{xz|~~tljkmwsx}|rtunrvhkmfet{u{}rvnx{w|ppgtwissx~yw{qylvzqwxvswstovxvupk~tq~qmrzvmpiouw{{ww|rlxmrxurs}sx{rxgmy}yqx~xqotm}qq~qtuipyppvglsqka[cfu{{wqwyusjsmapjbp}~zzw|y}y{wxwplpp{usoiltmqrmo~zwz}zjxu|{}}tunvwsymrswxptuvy{xgqsi}yr~y~w|wyxyvmtvy~qs{}{qnyv|}tx{z}~xt}uzpx~xxzsnotft}ly{}kzyv~}|~ys}}~{}ysws~{~yqmoo}~wt{qwzju~w{xwlkuqkfjwo~~~w{|z~x|~{~wprfjnehwy|vm~~v|tutnmx|wu|x||v~ryyvjelfrorrhir{}qzq|tdv}bU_eaolyy~~zwyvtvprogty~}{u~}x~{ornyyvoliwjZ_odV\ETginnpv{~yrol{{t~||~{w}wsvscak{ttvu}~yw{}{sjijlwnlZRU[`]bih{wu~wssfsmkrpos{|~uv}syruvrmovxy}znb^Z[jm|ytvkkc[ejknnmuuktzqnnopnplyz~swzxzxsuqnqz{|zxwsl}~|~y|zte_la^ivqtslknru||ppsljqkgihjrfiy~zt|{|{wu}wwzzlv||v{xlsmjgceqxxrlg_hvqxumxw|timgficcn~~y|xq||txtzy}ssqjjs|}}x}{ymb^`b^ehr|~rmuijsurjipoxwspinvxsswxtyow|u~tuvsnoyz{{xyyww|{{}~todcdZ`my|uwrpsx{rg}{juvlecciukmy~tuvy}{|wwwxovusrxrtz}{~~su~wefdbgnvzwurqpknwow|uyqzxpblsjlmpu~uu{}z{wpsulwqwvsstxxy{~rjc]Z\]e|vsohprnjmyorwlsrorrrs{z}wyzzvw}zvnflnnt~zx{v{y||~~yy}ujdbYcsoy}z}{st{vo~ngrkcbgjowox|xxx}{uxzzqqv{}wryy||ox}~ys{tnroeVakou|w{}zrlinytuu}u{wrgkpjoru}y|q}~yv}ylebk{w}v~|sspqw~|oso`[UV_kr}pt{svxtlrqey{jmkmv~y}trv{}x{~xyvfpoktz~}|u~~y{}ywxozxw{wsrtrwxj}}vvwsyvqy|{r{~u~y{{azvmjjmsxwrxxvqipy|{tstvx{ysuxuptuwpxia[dlqw}{wou}wznpyy{y}r{}||~yil}vncn~wy}zny~xrqqzup\Rozmjjnqlt|{vywy}}s}}umrz~pnnoooo|}{smaYkw|z|||zx|~xz}o_bYbz}w{qnrqv|}prunojefjjgicq|vvwnuy}y}t}xy{vmjp|xxzy}z{{ssohpia\iz~wqrv|svqx}|vsljocZ^iio}{wku~|{|{~z~zqljmtf_q|~uvzsx~qlfa\WScvpr}tu|nhknyqhnooollwvpupnmnpqs{tn{shd_foo~~{~}~{{uqe[ZYejz~zvhyzomjhfea`illq}tvws{}s{zzw}zxslgjlo}x~y|ysr}ztxrfhbZ_rwyy}yv~{z{yurpjdqk]dnhozxxxu{yy}wyxronompc[t}~{z|xsjhe]SO[rv~qsxnlqrvtelvtohgrsmuvuwwrooww~qqsg`[_lo|z{}xv~~w{~}tolgWScklvwoxtjhdcefbammi|~}|qw~|y{ruuutrrqmjvxuy|{yut~~~z{{wooj\ad_hvy}}}zxuwy{zvrqiig\emgs}y~zs~xyxtnhebdjdq}z{vyxo`\YUSUcw~~s}ynlmlqsginjhlyz|rstsuw{xy}yqdhqd^ew{{y}}{yyy}ts}x||uokiecZ[r}{}rky{mggfd_\az|q{||tw~vtx~yjnrpmihr~{|~vsqp|wwume]`eZY`g}zry}}yronkacmkuuu}vs}ztvrmhb_``\[_h}yxyyvz{z~||~~wsuohssknsvyvz|u}}stxxrnel~yplu|w||z{|xsmenxsuwtrolttlr}tieu}~{}|~zu{xon|rjhijedx|||ytpuzojjihgehywx}pc\bizlgmumnwwyz{zvv~|}~rx~xy}txwututrmgimkyv`Xlpyvkd^TV^ceajqnignnoypw|kj|yqnkgaZT[o}|wlfoyztf`aYUVTU^liq}z{{yolhaYYbdp|||yxzzzwvx}zy|tupipx|xrookhggehgd~|lhjdgis|vqq~~}splcWOU``kwqwo`cjb_bbecZfy~zsv|u||toq{st{zuwxxum`TNSWUg{wtttlepp]f|wqpog^jytks|xojjrootw~wqpoqrpoxw|yhafmhrz|yxyyutvnoyxvvqdiqjeecekgcv|xtvxwsurjt~woibZ_ehx}qpmfmkeoxwvytq}}wyyvrlhehij}zw~xypu~z||vsvsr}wvjba^[Z_ggadu~ur{lsvlfdabf_cw|xyz{moruvq~|ugeda^\[]_TVq~pgnsidusq}pwx}xxrole[TYjdctlrumjlrkcp{uu{tnqwqn~|qtunkjlx|v~trne[UY^Zfzwsolmngcmhcryutoflvvwqqywqpx{x~{xwxywy|}zmhh[U^`bt~}~{{||{}yszyqlvukhgc^adairyv}{rruuuwroxoigjjl{}{onvrktyy{uvtow{qjigbZUU\cbuyw{zq|zrywofaajqr|wzuiecba_aouhsyqszqebdnrq}|vkrynechk_[i{|ojhgd`^cfaj{}|{tztrpmf[`vwtwxx~wumhc]WU^s|vlcjj`_gfdqzzxtrt{xs~|zvrqyy|{tqomqzyv}||xuj`\WSUams~{wtssrqqv~{xrktzruxnkooq}}vqpliiiltxzzvm^^deow|svwronnqxvu~xrkmwrgeda[[fty~}{|uuw{|wtpjb_dsxuuqvqhgmrkku~xorjjhaZX_nrv}||wovx|qosinohcer}unjgfedjz{uwz}qfej`VYgpq{xouvkc^\__X`x}~ujffeccjjiv}}{{xurnkgk||{x~uqukc^YSONTcpwmpuoopmw|yvtrvzxwwtpkhipz||wsqoor{~wpsj_ZY\fnq}zvsqruofv~{xuqhdorhgppdenv~yxusqpsxts~tpmgZT_kkjywv{zxvw|~v|smx|qjhgc^]iytsxryypmpvqlyuy}xvsnijs{vzvolpumiv|sw|msykgfc^[^hiiw}wz~uq{}ynfjjfdlut{vojfeedflnqy~xnmqjdkolv|wpwwleb^[_a_hqx}roonnqjfs~~~|~zwvrljijhjw~z}w{~vutmie_XVYabf}}umpwvmen|~|woq~orwvqmnszwxxussrt~vs~}}iekdWRT\a]fy~tnwmrzrihnpd_jwxt{utsrqqvumr}}urojaYarniyuy}|zzzs}}oitsicb`\YYcqro~|yxy}vs~yr~wqnkgdemsqv{uqnsyspywyyy{{xnheb`aikit}}}zwuw|~ypipsmnka`eglvy|~ytpnmmqon}}zzuriaejilkn}~{qqyshb`\WXbkkm}}w{tkhikngfs~wt~{yxvqorzzrzvz~vrzzlwrd`^ZUSXdklz}xoo|~pp~}zrjt{jjpojiny~|~{}uolkllo{{z|{lcgi_Z`ntu}{zxyxw~|ypiquleceihai}vpppprxws|zu}|qkgda^\`r{|yqw|}{tyvmheb^]alxzppyywyvw}zwuutqnlifdelyztplmsyywx{txwkklc[X[bnrs~{~wmgopfchcY[fswsoljkovssyzwrqlb^ly{w}|w{{zy{}nbgnf[WVUSVez{y{}uoqxzswtl|{spomjlu{~}uqsyxpwqhu{k^[ZWV[gsv|}ywtmo}|tz|yvphly{shgmnnt|vqnnnqxzsz}si_Z^b_cpvy~{{|{vmlusidbacijmz}}skiklqupr~{zw{yqmligimsz}rs{~xtw|wkfdb^\_hsvyqoy}us~}xqkt{mdfheabhu}}{tpmmox}s}}ulrrgabjtuu~|||{q{shhqlb_cf\Wct}}zusqopuvor~w{ronia\gxunx~uy~rmurg`^[XW\jywuwyzsvwpq|rt~wsqolmr{|x{xspszysx|zumoroe[WTSV^ggl{}|}xps{yvrheimqy|z|vroooquut}|w~vni`Y^efiikx~yvmnvsidca_cmqop|{tstxxpp{sr|yplkgdejpkj~vw{stqhgea^_flkm|wpnysiq}}yrim|zgagifejszx~|xwuwzq}pglog`clnkq~~}ur}~xnhoqh_]`c_Yaquu{usrrsvrls~xvtqlhmz{w~ysy~}{}~uosnd_\YWV[eouywz~{yqq|}zyywurpmjkowyv{ysonqy|yuu~~wzvnnlc\Y\ckmmywu{qnwxpprjaclv}{|xspnmnrupo~~tt|ynhe_VVcnnin}}rt|xmhfdablz{qyvx{vpotvonxtgo|xnihfdelvyrxyquyum[VZZWMN_n|qr{gZnxcegkvy|xxutv]Xzx}~}twwmovrke`jkpy~|kv{u{~{sxrlrvrsoqorv||~qgUnz{yss~n~quxtqyvt{pthjo{~yfFTi}rourtw{}{n{xlxxhdnyzujoophqzy{o}{so_av}{uzrfjl}hdcjvy}yn\Yimyeb_[[ebuz}||q{ywgYlnkxux}t~s~yutz}wq{w~{}yoxupxxo}tw}~~vutejnk|kxkX]Xe~ntttqb]gioypqtqwnnsphaux~xwfszyv}yq|xrxi`aLTqt}~~oysu{z{sy~pnj_c^dx}ru}qooelc^f]]jdRfvdhloqtw}|naY`bcdt{~~~{vxy}nmusgideztul{~q|}n]u|rthWW`V^betvz{krrs\cwc`mq~xlz{qz~nrowoZ`iaiehzzomztrgmtutksmad^b_Zssmyz\evrxwvyyra]iao{~xuw|wwuvwrz|ub\bWSM_qx}u{hl{fei]gk^htlsle~nquvwuzsvwszvfhjrxuv~z}wtswxmj{yvdbePPolk}m}p~z{ynpe_hbd`gzzzsuxla~{jh]gsfh{{~yqv{mku{}mlwd_a_Zs~s~qpjmuhbt~lowjfmfZu~ozxu}xv{}qomdf_SZaeuy~{lupnn|r}tgcYcc]q}vmlftr_i}xvdSjncionq}{{y~vvamdY\g_ks~twuzpo~zt}yrv^^U]lhm|u{x}reqwsn]h][WXfeey~yw{zutyuyyd\qniu~~~z}~tytz}~}zvckZRUXcdpnxpfmzjbvjWVP`mhmes~||zsm`k_bcpsvqik~{~}lk}zu~}a_bjd\quz~}~x|r|yvpibnk__]ijxq|~ow|djurnhc\pbUdkuv}~|evmnzsoo|z]Scd^]npnz}ipk`wiptbZpn_gvwu}rnzx~~vpsr\\fjem|wukzymwx~~ng|cQ^dcYfv}~{rikrvqgcp]hpfirt|ho}nsn{gfik_]klp|txsthxsqvxio]PVNR}vpy{rrupx|r\ejghnox}yyn{p{wyouqn}nshXltuswtzdom{t}uqzpddhZ`hmernxqrujf^e_dszv}xyu}vzcnnttcohqkkz~zukro{p~zzsrrnfc\VZnmm||~rvy~hxx[ssjrimuv}x}}zy~~jvsloVarTY]s~}~vu}pts}irzt~m\bW^djkywziguh]hn\lsytg|~{~{xpi}|ojfb_phnpw{skwyrrzvnnzouypdQhmivu{ustp}oeiUdrbbc~y~z|ohz{rdigbanjw}zsrp|w}}~vfoguuVPL^a_t|}v|{~zwhhkmcirgszzqq{}soroifj]^elr|~~ypox}tlpkwxf_hXTVPowpz}txyvwmfdlfgownz}wz{|tsqvjvrjobespxxz~mw~lij{yuy{spozofahZWahes{sukkilbcnvz}ywymnqmhqq_enlg`~zvpyrkrltvqmvuhWZX[[ikq|v|uxyciysrkhyyv{yzztl{|idZYnb[[p~wu}}|xvzwmv|~x~}|nbzxm\_c[`gpoqmhqdcrh`ryynuv{vxsv|mddc]fnfju}}vz{rknqtw|xw{j`mxlhmg`Rcqtxz~zz{y~ncY]h}man}{v}srmmxqkc_jotqvxulswxox~ynyqnadijSFR`kov~vtvzvp~m^fdgr{svzmvyytjrffecaiiyzvvxvy|m}pvz|||svndvh`dbg`V_j{|lmldhshv~zq}wyfjyws`]qqbkmvpz|nsoyujpz}}~vzdbe`fldfd`lji{ogi|mrwonv{|}{||wus`xljhhgcit{lz|}v|scywjutw{~j]jbfscY^Zelo}~|pxxqxuomuu|s{yyy}|sskhuqgbQ\vx|{z~}wozqu~}r{~|zzyzplvplekle_fbftpyzhqysxveqrku}{v|zxhpjhuj`]fnrp~}~}{~ruocy}~vznmr|{g_urdb[ac\o{mnsmoph|{n}{}{w}nqrlgiXZnjenpsqtwqopy|||yxpnzde_il_Wgfab^t}{stphoju~}w|u|~z~x~udn}te]hmqyr{y{s|pvi]ynomzzpzwnqijiXZ\]^`pv~vussfryswjs{~wvrmuqvqpm_eljqi{{l{|zomwyuzzwsqdc\lpaSZWSosistzvdmsmml}~|~r}tfuqdnfgmf_msv|nx~~ztrt|nkusyn~~xuxyqw{s{ynpecsjfh`Zjwv~~|{sfhga|qswy|yklzrvojeV]imsl|i}xpggytjyz|}~olngkbnlc\Yfdblt}pu}yon}}wxfnw{ocnc[_e\q}|wyuqukisq|v~swjfiX`|kTNJah^dz~rskqwotz|~xyumwog~sX_smqtwrxz{hsxlhnpk|oxtozmmohjkV[^ec`ow}wyuogbfxvo{w}jkNbsSUVfxvkp|ty|y{z{p|}y~t{yrlXTdiSaZD`^kaUffcy|mp}|yvyxp|q~{uzzrsqaknmdpsmvonuwoXTg^VkVmtv|}}xkr~qVq~s}|z{soeskqvoqqgmzf~u}z~y{jmzpiqjvl_gmiyi}iz{|anx}p}qz~vmqoqlexv~sryz|~|tpjo^Wumwlai|hog_ei^S[Pd[[xwxxpmyu{qjrzxyz|nbjshoozt{~i}qvryz~wjnjwzwo~vvextplkx|mkmp{}}vx|y}wiy|vgy{ym^W]p[hkbfmgeldvbhsu~sdr{nrmlrogt}xjlvszpxsmjfrzkuzm{~oxpq}lk|{{inj~|qz|q~|tfz~zutyqjk}qx{}totnduhl^aigaeaefKivsqob`uwzl~qv}}nwwly~tvznrjvwiiwum~}}}lgzvn||~v}ysuqvmjw{vrstz\uy|{}|vzslxvi\kqn||pkh~`^_Ydb`bcbkRf~syyxl~trqxz}rulcsbftzruy~{oqnyztwwo~pft}u}vtriqhlgvvswlzvy{z}suxs~|qft|xw{ohai`eanmosutyYfv{tuizsvnl~npqzmp|xof~ujssxw{piqiu~}~zxl{w~x{nz}zysxpto|ezx{t{rymy~qcnur{|}z~tuaibmllqqmqVa|nukpgvqsrl|v|}|wz{j|yhhzkwsuqtilp~{zp|yiotnxz}~sdnjxxv{~}fi~}|zz~nvenxs`conw|}p~\c`fgejmmueUzvzvvuw}ryn{}z|}k~tvmlvdamtj}xwq|t~yp}gmz~ljxx}~vxjfijusygw~w}wx{ppkfrugqux~|xqw]igrps{zwy_cszptowpnskvtwt}zi}lxkqyknxwvzrnrux|tlrprxw|~}z~{nnqw}yzf}{|}{yu}wrgcn}pdlntx}ckjspqxusu\`|pvmppysounyz}mmzpovgiyvsy}qyuq{zq~hmzkiprvz~|{shmsztzze|}y~t{~}x~{wdyi]euscjow{{zebenmju{vxh_}||vz~}r{svx{||hznnqdqefstx}~tryzos~gjtrmvy~~xu{yejs|v|m}{vyy}w|q~}{wye{ock{zjpu{yx|lgnxut}|zjc}zyqt}{qyqrsxy|h|oqvmwln{|{swyryo|{~rxkoyrnswyz~|}~|~|kp|}x~wj~|}~yxtv|}sg{gdkzqelpxt}kjtxpq||wq_ixytpxxy}vxz|xnkyppqhn~vu||y~}yz{w|~i|rhn{zjmowr}xpht{vv{lpxxy|tx|wt}~qwapl_eqvfjnxuztfpxrpz{wiiuwx~wtu{y}|hzmotlpgjuz}|}}rw|vjtwhmxpor{v|}ywyiuzysqu{{{~wy|qsw~zpyfpreku|lkmws|~}oz{xxjj}}rr~w|vrryy~|j|pr{rrkmvz}z|z}{qwyoyylqzonnwpzxzw|p}{y{op{{~y}|uv~~~}rv{~szhordhpxifgrltx~s|{x}vih|||up{|wv}~m|up|soijp~w}||u}{u}}x~rqzjms}oiiqorv|y}rwzw{{omyxx}xqqv{t~ztytrmdrcdivnefmqm{zv}yy|qjx~s~xsw||rqylvwojhkwzzyxwut{kxplpz{nlnwm|~|{zs|zyow{{{{vy{|urt}}~{opv}xmthmpejqzmiismvw||}{on}||n|{uqwz~~owyq|{qllq|{vsx~w~wzo{poq|xmkntjz|~yy|yulwxz{|~z|~~trt|}{{{ory~y~mtinndhowkggrjvu~||ymm{z~nzty|qwyqy|pjkpyy~u}xyw~sxmwnknxxmjlti~z}wzxvmuw|y~w~zw~rprx~y~nx}rq{tzmlkhncfkvqkirrq{~zso{{t{{rtx|~prwrt~tlmqz|{|}ys~t~|~pvnuqlow}rnmvlw}}|}{yourzu|~{u{zwtrtzx|||ks{ypozsxnlljpgimxtljrqpz|sp{xry{rtx}~rvxvwxnpt||y|zws}u~}rvqwplowzqlmtjw}yulv{|ryv}yy{zxqqsz||wyxlv{xotysvkljmlehnwpjiskyw~}zns{pzsx{xtwvuzqnpuz~wtyxx{|uqqrtklpzvokrnrw~{|w{pr}ys~tx{|txwxytmpty}xzmovvpnwrrnhiknefkutnjqqp|}ut~yu{|rtw~zrvtwvvmptz|{~vt|}xu~{xwqqsvnmpyzsmqrl|z|~y~sq{vvx~suy|syvzwynqsy{y}xnlttpmvsppjjnphhktuojool||vt~wvy}stx|uyvzwxmpsw{z~uu|}yw|xxsqvwnmpxxqkpmlxy}}y}qq{utytvz|vzu{wwlnptw~v{ypotuqowuopkhnpgfiqsnhmli|yxu~y|xvvz~u{tzu~zlnotv|~zv{}zw~}vwtntxnlnuxsmooi~t}yupzxz~puqsrcbbfevzkme`jpr{xymelnlipslmpkr|usv}~lyy}||t{to{wosy}pvrtzzvsswwvwnggalswu|~{u}xr{vst~}|vb}xtlqx{|{yyyx|zwkltrx|zyhimomoyyt{|wxg_zl^hlozssr|ysy~qxtyxz}{|~qgkrtwlpqrrz{zz~|nztoigjefvfeipcgidoitmccedk|nicY]kou~w}zijlmlowvoxxp{{~vseavpr|~w~|qk]Wat{yvrojorkfpggyqklpuupkj\gwku~sq{wyxy|}utdagpvrwuzyxt|vuuuxyxmn}{~{pi_Vmwuzytpvyxrupqzmcgnyux||twx~|yxxtupf_g|wwvvtz~wwqxu|{l[V]iuuwqsynjrdokmreba`cwodcXZivuz}x{lhijknvyqtp|}{z}~{rmd\u~{v{}yr}kg\Ye}zxspmruogulf}unnpttpkgZhxpvz~}|st|u{{||zxpzyk^^eqy{yx}w}u{wttr{}xtlr~z~{{xridV]wzzwtw}xqtkt{rebiqzvzw|~|x|vtqr{nkact|zxwyx{uur~rsxm^V[druxx}vmwhmqirjb`_`tree[[gwt}}z~qjkjjmuxqppv~yx{{ztpd^t|yx|~xto~|kh^\h~yutyypj{ljwqprtvroe[nums~y~~{szyt~{z~~x{sq{vh^afpsz{|vxxyvtssz{rox}}~~}{zywqk_Vjro||~yswnpzwlehmsru}y|z{||xtqqrqfcl}||z{{xy{xq}|~rqwpbY[_fjq~upnwlqoeb_`sthh_\dror}|}wonkknuxrmrpyxyz{xsg]otou}szvjabgmmmtz|trz}{|vsrxxokq}}|pk{}{pj}s}{sxpqsrtmirsjcekqsuzx^wv\\heeghyyytpu}wqko{}j|yxy{|ztfs~yvzuvyyzs~ww|~|{r{|urp{z~n]\mllvhccgkpqoagyupu~|~|qnuu}}~twmrzwnkpuz|}skkeq{yujfdi|zsvnnv~xtlhlx|qaip~ushbkle_afkoppw{qlpqn||yron}|~yuywpe_`n}{{}zkv}yuzzi_^fgytjbetlee]]eqlmyvprykc_YW_lttuuoyvwy~~qn}{~wzoft}zv~ztvz~ld\W]o}vtstrl\bqlhjyslqrmlqbdqu{{~uwlpxwpmquz{{ofenq{wojl}}uxrpwvsmls~}|vlnj^nxlqsnfehlqvqtxnjmtq{x{|solx~}}wy~ohc_dt~}~pt}~xwzrf^`lsymedsqhjcafsuny|nktla^YYaoxyt}k}yxy}wm|~xyvotpfv~u|}wx|x{ve_[[dw{xvxwp`hupmjxtiklikoaftz|}wwlpvvqorvz~w{tjfisu~xpkr{xzuu}~{tqmozwrpkhndeyvrvvojkmpxynz|~~zsllrrwyw|zrnq|{y}wthe`bmxq||~}}ty~}zwqga`ioxwnlxxnplgjuyou~ohrlb_ZY_lvxup}|yz}xo{{}stroupesx~~yz|rxrd_ZYbt}}}qdrvqljwqgjkiqm_hrx~|{umrvuqqsv{uv}}yqjfjlp{tqz}{wzzxuplp{|{}pomjpj]ivz|}xrqqrv}nq|{~}wqmnlmy|{~wqr|{~t~xlha^cp}~{wxis|{ovywzrqvsutpllposss}or~~}q|zrplgiryzyuzwmdktdrwqmgqm`acepnbktyxiszfivyyx|ygondb_\`kw~uwzyzroituhhlnwwhnvy~~{o`dq|}zvuwskz~~{z{{zzffv~~ulxxhqxppnjjs|z}zpsxjcfgosfgs{jiw|wdt|svxwtqooit|xxyxngm~~|{phmumtk`adjshblrw}~|zmir|~lpwtvvspoqmtujj}zuisqjieaenux{yy}|{tqg_j}uwwwtpmlievuururieq~~v|}ilrjhfcdmw~yv}xszpfhjrxkhrw|}qp{|xnmd^e|zltohfb^ajqsvxzxst|{zukqsfbejurgox~||tlu~mwxyxusrpl}|zwxyqo{{y~wwztnp{y~whfhnvjcmsw|ztuvkhr{~{y}li~xrtutssspu~suqkp~nstmlifhpx{}~z|oinmist~{vvurpnlfrwpw}ojnifqyv~oiqliiggmw}}||}~tyykhjr|skt{y}yv~~~zw|scddbkorqjheabjpstzzusw}yuv}yyulkskadiuwkozwszzu|p|}yzzxwwso}~xwrwut}yvz~~vpnsutyz~{lgirxkgotx{}}skrullt{~~|yx~ikyrtvuuutoxx{xlopozrg^UQW]`gorvz{ysu|{pv{w}s~md}zy{zyxwqt|{uw{xy}pw}ruuoomjlt{~vzxrlnjkuofhp|zpu}}tq{zqc`f~uwysrokkpuwy~{usxz~~topwqecehlnpuxfvzvwwvurjeruhfw}{x{~vnjgfnzwzz~qns|{jglr|rg`anl_a^]hnkt}~~yvykge\YY[amuxx|zy|}z}vd^cusq~|ttt~xsv{vp~tpzxw|pikcjvz~ytssqoropw}}mxlpuonheqvqu|tuyyzzxw~~op~zjbhx~vnjmvobw~~}{us~|lly~~{sxumoxwjkruy}~hYZmxru~xcl}wpqtvwwqm{xsyzputmnpq}}~qhvqh_ekcbjzwxrenoefskm|rrtvxwrmzzru}uffgdjru~tkonjnlmonmpuxy|zu{}vhkzwk{snonmibVYfaZ`s}vvzvv{vw~ssupqnkkiedgjnv~|wwrhdky|tnushhq|ulpw}||{mliiszyuia[TTW[cmuxz|trvpqsmgiqzzwxsnegj`Zarysv}~~{}n`jl_]ftpm~~wyz}}~z{wppzxnkvwopytilmmnomnup^amf]_emsurptynfywvvwwrjl|{pmsxk`cdfquy}xxxvw{r^ccVT_q|vnlkkh`Ycnf^_ggXTYY_hit}rorpoljllkmqtvzypstkmomsy~vu~z|shfkcX]hqwsqlg`^]ZXY[\]a^\ikkrsopw~|v}ysqkirphkx~tpvxy{|}mqztqy|v|}zyvssy~spsrtzmlmaY^gbY_glprqoqraXfhacjry|{z}zjpwtw|{z~uu{tedks~z}zv}zymX\hfgrrhvyrqqqmd[bmg]\dh\V]dnwuyzx|yyxvvxz|uz~vx}|}yuyy~yy~oggi^ORZdsx~{wwriba`]]_bdildeojhnoot|~vv}xryz}repuqvxtyqqwufeoz~|yuttssrf^ea[bjp{wptpfgqqfejlmnnlmrj^jvrrv|xwztypovwkgpy}tqpmjkmot{zz~{{yuxxwwwtsvlY]gdenzyuqqqmd^kvpjkruiagox}z{wttssvz~v~uv{{~}suuljuztvz~}|{t|vjehhZPV`ltv|}ypnonmmnorvqjvyruzyzyrx}wy~~xgirmjmv|xzyx~vmt||zwtsrrtumdmndfjknw}ztrvwxz{yy~xinwrlmqx~vq{xnloqdX\dknlw~zyvttvw{zs~{{~}~{{x|}||{vstn\[db`ep}tlu}vmkoul`dnx{w}z|uqpomlmnpsmckrjjruy~x~xty{{}~}{rr~zy|ypu}vjegmh[]gqwu{yvspnnnqvums|x}}{{~}}tpvtmqwpjmqsvz{|wnvxrpt{}{|{tko}~wtv~~qlu~}xvutstvx||qmwukmqru|~vx|~}|~wjntojjmt{rox~vuz~}vu|urvxlaeowws||vsttssuw{yr}~|~~{wwtfbklikr}ypt~xnikqn`]gpslkw|{yxwuttuw{xlmyulnqqty}~~|||tw{|~|rw}zwx|wt~tkhlnbX]jtuu~xurommnqsjcmtkhnprv{|{z}y~vswxxyxvrrpc^fhgjq{~~wpv}tqszznkw}zyvtsrsusfcpqilty~|zporqqrsqqutknwwuvz~z~xyyulgpvnfcgmh\]kw{w|tsrqqsv{~v~{uwyyyzxtrqiaflmpv{{{umq~~upqvxnely}yzpxxmjkkklnotthdotkjqv{}|}z|zyuowzpfaaeaUVetyx}|zxwwwz}vlq}yqty{}}|}{tovzvy{sorttuuspoohbgmoqw~~vzzuv|unwytromkjjmj]Zfi`aiqxz{}}||{wuuqhhmnmosy{y~~xxxsjjtvldbeicY^n|~}yxxwxy{ys~|~z|}zompommkgccb\\elqxuow|sjfhnmban|~x|utvnlkjjjkorjaivsmrz|y~~z}zvvribjqkb_agh_\j{zvtqqqruztjo|{qrx{~}}xpuzxyyqnsvwxyxtrrnikpruy|v}zvv{}slu|u|qnnnnnoquocersjksy~y|~}}~{xuurmmpppqu{{||}{tmt|vmhhlne^hx~z}{zxxxy|zptyw}{yuxywwvsnkjgbbfhkowzsyzqmmrsjcky|tu~ytpooooptwpfkwwnmsx|~~|x|~~~}zyywrrtuuv{{rkqwpe`_bd[T\lsot|zxwvwy}wlo|urw|~~||wy~uquxxxywsppnjiknorx~vpu~zrnotytlswqnlkjikni]^kphdjpuxy}|}zzzwttuttu{}{zsloywnihkokacqyus|}vtssuvzwv~y}umnqqqrqmiggdaadfimv{{}vsuyxnju~w{{z|rooopprvvldlwukjpuwwv}~~|zz{zz}{yulgoskb^^ac\V^mrnr~~}~}ru|vy~~zzyx~upsvvuvtpljjhddfhjowz}|vuw{ypn{y|~yokihhgilk_Zdolcdlswxz}zzzvrrttuyzwskhqtleaaega[dsxuy}zyyzz~yps}w{yv~zpjmonnmlhdcdebbglpv{}ztrswwolwy~xwxmihhiilppgakxypow~{z~|yy{zuuwxy|wtphcjpkb^]`db[_oywz~||}}~sq|vv|}{wqw}tnquwwxxuqnorolosw{}vwzspruwqlt|{w|pjhggghkkaY`mqjfnx~{wuxwqprtux}}yrmt{ulgfhlkdfu~{|{wvwwx{~zonzww~{ty{piknmmmkhdadgfcgnsyyzxqnortnis~z~vpnnooptrgaiuwols|zy}}xuvyyssvy{wrmdckne]ZZ]a`\cszy~{zzz{~vnrw{{y}rosutttrmihmokintw}ws{|tporvvpp|~w{tjeccddgicY[gpngis{~~|vvrjlplromxy{~|}uwuz}xzvnwwkoyvu|~{qo}uddmnjputwnowwz}mksyuhp|zh`gnjoz|ppqxynvziiomjtwy}uz~}riq~qpx{ruyw~~zuut{uoxl_blkp{x{wo|ztn`fprs{yvz|vooehp{h_bhepz{}ur{|zzmjaZaj~{vrvsxzx{}~ty~}gcmsw|~ttlofhp{}xs{oglsqa]fdjvuz}{qkwyrz{qga_b]_akruuw~z}~{~{ywwu|rgkz~x}ywyytxzz{uywlmtqefmjrrmqot|~qjxrxujcfflkggfq|vqxy~vt~{pztmrzmivz~}~y~~q{stlfirpfmqorigjhosy|tl{|uzjcfoqsnmlju|zru{vo}rs|tnkpyli}}~|{}x~nz}ryohcgrphssqneeijonvxukxytbdnwrqoppnyy|r{wn|mzxrmkoy|lk}z|{x||~zmw|svnhcdosiuuqldgmormruxl{x{p^hsyrppsurxwzt|wjm|xronqz}lk}z}~w{}y~{lv{s~ukgdeoujtwsmfiosuosuwlwzzp\fqzspruxuwwxv{xgypxypnor{lhz{~}y{|v|{np|u}ujfffozmq{urhjotwqruvonwv\cnzvpty{yvxtzy~zfovqzolps}ohvzy}|~z}~|sy|so}w|ujeggp~}po{xummpwztuyuojux_`ivwpuz|{uwt}y|~xenyoznkquohuzwz~z~y|z~zqx}rp~w{ujfihq~~qo|zwopsy}vw{vnhtw^_jwyrx~~vvrx{}wblxpymkswphw{w{~y~}z~{y~ypy}qs~wyvkiljtqr}{xqtv}w{{ujlus\cm{yt|}vsu|xy|s_ptv}wkluxriztwty}yvtwtvmd|{~vxrk\^edt}wsq}}jcejszq{nzt|pqt|uoks{}wxxtsyx~|}|vk[ov|~xv{s|{z|~}}wy~pt|r|vsf]t{|zylgrnfmtkdbmou}uh}ps]h|ptv{yyzzssvwwm[]nr}oq|||px}xpmq{zw~lx~imnqy}|pkfm}mxybwon]ex|so}sodSgzx|jtymjmvim}{twp}w~xy|{ytq_txcl{yzwfvwhnunuvaZmpeqjjnolhyonow~y{{ydm}tmowx{}|y|}ly{{~}}xzlLUqrwyu~lztnwipug|{tngo{crdjuz~}|~}}~d||}uu_hwpjutzguwv~u|yvyyn[[iv|wu|q~yjv{ws|ylwxurn^evqeenlqwzbRlyx~|qkwftr~{}s}yrl}ywwieuv}w}oj}|rr~|pw|^Zurp}|{~tdvfrvt~vrpw{g^cs}evzww~vw~t~}|sj}}s{~y~rQPpvyq}urrs|t~tqld{}rhj[`vtuw~~twzyr~gouuzsxcj{zswzwkmzwm~|xqvo\X]p~|k}~~rr{oj|oty~llwzvn`]rytmx|x|vwYYxkymqz{|zto~usei~~}tx{y|zhxuaewroxv~lszr|zb_z|s~z|ojxbz|~zrsuc]]pzvhq|xwxvpyoz|~yr|{lh|{z|u\Qlu|qy}}}|rnkbpp|lc`Wrvutuzrs}||mvzndxtnsus}hn~rsk}zrl|qo{}{zn^YXk~}vswwm}qhlnovkdjw{o]]tvz{{dYr~p|tn|ytm||sojby~~{y~qt}|xrcsrc_sqltwxo{}s|~ge~~p|oq`Zgtzxy~woyyvhjw}tswvi|ktxyrp}ptzr~wzxyzqqqulrwiadw}pnzyrtvwtwxrpkpukjy}whqzstngqt~yvkyz{nt}|~g{}~spk|{xsytvj}mr_Vhuw{{z|{|~{nsczsrwwgVn|rwz~sp}sxyzssglvz}|wxwsxjznassxx}|uo{uusrqoy|{w}{vody~ce{yd`hphdx{ozw}|qsq|xz}zqvz~wxutceuwtbo}su|yiquwr]^x{}zjbjvqxkfjoobyzwto~wv}}jsqii~~nsyx~u|}zw]_gy}pt~y}wwmz~}m_p}l_hrxmy~yzvsvwogmazosukwyrxn`p{|skx~pqwue}|o{{{zlotnmuvzkou~prsfinymtvtpvmymsvwoj`usjpu}|~~v{{p`bn~{|xt~~pq}{utwxwalzonszzzy{|tqw}|lhnh}zrls|}rnjv|lw{tfgsqw}vj}wv{wwloxsvzz|mzzy|y}~mzzcaji]mywu}nvwss}lj`up}vww~}|yswtgksqhXk}pw~o|~|fewy|vyszqxz}ytz}~ywttq\vsrnuittnsuhizrk`xxlm~}lzzxoiejqohpvzqyomn`gqkl|to|z~~zy~srgdzloonrw|koppbntujgvkt|woq~w~ttnmogiov{y~z~{}}x{zv{sf|mtwfu}{~{x||xtjg}dhlosw{zzmvptnhdkuwt}m}}pco~qkitzuxu~~z{}tuniz}qowvwvuqpoovnn|urv}rgswyy~o}jrooklplpzz{t~{tm~zfacb[hwuuyww{yyxlnow~{tu~xty{dzphtmfYc{zljtxop|zr~}ibr}vvxzyuzv|q|vpqoebsywrny}z{it~}yfguvpnmjhgnfnppqospsuwpowp}|xuzuoo}x~`Sitpjskglsuyrtqxpvzux}ypmqswzr|s|ktxynmzf`bcqzvrmop|y~~~yw~xligbfw~zty~|qqswreesw]juygkx~|{inmbr|smw}~~ystvuw|vv{|~{zu}~o\hiumu~nWUV`]TRcShvlxnv}|y{uhl`T[dek}sj{wy^|omt|gg~u||uqqebeov~|}|ln}~rpppzr|w~~wv`~~nlko{ws|cecjkZVh{qloy}xhuzjrb{u`emtvifzsloiromxr||yry{ijxsnus{ut{wz{wwyacisvytwblncjsz~vo}uztps}{{zzxwohzjstyoqvw~gh[ers|txsik|tcfus]kiuvt~eWWauvw}opusyi|n{vyxqs|w~~mk||usuzvhrvgcm~|jsy{}~sww{x~~~m}}yy|y{m]arrvnhhnvukqylceccjez{ngxvtf]fxpjjle^uptoxzvwwpfp{}wuny~vzyxlq}u|hbl~{x|hrxzdpqsxqk~~zzv}ph~~yie^`e[uq|{}{vuzxicxpmce|uplnpiamxsx~oneizmxuurmlyurzruos~|rqvxxu{|x|qysbq~tgajtyx}pgx~vz|xqyzynx|~z}w{qrihkmgbl{~xlpnikr|}wikpm~uyreceqzyhjpf`^^iid{vqpan}suxwswww~uscezlv|vvpvt{phhny~~yxy{xyvl||}|ywokprne]fezvtrv`v}svct{uiab}ynnsogbtxokdwqnmowrlo|}|svo{xxz|zomruxu\^eedo|}u{ov}zu{zstys}{rwto}nxkuxyrxamxnrT[]YWNOgzrm{|vutnzj}sgsvvrjm`YX`rsrgklovo{yyt}~|tyuyxtpdqe]^i~z~{z~nvu}}vspuzyxleq{uy}|}|vuo{j`Zg|uz~vswxtfqhidaezuq_tz{wqtZifk{yururt~mnzsuk~w|yv~qq}v}jijghm{{|zwptxjirtgxnmmyel}w~u~yxhlhsxi^^_dktv{}uuxstxrrrl^a}{u^xts{iUgvfiuq{z~~tt}uq|o}}qkkmpfhukl[aryzyor}wx~wip~rwsqwsimy{}wmxi\\gs|vc_a^W_nlruvy|sn~rjzzr`yvnpqw|jehjZ[eo}zzywq|orwk}vhhrrtufgkmloru|smvx{wyovmwx|kvpnphx~~r~utmdf`sjcljb^gqnr}upws{zoex{yfvnjlooyxhcy}kYV]jtzzz{y~zyjdinnz|xekvggiomyzm{spwy~jyzhwyysq}iivx{|u}}wjiikbkurbbhd^^sqwr}}l~uwy|jxwvdun{zc\xyh]``hx~u{y}sy~n}qqbeemutjkwlecloq{}gwt{{iqv|vx|yjrtqkpsu}q{nhfmbfjmpcikhgglq{}v|tyud|uypznlvub^zsc[b]o{qw|}}v|rxsxwquppku~{jfjmoity}|{p|~mw{lnsu{qfpznjln{~kr|qnefcdhkj]dgdjis~x|ts}sdv{qxnkr~tg[ryj`efo|~}zx|~lt~xpmmfmuzqdlknnsyzuynq|{sf{|{px{ouzijrr~qrtwrke_cbimhY\dbekyzw}r}}vjp|~q{}uqzq^Ykjc^`orx}}ws~r|j}}l^`fq|k`b^lszst|xol{{x|umlqfxnfht{{xw||w}}yqlgdlqiYXfkbihmsll|s|plrhqxvyxw~uilfk}zunlyiuxjpmp_yuxrcainsnZX[Zdfuszqq}{xvvsywzik~rllv{|}|~uwz}wzwkejg[Q\rrnur}{s}v|stxqfkzvsltwzvo`frwsjl|uwxux|~u{ptttaYfu{|j[_\choyshyzp|{h_ismcchw}}~z|wz{nwz|vkd^hrl`Zijchgmsrmsysrqo|xvtv}ylnou|}yxovk~uklddsjsrecppreX]_cffq}~~~qryx|quypuwmu|zrqsz|~z|{u}p}~u{}vqhgja[Ylrpuw{~|uqn}yzyos~zttwyubet{rlx{|vt}q|youx}ydZd{n\ZZ^`gy|~xnv}ttwnafiidbgw}~~zqlp}|yk`\asrl^ddadhlnkfd~yuxs~zvtw}ltor~~|{|}zv|i~umlacphlzxndprtk__ejjiryr}{~pst{rlyy{j|vvutstxzv{~{yzwyx{vozx|rn|vpnfkf`\cpqsv{}{{qyuw||yyt{ldkwnuv~q}zzp}tjy}{v`Zej\W[^\fyyztz|qrqypbhgffdiv~yk{klv{{m]XZqurd_^^cjqpki`yxwzzurr}su}plx}xz}~|nrw~npe^mihp||sjfqtrfdemplt|}xp~{z|rrvynh{zrgwxrpuwvu{|vwyrzt~oqwpvyhp{rrkidkd__fmqtv~}{vv~~pzmfmsst~pv||osgwzyva[_zn\e}||zvw{qrzyuxmgu|nl`hks_YtdfeVtvtw~~{}urxlyzhqvpsURsjh_\]mvyqwvu}jinVbknlksd~r|coycxz}rvoxwx~vjwrfhWogZZ_qv|sw|~wx|l[p|zrfct~xz|nvpeZnYluwurvpsxxptvyl{qs|stmkbx{|}{np|vy_]ev~rTlul{uw|}{z{wxiij]xwlnefurruxulpkoty}m{r]Yz{uhkmy~kflvgnzqowv]l~zjuzsv{tyv}dyztxndVu~uzxvoibY|jb^PzrfgoxoViyy}|jmuxvw|z~xmtsh[l]yvkjp{|y~w|t|yr~|t\izufkwtqmmijoken|~}wig`qcmt|vskhez|{sz}djxryskroZxtmvv||~|}~{pmbl_f}~~xywvpsttw{kwrnWaxx~}psrhkr|zrd~yktbwntcbwzv}wyx{uy~xyx}goz~ywto[h|vx}wq||nf`~uegUywlgpy}oX`~}rsz}pnz{{yyzyyvpvzm\cUez|sqv{tx}rv~wq~r^b|yorzq}{yvqnksmhny~nf^i^bp~xtmkkz|y~n|mgvprxtouuyoayr}myx|z|yulbe[Xuz}utywv{hqutbZpuw}tjeonqzwqgwluey|urn_oypo{y~z~zx|~vsbzz}tkZjvqwpjg}neqqqefxsmx|zeXdyptzmy{|}}yzuzrrtvh\`Rmy~|x|~nx~vmwst~}k]^yxprx}wsmwojlv~qg]c^`u}yvqoo}}}xyku}rbnqptnml{}vhus|p{~u{~y}|vha^bVu{|wxzw|pdss[^oqwslfrwoprtokmxrqj~w{ud_v}ytx}wz~}f^w|rx~wxnnlqurgzpn}qugg{fnz~m{zqxxu{itp^pv}sgo}kX^q~vwycXlwoksvny}~{i~~u{vt{yskpnz~uufhuqyshb|x}v}z~|uq}|}~rlv}x^V`{`atgh_ry{pYXk}or}xyoay}rwlot{trryktyy}vtbZ\ntqszy}~y{s{vp{z~|q_Xqz{{idihaiuz|x_hfmuwj`\Z`vts|~|x}vnqqp~oixxytk|eaV|otrptwvtrvwxt{eUZt~yx}sg]yqeV]{iafs|ylwdXVYu}}nyx~{ns~r_UZmpo{ypke{huw~{thd_Xez~{wd_\[hldissry{~hzjbarpyrfn{~pm~l|{{hvraeqx~ty|}}{pm{phhp~~|yuujZQu}xcoi_baohlixjpnolgisrpyw~mrqrfegr}t}hfrbchuzwwasyyxzs{xrcf_Zbm}oiyfbXlstrbZv~jdikaPfxyyqmwkps{|wnmkmw~xq{znons|ys{wr}xy_Rhwqq||wumfkmfks}z|wfyjkl`dlywsz}yj}ykrjumagppdmyhzspr}|}{||z|xpmiTryyt|mmrqaWmnpjrxsryyk_isrwz}nyuzvwusu|urx|}fltbonvfuzrxuv||}vmhdilpsxxnlmi^\]itzrvytnoko`_}uxwfwpgnusktxwuzuga}uy}~{uy}to}rze_traWatnaftlmuwxiihkoty||z~y~quvyrorutvvpnrr{ixynokstriy|~~|{~uhdf\yzwwvpWuwsrcu|xl]^y|ocjuwrq{tv|vim{|wthd}kp{|rtzy~}}w}sw{zp|xvsrlmklrnry~suy~{nprsvzmmny~yv~wrr{qjnsxwy}{uzzrjiu{pf}rtuq}{tzjoyhqkTjlnxu|wrvu{r|dnswnr{~|~~~xhqugxmmhowimvxp||ls|qvlilm{|sfhpqaaYJ\kjjpomu~u~uv{~~z}rqru~pjq|}{zpisltrvn_fupwuyu}vzlZWevw|nl`\jeYgvzzmYqpp{njtzpqtrs}ywslz}wsms{}vy{nSet~|poxu~z~{}uqyjaqa[j|{pz~okrkvzulskhknnwrdepsntrx~odhrss|z~}vxwqfdnbg~j~xwwrvj`leUav~~pixuyxsnogp|trfruh`Vl~~~~cj|x{q}zxkz|qpuirumts~t|~~xpp{ntpYa}~xusqs~wqs|trjiujrifioghigl{vy{opwrbm~lww}roxkluu|xr{y~~~t}z}}|~wsqxj[Ytvzvozvfdq}tputw}m\lhcgwzpoqoq_jxvsxt}{yjvfbikrzo~{u}~v|stvlkd_ghivztu{srwuszpj}tjfsm^_}qYZheyww~rq~|{}sefpqu|{w|~|{{ssystgu|}rw}tibZst}~{m}xtqyie}vggj[dng_o{tnws{{s~y}}yufwomhcmpypkdg_hxto~w|zm|wpkbhqqpmuwwlirlphm}yqy~~oawwnuici|gnlnrvnt~x{p}|uz|`stma]xtqxuz}~xldH`{p}rtqp}{|hp}opoekvwvtqpulkt{w}z|pOWz}{fV_yncdfcu}xvzz|w|yyvdav{n\]yynrzwu}r{qss}{z||rz}qjdZ~m]jlVQVU_uuptwjvvlxyfbgpnhn}zrqr|xx|u~ykhrtr_i~{jUr_rw|xomw}wtuyne]o}vxg[oylc_c~oqjWiZ``bahpzrj{ztnr{ubhuw{|k}swmqemsxtnl|l}~}tto{ytp~y{wk{{zqraxsbacdalvtgnhgqfgjusd`Yp|q{ltzywtx|xhaxqzslxmd|wsvqlnbl}phjhb|oztb^bhleV{|liloh\gx^PZXez~mj|zw~tsoljyvzqityowu}qqyttnu~qionjcxz{xy||}rtwqXjuidhoodiucbnc]uzzy}st}zzs}~}vlmpzqzp|}x}v{q{vyxzxovqo{zskicg_ovtqxnnlm^syeew{d`yn]]e^ckewvy{ugbfnuuv{yuyxn~qxw{w|yorhitxsl|{}}y{vlqxvbtnccvxn^opgjpbhr~~gwtst~{rz}plqvqo}ywov}wv|jx{~yubcltknuvwooqvcf~siq~n_z~aVcf[r~y{|ylqlt}}v|yjkntn{{yx}stmxszqq}vvvkckpneuz}vy~}gyzpcmxr`fm`cgZ_w~|{y}yjwyx{xy}~|lhqzunss~p}{{o|quzq}xyxhfhkcjn{}vqyfozpek}sb\yyg]^VRhrt}rj~v}|wxzqgnrpgrpzkuuz~mkqjw}vxibinhgswwwsdysphvreUfwgaffZgxwt{k|vz}s{~zy|kmx~tqsx~{wz~vo~up}uxul{|~}llppcdgwzzaksleh}teb}yd`e_[ottzwn}xyxzyzjjpqiok{{xx|jy~txxrtmtuwncgkh_kk~}yy}i|zugo{pcg~qdccX^uwuzpo}{|w~x{oeoupgml{sxxpzyywi||yov~ngmqiYdh}supcwx{qnk`Wcthp]\cZw}zp|z|{yyrspokjytcajj|h|q}xtvjfu~jqliijjnrk|zsk{s_ukf^q\[acrnnzptsxxzxnoynb\rqb{fy~~~r~rs}xls~tvyqzyy|a_]cd]uxnyoqkemlo{xkkj_Uct{}|xvzxyxp|^gj_]bs~y}v|tun}pc}z|sesh`a[^nvw{nrj}oespdjx}k^bYisufzz}rrkov}j^nvkkq{~t|onm{il|vlrq]S_nsmg||lotnvjtviljrgefZ^~y~~yuxptttl{qpcntgmpxvwxvnk}ot}{~yxyznct]_^\tuu~}rwt~w{icyw`\dnyp{xogx~tizo{vr}rpS_jnpqoq~}~{xk{zwx~ymltut_UQ_ori|xqps~opnquuhvgh]i{xww|~zquu|pcffaftuww{psv|m|~zdpu}~tv{tyrb\sueajm{|y|rjmiyl|zaZ^kt~yz~~{ntu~}khytlorgXcxkwpu|vszpp}{{tvikghiXhcf`Yu~~}v{zosurjgoyliq{|yxvouhs{nm\Y\gvq{}kh|u||wlh~tw{lolpplf`_kkpuywzwxunhkp{`jtilew|ytrqyq{rp{wyznZYv~rqy{vwq}{}qgk|}mu{ve]r|r_U[^\Vsqsivzzurneu{qw~wq~uooxsvyqmqjl[\xkzjy~}r{qoezqqy~|nhfUjfdf_fiwq|o^gp`bfegk~~~r~}rwsxfvdhjmlos~xxx{u{{|yy`}u}x}{{pjzwlkwqljl`UY|vs}~yyif{lrtloo|tsxzpp}uf\lyo_Zakikry~}nsywyr}}h`kvvia]jt^gkZ_{zo~v~oyjdrv|{r{ttnrnptrqnoY_no~x~{ru~tf{_qxusvxqq~v`ttmkpazr]jt~vy}ujjlqputhdblpm{{|y|sn~{bdnxo]{mwjqlp|x|~xprtw~zwuy\[obnunehe[NUiebi|tuu{dxlx~yz}ntzutk}wqeqgwxntsnnu~jrlffghvuysyvvyxyk|}nmssvqcR^~em]aru~ss}~|yll}~jpwp|~qcY^qogryf``a}x}y~zrkpyd}s|zkfn}eiYe_`xlhgmvw~~pkss}wqopnnpqr|{{n~qhrtotiy~lbVvknbXxwrp{y{z}}nxuqd[RddqwdZ^ghdchp{}sywy}ri`os[~~}rqourhzrjtqjknqv}}~vpinq~t~qipwwtxop\qujnmkc`[e{dojm~lu{xqu}~|y}{ylyr}i_]kkirrf]ejqhoqtz{t{znh~vme~t{qquntsocbve[ihendep~}y}{wvwzykx{tu~t}w~fb}ohqqsucVrsela~}wsw~z|{}nuqpv|w{zw~zx{}l^\W`qnl_cidfiop}}z~u}{rautxdwywxpsnyowv{xk{jkbeswts}urprq}}v{{xury}xz|zkan{iZ]euo]K\m[cX|~qwyyw{wxv~yt~|r{wzrogecqvvhhpemqtyzq}tm~jytgw~nyovzlnmr|onl_xo^^^gefhguy{ttut|~z|ttqwyn||olwmpryjfZivim_|x|{~yrqvvvxqsy|twxmtqqok`c[grsddlec`mwtzs}thwz{hz}|w}mmswyrqb~v]fnmjeefstv|||sqzzx|}pt|s`v}l_hnqg[Q]obgYkz~xs|xoplszzz}ymi|}wuj}vwiyzrcg{kae{z|ykqo}v[nxtfhlquns~}ywywhkz~`SWanvloy{ogvy~xs}uuxxv~zvzmgyktvtuci{~zntjikvouuwwnnP]_qr]\_etsgtxmlyu~s}xyttyoesypxoe~zz|~tjemqu}}yfkuvuvkiov|xs}njaj{x}mf^jvjkyyxvoqxykgngsllojnpzskv||cq~vxq^]Yckmtyw|~wmgx|rjjhgb]`{yqup|~sqzumpuqzrjxu{h^d_c_Zdyrnz{ztxm~pbgrx}}z~~~zthk{{z{kbbcb^twv{}~qmyxt{{{jjjlwjljr~tks~vlq{sjz}uxzn{wcVYau}v~}wns|r|ojr|x}pea_kutosflyvv}qhii{qlvip\U^anqih|~uuxynwxw~uozseckt}yz}zwomxqs}wfXW`mu{wtxzhgyzw~zhtgom_iiputqqgpv{}x{wt{hZ\mtw|w{yuy|rp|z}ww{k\V^jtwx|sv{|owwrornql_|vshO^isoifx|tsttrv{nagzujhw~{|{yz~phl{tdm|iTVcqsw{~znnst{~el\iq_iq|~xq{{tqu}{s}wrs}ukob^_ju~zw}sv}~u~uupnm]WXfvwor|wnzxsormtgdml^Qbhpmjl~qot{wryx{{nfs|rlnxws{{ytuz}|ylcw|mXPV`hnpfwuvyptlcyhdmquzx|ywu|~}xwwvy~{ueifdhry}yvwlw{}ysxxq~yvxdZZfqy|}yx}x{yutyzzlXwohfUgswohczukmproq~}xqdh{ukbky}}vw|wuv~|zshrknoeTYlrxp}rq~pwnmbvh]rx{vtz}~{x}mtvqxmhki^m{{{tzszzyxx{y~z~zznVXhmcs~{yovuwkvukfslqmlXfyru~|rrwxstyz}|cZgzudruy{vqgs{||mo\^gnbVhlq|tsq{yso}}dfrx_nuxvp}~szszswo{jhekd`uvy~{rrzs~z~px~u|bXVdgez}zmzwvex|thrrhusiTjyuy~}xuxxrx~~y|`Zcssjyy~vxzxs~kxv{u|jlWWdmh]nrxyxpxp|~tk|nllpdp}{q||vvwunymshcjki`s{~{q}u||r}xl|~q|prhY[]fcxn{}zzgl}vkn}hpzr[a}|}{v{zws}yu{vmY]fsoyux}rzyvu~{ttguoe_PZfrip}|rozqs|ooysrmbky{u}v{}zhvrji]gmrgo~tyy|zwvukrz}kvrihZ^aiftruzx|jhzwnjrnyxdb~||xx}zzt~|{t{m}|pn]_erpzw~~y}nu~}szz|ytr`rlb^RZftowzrpvkt|ptzxxm^nzp~|x|}zgxnhe^gpvktqszu|t}iyywvgxkfc[agmgxp~tzyhr{tkvqu{ras~{~}wx}yy{}v~yxqmpoh`ckrouy}vl|vp}v|yxefs`bYV_mrp~}xrvomvtz~wbb~ox{~~y}|ktufiaclvqn|~tzsu~vyw|mr{}~wuvgwnde^`hmfnvtz{nv~vnzww|rct}xwx~yz~~u|y{plonkcdlokxy~x}to~|po~~{uzyzckpac[X`mllyxuwmtvz}v_g~}q{z~yy~z}}l{riibdovlmx}|y~~tsrnozqw|{{wmuol`eqfbjithu|{v~v}zlmtirsuxt|tx~z}|ukjuu_lz^^d^zyzx}zshorzupvhz~jxugtbUrh`nwsz~y|p~rktubhysu{}pzwrtrrlnhfuuqmt~~{ztururx{k}ysvnjalpdghjq^lwszzowtjrpvxr}~~v}~uyqpwq]sz_b`[qqp~w|vlo}z}s~urujz{l|qvbXsj`ikgo}rznsyffukysn{susxprleoplin{w{z}rt{vxyumz{ys~umchrhfheo^btko}~uw~ptqouqvzz{~~{q{qvybg~i_c[iqgv~wzzxpz|sy{~upskq~rn{yv[cughkd_qzywwxovtbtmkpr}txytwtqukgqkjcoywz{wytxr~yzzi~v}vtvsimpddhfkZfrkl{|xx|qwqqtlsz}ytzotuco}f_^]nogt~w}zwr~{rxz{wzssly|qpzxo]kvhlja_qrvvwqynf~lnkyy}~u~uzrsrgloiibsruxv{wsvxqjzxz|r|}vrnwnbfdh`\mkekwo|}{ysxokxw}{}utouhlsc`[lpgi}x}~|||u}~rtxz}s}{xvun|un|ys_exqpmb[hojvw}}quqinmjx|~}v|x}vwugjnjlcqnoqw|zt{wsk{{{y|r}|xtr{pfjeg]_kf^aie~|z{nmurzt{{uorgvqhbdsmdgyy|uo}z|puuxsr{ywytuus~zo`nyrulb\jfflnzx{~txmvzollyy{xz|x|rjpjohkrkolyzt~{{}y|my|wt{|xzz|v}ykmlkd^kf^W^Zizwy}wz{{~vru~{ow}tngqkaadYRSHGL\qptwy~xuz}vq{nqzusx{oiv{ylpjbb`TU_VTW[l{n~qeyvi||x{~xwuwmnzmihdfoywzpbmto}d~s~zq}uiflbdrccfeel~yu|mv}ow{s|vuvy{u{|}~uvoyyzz}~}xynyon~przw}w|zzmztrjac\dbdvieyggvlynrej}tffe}wpfqqy~qvmutwnrsl|rh}lfqx|vwwv{}~pmzqmrqfortobhdiupvvxj~{yu||y~trxxwc_^Yidcr_lqQkhln]i^jwdgjc}~jnfjlyrstrl|{~vvwxgr{t~{}}vyvvzoy{ufmhq{o}utn}{~{~}{|wq|dw|wx|{y|}idgcqhhxes{Xomrteofsnmqkoplqs}vtyqiwqwoyymsqhi}urzy|}~wuv|}zmnxttzzktwwvckkp~pxp|m}xy~yuut~w}r}uwi]f_kh`sfi|[_jgp_gbi{ldibtidglowvm~rjvx{swwlzzwy~sts{}{|hnoro}tjz}{{}zz}zx{shwww}~zxz}}z{pemgsmhzjqalopuirisslpj~nklss{wq|nfupyqzyntslo|xt~x~z}uwosz||whpz{~{nywzvfqnv{pyx{|ypzz~|xxwtxot{xyxz}y{eghfpbnraupZvxephijbuwgeegxegkpo|nywjk~}zxtm{{}~|ww|vqzy|posnzqzqo}y{||||yv{x}r}k~v}zx}r|tutuv}~vptmvmq~is~eupuspuiwsjljmloup~uu|lex~w|t}tvpvlzw{|y~s~rtqqos|wtjyys~ztntnysyu~vyyt{~|~qynpoqpu~zx|uprjrhoxdmxbq|jomlncqxlbdd|}gfkqk{qt{kfy{wu}xuvwwuz|{t~{yvxrzr{qzs}xwyz}{}yr}zsxp|w{zy{o|ymoousx}{|tzrwnqlwtwwvwkwvhijqlnxn}ws~ndt}z~v{sssxyz{{~q|}nmmsnqzr|zmz~|u}vzsytxwv{}}~ww}y{zrv}liiwumozvy~x|ttrn~sfvrj}unsothkvuf`bmthgqno~muej{}ux~|zzpryvt}~|xw~}~{xzrp{u|z~rtz{z{wqxrvr{uywywmy}ughjspvx~|~w}upvwxxzz{nw}zigizunnynx~psdo}|}~yyzo~{t}x}z~rv~ngitxnmytwxpsz{~yxyurzxz{w}wzxw{y|vny{tccg|ols}rxxyqxqjzrq~rrttuirwud`cspijvjt}nufou{}y{ukmvut~}zyw~z{usxy}zprv|x}v~xo~wuo}vutuwwuktwvebgxwrty{}}p}w|x|{~rx}}ofityooyqqp~yjm~~zwowu~v{z}ssz{pbdlznmwxq{vq{{vvy~vw|~zv|yszyvwxyultvtc`durnrsy||uzwlxxs~vswwwlswwg`drskkvkq~mwipv||v{rgju|tuz{z{zzsz||z~{mpxxv|tztrzvnp|rwxottwmmtuo_ai~|rr}x|yw}z~~xv}}yhgmsotyjxs~riw}wuqyzuyy|toxwtbagy|pmt|mz{q}~y{w~usx|w~u{yq~{w|stvwrksrr`]bs{pms~p~zxyp}}x~uwy{woxwxfbfu~qloxhw|ovjt~w~|}|u}||lfkywsws|wz}u~}yqlq{vypuznw~vvmxxtypqsuoirpq`]ct{qovqy|}x~~lhl|uqv|k}}pujt~~z{r~z}twy{sowtuc_dtyolrzjv}r|{w~uqv|u{s|}}s}~x|stvxqkspqa[aoxnkq|l~yz~v}xy|~xr|xzkchu~snq{iv~n}wkr|}y{||u~z|mdiuvrvq}yy~}w}}xujny|vwrws|oq|z~y~}ps}uwqnstrhqoqg[_ixrmp}tu|~yzjls}ts~vmtv|plx~yyu}|ztxyzotvtq``gvumlwviywzs~y}~spv}{ry}s|||r|xxpqsvnksnqa[`mumls|k|v{zzz}|tz}rfis~vqs}mqoxznp{{|||x|~}s|zztego}ystwq{}z}yzy~~~mkqwu}{kys~umw~y~{wumvysrjmorjjrlqb\aovopwmz{yknw{vxqrqx|qq|z}xt}zvquvwmttsq``gutnoyujyv|vy|~uos|zot}tzz|qt}uunmoqmhsmqg[_jupns~ouw~||x|v}~ziip}ytu~rkss~}tr}}~x{uxz|sy{wygekyztv|nz|yx{}y{|rjnz~vu{}hz}my|wou}z|{{tvlpyrpiilokhrlqg]alvrrxp{{~{}mnv{wyqprv|tt~||wt}ysortulvssqaahtsps|rlww|zzz~{rou{{zm{tyu~|{{vnwzspikmphnqmqa]cpsor|zjx}z|~z||rjny}vv||h|~nz~ysz~x~~|tuwztu}v{nekwu|q~~xp{zuuzsuw{wxzulp|wzrviuz|~wr{sx}zrqsuppztzl[]rzuz~k{s{|uy}~}|oqfkt}|}uy}}|wp}wps}|wx}zc`uivt|{}xur~xvy|yy|{pt}qjjrwmkjlgc_orkfjvsrllptqq{swk^brq~v~{~tr~|~yy|zptx}yz~~}zgVZnzkorngkprjltjx}|yqkwunjs{yy{wxzxmpzuy~yljnqq|yqly{slo|xvqquywwy}tgjx~qxwomx~y{sefmusty}hsorxummqsoszxr{uu~yw{~{ywwo__l~}}y{|yzz}xz~x{uidgjjp~shntjb]jkgb_cghdpkjkZXapw|zt|v}|}xzvrqwsxx~|~y}zoq`X_q|xquvtipqiyz{|~|ulkwqjep~xwxzuzyvmp{ptxysjkmoly~up~|unp|yvpquywyz~xkmx{pvwnmr}mjowvw{lmsnutnlproo{|t|}xs~~yuwyzu~xstd_hy|~{wz|}{w}uqsrjbdhll}}rmxskbdnkgaadheiqhmf[]iv{r~wx~{~~~}{rz|utwxv|~|}|{{zzrxtjm`\fy}{uxyvlstjy||}}{tkiuqliq~wuvxuzyyoqyn|poqnghnrs|ys}wro{{xsqtwwwz{|opzujpwruxunqyzz~riykpqnkpttr{~vz{tzzstuwrxwnsidl}~||xx{y|~~n{kghe`ckrtwpzxoiepojdbdgfhrikk`blzuy{w}}~~|}wov|xz||x}~{{~|}|utsrltmhmcco|xzzumwrl{|}}}yqhluonkw|uuvwuywqs|zk~jklifkquuwx}xpt~{wqrtwu}wxqv~uuzroqsqww}ypov||y}wxz{u}yzy}}iisqzs|zqt}~|}~vzps{wplwuoklz|ytrtwx~~}t_x^\_agnqovy|zwvklz|ztwuiiq{t{wt}uoz{vuv}rzzyuvyzvzn^_snheXW`lt|oy{uz{z|~vv}mq~yqyyrnm{zxy|}~|~zxse}}vnlmmio|s}}z~xs{zslluut|{zfiv|phdrvtonpssxx}}rquzz|~xg{|}{vvwvpwyyrxskdfprnt|v|{}t{~|}vvwrpql]t|xrhirgnzz{zyslittmhbkke^]cmszxxwkhnw|v|xu{kn{yt}vzorph`^gv~ttxz~kouknpoqtrljsfftw}}v}}tpjt||ywy{}|uvzxxxtbh}|xpnzutz|ty~yrmqx{~zpvlbpxuv}{wqqrtu}v~xpqvyz}|ldy{}{xxxso{|v}tqwqjcen{yv{t~|ys~~zuvvsrqhZtxmpviq{|}{yslkvtmhbkkf_]bjq{zz{plqy~u~z}~}usyjjx~w~}~xzznmoib^dp~~{~ujnmopprrngplanvzyzxtnoz|zvvx{|~vsuwvwwl`|}wpw|r|}}~ys{|wqpu}~xnkqcdt}x}ytsuvx|xwqrwy{}ziiyyzywyxtr|w~yomrnifjt}zt~{}~w~r|~unnonprnaw{st|oq}~}{voluunjbgjgb`emtz}y~ups{xxyy{|||yqmukgx{~~~~z{tidjifekw~{j}slmooqqmfknajw}yx{vrpz~|xvwy{}upqssvysg{~ztxuy~|}zrw{xtsx~||zywrifmdcw|~|vtuvy}xxqqvxz~{lmzxyxxzyuu}w{zukipnkjqz~z~tfr|w|qnoqutha~~}{z|~qjpoicbejnwzpvxqqw~~psnls}ymbfi[cqw}~zt{oyv|obegc`agpyxqmou{nnqbdrw{~~zuu~}xvrz{xy{}y}zl|stumo~rtvrljntzzsnnqy}yomqddt|w}|yz~su}}~|}|saj}qrtvx{zvyzz}}}{}yw|sljmsyxrqvhfsy~||~|v|{vuvvzuu}|}wfr}}w{~}tr|~|tuvqjfhms}~wywx}}wsmvr`lyopsvxzxtyuwwppz|{zsvwslikry}vpopuz{wy|mlwz~|~~y||~t}|zzyxvrmr~y}wxztlffioy}}{}ou~mihjouwtv|nkw|~w{}x~}zfksruy|~}y|}{~|z{ww|}ywx||yyrjtxz{{zwx|ztv}}ytssux~vpyyx|}jixwy|~}xx{~x}}zy~|unkmry~xwvx|}~{vonx~mawrlnqtwwtr||pxzsnqywxwtz{volmry|ysrrvzzxps|~~|z}}tyvvvutplr|u|y{}yqkikpy{z{}mr~lfeglqtsw~smx~~xy{mkvrtx{~~{{y~~zz|}xvzzyz}~}z}mrz|||{xvz}zvrx|ztqpqszzoszyzvgx~uuwz{{xu}z{}vle__eoytv}v|u||x{xr}|vxsuu{}z{pnyyz{|zvv{nsyzx~~zupwzxtsv{}{peeimqrpnvznt}sv{{~t~zqebu|{{|ztu{|xtsuyz~||sr|vwzyjl|uqoliikpv~v|{y}og{rilv~~xu}|pw{smkrzz~}r{xxy{zwsx{llv{~}vmhxplgc^dmfgw{vuzxqmcciloqtw{yvy|ude{{kefhkljglzyx||{yz}~}{oqx|}|vy}{|sjeopsvtty~|kcwodbgnw||||uirqifikktzyvsruzs~wy|spz~}m`szuy|nnwwvw}}wtmosrnnsz}bbrsy|qh_Y]d\at|~{~zwxz~yz~|~~|qmjs~|~~{{hoxvvvvwwx~y~sg_^gzsah|nikoswwuzwqz}|{{}}{z|sZZospz~tlu{}}{yxz~xx||vnjksu{||yy{lafkou~|~}xsqqu}}z~~whp{y{}~zw{yu{vnmsvvtsuzxhpsmmnqrpnuxv~~}}ycg{ysx{u~~zuqsyyx{xzyrnnr}}kr}lfgnw{rwwtqptzzw{zvpmmpxz{|joutyz~wnihmz~jmvqprtuspt{rlu{{zxxzzqmppnmqxt``tujjqz}wvzpiu|xy{{vu{~|wttw}qvyxvux{xy~~w~~{hdvyiabflpstzy~zz|{vrvxtlfbcjvz{pi}xnkmquvuw~sw|{}m_mzrnw|{{wuy}xuqjknle`_bl{thw~qllnqrpottfcknooqt{|{}}yusuz~~}q}~}z}~ztpnr}qrpe``abbbjurmv~~{xuvz|uquxwsqqtz|tz~}~ye_qxmhmv~~~rv~zxx~|}}pntuttv{wpkkpy}x{n_izshfiostqotvkgqwyyyyz}|yrpttoieejv~nuzwx{}}|xyd^p{sosz~x|zx{}wtpjlpokfdflw{vz}ujxrkjmoqontuhdlpqqsw}~ty~wqqpjb]\cpvpwjeegihfhoj^aikkknry}xrtxxussv|z~uc[n}y|{|}~}}{wsrrplhlma^iqvzyusv|~upvwtuw{}ym^ez{pklortrpuzrlu}~|yxy~}yxsqvyvqnouyyxtux{}~{~}~|wd`t|xztu||zuqopvzvtqkmpokgfjr~{oi{uqruwwuu{xjjrvyz~zp|~~}ww{roqojedgqznxtjggijhhnqfajprqrsw}}{uu{}|~qcl|z}}{~xuu{zustsoloqd\bhklot{zx{zu{}||~{m]cxzpkmrw{{z~zs{~}}wtvxuplmtx|vrrtutsw|skqy|}}}}|zseUZqztrv}y}|xvw{zusnknrqnlnt}rvuqqstsquzqipwzzz}~}zty~~z}z{~|vtnjmomhdekvvy{ojilnnotzrjpvxvtux~|~||~tdj}upryv|}{{}|rx~sooqqolorg^cikkkmt~ufk~ummqwz{{{sz}xsvxuplkow}}x}}xxz{{z~|sx~~{z|}~xjZ\pwnhjqz~}xtvzzvrqt{~zuvvpnpqqorwpdfmnmkknw~nk}|rorx~{|~{{zwrllnniedfoz}}~vrsttst{xmlswwutuz}{|{z~~td]l~{rpt|~}{vvz}{xwx~~s~|rmlmmkjoqfahnpooqwuhpvmkotxyy~yqv|~|xutw{zwwsquyyurrv~xzvonpsttyzqt{~}zyz|~~ra^p|vnms{}wwz{xsqrx~~y|qjzyroqtuttzzniptusrsyvjtzsruz~x~zwqmoqplijpzvsttsruztikqtspopu}~z}ud_p}xolou|~~~}ww{}|xwx~vtnmmmlkpqf`floopt{}oaeywonqw|}~x{|{|~ywrmmppmjjoyzw{spqsssv}zopx}~}}}{~~~zqb\mwtw}~}}ywrpuyyvuw~zoy{pjijjjjpsibhorssv{zmszwy}y|~zyz}{wtpjknoljkq{|{|tqsuuuyzopw{{yxz~||~{z|z{|qb_q|tqtz}zy}~{z}ys{qkjjjhhml`]djmnpt{ofrxpnptvwywry~{yy{}zvtnknqpmlovz{usuvvw|vpu|~}|}}wz|{z{zwpbV_tztopv~zuwzyvssx~|ourkijkkkosjaemqrrtzxkpyuw{~wzzvuvxupmhcdhihgjq|xttttsw{tjmtxxvvx~|~~}y{{nadx|tru{|xz~}{{pr~pgddeeejoi`clprrtyvil~|rmnrvxz~tw~|yxz~}ywtopturppu}}~vtuvvw|ypsy|zxwy~}}}x|xj]cv~womqx|wy||yvvz~{~qwwolmnnnrvofhorrqqu|vim|rllpuxzxz|{|~}xuqkkoomjkow}{z{wwwwwzxnovyxutuy|~|pbdvyoklry~~{uvy{ywvy|xxyojiiihkpk`_fkkjknv{mjzvompux{{y}{|{xvpnqtspory{|xrpqrru{{qntz|zyy}{w{}|}~}xo`Zgxypjimu|}z||z{~|s|unklllnsuleiorqoptzxmtwqqtx{~xz{xwxzxtqmhilnljjow|zxzyzzz}~srx||ywx|}}}qc`o}zpjimsy}}z}}{}xp||phffffglmc]billkms{vks~tnnrvy|x{~{{|~|xuqlloqomnrz~{y|wvwww{{qpvzzxvx|wuy{{||{uk]Ziyzrllqx~z||z{{qztkhhihjmw|yxtuwyzxy~z|pqxpklmopv|zst|{kp}vx{xuoopqnlr}yrsxzywvwyzwutqqv{~~~w`j}}vrrtwy||}wx{~xrz|zzxqko}zywvwy}~|tuxqnnqsu{{~ujk{wtvwtorx||ss~vrqpnmr|~|~~{yyz}~}{zxz~~z{zyqnnprtx~yx~~zxx}|tmgdhv~vqppqponllpuwxxz}yuvxxz|v{~vqoqrtw}|y}~|wqopv{}|w}~xutssu}yutsu|zqlkjklorokmruutssuutpnnpw{}~y|}uolihjr}~}~}|{z{~{xsnkkp{{yzzzz{zvpknw}zsnjhhgimuxy{~|zz|~~}}|{|{xwvvyxtsstvy|{xy}}|}vojgeflv~~}zwtropsuuuvxz{xttuvvwz~~z{{usstuwz}|yz~}}|{xspnov~~|x|~|{zzz}{wurqrw~|wsqppqsqnortttttuvurpnmqx||~|y{}wsomjjlrw|~|{{|~|zupliioz~}|{zwrnpw{zuqnmlkklprtx}}{{{}}|{{z{~{xwvtsux}|zzz{}}}~|~ztolhfhlqx|xvuvwwwwxyzyutuutttuwxx{~yvuuvwy{{yy{}}}|{zzxuqnlov~}~}|{z{}|wtrpopty|ywwwvtrrttttsttusqommqv|~{|}~}zx{{wtqommosw}~~{ywtpljjox~}{xtqrvyxuronmlklnpsw|~|{{||{{zzz|~|zywvuvz~~}}~~}}|}ztolihhlqw|ywwxxxwwxxxvsssrrrsuwxz}|yxwxxz|}}|}~~}|{zwtrpqv}~}|{zz{~|wsponnqv}~{zyxwutttttsssssqonmosy~~}|}{vspnmnrw|~|yvtqoljjmt}}|zwtsuxxurpnmmmorvy|~~}}~~|yvtsstw|~~~~~}}}|}}ytplihhjov}~{yyyyyyxyyxwuttttux{}}|{{{|}}}|||}|{zxwutrpqty~}|{z{}~|{xuqnlllotz~}||{yxxxxxwvvvutrrruy~~|{|~}{wsqomnpuz~~~}|zxvsqomkjjlqy|zxy{{zwusqpppsvy|~~}}}~~~~}}|zwusrqruy~~}||}}{zzzwsolihhjnt{~~~}||{{zxvuttttvy{~~}|{{||}|{{{{zyxwutrqpprw~~~|{zzz|~~}|zxvspnmlmosy}|{{zyyxwwvtsrruy}~}|zz{|{xusqonopty~~~}}}~~}|{zxvtsqomllosz~|{|}|ywutrrqrsux{~}}||}}}}}||zxvtsrqrsv{~|{zz|~|||{yvspnlkkmptz~}}|zxwvutttuwy|~}}|||}||{{{{zyxvutrpoorv|~}{{zyz{|}|zxvsqonmnorv|~~}|zzyxwutsrsux|~}|{{{|{ywtrponnpsv{~~}}||}~}|{ywutrpnlklnrx~~~}|zxvutsrstux{~~}||||||{zzxwusrqpqrux}~}|{{}}|{zywuromlkklosx~~~}{zxwvvuuvwy|~}}|||{{zzyyxvutsqoooquz~}}|{{|}~}{zxvtrpnmlmnquz~}|{zywvtttux{~~~}}|zzzzyxvtrponnoruz~}}}}~|{zxwutrpomlkkmpv|~|zxwuttstvxz}~}||||{{zyywvtsqppprtx|~}|{{|~~|zxwvusqomkjjknrv|~|{yxwvuuvwy|~~}}||{{zyyxwvutsqpppruy~}|{{z{|~~}|{zywvtsqomlllmptx~~|{zyxwuutuwz}~~}|{zyyyxwutrqppqsvz~}||{|}~|{zxwvtrqonmlklmpty}|zxwuuuuvx{}~}}|||{zyxwvtsrqrsux|~~}|{{z{|~~}{yxvutsqomkjjkmpuz~|{yxwvvwxz|~~~}||{zyxwvutsrqqrux}~}|{zyzz|~~|{zyxvtsqpnmlkkmorv{~}|{zxwwvwy{~~}|{zyywvtsqppqsvy}~}|{zz{{}~|{zxwutsqpnmlkkklorw}~|{yxwwwxz|~}}|{zyxwutsrqqrtwz~~}|{zzyz{|~|{yxvutrqonlkjjkmpsx}}|{zyyyz{}~~}|{zyxwvusrqqqrtw{}|{zyyyy{|~~|{zywvusrpomlkkklnquy~|{zyyyz|~~|{{zyxwusrqppqrtx|~}|{zzyyz{|~~}|zywvusrqonmlkkklnrv{}|{zyyz{}~}||{zywvusrqqqqsux|~}|{zyyxxy{}~}{zxwvtsrqonlkkklmpsx}~|{zzzz|}~}|{zywvutsrqppqsvz~~}|zzyxxxy{}}|zyxwutsrponmlllmoquy~~|{zzz{|~}|{zzyxvusrqppqrtvz~~}{{zyxxyy{}~}|zyxvutrqponmlllmorv{|}}wuzutwvzuvutusspouqvw|~~yyvwz{~{{vttstnnlkjjnpzvh\nphuz~zxz{uzxpo}~}pjhruod\dimlkjjz}nvy|efmrusz~rx|rw|{|rqttopyolxihry~|xyvwsw~qrhdxvnlc`hqyurpu{wyzpv~t{yt{rvy}~yzzwkx|wsws`dyz|}xxzqzkylq|vtq}vrtnpzwkfpqkzqxr_{xq~{uqvy{ztkxy{z~{sgteaorprrhr}ytv}uv}{{{~}{osttvmn|}{sxvyzpw|}x~~hrjxyr|ymoq}{v|jpiszyrbcpoegsxl^smf|fuysz}t||{~{u{jhklp`x{v}xtyr~vyxpv|tszr}wxzy{qgs~y{~trydbfr}sbyjpxq}v~sxyppouzqnqohxzpnvwl{upm{jdnwv~y{uzp{t|y}}}{s}nmkfjeqk~{kzy{wy~vr{}}z~qyrsiuznkxtttv~{}|xqoqqxkcsvx~vur{pfqoruv{~okskaajnmcpii|lzhk|yyz}wzyxx|mtinhg|}vtu{~oxvr{}}{{|||q~vwv}ii}|spyynlqsmdt~o}w}wonu{orxwo~y|vxtnoyqu{|rq{thzqhphotps~zvn}zpztz{|x~|ysv\aglgUj~~wut{wm}zstxxymhlvxtpzt|tj{j~yuvpinvonluhzs{li{xkemqo``gmuy{lfz|vqx{rtutz|~{z{xxwp||zwljrvlw|u|~vux{xxy~w}ypuu{ifynnrpkorwxlj~sri{t}~~}{}|~nkz|{ok{{vrku{qup~ttzpxubistp}vs|{t}}yw}`gkd\Wj}povt~v|}~|yy~xxyps{zshpn}|~~tps}h{}nsw{xw{ve`~sv{|rdiz{meiatuy~pdlxxsnnorr{zq~ww|{y}sw|}{}xmhrqgdz|w||{snpy{pjv{xcnwvtmhcp{qspvxz{yplw{~}}qs{y~qr}yrkwwlsxwo|spfzy{wcnxzzzr}tzur}w||{~z}}rswmaV]sumifjvozv{mx~~}pt~psjo{zyxx~n{{qtztgy~x~tkw~{{}zvz~vruyz|xzhpynqhhly{wvmmwqjrzjm{{wy}uopkafx|zt{vx|vg}rufhwtlgnln}qndrt|~zv|qp|x}zyyzuyyz{ww~~{ympxwpig}xj|~}zemx|w~xzxsz}}zws~}~r~iu~pagwp^Zbiid{y~r~~|s|~yly|ouru{|ltymm}tx~}zw}oqwr~x~~~vowuwtozqpigiv}pju{rwffv|rvwnz}~~qxz}whdlrty}l|~y~tvwquo||tnhpjgxip{{wv||lx~vu|~xu~xzkv|}{qtwwsy|xqk|w|w~~|imwtxy{}}x|w{|k~z}tlnt_[\[Yixrr{zt||sy{rq{usomq~||~t}wj~ztoprwsj|{w|rvmwyzk}zt~ysotyvnq|~wymcvmjm|~xnp|wxs}x~~z{|wwvqnozlum{}lux~}wrqgksohtmx{{xjm|{wrsv{}~ys|xw{}zt|umtx~zxwxvsrwhr|v{~oqremqu{usz}yzsv{t~zltrkid]]poonrvsyuu{zll}{tuzyrs|}y{wopxru}x}zuw~{{xh}woxsyy}rwwz|xmnxemnpursrpjruilrx~t}|tw{tw|vv{cm}mwytzyx~|slzwrtqikvr{{hdv~}}|vuuxzu~xyjx~yl~uvnxvz~x~vt~yz}xljrmio{yvz|zy}{yxwo~zu{|yrwwefmmgovkjbrr|{uz|}xxwtyt{zpqzz{~tytmsxn}zr{mw}wyuztmyiimy}xxu|xw|~mxnswoqxqci~vmnomn}{{yvvy}{xwzol~jirv{|}|}y~~y}worsrr~vywvls~xw}{v~~{}{zn|tt|}twzevtysuxupt{}ts}~{w{rtpkv~yrot{~~}p~mv||~xon|wnicanxtihy|}mq{xz{z~qv|v~||zxz}wy{}}xvyvn{{xuw}xrd{~onoqu}~zzrw|irx|ukpwmbrogilmr}|~zzz{yx{lxpopopv~u~y}sr|olmny~upmey|y{zzw~vx{{{oqxw}z}vuvoqlrtrmpxzxxzozrmu~zwytpruy}uu{{}qx~uuzxszvnnonozrqql|}||wno~|~uxzy|~|qu~on~|zy}vuuy}~{yqx~{}o~zkjlqv{zss}~~z{p}uytswrggy|vneaeq}zr~z}yzy|}iz{pxxusuxwtw~y~{~|zxsr~v~xgt|vxqm~}||zv~x|~wv|ri||nijjgkyrmt{yu|y{~|y|~soy|vszrn}tyz}~v}tu}ulyrmkmvxsdqvgbn||q}|~sy{}vr}v{ux}|vw|yzxuq}|xwk{qllpvxsopru|z}ypxpqrqqx}qehnpzvtwn{z{~}ttqsuy}~ypnt}~~trt{}wt~pep|w{oxzvy~{uvswvlsttvppleahy{qkijsu{yxzzrw|xrq}|}v~{xsmz|tpvvln~{xts{~~s|~vhfv{~{|qlx{ute\twpsifijlt{vx||}ln}ou{vxzyyxurtx|}pn}uu{sonkjqtikqoz{xkk|yoiuz}|||rklpsw}|ujuow}pnwxsy~wtsxxv{qmuvm{{xv|tiv~ooi_`cegnwwnfddgp~zxyyus{}oppprvyz|~q~x{wmjhip}xlksyznuxngix}w}{uqtz~sf|knnkjhfjwzspsw}rsxwtyuv{{ttwz}q{}w~|xxoloqps}|vsomy{ttovzzzzwrux}|yvtuttvwvv{ymp}yznlutny~xwy~|zv{urx~{s~}~|z}rt~rrsida_al||ofcfo~|swln{t|upwsnpw~zvyunmlkmvtmswz}~}nr|zrnv{xyzyz~~||~ypvxnzqdchlr}|xwy}zw|px~|{{}~|zxy}tn|npu|{umijp|zutw|s{rlt|tnoz}~py|uustx~~~~ts}t{vqnijxyv{{nw|tv}|z|}zu{~~p{{vsiffefnx}zurons{upvkiuz~xux{vvuns{|yx|uwynkkms~yvwxv|xsmy~zurwt}|xxwy~{syzjimigtwsty}|nrxyx|}{|yvx}wiw}poqpqw~xvsqu|xy}rhvvvupr~}x|}s}ypwzzyzzz~{ssy}xomnptz{t{}~sv~{y{uv|~zwtx{w}x|snot~xw{rsidnuvsnkny|xz~ywz|}tsn``gu|tonuyv}tsz{ss}{vt~|r~yx||{}}mwrb_m|}ywvtu{zzouz|}}yvz}~s{}{wvvyzv}qswyz|}}z~xqou||uonpw}x~tr{|z|}slx{~tk~{zzz{z|~||y~tm}~vpqx|qilrsyzzvhmx}}ysnr|x|qr|zy}|ruz{{uqtxvohdelwxqwrdhostrmimx|p}|}~xnwtw}}}}~xpkikrysozqcl{}uqprwzzvix|{w|}~yoy~t}|z{ymjq}zu{qmw}|xrmox||o~~ywxxz}}sy}}}}smyyqmp{zx}rnx~unp}|syvp}yxz|~rw}}|srv~~}|}ztop{yqwrpuxyyvrqwz}wr{~~|{}st}tvvnkkpwwknsjmvywsolmu~x|{~x{zst|{zvv{|rlms{ymqpgpzyuxyqmwy{x|z{|}xt~{wvzzory{|ws}wms{}wqqx{z|wvx|}rpx~~}}~}ytor|}{yz~sr{spx~yvw|wz|s~{|~|qx|w~|~xpszvv|zwx}}ov}oju}upryz~~}|{{p}||zur}|wz~xpot{tlrlcju}~zsmnv~~ws}zvv~{}{ok~{yz~~zwx|{opthenw}~wmnz{z{zu~~}~~|wolx~{{~}{zwxuuqks}}yvx~{yy|x}~|pr~tv||zyxvpik|~~}yvwlju~|ww}~}{{v{~|~zp}zvz}{yzrnn{|~|z|~w{qgr~}wyyvtyxt{|w|z||sw~}wojrxrxzmqrggntvvustx}}wqtypy|zyulow~~|xyxsvlfmsvtrrtwx}}rs~{v~}}|y{y{zvutrnjq{w|z{}vqs{}vpv~}xyyrw}v~|wv}}qgkrwz|}~~}zvqr|x}yosy}~zx|yu~z}xvx}qehmqsttuy|yvyztv|zz~yuwy}{qq}{w|uy~wqotyvrolouxwvwy||yspzz}|z{yy~prx{zwrrw|y~u{vuuy|{{tqooqsttvxzzwrt~~y}{xrptuw{|zvqqx|z|smmrz||}ytsvxrkoxzumirzt|}}{tppwxuzwx{z{yokpz{swywvvusomosxzvplvwu~{z}xqt~~ytsxy~wpljlrwv~~{ywusommqvyuklzw~uqt}ysv~~wwojjnuxtzyzsprvyzyvsvxy~~wrv}}~~{wsqu|x|rrsppx~}|xsoorvxxurx{|~xzwrrx~|}wrqu|ztposz|z|uopvwroqsxv{}xvx}zssz~|}zsvxtstw|~~|xvsonnprsrs|x}|wuv{~tu~}}vsw|unkow~{x|wsonnpppot{z}wy~~{}~~}}xux}~~zurqu{yz||wx{vpnrvvsv{vvz}}wu}uqtz}zxtnnyz~}wrooqsrryzvy{y~~{v|~wtv|~||vtuzx}xtpptwslv~{wuvy~}}wu{xrx}yuuw}~{yz}vpnooomkp|wuw{~}}y}}{|ynmsy~~w|z}~xrpppnkku}zy~yv}||}~{wrprv||zz{}xpqxysmls}yx{~~x~|}}zy|~{~~}ytonry{z}}|ywusoklw~{z~xu}~zuy}~|{~|{||wutuw|{}~zyxwsmkuwyyvx|~zy|vw|~}|{{z{z~ytqqty~~yzxz{trqqxwsrrvxvplryyt{|}~|}uqtv~~|xq~|z|lqsmnomimzzy|u~|zx|z{~{xuz}x}umrijruszsn{~{wwsy}t}swy~t{~}olvujrketxvy~~xv~}ntux{||{mt~}zyyx||~~tpwy~w~|vwtxvt~sh`gzqmxz|oi}|{ttuqvzvpugfgxw}kt~v{v}xtak~}|vlmgmx|svx|wur|}pwr|z|xm{t~|}qv{}}}vrvps~~z|x|nltv{ncyx{opvvzyxzsov}xy{qsv}xxl|yu}vzyp{|~z|z|~}|vqrz}}nxqu}hfgtn}zgtx}z|uryw{{youjy{xswz|~{yrewyuqzvryyqy~ylnqfrnqluz~yiv}z}yts}wzpzzyr{momfwr~{rylvz~znpy|}~rr}r~ur}uiiswuzeww|{txxwrmr{~wzueuka`oww{zf}|~zvvy{wyukqyyym||wq|whx}v~q|xq~~opwqjtnvwt}oqxss{zqlvyuu}||}~|xw{wvy{olwpnk{zwwruryzz~~txsxt{xwkwyihux}z{t|y|~}v~~vw|ypknprz~sqhwmemyntylw||yt|}vy}rsswmxy}|{u}}{}~sp{vwii{t|wuumrxrvrphxumqxmv|~w|~vuy|~||}npz~zu~wrlqjn}wp{uxux||{txyz|sy}trnovsv|ovwk{|~~}}zy~{~zyrlmruwunle{xl}wp}zpyxux{z}wyzpunlsuz~||}vp|vqz|tp}xvywq{yuslts}}kgrxo~m}}xz{zwrz~|~tuqxt~sxwjqgi~{}w{wrw{v{xz~|quzrrrrhuz~yjm||{uvzzv~wzyss{zsqtzomwn~uyr|}}~xsw}tq{zmspr}rmx{x}|}|tvy|u~stwu||~}wo|~tnjxwjl|uvk~{{{wpu{~}vy{yu}}tx~rw}jfit{v~}~~xy}wyu{~rsxpv~|ztw}uurs{ynpyx{}}~~yx}z}w|xsppz|}~{x|}tz}yuyz{|}qw~}|~w}|sxp{ngiywoy~{}~{|}py{tfr|q{|xw}}}slhp~yuzywyy{m|yzxx|~ttw|{{yy}~||x{rpvrnmxsmzy|}t{u|{wpvzwwmmyqxyoswr{}tz~z~}y~y}tmuzwxsw{zz{vz}|msvv{x{}yt~}szwyskfj{z|~{}{}zzvy|wxyrpsxzq}uzvpltmwuv~}vs~{|{t~}qstw{x}yz}x}}~xwtu|xqjmwvq}||~r}{w|{wy}xzwwv{{nszwvpr|}v|}|szv||}uuwyxuxpqru~}w|~xzywx~vq}zzz|}{osuqmo}xp~}{{|}|ttz{nnygq|w{~}~|t{|{wrwzpqry|ztw~ztpw{z~~{z|v}|~vv~xv{ukbi{x{}~xxzz{{qxypt~yp{|owqmwy{}|{}{}xwy|x{}rnqlsw}ywxys~~~}yy{}yw}v~tvtoy{zrov|y}}~{~y~uu{}sn~teszurz|{}ru}~zzzyxztz~y}~zsy{yx}wyuyvyzy~~~||~{~yptomjpzzotw{{zxs}}~|vns||yzyq~{mq}~|{y}xx|w~}}{xy{{wonus{ust~}tx~z}wxxww~xszxyzqgpy{|z}}~ww|}|xyvqxuptwpqq~}~svxx}x~zor~xs|y|vvtq|yt}{{}z}{}y~wxmpzztfitwr}~z||}w{|{wuyxxxyxwpr}zxsyy}}~xw~}{|v~~ywyxtywkvxvx}uwz~{}zwx|{s}~y~{{wtwvxsy|s{~~||z|}xq~u|st{|ojqxrwv}yt~t}zqw}z{}tww~|~yqutw|~zz|}xz|z|yz~~xqwxx|snfkw|twztw~|{}wysst~yqr~ytw~trr{}~}}|~}~vv~x{}x~wlvvqrz|rpv{~{~y|{|~v|||wup}~vov}zyxty}z|x{v|qutlmx|uslvy~y{yyxur{}z|zyvzzsyzys|vzy{~||~{}y}|zz}totqslowyos~~xt{~z|{zz{||{wxu~xoowvy|zuz|~z}zt}~}zyz}zuwurpvxmvxww~y|y}x}{}~us{y}yxnu{{{|u{}}|}u}zx~~~vqnwsqtxnnqy}}zy|try{|vw}|yxzzpz|{wx}{w~z}wzz{xqqjuuuqiouyz}v|yu}vwzx|{tuvmo{yxpz||~|{~{zw~~{{~ux~tpxvjrqw|xtvvx}|}{y{}|{~xv}x{uy~{xw||{~~{w~|~qz{~zvur{sjjrqxwyy{}vu~yw{vxxuuty}zw||~x~}x{x{vuoqrnxrqjkz}rwvxz|y}|{u|~xvmvyvw{trut|}~~}~|{{w{~zsxtv}ymrmrwqwsoqz{}~|z|{vz{~xtqk{~pzzwzsz{|}{x{zow|y|{{pplglxvuwt}}}wyss{{xx|{~zmu|{yu{~v~{~{}}yv{wsqmdosptys||x}}os}}zprplpnwqhhqnrsr{tyz~}v~{~}}|yz|~}}z}z}{wpeqx}usv}y{z|yzturs{t|x|w}xz||vfp~vmfbmkikpinms|ssxz~~~~v~{y~{tmmon|}qz~rnuwv{s}yx~{r{uxzsljfm{sx}~~z{uxvympx|}wloigkqlokn|{zw~~~w~|~yxvx|~t{zy~vlecbrv{y~|{|~||}~}|xumj||}}{}uxzx~znqps}{zsvntpiipnqrkst{wv|zx~yzvzy{uzxuqpjt{vxrqvztxrv{w{rz{txqnorhpx|zw|~||zx~zpmkz||xnmrlknokutzwwzv}|~|wr~~zxx|w||}x|wtkeedgvv{z{}|{}wmpsrx{~y{t}~}zyusms|{psplntnlmp|rv~}v{xy{~zx~}~|wx}{tnnkv|rywsswrwzz|zyw}vu{pvyvkjmvwuuzy{zrnpmx{wwnsnjluuxpryw}|~~zrrq}x}x~}}~~~|zzlnkhegds}~|}wy|wwuxrmn}~{~|y|}yzsoqq}|uqvqmklpykr{ywxz~u~|w~}{xy|~w}xxrnoqt}|wwxqpvy|x~}x{{yzxo{yytnqqimz{t{zwwsoomuyznqqquvnw|pz}~}~|{wsrxywxw|z}z~rruknkfcgk~}|{v~|z{yw~totvp||y|xx~~~xz{urrtv}wtqimvqlxszxtxyy{yr|~wutz{}|wvvsnlhw~svuuzz|ws{|{zw}x|{nxwmipqy}|{~vysnlns{|vx}rmwzrvsxwsyzyzy|~{yvx}z|}w{{toxlkjhinip}vw}}v~rw|womlz~}{u{yx{}}x{wspml{}rpyskpvvvkuywxwvzzx~zyursry~~}wxrjgont}y~{yxsztwz{|~o|qqstsrq~}~}zzuvrqstz|yuzyssu}xts~x}~{wxvv|ywx{x~|{|psulsskcimwv}{}{}~t}zyronno|w}{ywzvx~}{zrkhhs||usuvunrtnzxvwz}|xstvtqqo{}}}opssigl}{yyuvwu|twz{vxwu{xurqs|}|~|xyzwzvzzwsop|xysw}|~x|x{yw}{}zxxuw~{~yzyvx~{~~}z{ssuoknmgcr~}}}}sttrnmkx}~v|~}{~|}trnmmpr|z~vpprqxor{z~{oxxqusqpu{}~|}ryxvskmpmlxv}zzvuxww}w~|}ws}xywtsrt|t{~yww~z|v}{{uolkr~zzz~}}~|z|z{}~z|z|zton}|xy{|~}u~|{zzuwmrqhejmzv~xu}sssqnoq}~{v}|{w{nprsrsr~zwsqqtqwur|yzqs{qrtwywq}y{}{t{|kptqjhix}{xzxvvxyxz|y|wxxvqolx||}v~wx}zzutpmlmo~}~~yw|xxwrptyu}|}|zxvy}}vjyxiilnoqv|z~zq{wprstvx|}~v~{y{wmyvrvussu|xuurqssyx}zyxztozuv|wpsz}{|||tn|uqmjhjlx|zzxx|~~z|}y{wwtpkmp{zwy~|yzzx{zmonmlns~~|y}tywyntzvoq|~xvwwy~t{|w|zmsnmrqpqs{|y|w~}~vr|utyzxvszz}~xw||z{swuuutssq{vwutw{}{w{zyyyyq}tqtzvqu}}zzu|sikjiijs~|}yyu}}xvooopsu{}wvxw~|vsrynmnmnsy{ywx}~~pw~xuptwvu{}yyvuwwzz}}yqm{vprqppov~w}}z~~}~uw{{zupooz}yx|~~~}~v}tusoijt}{y||~}|~sw~|{z|tzwwyrorvv}towmjkjikp~{{{~}uvzoruvuuw~yxuuvwxz~~|xnwvmpsurlr~zy|~~||{yurzxrnmu~w~{xxvvz}u}~yz~wu~uqrpkihs{z{~x{xusqnno|}~~zxv~}qmkmqpn|}{xy~sw~vs|oquwww~}y~~}zrlwpjlnoqryw~{{~}srxwyxvuu}~t~xwvuvxz~}{zyos|uvsmlsu|||~~~}ozxtqommp|{{~xyy|z|}}}s{{pnkhfiq~~~y|x{qrqpopx~~z{~u}{ryqlsslio{z}{xxw{~u~{||qzwuyyxwx}~yy~x~zyxolwqostqomv|v||}|}vy{xyxurox~}xvwwz~w}{zzztxrkpvrmn~y{z{soppnnq~}|wx~|r~~stylhijlpt~|||}xtv}rprstxz~}}ywvx~{~xkp|tmkosts}}z|yxxxz~~~xpzyyzyxwx~{{z}~x{|~|yxwulv|ssqollq~|}~~~~wwwuqmq|{zz~x{|}}}tv~|{{}{otvssompuw~zqzvnpqqsw}xv|yz}v~|qjsminprsw|z~{}{vvyvwrow~{{}}}{yvt{}vsv|~|y}zwst{~trw|{}~}ytvyws}xw{{z{}yqm{x|ww~stz|wuw{plnow{uu{||zuurqtwztp{~||}|xpy{qv~|qovwz~|{tqqt{t}w}}u~{zwvwxwtznpuspv|wtw}w~yyslffqyvqzvpw{}v{|y||yz{{zxqpsm}rhjkmnfvmfmssmhtz{v~y}~vmlxx~uz{{ux~~xy{wz}}}uvkrqpriwqnqwytks~wv}yzwwyvytuzzsjir|~uzq|x~}}vpwz{wqjvromepljiswtiozs}v}}z}z|{z}vysrt~{yy~|~{~{y|||yr{}~wo~{vslsupku|ypr{qzwy|wvuyx{w~zy~pu|upnku~xyzu}z{umt}yy~|wkzyrpgkqmfnwtnnwpvwuyxw}xwzvupw}w}~~v~~}y}xqt}qzxwnmtwmpzztrxws{r}{vr~||{zy}~zyoxwprlm~w~}y|y}~wxomw{z|}rp|ttmgksniswppqwpzsxyuz}wxvxtp~~y|}}|~v~{uzuov~~}t{}xwnmtyorzvstwxu{r}ur~|y||xxpvuqsloy}|z{v}|~q~znow|zxupyttnihtrkrunrrwuxww}uy~~{zv|vywr}~}{w|~~~|uy}||}xqxpv}~zyw{wxrmo|vqwvpwv{wzx{{r{~y||}|surtqvro}|x{z|zy}}{}tqsov|v|sustssmgmyqnsnnvuzwyz~{v}~}wzwwx|wyw~~x}}~~zuy|}{yznxrw}{zy{wvxuply}tttovxx|x||ty~~z|xquvrrwwr~}{xw~x{z|z~}xun}wqy}y|qxupstpil{vpqlnwvy{w|~w~}txuzz}~uz~~|}~}{tv|zw}yvpx~sz~zytvyvpm|~vtpnxzww~~~u|{}{~xoxyotzxv}{ty~~{yv{}w{}tsr}vw|{}q~sovtpht|vrmkszt|yy{y{{xxtv~{~yw~|~|{|wrw{uv}tst{yx~v{r{ywnv|wpmu~w{}wyzy~|z~}~x}vqrpzzwzxu|~}}ux|xuurvwxw||s~znwvukr|zulir{ux}vz|{|}|wwt~~|{x{{y}~yqv~xursrxvxz}}y~t}|zpxynmv~wz~wzzz}}{x{}}{|wtspzxyy|xxx~~}~uzyxttrzswz{zx|qyxwmu|}wkjs{uv}v{|{}z}~|yzs~|}z{zx}}w}~|rv|vvq}vo|su{||w}}}sy}plv~ywy~{z{}|~vx}}zyw}yoyxzu}~~yyxzywx~xxs{{n|uq{|z|wyyzrt{~}qipzys|{{{x}}{y~z}}uy}{{|}|zxv{}}}~wszvtrs}ovznx~{}}yv~ynp{~wy~|~vz~|}{uw{~vz{{vsyw{t~|z}v~{{~wzyuvr|wp}rp~}|~~|zzzrx{}pks|xuz~z{u~zw~w}}xz{~x~|~zxx{}|}{{}wtxrsqrzqu{mx}|yx~{pq{~yx~{uy|~}tuz~}vx~wyvyx|t}{zyy{~|~}xywrtpvyqyxn}z}y{sx{}vmq{zww}}r|~}uy~vy}||}|{|}y|}}yzy|zxyxuwqoqouurzvq|}}|w~wqw|yy|q}xqy}||~qzv{yyv}~|y~z|~||}|zzxyxpspruvuzrvz{}x|tx}|{qq{|xv|ywvvu~yxy||}{{|~~}{}|{xx{{wvvuxpnpnrtvvyr~{}|wysx|yw~~q|yqw}}|~r}|s}|xw}{y~}~}~|z}{www{vnsossxvxvwz{~y{t|}}~zsu}{yv~zvv~uvxzw|||~z~~~x~{z}wx{zustxxmpnpquxuyv~}~x}xw~~{xz|{s~ur}||ztxuz{{z~~y{~~~z~zz{~}xtu{|prqqrt{txv{~{|vz}}|xv|}zwx~~}t|yt~~x~v}}}{}~}}xz~tyw{{vqqx|opooqq{tvxyy}~{z{xy}|uzxs|~}||v{{s~}zyz|y|||}z|v|z~}xss|~qspqqr|styw~v}~}{zz~|yvy|zwyyv~~z}~v~z~}{}||xz|~szx{ztoq||ornpoq{rsyv~y}}~{w{|yyx~xu}{~}|yy{t|~|w{{z}{{~z{u{~}wqr}stpqqq{uqzv{w~}{{~}zuy}w{w~zx~|}z}x{y~}}~}|~yx~~tzz{zuooyssoopnxwnxxw{}}|wyv{yzzw|{z|}|xxyu~xy|{}{}}~}}y}{x~}}ysowxusorouzps{u~~z{z~{wv~~xx}x}y}~z}x}y|}z|}|~x{|~}t{~{{xrmp}{ssmpnpyunzyz||~{v}~t}zz}yy~w||}}wvzv~w|{~~}|~}}z}{w}|wqnwxwqoqouzrs}x||zz|ww~|zvy|z}x~v{|z}{|||zz}|{wv~}zwqlmz{uumonovxqw~z{}|v{~~vyy~zzxx{}}ztzzyy|}|~||}|{~{z}xqnr~{zvousv{|u}~}|y}zxj}~urvtq{z|z}y~wq{~{yyvwzvojhrzuxqpssx|{w||{~y}~yyy|}~u}}~wtwsu~}uv~y}|~{{pr{rpmqltyupjgkwwwvnqpswzvw{}|sstp{sjqtq~|syu{~~~z}~zvzytu~wzywztopz}xxz{~~vyyz|{xst}ysyzxzs||z}vqsztxxy}|yrrzvptltyrjedktqwplonrtxuw}~~wpowtu{t{ywxwwu~}~~~~}zy}x~t}vuy~u}x{wqpv|~xzz}~~~}|y~|ztrx|tv{v~wv}|yts{vs}~~w~~z{sqv|tyur|xngejspwtmonqrvxu||{}~}~ysqwsp{~x|y|ywu~{{}~}zy|yzv|}wux{}v}|zxqpw}{yzz||~|y{zztsy}txzv}}yv|}yuv}usxxy~}{truwz~x}{w~{ohgorpysoooqqtxu{x{zz~~}~~zuuzqptv}~~~~ww~yz{}yy{zzy|}xwyxwyx}|yrqy|zz{z{z}}|wv{|vty~uyzu|{|w|}zyvx~vsus~||}vtvutv}~|}ulioroxupqoppqwxv}w~{x~}}zvv{uospz}}w~{xz~{y{|y|||~ywyyuux}y~vrv|x||zzzy~}{yuy~zuw~zw|uy{{|w{www}|twqu|~~ytuvsqt~tlmtprztsqpqoryyx{{~zz~~yvuw{rrro{~}zzxz{z{}}|x|~||xx{yurx}zvsx|w}}{{yy}}|}yvz{vx~zz}uyzz}y~|tuw}{uxqu~~~}|}}wtuwtqs~~xpouqrzuutrqpqx|x~~||}|{~z{uvupx|rnrx{||tx}}zx{|}z{y}}~|}}wv~ytwzrnqmooqrqu|~z~}~ux~|{||yvzx}|xwysstxzx|sx|}}}|uy}xuy||rlqx{}|xwuuy~rp||xulqqqvuv{qry~}vv{~}{yyzy~~x~{yshisx~wqlijpvtty}w|}~}|w{}}z~w~vv{}|xxusynq}xvwwz~|vot|x}}vsu~{stqovx{|xw{}{ysuxzutxxu|ynoxz||rtx||{yxx~w|~|~y}}{x~snmjoux{upqvzws}~t{}{x}~~z}~yvyv{{}}uzzmw~wtux|~zrpyxyzvtyyqsqswuzxy~|{}vsx{vy|yx}uns}{zusvy{{zzyy|z||}|~z{}yzznmmnrr~{utw|{sxvy|}~}xz~z~}}xvv{xw{x|~quzssw|}yrpxvx~zwuw}qrtuur}}yy~}{}wru}{}~y|xpt}y|usuwyyyzyy}~|y}{x{}|~~}|~{|}onpqnmz}zxz~~tzv{~~}z}}xy~{}~|tx}vsv{uv}srv|}yrqwsx}yxvw~rpuxspz{}{{~~~|{yssx||{{st{x|vttvxxxzzxy~w{|ww}~|~|}}|tmqsojux||}yx{z~~~~}}y~}~zy{}~}~|~vt~ysrx|x{yqsy}zuqwtt|zxxwzzoryxouyx~|~~|yywtux{~|{{uxyyzuutvwwyzyxy}tzvx|||{}|~plsulnvu}~{||~~~~}}~w~z|{z||}~~~}y}st~xssz~{|zrtz|yssxqu{yyxxz~yor{wqxxx~}zwuvuwxz~}z}yyxyzvvuy{~|xy~|{~}{w|~xt{~}|}~ulsxpqyxrsxouzsyvxzy{{{|y~~}yywoyxrqu~||vkmtsmmopuwnjtxqvyw~~v|zrtwy{|~plq~wqnlhnwllwwrqv}|{zztx~|z{zpnyxu~~~zvtu|xxmjswnkptuuuw~|~{y}yv}px~|z}wvupijuy}{wsqrnfksnrvrw~||~}{{~x{uu|yqvsmrw}~xpryokturquz~~z}~yuyywt{|{{{tq{{{urrtwzwty|rlqvvusv|w}yz}zw|ty~}|{qpolioxw~~zvvskptqwtrx}~|}~{}y{~~x|~tv}sqvpotx}ztyyqquvsswz~z}}|}~xvxwsv~{|{{{uu{z}~{srsuwzxy}|pnuxvss||yxzxz|~~y}yy|~}}xrpnjkutw{xxspsrxvpty~~}|}zyy|~{|yt{~~xswqnptx|z||wvuvutvx{{}|}z}{vvvst{wx~{|zwxzzusuuuv{~}vouzwrr{}{zy~xwxyy{~|~}|{}}}~|urnjmsns~{|ytrtzupsw{}}|}}yyy|~}}{u{{|zwyqooprv|}}|vwwvvxy~z|~x|}xvtruzsv}|}~zvz}x|xuvvuux||sv{ysrz|{{|z}wvwwvwz~{{~~~ysolnslox}~{su}xpruy{|||}~{}{x{}}|}xx|zzzsooppqv}{wzzyxy|{~{~yw}|wsqtztry|~|xvyz~vvwvuvy}~{uz|xsx}y~|{}xuvvvuvz|z||~xqmlrpksy~{rzvrtvy{||}}}{zx{|||}~|yz|~~{wpoppprv}|y{wu{{tomrtmmsw~wuyz~{{unnpsuru}z||}|vz}zw~}xvyz{|}wjxxrsy~{~~{}wsu|yrutx|~u}~|~zxxrpqrqnv{vvwvxzurwzwx|yxy|~~|wzuw~xwz}zv{|x||w{|tquzsotx{~|tvv}{vtsspnxyuwwwy{vtyzwz|uw~y|yloyz}~~}{~{{v{xpntumqx~}zuuz|zywps{wuvutvwrrvvtyxt~{y|~~~{zplzz}z{~wv}}tnpvqow|}}w}yv{~}{vrz|wxyww{yuvxwy{twxz}~~}|}}|ywsip~||vyyomstnt|{~{~zuy~~}xrv|wvxvtvyuruvw{wuyy|~~}||||zyxnn~{z|~wu||slquoqz~}~y~~wz~{tt|zwzyww{zuuww{yt{~|wz}}|{zzzywwojw}}}{v{~wnpuppy~}{||ux}|trzyvxxutxzurtv{ysz~xz|}|{zzzyyzrlw||{~{ux}xooupow|~}~~~xz}uszzwzzxvy}yttw|{sx|xx{|{yxxxxwxqjs~~~}xx~zqpupow{|}|ux}}}tszxvyyvtw{xqrv|ysx}yy{|{yxxyyy{rmv|||~|vw|yqruoovy}~~w{~}~uu{xwz{xvz~{ssyysy{xxz{zxwvwwxyplt}~~~yx}{ttvopvx|}~|uy|z~~utzwvyywtw|{rpw~ysx~~zyz{zywwwwy{snu}~}}~}xvz{tsuoouwz~|~wz|~wv{xwz{ywx}~vqw|tx}}zwyzyxvuuuwyrlry~|xy|xuwqouxz|}~}uv}|{yuzxuxzyuuy}xpr{}tu{~yyzzywvuuvzwnqx}~~|xvyyuvqmrwy}{|~~~yv~}~wz{wx{{xvx~~uqyxuz~|zwxxxvutssvwnmt{}xxzyxuoqwz}{~}~~}|}zsx}xv{xuxzxuuy}ypq||ux}zxyyxvutsuxtnsz~~~}yuvxwvpmsx{zz~}}}~}}~wv~w{|xx{{yww|~wpv~yv|~|wwwvutrqqsunnu|~zvwyyvopx|z{}|{{{z{ztwzw{zuwyyvtv{{sow|wx}xxwwvtrqquuos{~}{wstwxsmrz~}w{~|{zzzz}yu~yz~ywz{zwvx}{sq{{w|yvuutsqonptpnw~~|vtwzyqox~|x}|zyxxxz{uw{w|{vwyywttx{vot{xz~yvvutrqoottpu~}}|xrrwztms{|wx}}zyxxxz}ywy|~ywz{ywvw{{tryzy~zvtssqonmpsoqz~|vrv|zpr{~{w|~{ywwww{yu{yy}yvwyxvtuxzuqvyx}~yuttrqonpurqz~}}|vprz{qow~}|}vx}}{ywwwx||v{|{|xxzzxvvx{xtvyx}}}}ytsrqonmospnw~~zrr{~tpw~}wx|~|ywvvwz{ux{y~|wwxxwutvyxtuwv|~}|wtsrqpoptqow~}}}yrpy}tou{}{wv{}{ywwwx|{vy}|}xyzzxwvwzzwwwv||{~zurqqpnnornmu|{sr|uqw|~}xw{}|ywvwx|ysx{|{wxyxwuuvxywvtu|||}wtsrqportoow|~~~zrqz~tpuz~}|xvy|{ywwwy}zty}~~yyzzxwwwyzzyut|{y}|wsqqponprmltz|}urzwrv{}{vy||zxvwx|zsw}}}xxyxwvuuvxzysrz~y}zusrqppqtomtz}~~~|vqv~xrtx|~{|vvz{zxvvx||uv~~{zzzyxwwwx{|wrxxy}{uqpoonorokpx{~ztu}|uuy|}yvy{{ywvwz|vt{|{xyyxwvuuuy|yqs}{x}~ytqppopsrmow|~~~{urw|wtwz~|||vuxzywvvx|zuy~{zzyxwwvvw|~vqyvw|{upnnmnprnksz}~ztu{{wxz~|zuvyzxvuvy{vu}||yyxwvvuttw}{qr}|vy}ztponnorrnpy~~~~|wruzxwy{|{}xstwxvttv{{vz}|zzyxwwvtuzzquxtx{wqmlllnrpmr{~~~|wtx{yz}|}}wsuwwuttw{yv}{~~yyxwvuutrt{vpywuz{vpmlmnqtppy~}}|xttxxy|}}z~}|ztrtvuttuz|x{}|zzyxwvutsw~stystyxrmkklnrqnt}~~}{xuwyz}{}}|xsruutstw|zx{~}yyxwvutsqs{wrzvswzvplklnrtpr|}}|{xuvww{{{~{{ytprttstv{{x~~{zzxwvvussy|swwrtxwqljjlpspox~}}zxxxw{}{{{{wqqstttvz{x{}{yyxvuutrqv~~tu~ztsxytnkkmqtqpw~}}|zxxwtx|z~~zy{wqprtttuz{x{}{zywvvusrvvu|{truxtnkjlospnu}~~}{yzyux}{zz{ysprtttvzzwz|zzxwvuurqu~~vt{|usvyvpllnqtpnv}~}|zyzwrw{{~yx{ysoqstuwzzvz}|{zxwwvssw~wv{{truxuokkmqrmmu{}{{|xsw|}yy|ztpqstuwzyuy}{zywvvusrv}}wuz}vsuxwrmlnrsonu{~}{{|yqu~||~ywzzupprtuwzyuy~}|zywwvtsw}}xw{{yss{}yvtvusuxy}{z{|}}|}}||~~~y|rxzlry}{x{|{~|xz}{{sp|~~{}}zx}zuruvux{z~{~|z|y{{vtwz}}|z|}|txtoy{wz{~}|~pt{|wwz}xx{ywznpt{xqrvxzqtzt~{vy{||~|}ynjs~~uuumpqw}|uwv}{~zx|zxzqusx|}{~~~}~z}{x}{|tttqwzux{x|xyu|xy|tt|yxxputxx~~{}{{wovtx~~u|z~xrsqoluspvtlqrpvx{}zy{zwqqrkqyv{}z}yz~|}{zxyz}yzvswyw~~|v|}{yzty~~zx{vvxpq~yuxzx}~uuypmrnpv{~{{ukszxy}yrwwqrsuuvsy|tuzsvzw|zuzwr~}}yxwwutop{{{xy|tyz~y||~~{{v}{rrwz~~||}zyzvsy|~}xz|z|rvxtz|uuw|}}xx~xsupnwv|}{rprmq}}y{ztxtswtmpvoq|xswuy~{x{~z}|z~~|}{twtkfpor}}~~w{~|~}z~{tzztsry|~~{x||~zwx}yx~||y|~}{w|zv{}x{vx}{stwnr{slooy}}}}uwvpryzxx}tupsyupt|z|wox}{x|~y~{|}~{{zvtztmmuuz|w~||w}{{~||}~y|zxxqxzyv~}|}zz~}{svvsxxz~{twvnwyvur{|xv~||qnxzw~|z{wwujpyrrwx}x{~zuqxvvzuxxsv{x{w|}{zw~}~~z{{unsomss{|}||{}~yy}uy|ttouzwu}~~z|~{x{wzuu{~~z}~zs|}~~zvz}uyuvwpousxxy}}|}{vlq}|}x{xxyrxtxww|xty|}}u}|yuywt|~wtsztokrouzt}|~x~syzvw~xv}qopv|xxzx}~zvw|vz}x{vx{tvuxyy|yx|vu{ssu{ux{~yy~xvqmy|slz~}swxruvwqx}}qvz|{vtz~wv|~{z}}~|{v{siqkolv}~x{~x|zx{sytxyyyv{{yu}zuywu~ywwwz}z}zxyu~qrx}{zrzyyvptoqz}{x~x|}u|{whz~xvuzyuv{y|~xu{{~uuxrur~~}yyzwxoprisvp|w}yz}|{yy}|w{yuz{|~{~~}|{z~y~vzzuvtqu~}s{rvr}~v|}~r}|wvwqwxxy~~z|ywln{z~~~zz}|v|xw|x|rz~xyyo~swr}~|}}}y~wsxmlqkp}v{}~~}{~yy|||wzutzz}x{{}~w|~}{}~wzusty}}|wxvv|{}v|}~{svvqwsxsw~{}~|}z~}sm|xz{v}|~x|yw|z}u|yyzrwvs{|y}z|||zuxqkpkjypty}|~{x|}z~{~wv{z{s}||~|z}}~vwuusxxvv|~zyw}x}vy|{u{xwxtxpsz|~}~ur{yvupu}}z~|{w{~vwwr~ust||wy||~z{|sprjrtlln{~z~~|}~{vy{zuszzv{y~}{|}{{xzwzw{wvt}y{}y~~|~v{x|xxvvtnstxv~|~~|}~s{yzrpwvxy}}~}xzyvwt}yus~z{~vy~}{}zrtppxmjipwty~|}~||{}}yu{zttsv~||~~~}yzz{x|xxuzwrwy|~~v|x}zzxyyqusvswy}|}~x~|wrutprszw}~}}vxt}wvtzz~ux~~~}|wwtv|rokrtnpxz}~~zxyy||}zvt}z|zqq{}~z|~|xwxuqojrywy}}}~}}zw{yysy{xt|xw{|~~}xy}wxv{vrsqmtzv{{~}{}xzzu~{w}xt~}||}y~yw{|y~v{{twwrxyvyzw|}|}ww{{vyvoy|styx}z|yy|wpsjlvqoz{}}~z}{x}|vu{vx~{||}}~zvzxrvsooqsxzv|}yy}v{{sz{}ysvz|}}~~|}uxynrzsqvsx~~{~yuzwx{zz~{z|t|{{~{}x}}}rqrrrxqkuws}|{t~{z|z|yvu{uv{v{{zyy}{|w|xrtqrvvsw|x{xz}}{{w}yotusv{~{~y|}}|yvystulmxz|{z}{|~xy}~xuwpu|v}}~}}~}x}{tusoprwvwsw|x||~~vuyvvzx{z}}}~~wuyqs}wproquvtxy{{yzz}ww{xyzu}{~~xz{vtyuwvnruoqwuz~|{|u{wxu{{vy{|ur}{~}|~}zwztvtputpkspr||~}y~{twxvvs{{~~|~~w{{x~|x~{zqnw{v|vst~~}z|ytwpu|ssqy}~~~{v|xwuuzuvrvysrvw|~wz|xxwyxv{zv~{xzxqyvqolutqpx}{yz~|~}yy|xztv|{vu{vz}y}}uxtuupnspttw~}z|yyv|~yz{qv}}|~~y}||xryvtnrmkvzqssy~}~y|z{|v}xyyx~|x~|~{zy~{ut}}xyuspyssvw~~|{z{t{xrqpz{xv~~{|~{x|{wyuyxrqvuwptzzz~~{~y~wyv{zuuxwz~{}w{|tsow{wsv|xyrmw~|~{xw}yvswqv~vxx}{wyzurlkmkhkmtutv{~{y~}z{yyyy{{xyqz{u{wuz~}}|sruzz|}qqzsz{sxtzzww|{{z}rxrxx}}}~zxrxuqzt}{s{||xw~~y{yzu|yv~s|}}q~ppwuwvx{s~wjsky}pq{y|{}~~vu}qvxynxv~~{y}yvuxy{xz|l{xzys{{nw{nompqhnlkynoywv{}~w|ys}zorltot~~|~w}}z|}}}z|rqyzywr~wokjlqlkgwtlv{wzw{{~zzvtp~qnmlvo|{}}}~~xw|ymv~|rx|roolpqkkm{mrzxyy~wy}~{xy|ovtnsjrtu}|}|~{|zsx|sn|{}qxwsqokuqjjuykxywv|xy{yzzywym{|ppsmxs||~}~}vqxyss|yszutrokwojjwwl{|y|wzxz}yyzvwo{xpqnm|w|~||}~}}np{xuu{vtxvtqmlwijq{ql|{~zv{~w}z~|{z{qs{upqmw~{}x|||}slv{ww}|~}sutxuqissgpuylrzxxw~zy|}w}yzrwxsspo}}~{}}~}}}~x{knzxzx|xtsyzsljwijvxskwv}}xzzz}z{{~ttwtutov~~}~z~~w|ojvxyy~|vru}wpiupduxxnqvzwv}|{}w~zxsssvuqu~~}~~}{~|vxsjtyx|yzsr}ztksvfsztqmsxxu}}|{{zy~~{vsqvwru|}{}||vuwlqvw{w~vq}ukrwho{stmpwytz}|y}xx~}yrovyrv{}y|~}ut}wlquv{wwq}umtwjp}qsmmwzsy~z{xx}|}{qoxxrwy}w~|}~tu|umqtwzwxt~uovwksoslkw~xs{~y|vy}{~|qozztyx~v~|~}}vv{wpqqwywztxouunqoroiw~zsx}w{~vx|z~rmyztyv~vz~|~}uuywqppwzx}tzrxvqprprgt}ysw}vy~~yy|z~tmw|vzw|~x}z~}y{|wuwwtrotzw~t~uwvtq|ylujo|zvsww{~w|{z|ymr{xzy{{~}|w~y||w|wuuvutpqzw}xzxvxtsw~mrqjz{xs|yt}~|xz{y~qnxzy|x~~{v{{ztyxuttturnwzyw~~vzwvv|xlulq|zvuuxz|~z{zw{xnrzy~y}~~}}|x}x~wzrwtusruvpr|x{|}|xytxy|rntly|zu{zs}|}~{}{zx~tnuy|y~~~{x{}w~xsrustqqvvpv}zz}|zzyu{z{pptozzxvwt|~|}|~|zwx~|rovz~}~~~{~x}~w|zznqrstprxvq|||}|z{wv~zyorst{zwxtw{{{~|zyvz~yqrx|}|{||{yzzzztylqptqouztv{}z~~|{v{~xunttxzyw~{s}}|y}~wzxx}}vqty~}~~z}y~yw{~{vonortopxyt}|}z|}xx{wspwxzzw{tx|~}xxxyv{}zsrv~}}y||y{y~{zytwlnptpnu{v{}|y~}~{wuvpt{yyxyxu}}u|t{wz}|vrtzzzzzyx~x}wz|nmotqmr{xz~z{}~~|xwvsq}{yyx{t}}}}w}rzxz}|xtsxzz~zzxy|yzxy{qlnsrmpyyz}y||}}xxuvo}~wxw}u}}~{yzrxzz}|xusv{z}zzx~|~z{xyyyrlnsrmoxz{|x}}}}yytwo~wxw|w}~~zyyrxz{}{yvsv~zz|y{x|~|z|wy{wtmosqmoxy|yw~}}}{xtxpwwx|x~~~zy{rx{}}{yvrv}y{{x|y{zy}vy|uunnsqlnwy|xw~~|~|{syqxww|y~~y{ytv{}~{ywsu|z|{x|{yzw~vv~svpnrqmmuy}xu~~||~rys|yww}{~~y}xwt{}~{yxts~|{}{x{~w|u~xrtttmqqnlrw|{t~{~}twxx}xu~{~~z|x}rw}~|yywr{|||xyy{uz|p|{pvoprolov{~t}~}|~zszwzu||~|y{}vt{}zyytx||~|~zw~~w~xu~urqstnqpmlsxyx}z~uwz|wv}}~~y|}{~|tv}~{yzxu|zz|wz~z{~ty}|oz{nvqpqolou}v}z~}ty|}u{|||x|{}|ysx}zyzwx{{~|{w}}x|zt|}xn~unvqppnmqwzz{~zt{|u~~{}~yy{|}zyry~|zz{w{z{yx}x|wt}|}tp~qouqqpnnsz}~y|~yu}~yw|z~}wzy{yxs|~|z{{y|yz~x{|~}z|~}tv{{nv{mrurqonqv|}y|~~vwv|}y|~yxy{wzuu~}{z|z|z}{~~{y{{}|~wsz{|un{umtuqpnoszyz}{u~{{wzy|wyv|wzsz~|z||||x}{~~y|z}~}{{{tw}{ynvynpvsqoorw|y{~wy~w{x}}wyv}t{vu}}z{}|}wyz{|{}}y~uu|~z{qsysmuvqonquyz~{w|y}|w{~wxvyvyyt|}{{}}~wy~|{|z~w~~vw{z|zrv{uqprs}|||x||}|x|~~}~|}wrwz~x}{~v{{pqy{tuwvopwrmjmpyyr~xtp}v}xz|{ytxxzwxyv|vy}{yxvxuy~~ylkywrgjjlghqlhgjmw{|~{x}z}|x}~}|vsrn~uyzv~yr{vwxwzy{~xw{|sy}toryx{vxsr|xrpsvvsx~v}|sttvvuqmr|~yruquxryvtwuttvwq~~}|qkmu~zzrpmgpngdfiqtwz{}wx~z}~}zzwtrrsly}}~uzvuvswxz}}xuwwxs}vqqvz|zwzry}vqsu|ztt|~wu{yvsmnw|susquu|wuwvvvyruw~}tmlqv~~svomtohgimsrz~~zwy|{y{|xwutruqo~}}{z|twtswz}{vuywuu}~zsqswz}xw{tz~wsux~~xtu~wuzwrmpy{svqt}vt|xsuuvxzry~wz}zrlmrv}twtnurkikpss|}~{yzzzyz|}xttsrvsqz{}}uxurvz|vuwv{xtx{ysqtxz~~}{v|yw{vvy~|~zww}~r~~ysnov~~uvsq|}zsx|trtvw|vy}yuwwrlmsx|xsxsqvqlmpur}~y~|{{|y|{~x~}rpqruxt}x}wwwrtx~xuv~zwxuuwxsqty{}}}vy~y{zxy{||yz~ty|vpot|xwupz||vt}xprux}y{}}zxursqlntz||txyrvvqprvs}z~}}y|y|z~x{}rmprwxvvxxwrtxxuv~{wtwtuvrqv{{|{~xy{{|z{~~|~z{y{u{}upqv}~wyss|{|wt}xprvz}x~}}~}uwvqqnlpwz|}{v{{u|~{|~xwwsurwuqqryyvx{}y}w||qptx|xtopnlot}|wv{||tx~y{zy{|~~w|~}}~z|{{w}|yz}tj~ldhotwstpluuwxrv|spuzy}}{x~|~xwywwvy~{pr~wnhsttxwvtsv|ysw{zv}}yxz~{xu}~y||ysx}|tozrotyyrnnswtxmoxsrrruz|zw{xw}xx~}}|soz~}|zwqrjhpoqsoovokovxs{{w~|{x{|~zzz~zvztq{~~}}yvrqtyvor|vrzxuuw{z|yw|~|}~vy{||xs{trvvsymkxtooptyzxsx~|yw}{u|~|~}zvtu{|rntqqtsosslmtxrz{u{|}}xx{xy}|~wzzqt{vrsxzsn{|pwyvvw{}~{{xzx|~{w||z{vz{vwysxvjryrmnrxzytszy~}xyyu~{~~}vwuw}quursvtotsmpwuu~xv{~z|||{zyvy~{|wzzru|wsuyzrn~zqxwvwy}~}~|xw|y~~||w|{~}xyy|}yzyszvlqytlnsx{yttzv}xx~{u}z~}~{svxvtxxstxxqsvppwuvzx{~yx{~x{tw}{~{vw{wtyuuy{vn{svwvwz~}|{xs{|{~{~zz{}vuz~||{txzqovyolpw{{wtyvy}yvyzyz|~~}{tq{z|yx{vuwzwquwqtvt~y{}~zvwzzvtz}~z|vty|w}}xvxzzruxvvvwz~~{}{zqx|~~~z~z}{|vrw~}vx{tqsztlov{{xuyuv}yww~~z{~}~|{soz}{{{}xvx|zrtyuutu~y{|~~~}yuu}|zvtz|~|x{vtx}z}~~|wtvy|}zxz~{zwox{|~yvzv||xwt}yz{}{w~{wzqoqu}xru|~z{}w|xy|zwx}}{xvx{}z}vv}yrlqwtspxxyzz{|}~}yty}|~{xv{yqry~~{|y|ss|upqqsolpty|{wx|~|}st~y|~w}yx|xs{}}}wy{rrrihotursvou~~}{zx{{|}~zx{~}zvx|zry|~x{xpjryrstttuvwwtps}w~}|}~vtz}{~}zutw}vpqtyzxttx}yzzqqz}vv}|y|ys~}~{z{z{~uqtvvsusmu{{{zywy}zz~{wx|~|vx}yv{z{woxztttsrstvwtpt|w~|z|~zvy}~|x|wsty~tsvz|yutw{wyzqqw}wrz~~}{~|~|s}|}~~||yw}vvwwtvumszzz{zxw{}y|~}}ywy}}yw|}w|{y|sv}vuvusqrtvuqq{~zz}~x~~}zx{}|~z{~zvsuz}uv{}|xuvyxv{vpsy|us||y}}xw~|~}~~~}vzyxxwvysnvyyz{zww|~z{{~}~~{xxz}}zx}~yx~|z~uz|vwwusqrtvuqt|||z{}y|}z{}|y}~}|yvtw{xx|~}ywwyvv{uqsw|wrz|~x~~{x~}}}~|}}x{zswvusrromqxxtu}~zux~~{~zyzv~wy}xxyxurqsuusszz||w{}~x|~|}}}|}ttwrs||wxtx}}zy{xuzxqprvxss|{ux}z{y~}{urxvv}yty|ywwx|{swzyyyywsrvxrn|~}|~}}wsuy~yxrnwzyx{~~|z~yxzzwsppomiirw~ywy~wurrstuuuvwy|yz~{wtspoy|yzz|~zv}zywtx|u{xquz}}|{|~ytrsoo}{xutx}wmw{wz}|ttpqstxytu}||{~~~y}~~yz}ztqonrwsy~ww~z|qz~usrpot||qt{{|~~||}wv|zxyrpqruzxv}}wrt|}vootps|wsx|{||w|}|~wtvunt}zxzyx{vw~~vsppssorwvstpmw~}trstuwz}~}z{zzy|zuvwqwzwy{ngtvqrvz||||zyz~~{y{{yz}{yvuvwsu~wtuvx{uqxuot~~{{~{x|{w{}x}yvyzswzvv|{{~}|yy}~{xwz|wvyywxrpy}}}~xvwvtssuxxx~}|~zuwtlry}tpzxtttttuvxxx|}{yvtvww{xz|yx}sooopsy}z}sry~}zy{~{z}y}~xz~urwwsy}{~zv||}||}~|~yxzvrtwy{ts|xy{z{{xutvy{|zy}{}~z{xpsy|{tz{yxwwvuvvust~~{yyyuy~}~|xy~{|{tsrqpqu|~~vx|yxywtz|x|~z~wxyrsy{|{~|zwvvxvz{|}wqsuxunv~w~~uuvvustw{}z}zy}}yuwqnvz}~vuwvxz{zyyyyxw|}}|{{|yw~}~}zuv{~z|~{xrrsrqrv{|z|}|}zx~|wy}|y}yzyru{{{~}yvuuvsw~}vuz~yrx~~w~ustsrpquy}~}{~xrrljsy}}|zvxyxwvvwyyy}{~zwwy||z~zx}~y}~u~vooqsuwz~~{w}}}~|yyy{}|zwv|{z{uw~|{wwx{y{|z}ytw~~uw}~~|z|vuvvvvxz{~}|~|~wtqls{~}~~wvyywuttvwx|}zxy||y}~|y{|z~x{|qmnprtx|xy}~}|}|z~}~|xz~|yxrs|}~z{{~xssvzz{{xz}yrt~y{zv{tstvvwyz{~|{~zwyzvtnkt|~|yy}}{yxxyyy}~{{xz|zx}||}}{z}}v{zqmmnpruy|~}{zz{}~~~|xy}|zuns{}~|yz}upqtvv|}}vx|x}wy~wrqrtvx{}|wsvwupjoy~}~ux||zxxxyy{}yyz{}~z}~~{z~|~ww}|wrppqsux|~|y~zyzzz{|zx||st|}{~~{}vtuvtx~zx~y}~|}}xttttuvx{}wx{{tjmuy{~vw{|zxwxyz~~|{yzyvy~|||{~}|{|}{xrnmmnpsv{~}}}}}}|}~{wyyqu{||}{z}}z~}ustrpu{y|~}~z}~}}yussssttx|vu{~ujmtuvx|}{|zyyz~|~{|yywswzwx||}|yxtolkklnqv~|{{||{|}|xv||prxzz{}|zz~wrrqnsyxz}z||y{zvssstuvx~~~}xsu}{omswxy{~{|~{zyy||||{}{zvw|zx{~~~}~yxzvpnmmnpsz|~|z{{zz}}yyzsx|||}~}}{|}yuspnuwvy~|~~~y|}xtrrsstwztv~znmsuvxz}{z~~|{{|~|}zwtv{ywz}~~}~}vtwuolklnps{}~}||}~}xv}zswz{||}|}{y|~zurnmtwuw|}}wx|ztqqqrsv~~}}}ztrx{plpsuwz}|~~|{{}~||zvst{{y{~}||xprtqljknqt{}}}|{{}}zuv~}uuy{}~~|{ww{|xtqou{zz~||~{xuz|xtssuvy~|{{|zuqv{upruwz|}|}{z|}}~yuv|}zz}~~~z|}{qqutollnqt{}}~}}}}xx~wwz{|~}{}~}ywz|ysonuzxy}z|}wty|xsqrsuy}|||zuruxrqstvy|~~}~~|~|wsw|zxz|}}~~~{~zunptsnkkmpu}~~~}|}~~{wz}zxyzz{}~}{wvz{vpkovvvy|||{tuzzuqpqsv}}|{|{wssurqsssvy{~~~}{xru{zxz|}~z~y~wnnstpkjlos{~~~}||}}{yy{y{|{{|}~}}}yvy|yrlpvutwz}}~|}uu{}ysqrsv}}}~~|||||ywvusvvttuwy{}ysvzwuwyz|~{~|yqotvrljjlqy}{}~~~||zy}~{zz{{}~}~|{wx|zrlosqpsvy|~~vtx|ztqpqt{~z{~}}}|zxwsqvxusstvx{~}}{suxvstvxy{~~~{|snpttojijmuzxy~~~}yvz}zyyz{|~}~~~yvx{wnlqpopsvy}}zuty{xrpprx~|y~~||||{zyuoqxxtrsuwy}||ytwxuuvxy{}{{yrnottokjlpx{x{~~~xu|{zz{|~|~|~~zvx{wporqpqtwy}}|wux|zuqqsy~|y}~~}||||{{{wopxzvtsuwy|~||vwxutvwxz|~|{{tonqtqmjkow{xzzsx}zzz{}|~||{vtwxrorqpqtvy||xy}}zwx|z{~~~~~~~}|||||~}ws|{~vtx|~zyxwvwyz{|~~{|y}|vsqty{xvux~~z{|yxyz{{{|}~xz}yzyuw~~~~~|{}~}~}{~}|{{|}zwz|ywwz~~{yy|{{~~}{z{}}}|||}}~yv}vt{~{{|xuxz{{}~~{|~~~{yyxxwwwvutwz|~}~{z{~zy}zwvuuuvwx{}ysw~}}}}}|yuvzwssy|xuvy|~|xvz}{ywuux{{ywx|~{xz|{zxxx{~{{|{}ytrw}|}}||}~}{zz||}}{xxxxwwwvutuxy|~~|{}}{xxy~{zyxxxz|zut{}||}}}zwwzyusu{~}{xvvwx|{x{}{ywvuvy{zxx{}{yz|{{zyxy}|{}|~{vsuz~}}}}}{yyz{}}zxxxxxxxxwvwxwy~~}||}{yx{}{zzy{|yuuz~~}||}}}zxz|zvtux|~~}zyxwux}{}}{yxwvvy{zyz|}{z|||{{zyyz}~~||~~|wttv{~~}}}|zyy{~~{yxyyyyyxxwxwuw|~}}}{ywx{~}|{|}{wwy}}|||}|zyz|zwutvy{|~~~|{yxwtuz~}{zywwxz{zz|}{z{||{{zyxxz||~~}}}~|xustwz~~~}|zyy|~|yyyyyyyyxxxxvux}~}}~}{ywwx{}}}~}zxxz}~~|||||{yy{{yvuuwy{}~~~~~}{zyxusv{}{{zxwxz{{{||zz{||{{zyxyz|}~}}}}{wtstw{~}|zyy|~|zyyyyyyyyxxxvuy}~}}~~|zxwvwz}~~~}{yy{|~~}||||{zy{{yvuuwy|~~~}|{yxwusu{~||{zyyz|||}}{z{|{{zyxxxy{}~~|{{zvtstv{~}|zz{~~|zzzzyyyxxwwvux|~~~~~}|zxwuuvy~}{{{|}~~}|||{zyzzyvuuvy|~~}|{zywvutsuz~}|{zzz{|}}}{z{{{zyxxwwy{~}{zzxusstv{~}{z{}~|{zzzzyyxwwwvvx|~~}}}|zywvuuvz~}|{||}~~}|||{zyzzxvuvwy}~~~}||{yxvuutsu{~}}|{zz{|}}|{{{{{zyyxwxy{~~|zzywusstw|~}{{|~~|{{zzzzyxxxwvvy}~~~}}|{zxwuuuvy~}|}|}}~~}}||{zzzyxvvvwz}~~}}|{zywvutssv{~~}|{{|}~}|{{{{zzyxwwxy{~~|zyyxutsstw|}||}~}|{{{zzyyxxwvuwz}~~~}||{zxwutttvy~~~}}}~~~}||{zz{zxwvvvxz}~~}}|{zyxvvtsrtx|~}||}~~}|{{|{{zyyxwwxy|~}|zzyxvtsssux|~}}~~}||{{{zzzyyxwvwy{~~~~}}|{zyxvuttuwz~~~~~}}|{{{{zyxwwvwy{}~~}}|{{zyxwusssvy|~}}}~~~|||||{{zyyxwwxz|~}|{zzywutsstvy}~~~~~}|||{{{zzzywvwxy{}~~}}||{yxwvttttw{~}}|{{{{zyxwvwwy{}~}||{zyxwvtsstvy|~~~~|||||{{zyxxwwxz}~~}|{zzywvtssstvy}~~}}||{{{zzyxwvwxy{}~~~}||{zyxwvutssuw{~}}|{{{{yxxwwwxy{~~~}||{zyxwvusrstvy}~~}||||{zzyxxwwx{}~~}{zyyxwutssstvz}~~~}|||{{zzyxwwwxz|~~~~~~~}|{zzywvuttstux|~}||{{{zyxwwwxy|~~~~}|{{zyxwvussstvy}~}}}||{zzyxxwwxz}~~~~}|{zyxxvutssstvz~~~~}||{{zzyxwwwxz|~~~~~~}{{zyxwvutsstux{~}||{{zyxxwwwxz|~~~}}|{zyxwvutsrstvy}~}}|{{zyyxxwxy{}~~}}}|{yxxwutssrstwz~~}}|{{zzyxwwwxz|~~}}}}|{zyxwvutssstux|~}||{{zyxxwwxxz|~~}}||{zyxwvuttsrsuwz}~}}|{{zyyxxxxy{}~~}}}|{zyxwvutssssuwz~~}}||{zzyxxwxyz|~~~}}}|{zzyxwvutssstvy|~}}||{zyxxxwxyz|~~}||{{zyxwvutsrssuwz}~}}|{{zyyxxxxz{}~~}}|{zyxxwutssrstux{~~~}||{zzyxxxxyz|~~~}}||{zyxwvutsssstvy|~}}||{zyxxxxxy{}~}}||{zyxwvutssrstuwz}~}}|{{zyxxxxyz|}~}}|{zyyxwvutssrstvx{~~}||{zyyxxxxy{|~~~}||{zyxwwuutsrsstvx~~~xy{{z{zz{wxwwz}~vz}vstt}wqxwy~}}}~}luyv}~ywuxwx|suy}x{|~}~ztytwzoqyrrwqnvty{~z}}}}y}y|u~xp}{sq{{z}zxy~~|xssz{krxutoqtv|}|ztwy|vzvsy{u}y}zz{vxz{svz~uwzrmx{|wp}yzzw}rwry|vuzuw{~t}tvvsvqryupuwpu~~}~|}{{rxyu|z}}q{~~y}yz}smxuzx{}y{}|}z{zv||~~pv{~x|{~y{|ww|}}{gy|||wwxy{}~}ytq~v~tv{~~q~~u}}yy~wszw~{{yl{uz|~|~}~~||}~xuzst|zkhu|zxv{uyty|xvurp~|}|}vz~o}|y{}{}kury}~tkrrxxrtwwwx}~y|zqupvzw}zxxwxyz|{~rjrpzzmnuwyzxsw|rx{y}{suzj{y|~x|zyv~~wzyxeoru~pxw|zwvtxyt~~|x|xm{wqz}}{zywv{}|}xz}{~|ofurysqyxzzwtt{rz}yy||qw{q}{{}}zxw~~}z{q}}|~wfrwyovxzywvpxxv||wxxt~wv|{|yyzuu|z{x}~}}km{v|qtvx{zwstwu{~yy}~vzxs}yzzxywzxz}~yu~|qiyyz~zrvzy{yxu{wyzt{wzyq{zy||yyysy{}u|~weuyw}tx{}}{{vwvw|}vw~xvzqv}{z~vzxy~x}u||~}{dq{yz~vz}|y|zyxvuz|uu{xw|qv}{{|y|wuy{xzz}~}fnxwz~yx{{yxvvsw{xt{xu|tv}|}z|}xu~vzz{|}{jm{xz|{w{zzy{wtuzyu|{w{xvz{|~{}xt|}y}~{}{{~}pjwvxz~w~~z|x{ywvz{w{~vx{u|yzy}{tz|{z}|y|{viuwvxyz|{|{zwxyxzyuys|~xx}|}~{uzx}|z|z|ipwtu|~z~y|zxxywv|}twuvyvz~||wvw~}~|}~}~y{nlxvux{~{|zywyxuyytws}~wy~}}xt~|z||z}x|xwgtxvw~}~{{xxwzuv{uxuwyx|~~~{uww~~~}{z~xznlyywz}}}|yywzyvx|uxt|wz~~}}~xr|y|z~x~|x~{hrzxy~}}}yyw|wwzxvwuzv|}~|}ttz~|{|w|wxthvyy|}~~~|zxx{vy|uwuxww~~}{{sy{~|}~~{zwzvwnkxzz|~~~{{wzywy~}txv{vx~}}|yr||}~z}|vxwsy|knz{{z~}z|wzwx{ztvu~u|}~}}}us~|}z~z~sxtt{uju}{}|}|{|wzvy|wuvyyw||~|zqy|~}~}}yvuvtv{nnz}{{}~z}yzyw{~tvu}v{||}|}ur}z}z~~rwutzvku~||~|{}x{vx|xuuxyw}y~|yqy|~|}}~|ztsutwzno{}{{~y~zzyxz|tuu}~v}z{}}}tr~~y~|z{qusuzslv~{~~||}y{wy}vuuzw{}y~{wp|~~z~|{}qtstyvmr|z}~~zy{xy{xtuxzyx~|zpx}}||}}{{tsttxxnpz|z}~{{zyyz|tvw~}wy|}~|rt}~z}~{{wruswypnwzw~~{}zyzy}tuw}~wy{~}}rs}~z~{{yrttvyqnuzu}~zyyyuuw}wzz}~sq~|z~{|~zsttvxqntxs~zzzyvtw{x~{x|tp}}|{{|}|ssuuwsnrxsz}|zzy|xtvzz|u}wny~{|~~{|}|vstuwunpvsu|~zzzz{tvy}yu}~zou~{~|}}{}|xssuvvpotvr~{z{yvux{xyx}rp}}|z~~~y}}zuruuvrorwqz}~z{z{ytwz|{t}xnw}|~|}}~~z{}zwrruvupputs~{z|yuuy|xxy~}qq}~}}z~~}x}|xuqtuurprvq{}z{{{ztwz|{t}}xov}}~}|}}}{y}ywrqtutqquus|z|ywuy{yzv}~so{~}z}|}x{|wvprtusqrwsx~{{|y~uvz}yuy~~|pr~~z|}{}~v|yvtosttrrtxt~}z|{{{uwz~}zs{|you~{{}{}~|v|wurosttrsvxu~~z|y}yux{{}|s}|uoy{}|{{}wy{tuoptttsuywz~|{|yvvz|yvw}}|qr~z~{|y|}}u{wstnrttstvyv}y}z|{uxz~}{~r|{wpw~|{~{zy|~xv{supnttttuyxz|z}ywvz|ywv}||rr}z~{|x{}|t{vstmrttsuwyw~z}z}zvy{|||s|{vpx{}~~{yx|uwzquootsttvyw}{||y~vwz|~ztx|~zqt~z}{{w{~yu|rtrmssstvxwy~{~yxwz|{~xu|||rr|~z~{{wz}|t|urumrsssuwww~z~y~zwz{|}{t{|~tpy}{~||xx|~tzxpwnpsrstvwuzz}|wy{~~||tz{upw}{~~~||xx|uyypwnpsrrtvvt~{{|}xz|~||uy{vpu}|~}~}|xw}uy{oxposqrsuus|||{~yz|}|}vx{wpt}|~~}}}~|yv|xv}ovrnsqqsuury~~~zzy||}}xw{~zqr{}|}~|}~|{vz~{t~rsvmrqpruurv|y|y|}}zvy}|spx}z}{~||vx}}t|wpxnqrpqtuss~}|yw}wuw||tnt||}vw~uuxqqnlswyrt{nwsnspqtvtps{utqnqupijrvv~y|ux||vz~~xzxw|||~uos~}}~|y|xvuw~{}vrw~}urxzzypqmlrvwqrxkuqmqoorsoks~~vrpy~xxwqhgoss{u}~wrx~~x|{~yy||yxwsiit|z}y}wzztsuy{ux{op{xuqmrvwrpxkqskpnmoqokt|vpps|ypkput{x{|~|zrw~zy|~uzuwrppojiv~}}|}|{v|vsvy}~wv}}zroyxwpqvxxpwtkxooqoorrnq~|x~wpot{woovvy~x{~}tu~}x|{xytvsonomju~z|z}u{}xtvz}zv{||yuoxyxrpuxyrtxkvsoroorspr~|u|wqpu|~yqouwyx}{|}}vu~~~x{zzvttrnnoolu~z}~z}uy}xuv{}{w{{~{yvpyyysqvyysuxlwtoroprtqszu{vrqw~yrqvw{x~{}|}uv~~x|{xx~tuqnnpnly~z|~{|t{}xux|}zw|}z~yyspzzzssxzysxumyrqropstpvvx{ursyxrsww~|z}{~}{sx}~{y~}~t}zttonoplo}~~{}{}~xw~{xvz~~~xy~{}|yxos}}}xsvz{wtzotxqspprurp{{u|xtruz}vsww|x}~{~}~st{~y||xy~vuqnoqmkv~~}{|~{v~}zwx{~zx|}}}~zzoow}uw{|yuzrryssqprurox}~v}{wssv~yuwx{y~}|}}vrw~z{|{w~xvsopqoiqwz|~v||yxy}}xz|}{|tlryzw{}}w{~zxz~~x~{|~~~{y{ywor~|tmmrqtsims|{~|y}~~zytrssmluy{owvttnnvvustw~|}{}}}w~z{zxzont}|qo|xnjhfimlhps|~}{{w}~{yxzywnvwonpnsnglryw||z|}{y{ytsuvpsz~usr}{oousrqru}w~zxxv{{y|}{{|rsy}ywsrvztnjknlipr{~yz|}y~|{~|wrpurzzvvrvqimryv|y}yuwz||{vuwxsu}}}vquw}yv{ywtuw}x|yuutz}{}}{{~tsyuqrtvy}wqqsqmsu|{~xy}}y|~}|~xronvwzzyvxwmnszzy{|{uuy|~{wtuxvt}{xvquz}v{{yxwy}~{~~zutsv~}~{|y~{sxwpptx|~ysprtpqxyy|zx}}x~~xqnlt||{zxv{snsy}xz}|}vtw||xttwxv|}uwrt|wx|zzyz}y{{usrt}{|y|~vx~~~xpotz{ursusryzyz{w}w}xqmls}~}zxv{vosx~y|y}z}vsv|}yutwyw|}twsu}yx}|{z{~yz{urqs}||y}wy|}wootz|vssvtt{zxzzw|~x~~wpmmu~~~|yw}vquy~y|z{z|usw|}yuuyzxztvrv}y{~}|{}xy~ytrqu||ywz{}uopv||wtvwtw{{w{xw}{z|tomnx|~}y{}tsv{|x~||y}zttx}|xuwzyzvwtrx}{~}}~{|{{~{wsqqy~|~{|zw|}}{spsx||wvxxv{zyyzuy|}zyromtzz|{ysux}xz{z{}wtvz}{wwzzyzwvqty|~~~~xy~|ytrpv|y~}{|vz}~wrrvy|{yzzw{z|{zzuvyz~|~{uqnrxv|}}tuw|yw~|~{z~yuvy{||~}ywz{y}~yyrrux|{}{wsolovst}|wtq|~~~}yoszoxzx{~{wzvtwxx}}zzv|z}{yzvsvrsnsxxyu||wvy}}~|zsosz|||ux}qn{uquusqoovyv{|ts~~}}}}vnoy}wv}wvyzttvpqtuv|vvwy{~|voz{ruwwuszwttwz{{x~{topv|~|tuv~ywvrutronnvvtz|xrw~womt|{y{zttuoprsu}}ysux{~~|xsq~z|~|xu{yutwz{{w{~zsoqx||~~wpuy||uwvtqoovxuz|xsw~}}uonu~z||vtvppstu|xtty|~}{}xtr}~{~zuz{vuvz||x|{{ztpqx~|}~~~z{otz{vvvtrpnsyvx~~}yvuzzvpou~|z|ytwtpsuvxzxxrx}~{z~yvuzz}|uv{xuvy|}{x~wzvrrx~}{~~}xz~qr{~~zuvusqoqyxw|}~~zxvyvwqpu~z|ztvvqsuvw}{u{rx~~{z{}zwvz{|}vu{yvvy|~|wvxvrsx~{}~|}xw}sr|}|uvutqpqyyw|}|~zxw~~~}xuvrqvz{ztwvqsuvv|{u~zsy}{z~z|zxw|{}~vv|ywwz}~{w~uxvsty|z||{|vx|rt}|vvutrpszxw|||~zxw~{~uuvrrx~{|yuxuqtuuv}ywwt{~|z{}~y|ywv{{ty|ywx{}}wxyuyusu{z{{{z~|u|xqxzvvusqqwyvy}z~}zvz~~~y}swtqsz}||vxxrrtuuy|v|}tx}~}{z~|~z{{xux~wx}{xxz|}yuuyxssw}{z{{y{wz|ruz~xwvusquyuw{{}{wv~~xuwvrqu}}}xx|y{~~|}~}~}w~~}}|{|qw}zysqz|rr{{ywy{|ypu|z{}vwyplmtrszuw}}{ywvusu|z|vt}wvroxwu~~tu|yy}xmow~|tmjoqpyww}yw~}|}rr}{sv~wouzxwwyzxon}~|~|su|{uuwrwwty|~z}zxvv{}ztp{wu~tw}}~}voz}{|{vmmux~}xuyww|xz~{wz~||smv{xry{ywwxxtlq~~|ytry~|}}x|yw|~||~|yww|{y}yps{{uz}|}}{so}}}}wrlpxz{y}zz~y|zw|}|~~|}zrny|ysz|ywwxwtlr~{wrq{||~y|zx|~|}~|zxw||y}~|~xqs}yvy}|||zto|~}}uqlq{}|y}|{~{|{x}~}{}~}zzysoy}~}sx|zxwxwuop~{urq{|z~{z|x|~~}{zxwz}z{~y{yssy{v}~{zzyvpw~}vqoq}}yz~{}}{}z~~}}{{|~{vxurxzwt{|ywwwvsp}~~}trrz}y|}z|z{~{zywy}{y}~xwyvuz{vz~{zyywru~wqpr~}zy~}}~{~|}}|{z~{|~{svvuy{ytz|zwwwvtr|~}}trs{}y{~{|{{~{zyxy}|y|}wuxww|zvy~~{yyxwsv~}wqqt}{z~~}}|~|}||{z~z{}~zruwv|{yu{|zxwwvts~{~{srt}}z|||{}~{zyxz}zx|}vuxwx{|vz~}{yxxvsx~~~}~vqqw~{{~}}~}|||{z{~}y{|~wrvvv}ww||zxwwvru~{~xrqu}{}{{}{zyy||xz|~{twxv{{zw}~|zyxwsr~|~{spq{~|}|}{}~||{z}~yy||}stwtx}v|}|yxxwsp{{}|tppy}~|{~~|{{y|}yy||vuxuv}~w|~}{zyxtox~~~vpnu~~|~zz}||{}~zy||vtvss~x|~}{zyxtov||~wpns~~{~}|~}|{|~zx{|yuxvs{x{~|{zyvpr~}{rnp{|{x}~}|{|~{xz|}{tuurw|z~}{zyvqp}z}|snnw~~||{~~}|{~|xy|}vvwsu~x}}|{zxsnw}~~xpnt}~|~yy~}||}~yx{{~xsvtrz{||{zyups~y}zqmpz~~{~~{~}||~{wz{}}uuwsw}y~}{{ywqoz}}}~wpnv~~}~yz}||{}}xxz{}wruts|{||{zxupt}x{zrmp{{}~{}|||~{wy{{~utvtxy}~}{zywrpy~|{~xpnv|~zz}{{{|}yvyz{~yqsut}}{~~}{zxvqs~~wyztnpz|{|}|{{}|wwyz|~wruvxz{~}{zywuqv~~x{ztpt|}|z~{zzz|zuvxx{{squw|z|~}{yxwus{}yuywqpyz~~}{zz{}xuwxy}zrtxy|y|}{yxwvsu~xx{vru}{~|zyyy{zutwwy{upux}{{}}{yxxvt{}}ytxxsry~z}~zzz{|xtvwx{zssx{~y||{yxwvtu~~~ww{xsv}{~|zyyy{zttvvx{upuy{|}|zyxxvu}~|}~xsxxsr{y~}zzz{|wtvwx{yrty|}z||zyxwvtw~~}vw{wsw||}|~yyyz{xstvvyzsqvz{}~|zyxwuv{~|vtywrs~}z~|zz{|zuuwwy|vquy~{{}|zyxwts|}}zuyzts|z~{~|yyz{zusvvx{wqrv{~}~}|zyxusz~|}~ytwxrpwy}{{{}|wuwxy|zrrvz~|}}{zywtqw~~|vxztpv{~{{~zzz||vtvwx{yrptw~}|zxurw}}{uuysns}{}||}||}}xuwxy{|tqsv~}~~}{zxuqs}~~xvzwpr|~}|y|z{|}ytuwwy{vpqt{~}|zwss|~}~xtwwpnw~z~{||}~|vvxxz|yrqty~~~|{ywsqw}~|vwztou~|zz~{{{}|wtvwxz{toqu~~}{yvru~~}}wtxvnpy~{}{~||}~zuvxxz|xqqt{~}|{yvrqy}}{vwysow{~z{~{z{}|vtvwwzzsoqv~}{yvru~}|}vswvoqz{}{||}~zuuwxy{yqqt{~}|{ywsry}~}~|uvzupw|~{|{zz|{vsuvvyzuoqw~|{yvsu~~~{}}wrvxrqzz|{{{}{utvwwzztptz~}{zywurw}}|}wtxysvz}{}yyz{xrruuvyxrqv~~}|{zxuu{}{{}}ztsxwsx|{~|zz{{vrtuvxzwrsz}|{zyxvtv|}~{}~}xtv{wvy~|~yxyzxsqstuxytqv~~}{{zywu{|zz||ztrwyuy}y|yy{{wrstuwzyssz}|{zyxwuv}|}z|~}ytv{ywy}}~ywxzxrqstuxyurv~|{zzywv||~zz||ztrxyvz|y|yy{{uqstuwzxst{}|zzyxwux|||z|}|xsv{xyy~~}}xxyzvqqstuxytrx~|{{zxvw~|}yz||xssyxv}~|zx{xywyz}~}{{{}{yuy|vxvqokkotxz~|w}~wu~zvv~xzy||y|ungrummnpux}uwvv|xuwwzzsq|~}|~rtslqonsuvw}y{zzzxxuijvrupou{yu|vz}{{{~}w}y{tq{~z}|~{wturqjgnyzv{}uqry}~~w~}}znqu|}voqsqopvsmruz|}}z}xslppq}~z}x~|vwuxyyxqsvyy~y|{z{xry|~ussrqo|{||~}stpsw{wmttwvyz{tuw{{x|uzxzy|{y{z~y{|uwpw~~{z||{}|x|z|u{}quuy{z}~z|zxry|zxwwyqxtuyx|}wxt{}{}~tsmioq}}|wzzpunw|uwy|~|t~w{z~~~poio|{xv|||y|tvtsuy{{}|~~~|~rtyzz{|w{{w}|}z|v{ttv|y~~{xwnqrx~{yw}vv{yxq|y{z{x~~|yqustr~|wxxxz~}}xzxvszuu{~~}~~xuuqszy|yqup|yy}}|s|}}{y~~srkmvpt|}uxyyywztxz|}|~~|}|xz{wqtquw{|}|x~~vys{}sprzu{{~{wwusxzw}zu~x|}~}|xyqtx||{x|twwupo{xxxwy{yz~y{~~z~~uwtzxxtrqsuz}|xzyuu~}yuw~z{v|}}|yxs}unnz{w}z{|{|}~}~~{z{tw}}~|}tvropwz~z}|wy{twsyxvw{x~~~xzxqourw|~zz{{~yxyzstuy{}}zyxzvp{{|{|z|}}|~{|uupy{uw~{zyutrqsz{z~vsu~xxv~x{|y~~yztlktss}|~~yz{}{}{{|zsx{x{~}~~}~|xuvvqtz~|zyyw}ut|y~{vv~y|~wuqruv}}}|v|~vtttty~v}xv~z}x~xqzrw{~~z}yxsxxsyrvus{|{wvspsmnxyw}zz~y~xustyt||~w}ywv}sp~||}}w~yz}~}x{zvyxzy~}{vzxsru{sy|yx~qw|u}yqy{v{{}xtxrkusnq~zyz{x~zyx}wu|y}~|{vyuqstx~y{uy}}~{~|t{|su~}~vy{|z|zqt}|}{v~zz|yuzsqrv~||~~~zy~yuwror|z}|z|z{|x|zrry~{z}~~zu{xsutrv}}|}xv|{{|xrx~xyy{y{{}}sqtuy|{{zy|~z{ytvvw}~~|}~}txuspsmpx{{|wy~xz}wvxvz~}|zzwwursspr|z||z{trxzuy{w{~xtvuvxsw|}}zzuutv|}wx~{yzyystnnpw|y~{zz}{yyux{|~~~zw}|w{yqtxrm{}}{xtv~ur||zx|}vxtsunoq{|~zvyyx}~zu{}}~}z~zvwqklox~~z}|~~w~~vyzx{}z}{z|zy~xu|zpp{~{~}{|ytr|}rt{x|}|~yzytxumlr|y}~zy|||}|~yuzyy|{|zwwrooqwyzx{}z}xyywywu|~|{vxxw}xmr{yx{{}zvuzqw{w~}}}yzzuxpkkuyqy{z}{|~|~yv{wz}{|~||~yzxtsrquuy~}~}~y}|{{vyzuv}}{||v}x}vpyyur{}|~zv}~tu}wx}w~~~{}wzxpmpxpm{|~~~||v{xtyvyz|~{|{vuuststxv|}~}{}wx|wux{~}|v|y~zpxzwqswv~~{w|vu~yy~~ty~{zzzsspyqjrzx~~|{x}y}zrt}{vwzrvtpsqsu|~{|vyqy|}zy~u}q}}vrpxtsw~}{{}}v}|}{vx|vy|~~z~{qpv|mktsrz~{{|}zxw{|xy{x}}~wyzrroqxomx~{|yz|yy|{|v|||~|x{surupsqs|z{uz{x|xq}|y}~w|{}|ywypsyvpu{p{~zz~{uwr|ur||}yyyw{yx|uylu{wyv}zxuyuttyz|{~~uwxptusoxv{||vx{x{{|~}u}{ytp}qorpsu~||~vy}zwx|t~}}zzvyusqtnmvz{wvxxx~~vu~~~~}y{xzqssmtmxz}~z~z{xxx{zy~zwvvzy~xp|wuyryx~~{~uwsuqr~|{~~z}ywxwwssoqyyx}zz}|wyu{xy|~~}y|s}ypmqvoxz}wwy{~{t{xt|t~w}zwxwuqsusqw{||yv~q|}|z~y~~uvznpmpxnqs~{}~{y|xx{zu|~{{{xs}xxtqxssw~|}}t~rst{v|~~}z}~yz|xtyuquvru}z}z~ywzztx}}~}zvwqqvuo{zwpt~}}}w{yuvowz{{xvzssststtvx~z}~u}{}v|~}|~zy{ruqmxpklx|z}}}{{{{v{}{}|{~{u|xwsvrqrvwz}}z}xst~xu~w|~}}||yyyxqvwqpwrvyz~|z{{vvwzy{}xvst|uw{yulx|{}|}y|~tusxvuy}yz{xrtusrustz{w}zx}ww~yy~}~}x}xssnyvkjqxt{~||~ww|{x}|~|w}~|tvvqqusqu{{~|uw{w~u{zx|utsptxsxww}}}uz{vt|zz~xx~|xtwx}vq}wuvyv~wuwq||y~|uxyvy}}}||{|zfsrsqr~y|~~zoxwrnggo{vrswvu|z~|v|~}~tu~txs}{}|||{zyx~vqwxuw~rruxy|tzu|uwzvur~|pvsmx|z|{zmiov~~wzqlpvjkitsqvutz~z~~u~qszw||{|}~x{{~}{unswx~sspwusxqt~vu}qy}wxz{qx|lpqmv}{{rnllux}wtuoohrsmuqu|yx{z~yzuvwt|zxz~y{||tvuq{~|utpzxowqt|wtv|ty~}yz}stqrslsz~}sopjrw~|{zzpojoxjrppz|yy|xv{v{uu}~|y{x~|}~ysunvzzsqp~rttsyzwx|yozz|q{vospow|~~|xmslmrz~{|yqojyqjqpuyz|~tx|vxzvy~{w{{~z|{||qusovzxuqx|ntsvyyyz~vs}{|}ys}spsqqx|~}~rlrknrz}vrpn|nlqrxz{{sw~vwzu|yxy~}{zz{znsrrx}yvszopqwyy|{}tt||}}~xrzusttqz|~~~|{slrkpt|yrom{rjoqwy}~~sy}~xx|wz{vz|}xz}~y{~|nrqqz~{wtwqpquxy}|}xo~|}}{zrxustyrz|~||~|wy}wjplpv|{vrnvzknpvx{yx}y~yx|~y|{zxy~|x}uy}tmtqy}zws~zmqtyy}~{~qx~|}}zzttxtrxwv~}~zxtyzkkonx||xsqs~rmpuy{t}v{zyzzzyy{{zy{}qt{xlrqy|wt{oorxz|{~tu~~z|}zyurvvuyzv~xwpv{njnoy~}{tqs~vlpuz{s~}vyz{zz{~xz~}{zyz{nr{wlqrz~wv}pptxx||sv~z{}yxtruvt{|x}wsovymlor{~~}usvtlqwz|}t|{wz{|{z|}zy~}{v{xnt|tmqs||vx}nqwzy~}|p}}}z}~ywssuwt~w|~|{y|oqyujnou{{su|}nnuyy~~u|~~|yxz|y}|~z~}}}||~|t{qpyznnrw~xx~unvyy|}}tt~~yz{xvstwv{{u}}w}spxznknqw}uu{umuyx{wv~}}|xy{z}x{{|~~{~u{tow{pmptx{yzov{x{}ur{x}yxttwwy|s~{~x{wpv{rkmps{|yt|{oszxy~zs{~~zxz|{w~y{|zxwyos{vmnrt|yts|zy~|yp{~~x|zzwtuxv}ux}}|~w|rryxmkopv|}vzvoyzx||uy}|~}yx{{}x}|{}}{~|~uytov{rmort~~zsw}yz{vq}}v~zzvsuww}s{||~|xzqsyvlkopx|~v}wr{yx~{t{|}~|xw{{{y|{~~~{~{}txsovzrlosu~{ty}yz{vr}}u}zzvstwx}t|}|~~|vyprxwmkppx}~w|zr|zy}zuy}z~}xwz{}y}y~|y}z|uvtntysmntuzxx~yz|wr|}}vzzzwsswx~wz~}~~{}txqouxpkorv~zyty|y{|wux}~zwx{}yy}~{z|}~}xysvnpwxpmtu~{v}|z|ysu|{t~yyurvx|}w{~|~|{wttmqwummtt~wzv~zz~~yt|{z~}{wv{|}~|z~}}y~|~}~vyrupntxsnsw{}|x|}z{ztsz|t{yzvsux|x{~|~|xxstmpvvomuuw}w~{z}yuz~|w}{xvz|~}y|}x}{~|~uxrtpmtxtotx{~}z}}z{ztsz|s{yzvsuy}y{~{}~zxwssmpwvonvux}zz{~xu|~~zx~}{wvz}}|{}}{x{{||{twrtnnuxsovw~|{|{|wsvy{r}yyusvy|{{~z}}wxutpmswtnrux}|{~}z{{vv{v~|}zvw{|yzw{~y}z|vuusqmqwvqqww}{{|ut}y|uwzzxtsw|~y|~{}|}~zwwusnpvvpous|~{{~wv~|~zy}~{wvy~|{}|yyz}|{xuvtrnpvwqovs||~~vu{z{yu{{yusv{}y|~~|||~}{vwwtopuwrntq|~~||}xvz{~u}}yuw}|}~{||w}~{{}yzuuvtonswtossx~}}|yuww~s}z{wsty}zz~|}z~~|||xvwvqorwuopsr|~|{|vwzxz}~{wuy}|~|}xy}}y|{yxtuvrnpuwrour}}}wuy|y|s}{{vst{}y{~}y~}|{zwuxuposwtorrv{}}{{vxyw|}~{vvz}}~{|wz|{y|zwwsuvqnpvvrqur}}}wvz{y|s}{{vsu{}z|~}~y}~|{~yywtxvposwupssv|}}{|vxxxy}~|wuz}}~|~xx{zx{zuwsswrnpuwtqvs~}|xux~w}s}~{|xtt{~z}~|~x{||z}zwxsuwpoqvwrsut}|~xw|{{}u~}}yvx}|||uyyxxzuuuquuoorwwsvvy{~xqqw}rwsp{uusnnvsmryjiigolgiottrxv|}z|z}~~piy{twx~|vyzz~{}rqs|ztwrtxqpsy{z{~~v{{ttsu}yssxyy|s}{yqnuwjlutnlekjefkqrpttz~}~~{rjm}|}|tuvw{}x}||znpu|x{uu{usv|~w~}~utst|xqqv~wwyq|ywvqpx}ynipx}zvmroijnrsqur{|}}~y{}vsolrxxxy||w~z~}zwmqv{~y}ux}uux}~{xxxux}wrsy~uzwt|xxuqr{xvjkqx}wryplnruttus}{}}~v}yurmmu~yzw~}}zy{{{qorv{{}|t~zuw{|w{}yxv}|trv}zv{sy{xxsptzyoimqyt{xooquutvrz{~|~}}y||xvpln|{{x|}{x|~||unprw}~}u}~vwz~~{yz{{{v}}vsv{|v{vx|zzuqq{|{sjkls{y}srruvuvqw{}~}~~}~}{~zxsmkt~|zz}}y}}||ypporx{{zyxy}{||x{y~~xzztuy}xyyw|{{yspt~}~{ynkkmt~z{utuxwwtq~y|||~}{~}zwqlmy{~}y{z~}|{}upposz}wyy{~yyzyzy~yuw{}xzxy|{{xrpw}}|{vlkjnv}{wvxyyyst}z{~}{}}~{}{zwploz{}y{{~}{{|spoot{{wzz|~y~~}xzw{yyux{}yzxy|{{xrqy|~~}yzulkjnv}}xwyzzztu~zz~}|z~}~}{{zyvqmpz|}z|{~~zy|tppot{}x{{|z~{}{wyxyy~{vx{}zyyy{{{xtrx~~~}xxvlkknv{zyy|||xtzz|}{{~|}|zyyxvrop~yurv}}|z|{qpqzzr}wt|yz}}vxx|}y{wx{{~}}}zsyz}|zrjqylhshnyrrsx{}}w}~~}}~|}qpvqwqvvsrs~|~{||rosz~u{~s{|wz}}}wwuurz{xvy|~|{||}}{wu{~~}}sjnvzpxmkxrpruy{|vy{z|}y||yurvzwwxsrr||~{{}~vwxqquyz}u~zzvu}}{xwsy{wuxz|{xzzyzzxx|~ztvplqwytr~vttwz||u{{xz{y~}}xwvuty{||wtu}{}}}uwwqruyyu~zvwtw~|{wu{{xvy{}{x{zyzzxx~~}usummrw~szwvvy|~zu}{|wzyy~~}~|xwvttz{~zvtz}}{~~xuztprtz~{z{vwutz|||uy|zwxz|~xy}yzzywy}|rvqknqy|v|xwx{}}u{~z~wxzx}}}~~|yxwurt}~yux~~~{~||tyvpqqu}|yywwtxyx~wx|{xxz{yx~z{{zwv{}twtlllr|y{yy{~~vy~z{ywzx||x{|yyvsqv~|xw~|}|uxyrqoqx~y}vwuvyt|{w||zxyz~}v}}z|{xuw|}~}xuxolklt~~{z{}zu}y~~vxyz~xz~{zzxuqrx{{w|~~||{tzuqporz|}yuvtxvtzz}|zyz{zw{z|zxux{}}tvumlkmv~|{|xx~|x|uxw{}v|~yxywtqsy{|y~~}}xtztqpos{~}~wuutytuz{}|zyz|yw{z|zxvy{~}sutmlknv~|~yy|x~|uww{}v|~x~wxvtrty|}y~}|zsytqposz}}~|wrssxvu{{}|{}zy~~|}nnpjjlpx|xvy~yqqqgghlrns~}|zyywwvuv}ut~xvouvjoplmnqy~~y}zpppuzw}vqx}wujpvprtttvzz~}y|~{|zotrpru{ytxxqkqmgjltur|{vz~v~wyz{||tutyvqvkhnjjlnsz}|~}}~{z|{~trtx~}pot}soxrprsstxx||~y}zuw~~rvvsvy}wru}}tvsjkmtwryyuzx~y{|}~|tnsx|y|rkpmlmnsy|{}}{}z{}z~vrtx~xoqx|u~wtvvvwyx|{~x|ytx~~~~qvvtvx||uswx{wnnpwxt{~xv{wyz|}~{yrovy~|sosnnopty|{~||}z||z}urtz}~~voryzyxvwwwxyw~|||x{xuz~~~{rxuuwx}{utyy|unpszvu}}~vw|x~y|}~||wprxx}qsrnoopuz{|{~{z}z}{ssw~|}sptzyxxxwwxwxzxy{vw}{vvwtuvx~yuv|~}}rprxxsy~|yux|y{{|}}||uswuzwtuppoorwz{}}~{{}}vsv}~ywqrv|}{zyywwuuy}zx|xw|z|zvytutuz}wvy}}xssyzrv|}{vwy||~||}|}||}~yuxuv~wxrqonpuxy}~~|{}~|{tu{y{}srtw}|{zyxvs{zzw{{x|{|{zvtsrrvz{~~|z|ywzzy~|tsz{nlpv{~v}{ty}xxtz~~{ptuoostr{{|xvy~y~wrrtx~{pqrzvlnllqnpx|{}yywrqw|z|yjl{zyynlurkmosx|||zxy~|}|}xrou}xuqqqprsxzwwx|{zzyxqt~|}t|ypnurlovrouuw{wz}|zy{~~}|wx{}|rqttvwx}wu{tsssux~}~}|vtpivvuqnomnptvy{{~}zvy~}zuy{ynj{~w}~zz{zzvu~~wx~{uz{z{sv|}yzzz}vow~{uxxqouzusy{}~~}vtopyz}wql{z{wutqqqpqtvx{z|}}}wuz|~xyzkgozs~}vvwxwupv|wty|tsvsuvx}unq}zuytnpwwqtxy{{|tt{tnr{~xtplps}}|vurqqoortuxxxzxzzvv|~|zsusjjty{xxyxvqs{xsuztnrqtz{}~~|wwspu{}zrrxystxy{{{~tqyupt{}~}~qmmlrx{yussqqruvyyxzwyzwx~~}|y~~orslmv|~zzzywrs|xsuzunqru{|}~~}~~~zuvsrw|~{stzzsuyz{{{~sqyuqu|~~|~{olmnuy|yussqqsuvyxx{wyzxy}~|yzmrrlox}~{{{zwqv|wsvzsnrsx{|~|}}}~|~xuvssy}ztw|xtwyz{{{|ptzsrv}}}|{~wmmnqvx}yutsrrtuxzvzzw{zxz}z|~spuolqz~||{yts{zttyyppsvzy~|||{|~}}}vvurt{xv||uvyz{|{}uqzwqsx~|}{~}qmopttz}wvusstuwzwy}xz|yx|~|z}ypvslms}}}}{urz{usxzrptvyvx|||{{}~~}yxxtsv|}y}~wwzz{||}yryyrqtz~z~z}voqrtqszwvtstuwzxw}zy|zxy|}~z~}rvwolnu~}ysw}xtv{wpswzxu}}{{{z{}~{~xywssw{||{vyz{|||tu{vrru{z|{z~|qprttpwyxvutuvyzv{~y{}zyz{}}||~yrxtnmpw|vt{{utw{tpuxzvv|{{zz{|~|{{xyvtux{}yx{{|||||sx{ursv|~y~~yz~yppsuspy~yxwuuuwzzu|~y||zy{{~}}{~vrxsmmqy|vu|{utx{spuyzuw{{{zzz|~{zzwxvtvy|}yx{{|}|||swzussw}~y}}xx|yonrvtqzzywvuvwzzu{~yz|zz{|~~|{~wpvsnnrz}wu||vtw{uoty{ww|zzyyz{}zx~zuwvuwz}{x{||}||~~tuzvstx}yz~xvyyplpuvsz{yxvvvwy{vx~zx{{{|~}|{|~}~zoqtppszzux}ytuyxppv{{xzzzyyyz}|vx{ttvvx|~yz||}}||~xrwyuux}{x}yuuxsjlsxwy}yywvvvx{xu||wz||}}}~}{~~{{{qntrqt{|vw}{utwyrnt{}z~zyyxxxy{{uuzuruwy~~yy|||||{}zruyvvy~{w{ytsvsjjryzzzyxvvvx{ytz}wx||~}}~}}z|~~~}zzzqmssru|}vv}{usvysnt{~z~zyyxwwxzzttzuquxzzz||}}|{}yrvywwy~zv|~wsrvrjkszy{zyxwvwx{xt{|wy|}}~~~|y|}}}{yyxpnsssw}|vx~zutwyrou}}z~yyxxwwxzyruzsrvx{~y{}}}}{{}wswxvwz}ww}{ursvpinvzx}}zyxwwx{{uw~{w{}}|~~y{}}}|{yyunqsrsw~yw}}wtuywoqz~z{~|yxxwwwy{uryxrtwy{~{{}}}}}|}{uuywvw{~yv|}wssvtlksyww|{zxxxz|xu}~xy|}}~}~zz}}}}{yyxqpsrqty{w|~yuuyyrqx~zx~~yxxwwwy{wry{tswxyz|~|~~~~~|}|wvyxvvx}~zv{~xtsvvokrxvt|~|{zyyz}zu|zx|}}|{~{y|}}}|zyytqrrqqu{~y{|wvx{vpv}|w{zyxwwwx{ysv|wsuwxxx}}~}|}{wxxwuvz~}wx~{vstwtlnvxsv~}{zzz}}wxyz}}|z{~~y{}}}}{yyxsqrqqrv||yyvvz{try~zw}zxxwwwy{vry|usvwxxx~}|}zwxxwvw{{vy}ytstwrlpwwsw~}{zz{~|vy~xz}}|z{~}y{}|||zyxwsqrqqrw}|{yvwzzsr{~yx~yxxwwwz{ury|usvxxxx~}||zwwxwvw{~zvy}ytrsvrkowwtx~}|{z{~|vxyz}}}{|~}yz|||{zxwvsqqrrsw~|zzvvy{uqy~zx~~yxwwvvxzupv{vruxyyy}}||{xwxwwx{~{vw|ytqrtslmuyux}|{zz}~wv~zx|~~||}~yx{{{{ywvvtqqrstx~~z}|wvwzwqt}}y||xwvvvvywqqyyrswz{|}~~|{{ywwxxy{~|wty{uqprtokqyyx}|{zz{~zuy}xz~~}|zwz{zzywuttqprsux~z{~xuvyysr{{|}~xvvuuuwwqovysrvz}~~}~|zzzwvxxy|~|xtxzvqopspkoyzy}|{zz{}{uw~xy}~|~zwyzzyywttsrpqtvy~{z~xuuxytrz||}~xvuuuuwwqnuytqv{}~~}~|zzzwvxyz|}{wtxzupopspkpzzy}|{zz|~zty~xy~~|~yvyzyyxvtssrpqtwzz|}xuvyyss|{}~}wvuutuwvoowysrw{}}}~}{zzzwvxyz}}zuuzxsooqsnltzyz}|{{{~}wu||x{~}|}wwzzyywusstrprtw{}{{wvwzwsw~}{}zvuuuuwxrnszwqty|||~}{z{zvwxyz~~{wtxzuqopsrmoxxw}}|||}zuzyy}}{}ywzzzyxvtsutppsuw||}xwwzztu}}z}|wvvvvwytoryyrrwzyy~{z||wvxxy||xux{wrppssnnvwuy~}}~|vy{x|~}z}~~{wz{{zywusuvqoqsux~}zxxz{wu{}z{ywvvvwywppw{uqtxywz}{|~zvwxxz}~zvv{ztqprtrnrwuv|~~~yw}yz}~{z}xx{{{zyvssvuoorsuz~}yxy{{vw}|y}z|xtwywuy~yx~|{~}{~{nnsqnmnqtrqw{z||zz|}ww}~z}~|wrqrssrprvuqsvwy|}{{xxy||y|}|wqnpqmkpvwsswzyy~|z~|{}{~|}xttplkknqposwvx}{~~~~zz~uu~yvsqpnossopsuwz~}~{z|~}~yu{}xvtplotvrqtwxw{~yx~~}}xx~yspoprrprvvw{}y|}{~xsu~zwurrturpsuwz}~|}{{}}}vt{|zwrquxvstxyyz~{x}~}~~}~yww~}xtrqstsrvxx{~z{~{}|xtu~|zwustvursvxz}~}|~~||}~|wty}{xtrtxxttwzz{~~{y}~~}|xww}~zvsstutsvxy{~~{{||~{wuv~}zxuttwvstwyz}~||~~}|}{wuz~|zvsuxxvuxzz{~}{z}~~~{xwx~zwuttvutvxy{~}{{|}|ywuw~{yvuvxvtuwy{}}||~}}~}yvu{}zvuvyxvvxzz{~~|{z~~~~}ywvx{xvuvwvuwxz|~~|z|~}~{xvux~|ywvxxvuvxz{}~|||~~~{wuv}~zwwyzxvwyzz|~}{z{~~{xvvy|yxwxxvwxyz|~}{z~~~|ywtu{|zxxyxuvxyz|}~||{|~|yutx~zyz{ywwyyz{}~~}|zy}~~|ywvv{|zyyyxwxxz{}~~}{z{}zxutw}}{z{zwvxyz{|~~}|{z}}zwtuy~{{{{ywxyyz|~~}}|{yz~~~}zxvuw|}|{{zxxxy{|}~~~}|zz|}{ywttx~}|{{ywwxyz{}~~}||z{~}{xvtvz}}}{yxyzz{|}}}}|zy{~~~|ywvuw|~||{zyxyz{|}}~}}{zz|~~|zxvtty~}}|zxxyz{|}}}||{z{~~|zwutvz~}}{yyyzz{|}}||{zy{~~~~~~|zxwuux}}}|{zyzz{|}}}}|{zz|~~~}{ywutuy}}|zyyzz{|}}}|{{z{~~~~~|{ywutv{~~}|zyzz{{|}}||{zy{~~~~~}}|ywvuux}~}}|zzz{||}}}}|{zz|~~~~~~}|zxwutuy~}}|zyz{{||}}|{zz{}~~~~~~}{zxvutv{~~}|zz{{{|}}|{{zz{~~}~~}||zywuuux}}}|{zz{{||}}||{zz|~~~}}|{yxvutuy~}}{zz{{|||||{zz{}~~}}}}|zywvuuw{~~}|{z{{{||||{zzz{~~}}}}|{yxvuuvx}~}}|{{{||||||{zzz|~}}}|{zywuuuvy~~}|zz{{||||{zzz{}~}}}||{yxvuuuw|~~}|{{|||||{{zzz{~}}||{zywutuvy~~~}{{{|||||{{zz{}~}}||{zyxvutuvz~~|{{||||||{zzz{~~}}||{zywvutux|~}|{||||||{zzzz|}|||{zyxvutuvy~~}||||||||{zzz{}~}}|{{zxwuttuw{~|{||||||{zzzz{~}}||{zyxvuttux}~|||||||{{zzzz|~}||{{zxwvttuvz~}|||||||{{zzz{}}}||{zyxvuttuw|~}||||||{{zzzz|~}}||{zywvuttvy~~}}}||||{{zzyz}}}||{zyxvuttuw{}}}}}|||{zzyz{~~}}|{{zxwvtttux}~}}}}|||{{zzzz|~}||{zyxvutttvz~~}}}}||{{zzyy{}~}}|{{zxwvtttuw{~}}}}||{{zzyz|~~}||{zyxvutttuy}~~}}}|||{zzyyz}~}}||{zxwvutttwz~~}}}||{{zzyz{~~}||{zyxvutttux|~~}}}||{{zyyz|~}}|{zyxwvutstvy~~~}}}||{zzyy{}~}||{zyxwuttttw{~~}}||{zzyyz{~}}|{zyxwvutstux|~~}}||{zzyyz|~}||{zyxvuttstvz~~~}}}|{{zzyz{}~}|{zzxwvutstuw{~~}}||{zzyyz|~}||{zyxwuttstvy}~~}}||{zzyzz|~}|{zyxwvutsstwz~~~}}|{{zzyz{}~}|{zyxwvttstux{~}}||{zzyzz|~~}|{zyxwvutsstvy}~~}}||{zzyz{|~}|{zyxwvttstuwz~~}}|{{zzzz{}~|{zzywvutsstvx|~~}||{zzyzz|~~}|{zyxwvutsstvy}~~}|{{zzyz{}~}{zyxwvutsstuw{~~}}|{zzyzz|~~}|{zyxwvutsstvy|~~}||{zzyz{|~}|zzywvutsstuwz~~~}|{{zzyz{}~|{zyxwvutsstux{~}}|{zzyzz|~~}|{zyxwuttsstvy}~~}|{{zzyz{|~}{zyxwvutsstuwz~~~}|{{zzzz{}~}|{zyxwuttsstvx|~}}|{zzyz{|~~}{zyxwvutsssuwy}~}|{{zzzz{}}|{zyxwvutsstux{~~}|{zzyzz|}~}|zyxwvutssstvy|~}}|{zzzz{|~~|{zyxwvutsssuwz~~}|{{zzzz{}~}|{zywvutssstux{~}|{zzyzz|~~zwxusy{{}~~|}|{z{xzxyzyy}}~|wqnjjmmpnjhmou}~~|||zvtrrnonmoposwx}}{upmmuy~}wqw}wq{riddghioqutnkt{~~}|{wz{z{{{~}vqw{vnllruvytmr|x{{xvvvrttyzty}~|~}}xtuvvzurvxwzy}~~qqxtmgeajphmriju|yv~x{womqrw{rpwuuvw~|{qz{rjsro~}}|~z|{wt}|{ts~~x~}xrvzzvy{}~|y~}|ssyzokq}{u~xztqwptrnjhyz~|su{}vyq|}ulltut{yktuptvxt~{wy~sw~uuxrxw|vp{z||}{zzw{xwvwz~~~|x}|v}}~{{|pmytppr~vyv{srookoxjq|}{r~~{swxrqpz}yt|rmxouwyqsw||}{w}r~y~ywt}}ouz~yuuusu}yx}|~}zz~|~|wxykp~zrys{ypvyqoqlk|ylx}t{~wszzoppv|wu~kvtqz{zo|z|~}~|~|}wst}|zuzsp~~z~tuws~~x{uz~|yy}}zv~llz|pyuz{ptyrosnk{}nu~uvysx}pnps|yumtvpz}qzyz~{}{|wvp~{{vvtmx|vuyvy{s~y~|xy}~zyu|kky{oyu}zruysqupm}{nw~tv~xsz~poot~~{xlwstz~~r}w{}{|z|w}sq{|zuvnlz~vwzy~{yty}w{~|~xzxvjm{upxvw}txysvwprtoz|qy}}vt}oroy|w}mzozzxv~u}z}y~~{yzowx|zr{wjo{~|vyz~{}tzx}~{}t~|yxx}~mkt|nvv}z{xw|wuzuq{znu}tr~{xt{rrqtz|zoyqv{~uu||||{~|w{ss~yz}ytuzkjx{|{~yz{|vvz{~{t}z{wz|~okq{nru{{x}z~{v|wtz|ps{vo{yzuzwstty~xtxss}zu}vz~}y~}yzus||y}yvtzniq}xy~}|{~zsw}yz|y|{uz{}tlmytntw~u~~w||wx}uqw~ot{yyutwtwyzs|m}y~xy}t~||}{~xysx~xz{wuwtij{wxw}|}|}}uxwz~~t}z|zt{{zokozopt|{x~x|z|}tty|nwxz|xxuwtwzyzxynx~w|zv~||~||ywt{}xzzwvwrij}vvx|~|}uz~v{|~t|~~{|yt{{wmkqyoqu~{{y}|~}vuy}nwxz}yxwyvwzz{yznx}w|{v~|~~~{}yxsz}xwzwvwrjiyvux{}~~~ww~xy~|}t{|zzry{umjnyqpu~~{z}}yvxqsyv~zwxzwvw|vn}|{yzu}}|yxxsy~{uywvwumirzvv{~|}}t{v|z}zy}~z}{ts{xpjjtwotz{{~}yy{|oxu||z|~zyvu|}w~wry|xxy~}}{}zxuu||vvyvwwpkk}wv}~~~wz~|wz~y~u|~~w||~}{upxypjiqyqty~z}{yzowvy}{zzzwuy~z||p{}y{w}|~|{{xvt{|wtyvwwqliyxv}|}~yx}}xy}y}v}}~~~u}{{|{yupxyniiqyquz~{}|{{pwvz}|{{{xuzxzq{{yzx}}~|}zxtu||wtyuywplj{xu}|wz}{wy}w~wz~|~~}v}z{zzxtqzwkiitvqv~}~}~{}|qyu~|}}zxt~wsxzz{w{|~{~yxrx}{tuyvzsojo|wxy~{~~u}z|xz{yyyz}~vyzyzyyvrt|ohhmxrtw}}~~zsvxx|||zuyy|q}{zzx|}~}~zztu||xrzvzvomjy}xuz~{}w|~~zxy~~w}|yz{}xx{y{yyvtr}sghjvrsv}~zzwvyx~}}|ww{~p{z}x{}}~{{us||{qyxyxnnht|zt|}w|yx{~~zx|w|y{{|z{~~vzx{yzvvrx|jigquqt{w|~}{tzw~~}{t}zvuz}xz|~~}zzqx{|us|v|qnjly{xvt}|~v}}{~yy|x~|}z~zy|xwxx{yztvq|vhiitrru|{x{~vzw~}yt}ty~{xz|~|{yqyz{ru|w|ominy{xys}|~w~|{}yyzyz}}z~~zx|~vwvxzxysvp}uhijursv{}w{xzx~zu~ux{xz|~|{ypxyzst|w|onjmx|zzt}}~w||~|~zy{x~{|}z|~{wz}wuuvzwyrvpzyijitstw~{x}}y{x~|t}}yu|yy{}|}z{ruyyvp|w|tmlkv{}yw~~~yy}|~}{x|~w{z}{z|}xx{|sutywxtstr~pjiovsw|y{{}zyz~~xw~uz~}yy}|}z{wpxwxow{x{nnjqy~}w~~~w|{{|yyyyy}|z}|yvz|}~strvxvvqvo|vkkmvtwz|y|{zzzuwx~yy|~}|zzypvvxqt|w}pnkox~v}x{{~|}zy~yx~y||z|}~zvy|z~struxuvpvp{xllmvux{|z||{zyvwx~yy|~||z{wpwuxov|x|omjqyw~x{z|~|yz~xyz|{y}{~~yuy||y{rsqvvvtqvp}ulkovuz~yz|}z}wy~w|{yy~~x~}y|srvuvn{y{xmlltzx~{~tttu{zxzzw}|y}|~|}{}xonssy~vutv|x{svstzkgfmmps{wvx~~{z|vx{}y{z~}uyvxpt{wznljry}tozzyuvw||~|{~}}~}|rlruourkjkjsqtoqsozpjiorux~|x~}}vrx|u}{wzx{{}~{y{~zpg}zou{}}{~{{xu~ztuuy}{}~{{zz{~||z|||~}~~yslps{{ttouwwwrxs{zompvw|z}|tnq|trz{uypnm}zwzz{srpvw{{v{|z}vswvxq{~|{pkjos{zxnv~~||wsjimv{vpkqmoiilhqjcbhjnrvspwr}y|vsy{}z}~}~vonpsyvrix~}{~}}xx{ywqw{srptw{vvyv|yy|y~v{yy{xzvunoq|{{ytsywztwyv~xqqvx~~yxqpvz|{|yqopzzz{xrqpvwx|u}zz}ttvvwr}~|yojknt||xn{~}}}xxsqjjnx}ysmrpqlkojqngdilquxtqyq|w}xuz}}}z}}wnklowxwnty}~}|uz|yss|vrqruzyyyxxy|x{y|yvzxzywwvtrnsvz~yyrvyyzu{yzxsuz}~vw{wrv~z~z~wrotz|z|vsqrvyw~|v~y{|suuwut}|xoilow~~yt~~~}|xvrropkotzxqnsqrloqmspihmovy{svwt~wv}yy}}|}{|qiijqz{xs}z}~~zu~}xrw|utptw}wvyw{yz}}w|tyv{~xwtusuqsv|~zzutyy|wz~z~~wv{}}tr}{zvx}|ztrr~}{|wusrvxx{}w~~y{|tuuwvt~{ypjlnw{v~~~}y~y}vtppnpmqv|{trvs|~{uuvxwulszpo~}~~w{|~~zx}||y}ytxtnqptrv}{squxxy~u|~vu~{}}~~~zz|}|z}|~|xxz{yvr{|wyxqtqsu|{|vrwy~|w~}}~vyzxxutsrwx{x}y}}xz|tuuxxwzsvxy{{xwy~z~{y}|yz{~|z~x|qnllotrpnrvwplwwmo|~yxy~{{~z{}z~|~}z}~}~{{{}}{~wrqwz~~yunqqxru{oootruwysrxntvz|zyxy|xyx{zvv{wstmknquuz}|}~x}w|xxzwtz}}{uttwzrrutvtqvsnoosqs{xysosvz|y}}zrsv}~|{~}{||{wvriiinps|}upywnm}z{vuu{|}|}xz{|~yz|~y|y~|~~}}|~woorxz}uttyxr|toosstwxtoxqovw|~{xxv{zyy|~xxwwxruqkmotuw}~~{{y{xxxysyx}xxw{zrswuvsrxrpopsrt}~wzsrtyxx~|wqrv||}~~|||{~{{tsphijpsv}xt|yqm}|z~vuty{~|{}x{xz}yz{}x{{~~~~}~~}}wmmnw{{vwx|tx{qorutxxwotwmswy}~yxvx|yzyzwxvvtqtmlnrwy|zx{y{xtvvwu~|xxx}xqtyvvrtxsrosttw~x{tuv}y~v{}rpqx~|~|}~}}{{yusmokgjmuy~wx~zqq{||vvuz{~{||yxvy{wyz|x~~}}}{z~}yoklr{yxx||u|yqquvwzxspyrovxz~|ywu{zzy||vwxttorqmoqx{~}|zytwwsuvwu{zz~{ss{xvstzuspsuuvx{vvv|y~v}y~{rppw}}|zv~~}|~}wuqtpmot|{z}xps}zusqttunutpzvnnx||{}}squtrssrq}{w~}|vw}}z{xqhghqv~~}{y{zuy{||xqwvmqwvz~yxuz}}~{|zwxztuw{|yqjx{{zw{}~~}{stvststqz|ws}}xvv}ywsuwww~x{yyy~|{yyxx~x{xpqvw||yrw|tt}~}~z~vtrvqpouz~tx{|||utqsvxxu~|}x|}xy|yutzwuqruvv{z{{yz}|xxxyywokjnty|yzv{{|uv}vrz}~}}{zz~}wsqostwrw{ywv~~vpuuuvspq|zuy~}zw|~zxuyy{{{}~~}{z}{u|z{||{uowwopzy{}{vz}~~~}v|tqsvqpntx{{||~xwsuwzyv~y~}z~{}xw}{x}{xtsvvww{wzyyz{~yt}zwwxqljlruzv{~}y}~~xu}zsv~|}y}|{y|vuprsvsqyz{~zsy}~~~|{rqvuxusov|vt{~}~{y~}yx{{|}}|zz}~{~xv}|~~|upxvopzxy{yuy{}||~{u~{rprvrqpvy||{~zyuwxzzw~{~{~wy}v|yx~}x}}zwsvvxv~vyyzy{|}yrxzvuvsmlkquzxx~}}~|}uz}xrllprusxyxqw~tqrzxxx{x~}xv~zzz|~}}|wutkosuxuss~zy~wwsutx{~~}~}|zzywuw{~{}}}y{|x||~}~{x|usxzwww~v{uuxurttqrorrvsry|{}ztz~wv{{~{xu}|y||z||~~|}{z|~}}y|}}}xswxosuyzvpnwtno{xy}zvz|~}~~|w~urvyvtqvwzzy~~~|}z|{{tolxwsrnoosrsmwyrosv~ywy{}zsotsomolmov~~}~~{|{zzs{tnrtwzzw{{wosywtow~xvqrz}|xytu~{}{}{xx}x~}|{}{~|spvuwx~wsorswtrvxwy{uz|~|z|~~|zuvw}xrz~x~~zttwz~{}}{yywwsljcfnvwvqjopusokrysnqxrz~zxzy{z~}}vx{pow{{~wz~}zzz}|st~z~|wwttppnnjjsmfenppmpuwxw{uy~y|wqv{xqrnqrtx}~|zjcimsx||wsutpkpqmjgjonkmkkjkosvstz}z}||~~}vuxpps{}yontwrtvz~}wsmpptrsrsuvsosnnquxtoozztqyyu~~xywwvwyxyu~vyyvuyxvty~yzyunmnpqqrprnrxrjltuuquxzzyzsw}~~~z|xwtxx{~|}}w}zldjostwz}|x{ywrwytronruopnmljmpsppv~|}{}yy}}}z|~y}|uppvpihglmsy}wxz{{yzxzyzsrsnoqvtpmv|yz~~~|ywrz~zx{}wxy|~{rwsnmqwwvx~|tssttssprorytmqz}}y}{{xyyyw||}~}{xrxyoflsy{}~{|zxty}zyvvx~xvuttqtv{yx{}}}|~~|}~{}}xx|}y~~~xqnrvnmlptv{~~{x|zwx|~zus~{w|{{{~sqx|~|}~|zzzxzwxz~}}vouroov{{z~{{z|{|zy{w|xpu{|{y}}ztoowrmmnuw}~~}y~~~{|wvqnuulit|~{|ywuy}wwuvutvy|yz}~}{{{zwqz|{}|yz{}~}~wz||}ysppzytuv|~||{z|}xszzuu{vty|wvwvyz~}}|ysz}~}{}|z|zy{~}|tzyww~|}|~~~}{}z}yosxywswwyvy|zu|}wsr{wrqsvwzz~{{~~|{y}{{vu{yopy{vtqwvomkmikntuvz~uszwuv|}{x{|~|}}~zv~~wsouztrrx{~||wrvutu||yuz{x~z|{{xywywwvswztlrx~~~}{~~~|syzxv}~yxvxwxvwzz~|ty}|z}~|~w||x}~{vropzuqortvxz~{}}~}{{wx{wmqw}~}zy{vrsroprurtw~uwz|sw{yuy{ywx}y|~{vqmvuokloptv||~|z|{wx{vqowyvqszwuy~z{yyxxywywvxzqmsx|{~~~}|}}}tostpotuuu{||{{{tnswyuvx{{z}}v~{y~zusnqzuqostwy}}}|{xyvturrttknu|{xvrzxsppqnoptrtwwuyww}{{}{z{}{{~{vuou|wrptuxz~x{}{x{ytotzxtqwywu||{{y{z|{|}|{}|suz}~zzzwxx|yz}}zwty{vvyxwx~{~~~||upuxzvxy|{{~y~~~zxrt}ztrvvxz}~~{vz~{|}zxyvttvmmry|}~zysy{vqqspqrwvwz~y{{v{}z~}|z~~~w{{zuqmistplortx{~~{}|{vt}|wx}}zz~~}{{{|y|{zy|yrw|~~}}||~}}z~~|{xopwytwzyy|yvz~{}~{|~|wuou|xsrvvxy|{{~~|~z|z}z}|xxvvsttsqstmpv}|xytvustuxvx{{w{vtz~yy{yxy|~z~|}xx}yupohlusonsuy|}|xty~|xv{|{w|}|{yywxuvwwtvwpsx~{}xyywwx{y{~{vz~||zzxpow|wy|||ywz~~{}}}y|wz~{wtsms{zuuyz}~~zw~~yx|}zw|{{yvvturuvurvvqu{zy}wsusrrtttw{~urz~xz}~}{~z{~~~}zx~}yusnktxtptvy|~}}{vrt{yvsvyzvx}}~||{}{{}~{y|wvy~y~}wxxvvwywz}}vz}}|tqx~yyzyy}}~xux||y{|~}~}{xuou{yrrtuxy{z~|xy~y{~{y}}~zyxxvuwxuqusqtz~}wz~|uuvtuvywz}wyzuy}zzxwx{}~{}~y|{xtrkmtvomqruw{{~}zw~~yy{~}w{}~|zzyzwz||vvxtux}}~|}yzxxwzyz}{u|~|}yrqy|vvttt{~}}}}|{}xwsntzwpqrtuwyx}~|xz}xz|zwz||yxvvttvwunrqopvyz~~~zwxvuuwvx{vt~{vvzyvuv~||}}}}xx}zwrqkhntpknoruy{}}yvy~}zvxz~xvz|}zzxywx{}ytwvuv{}~|}zxxvvvxwy{ur{~}|{y{upqz{xwvvx||}~~~}{{}xxsou{yrstvwz{{}zvx~}zvwx|xsvxyvvtusswywqtutuz~}}{wwuutwvw{ys{~yw~~|yxx~}~|{{~}}~~}~|{~zx}}zvtrlnuxrqstxz}}{xv|~|xwy|~wx{~}{zzzx|~~xvyxxz}y{}vvu~|xzz{yz|xy{|vxyqu~{xuttvx~}nvzxx{|}}tyzv}yysmtvmnutpqtuwvwyw{vw|}|~~|{p}zrp}{}~~vqp|x{t{vqu||wz~u~~txw|}vtt|y|{wnwspt|yryxzxxytv}~{xzx|t{yx|{wtzy|{{yy|y|~}}zz~~y~xjfnq~wyyrlkhoyvr{{x}xxuyu{zz~rq}xxxvw||zyvyyt|{z~yznn~}~~z{~znsyy~psvonqkgiihnqnxwjlnmu{{|~|{~|xsxz}qswquuvx~|~|~xmelp|||wx|olplkpiillrtqoyuousouw{urvtz|~y{yx}{w}}v}z|~|uvrgmqoythqsttuxxvqzzsuzswysuwxsxqozztwvt|wstxuz|wwuxvz{|wsrtrz}||xtoqtlknhnutvulfqts}xyyz~{{}~rtxusxrwtpvvs{~rmjfo{|ztumsyshhkcmvnjllrtpotwzzu~{z~}~~}vv{vyu{~~}xx~v}{vxrrllmnoqzttyps}ynrosywuv}vwro{yz|ttxztyvtwrqvx{}}}vqox~{~~yw|torqtztqnkssnjvyzxmgvz{}{yz|~y}~zzxys~~zt{xrttpuqs}{{}|zz~zwnfqrtu}wx{orxrqtqnprnpootpnwz~|vzuz{~|w~~yrwsnzzywnnmdfqnjru~v{vx{rvrxytsuptxy}tvqt{vtwwyvqzyst{xt{xknqx|zpolrvsp{x|wpqorqqsyrulpwv{w}zw}{z}zuwoutqnoxuqq{y{|u~}wys||uxumrorxtvw|ustnpnqupmlopnruwv~~tuu{{~~y~|zyuw|ywy~wy~|twnmpprorwy{|wvwut|vytwzsqoyy~rwprvpur}ztvypwy}~uls~}{~}~|~~rnq|untzv||z||omnruntuwurktzvwz{}z|x}xtt~yx}yvnxxvyx|~}xz{vswsqrhkpx~|yto{pppqposrtviwx{wpw~~|wr|tz|z|y{~~x{{~}y~z~{yqinkjkhpu|~|}vyxzxw}z}rtzxz{xruwqzyruqvwvyszv|wtxu|~~~~{rwuprorwv}z}zszruutyyz|tttt|{~||x~zzwwwv~~{}rqusuosztx|x}x}yutmvynokgmr}z}v{ywvtsuwwnxtrrqxutzy{qqt{|y|{~zv||{}y}||~}{{xtsihhklmpuv~|~~~~|~v~}wwtz}xxvtwqtsqtwwvuxuu|sotuyz~|~~z~tqnrvpt{x|w}yyvwx~~z|psvz}y}x{vxzxxz~xx}~xv}yvtqvuqur|{||}xzuytqwvtmlkru~}xy{}}yxoxxuwpqwsstx~y{wtsz~|}xzzvzow~}{~}|z|v}uplnplkqptmq~{||{svtu|xs}uooqzvu}zyur|}yvyptu~~{}yzzx~{{{x{uvvvrsunu|~x{~{zyzzx}~{{|{|~}|xzw|~wsusnrmmzqpqz{~zyyr}zqmoyyrtsnpk{~tw{|tsysprmu}vww||swz~z{|w{w|tuw}||~}~z{|}yy}xxpslmlonioswt}{zsu|xzvyvwyuvv{rrw|uv~|wvmmztuu}~~~{zzyzww}{uyzyvjvyuw}~~zuxwz~xzvv~{~uu}xvxmpswoqsuyx~~zvz|wqut{qstwxoqblv{~|}~}yu}}xsoyx||w~|qnvy}v}ywryumsyv{{~|~tksz|puytvefxvy{qzyxr}yxqwwux~y|un}zwv{q}|r{zvur~}~|zu|~{~z{w{}ww{{v|ppuwrq{t}{~st|}||}|u~}su}}wpntumqmq{wu|~}tv|{syqtyomsvutqnosns}wxvsw{wtpr|xv|wx}}}~}ywwt|{tyz{||}xw}{zxruypxtkiigmkluysy~{wzu}}wvxvvz{muxonspz}wsvuw~~}~zx{z~sox{wzwxywtwyt{yqx|s|}}rw}y}{~{srx~~xywosyqvwt{~y~}}~txxssuvvwpsvqpxtpnhouxu}|x{zx{|rw|ry~zx{z{|~z~}}~tvtv{sy|x}}~|utvv|rntsigkcjvtoww|u{ywz~t|yyytyupxytrztt{wnsv~{}u~}|{zxxuu~yv{pm{ykpoptrnuyx||sunovrnpwtps}{xwt~{{zvrxplx{nkhkxxtqtropmw{x~zxzw}}{}sy}wzxw|z~ww{}z}~~}~}~zzyuyztr{~zwjfptnqlmowuxyv~z~}ttow}xtx|y~wnx{vqwyx~yzu~uu}uxx}{y}{ty~}|wptypsqrsprxv}~~zy~{{y~ttsswtnstuxw~{yv~vr}x|wrltslqswtigikutxyzw{y}}y}xu|}||y{z~|u|wzw}~}|x~~z|}zytqvwturnqgollmlsrptzz{uzvzzusvs{ty{~wwvsu|ww{|zz{~~uszwrdbegnjhy{y}|}}{{|twyy{ogopskkjes}tpysx|u{yzvwunpfkojemqxxhl{yuv|y}x~~|{~yuqvxzngxysxu|wy~yxyw}{}}|z~|z|{}uxv|urkmtomrsjmsmt~spw|w~|zr{y{o{~~}xrvmnxvxv{vpwlahe]kschthhv|xzpm|||xu{vy}zxstwtwv{srs|}}y}~~xv~xpyzqomt~}~y|}yztkzxy|wwopzomzvy~v|~qvrosnheiunmlkihvpmzzztlm}{txvtvypnzuzxqyooutvr{jssrvuuzxm{{ux{ywxvwz}}v}x}ufmsnhrwq{ux~}uwruwqnlpknqviuvgq|y{|so|t}~ryvzwy{yyux}y{|yuz~x{z~yt{|wwztqurwzx{|~~z~|~}}{|yw{}{{zujwwsqu{~v||~y|~qprsmctljlgdquqyur}q`qzs|yrssxqsrzsnxyrsnyutorqxx}wv~totzxzzxqyq{xx}{}tsvmkkrnlsovn{wyuwwrhvqgo{inqowvw~|~np}|~vq|yyzzzs{xwzyz}~{x~wwx|}~qs~ujrwsz}z}{~y{z}~u~~~}zyt{ypsvvqqt{yw}ujsyoi|xkkfloppw|sqyqir}v{rrmuwwzqs}thrwvtrszos{tqzvuwryyvz~uqtpszy~~z|pltpnmofnljws|w{zqlzvpq|{ngkt|vx|~wvq~~yzyzv~x}zvp}|zxyr|~x}s~|zom{}rpvzy~{{zv|{x{yyzy~vs{uwslrukr|~x|uy|utqnfisxtnssntnnvvywmtvwszptwqsvurwyuumy|yru|xrkv|xtyuslyssyy|yyus~srqpumjfnjkyx|{z{~qvzxvzwsrpu~vy}w|xv{qvzu|y~vzz{zw~}}~z}|qz~yq{uhq|uorxz~}}ywxzz}~qz~~v|zyvvtzzmjuwx}|yuxyqxmtmm|vkt}xztryy|u|por{wrvxvpnpuqv|o|qw|u{}stwzzs{|puqpl{}vxu~x~|}|t}{thrmnilsklepux}{|{{}uxst}pqspw{wy{~x~|}zy{z~uwz}x}~zv}}w|xw{ylsqq{}{zsy}~v~zwz{v}zzzruustoszsjqx~|vvvtnrntxwynqwxwntx{|u{xtvzsquzytu}qyusznyxy{wxyspxrsz~sqplu|~{vzz}}yx|xqtnvorrhpmlhmty{z}~vq|zz||}xyzxu|||{xzx~{vy~|zy}w||v}u{zy|pjioyzzys{~}v||{{vs}x~~~z|{rwx{uzxoqutv|}{xxuzzvxz~vuuv||nux~xxvvrvzyvlrxsnyvq{lz|y}yrtzz{zzzxxopjmu|w}|{||~|}uw}~lliommrfpkjprt}{|z|~uwwx~|}v{}s|yy}zv}yx|~twz~s{~}zuvqtrv|wvsy~y~z~wv|{|~}|}~~{|}{rrsypsutomnqt||~y{xywtzxqov{zvqww~zwwuvxx|{pxqvwmzuyzz{nnvuuwzwyrltrszrwz~y}~~|}{}~y~{~}yzzsuoyplngojhinpu~}{zy}}z}}wwyy|yv|xwx}tx|{|uvpknlr{wunz||{~|~rsvx|~{}}}~~|~}~{||}{}{zvxnnqwywx|vzyzt~~z~~qvx{}~wxx{{orstytrwm{x{wy|yw{~~vtxvwnknns|{rqy|z~}|x|zx}|{urywspllkzllldqtqryuuy{xvyz~}y{xzx{|~~yrzxz{~~rv{y{tvuq~}wxqv~~}z~{}~y}~{}{yu~y~z~~|x{{zwrtouytpjotss~y}zyz}z}x{t}|wo{yvr|}{}}|z{pusqvr{w||wtrrv}}qvvr|vswww{|{}}z}~~~x{z}~zuvtkqyyztspivjklgnpopvzyy{~zuuw}wy|}~{xwz{|~vwssumswr}u{~}~~|z}zwuvwvy{{{{w||}|}|ysx~vtxv}{x{rrqqtv||vz{{}x{vqx~}~ypxvux|yynty~|~ux{~}z{w{voytruopry}wwz}~}}~~|~}~uxzy|}~{uoryxxupmoqrmnufqneorv{{tqt~}|}|}}y||}zsvw}~}z~wvs|~|~sqvr|wwx}{}z{~xuzy}yzxy~xr{~w}}z|~qqvuwwrrtsxnloqx{vx~~~y{~wz~|~{v|y~{y~yzupruyu~yxvzwu|zv{npz~|uoox}~{}~yzsrw}zw|zw}zw|tnlowzzujrhjshijotvvvru|z~~{zz|~~{{}~}zy}|tnwvwtoury}}||w{}{}v}szx{~}|{vru|~~z{xvwt|y{lozupmoxvwwxx}vu|y}y}yv{}txt{yy~vyz{||szx|{uzwvynrwsyuutpu{{zx}}w||y{|vv|{suqqz{wlfjpt{shrejtqruwsootx|~}{~|zx{x~xwwz{|y~w|tntuy{yy{}{{xrwxy|z{|xtvxy{tsu}y|puzrsiafr|wsofqsry|t{}yxzw~syzty~~y~}||sstzysyy|{{xy~x|}|{{|ytxx{x||zvrrv{|qs~uifo}ubcZbcdjowztpsv~~|x|}{{wz}~}xx|~~|yyyv}~txymu}yvllmqzzmsqnxv}{{xyyxz}~zwz{y{wpoqw~xpntws}qjvkhjjq}}wtyt{}wtx|y~zy{|ztqnq~xvqiqusqpklkjustwtuvlntpojpxtzukjnu}{u~~{~}|yw{}~{vukkf`__fyxzw|wrkgillnpruqy{wz{||}x~xz}~~x}}xx|ryur|yuvvklnmmhrrrywsonnu|vnvvpz{vwutvwtuwzxmtjeomjgir}zuwqzv{z~~xswz{ytw{yvrqpnmorrgipkt{srtiluvvtxroursuy|}yvxyxu}zw}tmb`fikjls|rwxssrokkilu{pu~r{~{{}yyrv~|w~~~vpxsuyx||ytxmipvvrxokqtxwtrqwuktzqsspquzytpoq|zkriblqrqsuy|~{yx|}|~vlquwy{}y{}}~zpkkprowtmtxqqqmnoqot}trqnou~|~uvxtxz|}|}|x{~utl^aceionxvqqtwwropzun}yuz~~|}{~x}~zy{}t{}~}vlwqnty~wysonrqoyuopqrrvuoushs|w}zplnu{|wtrzvkpjikkkkox|{zx~~wz~|xzspor|}|xtorom{xntxonrtwxsjpurzytqu|z|yy}z{~xzxvvu{~~wmojgkiedjjtzvxxy{uq|sm}|}}yx{||}|{|y}|qw~xrpsvmlsstmv}x{yxx~x~y~}zwyzxx{~~v}uzt{}|qxusyzxtswrsxrtzuz{vsoptttwssxoqs{ugfztekpt|~~|~~yrw|{y}{w}{vuz}~|z~yzy~~tlxtqiqyxz{zxxwzvytvxuwy{z~w~~qrvwywuvyyqtnovxmhqsz~|qy{z~~|uspwvmtrrwspwpipvytwvnquyzwssyxos~ytywwy}}zz|z}qt{}|~}}znfhijkoupytjmpstqjilrrr{wt~~y||z{|~~}~}~zswy{}~okorx~{spqtuqmlstgmwnmquvutrpooo|~qwynlu|~wqrvprtjjntpmv}~}~|}zluspx{vxwprrnmormvxpoqssvwprwlm|z{xrrw}|{wwxsvw{}}}}|qnrjgihjmuqy{soosttsqosps{s{xw{~}w~~}}}zzzz|~~tsqmqtv{xsstuwrluqdpuqvwrnmmqtsnq|wrvusqtz|zywvqmrppmowpp|}}w{z|kmwyz}zv|uorstwtinrmrvusqusosssyvw~{zww|{ytssz|z}~x{}sjleelqppwt{{zyuqonqwwos}ytw~}ywz~~}}~|zwqv}~~|qrlku{vx~||{xusuqntnosnquwwqmoqtwrtytrrtuy|ytrrvyppolpmpuuw}||~~s|phpx{z{~zuruwxrsuopu{xnqunnru|uwusv||}|zx|wpuz}~~vw{sqpc`dhlqu}~}}~ztrtw{vtyxst{|||ywwuz~}~ysx{}~~}}|uvvkjqvrr}|w}{}~{yxroqqolprljnsxzvstxxwynntnosv}||{xz~}z{tlpjhhilnt}~uzpckkw~yy{yv|}yuy|z|}y|vvrqoov}~~~|~yvxrnnnpr{vmotkdehglpqrsstvsopyzz~uw{vy~{tuwrrsruwux|}{y~~xtyz|xps|ohojfhgfffjojekqnjrutsqsvwwyyytvzmltvsrv}}z|}z}{xuuyuwwpoieke^afggilhflnnmouzxpszxpstoswvrsx}yw~{w}{yyz}~|oknnnicabcdekmiqqilruvvvwy|totopxxqtzwzu{}{~~~z}|{|x}|ynjjllgdfkiaeqpjoqqquwvuuw}{mqxkiqvwxxy}}}{w~{{y||styz|xprpiikigghhnunkpppuwrvyqovurwqjnqsvwy}}|{}~|yzwtuzyzxppqpnmighhjvrgptkiouyxsprsnrtmnvzury{|xvyx{~spppqqjekjbhqmpsnklotwtru{vmrtrtuxy{~}|y~~}{xq|~{|tpvpiijkmidgnjkqnmpwwsuwxxvsxwstuwxz{zzvz~~wmc^b_`fiovwv}pqysqrolkkotuuxyv~}{tu{tnrx|}zy|vy{~zx~yvwpy}~}xz}roy}}}|||}~yoryuvywtstty~|xtsqsxyutw{rtxrpsvpsyyxwzxsw{tqwtsx{yy}|}|z|}wns}xu~~~~~z|~xzzux~~}so}yplkkmntvtzxpswzyxxxyypnsopvtpvyw{}}zz~~|}}|zxvvx}{x}wmkw|~{zsmllidjvyqin}|}nrxkglopomnpporunlx|ww}v|nq~~yrrzrnllhippqrkfjrz{xvvy}z|}xzzqnppqomouzxw~vvwu{wv|{zxrhjx}yspqpmlpusikyxpoppqvz}}|u|{x||}|wwzsry}yy~wsrx}}{yxsmjdfosx|upoqljortuqnrvvuxuqyzpmorvywyx|}}|yy{|{{wvz~~wrnnmfblsst{}vqmieeggginrjfrxvtwxxyxvxwrx{nkspmrw|{|}y|{y}||||}tnkrtu|ztleggbacfifcfmomqupr{zwwz{}ztx|qjttooooqsv{{yz~wtqrux~}zrpmjiiggeija`hkihknprppvur{|tx|wsuvyztkmrljklqxrrqhcahztrqmhhlplfgmngjnjjnmjknqurlr{pkuyyzwutrrutnju|spx~vqv}ytvz~~}wvuljmppoppqsmioolonhkplknoqwtkotuuutv{~|{~~}~zusjdelru{{}vwyusssrrrsvpfmtieiklljjlkkpsno|~|{~|{~ytlfhhfijlv|x{ywvqoutmmnklpjefghihjqwwu{{v~xv|~|~~{{|xkbkpignuz{wzqpqstqmmlhgljhntsorz~~}{wvww{~~{xskmrkhmt{{|xspoprpnsxvnryyxyzzy{}{{}~x~}wu~~wy|zxtlprffoqrx~z{ywz}{xz|y}~yvwyyz{xv|}{|xz|}|{~~||~|zw{tfhmihjlquy~~{x}upvztqtwzzy|}vy~zy|vtsuxz}~z~{z}|{{wnfijeefkt~y||uvvrtwurrsuxxx~}~wopwtoknz~}}}yxxtljsukgnx~||}~xsxvqrtstx|{vv|z}}zyyyzvppw{xvx}~}}~}zyyvy{vtvz}~||}wt|ysrvyxwyzxy{xqtz|ztv~~z}|z}zsqtuux~{~zy{}}}~}~wwzwsvxvz|xx~}wvvutpoqrrstrsx|}~~|ytpuxvtpot{}~~zzz||{~tnwyolpu{|vwwqnoppjjswttz{~|||zy||qks|vqtwx{||}{~~~xrnuwolorw|}zzyuqqqttrstvyzxwwyztt{}wvz}|{ztszyrrvy|zw}~~|ytlnupmry|{|zzyuy~|wutrpqsrsw||rny}~~}}{zzwrwxopvtsux{~}|}~}{z{xqpqprux}ztvxsoquvrnotwwz~xv~}}~~~ztx{qjqtqqqruvw{||}xy|xomtsonpu{}{zyzzsnrvusuvwxvw||wz|x{zuuuwwrklrqoory~|ywtlglsrnmtzz}|wz}xuvustvwxtpy~tovyzzxvussusop{~vqw|zzxxtmkputqsyxw}{wvqntuqppqvytorvxywx|}ywxx{}}}}|}zzwwuqnrtpnrx{~tt}zpnopqpopporwvsz{|~yx}}~}}}ywuqmhltutssx{zvtxwqonopoqv{zwy}}}wqzzuwz~|{}|xvyzvldmwuquz}}|zuswxvxzvrsy}}}~|}z|wrqz{usvwz}}z}~wwzzxvrlqwrptx}|~wz||}|{}}|}yu}|{||zyvopxxsw|~{y}}~~|x~|{xvuplqpkosrtw{{}}||yuy~||zux||zyzyy||vtvwwxy|~~}||}~|vqtuleknlklosux}xw}|vtuvxxx|zsv||~~yw|ztw|zvtuy|}~|z~}}}}~wtrrsohdjmjijqy~~}~~}xxxvttsuvuxzwy~~}}|wru{{tnr{}{{}|y}ux{}|xutqpqnjjrvoinw|z}~xvvuvz|zutzzz}}}}}xssvzyusvz{{}}~zy{yyy}ztuyzzwx{{xrppprxyuty{}zytvz{z{{z{~~yx|zy|zzywsw{wstwx{}}}yxyzxwxxwy||y|{y}}||zwrouzvtvuv~}~|zzywxzzzzzyz|}~~~~{{{ywrr{|xuu||~}ywwxww|}{~|z{zxrikwxpoquz}~|{{xsu}|xx||x{}}|{}|rnx}y{|~{z~}xy|~wtxytoigptomorvz|~}~zxwwzzxyz|}xv~|y}}xv~zy{}~|y~{wt~}xvstsqolfgpqosy{zz~~}yyxvwxxz{tz}~zuxzvvzyxyy{}}}~zy~~}wrw{ywurqqopsqllprtwz~}}y|{vv{~{~yv{|touxuttuxxy||~|z{xwxyyx{yqqvwx{{vpmqtqmovxvuw|~z}~~~~}{|{||xwvxwqmqvvssw~~||zzywvuuvvv}{ssvz|yxwutuqlmuyvqr|}}~~}zvzvuy|}{xxvuvvsrx}ut{{||xxww{}|wsv~{wy||{xwwvurmkotvtrty|~yw{}zwvvy}{ttwz{z{~~zvvwy|~~z|vvz|||}{{~zvy|{vsxytrqomkoutprwz~~~tzu{~yzus{ynghhrlp|~~wvyvrou|~ww}~}wutzvop{}ivrpyzttsusu}uomfQbmwqz|ry{}xmqyxp|y{yth{mjxv{}wzs{rut|quwszz~nea}zig_dponflsz|uph{}vurwvwvuvx{ped{yqqyukw}|xo|}ru|c`jyqhkkkngqderpmruxrvugngqszzzlpzyy|~|pmiiw{vy|{y}}}~vx|~urpmgh|fkm\po_fcomdqpqk]xyvxmu|s|~wyvwzopt`\e]zy~u}z|yy{pfojrp~z}nki_kicftytlukkokogzz~qqpj~u}prkrkprq}z}{|~q~vtmx}zwuf_bqm}uxjd}oigeqhm|~njxvujr}~r{vqp}{vr}x|{v}xaei\ylw~s|~yv{zolsjiVeyvlnxtqn{urn{vopmg[rujwwljjhyvoqwvrolqqv{|tzvxp|ukw}{u{lb[f^f{vzlhh{sn{wkgunpbbtnx{~{r{{vztw|y}rpoZhshw|q}{~}}ytuwp{s]Rtu{tphxvnjosglkn]bk`ixqplj}wssy{rqhj^m{|s}{xux~{y|uqlkn`kzwsjdquit}yu^mzossgnmfv}}{{~zxt~s~qjp`\r`\ex}}{{}~z~|z{oYluuy~vynotlmo~pnoriithitm{|wr{tvrstv{ikinelqw|z}zwrop|rwqjj[dltpwqk|sy{~kvyxwjppfovt~x}rwttszxq{vwfawg_arxz}{z}vw}|{z|lUhjemux~{~yyxpsz}lnp`ajarpoo}syl{r}uxxxhmkrw}~y{uzrt{tu|uzyvnrk[`dca{y|wnotnsmhn^d_gtpt{y|}z~y}~rytfdgimy{{|}|zrt~f`g\cirytzvkvmmfhzocifed~~q}zu}xrszfhia`]x~}yxy~y|vqxifjhfu{tqh~xchtnlhkxpml}|}~|xwo~nndjx\Wdhd}}t}ymtq}}tvohndcfhuxs}wwsyymxtppmlpyx|q}sqplnfoxlywcsoss~~~s}m}kjzxuvoiqlXeYchs|}||yttxoqsiinoy{t|jwikztyw{yxoxpnmnq|x~vxsoxyu~vvsdjVi\NShk}~~yyrtomwqaoblhsqozwt}y{}ytstknpjpw{yqt|}|}w|sthainbpti\srw{qstfl\pgX\poz{}z}yzqrlgqoaskw{}}wpq{zspymk`kijrz{xtpl~i`isgu}vewz~}}~|z|uwtotafXhjZaz}vuz|~vvrphbfegtx~~tlx}r}l|vtmpwqy~}~~v{uy{lpexq]]nefy|pw|wvqnnto{vxspride^bspwyw}wxtlmpox}}y{oqkufj|ptklvnoiz{zwtvsht~p}xxmkre`fVYWm|t{ywyuwxooohs~uvxr|jnuxwvzyowtnqlipyzwyynywu~|zxryfbYchTObgd|{zotxionbef~~pu}}{ywz}wooplpolstvvz{}}~sqkxyd`oh_ose]qsz}|v~vigevk\evmx|}wy{x}ysukgpierilq{}|}~{wpqvrpivnjahkhrwzvrvthszisyiu~}~}~tu}opsb^Zil]bz}}t}|t}xz{~y~xrztppgabdgqt|}xx~s|wosyt{svlifrlb`kp_cxy}jy|}~txw|tr}z|~x|zvnsd]d_gkpwuxwuwmllh{{nz{t|xxwqwjtzrklifbtzw}|t~k|}yx|stltmdm[T^UYympz~vpxpjr{zstryn{xmwfmk|sk{rxjf{{twzl}v{wj|txh^ga`b^gf{|z|k}omelveu~~n{}z|x|zptxpehocptz~~{sv~~vvsupslaqgagXQnbajt|~wr{vliupos~}z|oppjyokxwe[wru|w|r}{ju|px}z{~kncqlijgi_lqtuo{ppoykt}}oz|s|{qqumhfpcdgqkmz}xz~pwt|x}zu|}m|sfndXsgUXWqx|x~~u|vq{}w{}njlfuwiy}yrayxmolnus~pnteqw{{z~rwvfgajbdeeaewu~zxv|xnh~{ys{yzsztknphpsxfr{t~r|t}xyuwoom}pkmY^pVQOZhbqv~v{t{rmw~tz{ssnl|xwrh~wsulkzox|x|}qw{jqtuuxlwqehdgYa`hehpjs{{y}yw~jiz~~~~x{zv|vimmmcqqhosow~y{{~s|zzz}~qoxlhlakx]SN_mdnxvws|vs|zp}xsw~twh{xonhapkls~~{vwfrqnk|{w|xz{hjjrehihgcoisuzuz~tf{zz~wtsxhimn^emdgrm}xz{vylprvywxrzpqjd{m^Y]kb[n|}u|uy}v~z}v~uo~suyel|yztfinfszz{rqjkqgdr}xwsqri^cdk_hccgsyuq{~|}zz~|xyx}}qkmqikumfjhs}}yxxyystqvtxylhjfyq]TYhaXcpqog}yy{zmvxtytxxyvpu|wkmwimnuq~xrkn{plx}z{||qdhhmwukqptvynyzzs}qu{|v{x~rxnrxv{wwz~pmz|~sqmrxywrcountwz}zzrw{}|z}~~z}|{~runxnsg_mh_luv|tr}q_\~lm_Zilmvxwnyss~xz|wxw|uxvskz~{wl}~os|wv}}sgqxk[irqqxyyopx|yxtw~q|wyyznp_d[utnrlggdokdkeegg~ow~p|hluqq_mzywqfolz}yxdqpvwmtu~|owlzrtytqsoomxsxxqkoyty|yy}zzfQ]P\dixpyqkY__Zqfcqftrfe}l~w\f|}zfchrxz}r{\hmcymuvxzuxfsjhzoypunpouo|rvle~qy~|{}ruQ]bN``x~|tvzo\bi]^t]qpk~qo_xmkk[u~ux~bcltwzwh_xkkss~{wx{|t|mmhubquso~}nqvs{o~aur~{~~v|~|z~pdPf^Tad|}~s|{j\kj[fj`tjo|rid|ksd^z}r{|bepv|y}ch~mqqw~s~zyvuykfiq`suuo{psytqzaysz|zvz}}}z{p}_Pg_Ycf~r~{k\lk]hk^pfo}rkcqvd]w~s|}dgq{{{}eg}nssv|vxz~wpvh|eal\pxwt~ts{yztdvv{}~}}uz}~y{mxdLacYhgu}~s]jo`bt\lhg~tnb{xrlXo~tukioy{oc|vrypy{{v~z|jzxgmnXe\f~z}|zzuvty{mkz~|w~xy|}}w|nnoPSg\joytzj`si^sd_j^t|rhku~~]_w{p|mo}}{co~p|sz{u~y~om{kaz~X[aZ{yxsy{~y{g}zy|yw~|yx~}~wzty{mhpXMechyur|r_om`ko[h^ksmd|{dXp}s|qryz}ij}q|yuwzw~ysgwl_p]T`Xv~~|u~t~{ixx{{|t|}}y~~y~v~usqvjgnZLdej~wt~udqqclr[g]itne|eXp}r{utzx~km~r{uuzv}y~rgui^r~ZT^X{}vs||jzyy|ys|{v}yy|t}tmmoghmXQgcryu|sfuocsk\h\rtkj|_]u|q~wsvit}rt}vx}vw~zkktc_zuRY[]{vvv}zvnzwystzt~s{wux}dmhglgPZfbw}|plxjh`cb`xsez|sXh|wuvwszz~ul}t~qs~vws{tgrl^m`Q\Tryp|z~mzyyz|yq|wv|w{{rzrhjjfklWTe^vzuyn{riiag^{}ujr}}\cyzrvvot~~mzyyqxy|xtq}wjnoaglPYTd||{rwy|rrvztqu}|v{r~s{p|taiejobR_ael}svzlr}`gajwsgl[o}tx|ttrsy~urtuotx}uvthri`r~\QZTu~}|zoyxp{|{v~x~xp|swzv{zp}|nhaddlm\Tb]q|m}t{xn{uaj`vwrle_u|s|wwrypy{tv}tz{o~}s{vytqhqf`vzWSYW{}{zoxx~q|y~tyz~vo~stzt}xo|xl~c_acll[Ub]v|p|v|yo}ubjaxwrke`u}u}wytyqx{vv|u|zm~rxvwrrfme_s{ZRZX{~|oxxry{{v~t~wnytxwuwwm{xyhzi[``il_Ubaqpyy|qzdjdqxvhl]p~x}}x{twv|zs}usott}orsehf]j}gR[Xs}tuxvt}vxn|or}}rwpxrozxllwZ\]dkgY\gg{u|usrfjexoo~`dy|zvt|~tx{yvp{qznyzok`g^_uxZX^dyqyz~qzyrxu}sm{wtup|r{iy~srbvbY]`ik_Yhi{xxxr~fjd{wugf_t~~}{yz}xt|w~xm}qups|ln^b^\p{cX`c}syzsv{zuo}umww~xsrws~htqs`sfW]_ilbZhl{yxyshkf{wwfh`t{|{yt|w~xn}qtmt{lm]a]]q{cZbf|ty{swy}r{p}smxw}}vsp{pygt{pp^sdX]`jkb\jm~xywu}ilhxthdcy~{z~ww|{vpypwhztmh[`Zbww^]aoyv{~|qywqqy}oo}}}~~rtp}nlhzqrfdsZ\]eli]`lr|w{v}tlks{|ktt`l}vzu|yss|rq|pn~lo_]]\n{kZacy{}tuy{s~o~slutz|xsq~nufswoo^tcZ^bkj`[hj|y{y|~npp|tl|dh|zv{wz|s|uupyvjnoe\^[izr\^`zz|z}yrzx}vm~xlp|ry}|stuvpgk|~ms`jp[^_hkeZahusy|ytqnz~i}ncr~v}v~{xq}qs|ju{ko]^\`r{gZ`g~{~tvyq{pqlu}|rx}{yur}m{sepuno\rh[^bjkaZehv||}ssr|{iifw{{{|x~~yqypt}{gzvlm[][cuyd]am}|swxowr~olu}xtw{{wtr{j}odqrml\se\_ckka[fj}y|{ttt|{iigx{||}y~yqyos{~yfyvkn\\[cu{h_cn}tuwo{opks|{rvzz{ts{mtrcl{tioZmj[^bjle\fkv{|}wuszi}mftxx}|ws|ppy|{hq{hp`Z[_p|pafkz~~yrxxvvk}tlozqyxxtsv{xgzdds}}ygna_r_]_gnlbcmv{}|{vu|ukzgl}}{zz|srtmry{rd{ojkYZ[gy|jgj}|~}vwmcbguqjiry}|{~vwlszsstsztsru{xsvoy{{}zvyzxrzw|xwon}zpiuw~pqswwswtxvxu}|ozu~~{zx{tt~uuw{w{x~~tk\hywx~||}|u}vo~}nz{nwy{zeUd{z}|sdlmo}z}v{vs~}rt}~qwo~}tx~{yzlgV\wv~v|{}xys~y|{vi_L_}kki`luos|qjtrhrwvrj}wlz|n}~|vuzv{sfd[Ws|xv{{{tz~}|i][N[zwugmunoyqdmnch~spofuuxlw|mz|xs}{|tu}ped^Wnzu~|z{t|}xxg^]QYv|zjnvnozrbkmae}}snnessvnx|nxzwr}|{uu}rfe_Xl{u||vzrz}xxh_]QYr{lpwmpzq`jj^f~}qlict}pvs}{n{}us~}}tw}pff^Yk|yv|wwvr~w~zvhc_R[p|{msvks|m_li]i{okgduxjxuyo~~tt|sz|ohi^[lxyy{|twss~{u{vie_S\my~qxwkv|m_kg[jzmhccuwhvyzptt~~tz|oii_\jv{x||tvsr|ys{vjfaV\jvtxykv~o^mi\izlfabuwgu{|q~ut}uz}pllc^it}|x|zuuuq}yr}|{xkgbX\htyy~muu`jl]d~}me`^qyjo~qzyt|{vx}rlke_gq{yw|pxo}wqx{z}zlhe[[eq~zrqzdgp`_xpe_[jznjtv~tx{yv}ummhafox|vp~pxwqr{z|ohe^\dm{||m|oari]lvg_Y`wwiy|r~~xuy}uzznljcemu}|xwtuoxqlz~}wzsffb\bkw~qtzekraaz~laZYl}pntvvx{~wv{rklgdlr~uq~qv|smovvwjdd^^jsm{}{}wrsqbnyvspxtrrryqkxvy}ojqysouyyns}z||{xmkkisqx_p~xpnelqkqkjolzznfr}|ws|t{tylx~qr}~spvxv|u{zl{j}yvrdrkppspsy~rizjpwwgnlUjlptrppq\d{vmzuts|qztzy{x~jj}vz{pio~xsn}{zt}}|m`nsfw}rj`jnjsz{bqggmr|nno}|~t{lnbgnko{}}ytntwyxt|~~wrzzxo~yshbntrxprxgjue~]jY_vg{{quf}|q|trluit||}w{mgxqp~zvov{x||syv^nid~|~t|srrz~utsl^psqfjtkx{|q}l{w]ryblbyn~uiuwvy|z~{uxu|ry|~{~nhlilj}yozigzscbwbhz{gd}zz|w~q{rtmivsqwutouqfwtfv|xyxx|yu|ycrwvylwvqyrqprhkinwekp|qpxrxlr}ed`jys}tutlts{zzxy{vwz}wsx{wkfduottkuowtzxlwv{pcxqs|sr{rs~rkuqpkty~xziole}ytzrmz}v|}xyuy{qt_r~lx~vqelznljqio|mr{domdckzjn}|~oopymr~~~w|{uxqpu~rfpphnwyxilqnssc}xotnnysu|strvrtluqmqt{roaqigpu~~~~|w~~~|xcc|t|{yz~y|tsqlurmaqtjkmn|vmifxtiyy|~y{|xtrplss}}z}t}|zs~{}rxfhnrnxwnuie}}sihxrps~sivxrlwqrjy|~~xztfxvhfizw~zyy{q}{dexvyuuz{vuiruufqldkhe~fer|qix}zojz}xqhosvpwyxvzx~y~liholi||{~x}|~swuywl`nb~}mfhbdkzuw|~s~ywkkntyxu~~tzwcWzuji}w|yt{rj\bmsp}{rsu{yyv}zygbj[bw}skw~|{{oes~{w}y|u~ucbt}zkq{wlgw{uwqc_vyqpzvyj\br_dgaVUltlhxz~w{zm^g~|jqwrxrudku~vxtqdg~~qr}|zmcwr{g]ly}sjf`OYnh\erj{tqqy~yl^]vjj|su~uyut~yimy|u|scswm~yjsumqvmapxxsxcXlq^^vkqromrtvmaWk~|le}zn~}yzuhw|olzxwkm}n}|}npxgerrcnw||xr]hvd]vwjwopnmqmeWdxyyqd|px}}|zbo{tjzuwskpwsm|japtejvvxxbgxi\rzi~|ppnlpmgW`txxvdzqv||{|bn{uixtyulqutl~~laptdhtq{}fj{m]p{j~}qpnlolgW^qvvxdyst{|z~~cm{uhur{{vorrsl|mcsucgqn}hqo^rxj}rqoopmfW^ptwwe{qvzyw|er~rgtm{}urou}qm|~jgvs`hkk~nxm`vum{srppomdV`opwthpx}yxw}xfvnhrh|uwnxzom}}kixs_hiiq|pavsm|sqqqpndV_opwwhqx~ywt}zgvogqgyvxovzom{khwt_ggfu~m]oj`tnffilnneUYit{|u{j]txkqxtqnwxzw|e~km|yv}{vs|yqrt_st_Xql|xzyqox|ehuunvrtaty}|k_v~mwok{o~|zxzp|skulQ^l`_jbot{|lk{u|rrsrrqaarxqzzpswmniirv{lg}w|twr{nptl}kakkU[qrfqok~n~xtrxyp}vo}tmnuwxzpdp|upnisj`[}rvwxp}~~{x~snuulpv}~{~x}zwx~mqw~wpylYnrron}|p|~{xsZWixcqsrlp~~jVZ|gswkkyxzwirzpdwruw|wvyswzueour}x{j~w}~yzokzxvvruzsiblrvpbZ`klskuy}}{flobpzxj}~|w~yseilmgugr|ppy}iqpsho|}wnp]_mp`idT\jupfyt|z{icixvhp|ruuxuvjnv}usvfdYy||z}~zwls}wx}jWT_dpu}q~vu`trclqxe[|nop{sjnm|r~~wtvr~}zi\cq|pvyq}}qzzu|{ia_\SVkpikxtpxsjokkjtlcXS`htny|vwywumirwigq}|z{oiw|{msj^Zupss~fx}}kmfwsjwtfjneaqspvys~pzyuvmrpukg\p}{xwu{v~}wolXQ]n}ntl^nnphn}qmde]]tlri|y~jg~wsl_imjv|ut}}yk{vq}{}cRcunp|~y{rmunhcqsfm`\bu~{v{xvtp}u|t}ouuabc]cz{}}|x}|yysxl`Ohddbn|yvjttqknv|hWXstgnv}r~|v~nyipjffW`u}wv|lnlqrhn|bfag`fwz{{opt~|rqiimcj[dvjpcv~zto}uszvry}x}uh\i}{{|||~uxsxxkhiVW[V\|~pzut~sokrdkjiakqrt~ytwwzts|wumimYatr}zvvyzxvtlw}yl]Xl`bekzr{z{lz{ibcqueZ[[[^{~|y|}uo|lxp_jfilz}xz}|jr}~xzttzmkl^Pa~~wy}sqnu|lhb[qdegjpyvtx~t~zvnZktgWWbgqxoowu}xtkd^^`g]d{ustmiwrfn}qlmvj]fxywy}||nm|thgk^owtsrxhuqpw|}|q~yk`[u__muy~twvsqpzzghjmmnq\|{zywqsvppspvwxqsypquulm||x|wtsy~cuuaRMhrhgtoywjx|ejZps^loxy}qs~w~}ql|we]emqw}|y}x~|x}~r|~xjjda__eoh{jmljpiaW^sxifopq{}z|yr~zprqjbi^{zr|zy{pq|}|suskZmq_UYqtxzowvvsntl_cgjjsylyxs~zzohqg`jffaw{}}wxxspnfkcbeccnrzpkmd]pvjfbw}}tt}w~|wvrvslp|k_blqr}wznz~zq{}|yvhjzq_Zfjpvwyzokupdgmoflyup}w~uwwmf`cbkfggv~}vxwot}Xgs\]Tdnopz{u_psieqrowjp}~yw|~{zsfmpd`ertpx{zplumj|y{tr~jc^gba][ep{zubvqbf`lppjsyrklyxebnrkfkxzy{{vrs~v~zntbV]UMWllpru|ekpjkomikuwz}qztw|tsbnickes{{|y|t|~|}}|||~_kv\^]`fq}zuxlj`[Ymtmplls}w{fliabioikwtnszqu~|xv~{|}wuvmUVgnmlyrlnejkimwxvutyy{xpoxztbXUT^qrvx{|vopu}yuw{ystlpsgOZj`gfpk]dwwrs{~~s{zv}~{rspera_igkwxs{iffhn~yv[qtZWV]elrr|~ws|vo\nviqhrxx{yturx~~vty~rbXeuz|}y|nttxxzz~qssl\\ttaSRVVd~zlifgkltusu||y}{tgpwpkgZfunxx|zskp~ystmekoaQge_lqy|~rhpz}qca^`f||yxoqlitc_dfoyzu}{xynj{{uysro~uq[hypkhe[\sztx{tt`lxhqty~~w~sz|nkbebbnnf_`gm{k|zqqsz}zzxvvpeea^_cihay~zyor}yxymfo~x|||{}sfepldhg`vyvopwoitrops{wsny~xmljg[Vpniqz|}~pkmrwupj}|spv~~unmoxjnjcprvzxsrtu~wsr|tp|{jwnlY[`VQWmzwuv~}}qrcqstu|yu~tw{vojvse_eouzw|{y{|~|iu~u{||uqsqpg_b_]^botmusqke`br~wrss}{~z~qow~uuzsfhhg~v|~tlrx|}zrtzut{tkea\aofbW\t}{w{tkkmrt}rx~}~|ujzodpb\\]dpx~tuynx}~~}y~mm~lgadkhhdektxqi{trij|zwryurmhsqd`dmrx}u}|}lfhkkv}~~zu}xi_]jvpkkiotqt|xwvtpqy|}~x|wxtrz{nc[dqseigi~xuvmlstx|z~}wvvuvpdUhnbbVYadm|xkw~z|tvxo~~~||{oszdlqjimsqpw~rjss{zoxlr~z|sj}vi^`hfc__dku{lvvrohhhkoyr|t{|l\fpsrrwur}xu}{|vtw}yvu{||~tomhkw{p]Z^Y^jgimj|w}~xvtrnotv}z{vzywmddpqcigbgkp~}stsz}~yw~v||}uurqrseXol`bagnsvu}|xnlttutm~q}zowzkpmgfimijpw{yznyxnwmjkms|zsy{{|sjwyujgcWZeotv{xtzzsuxy{~yy|xw{topnrwuvl`\^nvnvvvx{zspqwutty}nhrniaisuyqz~}}x~}thejo||sf\bnzsqlhjlgdosvx{rvrg]Tlug`[et{rq|~||sx|~|mswnv}xqofhs~}~nkw~|qtyqiaovnxwmpmwyrf[gsx~}l^n|rb]_`d`bvtox~}}z~sf`abk~qtyu{ztim~wnyihnuy~r~v}oqnwai~pw[\hl|~paYj|tyyzgh`ax^[d^uwx|xr~x~~xwnxmnyxz}~eenwtpoq^hpt{{wlighjrttmqpezz}~xwx`morsuma_y~u|yyzt}qknosmv{uobvznw{rvmncZnru]_ksoeo}}uuzwhmrwsedrh^glyyxqt{{qmow}ttrorg|ortyuszw}ozv~ppnhwp[h|yh~|r{{stgacYhhnb`u}wtyu{y~mquzpuu}~xnwsz|rwjkvinxntvl_htm`mbirggp~zr|mspsnqk]kvhivzk~o}phuxy~w~~wxwuu~{mwnryz{roykqfrois}tavwqowpw|wkkmebegi_tmprnx}worxx}sv}oi|qjrzp}zkr{xmt~kcakqfqrjru~xziw}r}iemiqZilnznumwwx}rrw|y{{}~vwvwoi}xo{ouv}{n|objtdclxtdqxrqv{vtxt{~]V_lc[lrp{u{~}smrpvrpyllu{upu|mllxyaqw{tmfgw}ehzzmxy~qdvth`cgWhpxo~yuz|szfu|svwj|ylhymqlj||imuxjfmwotrdsxvfhvxt_coimfhcqozl{nqzswtv}rqpqndu}yojxupln{quemzuuz}luyp|psxugghgh\dmyv|spiw{x|}xsu{{jujetkhv|kswkow|yrisriingewsnyyentsj`fmhqln{uu{w|r~skr{rp{~yvrsmplhuvn{wuyr~zuz{lbW[eul\csrmv}plosmgebkfk~nczz}po}tko|}|wuw}yojzrpukmzumnrvr}n~olggfkmljorfluwtpsrhqlajozpgx}yrr{|}|~kqs{sytuiv{sfnldyvmwzxqs`tvokplq||i_y{v|mffq~ulZqpiszzo~{{z}{y|xpvkcq}ob{{}lo}w{wwfttzwmgolg}~zlx{faVhtpfi~|fqqz~}xt~{x~q|qrk|sayxu_k}pw{s~oqemomnvv~itskag_itrfz{kw{v{o}uowzkhh}tmyuygz}wy}zrqpjnphgox{`wruzcfeaopfqvxohzxru~|{{}jonpzrdttyykn}xy|~~zvdgsvoruwrnmn^cbn}ti{|z~ft||~|z~qw~xu~tvvnlhy~jgxrf~yx}w}utkgopmqrrxwgnmubgfarwlu~mq~nuuyv~lgbp|qcu{t_l~z{ztskrvonvxuwg{tqd`]_t|mh~~~sg~{{xyx}u{v}qmbh{xkuxxxily~y|tqisxoglrt}mq~nlhearzql{~~zk~|~|}|sq~vtq~jhcm~tdo{}gf}}y}|znptpnrusxfnymiee`nvngyz|lz~z|vrzxn}|oh`hxtjpvwxfg}x~wxou|tkmuw}jrysmhddtuki~uh~zz~zy~ur{x~yq}kifs~tgqvvwhoy}z~{nsxqkquuzft~uukd]bwxniz}~uo}~{~yzuvtpzzlecv~nbn|twrdq}zx}|qwyspvzwxfx}zyjibfxtif}}oj}}|qpszu{y{xjliffj_hona~y}}|{}}}vfo|pyeuyy|{tsimqqpfxxpgo|}{szuxxvq~xv{yv~k}lrrhpZ\asrxwptq~yxy~mlupboqx}{wel~qsqwvsxunvy}v~~~~swjq|lq~lzrxxxd\xrkmyuq}wfaes{uwpjyvx~{o^j~hemtkf~ytv}}~x{zptx{uy~|itynolpdjxmgeylf}|jx~qnw{sf|vpwy{rrc|pupbjq|}~}y}sqswwcx}xt{yw~swkrsfmmtnpuntyi~wndx|o^vrwwhdotzzhiyw~w|{~r{szhek}t~~~nlrvya_uyh`hog`}~oy}vvwwptvvt{|jjmsytuvnux|omxxxxwu}|{{how}{|~zznesugp`sslsbjw||msmhnrai{tj{tulh~vmjoruyyv}|vt|}|z}|}eu|zxty|x|~larpthw|jpvyi`x}z{}smnrnnqu|||tupig~ye^zvhowuqz}y~|o|lqqtsxvzxsb_kywjgqmsuzldus~}z~ucjxtty~{xpnrtuwz|xtzi|w|z{u~}~ucqyggqymt|nlykpmxkmeo|dnow}~z|w~xzy}sd}xnyttlpl[mxpkx~nv}|{}}~ytyfx{uvrsz^snsljupkXe~yhgx~pv}|qtimqzyw|w~t~monrqvwsnxy|s}~~vyws~usyyxltx{ttokhqxlfirtqnsf{yys~|tboyinvrep|fewqn~svoxp|}z{y|~towtyxzjv|p~}f^ciakqjgg~zw{ty|w|~~uvtivuqy}fiuwer{vlb|wyzsr~pu}rvf|o}wvqtixlsphbhqohjrt{oyx~x|q}|wivyzkquztxqknx|zxy~~u|vlyxoagwft|uqvzvxz~}{wx||vacs}~~vxuw|xracjkidcbm}uny~~~YWgbgoy}ru~|vxlvxhvxuwxzp[Tahomojjnp}rx\Cy|rllkpjibgwxgtlwytxu~z~|txkXer~~~z{syoqz|tx~pGs~zplpvhsw}rtrwrpzqwtnqq|ee{}kcaoTTjbalquwyx[Wwi`iZchln_cyldksi_bqw}jkXlzwvwu~}wsv{uh_jrO{uu}fyqyipyo{v{{i|zosmrvzlcTm`_||svzqonn|nSsib]Rqxzwb[pddq}rfT|utykznyqWlmhisywqozuk]wZ|wx{twv|rnuxexssxtqwr{{}gov}sn\ao]txznjjhrzXhqcaPq}o\ffdnxqXiphynp~~\dqhil}wwht|wt_qno{|}v~~stvvf~xoh~oxzio}}jbmuxmk[xhxt{ujkjl|hZwghV\||xb^{saesxocZusxvn{h\v~mlm{}}~}eir}xsgj~izuzwtwvhzvxdq{{t|m~sy}~k\dnvml^xr~{t{xllkl|nXxkj\Wy}ze\uwaanvmeXz~zryozn]rqnozz{{|ddnzysjjlzw{xtwvi{w{d{q{|sy~ntzy|~gZakslj`}q|w~zoonqh]{hm[Yy|~ua^{r_`mqi``vtx{ti_xsqs|}~}zv`dnzurgq}p}w}wuwwfyxc{owr|zmr~w}w^Zbopkef~k{|wqqoy}\oyhoTe{{zj\gg^cpkfZttk{|zai{tsz~wuy~i_grxpld~l|z~wxxntvilpurtzo~xwxy}hX^irkh^vm{}vtrviekr^Yvzzo_`|n_`llf]fqo||hd|usv~{y{}p`eoxplaulu|zyy~toxpfprrr~wpy|vyy~lZ^gqif[oin{xvyoensgUqxwn__xsa_ild^bp|sqncvyuwwz|{vaclxrmampe}zy{~j{zbysntqxxto}u{|{w}v^Zanlf^bq^}zyd|}nuYcwwse^lg_clea\z}o~g}emzx{su~zj`frulgbva{~}{pxwnfxntqq{zto~}wxx}mY[dohdZlif~|}xhwtrXkwvpbasyd_fkc``uuzhwfq|{}qu|z|e_erqjcfv`~~|mwji~tmxzppzuqn~{zy}~{vwzjWYbldb[oij~xlxvtYiutnbcuzd_djb`cwv|iygq}}puzx}{d\bnpiefyc}p}xof|umw~powupo|wzyzsswlVV]idb]nrg~ks{_brrpdbpi``hbaaqj~imzu|x{zvx|kZ]fohgdzot~yr~~~dq}|ynqsnqwpqvyt}tt}{rorq\QVbhccf}kqwt{q\lqpjbi|sc_efacstzoriv~ptzttyr\Y_kjgfv}moykiyyyoo|wnmuopt|vx~v{vv}zyomnq`PS\gbdfs~utxzw]ippkbhyxe`dgbdqymvit~os~zstvs]X]ikhhtnqyogvwwoo|xnltpqv}uw|syttzxvmlmo_PR[eadjstzw|v_joojcl|uf_efbfxv|orjw{}os}|}vrsvoZX\ihgh{}q}szjivvumqtmmsnqzxsz~movwwskkmlXPT`dbdqnswnbnnnggsndahdelvpzmpzqyww{zyrstveXYahdgnotzyepttomx|pjpont~|xuzvpxpwuumklocRQWb_af}ryx{|cjoojfo~vhbffci}tyosnzyxq}t|{{tstwl[Y]fcdh}qtw}}gmrsplvrkopmr}w|uyxm|outtmlmpfTRU`]^cxpp|gjoolgq~{ldfhdizt}lwnxy|mrzy{urtwq^Y[ecbdvuj}y|miqqqksumlplp|st{}|k}psttpkmomZRR[][_nve{|ugonoimyridiehqtovpt{pxwvyyxruvwg[Y_d`cl{g|w{~hmpqnny}rknnmtx~u{vwukzmsrrlkmogVRT][\bunkojponjr~phfjfkxyzl|~pwz}l}tvwxuqtvtcZX_b`cqyfxzzhnnplp|zpjnlnwxywxvvqlwmqopjkmodURT\Z]exlpqkoonktrigjgm{y{m{qw{}l{uuwwtpsutdZX^b`dq{hy{~imnpmoz|qjnmnvx~txt}t~shykomniikmfWRR[Z]exsmykonokr}ulfkhmywsvst~yqrzruttoqsti\XZb`dnpxz~pjmmmkuulknlttrwvw{fqmklkjeijk]SQW]]drnomnommx}qijkkuxqyryvzj~qrqrnnqso_YW_ack}|p~{zzjlkmkq|xoinlq~{zwss|ws|ji~rhjhidgikaURT]]dqs~tmnnnmwsjjllu{r~|rxw}}jzrqpqnlprpaZW^bcl|qzz}kljmkq{ypjnmr{{vrr{urzig|qghggcfhkaURU]]fsttonnnnyskkmnyyryryuyi}pqopllpro`ZX`aeo|s|~zyjkjljr}vmimlt~vx~ps}ptwcl|lggfecgij]URX]_iyprpnpns}{qjnlrz|utu}zxonyppnokoqsi]Y[aajwqrnkjjny}rhggl}uudp~ppz|ueuwyztmoswsgcdllr}zzy|o|wwoijlnq~{ovypn_oxu}~~nx}zuvy|vhb`gfkuutwszrrruv}spmjx|hpspyouqgxukhig[XXabiuvr~}t~zvmmnrtunjerlptowz}ixuy|~}rljprv~qtlotkyocccghs~xnhifpon|ulr}w|zcixorzpgchjlvp~|tgd^f{xyn`dlz}mpvcbwpkorvuxyzpfbchhq}m~}~|v|{}wrpruywvtvy}|~tzjZlqjs{ne`c`dnwifxp{ttzs}xznmlpr|zohgeq|ywou{nv|{}~vquxyxsrz~t|{ieadelwxnffdiyvyjtylnvvt{i`uvouzqkinmtux~{y{{xjba`o~{mchq}wjsr^exolnrvvzz{ogcfiku~lz|zv}~}xspsu{vutxz{zwpvgXktov|sjdgeiszkgzowttxs|w~qnlpqzzofgfsw{ps{zro~zutyyz{zt|||vtiedgit}sidefuynly~rkpy{nur^f{xx|zqlmpq|r{wvwy|nb`bj}~zjfku~~ojtk[kzrsruvx{|zoiejkq|ynwz{~}|yuquw|tvvywy|jlo]Zu{{skhkir{|hqwpy}uy}zs|y{qmmpr|tifhmzxvpvx}kwytwy|sxv~{~wojehipzwmefgrxxjqwkmt|lpud_y|vposs}u~{ustu}{ncaem}|mhlu~|njtk[k|uusvwy}}|qkglms~wnuw{}|zurvw{tvwytw|tfmm[_y|}vonooyzg{sq{xux~uxwvqlnpv{pghj{uxwjmr}~puyf^x{~~}smlqr}s}z|s{}~wv|~xssuyymhenvlm{ymmt~ku~tv}xtqvw{{|}zyr{v}tpptytjejom{tqw{yubqxsosu}}{|pk{{tlgyvp|spnsv~rkknup{~rnt~~zu`k~ysotu|zuz{{tx~x}|{sfbcxwlr||}yshdjtur|tak~|}~{uostyzrzxzumr{tvxtptwypqqzy~w}v__vz|rjikmx}te{rnw~{wmkpy|ryt|ysort|tiggry{zorxzrl~}zy|~xtljs~{ysr}slfgjpz~tjgfl}ysq}{ont}uw}o_o~|xsww~wv{{vx}~|x{}zmdcev{ymeip{~tnyubezyxx|~vqrtx}nzt{{wnntzx~}wrtw~uts|}~yztxyg[o{|}uprqx{in{moz|}|xqimq|~wr|uwrosvzoiik|yvpt{{nq~v~uz{{vslmu{}tvunhilr{|sihhpyrq}zoot}sv|m_p{vyzwvzywz~~}wzzxmdehy}~{qgjoy~|tmwweav}}}ytswxozrs||}{xspw~|zy|vsw{ytvz{}wzsnvo^c|{tstuvgztlqzy|ywposxzrwwx~wqpsw~tkilv}w~srv~y|zlx~|tuntwuwuuryxszwpjlnu|ypikm{|pq~~vmpu~~vnvwgczzx|}s}xy|ytvw~|rifktzznimr{~xnnxqai~xtvx|xmvnsyx{zzvyxz{tvy}vwz|{uvy|sjqse`xzvxw{irzlmvwzyxtqtw~ystzuzspsv}vmlpyw~~trv|y|{mx~yqrjpttwvwu{zu{~tmop|xy}qt{rox|yuom|xtjz{rx}yy{zs{us}sprw{stu~zwwvhp{{}yvr~um~z{zu|uu~zuvv}~z~{p{yvv~xxzzyr{~vlpy|u|ymz{vpxz}khqvz}|xrssy}{vv}q}}xyzzwy|z}u{}sjly|txzlu}xyy}z|w}}}}{kt{}yrnpsqzvlr}{tkjrz}vz|mtzsssz~|~v|~vuty}|xwu~wzypgfq~zyyhl~}ww{{wrstyvr{rx~~vtsw{{vs~nu~||{y{|{}tv~uou}ywzspqsy||~{qmlmruytqmxoq|zxtwzxyz|xtq{woglu{vn~|~x}}|{{uzqkxyzz~zww{yn}pir{}~xqghnw{u}xlv~~ytuu|w|z~vutx{{xxv}uu~yqhdlwnlzx{~}xssuxwotv~wtstx{yur}pp|||z}}~~xws}xprxu}wrss|v|~xpompswupmqwlw|zwuz}}y|~v~{pr|~zsjgox~rr}}x}~|{{|wr{mlyz|{|{{xp~oirz{|vogioxzvxlx}xsuv~x}z~wvtwz~~ywxu~~spy{{wpienwtm}z}yusvx{o}vrz}zttrwx|ytpvtly~x~su{x{{qr~wsxyqxy|xvuy{~ytmq|qzzyssv{~~|rpdl}zummpu|}{zv~zuzxv{}ywvx~zurqw~y}ovphpuyytoimpw|zxxtky|{zv}uuzxqmkt{u}~vvjlvz~{xrqw|~wz~w~}}}zx{~unzmoyzvstwy|wvw~zwx}vr~qlu}~yqrty||q~~{z}~wjw|uy{zrlrvnz}xyz|qs|~zullry}{ts~z||xsv|~~zruwwvwwqoo~|py|}w~vjfpv|~zsvw~}{~xq{pzy}tt{}{utmkn}zw|{}vtox}xqsv}}lrxvuz}znxzt{~|vqsty{~ztryzs}{y|{nowy}zvopsx}{{lq{{~~|zvspw|{qlowlu~~{vy{|z~y|onx{}ysqtx~}yv}}no}us}sxxxx{~xw{|zzzwryz|ytlhkmsuzxww~{lk|}rqwx{{}yvoqsy~yvxsx|~zv~}vty}~{{khy~yz|rn{opuw{ywpppswzzsprsy~~|}}|yqwz~|wrtv{~{zwqvzvzz|yvomos{{xxwt~~wvx||}}wuwpv|~zunpsx|zy{uit{p||v{~~ysttz~{{z}zy{}|zy~}~ytllnqwy{utt~uhm~}ru~suwxzxuprrw|yx{u{}|{v}yuyzzx}pjyrj{|pnux}{yronotwytrswu}|}~z{wy~{voprv{|}|pyu~vxxyyuqlnov||{~qx}z|~|~|ttpvx{yupnsv|~|xx~}njzyrxv{|~xvvw{~zwy}|{|{|xy~|woopswyytsvvip~qu}suwyzwtnppty}}yzv{zy~~xtwx}~yx|}merukyunrsvvtpmopuwzvrs}{s}~~{}|zv|||xrqtv{}{yzsryttorrvutqqsv||z{yt|z{}zw|qrwy|yuopruy{|vvxxknrsxx{|~|wxxz|~~xx}z|z|}~x{|}}wqlnoux{ytuyqhtzq|zqssvwusqtuz|{wx}u~}yv{}|wuxy|}~xuu|viizqn~rorswvuqqqrtuvppuxv}~~~wy}~{wqstx|~~yz~yox}rzyopprsrqosu{~{|t||~~}}wuqvx{yvportxz{vtv~wjj|ttvtwx|{{xyyz}~}xx~z{}z}y{}~~ytnpquvxursy~qjwvn|vorrvwvssuvz|zx{|v~xx|~|yrstwyzzuuwrgoxmvzprruwvsprpsuwtqt}{rz~{{w{{~{wrrtw{}~zz}~qstv~qooqsrqorsw{~z|w{}u~vptvzzwrpssxy{vtv~zljzutwuwy}|zvvuvy{{wyz|~{}~~y{{uqsswxyutv~|njxsqtpqrutsoopsw{}z{yz{x{|~{vruuxyzvsu|ykfttptoqrvuuppopruvrtzvux}y|}{wrtuxz|zwzxp{wp~vnpptssomnotx}{|yvv~ttwxzwtnpqtvxxtv{tip~zr|vww{|{xtusuwzxw|v~~~wv|~zttuvyyzuvzymn}|qtsqqsvtsnonpsxyw{y|xwz{~~{vsuuxxzvsv}zmfswrxqsswvvqonmpqursyyt~}vw|{~}ytuuwz{{wzzot{qx|ponprqokmlptz{{{yt~ztvwzyvqorrvvxttw{nivwsztuvzzzvutsuwzwx~|w~}wx~}{uuuwxyyuw|xmp~yot~rpqrutsopoqtxyx|y{xxyz||ytqsruvwtru|{mfqyr|ssruvvsppnqrursx|szzwz||}zutvvyz|xx}sp}sptmmmppqnnoptw{y|{wvvtvwzwupqrsuvvrtyvjl{~tvxttvyyxtustvxyvzxw}}x~~}ytvvxxywuxtksunyzonmprrqprruw{yyx}~z}wyx{yvqpqqsstppt}vjhwutxqrrvvvrrrqsturu|wt}z}|{||~{xstuwxzyvzyouvmvznlknpqporrux{{{z~zvztvuxwtpoqrtuvrsxxljxuvytttxxxtuttuwwtxwu~~}y~~~}zuwvxyzwuztlttn{yonmqrrqprqtvzxzxzz{wxxzxuppqqsssoqw~rgk{|t{urqsuutqrprrutsytxzx{||~|zutvvxxyvx~}qp}xnq~qljkoopnppruy{z}}{v~~vvvxxuqoqqsturrw{njuuu{tttwwwttsrtuvtxwu~zv~~~zvxwyy{xw|tmw|qpwponqrropporuxw|}z|v|xxwxyvroqqrstqpu|zmfp}yv}utsvvvrrqoprtrv~yu}ytz|||}~|yuwwxyzxw|wousnzxnlknoomlmlorvv{}}uvzwwwywuprrstutrv|ujmz{t}wusuwwurspqrttu|uyztt}~yyzz{{{xz}qo{yptupnnqqqnomnpsuv}||zu}xxwxyvrpqqrrsppu}zngp|zwwusvwwtrqopqsrv~{t{zsv~{|||zvwwxyyyw{{pr~unu}qmkloonlmkmosux~xt~~wwvxxvrqsrtturtz{oiswv~vtsvvvssropqtrw{t{xsu}~}yzzz{{{x|{pq}wputpmnqqqnomnpsuv~||yu}yxvxxuroqpqqrppv}ynfn{{wxusuvvtrrpqqtsv~~uy|vu||{~}{wvwwxxyvy~snyxoo~umkjmnomnnnpsww}}}wx|xwvxvtpqrrsssqu|vllx|tx{urruuursqpqstsy{~z}{|yxv}zz{|~~}z|zzz}|xw{}zwz~{yuv|~{y{z|~~}~~}|{z|{|wrsuxy|~zvz~zy~}yyuw{{vvy}~}~}|~~}}}|xvzzuwz|}z{|xy~}vruuy{|{zzx|}}|{~~~}}vz~|zvyyz{zwsx|~x|}}}~z||}}~~}{xyz|{{~wz|wy}||z{zyyy}~}xw|y~uwzzxxzz}yx{vzwx~}xuz|~zwtrx{~~z{~wv|xyztz|xwy|}~|~}~~}}|~{zy}~}~~||xwvvyvsszswsw}yz}{x~~}}|zvy~|y~{y~yyu{~z{~~}{}|{x{|~|~zy|}{||wwz|}yxy}}z||}~~{w}yyyy{~z{{x}zwuz}zz{~~~}z{vx||~{{xrvu~~~|{{~{y{wyvxyx{ywu{}~{~~~}~~|}|}~~{z~yzuxwtwuvsrwzy|}}~~y{|~|{{~zvzy~~~}}}wy|}}~}|}{||xy~z{{~~}yz}}{~|x{x{~}{yuv}{z{zz}|x||||w{yyxz|{yx}yxx||}}z~|~}~zuvy{|{~}}}zuu}~~|~~|yyxwuvvzxyw|||~}}v{}~~|{}}}|~{~|y|xxw{wvxsrruwz~}wx~~xz|~}{|wvv|{~~|~y{~~~z{zw}y|yzz}}{zy}}|||zz~}y|}zwvyz{zyx}z|x}|zy}}~~{z|{{yz}}|z{}{{z~~z~|{vts|~~}|zx{|~~||x{x{zzvx{vxw|{~~yy~|~~x~~{|~{x~}}{v~zwwwxvvuyzz~~}y}~~~~{{z||ws|}{{~|~~}~z{~y|}{xyzxy|{{ywz~|vx}xyyvxy}z||z{{{{xy~~}z{~}{{{{|{}yx|}x|~{}y~~zxusv||{}{zy}|}}|{{}||{w}|yzx|uvxz}z~{{~~y|{x{~|~~}|y}{}~~|~{~{zx|xyxxww|~{{z{~~xy~zxxtxzz{{~{y~~}}|~{z{|yuyx|{{|vx~~{zz}~yutwzvy{{}y}}z{|y}~~~zz~yyy}{wz{|~z~||wzyxxtv||zw}~~}}~~{z}}{|yzyyyxxyyuw|}~|{|y}}}zz{~|{~}|x|~~}}~z{}}vyyvyw}yy~{x{}z|~wzuwwz}y}~~|y|}~~}{~}yxxywwy{~{zxx{|yy{}~~zxx{ywyz{z{}}|}{x{}|~~{|}}|x{}}{}}}wzzy~vsuuuuy|~|}z~~}~z}zz|{{wzzvxxyux~{zy|y|zz}}}{|~}zz|~{}}wzwyyy{}~~}yuv}~}y~zwyvyuxy||y~{}}zz~}yx{zvuwuy{yyxz~~yw}z{wvyyxxz|{z}}}~~z}{}~~~}}}z||z|{~}v|~z{{}wtsxyyz{z~}{|~{}~{x~{}wxzwvsz{{|~{wxzz|~}zyy}z{|~}}}|y}~}zz}~|~||zx{|{{~|~zy}|yz~}|wzwxyxxz~~}{}~~yz||{|vvwv}z{ws{~}{|zywt{yzzw{zzy{~zw{~||~{|}yyz||z|z}~vx{~zyzytsuvvx~}}~~|z}|y}|}{yyyzxxx{|~}wv|~~z~~w{z}{|||~~~~|z{|}{{}}|||~}{}}zyx|}}}zw{|~yw}|vsrvx|{~}~~{}|~|~{|{zzy|{stxzxy{}|}|x}|x{uzzvuxy{{}|xx}~}}}|zy|yxz{~|yz~{zxx}~|zvt{yy|}}~}}~}|~z{~}||}{wy~wysv|wy{~{u|~~|wy}{z~{|{xy~}}||}}~}~|}||zu|~y{|vx}{xy{|{zvwy|zz}~z{~~}|}yww{{utuwxzzz|yxx|}~||xyzz}{|{||{|}{zx~|z}~}|{}~|{xw{~zy}|{}|xz|{yzxtwv{{|~~}{~{{{|x|yyvtxyz{{{}zx}~~}|wyz|~|~{|~}}{yz{~|~~y||~}}~xz}{z{|vx~z~{y|~|ytt{~|y|}}||~{}|{z}{yx{yuuwwwyy~zxt|~~||wwz||xx{}{}yx~}~~~~}zzz{~|zy~}~~v{~~zxxz|zz|vwsu{zxz~}~}|}}x|}w{yutuwy{~}~zwz}{~{wv{{y|y~|~{|~||~~|~|}|~~~~~~yz}}|}~}yx{~~{|~}z}}zuuwyzyyy~~~}{}||{{yz{{wvvxt|||ytsy~}~xuw}{{yy{~~{}{}}}|~}~|~{~~}z|{z|~}w|z~}zy{y~~xystxyzxyv}|~{{y~yzy{xwvx{z~}|xx{~}zt|zzzx|}~~{}}yx{}}|~{~|~~{{~}}|y|}~~yy{}{yz|}}zwwyy{ux~|~}z~|}}~}{xxwzwtwuw~{|}zxuwz~|zx{{z{||}z}~~|~~||~}}}{~}}}{}}{y{{}~}wz}~}~z{ysuvxzw{~yvw{~~|zxwuywxyttwvsvtvwv}~~y}|wuuvuuwuxzvx{xwzy|~|z||z}v}|wuvyy||}{uy~|}~|~~}|~{|z~wtvy|z}~{}yuxywz}|~~~{}~~{{}~}}|zwx{{ty}~}z~|{}|~|x{~wsuuw}{~|y}~}zw{}{zvvyvx||~uwy||x}}vy~zwz|}}}}{yuxzx|}zv{}}{{|vz~}}|}}|~zuxyzyz}|{~}uww{{z~|~~}zww}}xvwuw{yy|y||ux}}~v{~~}|y|}~~}{{{z|tx|~}yz}|~~~xw}~|}{|}{~}~zzwz~xyzz}|~ywxxx|~|~{{x}}tw}{~zz{~{|~}w||w{~~~~{yw~yy}}{|z}~~}~vw}}x{~~{}~xy|{|syzty~{w{xyz}{z}{}|yw}xy~|y~{x{{~|}|yy{z}}~|}wx|}~~{}}~z|{}{}}zx~}zx}~|z|{~}z}z{~vx}yz}zyvx{~}|{z{vx~z~|y~{~}y|}y|{v|yw|~|zz}~z{{|z{|}|~~|wy}|}w|{||x{}}ytzxv{~zy{xzz}z~~}|}zyx}~yzz{~yz{{~||w{~z|~}~~}xyz|z}~z~{{}|{x~~|y}y}}}}}|~{~~vy}zz~}zzxxz~}|{{|ww~}z}}~{}|~y}~z{~}vz{w{}~}|y~}y{~z|z}}~}~~~zu{{~yw|~~|z~}vx{wx~~w|zzz|}{~}|}|{xz}y|}y}}x{{}}~~|{w|}y}~~~~}~x{z{z}}|||}~|zw}}|y~z~}~~~}~{xy}{z~}zzzyz}}|z{~}yv}}{{~}}|}~{}~|{}~wy{wy|~}z{}z~~{~~z}}|}|}|vw|z~|w}|~{}}|w{zw|~{x~{zz~|~}}}~|{x}|y|}z{y{{}~|~yx}{y~}}}w|}z|{~~}~}~~~~~}{yv}}|{~}}~~~~|y{~zz|y{{yy}}{z|~yw~}z}}|}}|~~{{|~}xz{w{}~}y}}z}{~{}~}~~~~{{}}ztxz{~{y~|~~{z}yx~x{|z||}}~~|zy~}{}}{}~z{{~~}|{|x{}y|~~~}yz~~{|{~}~}~~~}}}}}{xt{}~}|}|z{{z|z{}zy|~|z|~yw~}{}}|}~}~~{{{}}x{{w|}}y~}z}~}~|~~~~~~|}z{~|wtzy}~}z}~~~{}}x}~|x{z~~~~}}}{w||y|}{|{|}}}z}yy}zy~}~{x~~|~~}}~~~||~}}}|}{xty}}}|||{|{{|z|~zy|~|{|~xx~}~{}{}~~{{{~}y|{x~~}|y~~|}~~~}~~}}}||{z{{tuzy}~~{z}~~~|}z{z~{{~~}}}zw~~}~{|}|||}~}|z~}xz}xz~}~~~yz~}~|}|~~~~|~|}|}||}{wtz{}|||}|}|}}{|{y|~~|{}~xx}|~|~{|~|{{|~y||x}~|}x}~{}}}}~}~}~|~{|~|z{}|wsyy{}}|z}~~{~~y}}{}~~}|xz}z}}~|~{}~~}zz|x||x~}~~~x|~~}~|{~~~}~}|{|{~~}|{}zvtz{||~}}}}}}~|||y{~~||}yx}|~~}|{~|{z{z|~y}{}y{{~~~~}~||{zz}}}|z{{~ytvyy||~{}~~~~||y|{~~~~zw|~~z|}~}~|~~}|y|zy~{z~~}~}x}}~}}{~}~}~z{{z~}~~}|{{}yvtzz}}}}}~{}{y{}}|}~~yy}|~~~}{{~|zy{{~{|}y|{}~~~~|~||~~z}yy|||~{zyz{}xtxyz|~~|~~z|||~~}yx~~~}~~|}}}}~~|zy}y|z~}~}z{~}~}{|}~|~~{y{z|||}|{{z}zvtyz|~~~}~~~|||zz~}}|}~zy}|~~~~~{z}|zy{{|||y}|}}~~~{}}~~}|{{xx|{||zyzz|{vuyx{|~}~}z~|}~~{x|~~~{||}}}}|x}{z||~}}|z}|~~}{}~}|}~~|x~|y{|{~||{|z}{wtxz{}~~}~}}}zz~|~~}|}~zy}|~}~~{z}{zy||}~|~|y~~}}}}{|~}~~z|{xx{y{{yyzz~zuvyx{{~}~~~}{~|zx}}~|||~~~|{y~~z{{}}}}{{~|~~~{{~}}{~~~~~{x|y{{{}|{{{z~{vtxy{|~}~~}~~{{~|~~}|~~zz}|}}~{z||zy{|}~{}y}~|}|~~~}~{~{wx{ytu{z{}||~}|z||~|z{w||~~}}~~|{}{w||~}~{zy}~z}}~{|}}}|~|}{{}~xx~~|}~}z}}~~|}z{~}wx~~~~}|{}}}~{zxxvyx{{{~zxz|zux}|~~~}~}|}~~{}{{}{w{~~~~~}}~|{yxxzx~{x{{wxz|wuyzx~~~~|~||{~x{~}~~~{}~{z~~~~|}~~}~~|~~|zx|zyz~}yy|yw|}~~}}}zxy|{y~||uy{}~}{}}|~~{z}~{}}}}|~~}~yy}z{||z|~yy{|~}}|yxxz}xz}~}vvz|||{~z{~}|z|~|{|~~}{z~}~}{x|}{|~~zz}}y{z}~}~}zzy{~|x{}{{uy}}|z{y~}|}~}}|~}~|~~}y~}~{|~|~yz~}{|}yxz}{|z{}~~z|{{~yz||}~wy}|yz}~{||}}||~{{}{{~|{z~~|z}zxx{{|{y}{||{~{z{~}|yy}~y~}{~{|{|~}~{}~~z~{{}|~|y~|z|zxwz{||x~~~|}~{~}zz}~|zy}z|}{~|~{{{~~{~~z~||~|~}z~|y|zxwy{{|x~~~}~~{~}zz|~|{z}{|}{~|~{{{}~{~~z~}}}|z|y|zywyz|{x}~|~~{}zy~}}zz}z}|}|~~{{{}~|~~~{}}~~|{{z}yywzz|zx}~|}{|zy|~zz~z~{|}~{{{||~~~{}~~}{|z}yywz{|yx}|~}{|zy|~zz~z}{~|}~{{{~|~~|}}~{}~~~xz~~|}|}||}z|yxzxyzy|~~~|~}{|}zx|{{y}~zxuwyyyz{~~|{~zz~}{zu|{~~~}z|}}}|{|~{x|}x}{~}}yz}|}~~zwu{~~~|{~||{}}{xxv|{}~~w~x{{|}~|zz~y~{{|}z~|z}~~~vwzz{~~{{{xzywwxy~}}yyzu|~zzx||}{}~~y~~|z|~~|~}}~}|{xy|}{z}{{{}~zzzvsx{~xzxt{|{zz~}{yx|~}}||~||~~z}}{{{~}{z|||~{wxzxxy{z|~yzyxzvx{x}}ww}~~}}}~}y~}|}~}yw|{{{}}|{}||wvwy~z{}yy}~~{{w}z|}~~~|||{}z}~zw~~}z|{|}z}~}}{}}}~~}{}||{yzz~z|zzwyxz{||}xxx|~{|~}~z|{{}~~z||~}}}z{|~zvxxx{z}|zw|{zzxv||{{ww{x}||~||~}|y~}~}z|~}{~zvv}{}~xxuwy~zyxz}|{w|}|z{}}{}|~~z{{~~~~}y~||{}}||~|{}}yv{||}{|zwxy||~|{y|yz}}{}}~~}}z}~~}zx~zz}{}}z|}y}yz|zyyyyzx{~}y{|y||~}w{}}}|~~y{~|~|y~~z~{~~|}}~z}{|}|||xuw{}xx{zz}~x~}~|zz|~~~||}~~~{y~}~~}|{{}{~}x|}y~~|||yz|wv{{~~}|~|z~~~}{}{~}{|~}~|{~~}|z~|y}|{y}{|~z~|~|{~xxx{{{}~|~|{|~~}{}~|x}~yy}~|{~{|}~~~~}{{||xy}}|uz}x~}{~yw{~}|{~~}|~z~{{y~}|w~{z}~|~~~|{yzyv|xx}wwx}|yvz{||~}{|{~~zy~z~~xzx}~{{}~~~{|xwzy{{x~zwu}|~}|xsvvy|{{~}|~|{|w{~z{|{{|{{}}z||{~zsxzx~y}{y{~{|wutvv{~~y{~{}{{z~}xw}{y~}{~{{|~~}{suzxz}x}yw~~yyuvux|zx|}~}z~}||wz~|}}z{z|~}|}yux{y}{y~ywx|~{wvxvz~~xz|||z|}~{w}|~}z|}{|~|}~~~||yu{{{y{}zy|~}|yuxxy|~~|z|~}{z{~~|{wy|}{|{~|~~|~{ux|{}x}yx~}}zvvyw{|}yy}}}{y~~~|}~{yv}}}}}~||}~~~~{~{wu||}x|}yx}~|{xvzwz||~zz}~}|z}~{}|{w||}{~|}|~~~}}|xt{{|z{|yz~}}zvywx}~~}zz~}|z|~~{|~{wx~}~{~|~|}x|}{}{{|~}}}{~}}}~x{zx{|~zz|yy|~}}|xz||}~{x{|}|yy|z}|z~~}|}}{~{~~~~}~~y|{z}}{{}z{z|{z~~~{{}z|~~~}zzz~x{}yy{|~}{y}y~~{}|}|}wyyzyy|z{z~||}~||yw||~{v||yy{|~|zy{x~~}~~|{zzzz}}}|y{{{}|}}x{|z{y{z{}}~~|~{~{yzz{|||}wu}~~{}{~{}}y|~{yz~zw|~}{~|x{}~||w~~}~~~~~}}xrt}~~~~xy|x|{|}|xvv}}|x|xxu|~|z{v~}~|yty|xxz{{~{~zzvy}}|}{}z}|{}~zxy{~zy{vvyy{}}yzwy{}|{|{yz}}z{zu|}z}z~}}~}|}xytsw|y}zyyt{{}zzyw}{~{~~~|~~zz|zzwy~}}y~|{}}}~}~~{~~|zx}ytuux{y|vzzy}{z|}y~|z|}|~{wy~~{zww~|~}y{|~~|x{wu}}}zzx~~~y~}|{~{||zzzvw{|~~}yyx||~{}}}|}~}~~~~~yy||zyx}~}~}}y||~y~~}}zy|{|~{}}}|||}yz~~~zyz}|~}||z}}~}}|yz~~~y{{~}y||xuv~|~~~|~}{y{{xz{{|{{||~{}~}~}|yy{y{|}{y{}~{}wz|{{~~|z||~}~{~~|~}zx{~{x~{}~|w}~~y|~}~}~yz}{yy|x|xy{{{zxyz~|}|}{|~z|}z{}xz}}~{~~}}|||||z{{zzy|~}z|}~}~}{uw{~}{~|~|xvvwz||~~}z|}~}|}||~}~~yz}{|zv}~xxw}~zyvxvy}{}~~~}z|{}~}|~}{|||yy|~~}|}}~yy|~}}}yz|yy}|y{{z}~{y}~|~}{|}}{}~|yz|~|{}|~{}}}~{{yxyzyzy}~}}~~x|}~z~zuyy}yz|}}}~~z~||{y}|{}~~~zy{|~}~~~}|}|~{zy|~{x~~|}}xvvwv{|}{~~{|~|yy~~|||~}{}{}zx}}z{~{{xyywy~~z~}|zx~y||}z~~y{{|~z{}zyzz}~~}|{|}~~}zw}}z{}}{~~~}}}zywx{|{}~|{~}~z|~~}~|{z|}{~|xy{}}}~~~~{{~|zxz{{~}~~}z{xv~}{|zw|~zzz{}zxxz{{~|}}~z||~~~}|{~{|zwxxx|~z|}}y}}y{}{}~{wx}yz}|wy}~~}{{}}}~}~~z~}~}|~}|zz|~|{{}~~yzyxz||}~}}~~}~~}z}~{~~|~{{~}{{}}|}|}~~~~|{{z}zyz}~|~~||{{x{}}}}}}~yy}~}~}{yz{}~{|~{y~~z{}}|zz|~~~||y|}xz}~{wy~~}|{z|yz}{~}~}yz}~~}{{}}}}~{~}~}||{}{}}{z~{xwz}|||}~~~|{}~{~~~{}z{~}}}}~}|z~~~}~~~|{}{y}}|~|}}}~~~~|yw~}~}}{}~~z{~~~}||~}|~}~}}{|z}~}||~|||}~{yz~~~~}}}}}~~}}|z{~}}~}||z{~}z}{|{}~~|y}}||~}y|~~}}~z~~zy{{}~~|z{}{}~~~|}|}|}}}~~}~|}~}|{|~~|}}}}~}z}|}{}}yw|~}z||}~~~}zzz|~|zz}||}~yz~||~}~}z|~}~}|~}}~}|}|||}}~~{y~|~zzx{}||x{}||}{{|}}{z}~{~}~~z|~}~~~z|{||{}~||}|~}||{}|~{}||}~~zyy|~z{|}{}~}~}}|y|}|}{y~~{||}}~~~}|zy}~~}|{}}|}~|{~~~~~~}|}~}{{~}}}||}~}}|||}~|{}~~~|{y|~~}|{}|z}~~~~{|~}|~}}}}{}y{||zyz}|zy{~~~}{}}~~{{~~{x|~|{~}||{|}~~|||}}|}~}}zz~~zz{}~~}~|}}~~}{{~~|{~}}~}}}{|{|~~{{~~z~~||~~}~|}~~}~|}~~~~}~~~~{z~}~}|}~~}y{~~|}~~}|{~||~}|}~}}}|{}{{|}}~~}~{|~z}}{|}}~|}|}~{~|||z{~~~||~}}~}{}~{y{}|||}~}zw{~}|{z{~}~}|~~|z}}}~~~~}~||~}z|~~~~~~~{|}|~~~~}|{z}{||}~~~}{|||~}~}~~|{}~~~~|{~~~|~~~}~}}}}|}}zz}}|}||~}}||}}~~{{yz}~|zz|~|y{}~}{~~|}~|}~|}|{}||}}~zy|}yyz{}~~~}}~~{|}}~}~~}|}~|y{~~|{}~~|z{~{|~|{{~~}zy{|~~|}~~~~~~~~}z|~}|~~~}{}~~~|~~|z{{||{}}|yy{|{{|}~{|}{~|~|||{}}{|~}}||~~~}~~~}|{|~~~}}}ww~~|}|{yz}~~|||~}|~|{|~{x{~}}}~~}~}|~~|{}~~~|~~||~}}~}}}|~}}}}||z|{~||{{}}wz}~|}zz{z{|~}zzz||||z{}~}|~~}y{}z}~}|{z|}~|}{~~~~|~~~wux|~y{}{|{}{}|}|~~{yxz~~{}}{}}~}|~}x}{~}~~~|~~}z{zxy|~~~zzx{{~}}}zy|y~}}~{|{{}|~~|{}|~zxv{|w||zw{{}{|wxz|}{{z{z|~|zw{~{}|zy~}~yxy}}{zyz}z{}~{{~z{||{y|}xy}{yz{}~{yyz{}|{~}{}{}~~~{x{~|z|}}~|yty~~zz{~{z{}~{~~}||}{yzy~{~~z~~}~||~}{zxvy{y{z}{{z}~|||{}}}~}~}~~}xyz|{{{~}~~}|~}{z{{|xsuvz}~~||}~~||zzzy{~}y||yy~z|~}}}}}}}{}{~}z}~}|{|zwxv{}~y}}z}|wxw||}|}||{}~~|{~z}}}zwvvz~}~|~~~}|}zuxzyx~}}~}|x{|}{}{z||{}{zz~~zxzz|{ywz~~~~~}}||~|~~wy{}}~~}}z}yz|z|~|{wy{~}{xz|z}}{~~||}~}}{|~y{}xvutwz}{|x|z}{~}{~~|}~}||xy}|~||~}z}}~}{vusuzy{|}}~~~|{~{||}~|zw|}z}|}yyy{|}{{}}~}}~{zwux{xvvv}~~|~{zy{w}{vy~xxvw}{~}z}zx|z~{}wz|{}~}|~}|y}z~~~|}y{|y~|zxyx|~|yy~~zxww{z~~z{yw{}}{yyy{|{~{{}|~y|y~}}zz~~x}zx{x|~z{~zzwy{y|}{~vy}{}y{xy}{}}||}|{~~{~}z}}}}y{{~|y}{{{}z}~x|~}xwwzz}~~|zwz}~~}{yyy|z}~~|||}~~y~}y}~}||{y|~{x|yz{x~{y|~xzw{||}}zzuy||}{xz|{}~{}}|~~z}|}~~|||xy|}|x|zx{{}}yz}zyx{z{~~}|}vx|}}|zyy{{z}~{{|{|~~{}{|}|}|yw{~~z~}z{z|~yzzzyz{}|~~y}vw|}|}}|yx{|z~~{z}}}}{|~zz{}{{{|yz|~||~|{z||{x~{yy{{|~~}y{ywz}~{yxz}{}~~~|}||~|z~||z|z{zyyx{~||}{y|{}}y|zz{|z}~{~|}wx}}}{xyz|z}~|{~z}}}y|}yzz|z{yyyz}}}~}zz||~~|y~}zy{}|~}}{}ywy}|||ywz}|{z||z~}~}~y}zyz{zy|{zxz|~|y{}|~zy{y{~|}~~{~~y}yxy~|}~{yvy{|~~z~~{z~}~{x~{zy{y{{zxz|~~~y}}|z}|y}}{|z{z{wyz}z~}zww|~|~~~{z}{}}|}~x}}zxzyx{xzyz~{{~~{z}zy~{}~{|x}yxx}}}~{zyz}}}}{~{z}{}~zy~zyxzwzyyyy}~~z~~|y}~|y|}{|zz{{wy|~{~|{yy}|{{{z~{~~|w}zyvzxxywyy{{}~~~yz}z{{{z|y|yyz~{~|{yx|{~~}yy{}~}xz{tz{|zyywz{|~}~}~~|zz~{{~~xywwy{~}{{x}|{~~~~zz~xyy|}|z}}}}ruy|}~}xz|||}{yz}xwy{}|wtywvvvzzxv{||z|}||~|{z{zzvx~uvxxz|{yz||}|{|zxz}~||}{}ywy{w{y|z~~}z}z~|ttv|||{xuw{}~||~}xw}z~~{zv|ww{}{xvyv|~{z|~|{}~wvxvxz}{}zyuw|~|~}~|xux}{|~}}~{yx~zzz{~}zz|z}x|~{yx}z{~{~z{|vz{vxz}yx{zw|~}}~y{{w{|||yzzyx~}{xx{~{|z{}{yxy~|~}~|{~zu{zvx{vuuw~~~{y{~xv}{{z~}zsxz}~~{~}~}{~zw{yzz~|y{u{xywyyzzyyz}z{zyw}}{|z~|~{wz}yzx{|~{w|y{}~|x~}|xz{zyytyywx|~xvtz}~|yyy~z}z{}y}~zvz{vx{x|~|zxz}{}~~z{vw{zuuyyyy|}|y{}}zxw|}{|w~|vyy}{zx{|~}}~{yw~|zy~|z}}|yyyvxxzxyxz{y|x~~}z|~zz}{vxwwxy{}wxw~~yy}z}~}yz~~~z~zuzzvw|zvx~zy{y~~y||zvzv{|}|wyx|}|y{}yyyzw}}~zyzwyyuxxzywy}~~|}~}y}{zwyz~}}z|{|}wxwz|{wz}|}~~zy~{|zv}xzvrsvwyyy}y|~~~|}~{}z}~~||yx~|vy}~{yz}}{~~{yyz{|{yutxz}}{zxyx{|{z{{}~}~}~~}zxyx{~|~{{|}{~~|y|~|y{wxyuw{zzy~{{~y~~~}|{z}}}x}|vwww~~}||}|~}|z||{~}~~}|~~}z}}~{vy}}~~|||z}||~~}}~zzz{|}~~}~~~}~|}y|~~|yzzwwwtuyz|xwvz|{{zwyxyzz||||}{}}}~|z{}|{{~~~}~}~}|{~~}{zz|{}{}~~~~}||{|~~~zyz{|{{z{|}yzwwyzz{{zyy~~yz||{}~{|~~{{{{|~~zww|{|{}}~~~~{y}|{}{~xx~}~|}{zxwx{}~|{vxzxxyyxy{zy|zxz||}}~{||}y|{|}}~~}}~|{}~~}{{||{|}~{~}}}}~{x}~~}|{{|~}}y{{{zyy{z{|zzyy{{~{w{|{}{y~~}{~||}~~zxz}z{|~~|{|{}}|~~|{|~zv|}}~|zzzyy}~{yw{|{z|zz{{{{}zz|~||~}~{{}|}|~}z|~}}|}~{}~~}}{~}z{{}{||y}~|}~{}|z~~~|}|~|{y|}{|}zzy{}{}z{~}|z{{~~zx}|{z~{|||~~|wz}|{}~}~~|}}~}}{|~}~|~}~}~{z|~~y{~|}{|{|{zyx{||}{zzz}|{{z}~}|}~~{}}}|~}|}~~zy|}~||}{|}|~|}|}|{|~}~{|~}~~}z|||~}|{|~||y|}}~{{zzz{{z}}~~{y|}}~~z{|{{||z||~~~}}zwz~~}~~}~~}~}|}}}}~~}||yz~{{~}}}}{z{|~}|{x{{|~|{{{{||||{z~}}{~}}}~}~~~zw{~}}}~~}}{~~||{||}|}}~~}z|~|}~~}|~~~~|{~~|{{{||}}}~}y{|}~|{}~{{z~|{|}~}{vx|}}~~~~~}~~}}}}|{~~}|}zz}{y~~~}|}}~~~|{|}~}|{|||}}}{~|z~}~}}|~~}}}}}xx{}~~~~~}||}|{{|||z}~}~~~~~||~|z~}~}||||}}}|~~{y~}|~|{|~|{{|}{||}~~~|{xwz~~||}~}|}}}}}|~~~~~}}{z{}|xy~}}~~{{}~~}||{|}~~}}~|y|}~~|~~|||~~}~~}zwy~~}~~~}||~|{{|}||~}~~~~{z}~{y}}~~}}|{}}}|~~}zy~|~}||}|zz|~|||}~~~|{xwz~}|}~~}~||}}~~~~}~~}}|yz||{y}~~~~|}~~}}}}}}}}}~~{{}~}~~{|~}|}~}|zy|~}}~~}|}~|{{|||}~~|~~~~~~~~}{{}~|z~~~~~}~~}}~{z}|~~||}~|zz{}{{|}~~~~}{{ywy}~|~~~~}~~}}}}~~}||}~~||{y{}|z{~~~}~~~~}}}}}}}{}~~}}~|{|}||}~~~}|{yy}~~~~}|~~}||}}}~}~|~~}}~~}|{|~}|~~~}}}}~}{{~|~}||}}|z{}}{zz||}}}}|{zxy|~~~~~~~~~~}}||||~~~~~||{z{}}{{}~~~~}~~~~}{|~}}~}{}~}{|}}~~~~}{{zz}~~~~}|~~}|}}}~~|~~~~}|||}~}||{}~|}~~}~~~|{~}}}||~}|{{|}{{{|}}|||{zyy{}~~~~~~~~~~~}|{{{|}~~~~}||{z{}}|z~~~~~~~~~|}~}}~~}|}~}||}}~~~}|{zz{~~~~}}~~}}}}~~~}|~~~~~~}|||}~~~~}}|{|~~}{~}{|}~}||~~}{{{~}|{{{|}|||{zyyz}}}}~~~~}~~~~}{{{|}~~~~~}|{zz{}|z{~||}}~~}||~~||||}}}}|{zzz}}~}}}}~}}}~~}}~~~~~~|{{{}~~~}||{|~~|{~~|}}~}{|}~||{|}~|{{{||||{{zyyz}}}}}~}~~~~~~~}{zz{|}}~}}}|{zz{}|zz~~|~~|}~}}|}~|||}}}}}|{zz{~~}~}}}~~~~}~||~~~~~}~~}|{z{|}~~~~}}|{{|~~|{~~|}~~||}~}|{{|}~}{{{{||{{{zyyz}~}}~~~}~~~~~}~~~}|zzz{||}}}}|{zzz|}|z|~}~}}~}||}~}||||}|||{{z{}~~~~~}}~~~}||}~~||~}|}~~~~~~~}}||{|}~}zwx|}||}}~~~~}}||~~{}~z}~}}}||{}~}}~~~~}||{{}}}~~~~~~~~}}}|xvx}~{yxxxxxxy{}|{{}{|}||}~~}|{{}}}}}~~~~~~}}~|zyyz|~~}}|}}~}}}|}~}~~~~}}||||zyxyz|~~~}|{{|}~|}~|zz||zyzz{{zzyxxxxz|~~}{zzzz{{{{{zz{|~}}~~~}||{zxwwxz}~|zyyyzzxwz~~}|}~}||~}~~~}}|}~~~~~~}|}~~}|||}~~}{|~}{z|~}}~~~~~~}}|||||{zyxxy{}~}||}~~}|~~}{zz}~|||||{{zyxxxy{}~~}|{zzzz{{{{zzz{}}~~|}~}}|{zxvvwy|~}|{{||zxy|}||~~|{|~~~~~}}}~~~}~~~~~}}}}~}|{|~}zz}~}}}~~~~~~}}||||||zyxxy{|~~}}}~~||~}{zy|~~}}}|{zyyxyz{}~~~|{zzzz{{{{zzz|~}}~||~~~}||{zywvwxz}~}}}}|yy|}|}~}{{~~~~~~}}}}~}|~~~~~~~~}{{|~|zz}~}}}}~~~~}}|||||||zyxxy{|~~}}}}|~}{zz}~~}}}|{zyyyz|}~~}|{zzz{{{{{{{|~~}~|{}~~}||{{{zyxwwy{}~}~~~{y|~}||}~~~}|{|}|~||~~~}|||~~~}{{|~~~~~{|}~~}}~}z{~~}}}~~~~}}}~}|{zyyz{}~~~~~~}}}~~}|{}~{yy|~zwvwy{|}|{zzyyzzzzzzz{|~~~~||~~~~~~~}}}}}}}|||}~}{{z{|~{{{|||||||}~~}}~~~}|}~|{{|}~~~~|{{}~}{ywxyzxvwz||{zyzz{{{{{zzzz{{{{zzyz{|}~~~}|{{|}~~~}{zyz{|~~~~}{zxwwwwwwwwwxy{}~~|{|~}{z{}~~||}~~~~~~~}}~~|{z{|||}~}}}}~~~}|||}~~~~~~~~~~|||~|{{{|}~~~~|||~}zyy{zxvwz||zyyyzzz{zzzzz{{||{zzyyz{|}~~~}|{|}~}}}~|{zz{|}~}{yxxxxwwwwxxz|~~|{{}~|zz{}}}}~~~~}}}}~~}||||{z}~~~~}||||}~~~~~~~}|{}~||{{||}~~~~~~~~~}}}~|{||{xwy{||zyyyzzzzzzzzz{|||{zzyyz{|}}}}}|||}~}{|~~|{{z{{|}}{zyyyxxxxxyz|~~|{{}~|zz{}~~}}}}~~~~}|}}~~}|}}}{z|~}|||}~~~~~}|{|}|{{{||}~~~~~~~~~~~~}|}~}|}}{yxz|}|{zzzzzzzzzzz{{||||{zzzz{|}}}}|||}~~}||~}|{zz{|}~~}{zzyyyyyyz{}~~}{{}~}{z{|~~}||}~}~~}}}~~~~~~~~~~}}}|{{~~~}|{yxxxxyz{||}}}}~~}}{zz{~}|}~~~~}{z}}{~~~~}||{{|~~~~|yy{}}|{zzzz{{||||}}~~}}|}}}{{~}~}}~~{yxxy|}|{{zzzyzz{}~}}}|}}zyyz~}|{|~~}{{}~~~~~~~~~~~~~~~}|{z{|}~~~}||}}~|ywvwz}~|{{zzzzzz{{|~~}~~|{}~~|zyxyz}~}{{~~~~~~~}{zyyz|}~~~~~~~~}||||}~~~}||~~~}{yxxy{}~}}||||||}~~~~~{yxwxz}~||}~}zy{}~}||||}}}}}}~~~~}}}}}}}}|{zz{|}}}~}}}~~|ywwwy{~~|{{zzzzzz{|}}|}~|{|~~|zyyyz|~|}~~~~~}|zzz{{{|}~~}||{{|}}~~~~~~}|{|~~}~}{yxxyz|~~~}}}|}}~~}{yxxyz}~~~|zz|~~}|||||||||}}~~~~}|||}}}}||{{{|||{|~~~~~~{ywwwxz|~}|{{zzzzz{}~~||}~|{{|~~}{zyyz{}~}~~}||{{zzyxwwwxxyyy{~}zy|~}~~}}~}zyz{{yxvvvwxy{}}{~~~|{zyz{}|yy}~}||}}}~~~~~~~}||||||{{{{{{{|{zz|}|{{||}~~|zxwvwxz|~~}|{{{{{}~~|}}|}~~|{{{|~}zz{}~}}|||}}}~~~~~}}|{zzzzzzyxy}~{xxxyz{|}~~~}||}~~|zyxxyy{|}~~}~}}~}|zxxwxy{}}~}yz|~~~~~~~~~~~~~~~}}}}}}|||{{|||{z{~~}~}~~~}|zyyyz|~|{}~zxwwxz|}~}{{|~~}{z{|~~}|zyxxyz|~~~~~~}}}}~~}~~{zxxxxyzz{{||||||{zyz{}~|{{|||{yyxyz{|}~}||}~}{{}~|{zyyyyyyzz{|||||{{zzzzzyyyyyz{|||||~~}}}~~~~}}}}~}|zyz{|{yxxz{|}~~~~}}|{zyxxxyz|~~~~~~~~~~|{{z{{{{{{{||{{zyxy{}~}{zz{||{yyyyz{|}~}}||}~||}}|{zzzzzzz{{||||{{zzzzyyyyyyz{||||{|}}}}~~~}{zz{|{yxxy{|}}~~}}}|{zyxxxyz|}~~~~}||||||||||{{zyxxy{}~|zzz|||{zyyyzz{|}~~}}|}~~}|~~}|{{{{{{{|||||{{zzzzzzzzzz{||||zz|~~}}}}}~~~~~|{|}|{yxyz{|}}}}}}}|{zyxxxy{|}~~}~~~~}}}}}|||{zyxxz|~~|zz{|||{zyyyyz{|}~~~}}|}~~}}~~}|||||||}}|||{zzzzzzzzz{{|}}|{z{~~~~}}}}}~~~~~~~||}~}{yyz{|}}}~}}}||{zyxxyz{}~~~~~}~~~~~}}}}|{zyyyz|~~|zz{|}|{zzzzzz{|}~~~}}|}}~~|}~~~}}|||}}}}}}|{{zzzzzzzz{{|}}}|{{}~~~~}}}}}~~}}}~~}{yyz{|}}~~~}}|{{zyyyz{|~~~~~~~~~~}~~}}~~~~~~~}}}|{zyyy{}~~|zz{|}}|{{zz{{||}~~~~}}||}~}||}~}}}}}}}~}}}||{{zzzzz{{||}~~}|{|~~~~~}~~~~}}}}}}}~~~}}~~|zzz{|}~~~~~}}|{zyyyz{|~~~~~~~~~}}~}|}}~~~~}|{{zyz|~~|{{|}}}|{{{{{{||}~~~~}|||}~~}|}~~~~~~~~~~~}||{{{{{{{{||}~~}|{{}~}}~~}~~~~}}}||}}}}~~}{{{|}}~~~~~}}|{zzyzz{|}~~~~~~~~~~~||}~}||}}~~~}|{zz{}~|{{|}}}|{{{{{{||}~~~~}}|||}~~}||}~~}}|||{{{{||}}~~~|{{|~}}}~~~}}~~~~}}|||||}}~~~~}|||}~~~~~}}|{zzzz{|}~~~~}~~~~~~~~}||}}|||}}~~~}|{{{|~~|{|}~~}||{{{{{||}~~~~}|||||}~~}||||~~~}}|||||||}~~~~}{z{}}}}}~~~}}~~}~~}}||||||}}~~~~}||}~~~~}}|{zzzz{|}~~~}}~~~~~~~~|{{}~|{{||}~~~}|{|}~|{|}~~~}||{{{{||}~~~~~}||{||}~~~~}|{{|~~~}}}|||}}}~~~}|{{}~}|}}~~}}}~}}~~~}|||{{|||}~~}}}}~~~}}|{{z{{|}~~~~}}}~~~~~~~~~}{{{}}|{{{|}~~}||||~~|||~~~~}}||||||}}~~~~~~}||{{{|}}~~~~~~}}|{{|}~~}}}}}}}~~~}{{|~}||}}~~}}}~}}}~~}||{{{{{||}~~~}}}~~~}|{{{{{|}}~~}}}}~~~~~~~~~|{z{}~}{{{{|}~~}||}~||}~~~}||||||}}~~~~~~}}|{{{{||}~~~~~~}}|{{{|~~~~}}}~~~~|{{}~|||}~~~~}}}~}}~~}}||{{{{||}}~~~~~~}}|{{{{|}}~~~}}}}~~~~~~~~~|{z{}~|{zz{|}}~~}}}~~}}}~~~}}|||||}}~~~~~~}||{{{{|}}}~~~}}}|{{{{}~~~~~~~~~}{{|~~}|||}}~~}}}~}}}~~}||{{{{{||}~~~~~~~~}}|{{{||}~~~~}}|}}~~~~~~~~~~}{zz|~}|{zz{{|}~~~~~~}}~~}}}|||}}~~~~~~~~}||{{{{|}~~~~~}|||~~}|~~~~}}}~~~~~~~~~~~~~}~~~~~}||}~}|{|}|z{{wy{|}~~~~~}}{{{~~~~~~~}}}~~}|}}zy|~|{zzyxxxyyz{||}~~}||~~}}~~~~}{xwwy}|zyxwwvwwxz{|{{{|~~~~}~~~~}|||}}~~~}|{zyz{}}|{{zzzzzzzz{{||}~~}|{zzz{}~~}}}}~~|{zzz{|}~~|zyxxxxyzz{|}}~~}}}}}}}~}|zxwwwy{}~|{zyxxyz{{|{{{|~~~}}}}~~}}}}~~}{zzzzz{|~~}|||{{{{{{{||}~~~}|{{z{|}~~}}~~|{zzzz{|}~}{zyyyyzz{||}}~~~}}}}}}~~}|{yxwwxz|~~}|{zyyz{{||{{|}~~~~}}}~~~~~~~~}|{zzzzz{|~~~}}||||{|||}}~~}}|{{{|}~~~~~~}|{zzzz{|}~}|{{{{{{{||}}~~~~}}}}}}}~~}|{yxxxxy{}~}|{{{||||{{|}~~~~}}}}~~~~~~}|{{zzzzz{}~~~}}|||||}}~~~~}||{{|}~~~}|{zzzzz{|}~}||||||||}}}~~~~~~~}}}}}}~~}|{zyxxxyz|~~~}}}}}}||||}~~~~~}}}}}~~}|{{zzzzz{|}~~}}}}}}}~~~~}}|||||}~~}||{zzzzzz{|~~}}}}}}}}}}~~~~~}}}}}}}~~~~}|{zyyxxyz{|~~~~~~~}}}|}}~~~~}}}}~~~~|{zzzzzzz{|~~~~~~~~~~~}}||||}~~}}|{zzzzzz{|}~~~~}}}~~~~~~~~}}}}}}~~~~}|{{zyyxyyz{}~~~~~~~~}}~~~~~}}}}~~~~}|{zzyyzz{|}~~~~~~~~}}|||}}~~}}|{{zzzzz{|}}~~~~~~~~~~}}}}}~~~~}}||{zzyyyyz{|}~~~~~~~~}}}}~~~~~~}|{zzzyzz{{|}~~~}}}||}}~~~~}}|{{zzzzzz{|}~~~}}}}}}~~~~}}||{zzyyyyzz{}~~~~}}}}}~~~~~}|{{zzzzzz{|}~~~}}}}}}}}~~~}}|{{{zzzzz{{|}~~~~}}}}}~~~~~~}}||{{zzyyyyz{|}~~~~~}}}}~~~~~~}}|{{zzzzzz{{|}~~~~}}}}}}}~~~~}||{{{zzzzzz{||}~~~}}}}}~~~~~}}}||{{zzzyyzz{{|}~~~~~}}}~~~~~~~}}}||{zzzzzzz{|}~~~~~}}}}}}}~~~}}}||{{zzzzzz{{|}~~~}}}}}~~~~~~}}}||{{{zzzzzzz{|}~~~~~}}}~~~~~~~}}}}||{{zzzzzz{{|}~~~~~}}}}}}~~~~~}}}||{{{zzzzz{{|}}~~~~}}}}}~~~~~~}}}||{{{zzzzzzz{{|}~~~~~~}}}~~~~~~~}}}}||{{{zzzzz{{||}~~~~~}}}}}}}~~~~~}}}||{{{zzzzz{{{|}~~~~}}}}~~~~~~}}}|||{{{zzzzzz{{|}~~~~~~~}}~~~~~~~~{yrxwqsrttv}snn}t{mfiehmpvtvvywtsyvnnjs~{~pzut}mp~~sp~ylpyskowmlnmqiyul}|yo[ek\ktilu|wq}{nz~|~{~x|}~oitskq|rnlm{sy{{l}o{zop}smpuzp~x}}{vufrx{{r~dOUbzxv^flbeVacejpept{ugxw}yrpnxvnr|syrn|~wmovq{roqq|ux~wqrsivq|{rs}zqnux~wzx|~ww~kjb]e^hquvmq`hoZakgdvmroqw|vk~tnm|rlpgkhlmq{xz}p|ovxqltrx}suz{}tukky}{t|xlyzz~zv~{}~q|}|wnlnUegcs~wmshcbqgejhltqp|lw}qtwoynnkk^bkiw}|vyzmwqt|uxy}}z~w|r{uvvftvtzvvs||zv|{y|t{qrh_WZugsqzykqrmgjekpsr{rh{tkpvkmhgxk^hucovt}woyzdpop|x}{~|zup~nhvwcjxu}yy~}uvp}}|s~~~ww\ha^djmv{twwbk_jwh]nsdy{xjzks}wpvebfhfw}yx~}q~opq|xpy|u}zztltpopspv||~qv{y||{i}}||tyus~{wv{hksWYbk}rp|sqrsgoujp~v{~lsunlwe_bqk[Ybxk}||wzssqtww|xy{{|qflzvlsxwp~{}w{wzsowus~~|vwtg_bb_m{||s}uoktyicntlq|ohwzrswmvolkekv|}{vyxuvc{zjo|s|~|lqpgosbgsu~{xw{wxns|v}}zy~xuz|yvjm_^kY_Yw}u{ur}rilmvpwpv}xttoo|tmd^roVWolostwxn{}}z|zsuyuonirddzok|mv{{z|szyyuwq|mx~z|{rt{rxqg`ogfnsy{wsvmv_iwyxkwzpknbocflpmt{{}swowrdpzvz{{u|}wzvgnhjjwq~x{}~zz~t}~}~w~~z{x{yvhwfZeXaf^{ystu}ypoxmx|noukr|qkzpmjepfahly}ux{noxyyzxv|vvesxgfik`f}~q{u{z~sy||uyzwtx~x~vyn|ot`kpnh_dkex{urmj}}gnz{xxfkymjoefcuwsuv|wlv~tqz}znxt{yu|rovvomdi{}oy}xzz|s|ttnq{t~}|||{zz{dbpcjm[fc|~}yzq|twzrlmxptjb^aota`{{{~lyvv~wsxy~ryumtmx^akr^p}|}{~{tzs{{mp~xtz}~pykz~oaclaedmhhx}uvkxhvtmy}j~}}r{mcwtssuzz{{r~v{pvwz}rztusgvuiaiaorr}x|sox~wvwwy}y||||ttxvzreihk\i\bwpuqzwy|ywz{oyyvtjmtl][axvjovwszv}|yws||qwiu~kk]o}nbfpqz}{}z~zsu}yky{w}|nsy}~yspiqks~mqvgZftshfo~uqwuo|wuwiyvqfnzhuovz{x|{xqmunosy|uq{}okoeindkv}u}w|zu}w~wuvzwy{|}v}~yzuiqkked[\lbgroz~{z~xyn~}uvuvxkmmljit{wpx|{xw}zxyzpytdrmlg_in`krzq{}~~}x~yuy}usz}ms|yvemzwnqnd[pm^[fwrtn|xwu{|}u{mp{{jgrnx{qqwztqp}r|ztzkpuqkzqtocartx|{|{{quurqxtvo|z~|xuzquqqvxlkdhpnc[i^uzsuzw~p|txjlknrsgcoruw}t{|xw}|{x~vkuqtyhi\gui_j{|uzyv|wrw{{rzpy~y~|xzvtqr~woksp[^hd_ckgiqu}u~wx|uz{vyq{sryu|s~w~x}x}}{vt~xlwyuwnrkfrjdlkpzvt~|yrvznsty{}}{~{vzvs|s}{nfkiljifjkdi]d{~{|w~uzyrldnzvjamt|wtvstzwu|prqdywpaYiyqmq{uwx}xxnm}t{vxq}~n}|uuovuwdm~mhlleeh^]^umiiv~~|x}y}qo}|{rl|xwtzz~|{upx~xwypr}mtfrudheioujxw~z~{z|x|zzyiv{}{z~~~~||~vzvuqyknje\ijljc]elc{yu~uqwtnrxqksyz{zy|zuzv|}xvqnumeoin_`tppz~w|zvus}rrmov|}~~~yuzinwvuffpsninfYcmdZarv{z|{|~zssip}|znr{||zu~mv}vsxvshm~uqcfqmrngrwwuxzqw~upm}otsz|}qm|rzir}tshbclcddiinlcll|{||yxurln|~wony|~|u{vy{qutw~xhim^tmf`atyvyyw|x}xptwr~|{pwv{w~wloqnwkrdasoecnkdXbeheyx~{mvxrtwwypz{~}~zx{zznhsrlkqjmkbxjmwyw|y|u~xx~y~xxz{wvymsmzwxuxwkpvrwmoqjsgce`_lmgklptzry}r}x{yxwqk{u~~wys~{rtysvxsstmk`_onobe}}||xpv|mq~s~szyzu}|w}~}u~pop`svpgZdqpffjc\fhaew{rv}jq~utp}z~rv}x{wukknibvneeeyurtv}{orfeityorznjhklshsyypyuqjfnlnvxrouurrqpcllcd|ujrq||~~~pjroj|z~|w}vzy{{yw{~y{oeSz{jmrtuz{m~z{{trstlxz{}tZojmhipqklym|z{xxzsoxpq^l~|xid_mmfi}|{z|wy|||stzuns{s`qk\^b\^Rhnevsor}z}su~~s|quv}ufnmch}}|}{wzyzkq~xxs{~tmakok|ssnso~{xrx}vppsax}xxljhy{uw|~|zlkpmz|}pzxxuvx|vtnrgidn|sotsfppcodikks{tppmrr~zujupttknqeu}qsnrxtggh\^lx~tpolh|zosrvlhuvuq|vpd_haViun|uyzptwvty|x~vsrd{{zvruyy{}||sy~xz{nca][YV{}vkvldmpblnk{yy~~|~y~u}ywpnkfar|}}wyljeirzprxhp{lpsrlg^dobfoq|uormvor~||ywmfncdxmw|trkzy{z|x~vs|}~{teiut}~ux{rpruy}ywxvuxmwqo{yt|xnvwtwuvnlrvih`gvp|zy}~x}ui_`dqzuk^kfZhncgairfcaebhowzx~zsott~wmrqu{u|}~~ytmfWfok||uyz{{}povrpqolvjx~{vjzxstwylqt{rrqkmnlt{wx~v{zvuwq~~}}{{{mutw|sryv{wz}z}ykjbmjm}~|wzv~}ytqfitu|}}tzvnvv|wqgj_W[imv~txyo}tptogtvqqeisxpusnrgchokoevzp{uonqfnleqzx}}y~{trlyt{}vxypsowmlqnd`^ivn~y~y}stx{s}|xpztons|w|wr{u}zy|y{w{wx~thd_idY^`mw}{~}xyz~~~mw{xy|||~zzx|~~z|tunq~~|||{~~vzztvy~~|wwymqm|vieker~~tpo{rnme`p_brmtsmdelypsy~syuXdddy{v|upuxwzsvqehqvskfskz}}mr|}|wtin{quwuoofk}}ttk]htugdkoklyyx~z~vv{}u{ka__erku{tnz|rxumuuvrgx|k`^bjuw|dnlgt}}z{~x}}~{vz{vlgevyyyggeenmotnsv}wxw}tookpz~thcinqysvkeo|svuxz}yvryxy{ojumrwxwtqgjtz{}squzv}|z~x|y|zw|sgh_l}ztxzuyuqpqr}{wuyoxxqqsuz~rgcbizv}{o~wwrv{qxy{{r_`bfq|tz||z{qtvwsxx~~v}kqnhgjitqs{udjtmuyzpjuuqssrqrx{vqppt|{~wxztuw}qw~~z|pwyyz}}}}zvimhiop{ortrtvz~m|z~~}wtxuq~zxmadqnzv{strmpwvx|}|y}|zzkqznpkku||~ywmdemoy{x}}}ruxsupn}tnrektz}x|yyynw|qs~y{twy~zmonibcevzy}yumqvqxywqz}~zyuillotyusvomr{{rrr|u|y|v~znru{vwrqpnalvov{zps}sjrt}w}y|}{w|wxz{{x{{qkmjhixyr|wxvurqowy|~psxnjqfisrvxzwz~rollptx}~vrnjkxsw|z|zzlnkq}~{|xqyxuptor~xqfee`bjnvzv{~wqsrtuz}~{rozontvrrx}woupoz{yytyy}yq~{ypwtxqxnrurrkmtmrx|rruwmlt~tr~zwsuyzo}tmrnghpyzrspqutwtr|{vuwjaerhhopyrysjkls}~{zplmolqtn|us~vfeukjkpy|{~zvvoshm{{vyz|zxz{k`zox{}uunkwslntz~~qz~|y~zv~uejnhs~}}{{||z}{~u{zhVjqw|}w{}|}wpzt|us|~nO^~ymrkn}|uq}}vn|xz|zurvnrwu||x{zwtp]_|yxyx{xxyor~yrk`h}wqnnicsnetvwuuwwjqzqgiw{{|y}{uvrrsokpeal~y~~u{x{ztjb^R]wvnilb_dihpkhursukhus~{{utmcg\Wiz~{||~y~xrf`gZbyvwxqlinedecdgmocb{{w}srr|~tyvrrwrffu~w{~rs}{|kgrm`atujs}khonpsttpgpn]]hqy}}}zp}tutpiciua_q}}vz|y{|qskd^b^Uiyt~yoihfdbmqglmstkp|z|}||yz~{zxkmrjg]`mxy{zz~~}wpld^[ex}~tpldpofihedgdiluwu}tptvy{ntysppjchq~}y{rzzsnpqbfi^g}yrw|reqvspnondfwf^mwyz{xv{qmkhfeeoeYtz}vw~yvu|kb`b]T^~}wsieegiiphcotuhiwy}upvyz{}}yuifid][k}{~~~{xw}}~y|vmfcf\_s}zzutlmmcc`^ejdlrz{~~zxsonnwqilgjron}~yzx~yxsnlheeY_nkvzotvxokrppsrokgohaqy|}~vmyyljhb[\fojyw|}{}{|wklaVV[`cszo~mkliechocdnqnu||{{wsqv}{vqol`Zmnek~}~zyxxyxzw}qqoigeemjjusshhsicbbffasxzyxokmnmqh_hinqox}{|}y~xzvsoi`ZYbafnt|rw}xvjmvtqmjigfqor~}}|~xojcceknidx~z}oi{~r~wtrfcnhgs|{~|rnhs|nlopqrqvl]lyxz{y}}vs}w\cvnkeozos{xdyxqzzqnv~yuwxfbp|ynntlfqb{}vmgnideiqrn}x~yq}rTSowt{utvw{zu~pu{or~|v}wwponlnmmsokvwr^Rbzwna[]bjlnqrstedmwwrkessklmmpnjomu}~~u}vkblwnebWZ^X_p{}wyu}}omolgb`d[Yho{{xrr|~mo}~~~mkxvljhgjpxn_r~s|ulhlhX[guxuz~yuyzlbgslbm{z}~kc``bcdh[Xivyx~xovqeo|{u}{yvof_ciVPezonurjmh[aiouvxzkbtxx|~{wtyub^j~z{rnnqz{uytorh[bq}zzvprnjmovzzwnbjpmnhjrxy~tsqqssvyquw{zrsqjgWU`nrqvwy|}~psvwvszsttrtvupis}{|wwx}vipw}~{namt}~{nilg_cmwqitzvkxpjlmifeke\isy}~~v~uks|zmgb__aio[Yn}{v}tklpc]eo~wspywvyzocisjjw}smmnnqoafs~vloyjcjv~w|{voo|zxtme`cf[Xb{wmflrmleZclsy{{|vqwwz~{y~{mnt~yww}~~ukd_b[OWf|{xlmxy|}vjnvoqsjejpxsjtvrrrsuvzrr{nmrqd[iyvv{}~~uozwiovkggeaahna]u|~z{~rn{u{slik^Vn|olnqmhp{rq~o}|srplhhmebvy|zq}{q}}rrwvssyrrsnigfdhof`nwwokopgcg``r~||xsqpnfajkt{ujfececW]n{||}~~xpmppvz|{{ss|z|qnkd\W[``p{rmsxwmbn|}{w{wu}~zurwwpy~z~uomlou}usx}xi_[``[k}ywvtsse`x|ztinwnmssgckwwtzwwxxy|su~zrcZalb^q~wotz}}vy~pxyokjf`^fqge}{tw~}vwwmtv~{unjmpfk~tqrwumrvq}wn{}umga\Z`a[h~}~uv}vlfgggmmk|wpmjihinklv~|xqloplmggx}}ypyxkfda`b`bcrzrnmol`gvyswzxwsnjjgdu~}{{uy}~}qnjc][abcx|tmmuwf^lw|{ywrmzpmvywtv}xv{vtstxoqqmrnaZ\aZ_r~}|~vem}sp{vnqvrfdqwpyzsqqqrvnhx~yodcpndn}xqvz{}~rq~swxnkjgcagrkdp|x~{ou|}|ytnkonbcq~tppuwqqpn{|}wpjffkf^ix|{wyu|zxpfehmrkiwzvtrqtumryy~|vphhmnj]^jv|{|qmjeehi`Ygz~vqqtkajt{|r|{{zvrrsictyy|xwlhd]YZ_XYlz{yqktzxtrlqtrquxnmx{wuuvzppunrsiacdZ^lyyhjxzooxqijnphbkqlszyz{~umw{xtlc^fibfp}zv|yt~~snmkgdgpoiluxv}~zlju|~|vwwtpmqulgoz}yy~{sjqz~zuxtkdcgj``lz{r}|w}vvqd_dmpgkyxtqporngr|{~|wniqytegt{|xzv~vlhe`_dlfXdzyvxofnx~svzywsppsk^j~}xz~}vxz{~tlgb]^d`\k}vu~xmszzqy}qjloorxtozzwvvv{|tyzmedfb^`[Xfvzry|rx}sllmnlgjjm|~wttvxnlz|}vsoicadelzvu{wuyx}ngec_]`iim{yysq~~{}mknlhfjpln|zvtv{zio|~ppupfaekehxzks}rlusiikeZYfqowyvutvsju}zvskbaovln|vx~z|tysgc`\Y\iphmwx}zssmeo|xr}}yxvspqxwouzxz|vyyxurzxph`ZWZbcctzuxvwxov{ulefipwut{tqppptyvz}yogabgjkfi{vxuoxwmhgfhlnlitxttwult~ru~snkgbaddcuvtzyv~~pkhdacjmm}ztt}xoyys|qdehfeiqqq}xvvxql}{ohlngachdfurtupxtigih`Zcnnwxvttwrjv~yvslecnupuwtz~|wxuvja^[WV\imjr~}|po{{||wvusonswpmv}xwz~zuq{}uyuqog^YZac_hz{yrq}zps}yvsjcfnxvr}}xvvvx{uw|xoh`[bjlbao}xyzt|zokiggmsnbj}|~{utwpgozqv~urokhilf`qz}y~t}pkhdbekfgw}xvvpx~xuqedfefkoijw{yy|ro{voqtqlmldht{nq~}rszrhgihc^cgfq|{||rlx~yvsmfelmkpwyuzzy}ohea]]ckiiox~}noy|tuvtpqvwmkr|zxzskv}y{smnj`YZ__Y`o~xsx{}yyshelvunuyvvvxzrq~~|rme]]gpg^gt|xw{}rkigdhquhap~||ymmw~}q{wrolhgkk`_r}y}}v}slgccijdhx}xuz|tsx|~{rr~rf`achoqkp~~zx~wnjmonnjbgt~|psz|qrxpfcddeccbcp|z{|slu~z|yvrmlmmnyyuy{vywrfa^ZY]fgiu~|xpx}umpqomqvsnu}yusv{ojyz}}nhkld^`fdco~tq{sy~vqsrhbhssr~wsrsuvmn~}w}umic[W`nmir~~~}tljgdenyunuzuy~|zvklw}qruolkheflqkiv{xotupcSSUSKPbttlr]jyadfou}xxxtY`{|{zv}ppslhchozwppystyvrz|xtsnolkkr}roY\vmmyl{sxyzqotu~qrfktvPMWrtqr}xvs~qz|lgszwrjkgvxtj~{xh_k}{|{}~pekwvednnpwtcc]wtk]VS]bz|}wtaspsno{}vuvwvtzzzruqt~ouwonxv|yuremek{|eYTZntzz{k^bgemcknsxvxulk|xyguwq}|x|{r|wu~nhVLefx{tu|suwu~zkg^Xim}qtil`^eY^hVbq_detl}~txpbad`]lzz|||ip~vmi_szxwsvs~}jb{z~v`]aX]V`ks{~m{svm]tl\ahzoz|w}vvt{dif_[QvpvumbiorsyzijceY_rlu|bixgmnuvcidakq~~yyttosxuz}z~sgf[PM^duysf|lchhtg`qnodcuo}uytvrmywtp}mmjoht}~|u|}qgvszohhQ[k[iwp|qz}}yweiid]bswxz~wzqxfd}ojceqdgxwx}|zymrv{y~vxigZTin}smnp^dn|ru{kjm[l{mwxp~rpxukiV[Z^bs|quwpuxy{qeibXfxuokyoasz{nXkjdife}x{zxw|o|roq_]`Tl}{{}mtvsxqrtc\]mce{zz}qitu|kod_Y]aXizzwxywq~|qy^kqgmss|{}yu}x{~zvv\XU[S^rqxpdmqi}r]YWhfb{hx|yx~vpnj`adut|yqisxsxzurmaP[YXjyo}|x~{krf\afig{}xsxysu{opwpv~pt}d\Yl~}||}xlvlswvlk|pxuj`Q^nz}t{ymjfuwgplUaqthu~uzt|z~tumy~{pl\cu{~||t{poxkjo~~|{w{hwso`erz{{r|qzq|}iokidRe{xvvserllfnef[`hgzxps~~~~tmuoZ\`^gemowvc{}recpwYcuruft|}v{y|qyrp}^eijeq~ymywiy|ymb}{b]h_Tt{~wqplt}wepbVmiiiqjn|}x|ps}wwmccgneyz}umz~noxsysmkS]S[mcn|svvsrzummjdfrt}p~m~tnvxxwg{uhZheqywxkex}}|~zjo{qgf^_acer{svnfcfflmqysw|q~upyfkoyfstpdl}{x|}gxf}yrmupjj^Xagn{ywxumiwmpl`rv~y|}}rh{rpk`pYT^xu~vy~uy~lrvyee`^\^dpx}f}klqqftopfz~y~w|~v{zklxtqsiYiegmry{v{tozqhlzjztXWmirmvv{w{fjVlmX\qn{~vvumv}pmpfdmghuv{tw{olfk~cSRfYbvuz{||mkkagti{uuo{xuwzxm|~qie\\hjn{rv~l}xyqnurdi^WWPmnp|{~}vyulimijsr}ytyyxwnrqq~xkyqpaamp||~zzkem}uzrpmqzvjh`X_eeuvzwytjkmdepy~y|noexxlhso`nnkaw}q|tklo~ntsstm`_\X]gk|yxw{xiystll}wz}wqqjpoeZ^l][h}|y~w{{ovx{y~yil{ub^`[aikvvrlrgouclvwnw}|z{uptst|xijh^flgszx|omqsy{{t|c_swiqmbRaqsz~u{ueb\rpgntyypqqmprieigjsrx{|{pzwx~wouiabbpeRSiek~{|{|tapfkzou~xp}uhuzizij^Z\m~y}u|{tw{ukmy}~{gijukg\^eZS_pzzuzswsrhn|~p}r{}sxqsukp}fex{udk}q~oumep~x}zc_hhfgYblXdoxst}ppnlmltt~}~|}yzuvbYjsngkt_lxs}wtyxrq{yszzsn`rrlm[Zb`jz|~zy|ydd~prt{x{}zkgnduy]^\lvm{y|p{zzyz~zx~zvrvnlpd`rnl]Tpoz|r~xqjtsixwx}}}xgwruynsqchg_vuxv{}qrmu|}}}yqntfe_akYZdgin||{vue]t~||}~}t~vswzso}pknbhgq~|uvxxzrtz~suecpf[]SflYfp|~t|wwollt{w}~zzxoxlfmv{mgqhjch~y}|rpyr{{{~zt{vsf\uvuoYYgfolxy|tn]ttjrv~~v~ttrvpjwgb^bhqz}yv{pm{|whturjddjmmjo]cdhiur}uiquvy~{zwxpgehv]`][sptv}xkrkns{ynllash`dckaWckpos}y{wgouzuyxo{vvsy{ttdw~tn_^vy}~~|qhncxvy~zp^jrklfiVX]fivw{vqz|kn}{}simjlp}unemhdlyyt~|olyz|rrncijnbfbUX[Uz|wwfmlx{|qdi_qj]Ufkbl|}}vvs|qq~t|wzrhkve_x{u^Smminnt~r|yiinhvqn~xtsp}teghg_nszqzxtjbpypw~suoo`emph_gZ_brnrvg||z}~vzrktympsm[flejrs~~}lnsxm{}~|~~ojnb^d\boZQQeto}rzntrw}shbcuqk|uh[qwy{}}|yiowru}wzopy~z`gtopqdbgX^espzptd`muyidjzbblsviswryyu|tsvx}~z}y{s}{]pnytT]^Zkyy}}{z~y|sol~pgXjmmwcy|y|{ut|ootvnt~q}wdhhnh_e~ntnzh}|ywjkmcibzuyv{uzw}n{ylsxp{y}|xeb^g|ynweuy}w||p~pyjsjrt~}ysz}{nbNQ[ovqoozhgllrdssespihhdextzneos|vgxofvmwv|qmqgqgq|~~pv|eeYRSkx}ryj~{u{z}nr{ymvpx|swqhpc\TQ`hwt|obZbn]nf]kd\jwhd~vqrwxhbwbZg`qpuq{zt{y}|v~|~~rwzpw}kdblj}sy{tkic|w|}w|u{mhnt}~u{pxneT]bn{wyt`ebuumxmpwqkmp_e}wmeeow_[hW]dh|}zoqtnjxrpw~}uysl_[^d|wzutv}}ttxx{ruv~}{s~ompdROXWo|kkj|jljmrkbkqfUs|zqmowztcrldilm}}r~ykmocnj}v~qvtviYQZ[q{}omjxtuy|{y~mxry~{}}n}qgohXO_`o|xghcxkbcdkpmrzs^zwnu|xckk`biewyq{sn}pwm}ryq|p~pvxk^edtzxgijyjrysz|{sv~u}}w~ouwkW_[ft}olhx}egmwuquzx`i~yqprvufbkddoll~zo{hmkmsirzxwu{v]^cgr}~up{qpv}~}ypy~{{zqrgooXQ]\kswrpvdekpmovvmWp{zxrqxzviopljujxtyyhwtfqgx~uutsyilvkU_`jrwpn~hjpzz|qv~~}~{jyg^imYYaet}tnrm\cmqov|}tb~xtsyuoejhhivfty~{~sz|r{oxn~usq|rr|gm{q]ffqyzpp}mluww{w{||zz}lzoamq]Xaet{zst~gktwsz~|w_t~yuppvqkehkmjznvwynzyksmtwsnt}|pheus]bgowyv|wjq{~~|lz~~|z|~nkrX\kdT^cs{uu~kclsqs{xxee{ztsyzpnjlopv}m{}~p}rozpown}w~w~}kqnZgtf_hm}|stgiu}|p~zz}~|~{~yvq^n\Vio^bkyxrvsbltuu~td~ytqtwkhfimqrm|~s~~s~vr|qwvws~|ypmt]evncls~twqmxrzyxvy}||}x|}}uwbpaVhp`ajv|x~|gowxw{pa||vrnrwlkijpvxst}{r{spxm~vuxtznlqZdtlcjs}uznly}{n|}||z|z}||z|zsp]jYVhl^akxyxwfqwuu~}yif}zvr{{onkjsu}pz}suwyqryszy~uy}uzeoh[jsgdmyys}jr}{||p|}{x~}|{~uwyz}psccgUaohaiu{v~nn{yyveyzzpr}rnlhjttr{{y{x}xrxt~wy{rz{{}zzritaftpehrxs~vp}~o~{||{vv{~||~vyzy{xtnblZ^lk_alq|{yo~}y}wgqzyqo~wqqjkuuvvz{yz}wprtxww|wy~}~xugsdcqpdblqxw}|n}~z~}pvx~yw~|z}{}v~v|{|oqai_Yfj`\enr~qz~xyzmk{{n}uvoiptz|mz~w|zvpyvwsx}uxknm`jsjbfqo~usv{|wpy}uwxz~ztztxx{soicj[`lja`mm{{u|xlv|vn{utlhqt~tsy}zzvryz|vxr|x~twjridosicjpq|zu~|}~sqt}yrx~yv}|vqzryw{qoheh[blh_`mj~z~{tkv{zsnzwvmltxux||{yuqy{{wzs|y}~ruipgcmqicjpq}{w}z{}qpt{twzw{~xqvqvuxpkhbgZ_iia`nm~~z{~vmu{zmxxokrwxu||yzyq{{v}q}|u|y}priljajrnfjuqw|z||up~yv|ts|}{v~}xtm{~rosrsgi`g`]hnidmtw~~}qox}ntxxslnv~t{}~{tv{w{srtxyzwophodfpsmiuuz{||xos{o}~xsy}|rz{szkr|vlspsihccd\dnmgjwt~}rmxzzul|yxpow~z|{~zws|}}xxp}vuxwwlogmfdntpktx|}}}{{zpqyq{~ztw~~~}tx}u}{knytjomphdcad\cmpklyv~tox|zxl~zyqowz|{~{ys~~xwnz}ssvttkmgmfeourmwy|{{xprwn||~xsw}}sz}r}wgmupholnfdbdd^fpqlozwrp|w{sn{zwppxz}~}vwz|qozyquuurlljmehqupmyu|||{rov}y{l}}vsy|yr~~|t|pgrskloklddcf``jpnitt~~xqtu~l}{|upt}}~}|~|}s~w{mvztrwssmjjmhelsrlsvx~|yworunz}~xrt}~|}r|s|vgmqmiokjfbcfa^emniotu}suvqw}~xqqz||~~~u~y~nsxuqwsqojkoiekrsmotq}{{qru~ss}~yrqx~|~uyv|{yhiominlhiccid_cknkjsnvu}{{{n~|spu{~{x|tnvvruwprnipnfgnsplsm~}zspv~u}k}}vps~z}~~~{st~{rfmnkjpihg`fh``flmhnmt~uxvtxzsrz{~x~}oqusqvtnqjirjfiprnnsn}ywggoznpsg~{xvjfn{zz~pklkswvsq{ja^vhgjgwysu|}zyvxz~{wxtxt{qvfglxwmprt|ust~|yz{vx~{zztnml~v~vvvxwyxtjq{|ysmoow~}}{vZT[uxg`S`d^bjsurwrx}ws~}zzvhi|||nmop{x~mlu~~|oZ_nysugab`noeckyxo{lyxwnio}~y~xlllowyr}w{wleT]k{tt~trv}}}|wxz|qlfj}qec`fxnjjftwwzoxrph^hqy{x~pyww{~xx}|sqnkdezxzv{~uy{|ymoa`TXk|oztiilpolmcr~lxwnqpogr~|~z}y|sx~uurs~utsv{s{{rheb__xpnihsmcdlwmnpdtspojlv|~wzu|rklkvyxur~}{ylpfg^^n{sxy}y|rpvnnnrnjfgzvmohpztzqrqml`hqx}}utuzx{yttqkkifh}z|wzvxy~~|}{uhg^`W]o~s{onptspoeu}kvvjmmmgu}|~y|y~{qvzuvtvwvt{}}u~|x|idba]i{rsjptkdhqujqiitpplim{zyzxvzmkjmvxqwxzrmife[ep||{~{}|{~}xnvqonmt}rnl|rsmp|wxwppmnacjqyv{py{~zsr|uhihdduy~~yywx{||~}{~|zwkf``TXdq|ywwyxvogyzkvuilmjgss~~{zyy}}tzzwuop{{yuwty~|w{xgdc^[im}}wpyukhmtrnojpsrphejyw|wwyxmkhvxxur}w{slhj^Y_fu{v}z~vuvrojgm{ty}xvpxw{yqnqj_bdlw}zuz}{{ryzs}rhjhbhnox~}wzyx~z}{}}xxkcc_RU[gy}|}xjflcqyz}~runo|~~~vqw|~myz{soqtx~||ut|ypqzmhkgdmjovfsl`cipvyyupszuzw|~~tnsk`bbjw|ro}}nwtkprov{wyxzw|~|}yxzznilng^ch^^afr~~x}yxyzx~plpmluqv~uegjqvstuwvrnp|~{slo~y}w~zqtv~|nedo}zq^^^[ahcs|~yxlkuwy{}~~xv|sianxzvuzokmpy|}}ldV]ku{wrpmha`hmjyrnvkoj_ZZmpr{z}w}tinpefgkvyn}oiho{yrusuzx|{m`W_w}ylpj___dmutn}th~wnjks|z|svykjmlrzs~ojbaq||}|{|{xuz}vokz~z}}sr|kcaiy}kkifookz}}|{|ondlx|}zw|{~~{{xwsf`bzuz}rrtwz}y{jb][m}~{xsrpkghpmozkpqfld[Wcvu|{zw}ohrnhklqz{vsl~skjp~ttvu}zwqm_Y]worsgeefmtumwyew{oklt~}yvrzohmms|u{y|nidbs||~~{y{~}zwol{}{|{|nkxkddn~ponlvrm{}||{{yooemy|~yw{~|||tspe`hu}|tttw{}usec[`r}yxtoknpjwtitkgj`Z[otv}~w}{insjjjksz{t}~iw{okkv~tww}{~z~|noe[Zm}|v{pjhfjqtos}jp~unkpx~x|szvilmr|st{yrmg`kv~}yz~z|z~{rn{}zyvy|{nmyofcivyvtu|ro{|}}}{xsndlsvzx||~zz~z|}tuqgamzzz{utrv~~}}~vpicX_js~zspqko{mmvjjka\eplt{|zvitoiigirwvuwo~{qkks}xz{xz|{xopf\apq{tpjinssp{xlzvpmqwzr||v~tjmnyynv}~zxspcahp}{xx|}towvz}yxvu{{zndd}wyzy|zutrfYWZjxwuz{hqwnpqommoju~qwy~vg~~~~}~x}~|}vw{{uxbXYk{yz|txptmbbiet|~krtmnlmu}mafremi`fly}s{tkpy~}wxvvrdY_bmz{wuqsy{x{xwtkiq~~vrv}|kTP]r{nzqmqqpoqlpxr~yqvpgjvvzyxtrw|{y~quyma[btxwyvzxxwjbjtvmhYQ[ht{lslgfcekpor{tp|}tmmv~{ruz}u}}ohf`SUewzwu~rtrhiw{~|~snsk`\dxrjkqymjsvwxz~rprlags~z~}uvwmum_`ZR[ox{yusw{{zvz}us{|{wfbaYVg|}yywtrpiq{nmwngjeam{~u~{vuposvsqvodileagyyz}x}twvlmz~|rts`[ZRXoyrwnkhehmonswomwwpnt}{vps{ws{{zxv{tc^b_[i|x||vznqnju|y|~{wyreejebi{nknxwjouuvvztimogfs~u}{unzyijm^Y[V\r|yuv{|{~yvzx{~~z|ua\b_\l|{ywuqipzoisncgebo|u{ztsnmprpoqkbekgem~{zy}syzqs~|x|lnm\WYV`v}xyqnjgimnnssmmuvppx{{sos}rsyxxutxn]_e_`o~wxyvosory|qltrrrrty||}|skn}~}wumq{wu}sdvdirzu|n`kumhcanuu{|~~~ldshhlmrz~~~vlvtikrytcYrtrnbfllv~zvojr|yytqrzzvxpdei|xibdka\lpikrx~{}|zywmtuhinlv~whs|qmmknqqsww{~~ngrfXYf`Ybjadlkrysx~{{wuqoquvy|zzzzuuvrjvzzywzrpjjnuu|yut{{~uxqnofk~}ugT[{~lgnpux|{uw{pjqiadlhZi{ztss{yghsljt~z|fhvtjs{{{zpjls{zxs~~tsup]fujkrjmtqrsuvw}snzxupnpq|nhv{~yg^l~wrut{|{~}ttuh_kyx~rlmpswytsi_[bnzsui`hjpxsw}vpfdgrtilljkiwrkmquxxy~}faqlflu}}vnsrehor|zwwrrkkd_^alz}wyv}}|}y|zt{{~vmkoysustthehfhr~tk`\[^fnqsuoruv~}xwxvnuzprjZV_c_mys|qprxumd_en}spqmoxzvpebg]TZeb`mw{|yvysakyusvxyyxy{gmzpeceltxzzpy~}}zwf\bkip~ulihhhhltk\jtibacfhdjyumrzrhnqw{twurng`Z]eo|yuzhqlZTYh|shllqphlrrrvwndc_[YY`glqtstsmrsv~|}}w|vleid\dv}~|xtvqlfhq{}~wyztponecnniicei]V_ifhtyzzyyxkxvrrtvy|pzumpx{{{libSTdosqhsneeilonntta]nncafmrqn{~qlruiit}vuqmimw{u~s`gnb[`kx}}zosuxvlntssvxrhghd_\aejnrtz|xxosocgv|}{{xtw}}mb`\URYft|~yvvsloytni`adZXeoow}u}~vkx{pnu{~ux||z~~tl`[_YS^lowt|uommnnnsxncq~vnnsxxuxz|s{{xytkiowzxx{~~|{}~~xdeoh]]eox{uuz~}sv~snrqhcfjpw}{yoqodfrstww~{}|x{|xz|mcecYTYbm{~w{wrjmpfgpolu}}qp|zlfhlooxx}~ullnqw~~wpdbcYV_bap|zz{{}pm{sllnpns~zt}~wiikbWX^hu}|tqwrltxsypdlsi`bipsz|powtifhinu{|~vv|tqkfmngmsnr|zz|}}}t|{xy{xv}~tkpnaZ]bkxz|xoptliplhpyvq{yrpquz~wt~tjjmomww~}wzynlnqv~~ynfie^dhenwmuymjkkgft~vpryvlsqjroa[]`gq|~{suzstzus}~okuwkdfkop~wikrh\YZ]cluy}wsjcfd]beahw|}~v|zwz}||pell`Z_fq~~xwqlcbheemmku~~wyutuwxy~xidnumeglpow{zzvsux}zuoefhbdljo~xvvwx}{pr}wopsrnuyvz~sluwu|~tfhpg]^ckt~}v{}w|{sfgqqfdjrvx}wkrwkbbeioty~}sx|{zu}yrne_edajmmxz~}}sc\feZY`is~{vpgfkfglhisz~yv~}wttuwx{~tgeoulgltxyx|}stx}w{vlf]UY\Zdlo|~}}}}~pgkvukjosqp}|vv|{op}w{~whampgdkr{~vv}{||wtttsuwma`ksmjpyuhmvnccgkqv{vz}yt}{uqibhjhpss~z}{}uwy~}te`lmbbhq{~{vpggmjloknx~yy~zxxz{|sijtxpmry{}|s|vnorw|~}{voi^Zaacjjo}zonu|umnqpnwzy~zs|||~rejulceks||tmsuswus|~zyyxy{qd_dmjbdkqszklysfdgkqw~|~}x}vrjbfkinnmxwtzzy}~{{~z~~|zvl\[if[Z`hs}}zsknsqrpkqz{xyz|~|ploxxnknqpwzwvqruyztzxoke[[baccajx{vwwqqqmp|}yv{{mdpsf^_dkt}xu|}|zsu}}ywwwx{xkcbhohacggl~{owthfgkpv~{z{ytpg`fihgbcoy}|yvvsp{y~|z~~}{vl]^kfYW[blwyopwvtnipx}{xwyz|xmiiqtkfhiiqyr~tkjmqw}|txzqlg]Yaded`fs~zx}~trrnmy||~u|yzzpcivnbadks|xpt{zyrnv}~zvuvwy{rgefnphdfhitxq}qgfhlqw{y~|v|yvridlpomgkwzy}xuuqp|x|{z~~~{vk^`mi][_gp{ynnvwtmhnv{}{{|~}qnmsyqkkmkrzpzwljlqv|~}uv|rmiaY^ghgcgsyvv}uppnlt}}{}~vhbosg`bhpy{rmu{zvpr|~}{wttuvyvjcbenohhlnr~po}|ojkou{~zv~{spja_ioolir~|xx}xwwtvv}vwz{zyvpeZani`agpzwos{}zsqz~}|yvvxy}~tkijrvpmoqr|up}vmmqv{}~vpu|ulhd[W`ikigo|{z}yvvst}|w{z{}xmaespfdiqy}wnpz}zsqy|zyurqqrttja``hojhknq~vq}tlkoty}}zt|{sokb^hpqnks|xx{xvvsv~}}uy|stwwvurlaW_lhabir}vnq{}xqqz}|{ywwxz}{pjikswqoqqt}qr~~rllpv{y~z}~xomwxnhf`XZgnnjkw}|{~wvsqz}~}y~z~{sfcoukdflu}~zplu}{sms{~}}sjno_[dfnjsyx~x|zzn~tonvrustrgoxritswvt}}qt|}xr{sp{|toc[Ydginkpeepqfltw~{zxtlups{y}wvncksnlz|{|ts~~|{yxu^h}k`baegegXZdd[gpv}||x}xyvy~zuytwy|nl{xnairqt}yu}iWkyokjffURZ]Vckoww|so}z{zvwsswwpqmprrtsypmtcisuy}xx~~wpqyiXl|wvpp]UZ^Xaeekluywotv}~}{uottpnmptsqllyxl}|ghs|~vr{~yjbprZezuwtwiZ\a^`e`efmrvusx{z}ppormqsz{zpimlx{mgq~~~urysact`]}vqqqvsa^bd`c\^afjqusrtwolv{krt|}~wmdxsrywhlz{z{|~vu{jYjmYqtpmnpxk`agdb]Y]aefottuxrmlqquz}~|shdo~}mit|wv{zv}s~wcYocbypmjkrufbeid`X[`fcgmup}x}ohr~otw||~wpbk|v}{jl{ywx}x~xt{pZ^o`qrnjiktqfdike_[_eicjotnxkg{yptwz|{qi\tzujpwuxyw}}swtgThme{mmhjovmggmke]^bjfdkppv}~qglrptwx|tk_`|~mlyzutyt{}~xs|vn[[qgurmmjmtskikoib_agldknplxiez|optvyxkdXtsjqxswwv|||svyncWnml{opkmsvoljomfbcfogjnoj|}lcqqmquwzmfXfykoxrsvp|~ts{|ogVfqhqrnnrwrnkopicdemjgnnksqcitlntuzpf[\~nnzyqoumy~|vrx~~|njYcqf|vvqoswtqu~mb]`^]cibc{iyyou|~pbUust|r{wy~toot~on\l~}wp{yonnpodWKYnjs~|}sprrnztlcyuxvpv{tm]bxuicsvq__eatwsp|utvxrsxxqs~zynm_kpf}y~yuuw~~jbnd[Q_sl]auc{|ncwos}xgXYtywttuwquq~ywvzpfvwibe_ITc^l~nyu{uuvofgkkzsk\l|}~}{vrwne^pfxvq{yqvkjtwunosvq~|jYSm|oz|wrn}wjckljkqwf|`gx}sss}zphtohm_Y_ttgr{zs{{{z|{zyw{wtwzm_Sunfqj~{kdcYc^iphuw\mw{qmrftpu{~k]evfu}yu~y{|eywipdhaW`f{rjq~{{|phimvpkpvop]f~}os|u~|vw{wh]}qomrxrnjuoo}wxslinqxsbUevf[mtwpuqrxwwntqsrVsrvuz~ywtfpdmeamrzqr|zzsv{{{|kryy~v}vstuvl_Vuplkspgd\jbw~wdlv|ujogjglryyk^hzo_muyrz{|h}{xcl^dfZgk~suzqghlurnmvnoc`s|t{sotxo{}wrrsuoe\t}rspouevu{stykl`_ekpgZYordhy}xopqmlomllys}ih}}r{~y~kfj^qchr{}ttos{|x~}}py~wtxrr}upffdcZS_}wwtkmabsi{|}kzxtpkltc`dlspf\mzsjy|{zuo|tttttvp}}z|m]gTgc\juxxkdensrmouptik}wyt{vqrpn~xrgihg`Ya{vwvoiu}~wqls_Z[big_Vfvun|{tmtiiffdgxz}yfuv{qclZolanyw|slox||vz~}wy~~{|srno|sj]_][TP_}|zvpsdhzkw{}q}zzvknm`]^hone]myxq}w}pqqos~qyqjmo]kogff[Ih~v{}upqzpn`gs||zu~yxw~pfthdcc`li\Zr~}|ykuz~utp|{|{{f{afk_U\n`SYepnu~{t|utruln~xxotaif^tj_Rflpmxtuvtsqzu}|rcaul_dZTOc`s~{y{yywzm]|xtxzit|ickrmfWUc|xuw{yz{|{yqm}xlqr~srkaYhkgZWWrxrttzthulfkkfvsowrwwetlnujhdgbp{~}utujj{oqs~ziuyn^io^YTkjxwy}rvpmn{etuet|~~zrh{{p}xqqxcUmroo|}{zyxuiixxyyt}|mnogbZXkfUVkipz{jq|s}|qskyvyvgnnxxtyg^i{eYY][m}tn{~wxxx{qowuqwyyg[nc[QcbX[r~loy}}xxtizwqzvx~|vvxwu}xfgkick^^}sil|estzwuvv|~sdn`c\TUff_mpqx~p|vgz}|et~wzxychpukhb\njwmku|~xolhmofyl}zy{~owfaf`b`^Q^s}ry}}vpk}wlf}qhrw|myjlumh[kcfxsu}}}xvz{xppwys}nz|tnplpfQ\kc]j~wus{|umm|}oq||}p}pqup\kqi\hukogtzzzxyj~rw}xtxskk~hWboZQRWK`wxkyyvwwykmypuxzguib_il_Ufwjryxz}~{mt{jvvx}ssinz}i[`dfhbPfjnqzolts~xy}|tu|pei_`emc^pztxuw{pn}{uwvwolq}qeghe]iS`_^rtu~z{zjiodl|z{~ys}hhccfk_Zgr~|w{unrzodl|ttwogc}tijfbW\fS^gv|~}yi|wtvws~~v{nq~{r\]qoeer{v~{z{pqvzplw}r|{}{vv~{nbZixvvttty|srnz|nptvxmpwsmef`v}w}smzhvzvxsmosrvy}moyiY_uwvplu|trzsxiqz}}qs}wtvs~f[h\^\_luidq~}uzxmiprqvu|uzqcin~hjki|}ow|~bcxkkyku|}r~|tk{uw{w~soci^qxm]RMLM_h_yimvx}vryz}}lz~cZY]X_~t|zzyo|vvsmx~|r}xvfZ`of|rl{pr{y~{ywz|wwnpu}u}nsxvec{wlromadsyvsqsjykxhsqhool`fyvnmhccbwytly~niszctny}~zxzyolllpqsqfyujrtyum}~~v|xlis|tspdk^sty~xudadpmupzpfmk`ew~o^mhz~}wbgltrgvsx}vnr|}zlyw~pz}rjtsm{{|vynouyxn{tv~}sx|ptyx|y{pflf]f~xqvtrxgggaf_uvzmfnmm\dz~vooocbkenex|y}qj|txt~ywyu|w}}}egoy~|{~ukrpqhr~}~wvrundl]ilffd{wvohs}qnosg^t~y{hjcyxx~snbfi_bsusr~ytv}v{yr}ytxqzzsuossndzsvt}x}~zz~}vzpz}yuvqpeV\w}vvxpfitzuhkdpxv{okl{~snvldfgkXnqtlepuysx|vv|xqzu{{izopvrtq~vkp{tuy{sxt{ty{xv}|v|}k`d]notyv|~p{yej|uhXi}pllkdnxnmgxwpmmfZ`|~ut||w~tuy}y{uv|qsibflrb{~|vuslq}}w~y{w~}r{whxrdSSkpeft|zkv|wnoe}s||qqrgepxtfdeatryxx{ww{uzzqr{}j{vlwuvuoxii{uss|xq~{z~w|{t{w}wyw}}yxt|yuxvvijr\_L_lawyux{}nlduymkfQYfd\Nakf^ejmlyhv~uxqriddlc^`hi~}z{{{wngs|vu~z{z}uyptolqun[O^eu}}yywmx|sqqj__v|wus~fc[g~usmw|ojpdz}roz{w|xxju~qkkVW`~{}{v|||yz|{|{v~|ntys|sc{znm|whQRrw{~rtxsnqwjnbwzpgijwzkbitty}vqvqpkpxtq|zkj~vh{xxztthemwz}~yz~}yzunv}}wutyx|}~{yzppmqulPNk~yq}}ksoylbv|p_Y\at~jehsvv~zp{w{iu~wu{}jkstxpxtqa`uvpt}}zrw~|wtffvx{xr}|vxxkzoffuocMVxsfdy{u}xjuod`aelxvddnlkpw}w~ntir|qr}frwvdyknppe_g|}jo|zx|y}ryyhmtvwv{{rlxx}{ww}uo|xoliilc[q|lovuxy}rrwsd]adj}rglfhiqsuwmmf|tty}r{o~{osywgZhvklw{|z|~iv{tuqns}|px{}{xuyy}otvuiyqlglvfZUo~wrsw|uz~nqsxkfginoolwwty}~rvqdxxoswkz~~suorvxbZhkgrw~ywlnttzs~~{uvu{xlq}scpnejsugUOg|{sp{|~|{mxpukdbgitxgntrwotzajumysgm}mttsx|m[Wywz~{}}t~roppy~n|s~o}}t{sbp|umkfcnt_V_xtx}~~{~uvppmgb`kwxpdoqwwszxmqntznjyssxomowt^[qzvzx{m~womszvt{}ru{mrw|peq}vrexridlseY[yzz}qwrqpgfbpyuiow~~yj|q~otppqytfgpvpvx{vnxreaqrn~z|v~|qtimrvx{|ks}~}wlwxhgirpnqvzw]xy`aepxh\Sn||kyv}~yjeboy|zvrvzx{{kny{gcixtw{kajbY[wyu|or~wwp{lrtvw~}jt|{mmsjilnoutthZ}phpsz{ohe{~ywu{wuy~{|yysi^_blmmuru{xq}~wz|x{|}sdp|otgsvjjnc]aznz{yu~zlk|}}izt}w{tv}oxucefgceszm}cms]emquof^csypvv|jdfnrtzs|xu}{rsthgvxrvd_`\TWozwnx~wpt|mttv{ygru}uhvnjijfm}ms_a|knrusrkikxx~|~ywwuvxx~zuhdiqrnnumzz|p|{y|}}uy{}uldupqeuupiibbmnk}tvms~vjzo{rvozxrnhjnggm{no{atf^ersrld]bjv}wt{~khlrou~s}qy|{xzvsufx{o~rc_YWRZqywpz~tixpqpv|~rbo|srgwqhhibn~mq`\|okksklihksu{||{uwvxz|zqkowyrtspn~pxysuytvywmepoqigulffdn}rku}vwsq{nvl{qtqq}txynlpqhous`yodp]^gwqrle_ah}{xp{minpmyuo|z~ywodw}kync^VXS]q~zs{qevrmmtz{obj|v|qfsrfcg_k|mncUyridngfhhlrx~vwwv~x~vqrxuywrivw{nn~yrtv}|nhd}tmm`vwsdggo||qr~zx}ssu|{pyl~}ouns}|rytpmtokutc^{dgh[]kxqvnjddpzupwmhpmo~{r{{}yy}hczxhuid]W[Xfx~mdpvnsiw{tni`]jy}n^]d]]T`pms{zwql|t~}yq{hlkswyx}}]x}z|xqyo|{{sz}ji\YfomnVm}~w~{xor{igt|rv{{vr]guiht}~{u{xoq}~|jnmkxz|uf~psy~ylgdsk`bcv~yuqui}uepgqkgoyrz|p{rjy{m^eontdzz{ut|w}~|nXffzplqy|j]dnwvvnyqpztafohzvwo]`moob^q~vhu|zxttuy}z|syo~}xu~nZTggy{zqwxpcHervqh|osq\gwywv|vniymkpu~rut~rgk\|z{u}xq|qx~oT\w~{{hc[hrmihf^gvy~ti\fX}ynjls}wu|z~wfrtlusptqzzykv||qx{vs}||ocXf]|xjotwwvovbjrn\fmgsrfqqabmok]fknl{qn~xz|}uw{w}sxvvnX]iitsgs~s^[upyguumy{d`uyrot~zyhs|kqq|yoxxxxjlWb~~{vuyknz}dYn~wss{ukjtumjlb`lw}{pab[Zwuooq{~u~~}~ptixrmiad}||un|uwxwuu}~{n_^aW~{|vyz{{wuqXv}qn\gkjke\rtfflle_r}l~mnzojx}~}wu|z}qutvs][hjmj_hykgzv{ot{rwyg^r~tppz|}popsv|}rr}vuzzlj^X|x|q~spe|i[b}yupy|zryyvmribcpyzrg`cTqy}vsu{zx||~m{}jhqohc]oyt{~s|x}|rpxysg\^]^~~}||{|vwmYzxrmZdjkgd]wuhjkkdey|ovji{sp|zv|y{spnpp\Tbikjfl{po{|usxx}xg]i~xwu~{xjzqy|~wqzuqq|vlgfYy~rsvxny}k[Uk}}~}|{{zuqsfaerwtlbba_~xtyw}}zx~zgwu_dlniik|}zxr|{{rsjo{wqztnwuhq~dsouy~~novwqmou{hvx~uztrmmq|hqoszykjrzndnruqguyywz}vpz}zuqtuyu{{vrwvoqX`~xiYusv}wyvlzwjv~ymhkfnf[UVeuq]dw{kjpzn{h|ylr|rk}r}|u|lebq~z}|wxx~~|l~{yr}yr`\lvopogiuwr_gel~p{lnsw|kj~{rfqmoosmyi[wtzlo|qjxyz}z~rgihwr}rYrrm`ryedhsniuibixvzwyyyzxsvtu|t}trvxnjirkh~zy{|fg{|ywvvh[dx}h_fovj]dhfknug{redoosisrju}tnwhvzsxg_p}v~zu~xzsr||vyywl^md`bxulfmlgbi{ior}ztsfesypsfkq|{uyu{jwiaov}y~nw|qmv~|vszx^VZn|lxrhsytaNgwfhsoZgusnu|zgqvqp|vxk}}r~tqnch|srt|~v~~{y~{{isobfsy{tgdmssgjkjtuj]]bs||{rtwsj|qhtwvgXpxy|ozxwzw~~~|}jjqa]_~~paqpkcvrs}sdqrlkz|}qux|w}{ttz}usqsieiutytm|jl{tkzyymiakoqtjhlqmfpupuuy`iyooe{qjvlswlbvyulytcnrpyx|}vz}y{wwmr``c`[urxylhmwgnf}nunnemuu{}zqzxrkkuxz}yybb{r|x|vx|ul|zxs~|x}{vetjcegtvg~qrflytkauva_ef`t}noytu||rwkporrvz{{rqs~k}z}|s{wxulZ\w~zwnrlgmx{xorflzptnotvz{z}~}cw|urfz|xjj|h^cmr~xn|~|yxt~topjjgrdnxlhfzoYilp~vsrfp{|wom~oqgnjzoyZdlgy~iiywkpszqr}~u|p|x}uk_oanyz}~{fnazygfdldt||yhtwoyuywos|hvz{jjnwni|zivx|gst|~nsyrskn~~wo^}oePbv`Ncen}zztqyzvwyxy|pomu}~lauquqkfie}plu|~~w{ZVepqx\^oYWh`o{gkyt{kwonxr|~zty}wk`sts|y_a|y|p~jb~z}xvtyg{qNYqwr^r|elnlk|krpkrpnpkgmp}yuy~tkvstvg~lubdZhzz~pqz}jbqbWmtzdlidspvwor}zmnumfZj|nxkivnnvt{k{~uvhc~nysrvw}y~|xz{jVhzpyprnjokjljzsqoswlckhknf{w}q_wncgf{uztowslzzz{y{u~wzt~x~wgYihxmtunh`r}vx|zlloc`lqdv{rymozokoqv|kcjeoz~}}sw|yoxw~}uvgfeahx~|khniqgmxnps_c}r[ZYi}vlxzsqjmhnm|{~wwv}v~xz|znq}xzxtfanjtvszoggtig}inifsnbj{z}wk}{tv{kutv}{}yp{rqifrxwulwv|~p}{tnqeY`lw~kyqca`vvs{zifyxq|wvy~ymots}wzqklgogx|vrgv|vun}~x}}}~xzrqa\jgdj}yeak[k~wy|tmtt}z{}snxuuqipetyv{vovvvsftrjdpnn~m|Zdyylmmar{xtveszqyuqk{{t{v{yoqyrwjlpp}~qksqxpu}{y{tdsklwnqtokv}xx|zynd^h`Xc}unxx}{|rwyrtfptyyw}{s}~mphWUp|~xpgpwmusz}wvy|sq~~|yyz|vkjtibiuz|sx}~shbgtjdqrbn~~uwut{rx~uwkZ}bdq|zkjolxx{vkglvucexxqtu}wtdg|^abbc`cmwt{{{yv~}po|qz|{{~j}sgihkkrsuul{wsqv}{hk~xwc{xjjjaX]eoqgyisrpnmpgcxpy|yuyyymdqh{{yso{zc{y}yxlnky}mb]^vz}~ykaacpgVrvxuii_kf`\\|ri~x}|zw{tmhpjnx}{rx}~|zwsjtq}w{yf]e`p|zqz{zyabwojhreYtm^e[hz}uzp}~yqwzvwnflvpzu|t}v|zqkql|zxjaecgxuwvrzo\rtkmxu^czkW\UZ~ikw}tyyvkbjvnv}~{{}}{}noqwzzuuf`mwqz}wwzuyxemqeeonY_xbfgbxt}~siu}w{v~v{zooupt|~~ys{q~uuymu|}whbkqkv{vvrrq\jxonz|if|pY]^Ysv~wmrz}w~xzvkmvnow{|zyzm~wr{ps{{xh`hnjvy|z}jvyifqs`b~qY^^Xpypk|yy{tyvlovnoo~zt}xnurzpx~|yjbggdjlyzvbuxkhur^fo\`ZYouqqsxzuy|vwphkndko~wx}y~rqqrhzzvp``kihnu{~vexwnkvo]g{gZc_`swunqz|ssztrxslrwptn~y|~xvoyuvn}||znjngcgjyfwuifvtckn`d^^qsprpv}}wry~~thmphnlztv}}uxtsjx{wtgclhafg}}m||rkwudg}o]`]]puqzqk}|~v|zw{vhiohkltx~||p}stls~zvuj`giche{pw~showgdzx`]\Wgqmss{~ywyx|vxwkhojhljsyryopim|~wun_bf_diuyqyoiuj^lyd\`[eyyxqu{{tu|vuvlbjojrp|z}~zzytsuqzzzyl_fi\`vl}kxigsxmm}kY]aez}mudrzzp~twnyfYqmt{z{~~wum|}whlg|pwtaPV\_p{{z~yjbxxmynXfZ_}tktpy~wynm{y}yg^hq}~}y|~}vtpufqyzxysrirwdQZcde{y|zhvkcwna]_Tjw}xtu{wukv~|vmipeptz|p~|srshy}uv{tco`Ze[xus}q|ynnts]e~sZ[_mxqyqnyzszrxrq\bnouq{yz{~zp}zq{r||l~tunzc[Yipcw{oyxiqqutoo`eYyos|xx{ntx|zrdfhdbp{y{vxs~snu{jxrwn_nt]^fnzwy{~noloqftbajnozyzzvkp}tqw}n\plqs}{|txtzplzqljqihd_e\o|q}mglrgbvlhgvv}v{t~vx{|slrnwpyd_is~zwsxv|n~uwn{rumnurcRakhp{zuhjdupsnke`|{}p~zz{~numoqsl^fwppzywmvsvsms|v~uykg|vjfdeaatx~srjzjx}l_gpsq|}}}}{~t{skjmnZnnpolx}xw{xm~{sm}qdhbleel^dvu{ymbr}{vpkj]szxsxu|}xrmsyowpqfdxsvzz|qtzsoyu}wvp{qklk]P\tvw}uy|}ogqq{ptjir|}xtz|er{pgawn`bgdh{|t|n}r{xv{|msodbZbV_srxt{psvy}yg\f~zyzzprt}|nle`qmpoiu|p}{xjlznorxxy|}jk_sh_mjnks|}stxsphp]mx|hwoz|zrm}wmq}xroocYkzq{~}vnz{|yywzfkqh\WiobY\gfq}qvn|jut~vz~{}||xznqx~{nnpdcblbj~||~pvz~uow|u~ypv{xebkkc[WU~nwzn}sxdgp{vjmkkp|uwzcxzo|rtverfv}z}|}}}{x{uh{tw}x~q{qkixmmXfvfksvoj}udjyimlu}szz}wp{z~}_rvXjmj^Zfpltjt}{xkysjq}cUXgjpf\alkrm|}sr}{xvuupz}hozh~|xww}ka`tinquwuvwpff}tdvru{{ztw`_{phfvocV[r|{vw|~jvxy|vy|jryitmYUgipqeo{~z~}{xyzzsv|yj{}}osvjqesfiN^c[ed~pqrtvnrsxv}wmx}xaluebjwr_]vvwz{{sz{sr~xycorvaVV_ce`anus{p{ttukk]mytww{olvwuuwmykturwq{||vmlnr~x}|{~{fq~xvpkfqufVT`qdXU||}stinvz}~~wjsssfUbhmsaanroyrqn|roz}}govhmgsyehXigdsqxwxifhm{srwtvzxt[ktddiofTb~xu|}||xrrnrz|z}y~gmpvk]RP[osjg{zxwpzpx{qem}|tun{~mfmzol`atfrntv}|umqu}}puw{t{pybYo{q^XWgcVUiqutyuix{{}~tgxqrj\`hktpdoz}~~ufy{motqdh|szzu}jkgurgcZve^gglt|zspty}pwuqw}x}nnYtmcefd`_u}|~yx||~vr}qst{~{bzsgh`ba^beeYbssw{utzymdxvtrs|wup~yoq^zqaliqlqw}~zvu{|zqvmw~nZ`{yfXY\gYR`qnvv~~}wloov}{{w{{qkbflnskdnrww~zxpovyrbkyzm}tmwjkmriq_ez[T]inu{|v{~~qx|~ryq^rwb]ingZWkprxxyo~kqstv{||per[g[\uulctpsqaolljjxtrmuy}}vmct|vs_|~mlvrz}h[mwg[a{m^jp}~{orYStkuhck_rv{{|pmt~nrx}wgfr_vuo}vxp^qoohp|xws}}|uxjdqyshmxpadp[]k_n_beowtzorvwtfsnjmY^fdkez}spvnnguqgox~}t{}sjqzw{z~}iidY`Sc]ozupfs{{~zrpz}ecrh][Tnzphk~|rju~vvtsy|ynrwnzrwpqwvwmznmakX_hotkl~~}pu{rryjbnka\\rqx|vsw|jxynqnww}}~nhrrqtvuw~zrmqlc^smsw~tn~{lsy|}wpnsgVftpj^]wvt}nh}}xzusmyntw{swqr|z|{tvx}toxgphoogmkmei}qq{zlj}prw|{k_e`Z_dzoyyz~ysvpysp~wsxtokz}ppo~zyyxee\nfnyrr}u~v}zqw{}mTcztede}~tzxrzzyndrjq}ty}letvwy~|synsnutaipsjk}{lqxxqt~tliqsgbjnigi{z{y~xxzqyi{~wq~ynpuuq}znrx{pgef}tuv{wv{skzxswy{~|x`Sesmchq}suz|{on|wx{uqyg}yz~{{ow{{uo~yx{{yw}tjvorbbs{rcg|qsutjy|{nprvmejkkkr{}x||wqz{zwzymrwgis{sq~tmjlzmmezysy{zx}uiy}uwzzz{jVf|yb_fwt}uw{yrnw|uy}oyjs~}{{}pq}{zv}qz~y{yuysj|z~m_mtsjeuwmrttju|vqomrmgfdlpx}~}|}x|~u|~ouyoopgirsoo~qghtvten}tzxrmvpxytlvzw]^t|jaivwt}rvs{x{~qvvmtv||piy|wtkp|{|~lw|xcly{tki~plszyrrskhlpknkjoov|vzw~yywxv}|mpiuysy{}|nkomw}pmx|ykomopg~rv|xyz{delqf^mkn}~p|{}w~~|zrrryrx{{lout|vqw}v}}{u{}{shhtvioyzwwme{}ty{k}tjrusd`no~|vy}|z|~|||xzy~wxvrnl}}szxwsnms|spzyuuynmni|ru~yx}r{{zhbiulhurrrxx}x{{{quz~qw}}{kj}xww}~uy{v}~}w{}xzjiuwgo}vsvmbww|zkwhpuqicno|yy~}~~{yv|zxz|z{wqttsiu}uyys{zsqvyo{xuv~sgudoosz|lw~u}qcctxkuxtwww}x}rxzzwn||u}pft{v~|yxx{t}}|{sriqzjf~zlrpbq~}zkz~klupnhpow}x~zuxys}{}sy|ynnuzkqzvxxqvzwz}p~xtvxfxxeiis|hqsztgbt}pwyxyzw|x~rw|}xl{ypyz|qfuwv}{z}x|{u~pvlvzhhxhpmdu~{yl}kmtpmnvlx{zyuyup{y~wxtztmk{yirtvzupz|wuxwt|rinegeywdzxtzpgd}vsxw||{}wzm|ouxzrr|zz}~wknxnyyy~}x|u|{vxsr|odyjgpen|{zmzskrpok{nm{}~{}uyuw{osvwtuvwphsiopsyvow~z|wzuywj~rgf`r~guzrxrlbzvowu|{}~u{m}pttxro|yz|{xmoxgttx~{w}u}xwzxwpg|iflfp{wyp~tntpok~ljvy||~}u{vsnqow~ztyupfvzfnlsxsoy|wy{v~somidav{~}k}wvxrkf|mqtu~}x~}w}|q|}o}|svnsy}{zvjtserqx|wu}}v}tvz{~mkhhhgvztxrwsvqnlkkrwz|}|t~w~u|xy}us~{_u~y{siswktu}w|zv{rxxqpslq~uyw}otrtljsutt{xuuw|tyq~uv{yz{p|{pvqudowktzj|kql}~v|zruxtmqvzwu|ifhfhfzkwv}|x~~uzp~}py|tftpxplulqsuwp|}psxqyq~zk~rwrwmquqjq{vsv{w~~~v|}z{z{y~z}~|v{tvuydnoerue~lrkzy{|sxzsntt{}}{yynniddh{mu~s~z|~sr{uhu|yzhztvkhwpoqqvnyzzvu~utrzu~gx|~txppwsllvsuu{|wzz}}{}q~xz{||~v{uyuzilvffobrnomwvywxsp|uw|uv|vkzfedurnwn}z~}uzvtzivsvvqyxyrentlupvnpxxsuwtst~xuk|xlusrjvurp|xv|~w}qyz}w~t{}wvwzohwnekbdrkjryz}y}zu|xpw|tyzzlnjhvuky{ky~yyxqwktrmm~wy~vhmtjplunmttsr|zzvyxwl{|rwrtkwvtsy{s|uzyvsw|}x|ovy}y|zyynnxmdi`i|kgfvvx|~zxrvxwsx{xsqkp~ul{}uk|y}ywqti}pll~{vwksrkmkqfoptou}y||x}}ru~t{|zzyuq{tvt}sqw|yxtwuy}z|y~|wovtyu~yoywiee`xtfbi{wp|suxxxoyxtys{sq~ru}xht~u}z|~stmwuniz~tu|}uu{ookpefjnqp{vzyz{|ztvu{~|~wyyu}vntvtrurst}y}z}}|qvrwq||~|w|offbwugagwrku{n{}}zqz|tmqp~xxwxziszs{z~~w{|}utp{xpl|~rpvz{zvupsgegjllxtxwvuz}ywvz~}ci}tytij{oo|rbWejsxz_ld_msntytuel}z{jQpt|xm}qhvujxsg\iqq}eY|pb_b^hZbvvv}um{ztwxztxqllu}Rko||}qxvhiy{xYzkitehjhxqn{y~~ui|wc|~xt~xvzwxykpx{~ehbfaWWsphfnk{~ym}rvw{hgpri\Yp[q}w{w~|~ympwvnzl}w}tdeub{op{p_fegeXh~p|t{qwtu{~ykdpsuvkngk_Okfcj_gvq}i~qninn]fgjaYWd]i~v{xoz{xu|~unl{qz{~lnonwbb~o}u}yyvrrssxwuw{qpt|puoqsYetctbx{mvvwyssryignmm`_doe|{x~u}yex~p{h|v|{xuyysgepysvpu~rdmitq]w{us~wuw|ntswkhi|~qmnku`Ztclch~dyuwiwiliohYbchZZ\rf}}y}sxwqx~ry{zmms{v~lsqxybu~roxt{{rq|~q{}p{{oqu{qoq{tvs|kb{lqimjzoosrwp^fin``ayl|uwz{}lsqyi~q{{ujptqefovzqzxipnvucz~w~s|{~wv{~}}}~|glouhhjtsrpubexgoar|exwtjuimjpdW_eeXZcvh|{}r|~q~tpu{qkkxsvt{pky{s{xqv{qpr{v|ou~{{}sjqwsjl||~y|ug}xrokqyzp}utttucdioc^^tnvxw~{z|{qrvyk{nv{}|wgmrqdbjrx|psqtsf{xzs~z|utyyy|}{}wejopbbkt{|wwteuzomcrlvxlqrnompa\bi`XWkkjt}{yuuzorxyjgmtz|yywk{yxpz{szxnk|x~yvztt|yzz{jkqvieixryzwjm}wrvwuxpchnk^[gsew~|{~{}ypzsowv~t|z{z}nhnsh__tpt}zywvvnq}{qx{~wrxyyy{{zzxzpejpj_^owp}{tm}ummnrvwkuuppoqc_ej`XXmfk~r~z|vuyopvxjekq}{yq|~yqu~rw|yljwrzxvzv|qx|vwvxmflqjbdzy|{z~r|r~}|q|wxvxhgloc]`tfuv}}}|ztzzon|xq{xyxvghnnb]gyj}}zyup}{uxy}tp~z|ytyvvtvlbgkf]`uvu}wu{ouosxtjt}vpqoqe_dh_X\qgou{wv~u|~|}nlqtidlt~zu}}tu~yssxynjstz|snwqm||ursrqcflmddty|}ws|utyvvvrehmk`^nwk|~~~{wuvtzo}wnr|ttttobfjh_avzt}}zvwz~}}vz~yqszvy{ulv}wpqoqe`ejdaiy|x}|qmyvpiv{uopnpbaeh_\fyj}~|yt}wt|xyxwijpqihyz~zy}{wx{~{orwtllz{|txsoxpgmwumonpiagljfnzv}}wo{|uwtvjfjmeak}n~}}}xwzsm}unkxzspqppbchibcut}yz}z{ux|yrtytuwphqwsknknd_eiecmy~|~vo{uokw{toroqdchia_nzl~}~uyqtwwwwsgkomhm|zy|zt~{|tnsunjtv{zyt|jw{ogfqtmjmlmacijfh}~yyrwvxwxqgknich|qy}~zy}uovumisxsmqnpebgicap~q{}yy|twzwpsyrtukentpinjnc^cfa`or~~rumoy{sstsqdfig`buqp{|xy}tnw~zsxuwmglnifrsv|uv||~||opsqjj}zu{u|pm{sicjqphmkng_dhdakt{wt}|x|x{njmmfdsxl{x}yo~|phkuvnpppmacfd_dxpu~{}zv~{twysoyv~|~oy|pgfpskjmjl`_dd^`tuq~vunr{{rwuvrgikgafxlty}wy{rmu|xrxtwngkmheqpw|ts|{z{~qnqqkjy{p{v{tjvuiaclnfjkkj_aedag{uzztzx~z|ulnpkfn}m}|}~~zqu~tjgouplrmqfaefb`nxky~vv~~vtwwqpwzxmxwkcemofjkij^`dd`eytv~xuns{{syvwthjliel{nx{~wv{pkqxupwrvnfjlihsry|}spyzw~x~smpqmkxoz|vvvinuj`^glffkgmb`egehyu~vu}y{~y}pnqpkm|{r}y~ynxznefnqimnlobcggej|wuyzsv~y~stvurw|~~wqkwre^cjickgjh_bgfgru|{~rntzwsztyqjmnkkwotxukipuooupvkhlnloxwxppx|uy{w|onqpmq{u}z}x~|ryzneppc[^fhaihiladijku}ysx|xy~xoqsppzs}z{|nq{pebinijojpgcgihl|~txzqs{~w~~|ssvusx}}~~}u~|phtpc]`ggbkfkj`dhijvv||~rnuzvu|u{slopno}q}~ryrhhoslrspvjjmoou~~~wtmryxs}wzxmoqooyp|}~zy{tt{tghrh]Zagbekfnfbgjkqy}uv}x}}rrtsryv}~~z~rnzsgagmjhqjpkcgiikypzqotyt{|y}qlkkoz|srt~zx|vu}wqbfmwvnlhgtpxxpuwxzw|tv{~u{}uv}nuntin~~|~t||{|}|y}tvwuvs}qmpw|w{vw~~xvpluwyt|z}xu~xje``ro`e`eibfklozr{xz~}}yx|vnhow~z}{uz~}ot|vu|~kbinvmtpgcpqgcirss~x}{onljm|tu}z{zrywv|z{}p`^ifhsw~uqttty~u{wmhcfw{vukkohrqrztrzuibnnhjt}{yy~uwxwysxzvrfmpwwx|ssz~{zxw~vxseV[echw}qlkiioyhjoq{}yvwnmury{v{splkpx~~zxt~x|vpi_ilyznnwmdcjoltxrzpjiggozr~s}|xyuqzsx~z}rwth\akhjuyvxwxzz~oiffn}zoqskust|sryshbnoikt|{zzvxxx{qv|~vsoensyzx~trx}zzxyrqodWXhkmxzqpnmq{rewlv{yutpjvu}z~vv}}|vnmms~z{}tz~x}{skiceuxwmswldflnmxsrwkhhinztxvs{}zxxrsyqz{ztnslc\jtsu}{zz{~v~|xzthgjq}ypwroxux{psvneettoqw|x~z~xyyz~~pqxzvpnkiy{x{|stz~|~yyz}silf^Xeuvzyusrsy~jppowxurqkr|yyzuw}||z|{onpvz|wvvptupigefx|}rszpgflomwuowmhijp|vuyqy|zwxqsypx{y}rlqkd^lwvx~~}}}u}{|~|~txrhimu{s{tr{wzzpuunehvtprx|y{~zzz|vrypvwrmmhm{zxysu{|~{yz||y~}xjjjc[\nuu||zwxyqifqnuyxqx|wvv|vmsz~}}}vp}}}|ytmuk|mrwvqolgstxztnxtbaouq|wrrtzvqvvytyskcltppvtkqnpw{xwuq}|t|{|{}zyzyjiivifmkf`isoovzvswzvuz{wtrktvvrgmnr~~t|wpppt}~y}~{rnup}|yqx|{vkcgxxvsmhe_gkhx~~umuvrv~}yxwyzypgomxwoozvrv}{~~{m^biir}qkgfjsqbwnnsuqmh`gffw}~twxqjfqpjlt|}||rmilu~{ut~w~}~~wocejhzoejldbhpsxw{}sollq{|owvz{wuqlusrvkdmmltywuw}{t||vrztzyzz}xmgdiu|ztklqhrz|~}{tjosnnv}zyyu}wpgmnnwqzzssz~~|qd^hijuvpmmswjfykosrnjdbjcky}{uzvqiksoknuz|{yqmkp{||uxw{zrjafll}sjpqigkqsyv{}tpmnt}{owuxzwtqmvur~}tjcmppv}zxz}s|}{uq{wz|y{{z~wlgekxyrownq|}z{tljtsqu|}|{{{zz|~{tohitr}tv{vy~yyog]cpty~wsru{ygprimpnif`flhu~wvzupipwrorwy}zx|snmp{}zv~zy~}wwunhdbpu||smutmkost|~s{yqons}zssswxusppztwxzumfftyz}{{yyy|~zwrvzz}~yy|||xqvmhhmz|wqvypyzz}vpipywwz~}}t{uwwrmjguz|zu~zy}}}{stng`csy}|xvy~}kmviknmieafojuxw{vrlpxspqvwzxu{sons~{x{zywrrqkfcdtwvrx~{}wrrtzqo}nostqnffomzyy}|uuytoio{|}z~yty~}{yv|qrvzysqowymssljkr~}~xjvlnsqpvtpiiuwvz|wzzz|orutpmhovqwrv~|}yrxuxzswuojhtz{xowu]Z^`_^\blfp}~|ztjjqqty{y{yx{wvxxxossw}xs|~wuwxuros~{pgjluskpidbcjqoassknqplgbiifwx{{vqkswuw}|vx|ytvx}{y}|}shjjg{}xne\`c__dknw|qvysolmrxqksortrollupq{qkrtv~|z}w}vy|tqu|}x|umfagc\ipty{wsvuohafhehpwyw}zxw{wt}zunmslsyw~}z|{{xu|xwjimkp|}upnqsj^snilnmiebieapyy|{vqksursx~{wx~|~}xyxy~z~{~~{sjkmer{shfjfbejnu~tt|wroorvqf{zqrtspmktsmy|ukosqvx|zyx{w~yttz~v~xqiimcdosw{zutwsnfcihgksx}x{yz~qzzz|{vqkorkwyz}z}z~~{xvy|shiopu{vuwxmbwpikmlhdbjfcr{~z}|wrmswttx}zwy}~}yyzy~w}~|}vohhpkrypjoniilpt~zrzytqosxxlrrprsqolqxpt{ulkswzzzwyz|xyroimwvvz~}xrwuvuronvypyuxrtssvz}uoipzukhpqkv~}{uz}|spsywnz~~ztntxuv{{ddvqqoonr|tt{vyvt}{}|~~{lcdi~upkmrqqmpxywxz}~}wtrttrppy}w~xsnu|xuuw~{~u~rquz{}vvvt|ud^uyifjnoomqyqnvvvx|ytz{xx{~~welxsvyot~{y}zvyypf_bfmxsmnnhaabdnsiowust{y{pmkiotqrhbkoqsustz||zwwuoikmotz~}ysomlntmquwonomq{|~wkpsrqonoz{~}~~}zh_tymnxzyxrqt|~{z~zhit|rjoxvvxjhqtz}{xujaupffijjkoywr}||yy{x{}{{|~~zwutw}w~{yyvngdkop~qfdcfmottleccfoqiqyzz|ropijotz|yxwzz~~}v{z{~xsqtz|tuysmgcdkrhcjklmmlnsqpljpqoqx~{xxvxxtqowyu}~~}{z|{vunhcalux}y{zwx{xeeysb^`cehmx~uw}~|~~}{}~}zxy~{}~zxxw{vuopqmhcelgjzwspovvojikr~zv~pzwtuuttx}~{tu|~w~|}|zxz~w}|{vx{vsquz|tw|xtnijsyv||yxwtuyxwusz|zz|~x~xstusqpvzrv||}|~}sutwyz|}yzvplehorv|vlrjkopoot{tpx|~~}}z}ywy{xowzwx~{~|}}|xu{}}}unmkgcbipmx}z|}zxx}xu~stpqqqrx|}zurssqrqw|pxmlpsuw|xw|~}{~~zwx~}v{{yyyy|~|yuy|zyy{v|ooytvwurpuzst~zyww~|z{}~w}}wwxxz~~}|vsnnw}tzplnoonpwyoqxyxwtppqpuw~}x}|}{rtnnnosz{|~}|}|tonmkjnxzv|ywxuw}|}}xjcwumnrtvzy~}|zwvwtqnjouwy|}wtzstwwwx}|zw|~||~}{yy{xuyxvtroqroolnvz{{|}|v}nw{keffefjtxry}~}~~~|}z|~~zx{ywvsxlfx}nhjoqrsx}upwzyxwttwvxyx|~zwuuy|t{~}~tzzrrstvz|{{unmljimwyu~{{xz|||~z||nhzqfcegjnw|v{~|{yvuwtrpmsz|}|~~yz}}}{tz|{{}|s{vwyz}|}}{zwvyxusnosrpppszzqtvovkijihiowsq{z{{|}}xttrmkghpw{{o{xkghhggjrohouwwvtsvuvww{}{{}ux~~~y{zxyxz}t|}kddddekvyu}~}zxy~}rv~zki|rhfghjnxzv}~}|{yz|yxurw|~|{}~~}|~|~z{wv~{zxvv||v|~}|{yyzwurnqtsssuzyry~u}xqponmowxqv|utpqppt|~|{vrojnsuy}wxumllkklsulksvvvutvwuvvx{yr~zzzyz{{}~}}pkigddiojhs|}x~~thqslllklq{y|~~ywrqvy{~{zuuutty~w~}tuzyxwy{xnr}{{zxy{~||}zxurvyxxxz}v|w}zqoonmpwyqs|||x|vutrsxz}|ywxuqnijpsvz}yurrrpqv{tntyzzzyz{xwvtz|zouwttrrt}~}|v~uligedfmoilx~~uky~pkjkjms~y}}}{wtoosuvy|{{zyy}}vz~~~||y}tv{|{z{|xnr~}~}|~}zuwyyxyz~v|vyyolkigiptmlv}~rz{rpppqu~zy~|yvvuqoljpvy}}}ussqqtzzqqxzzyxwyxvwuv|~~~yv}zsyxxwy~}|}{{r~oiggfglutov}ok|xkggghlt}yty~~~~}~|zxtv{}~}}{xxwvx~ytz~~~}|}~|||z~|sy~~{r|~}}}~~|wsvwvuuw|~uqzvyqonmmpxxps{wqtoopqt{xz~}{zxwwurqnpv{~}wsrpnotxqjptuvvwy{{||{x}yowwuvvw|~{}{|r}qihgfgltuou~}pm|pllmnrz~w{~|{z{xvtpsxz|}zxxwwyzv{~~~}}~~}}|z~~~ysy~xq{{{{|zx{|{{{~wwwsrmlkjlrxsnu|~rxzqnnnot}xv{}|{ywxvsrnmrvy|zywuuy~zprwxxxwwywwwvx~y{zprxuuuvz|~x~|rzsjgfdeipqknw}|ol}yniiijnv~zu{~}}|ywtprvxz}~}{zxx{vu{}}}||}|zyvv{~~~~usy||{{{wnn}~~~}xxzzyz{|uz}s{|romkknuvnow|vqtnlkjmszwquz{zywwwsqolnswz~zxvuw|}tpuxyyxxyxvutsx}{}sm{}wvvuw}~~|~~~~xx|qq~rljihjpuplszxmtvmjihinwztu|~{xtrtwy{~~}{ywx{|styzzzzzzwutrsy|~yz~}vlo|zzz{~zz|{{|}}uz|s|uqpnmpvwomuz|~unzunlkjlrzysu|~~~}|{xurnmpsvz~~|ywx|xqtxyzzz{{xwusv{~}~ulsxuutuy}~z{}}~y~xpzyokiggjqrjksy}un{umkjjlrzztv}}zxsruwy{~~}|zyxy~}tw{||||||ywvstx{}~vx~~}{tkp|yzz{||}}}}~v{{szvqomlouwolsx{}~ypxyokjijnvxrqw{{{zyywtromosvz~~|yy{~trwyyzyzzywwuuz~{zokz{uttux~~{|}}}}wv}|ro~skigfgmsqkow|}qo~rlkjknv|ytw}~~~~}zxuqquwz}|zxvx}wptxxxxxyxwtw}~vkaiz{ogeegjpzzz|wxzz{}}wz|zv~|zz||rorssrrsuuuvvx~xnkz{xwsmkwlr}~|}}}}~z||yvtuy{unqvxz{{{wrszwnrwollmou}~}}zwv|umwzussrsvzxposuuvvxyzz{{|{rq~~skgfjs|{|}}}~y|~xsnms}zuy}~}{ytlgo{zqkjjlot{}z{{y{|||}~|trx~{zyz}{vwz{{|}|~wkecelx}|yvspmnoppqsvwrpuz}~~yw{yxwx{}{uuwwvuttuuwz~}vw{wwxy}~}zrlou|zuqonoqpjikmnnoprrrrrsw|}ut|{rmkknt}}}~~~~}zvsponnrw~~~~}{xupihpwsmiiijmrvvuy}}|}~~}}}}xstx{~}|~{y{}}~~{|rkgeglu~}yuqppqqqrtuspquxz{||yw|~|||}~~zxxyxwvuutstuy{x~{yyz|}xqnpsv|zvtsssplmnoopqrsssssuy|zvx~~xrnllnsz~}zwtqommnrx|zwrnnuxupnmmnptvvvy|}|}~}}|||xttvx{~}|}~~|~|tmiggjqy|xvvvuuuvwvssuwyyzzxwy~}|||}~{yxyywvuutsstv{~{}~|{{|}{xsonpsw||zyywsqrrrrrsttssssvy|~}xwz}{vqonoqv|}|zwtrpooorv||xtqqvwuqoooprtvvwy|~~|||}|{{zywttvx{~}}~~~{{|ytnkklou|~zxwwvvuvvvtsuwxyzzzxy~~}||||}|yxxxwuutttuvy}}|}}}}}zxvspoquz|zzyvssssssstuuuuuvy{}}zwvyzwsqpqsw||zxusqopqruy~}zwtrsvwurqqqsuwxxy{}~~}{{{{zyxxvtsux|~}~~}yvvvsomlnrw}}zxxwwvvvwvuuwxyz{{zz|}||||||zxwwvttstuvx{~}~|zwutrporvz~|{zxvuuttttuvvvvvwy{}~~|ywvwxvsqqrux}|ywusqoopqsw{~|yvuvxxvtsssuvxyyz{}}~~~}|{{zzyxwvuttvy|~zvuutqnmnpty~|{zyxxxxwvvvxyyzzzzz}}}||{{{zxwwvuttttuwy|~}{ywutrpopsw{}|yxwwvvvvvwvvvwyz|}~~}|zxwxxwusrsux|~|yxvtrpppprtw|~{yxxyywuutuuvxxxy{|}}~~}|{{{{zyxxvuuwx{~}ywwvtqonnorw{~}|{zzyxwwwxxyyyyy{}~}}}}}}|{zyyxwvvvvvwxz~~}{zxvtrppprtx|~|{zyxxxxxwwwwxyz|}~~|{yyzyxvtttuwz}~{zxvtrqpppqruy~~|{{{zywwvwwxyyyz|}}~~~}|||{{zyxwvvvwy{~|ywwvtrpnnoqtx|~}||{zyzzz{{{z{|}~~}}}}}|{zyxxwvvvvvwy{}|{ywvtrqppqsuy|~}||{{{{zzyzz{|}~~~~}{zyyyxwuttuvx{~}{yxvtsqppppqsvz~~~}|zzyyzzz{{||}}~~~~}|||{zzyxwvvvvxy|~~|zxvutsqooopsvz~~~}|{{{{|||{{|}~}}}}||{zyyxwvvvvvwxz|~}{zywvtsrqpqrtw{~~}}|||{{{{{||}~~~~~~~}|{zxxxxwvuuvwy|~}{zxvusrqppqrtvy|~}|{{{{{{||||}}~~~~~~}|{{zzyxwwvvvwxz|~}{ywussrrqpqrux|~}}|||||||||}~~}}}||{{zyxwwvvuvvwy{}~}{zxwvusrrqqrsvx|~~}}}||||||}}~~~~~~~}}||zyxwwwvvuuvxz|~}|{zxwutsrqqqrsuwz}~}||||||||}}}}~~~~~}|{{zzyxxwvvvvwyz}~}|zywutsrrqqqrtvy}~~}}}}}}}}}~~~~~}}||{{zyxxwvvvvvwxy{~~|{yxwvutsrqqqrtvy|~~}}}}}}}~~~~~~~}}}||{zyxwwwvuuvwy{}~}|{yxwutsrqqqqrsux{~~}}}}}}}}}}~~~~~}}||{{zyyxwwvvvwxy{}~}|{zxwutssrqqqqsuwz}~~~~}}}}~~~~~}}}||{{zyxxwwvvvwxyz}~~}{zyxwvutsrqqqrsuwz}~~~}~~~~~~~~~~~}}}|{zyyyxwwvvwwyz|~~}}|{zyxwutsrqqqqrstvy|~~~~~~~~~~~~~~}}y}zyzyxxywyzy{zzy~}~~~yy{z}z{wxxwtvuqsqppssw}|yxwz|z}~{{qoxvkmnpprrsz~z{~|vu|~|wt{zyyxxz{wupmqtroursstw}y}~{y~{vs{xv}yr{~{}vm|xpw~~|ztvwvzwtso~~v|upmqx}|z~zsv|~x}{z~ux{z{xyosyqt{}|zxtx}{}u~|zw{{~wvvr}znsyzyz{tyv~xyxvy{{xuxzx|~un}z~|y{{~x}ztxu~vxz{suuty{u{xp|tooux}nmxuvz}{yrvz{y~tyvtsjou{~z}w|{{vx~}y{{wmswpzyxzsu||puuouvrqtqhmrnnovuwzx~~}xy~}~xxw~x{ztsnow~}|}{zrt{q||nhfu~zy{qllkhv|~p_dpmrprrq|zyxxyy}w~}zrt}stpssvxu{}}|r~}}vlhnei}wnrqwywpkklmx{quwoxr~wzwxqt}~s|~qp{{ysvx{||}vz}wt~vuzz~lgsshku{xxkwxvyxsrzomntws~zzp{w~{qw~yrz{ww{zvt}xx{s}|zur{|~}xw}~zx~qemy}qptu{}uqzxxqnmu~qosw|ot}yyqr{~ryxy}y~y|uxxvuzws~|s~w{~|r}{|}jip}yuqx{}svy}yojlsxmmvysm{}vpr~wo|}{}y{yw~y}wuzumu}vt}yy|~~}zxjl{{uzu~~|yrs{}slenwrj|wwlp{v~srtz{{|xz~zztvvwnp|tz|{x|~zxrlrxtvv{||vsr~ujjrwhnuvpvw{{tv|sy}t}v{|xxyyulpy}v|zx}{zwzzu~powvryq~~xqs|uftwthyuxour{zszu{x|}}w~{zyuxy~vntz}~}}~sy|w~vuvopurstr~pt~vlxvufy~rxqut{znz}vzzv~xz~|}}|ryurs|x~||}sy{{|ts{upqvtvuvou}umxvwkwosrpr}}wpy|zxv~}{w~~~ymuwvv{}}{||tz|}}wrwtpntsvy{~tu}ymuwvou~mruns~~yrvzyxy}u~}~|~~mr~}xxwz{|{{{v}}{|yqvurprrz~}{s}}{nuutvw|ulvom|}|xv~{u~y~zy{~wzrlv~zz}{}~}|v~v|{|w{uuqstppv}s||suvst{yyrrvjwzx|~}uz|w~|{z|ww{znr{|y}{~}}~wv{{~y|s}zyutorwrov|u{zwzxss}}xuoxlr~~{y~~~qv}vz}|wux|pp{~||}{v}}wzzz{~|rzxxvslpxvps~}w|xy{ysq}uxpxpq~|yz|~qvxz{~|~yuw|tsz}~z|zyw}x}yz||{rxvvtsjoyxpv~}z{vz~ztrqvqvrtz{y{}}pw}y{yy}wvx~xuty}|~~x|~w|zy|{{y~svuvupipzxpz}yy}zysuzpwrvvxyzx~}{q||~~w|s|xw|{xvrx~|}tzz|}~xx~{wstuuwniszvt}zwzzxu}tqwpwzwzzz|ut}|~t|t~xxy}ywtsw}{|w~}zz}w~}~zuqsuxsjnvxszy~xz|{vyovtq{{xzy}{|t}}~y{zpz{xy}{ywrs||x{z}ywy~}}{xroswvmkqwuvwwy{{~xwvpxotxyx}{vy|}{|v}q|zy{}xywrtz}y|{z{~xz~~zvypptwslmswtyt|{z||vzsryn{uxx{~w}}xxvt|zz|zyzvqw|x{|zy~|y~zv~uzoptwplnuwwxr}x~|xrtyovwy}{y~}~x}{wt{{{|xyzwqy}{|{yy|~~xxs|s{pptvnlntw{ys}v|ytszqwvz~{{~~w}|}~xtz||{xyzwr{}}~}~{xyz|wv~r}pzrnsunlmtx~yt~v}}{vqyt}zux{}~}x{||ztv}}|wxzyty~~~|{wy{zxzw{t}nvuortolmrw}uvz~{zqvy|~vv}{~~~y{y{xxr{~~xwz{vx~|}{}ww}w}~v~ywxusowrpspmmpu}x{v|}}}vqx}~{t|z}}|yzwt{tu}yvz|zx}{z~yv|yxx~~w|u|xorvoqqnnos{|vy{}~zsu}t}|x{|zzvztxwq{{wy||y}y~z~~{v{yw~|}~x}~|uyxrnvrqpnopsz~xyz}|vs{w||wy}yyvxvuzrw|wy|}{~x~z~~}~|w{yv|~~}~y|}{xxutmutqonpqr{~yyz}|ws{x~{uz}~xztyvtzsw{wy}~~}w{||~||x}xv{|||y}|xywsuluvrnnprs|zxy~~|wu{zyt|~|xyr}uu{tx~zxz}~zyy|~z{z|wv{x{|~|}w|rttmvvpmnprt~zxz}|v{{~}}}vu~xzws~tv{tz}yy{}v~z~~|{~{}yvw~v}{~~|~yzzouqoxvnmoqqx}yx}}{w{}~ytz{xzr{zsyyu{{yy|}zxy~~x}}||wvy}x}{~~~~}|wrqvnsyrmmoqt}||x{~~z~|}|uv}wztv}vt|wx{zyz|u}}~zz~~~xvw~}v|}~~|~w}{nttnwxolnpqx{~{x~}|z~ytzzwyr{~|uu|wxzyy{}zx{w{~{wvwz}y~{~~}}{wvnusoyvmlnpszxzy~x|vt}wyvt~||tv}xwyyz{x}~~}w{~zvvxz{{||}~{~yytnttoxvmloptywzx~zuutwtzwp~~zuv~~uysv|uqvz}{wx{||~|xz}~zxz|r|zvsx~|}}z~vy~smsvrzzrqtuz{y|{~}~zu}xz~}wkryzspuwqywy}}|xy|yvvxuty||}{{y~wyuoqxsw|vsux~}xvqow{xy{w{r~}ytu|wyrtyrtrlw{~}~{y~zsot|wyxsonlrnnwpifopmsqlmotxs~ztyqmoloy{yry~~{x{}v}{z{ttuzxsqqw{yxs}{w|}xxtv||}z||y~~xw|}{xwx~}ryys}ytqv|{xunvrnrtr||}~wrortsxp}~}vpkoqlutnigpsmrsoptyt{~|~~yotoilqqxvx}}|y|xuz{ussw{tqpuxv}sy{ty|x{xt{|}~}zwz}|}|zxxytvwxyzsqv{|zunvrmpvvx{~~uqosqqypxzrjppnwtolhovoousquys}|z~~vpumilrtwy{~~}}zzwx~}ysssxzsqsxsw|qzxt{x|~|xv}{{~~xwz|zz{zxtv}zwqty}~~|{ptvonqwwx|~}~{tqtslutn}zoormwwrolksvmpvttxsy~|z|}~}{rwqkjotsw~~~~|x|zxzsrsv|vstyrtzrszuzw}}zwz~x~}~yxz~zz~{|}{{stz|yrty|}~|~}rwwpoqwxtz~z}|~}wuxsmtplu~strq{xtqnlswnmuvvvqz~||z|x{~{wzsmlotrpy||~yxz|}}zsrsw~~xuwzrtxrryz}|wzu}}{yyxz}}zyz|zt~~}~vq~~y}{tty{||z~txxrppuzurzzz}|}{}yx~|vtrnxzvtsqqxwnqxwvkotsyuwz}yv}|z}ntspoqvzur{}}{urz}t}wsqor|xvzvrxuvuyzuz|~{{}sxlikmqtoottz{|xxxvtv}~zusuzzu||~vqtuqgbgcfhkvupv{|ut~{zywuwztqy}wmrsr~}yyz|~{z{xvy{pigjomfluuz~}|{{}~{upzsw}tlils|ytxtx{yz{~ptvtzurppontx~~|z{~xsy}tuvttuvwy|xwz{x}}smmt~zopjipmnvsow{{woy}zzyzxuvyspy{opvsy~|||}~zzzwwxz|}uporurlpz{|{}{yz|~~{tp~~swxrmlqzuxvs}{{}~xztpxu{zwtrqtzuz}{~z|~~vvz{qrwwvvww{yy}|z~}{zvomr{srniptppvts{}}|qv|zzz{{xtvyss{uqxtw~}}}xsxywwxyzustvwrnw}w}{xx{~~|vr{}xuttpnry~wzvs|~z}~{w{usxty~{wtrwvwy||}~~xyy~}rowzxvvv}{|~z~zuuqoszvwnlswsntvuz}{sz{z{|}}ztuzwtxtwxsx~}|ury{xutx}ywxxxtpx{u~zwy|~~ztw{y{trtqquz}~{{tx|x~|}{wvtysqy~yuut}|x}}~z{zz}zpr{|xusy~}|tssqquy|~~vpuyxpnuww|}~uz|zz|~|vsx|wutzzrt{~|{rw}ztpr|~~}|{ysu|t{wx|~~{vzzxwqtusuxyz~~vy~vy}|xsw{rot{~y{v{}w{}{}|z|{ss{~ytqx{{|~}usutstuvzzvz}{smtyxy}}}}w}|zz}}xtw~{ss}{rsw|}~uv}|uopwv~~}yv~}v}~~|xx{~~{x}zv|qrwvz~vtxzwnkuywy|}}~~|su|tu~~|ww~vtvzvt|~~}no|js~~~zx~|z~~~}}yxxvz}|~wy|~{rpz}~|}{lp~wuvxyxuru~{wvvzxv|~|~~wwzxuvz~||}{y~}wqqrw|wyrmquxvnkv|yxy||~{~|mu|uv}{w{~}{voqsrv{xwustvurstxyuy{xy|~~|zx}qmtz~y|zu}~zhlvsv~{upszvuuoswtv}|y~||wuxvqtyxx}~~}x|{pglv~|ssuvvrlnx{vtv~z~|}}yx|~pnyyy}~z{~}{tstnlrwy~{wuvyxsoxvsz}yxy{|{|~ux{xomw{{}{y~xwyqhpvy|usyyuzwpsyyy}x|~z|~ysquvqsz{~~{xvwunhp~xuvvvvsoq{}vsy{}|{}~{wwyvptwz|{}~|smpsnmu{zwwy|yor|sv|~zxxxyyzvvwxunr~~|~}}~yuuqmprxyv|zv||sqx}||}~x{|wz}}yrnrxuqv{~zxvtsqlo|{yyxwvusry~xsy}y~}{|~}zxwvttusy}~}zrlotqmr{|yy{~znt{tv{~~|zxxwww|{vwuvvqs~~~}yurosqox{~xz{sr{}{|}zy|wv{}{vpntytpt}~z{yvrsrnr~~}|yxwwutz|vv{wz~{{|~~}|zxvruzrqz~}|uontunks~|~~ss~}wux{|{zyxvuu{{wyuuwurx|xrpxsjr{{}~wsy{z|zz|uvz|{vqotzwopz{}{~}|yttuqqx~}zzzxux|xxxt{}{{|}}}|{zuqy}qnw}~~~}{y~yqs}|u}ywxvy~}zwvvvusqryzz~|~|vvtx}}{}~|vu~xis}}|~yih|{mgfkswqmu}wv}~}z}~{|z{tstpnqttrlq{|zyxyyzz{||yqs}voy}z{}vox{pjjoqjfovrv~{vvty}{yxwtsx|~xzxw~}xtw{}ysz~~~|ulpztowyookjt~wrqtvpkswru{{{}{{}{~|~|v|~yrqvxrjpyzxwwwwwxyyyskp{zuz~}|uwvt||wtuvrkpusxxyzu{~|{zyvty}~}uouy|zw|}sx~}|||{uklxzvx{zysmpqpw{wttwwqqvtw}}|}z|}~~{~}~~ysyyrr{{onw{zzyyxwwxyyvmkw~~}~~~~zsv{z~}xtrsusqsswyx~y|{{{{yvw~|~~}}vos|yu~zu}~|zzzzwnjs|~~}|vurmpwwz{xvtsvwutrv~~}zz||~~|~z{ztx~wqwwpvz{{{{yxvvwxvolu}~{yvrv~~|xvsqsvvtpvxy}}z{||zxy{}yy||vpu~{v{|w|~{yxyxumlv{}vsoltz{}|yxutuy{vpv~}w{~|~|}{}xw|}xt|yty~xuyz{{|{zwvvwwtmpy|~zvrqz~|zwtrtx{toz|w~}z{|{zy}||~}ww{yrrz~{x~{|~~|yxxxwqlsyz{uplpyz{~|{yvtw}|qq}}wx~~}~|||{ux{xsyyy}{y{{{{||{ywvwwtnpxy|~~|vpox{{}{xtty~vnw|w~||||zz{{~}xvzyrrxy}~{}}}~}~|zxxywpnuwv}wokpvux~~|xvx~rr}|vy~~~~}{~}}|uvzvsx{|~}|{~|z{|}{ywwwwsnrxvx}wonvww|{wuzzpv|x~|||{{{{~}~|vwxsswwy}{}}}~}{yyywqnuwtx~~}sknutv||xx~yry~ww~~~}{}}}}zuvvsvzz}}y}|{|}}{ywwwuopwwuz~}|~}slqwvy{ww~xsyy{~||{{~y|~~~{utsrvyw{}w~}|~~}zyxxtnqxwty~||}rkpuuy~|yxzvz|vx}|||z}~}||}~{urrry||w|~{|}}{yxwwtnqyyvz~~{y}}tmqwx{|xw|{wy}z{|{z{~z{~}}}|}~}wropw}|~wx}|}}|{yxwtnpxzwx}~}xy|xopwy{}zx{}z{|~xw{z{}{z~~}|{{zyz{ysoow|v~{{||zywvtpox}{z~~|~wuyzsrx{}}zxy}|{}|}|z|yy||y}~}||{{yyz{uomu}u||||{zyxvtonw}{y|{~yuwyuty{}~{y{~|{~|~~~yx}zy||y|}|zzzywvxzwpmuw|}{{{zyxwtppy|{}{|~xtuxvv{|}~}{y{}z{|||}yy}{z}}|{{{zwvxzwolv}w|~{{zzyxvsnpz~{z}{|ytuwwy||~}{{}}y||}~~|x{}yy~yz||zyyyxutwzwnmy}y~{{zzyxvrnt}~{|~{{|wstuw{|{~~}{|~zx|}{}y{}y|}|zzzzxuuxzulo|z{}{zzyyxtoox}{z{~{||wttvz}{{}}~y{~|~|xz{y}y{}|zyyzxvtuyxplu||{zzzywrnt||z{}}z}}xtqrw|zx{~}zx}}{{z|{z}}{zz{zxvuxztloy}|}~{zzzyxtnpx{xxz}{{~~ysqt{|xx}y||~~{w{}z~~y{}|zyzzyvtuxxqlrz}{{{{zwrnt{zxy|{{~~{upou|zvx~{x~|{z}}|z}~|zzzzyvuvxwpnty|~zzzzyuonv{xwy|~{{|wppx|xz{y|~|wx~}}zz||zyyyywustwvppuy}{zzzyuopx{yx{}{z~}zunmw{xzzx}~}y{zz}}{yyzyxuttvvrrux}{yyyxtopx{xxz|~|y}}xpnx{|{y}|~{wy~}~~yy||zxxxxvtrrttsuwx}|zyzyvpqy}zz|~~yz~}|{xqls}}{w||}~z{~zx|}{yxxxwusrstuwzy{~zxxxvqpw}{z{}~~{x}~}|vpr~~xz}{}}yx|{yvy{zxvvvvtrqrrtx}|{|yyywsqw~}{|~}x{~~|{{zvpq{~xy{|{z~|~zvy{zxwvvvtsrrrty~{~|xxxvrpw~}{{}~~~yz~}}}yss|~xz{z}}yy}z{xuxzywvuuusrqqqsz|~zyxwsrz~||~~zz~~}|{{{wrs{}x{{z}~z{{|xvy{zxvvvutsrrqs||}}yxwuqs{~|{|~~~}~z|}}}|xtw~{y}zy|{x|}y{}uvyzxvuuutrrqppt~|}zywtrw~~{{}~}~{~}~~~|{{{yttx}yz~~{}|{xw{}}}~zzywwusrvqonmotvv{~{{xxxyz{ywx{~}r~{nlmpywtonqvxx|{|~|{{}~~uv~~{z~}~}|~|yx|uw}ynx{xzvxz{z}}{}}~}tx{snx}~}|~|xuuxvoz~xx|vt|~txpkqwvyxx|}ystz{s{}}|~xuuz|ovzonrrptu~v~|~~xpswztsykmpcekmkkfjs}{~|nwqr}~~~~~~uswywumt}}{vu{owss~}zvqx|zxws|y~}zxpqr}tqw{}{x~~wms{~}v~zyyrsrwxwy{yz|uouy{|{puswpqsls~zwsvt|vurrmkogfpsov~zz~|||tvssp{|zrvxnmspisw}x}wxwtms|vt||povxz~~yuxx{ut{uq{~uvz|~|y|rtzqq{~y|vzyy{vwyzwz{rnvvnu{tlqopnwwx{}|}wvusu}|ssqojosqqutq}||zwz{vzx{y}~~}||~}{~|yzorwsmvyy}z{}{ywuosz{rvwy}~}zsvzouz|}|}xtzy}|spsysu}~wu}wqy}trxutr|ywwxou|xpwwvwtpp}}ptqqnzzx|vwspis{wvzz}~|{zyy|vsw|~}|~}uuzsuzoms~~||}tvtxtwvqoxwp{{x}|yzyypu|{vzy~xy}yyurrlluy}~~wt}vx~z}yvnu{wsz{qotwuzwzx~z|~|z{|{u{uwvxu}zy|vzvxvytt~~~~}xw~wowypr{{|~~}|}}|xrw{uquv}u{~wr{vuq{zvxyrwz{s||us~|xzx~{y{wotvqsxu{y|w~usxyx{xzwyrv}{twvwz|rttx|||{}ysuurvtz|uutvyvyvw{zzz{||{urzz}y}|}}{|{smptzkhuxw{ywuwvqv~urw|t~{y{{z{|~{{vvxu}{|vtmtsq{yvtuwvyyxypxvwvz{~yy{tttsttu{}}~~zuww~~nmtwy|}}www}{wy~u{|x||}twr}txyx|~z~xy}}zzwyyyv|sstx|}xx{x{r}{}zsxr}{pmmnwuw~~~~{|}y}}wuwso{z{}u{{yuytvtsrntzsq{~xz~ytxz~~~wyvt|xptkjuppxzsyx|z{}~wy|zty|syz~z~|{z{}}~wsvypxzptz|}z||x~y~{zx{|}xwzwp}|stwtx|{}}}{uuwu{vmrut}zs~wu~tu|qttwzy|~wwyp|}{}{}~z}v}ovvrssnyyzw~{|~~{}y~~~yv{wsyumx|s{{}|}~{zx}zv~tszsrvt}t}|{}xy||t}ywrqywuvy~}~{~|xxxvwpqyry~~||zxyunztwuu}xyzvuv~yz}}xwyssqxukovw|yw~z}zz}z~{z}v|zw}sl~tvw|}}|{~ux{xttqsnq|yx~ywyz}}}ttzzzuyyxut~~w{{zz|{{|vyqvsnuuz~~{}xsvvou{|x}z|ut~{zyxvzz~~{~wwvqq{vntzw|vn~{}{}~w}yvyrtxtso{~~z||||z|ww|ssvt{qvytt~{|z|{|u{{{~uxvuwwyus|~{|~|~xwqtuqqvtzy{||r|ussqz|u||u}x~xx~w||~{~yuutxxpqywwrku{{{{w~|y~uvyrsnx~||~{~{|~{||wvyzzotrqqwxy~}x~~vzy|y~}}zr{tzy{{y}y}y}y~}~qss}zrljlqmutw~~uvw}|s|~z|vs|}|tpzvy}{z{y}}uz||y|}}{wnn{yvt{{}uyqrvpxzqmmot|{w}~yz}|~{}|s|xw|wvtuu|}p~}{}z{|y~~x|~uxwzpqyz}||}~|x{~|tx{qtvpjnqy{z~y{y}zz~z{||}}|u{tqy{{zzy~}wzz~t~vs}tsyru|pozyz{yyzyowxrz{urtnw|v}z{}||x~w{yzwqmpv|zsky|~u~z{zvswvy{vww|uyx}yx|tzuq{vwo{xszuow~~|u}|{rw{ytryurzz|||{y}{{|{{rtvusqtszowz~x}vz}{vx|{wv|y}{uvz}~}y||xuyvmh{zlkqnt}|||}y~{|z~y|wsuzzu{wx{~z~~~uzx{}tswuqtvrqyw||zt{{vxyqz{uu~{y~v~}{~~{~yx{{ty{xuxpmxsquy|~{}{}~}zw~}wyzxz|tzy~{}~~xzxwt~{vv}tw|thrzvsy{~z~|vy~{uwxnsur|~v~~{}}z}zvz~xwtqxsunmtzxx~}{~s{x|zwt|zv}{||}~x{~y|}xpwrmopkqvtw~{|x}}wtxyxyxz|v~~}~}w}zzvrxysvqnqvpsx}}}~~t{wywt||wy|x|yq||~}}zx{~xxyzv{ppwroqzvnv|}}|{z}|vy|zwxs~z{{y||w{y{uy~xz}}}ulu|towvvt|{|v|u|xowvswtt}|~|{x||vwtv{ssoqtwpsz||||zy}|{zvuvyyy|zx~z{v{sryppqlquspv{z}{vuvtvsowvsnmnkpyzuwztyzyv}~~{{~~wv|ljotxsrw}{uuxyniz~~x~s}|~xqqvrw|ty|n~rsy}{}}~|{y}{sowztps{yqo|{mqvx{vpqzzpv}rw{|~~{z~ztv}vvkv}z{~{~xvt}s|zxwzyytxtsztpz}||zz{zlz~}umq~rfs}uxwp|u}}|~y{zqorpz{}~ptqkqtpkoi|ullnomqjvnpuwujx}|zzyr}yyuo||q|{tx~z~wx}yqqrtnikozx}sqr|rsltnmsxntrsuwzs~~rt{y~||~}wzt{|nuz{{v}{jp}xxttwu}zsvx{pjkwz|yxwoms|pnhxydkuqjqlnrxnmx}umwzwyyz~}x|o{~y{}r{}~||}~qmrywkghzw}{tp}ropxis{urvqr{os}uu~zx~|z}~{t}ux|z|{s{wix{xu{zp~~tpw}wniky~|}yvomz}mjpzudnvnorllyzjpxypv{|zw}~y|~x{p}~}z{r|z~|vzniovrkhgy|vq|sky|zkpxrvxqp~st{}uz{w||}~}~~wsx|{~||twzmwyzv~y{v{}yzvlpxuonj}xyztmx}scvwtnhrmruojvrouwqvv~y|~}w|{yt{{~y~x{{z}~~uo{vhgoqmojw~w}qx~}lrtwnqru|wpsvx{vw}{||zxw~{rvw|ztxtwvyvzvw~}|rx{pkosqsqv}p~{qryxogsnqkloxunl{~vrurrw~z}wy{|{z{~z|y|ys{~~|xnqumhinmsquswt}zwm|oxpns}{tp|~yyuuy|~{}zpx}uww~~}vt}x}wxtyyq~|~|rtvqnmoowt{p}ssyusm{xjtohpyvpmx~{wtorxz||}s}{}||{}z{v}yq{}x|umpomkjjmus|vvz{xww{oxskx~zts}|wqxy}yy~}~uox{xyzz}vyw|wwq}|vq}}|~}wrsprpmlrvxzx~}syvsuwzskvljxxunsz}}xpmz}uxzut~}|}~z|vwzuw}{zs|{wppllplhisr{||{{|wx~}|trymv}{ws{~xpu|z}{~}~}yot|{zzyzz{u}{vo{zurz{}||}xurmssniqsw~}}|}w{vty}vvmtqmyvuoux|zomz}wx}}vu{~{|z~sx~xuzyz|~zt{ywrrjkqogippzy}|xyyyqysx{{wu||zou||y||~|zrsyy|{y}|}t}xoxxut}zy{z}{xwskqvqioqu}z~{~xuzwutourwuvqvxyrlw~z}~wz}zuv{}|wxt{yvy}zw~y|wvvtsmfnrmfnmwzyzyzxzry|{xyt{{|uq{|uw}|wssvyy~zvsrwvtz}zx{xxvwnjqvpkqo{{}}{~~xwwswot}wturzxysoy~~|~ztv}zwuw}}~yxuxww}x{{~}~}xrrrskekrojomz{}}|{~u|vt}wxu}{{xs{~yxzu~~||xvsrw{{vxsutu{y}}}uttvphlsupsoz~yzzx~ruxn}suqyzw{zrw}}w|~u|x~{zwtvywwvw{y||wzt|unorofflrpsou~~y||u}wx{xty{|xy~{xx~xz}}}xxwsp|~~~w}}wvstx}{{||wwz}rpsskimtuytw|u||z|}rvxt{vqu}yy{|wy|~}zu~z|{x~zz{ws~{|zvuz|x~w{pwrzplopifipsytu~w~~ww}y{sx||}}z~zv||}|}vz}~}wxyvqz}~~x|zytty{x~y}tyt|torrkilrw~vz|tz||~utzz~xox}zyyxy}~}}wvy}~y~vz~}xz|xu}~|}}{uv}{x}{wwruqwqlolggjov|t}}|x}xxzs}~}|{y}~~vyx{y~tx~yvxytuz|y~{||xtv{z~{z|vzuuysprmjjnr}{x}vw{}|uv~|st|{{xvw}xuwxw{|t{yy{zv{y|~~~xv|z{|~}w~~yszyqsuoolhgilu|u|{y||w|}u~~~|yx~|vyu}vwu}yvxyuwvzy|{}zux{x~}}{}w{|xrxvrqljjlr}y{~|yx{~wvztw||{xtx}}uwtyvy}tx~xxzyxzv~~~~zx~zz|||y}}yvwxtovspkhhijtzv~y~{{~y}|x~~~|xv~{vysu{{syzwwywyuwy|y}|xy{xzy||y|w{urxvrlkjknyz|z~zy}}wx~|x|{|ytt}}vvvruz{t{zxyzz|u||{~{||y{v{|~~}wzsxsnvuoihhimxw}~z|{z|~~~~{vwvxvu~vx|uy}ywxxy{s|{}|x}}{}~zxyv}~z}sxwovyrkjkloyy||y|{}|{}}||yts}}twus~xu|ww|zxxz|v}~~y}~{xz~|w{{}~|}{w|qrvnpxrjhijmwv}{}~{~~|vu}wwws}~{ty{vyywwyzwy|~~~wy|~{xx{y}w}~~~xxpwtoxxokkmowx{yw|}|zvrwxtwqy||xs{zwxyyz|}z~yy|}yxy}}x}{}~}|z}u{ynpunpxqjikmtv{{{z|ytw{uyqx|{{sx}wuwxyz~yz~xwvptmkotz~}yvuw~||~~~~~~wsssxxz}}}vxmvyvx|{|u}zvsv|y{{{~}u|~~}w|tsv{|{||x~|qyzsqxzu|{spqrxvz||}w|wsu~{}w|xuz|tr{~vpnryspu{truonqw~zvvw}z}y{{|xy{{upnq}}~vnxnkyzuplkqywxvs}{}zzvx{{|~v}|~yz}xtwz}wqvwrutnkkpqn{|{~|{{yv{{zy}|vuux{}y{zzzxzur{x}x{|qo|xosmjlpxytst{wy}~|xz}|z||uqopvw|xpv}xqlkpv|wxur|yz~ywuz{~~w}|yy~yy|}xvyzzz{yzxtvrnmrpnzy||{zwuyz}||}}|wutv}~|x|~z{||}ttxqy~~upystlkkpyytsu|~}ux~|~{y|}{y~~{}z}vspps{~|yx~}tmlqvz~}xztr{xx|{}ztx{~w~zu}{~yyz|~zx{|zwsv}vwwspspmywz~|zvtw}}x{|y}xvuvz~~y{~x{}xtzqrzs{xwplknu}vstz}y}r~}zzy{{uz~}{}|{urqrvz}~~z}smpux{|y{zqyzvx}||ysw|y{vx}|{yy||{yxz||yupxwvzvtunryu||yutw}}tzyx{xvwy|}~~z}wx|~uwzqsyz}~}{qmmov}xuu{{w}u{wvw{{tz|z~}~y~xtrrtxy{}~~}xqrvyz{x}|szzvw{z{}utzx{xy|~yy{{utw{|zxrv~uw{yxpqyty{vtvz~ysv{ywvwz~~}~|}}~|uv{vt{uty~~{{xpnos{|xwz}v{~zy{~trw}yx}{~{|~~x|xzvruxyyxxxv{tppuwryyv|y}~~||~xyvnkqx|}|wv}|{~yrzwu||xvutxzxwvwx{}z|y}w~wnglmkvtqvy}}zxsruz}{||rqvyyosz{x}y~xuz{rhdefkqutsw~y|{y{yz{{zwyz{}uqot~~xzxx}{~stx|{kimppokmroz~xv~wt{vv|}vpnpsy~ywxz}}ysqw{vnounnqsuy|wxv~wrrv{}}{zxyuntuyvy{~~z|trru{vrpqtwuqpt||w|wvz||{zwu|{x~yrpqw}|w|~tqvtwxzzvqnrmp~|~xy}v|yuy}ztppqtx|xxz~}|{tqsyww}urttttvzywy|~{vuw|}}||{|wosqs|z{~|w~wtrrtx~xwwy{wroqx}z|ztz}{wv}zz{tqqu{~z}y}~~tssox{tstkp{|~|~{z}zxvz~{vsrrrsx~{z}|z~}vssv{}~{zwxvtrrv{zy~}}}zxz}~||}~xqupq{}~~}{vzvtrpr|}}|~|uppu}|~|{}ux|wv}zz~xtstx||y{z~||xutmr|}zxyooxy{~~{|~||vy|}yvtsqpsx{}{|zustvz|z}~||{xtrrw||y{{~||~}||~tusnu}{{x{~yvspnrz~}wtty{y{}}~uy{wz|w~}xutvxz|z{}~{{vvrkrz|||}{qtyyz~~{~}{|xz|~~}zwusqoty}}}~~{ussuw{||~~{wsrv{~~|{~}x}}{|vwtnt|}y~z}}zvsomp~|~|xwz{x{}~}~{v}~xy|v{{wuvwy|~}~}z}~zpotwtkhptqpssw|wx~~~x~{y{wxyvps{~~}~txq}|wz|vyyw|~{|{}}yx}zqt}|yyz|~|~}vxuuvvusqyywuvxsrx~}}}z|~||~{~}zxuw|xtvnmrwxtlluxtqru|xv~}~|q}}}zx~|~{wsx{}{xvutsuvspqqvrnsxz{yvvy}{~tt}|zyx~~wkw}|{vqnvytuqnrqorwyyxz}}x|~~z{}xw}}||~zonx~srtvvskirwsnntytw~|z|~|zr~z{|yxytqw{}~zwvwxupmuynmswyywuux{}}z}tu{}yz}~}qpxy|ysv|vwxonstrtwxxy|}xw||zx|~yx|~{||~}~wnr|{vwwwvqknvwplqxsu}|yy|}~~v{}|}|{~yx{ysrvx{xyzzrkt|qlqvxwuttuxyz{xu|}|z|}vtutx}~y}xz{rnswuuvvwy|{tv|xz~yw{|wz|~y||}zrrz||{zxtpovyrlrvqv|{xwz~~~~{|y{~||zx|~wqsv|}{||}~vkt~tmptvvusstuvw~~{~}wz~|}~|~zzuquy|{|xpqxzwuuvx{{uuzxz}|{|{}wq}ztw{|||}}||wpry{y{~{yvst{~z|wmulmt{~~}}z{~wu{}|yssxyqlow}y~wyrkwxnjjklmnos||uwxy||x{zvz~~|y|wt{vtx}~zqkjlptrloqmotwwtqs{{tv{~|~~~{yxqklnjhjfjtusqrtwyzz{{zvzysy~~|{|~}yuz|rqz{zxnhnqokjloppppppos~|{{|wtz|~~~~~}y{yuqjktnbgs{yzz{}{ymn{~xqmmpwxmev|t}}upuslkggnvvw|~|yuv|~zyzwu~|mmy}xspquz~wtljryzvronquqhitwv{vqmmu|rnpppw~}|{wy~~x}zurtz}wmfovlflv{~~~|{}xpkjnoln{|{}|xvxyzyvsqs{|xytnmsvssy}~}~}}zphpysou}~{x|~vrtonsy{ytpoptuqow{}|ywusqt~}||upov|{{~}|zxx|~ysy|xussux|~|snx}vprzxxzutz~|wsqquxvv~x}zvtsrqonmr~}zuuz}yvy~|z}||}|qoyztqt{~~|}zz|xsvuorx|~|xvtsutooqr{~||{zxuu|}uopvyvv~|yy|zv|tx}}zxwwwy|vmqwuqot}~}xtvz|zwspnpuxy|}~~x{{wuutrnkmvyvz}zwv{zx}|zwwx{wv}}yuty~|{vrtphinty|||{yyywurr|{yxvspqy}wv{~zvz}}|w||tvywvuvvvx|zuz|yuppvz{{}|zvwz{{{zxutuwwurz}uu{}xttvvuropwwu|}ywzzuy~~{yy{~vv{ywvsu|~~yvytmkmpsuwyyxwy|{tq|}|{xtrv{wx}zx{zx~{{~}|{z|{yxvtrqsw|{w||{yttx{{{|}~||ysrtuwxz{zxvx}|ss~wyysqqqommsurv~~||zvy{}}zxy|{x}|wxwrrx|~~|xsuxrmmoqsuxzzxvx}{oo|}zzzxuuzzuy|ywy~zx~~}zz~}|}|zyxxvsrsvz{wv{{wz|xvy~{}ysrrrssuwxvuxxv~xw||uqppnllqrmrz}}}~{{}|zwuux}{zyz}{vx}~wpqtojkmpruy|~|yyyqy~xuutstz|wy}{{~}{~{{xx||xvvwxwvvx|~{x~zx{}xtw|~|{}wuuuuuwyzyvty~uy|zvuz~|vrqqooswsry~~}}}||~}{yxy{}{vz~xuy~}yyvqutnmnpqrux{{ywzwv~~xvussw{vtz~~}|{|zv}}wuuuuttvy|{x}|wy~}xtw|~~}}w{ztrssssuwyywuwyx{wwz|wqnmlmswsrx}~~~~~{{|||{{zyxy{|xu{~wsv{}zw|{~uqurnnoppqswyzxvyzzyusrtyztsx{{zz{|~wt~~xuuutssuwxvv~{x{|xy~{yzvqqqpppsvyyxx}~~{wvy{vplkkntsnsy}~{}|}}zyxwwwy{zw{xw{{x|}{~zrsrnmnppqswz||{|~~~}|{vsrty|vsx||}}~~}yqy|xwwwvvwxyvv}yz~xvz~~~yuttsqpruxxxx|}~}zwy}{vqooswtorwz{}y{}~}{zyxxxury|wvz~|xy~yxuonoonmoswyzz}yusuzzsqvxyz|~|y}v|ywvuuuuutqt~|yz~zuw{}~}wsssqonpsuwwx|}{~}ywz|xrnnrwsnquwx{~yx~|~|{zxxwvrou}{vuw{~yw{~~{{vpnoomlmoruwx{}~|}wsty{tpsuvvy{~~{}yy~yvuutttsoox}zz}}xz}zsqqqommoruvx|||}xvy{xrnouuonsvwy}z{}~}zyxxxxvqqy~{wwy||xz}{{}|uonooonoquwy{~}}y{~}xtuz{truwwxz}||zz}|xwwwwwuqqz}zz{~ywz~}ursrpooprtuw{~zy|ww{|wrqvxrptvwy{{}zxxxxvqov|zwvwy}|xz|z}|soooonnpsuwz~~z}y{~|wtvytpsvvwx{~|}~~|wuuvwvspu~~{zz{}ywy{|xqpponnoqrtuy}{vz{vuxyvrswsorvwxz}zwvvwvrnrzzwuvxz}~{}~wwwpnnnnnprtvx|{}~yvx{yuuwsoqtuvwz}~}yutuvvspu~|{|}~yy~xxyqonnnnoqstvz}x{|vsuxwtuwrosxyz|~~{wvvwvrou|zwwxyz}{{~vwxrpoopprtvwzz~}xuwzzxyxrptvwwx{~~{xwwxvqrz~{zz||}~yy~w{xsponnnpqrsw|}xw~}wuwzzzzuprvxxy{||{wvvuqmqyyuuvwxz}}z}{w}}vrpoooprsuw|~z}{vux{{{xqosuuuvx{}~~}~~ywwwtpqy|ywxz{|~{y~wzztqommmnooquzzuv}~yuvz|}|uprvwwxz}}|~|xvvsolrwurrtvwy||~{y~}xsponopqqsv{|y~|wtvz}~ypnrttttwy{|~|~|xwvsoqxzwvxz|}~|yy}~{wrommmnooptyzuv}|wux}wqrvxxxz|~{}~}{xwupmqwvrrsuwxy|~}{|~}yuqppqrrsuy~}y{~zutv|vpquuuuvxz{|}~}|{|~~}|zwuz}xwwxxxy{~}wrv}z{|}~|ywvvwwvuuwyupsy}v}z}{y|}~}}|{zwot~~|~|yw{|xvwxz{}~tq{}~}~}zyyzzzyz|~zvx}|vx}zwy{}rkq}{z|{}~|}~|xw{}xwwwwwvvwwvtx~}}~~~~{wtw}zzzzzzxvssw}wwz{|||}}}}|{yuuz|yuv}~~}|zvsqpprv}zxyxvvyywutuvwwwy|{wvxzzz|}||~|~}ytpruutw}|z}}zxy|}zxuw|}zxwxxwutstvx{{xxxvttvx{}|{{zywusty~zxz{||}}~~}}|zvtvzzxx{~|{ywtpnmosx~~{z{zwvxzzxwwwxxyz|{xvxzyyy{|}~|xropsstw|}{}~|zz|~~|ywy}}zxxxxwutssux|~~ywwvtrrtwz~}|{zyxvwz~}{z{{{||}}}||{xutvxxxy{~|{yvspnmosy}~|{|{wvxzzyxwxxyz{|zwwyyxxxz{|~}zvqpqrrtw|~~~|zz{~~}zwx{}{yxxxxwvtttvz}}~{yyxvtrstvy~~}|{zxxy||{{{{{{||}|||{zwvwxxwxy{~}|zxvsqoorvxz~}|}|xwwyzzyxxyyz{|zxyz{zyyyzz{}~{xtssrqqsv{~}{z{}~}zxy||{zyyyyxwvuuwyzyy~}{{zxvtsstuw|~}{zzz{|{||||||}}}}||zxxxxwwvvx{~}|zxusqpqsttw~}zxxz{{zyyyz{||zyz{{zyyyyyyz|~|yvutrqpqru{}|{|~~|yz||{zzzzyyxwvvvxyxwy~}|{{ywusrrstvz~}||||{|}}||||}}|||{yxxxwvvvvx{~|{ywurqqrsssx~{zyz{{{zzzz{|{yyz{zzyxxxyyz{~}{xvutrppqruz~}||}}|z{||{zzzzyyxwwvwxyxxz~|{{zywusrrsuwz~~}}{{|}}||||||||{{yyyywvwwxy{~}|{ywusqqrsstx}}{zz{||{zz{{|{zz{{{zyxxxyzz{~~}{xvutqppqsvz~}|}}}||}}|{{{zzyyxwvvwyyyz}}{zzxvtrqqruwz~~}||~~~}}}}}}||{yxxxwvvwxy{~~}|zywusqpqrsux|}|{||}}||{|||{zzzzzyxwwxy{|}~}|zxutsqppqsvz~~}}}}}}}|{{{zzyyxwvvwxxy{}~|zyxwutrqqrtwz}~||}~~~}}}|||{{yxxxwvvvxz|~~}|{yxvtsqpqrsux|}|||}}}|||||{zzzzyyxwwxy{|~~}|{ywutsqppqtwz~~~}}}}~}|{{{{zzyxxwwwxxy{}~~}{zyxwutrqqrtwz}}}~~~~}}}}|||{zyyxwvvwxz|~~}|{zyxvtsrqqrsux|~}}}}}}}|}||{{{{zzyxwwxyz{}~~}|zxvutsqppqsvz}~~~~~~}|||{{{zyyxxwxwxy{}~}|zzyxvusrqqrtvx|~~}}}}|{zzzyxwvvwxy{}~}|{zywvtsrrqqrux{~~}}}|{{{{{zyyxxwxyyz|~~}|zywvutrqpprsvy|~}||||{{zyyxxxwwxy{|~}|{zzxwvtsrqqrsuw{~~}}}|{{zzzyxwwwwxy{}~~}|{zyxvutsrrqqsuw{~~~}}||||{{zyyxxxxxyz}~~}|{yxwvusrqqqrtvy|~}}|||{{zzyyyxwxxy{|~~~|{{zyxwvtsrrrrsux{~~}}||{{{zyxwwwxxy{}~}}|{zyxvutsrqqrsuwz~~}}||||{zzyxxxxxy{}~}}|zyxwvusrqqqrtvx|~~}}}||{zzyyyxwwxyz|~~}|{zzyxvutsrqqrsuw{~~~}}|{{{zyxwwwxxy{}~}|{zyxwvutsrqqqsuwz}~~}}|||{zzyxxxxxyz|~~}|{zyxwvtsrqqqrsvx{~}}||{{zyyyxwwxy{|~~~}|{zyxwvutsrqqrsuw{~~~}||{{zyxxwwxxz{}~~}|{zyxwvutsrrqqstwz}~}}|||{zzyxxxxxy{|~~~}|{zyxwvutsrqqqrsvx{~~~}}|{{zzyyxxxxyz|~~~~}}{zzyxwvutsrqqrsuwz~~}}|{{zyyxxxxxz{}~~}||{zyxwvutsrrqqstwy|~}}}|{{zyyxxxxyz|~~~}|{zyxwvutsrqqqrsux{~~}}||{{zzyxxxxyz|}~~~}|{zyxwvutssrqqrsuwz}~}}||{zyyxxxxyy{|}~~}|yzvxwwxuuppooqqvx|y|{w{~|}~yup{w|{~~{}~{{|~z|~~x}{simszqrmmslqwzxzuzyvwsyzxu~~{sx|}xvyx|}{ut|xy|yzv||{omstwy{~~tyvrtwvsrxxyqv~~x~~}wywrvxl{zqppuwuyw|}{}}}{uxvsywwus{~u}|}|~z{vzyx{tkwmn|mnmq{~}|xruywyyprw|x~|{~z|vw|}ywqx~}v|~ys|kr}vus{yppt{x{qmr|ytxx~~|{t|sozy}|~xv{|mqst|zy|}|}t}{prooosuxwn~|vrv|y~zv}y{ntz{vyz{~}puyvuuy{{{u|olytvm|poztsxsftqm{{|zy~w}u~~{qr{}}t}yx~|~}zx~xzwvo{}vxxowwoo{cuhppm{}|||xuuv}{qvz~vw|y~{}{{ouuozw|r|xr{xixug}okunr|w{}w|u{}v~~|~t|yxuty|}y}skvqyxyr~wwusp{mtzhtvnvyy{|q{w~~ux}y~~z~ty{|~wmnso{~qz}uwtpswkzwptst|}vz{wv}|}~u{qw}}{|~|ysvy||yskuwr{}o}wvruurorpsuqv~{x|q~{~uypww{{}{{x{{||}~zzqprwoyvzysxuuqqj|wpvqxv}}|~yu{}z~n~{swzz|}}zy{x{~|{uqnyust|ttvutrlv{nzvst{}}p~zyqt~rvx}}~xy}}}~}|wqltttu{xvzruuqm}qsxsvx}}|}su}w}}yovry}~{y~z~~|z|tioytzxywxtuttm{voyqvyzy}znx}}~r{xrw~}|~zwy{|{||vjjvsx}wyzuuutky}jtswxzyz|}n}z}t{zrw{z|zz}z~}~yy{xkivquxzyyvqqox~krqwxyy{~}r{}}uw{utu|~xzxz{}{zxzpiqqsw||}yurpowmruvv{}z{~rx}sy|rss{{|xzz{zwy{ojqosy}|zwrqqvpsrwx{}{{}~q|{}|~qx{sttx{yzw{|zxz|ohppp{{~zzvqpyqtsxxz}y|~|pz~{}|tyztvt~xz}xxz||xxzynipoox}{xrqwsrszxv|{{{|n{y||}uu|uwuxz~wx~}{~{xx{phnnnw|yqrxsqtwxuz{{{|qy{~{~}yt|vws{|}|u}}~~z{wvztjlmpv~zysru~xosvztz|zzvr}}|}|q|wtsyxx|~}xwu{{kinot~}zuqu{}qss{xy{|{}{py~}}{~txyttux~}w{}{|}wuw}rhmnry{zsryxpsz{w{|{{}rvz}}|z}xszuut}|}x||}y{uszxiknot|uqwsrw|wx{~z{xq~x~|{z~tvywvvy}}vz}{z}vsrzqhlmqz{st{zpuzyv{}{yzru{{|~|}yrywxu}}xz~~u|zrptxljmqu~}wrw|wrv|vw{}zyyr{w|}}z}vtywwu}}v}~{s|tpowvikpvz|vrz~}sryzvz|}z{ur}w}~|zztvvww|{|y}z~txyopsznjnty}~zrw}{qv~yw{~|{yqzw{}|y{vswvxx~w}|~}|rzrpoxvkmqxz}vs~us}|wy}~zztt{w|}yyzsvvyx~zy~z{swuoosynlnvy|ys{zrz}wx|zyvr}}vz}{z{tuxxy|~~v}{}~wrxomozrkmsy}|ux}tw}xxz~{xwrxxw}~y{wuwxzzv~}zzqxpnmxukmqz|}wuut}ywy~}yxrv~zv}z{xuwwyzx}~|}x|pwqmnwwlmqyz~xuwt~zxy~zxsv~{v|yzzuuvyy~z{{}w}qvronuxmmpxxzuxtzxy~zxsu|{vzzzyuuvyx~yz~{}x}qwronvxmlnwwzwzuzxyywsv|{uzzzyuuvxy~x{~|}x}rwronvxlknvv|xzvzyy~zwru{{uz{zzwuvxy~yzz}w|rwrnnuxnkmvt~x}t{xx}{wru{{wy{zzwuuxy|{zz}v|tusnnsyoklut~wu~|xy{|xsu~|{yv}z{xutxy~|~x~yw{wsunoqysmltu{z}w{xyz~yutzz|u~z{xvtwx|}x~~{}}{xzrvonoxvnlpwv}z}yyzy{vsxy}uyz{ywtux{~~z{~y}v{turnntyqmmwtzx}yz|}yst||zzs}zzyvtwy~x~z}}~yxxsunmpxuomswx~|}zy{xzusxw}tzz{zxtvy}z{}~y|}~tyttpmnsyrnoxu{y}yyz~yst{|x|s~z{zwtxzx~z{~{}}yuwrtnmowwqptzxzzzzx~{vrw~w{xt{{{yuuz}}y}x}{|z|uvtrrmmqyvrqyy}}}}~y{yzttyv~rzz{zytw{y|}{y~z|zysvstnmovztru|y{|z{y}}wrv|wzyr|z{zvuz~}y|w|z{zzuutsqmnrzwss{z~~y{xzsty~u}q{z{{yty}y}|yz~|z{ywsutsmnpxyusx{{|~y{x{urxt}vu{{{{uw{zz{|x}}y}}xxttttnmnuzvsv|y}z{y|}wrv{wy{q}{||wvz|y}x{}}y|~xwusstpmnszxtu|y~{{z{ysv{zw}q~~{|{xuy~~z~~wz}}y{}xvvssuqmnsyxuu{y}{{zzsuz|v~q|{||ytx}z}xz|}x{~ywztppt{{wv}{|z{y}x|~|xryy{zyxz{{~~{~yzzvr{wvrswrnnqwvtszxvwzzuuut|vqtz|wr||}}yssu}}}umt{siglgdfjqqoous|}||{}wz~}wsqw{~vstvu{z{yu{{uutsqw~yv{uqquz|yy~|~zspqnyysmot|zr|rw~z{zvoos||zvromr~~uwphginpnlqot}}~|{{~{plnwy{yxwx}yytvy~vsssrt|}x}|tsuz}}z}}}||wxrw|wpmrw}txyq~|{{zrory}}{spnmox||}rmlorspqso|{~~}~~y}{|tllrzz}{zw}z{xuyz}ssqrrw~{~xzttw|~}|||~y~~xxrz|vooty|szwt~||{yqos|}wpnlmqz{}~sonrturtsq~|~~~}~~z}|~zskmt||}{yw{|xvzz~{rrpqrx{~w|vvx}~|z|w}~xysy}xqpty|tyxt}|{{yrpt}vnlkmr||{uqpsvwvvvr}}~}~|~|z|~}}~|ytllt}{ywz}{zvx}z~~|}sqoqsx|vwwx}wzw{yztw~zsptx|wwzt{|{{ztpt~}~umkjnt~~xxrqtwyxxxs}~||~{|{|~}|}{xsmnv~{yw{}|zvx|{}|{|rpopsz|vyyz~|vzw}z{tx~ztqux|xxzu{||{ztqvz|rlkkov|{yutvy{zzwt~}z}||}~{}~{|~{zwrmpz~{yw|}yw{}z}z|xppoqu|zwzz|}xxyv}|zu|~yssvz{wzxw|||{xssz}~{{xmljlqzyxvvy{|{{tz}{~|{~y|||~{}|{yuont}{x}~xwuux}}t|vuw}|}{~xz}}zz~{|vxtx{}wqot~|vvvtv}xovxvqszy||yxz}uuuv{{tvzswwsuszzurt{w}yp{vqsvyxxyy{}~zy~xryyy~srtw{zxmllmqy~~wsv}~su}{wyww|}{{zw}||{upos{~yrpt{zvqmtxw}y|}yx{}ttssw}}}zrqwsx|yzrw~~wvow~{{{||yy{|~~yw{}x~|}~stxwyxtpmnsx{w{x|ysruz{|{zyw|}{z~x}|}zwsrvz|ww{x{z}~|tpmr}~||xy}vsrqsy~{ztqyvu}{zwxqt~~}z||}zwx~~yz|{yszywz~ywsrty{zw~v~vstx~{y~vy|{y~zw~{}zyvruz|}z}}yz|~yrony{yv|zttqqu|{ystztw~y~|xupx{z~|}}yw{~|x~|}wv|yw~|zwssvz{xzyy|vsuz~~}y|uy{zzxz|}~|zzwrx{}}{~|{~zz}wqnp}{wv}yttqrv~z~ysu|vxyzxuqyzz{}}zx|~{x|}wv~{w~|zwttw{|xzyx|vtv{~~~y~}uxzzzyz|}}{yzyry~~~|~}{yxzwqnp}{~xt|zttrsv~z}~yvt}zx~z~{wwry~~~|w}{~|z|{x}{{{v~x|~{yutw{}zx|s~~wuw||y{vvyzz|y~|}zyy{tx}~~{x~vwwroq}}|ys{{uuttw~z|~zwu}}y~{~}{xxtz~~~}v}{~|{}zx~~z{}wy}{yvtw{|zx{s}}wvy~}z{vuyz{|z}}|yx{}t{~}|w~~uvwrps|}xs{zuvttxy~~zwv}z~z}yywu|~~zw|{~}|~}yy}{}{yx}|yvuy||xzwu~|xx{}~x}}uvyy~y}|~|zy~{t{{xztxuqpx{sw|wvutuzyyyonpqtyqrz|~}{yttpyu~y{}~||}tpzz~z~su|yxxww{~}zywzy|tqtuyyypn|utuzzov|vx{}|{{}{~zy{{tnrwv~zxyxy~ysyy|wtpouolprv|ut|~zxorw~|~uln}~|svznnvsrrqquz|{{z|vu{|{yuxy~||~wln|}}zltyrtwxwvvxuz{w~vxwqmrzz}{{zz~}urx~urvoknptyrqx{}~{zxvoqzz~|zqko|}{~rowsqqoorwyyyy}|wuy||vxz~}z}zwnmwvxxvxyxwvwtw{v{wvxsnp|~x~{{yx{~wsu~wzwnoqtztpwz|}~~|zzyrou~~|~yzummw}zpvvrrpopuxxx|{zzvw|}ywy|y~~{zslq{u~wxzzxwwus|wv~|twvqnv~y||{zxy|{uty}y|uoprxzrrx{|~~zxzxpqxz|~{wxrmq{wsyusspoqvxxy}}zz~ywx|xwz~~zzyyqmt~~w~xy{zyxwtt~}vv}zsvuppyy~||zxy}}~zuu|}y|upqtzzrty{}~~~xwzwprzz{|xtvqms~xtyvtsqprvxxy||zy~~yxz|ywy~zvwyrowvyy{{zyxut}~vu{{rsuqq{{}||{yy|~{z}zvx~x|yqqsy|tsy{}~~}xtyzst{|}{{{ypsqot}txwttsqquxxx{{yxz}{z|}|{vx|z~tsxvryv{z{|{zyvs|wty{rqtss|}|||{yx{~~|zwzzxzx{{rqtz}uty{}~~|xrx{uv}||{~yy}wnqqpw}uyxuusqruwxx{yyxy}|{~}||wy}z~}qryvu{w|z|||{yut}}vtyzqptsu|}||{yx{}}}~|zxvzzz|y}zrru||tuz{}}}|urzzvxz}y|wx|smqrr|~yvuuvuvxwzz||{nmqlqx~~|~~~zqpyst{yrvyy~|zxpponpstuxzx{z|}|zux~}ql}onqtwxy{}z}vtxv|zruyzwytnifgjlnqttxy{}~}{}|qj|yuvy{}}~~w~|}}zvqoxyv{}uw}}zw{|uomopqsvtwxx~}|~~yz~}xxrl{}{yyyxy~{v~zvs{~z~~uuz{ywz}xroopprtswyy~||}~{~{||xxrjw~x|}|zvqnu|z~~vw}}xvy|xutttvwtxyx}~|{|~zz|zwxsju~}{{|v~}zuqw|wuz{~|wyz~wsrrrtvswzy~||}}y}~{{}yytipx}}}{xsnq~{~yv|~zuwy|xvvuvxuwzx|}{|~||z|~|xxvkn~}|v|~~~~{wqr~~{uy|}xxy|wttstwtu{y|}|}}zy~z~~yxxnjz|{}}}zvqny}|wx}vtx|{wvvvxwtzzx~}{|~}|{||}yvxsjv}}{v}}}|ytov}yuz}}vy}{wuuuvxtw|z}}|}|y|~}z}{vvvmn{~}}|yupp}}}~wy~|ttzzwvvwyvt{zy~}{|~}~||~|~{vtwsmz~~{w|||{xtpy~|~yuz~{vz{wvuuwytx~z}}|||z~}z~|~}|xstvpq|}||{yvrs}|xx~|tt}~ywvvvywsz|x}}||~}{{}~|{yuruwr{~~~w||{{zxusy{{|ux~}w{|wvuvwzvv~|{}||}}|z||{}|{yvqqvvt~{|{zywtu}{~zw}~{vv~zwvvwyxty}y|~|}~{~z||{ywsquzw~w||zyyxvtz~z{|ux~}y~|xwvwy{vw}{}{|~~|z|zz{zywtpqxxx~|{zyyxvx|{yw}~}zvx}ywvvxzwt{}y|~|}}||z{{ywurrx{y|x~{xxxxvu~|z|zuy{y{xwwx{zuz||||}~|}}z~}xzzywurptzx||~~zzzywv|{|~wy~~}|yu{|ywwwzztw~{z~}|~|~yz{yxvtqtzy|yz~yxxxvty{{~vv}}x|~{yxy{|wx~{~||}}{z|~xyzxwusprwwx~}{zzywtx}|yw}}~}|ytv~{yxxz{vu~~y}~|}~~}~yz{zxwursxxxzzzyyxurv|{xv|~}wx}{zz}}xx{}||z|{|}xyzywvtqrvtu~~|{{yvrw~~zx~}~}yrs}{zz|}vw~z}~}}|{~z{{zyxvsswvu{{zyyxupt}|yv|}}vu~||~yx|~}|{y}|||}xxzyxwurqttr}~~{{zwrt~}w|}~}zspy}|{}~yv~{|}}|z}~zz{{zywtsuvs{}z~|yyxvqq{}|}vy}~ysy~}|w}}}|yz~|{~ywyyxwvsqrurv|{{yury~yx~|~}|wpr}|}}vy{}}|{z}yz{zyxussvuv||{xxwtpt}}{v{|}wt~~~{x~}~}{y||}~|~}wwxxwvtrqsut{|{zxts}~yy}|~}|{wqu~}|}|vz{}~}|{|{xyzxwvtrswxy||zxxwtqv||{vz|}wu~~{x~|~}|z~|{~{}|vuwvutsqqtxx}{zzyuu~~~zw~}{~}||{xsv~||}}vx{{~}}}~~{wxxwvutrsx|}}{~{wwwvtw}{}}vx{|~|x~}|w}{}~}|}y}||{utuutsrqqtz|{yzyww~}}{v|}z|~~|{{{zvw~||}~ww{z~~~~}~~zuvwvuttsty~}{}{wwxwux|{|}vwz{~~}z~~|w}{}~~}|y}~||ztsttsrrqrv|{yzzxx}}zv|{y|}|{{{|{wz~||}|vy{z~|~~}xtvvuttssv||{~zvwxvu{~{{}{uy~y|~~~|{~yx~{}~~~zy~~|{wrstsrrrqsx~zyzyw{~|~~wxyz}}|{{||yw~|}yv}~z|~~|~zuuvutttstx~~}|}xwwwux|{|~wv~zz~~~~~|y|x~{|~}||y|}|xrrtsrrrqru{{yzywy}}zw~zy|}|{{{{yuz~~{v|z{~~}|{uuvvuttssv{~}ywwvsv}||yv}zz~~~~~}{v|}y~||~|{}y}~|xssutssrqqsx}|zywux~{xzz}~}|{{zwrx|x}{|~|{|{uuwwuutsstx}~zwwurt}}|}zw}zz}~~~}}zuxz~}||z}z|}yssuutssrqrv{~~zyxtv~~}x}{z|~}||{zwrt~x|}{~}z|}~|vuwwvuutrsv{}xwvsrz~|}~xz|y|~~~~}}{vu{||~}zz|z{urtutssrqqsx}|zyvty~{y~y{}}}|{{yury{y|{~{z|ytuwvuutsrsx~~{xwuru~}|}|x|zz}~~~}}|zuwz~|~}z|zz~ysrttsssrqqt{{yxuu|~~zz}y{|}||{{yus|zy{{~~|{{~xstvuuttsstz~{xvusw}|}|w|zy|}}}}}}{wyz}{}}{}zy~~yrqsssrrrrrv}{yxvv}~}~{x~}xy{|{{{{{xv|{x}|z}}|z|xrsuutttttuz~}|xwvtw~|{}}xyzxz|||}}~~{{z{{{~}~zw{zroqrrrrrstw~|zyxw}~}}{x||xxz{{||ywyvupmmmqs|{{}~~|orupmnknoqx|{~v|ztpv|y|~y{{|~}wx{z|tq{|vuwtutsv{x}~x~~{vyyyw{yq~|sixuqs|~|x}xtyzuwz|{u|t{{uwxzvy~~~}~rn}{sqsjklqnkpwuy||rls}y{ussyvuwvsloq{vrtzxwu|{{vx|zwxz{|{ons}{s~ztvywxvv|rrrtvxy|y{tt~}|xup}~zyunvtxwyz}y}|}{{{urut{y~{wuntwwyuxsuqo{x|xrwz}|}uyut{~|}}ynvuw{y}vyxtx{{{x}w{xy|tqslw}z{~wz{{~vwmszrvvz~z}~x}ytvux|x}}ywsqspuwzzy{qusntqrw}}}yzx~}~}ywsiopu{}x}~uvvq|wxw|ytxwvto~|}xrsm{~yxnv}{zxzwxzvuwyyw|~zy{zsmt||{~}zx|tvwvwt|}vzxw{wux~~|}~vprpwy|{zx|x}}zr~wuzwyusyt~}z}yy|xzu}|tyrswwy{~{||{yuzvouu{~~yrsoooy|~uttn{xrqr|{x~u~~|}|~~}xyntvlnq~~x}}z|w}vvxwwwzxwu|v|}z}zxztwv~~z}ytyz|x~zyzu~vtrzwry~vvutpq{}{zy}}ts}}}x{{}z~xuxx}}|ztjv|z}z}z||{z|~vz{|vwztqtoz~~~~}|zvxtwyxxtsxtsx}{}u}{ut{y~~zzryymkjv|~z||vxw{uvwz{z|}{xw{~~~{x}z}urprtr}|y{}xvxr|trpyysv~}z}~xswtrw{{{t~{{zz|{uwtxtz|}~|{}|rt{yz|~{xzx{|yvz~{}vrpwurr~~|{~}y}~rwxxv||{}|zsw|~|zz{xwvpvzut{okvomo{}w}~tzxzw|w}}y|{~||zz|zzssvqptv~wx{{rvs|tvy{|u|{yw~~{}}zusrslkw}||yyzw~tu{rvuzx~}}~}~z}xzyvyumxzz{}|~|{{vpuyor}|~~y}|{~{vsusxx}uv}|ys|xxxx}}}{x~{~xuuuwjmrw{|yy~{z|}zv|pxuxsx}}||}xurrns{}~~}}}{{}~v~xxu{||z~||}}{|yqtrsro|z{~}{xuuux}~}|w}z{}~~z}|xonnrs}}|~|{~{ysyxqwy|ry{{}|}y~~z}wtnxwzuy~}}~|xvzxwyztv{}{|}z}{~zw~trjr{x}|}z~zxxw|}ywuuux|~{{rrrptwxz|~~y|ywu{~~yvvrvv~y~{{{|zuvzwtpwy}~}}vyt~wx}}tz{{s~}~~{~ztplwtnq{~~vvvy~|zzwxx}vsy|~}uwvuuyuvtqy}zyx}~{xxvpuvz{{x|z|wy}{yqvuwy}wyv}wv|yt|zpy}z|stpztnnwwyzz{~}|{{wv|tssx~}~~|wz{xwxwwvou{{~{zz}}z{xttux{~~}{{x~yy}zrssrtx{|x{x~ww|~v}qw|{wur{wljrus}}~}~~|x}{wzzrss}x|}~{||{x{yxxrovvuz{{xz|xvvwy{{}~}y{}~v}xtvqpuuu|~}zz{wzzyys~~}z{}{|xtu~slmwrs|~~{{zy|vtqu}vu~{|~{{zxwwprusu}}{}z{||vvxvxw{~~}|~~|v||suxmsttu}~~uy|y{{yz~{u||~~~~{su~~llt{oq}yz~|{y}zsvr{uuxxu~}y~~{yy}wzwxuyrnx~}wx|~vxyysuu}x{}sx}zprtrrqww}z|yzxz{~}zvzxywy{|xvy}wpn{qmtvuu~{~~yx}~x~~xyq|w|}{~~|v{totmk|sqv|~z{x{zxxzzywx}}x~}{yq}~vysprrxz{|{}yzxuzxvs{xz}}{}xv}}wtwzovxussz{zztzzwvr}{y|z|tuyuvwswvwtjx|x~}xw}xvxr{{|~~{}{v|sswrnqvuwz~|z}|~}{yz~{t{|~~||}wyzuwqm|siqr{||y{wz|}vxyxzz}}|~||v{}wqxsponyvz}~~t|uwsy~vz}}w~~{|z{uwuvsnvsr|w|}~}z}wxxwxx|{~{v|zutzw~}|om}xxyv~~vtyxp|yttt~}~}|v{vwtvrppwvp{}wyv}vwyy}x}~{{x{rw{nnjquqy~|~~~zyw{}ys}{y||wz~ssrxrprtxw{~uvyzy~zs~}~~~wz{|rstprqix|sw|}}y|x|xx{yxx~{{}zv}z|}ss|xwwquxu{|~x|xtxros|zz~~~z|x}xzvwwwumtwxt|xz~zwywzu|{~y|vxuqlrxssyz{rt}y}~zv|yruvx}}{}zwwvwvotqtpvwy}||z{tx|}|x~|z}yuzwqqlnzpnnwz}|~x}{zw||w|~~~z|{sz|zzvoutpsv}x||w{wtry{uw|~~|||~y|yyw{uruuspvxwy|||{z}|~~}~~|{yuw}|wjv~xwy|wtw}vyy|zumtxt}~tsx}qvx}{||~~}zx}~ovxzqxzyzr|z|ypxyw{}{qy{}y~x{vtxvr|y}w|y}{y}w}x}~|x{uwy|t|wu{~}}zz}yiry}rjsqpulpk|ohmt|w~|~zyyy~wxqt}ywx{xwx|ux{|wsp{xt}wpptwnrlzyvywv{t|{v~xvw}wmywkjrt~}~utltnp}~vrpznkntknktyq|~y{~|x}zr|qpxw{|}y}zz}~yyrvyt~r{wsuvqtrq}sx|vu~}wv{r{~yvz{qn{qkruryrlmslz~~jrvpqsomok|tw}vx{~x}zzrwsy{w||~w}{}|{sr|s{vw{uxwvrqk|uqzutx|x{zts~|uy|zl{ulpvuzz~}smmvls}srwosrplpiyto|vu{|zw|syrw}z~{zv|x}zz{{tt{ryyy~xzvustk{tnwrtv{xz|us~~x{{{p}tnqwtxz}~~umpvltxuw{tuqpmoizqlurx{}yy~uyuv{yzzyv{x{z{|pryqw}~{{}wvrsm}xluptvzz{zwq~z||znzuprwws~}{}~~}ynmslqx}w|wztsppjvxjsouy~~tu~|wyz}u~yx}vzx}}~upxst|~~{xwuotmqqqvy|}|~}m~|}~z}zrtxqtt{r{}~~}}|y|~tkpolvy|~{zzuvrom}qlrqxy~w|}|v~wyw{{|}v{{xzz~y{|oswryy{xup{{jrotwz}}{zn}~}|zzx{pxwsuu|r}{}|xwz}qjpnqy|~{}yuvqopplprzz~u~|zv}x{w{}{|}w||z|z||vxzmqut|{|xtr|}kpouwy~}zyo}~{}{zvypuwuvuu}|}|trv}qiops{|}|vxrpq~ukqryy~wy||{t{y|yz{~xy|{~y|rsz~potu{}|{tswpnptxw}|}|n}|y|{|uxrrvtwuz~}~~~ysoqzsinqu|~{~}wztqs~zlpszy|xv~{z{tyy|z||y~x}z~v~zpry}ppuw~~}zstzqmpvxw|}}zo}|x{||uxrruvxwz~|}~|x~qorzqjprx}}{~yzsrwxmpv{y}uz~~z{yuyy}z|~~x||}||tupr{ymqu{~}xsx~onsywx|}tr~|w{~yuwqtuyx~y}}u{wnouylmpuyz|{wpu{rnszzzzt{~z|vwx{|~{}~zyx}|z~vyzpqv|qosw|~~{sw}vnryxw}}|wo~}xy~|uxsrtwyzy{}~xwzoorzpknrw}||zrt|wnrzzx~zt||z|xwxz|}{~~|wy|}z~xw{pqu|rnqvz}txxnszxw|~{vp}}xx~|wyustvxyw{}}xwzopszokmrtz|~zrw~yot{yx~~yt||{}yvwz{|z~}w{{~z}yvzpqt|snotx|vx|pr{yw{{vp{~~yv~~xyvssvxyx}z~|u{qprzsklotu~}uu}rr{zx|~yt|~y|}{wvx{{|}~~w}~||}||~s{sprzxnopxv{vwqzzxx|xqu}}uyzyytssxx~|x|}}{vyvopuynllss}}|t|{px|yy~|vu{y|}ywvz{~y~|w|{y~||ytypps{tnnswyyxut}yxy{vqx||t|yyxtrtxy{{{~~~y~txsnouxnmmus}{t|rz|yy{uw{x|}yvuz{{|w}{y}~zzxswoprzuooty{zxwt~yyy{uqwz|tyyyxurtxz||{||~w|tvsmnsypnnwu|}v|tz~yz||uu}}u{}zwuy{}~~v}|}z{z~}vzrvonpxyqqt|z}w}t}zyx~|wquz{vtzxyvssx{}}z}z~~wyussmnryspowx~|}w{wzyz{|uu|{~t{|{xuy|~~w|}|z{z}{txruonpxysrv}{}x~wzyx~|vqvx{vszyywsty||{}z|}uxttqlnsysqqzx}~w~x}}zz|{tv~~{}s{|zwuz}}w~|}w||{{~wuvstmorzxsrz||||{yzxzsryv|qyyzxwrvz~z~}{|{}~ztwstnmowwrpuzy|y{z{y}wtzxvx||{zuw{xzzzx}y|{zsvstonpwzusv|zz{zzx|vqwv{uszzyxstx}~{|}~z{{{tvttomnuxsqsywz|{{y~xsyx~yu}||{vvzzzz{w}}y}}zztutuonovyurtzx|}z{x|vrwv|vszzyxssw}}|}|}z{~{zuuuuomouxsprwu|{|y~wtyx~yu}}|{uuy~{{z{w|}|y}|yyttuvpnouyursyv~{|y}~wrw}wzzq|zzytrv{|}|~y~~z~zzvtuvqnosxuqqws~||z}ztx~{z~s}}|xtx|}}z~}wz|}y{~yxwstwrnorxwsrxv}~{|y{suz~u~ryz{zxrux}}{}z{}~{{{xyuswunnpv|||~{|}xrvxzvzu~xu}~~}yz|vvurux|z~~{~xwvrwypjgr}sswt~sn~qfkqzrpwkwywyzwsx}~}yz~z|{xici}}qojn}v{zsvyxuzvxw|}|us||qqxzxzqyz}utwyxw{{vvvpssiglw|z{x{oqwsqsxplthrwuvwtptz{~|vx}}}~xorjcgq}vu{y~}}{~~yt{{~y}y~~w~tsw}x|z|vuy|wruvvuxzuqummsljoy~{y{xru}{yz}yownpyvxxwrty}~|~~wy~~vvshnlflu|y{~}}{|zu|}~y|z}w~{~ttyz}|}xvz}wruuutxytpumltnms{|w{ysx{yz}ypxoryxyzxsu{|{|}wz~~~~|stpeomipz|}}}~}y}xw~~}z}z}z||z{su{~|{~wy}z}ustttuxxqquipunpv}zw|ws|{|}vtwowyyzzvtw~{z~{yw{}~~}|~wqvjhrklt{}~}~~y~v{}{}{z~~|w{vrv}~}}w}y}xststtwytowokwqos{|v|zqw|~|uzsvzz{{ytv|z|yz~|vx|~}~}}|~zqwpgsnkpy~{~~yuy{}~~||~{xywqsx}~y|y}ystsstwyuowqkxrnqx}w}{pux}x{uw{{||ytv|y{yz}{vvx{}~|||~yqypiunjmv{}~zuxx{~}~~zyyxqqty||z}zstssswyvoxtkyuopv}}y}~qsux|}yy{|}}zuuzy~}w{{}wuvx|}{{{}~{rxvhtrklr{{~~}|wwxw~}x|wyspqt}{|~uutssvyxpszktyqpsyyyqtywqptx|zz{{~}yy|wz}~|{|~wpxwuhk|wttwx{|}yx~{{{x{xpstwtsss~|~{z~}yyvsps{{}qyxz|z{{zy~sssowzxstx{}|{|}~zwwzv}|zzrkkuppxpmmpswxysu}}||y|~~xrstx{{tqoo{~x{ww{|{z{tsomow~|v}|}~~sx}zwuouywrruxzzxy{{}~|vsuzz~|ysniio{}xztrsuxyzts}{z~|zw~}xqnsy|}wtrzz|zwz||x}|sromow|w}}}z{rvyyruzyusux{|zx}||~~}wsu{|~|}wplhjq}w~wuuwz||wt~{z|~{~zv}}wqou{~xtr{{}{xz||w|{sqnmpy}xzyyqw{zsvzyutvy{}yw~||~~|wtv|}~|||}snkhktyywwx{}}vv~{y{}|z~xw}~~{uppx|}wtt~{~zy|{xv}wrpmns{}x}~|xzvq|~}xty{yuvxz|}wy{|~}zuty|}}|x}ypmiinx{~zxy{~{t}|||x{|z}|u||~|xrpsy{{wt{|~}y{}}|uz{sqnmow~}{~|}|xyypv|ux{zwvwy{yv|{}~{wtvzz~|x{{rnkhjs}}zz{~}uz~}y~xz{z|}ty}}~}ytpquvzv|}~~z{~|~tz{trolmtz~}~~|||y{yot{wz|{xwxy{yv}{~~|xuvxv~|x}{spkhipx}}|}|t|~|y~wy{z~}tx~}}~~zuqqsr~}y~~{||ty{uspmmrx{{~|{{y{{pry|z{}|yxxy{{tz}~}yvuwt}}x{}uqmhhmty|~~}}zzxyw|~~zxxzzxtwy~utxvotwlmtpnnllpw{|~}|vvvvzrow{{svunotxou~xx{{zwvxvx~{x~}v|xrqnlov}xtxwtqomtsmorszwpv{~vx}}zwxxyutx}yxysowtjpsnnljkpuyy}z|~|yzz}vxsov}wyuisxrtvvtsrspw|{}~ytw}yzvurqu|}vt{ysotrklnqwrmtx{zu{~}{|{||snw~{tzvjpqmlkhiotxx~~{{}yzzxy{qs{~pv}uuwwusssqw}z|{}xtw~w~zvusru|yvxzsvvnmorxunty||w|z}||||~|yroxxz{oosonmkkouyz}|}|}wxy|zx}{tt}st~xuvwvutusvz~}x{xuww|{vvtsv|zx{|tvxpmorxwpuz}~~}x}y~|}}|{yxrqzyz|posonmllpvyy}|~}~|~wwx}zy{||uwsuyuwxwvuusx~y}{vzxuz~w|yvvttw}~zz}|uwxpnptzvqw{~{}{yz~~~}}|}wwvrty|zossoonlnswyz~}|~}}}{vwzx|}z}zu{~r|xvxxwvvts|zywvywvyy{wvvtty~~}z{zvzvoorxzrsy|}zx{y~~}}{yzvwtsz|xtsuustwuuu{{yy|~xwz||}~imyusuwvvvury{wzxswwv|{~{zzyy}|y{wsmrrkjmuysty{}~~}zv|zpp~z~xqhj~zvzspusssrqsvxwz{yvsx~~}zszxpquy}~~~qv{wy{{zyvrx{rlq|}|}y}}{ytpw}upqw~}|vzzrpsy{rprsu{zyx|zton~yopnkouu|~zvvnkkkkoqqtty}|{}|}{rvyy~|u|yw{|q{ponlrzupryvpuxx|{u{wvw{}}~~}lk|qilortuuy~xpx~xux|~yz|w{z{xy|{~xnv}rjgiklnqqtuv|}{{~{wyurz{}|}}z||u{vljrqjjptmlrsu{||||vz{yz|}~wtrmt|wvtssssxzpnxyssvz~tw~}~z~{~y}ywtu}}wsrqprrputv{{yz}}utxuu|{~zpy}{uuzunmsummrsv|{|{y{y}|z{|}xprtrx~}{yxwv{{qozzttw{~rw}y}z}x|vwvyyvutsutrwvw||{{|tsxwv|~zxoxwv|wpnsvomruw}|||x{{|z{|}xoqww{~}{zyxwz}tnx|vux{~ss~yz{yzvvz}zwutsuurvxv{~}}}{uqw{y|}{|ynvxu{yqorwrmrux~~||w{}|zz{|~~~}wpqz{}~~|zzywy~vox}xvx{~sr~y~~zzyy~~vv|zwvutvvrwyw{~~|zuqx|{}}{|wnwxw}yrpsxrnrwz~|{w{~{zz{|~}zuos|}}|{zx{~tpz}xwy{~~|qu|y|y{xz|ux~{xwvvxuszyx}~~~~{xsrz|{~yrq}wz~wqqvwpouy~|~xw|~{z{{}||xrpw}}~}|zz~{qu~{wxz|~~~vq{y}yy~{w}xuz|zyxzxsy|x{~}{yuqv{z|~z|wpx{|{truytosx|||zvy|{{{|{~zuot{{~|{~}st}|xwy{}~~~xqxz}zy}|w{ytx}{z|zuy}y{~||zvqtzxz{|wqw~}vtw{uoty}z{zux}~|}}|||}{|vpsyx|~}~tu~|xwxz|}}~xrx{~zy}}w{{tv}~}~|vz{{||{wqsxww|{~zrvyvx|xqsy~{z|uu|~{{~~}||}{~}xqqwxx~xt|~ywwy{}}~{su~~~}}|y{xx~~vsz~yx~z~||}{yspvxv{y~usz~yw{}vqw|z}xsx~}z|~~}}|}}{}zupsxx{vw~}yxxz|}}wsx~|zy|}wz{st}~y|~|~{~|zxrqwxw~z~~~{ss|}yx||urx~~z~~wsy}z|~~}}|}|{{ytosyz~wx~}zyyz|}}vsx{zy|}wyzrt~y||~{{zwrqwzy~z~}~zss{~yx|}wrw~z}xrx|}~~}||}}z~zxups{}yx~~{zyz|}}~wsv{~zyzxw~{ts~zz|~{}|yxtpv||z}~}}~~}{tszzx{~zsv~{}ysw}}~~}|||~}y}~ywuqr{{x}|zzz{||}xtv{~zxz~xw}~{ttzz}~{}{yxtqv}}{}~}|}}}}~ztt{zy|~ztw{}~xsy}}~~}||||y~||xwtqt}zz~~|zzz|||~}vtx{~yx{~ww~~~ysvy~}}{~zxwrry~~~{}|||}|}}vsv}zz~~xu{}{|us~|}~}}||~y|{~ywvrqxz|}{zz{||}yuv||~{yy}zv{}{ur}||~{}{yxtqu|}|}}|{||||~~xstz|{~{uy{~~wrz||}~}}}~zz{}zwvspt}|}~|{z{|}~zuuz}~|yy}{vz|{tpz}}~{~|yxtptz|~|}}|{{||}~wsty~}|wz|}vqy~zz|}~~~z{~{~zxvsorz~~~}{{{|}~zvuy}}zz}|wz||tow}~{~|zyupry|}~~}~}|{{||}xttwyz|xqvyy{}}~~|z{|{xwtoow|~|{{}}~|wvw~~~~{z{xw|}}wpr~}|~||~{ywrpu{}}}}~~|{z{||~{ustz}y~~}|tq{|xy{}~~z}|z~ywvrnrz~~|{|}}yvvy~}}zz}}wy{~~{tov|}{~}zywqqw|~~}}}~}|zz{{|~~yttu{}y~~{sr}}yz{}~~~y~~{y|xvuqns{~|||}~yvwy}}zz}}wy~{~~{tov~~{~}zywrqw~}~|}}{zyzz{}~yttu{~z~~~|tr|zz|}~~~y|}{x}}xvusor{~}||}}zwwy}}zy{~~yw~{|~}{wqt~||~zyxtqv~}~|{|{yxyyz|}zuuvzz}}|vr{|{|}}~~yz}zx||wvutpr{}|||}~zwxy}}zyz}~yw}z{~~~~}{wqu~{|}zyxtqv}~~{{{zyxxyz|}ytuw|{~~|us}|{|}~~~}x{|~~yw|{vuuspt~~~}|||}~xwxz~||yy{~}xx|y|~~~~}|zuqy~{|yywsry~}~}zz{yxxxyz}{vtvx~|~~ysv{{|}~~zy~|{wy}xuuuqpx~~}|}}zwxy}|~{yz}~{w|~z{}~~~}}{vqt|}}zyxtqv|}~{yzzxxxyz||wtuw{}ztt}{z{}~~|y~|~|wy~~yvutrot~yxyxxxyz}~zuuttz~|{{~~{t{tsvzzx|~~{zywtvz{}~||~}wwtyzxvuvy{zw{~{vuz{wz}{~{zwy|yyyxwvx{|xxz{{|}~|yyxxz{{~}}~zzxsv|yywsqu|~zwz~}z|~{zyuppuy|zxww}yusstwwtv{|z{}}ysptxxx|~|tpquy~|ywvyzwuvxxyyz|~~{wvusuyz}xrqtuu{|ww{~|xwz|{yy}}{yxvqorw{}{wsrtwz|yxyzxvy{zz{}~~}{vrqvxxz~{tonrw{|zz|{wvxyyyz{}~}ywvutvy{~{toqtux}yz}~{xy{|{y{~|zyxuqpsy|~}yvrqux||zyzzxwy{zz|}~~~}|zurrvyz||xsnnsy~|zz|{wwxyzzz{|}|ywvttvz}~ysoptx|}z{}~|yy{|{z{}~|zywuqptz~{wtqquz}{zz{yxy{zz|}}}}}{yvrrvz|}zwrnns{}{z||ywxyzzz{|||ywvtsv{|wrnou{~{z|}}zyz|{zz|}|zywusqtz~yvspqu|~{zz{{yyz{z{||||{zxvsrv{|yuqnou}}{{||zxyzzz{{{|{ywvutv|}zvqopv}|{|~}zyz{{zz||{yxwusru|{wtrpqw~|{{|{zz{{{|||||{zxussw|}zwtqnpw}|{}|yxyzzz{{{{zxvuttx}~{wtporx}|}~|zz{|{z{||zyxvussw~}yvsqpsy}||}{zz{{{||||{zywtsuy}~{xuroosz~}}}|yyz{{{{{{zywvutvz~~{xurppuz~~~|z{|{{{||{yxwvtsuz}zwtrpqv|~}}|{{{{{||||{zyxvttwz~~{yvsqoqv|~}zzz{{{{{{{yxwvuux|~|zvsqprw|}{{||{{||{zyxvusux|}{xusqqsx}~|{{{|||||{zyxvttvx{~~|zwurppty}~|{{{|{{{|{zxxvuuwy|~|zwtqpqtx}~||||{{||{zyxwuttwz|~{yvtrqruy~~||||||||{{zywuuuwy|}{xvsqprvz~}||||||||{zyxwvuvxz}~}{xusqqsvy~~}|||||||{zyxwutvx{}~|ywusqqsw{~}}|}}|||{zyxwuuvxz}}{ywurpqtw{}|||||||{{zyxwvuvy{~~}{ywtrqqtw{~}}}||||{{zyxvuuvy{~~|zxvtrqqtx|~}}}}}|||{zyxvuuwy{~}|zxvsqpquy}~}}}||||{{yxxwvuwy|~}|zxvsrqruy}~}}}}|||{zyxwvuuwz}~}{ywusrqruz~~}}}}}||{{zyxvvvwz}~}|zxvusqqrvz}}}}|||{zyxwwvvwz~~}|zxvtsqqrvz}}}}|||{zyxwvvvx{~~~|{zxvtsrqsv{~}}}}||{zyxwvvvx{~~}|{ywvtrqqsw|~}}}||{{zyxwwvvx{~}|{ywutrqrtw|~}}}||{{zyxwvvvx|~~}{zxwusrrrtx}~}}}}|{{zyxwvvwy|~}|{zxvusrqrtx}~}}}||{{zyxwwvwy|~~}|{yxvtsrqruy~~~}}||{{zyxwvvwz}~~}|zywvtsrrsuy~~~~}}|{{zyxwvwxz}~}}|zywutrrrsvz~~}}||{zyxxwwwxz}~~}|{zxwutrrrsvz~~}}|{zyxxwvwx{~~~}|{zxvusrrrtw{~~~}}|{zyxxwwwy{~~}}|zywvtsrqrtx|~~}||{zyxwwwwy|~~~}|zywvtsrrrtx|~~}}|{zyxwwwxy|~~~}|{zxwutsrrsux}~~}||{zyxwwwxz|~~}|{yxvutrrrsuy}~~}||{zyxwwwxz|~~}|{yxvusrrrsvy}~~}|{{zyxwwwxz}~}|{zywvtsrrrtvz~~}|{{zyxwwwy{}~}|{zywvtsrrrtwz~~}|{zzyxwwxy{}~~}|{zxwutsrrrtw{~}|{zyyxwwxy{}~~}|zyxvutsrrsux{~}|{zyyxwwxy{~~}||zyxvutsrrsux{~~}|{zyxxwwxz|~~~}|{zywvusrrrsux|~}|{zyxxwwxz|~~~}|{zywvtsrrrsvy|~}|{zyxxwwxz|~}}|{yxwutsrrrsvy}~~}|{zyxxwxyz}~}}|{yxwutsrrrtvy}~}|{zyxxwxy{}~}|{zyxvutsrrrtwz~~~}|{zyxxxxy{}~}|{zywvutsrrstwz~~~}|{zyxxxxy{}~}}|{zxwvussrrstw{~~}|{zyxxxxy{~~}|{zyxwvtsrrrsux{~}}|{zyxxxxz|~~}|{zyxwutsrrrsux|~}}|{zyxxxyz|~~}|{zyxvutsrrrsux|~}|{zzyxxxyz|~~}|{zywvutsrrstvy|~}|{zzyxxxyz|~}|{zxwvutsrrstvy}~}|{zyyxxxy{}~}|{zyxwvussrrstvz}~~|}|y~|~{ywwsqpnmnjnnmknokoqstz}{yzyzy|vvutonmnsz||wwzx}~~||xspnlryzxupvuwywvuvuxzy|}}~}{zslklollegmomijnv}rlv|kihjlpst{{{ux{xunpqqrv|~{wxysjjrwunntz|y~oewvninoprwyy~~txyzz}zursx{{xuxw~zzsf~w}}|wwvt}zpmu~vvxplkncdi`jopkd`dmpy|wwy~{}~r}y||z~rorzyf{ul{x~|~uvw{x{rxtxzysnlo{j||u{{yu|tjwwvrdlmqrjj^jplgddiyzzyvywyvyzz~xy|u{{t~{xunzmvvl{zyxu{|zr}~v~|z~~|yz|{wylrqst}}{{{wp{slxxugp{rstpfclibaijuz|vyzzuxx~~{|v|v~|~w|{xv}wsuo|m{|pvuyu|{||{vw}{|v~|||~}x|w{xntm{}|z~z|}tsqnxyqht}rsvpfemj`bjky~||xu~zuzw~zyv}w~zy{ut~|vwtr}m||swu{x}~||{tv{||v~~|}{}xxtywpum|}z~y}}vrrmwzsgp|tsxqheonbcjkwyx{xwyu~~yyt~yz}xzrp{x}uvwrqxwzv{zz~~vs|~u||zxwsruxsxoy|z|x|~xq{skszvhiwtqzrkfouhdkluz~xvyzv|yzs}{}{~wyonuvzswxtsw{}x}}z~vr}t|{}zx~}vr}qwxu{q|~{}x~~xs~rnszugivsr{qkgrvgfmmv{{|v~z}}v~zztz}{|xxymlwzuupxuzq|{~|~su}zt}~{}||{vtsxqyu|zs~~w{xv}oqv|oemvnxzojl{tgmoqz{uv~||xz}~wy{||w|}|plmsvpuxtxs}}}yr|}}syy}~||y|xts|~pwvz}sx{ysrt}vgjupt}rmjzzkmppv~}~x{x{}|zx{v{{{x{{sml{uuorwrzr}{sz{~swy}}{~yzvts|}pvu{}s~}~y}~uuuxhjuov~rol}{loqpu|{wxyz{}yy|w|{{}x||snl|usnsuqxr}|sz{}swy~||z~}yx}wsryquu{~s}|||~xwt}}kitqr~tqmzoorqsy||z~s{}{}xx~{|}yz}woltyrnqwn|q|vv{wt{~||y{}}|x||zqruwrvvvz~y{xwwuhpvoz{sqrxossst}yyr~{zx{~z~}}||w~~~}|snk{tqmwqsut~~tzy~uw}~{|~~x||}|w~y|toqw~sstzt{|~}zwztirup}ytrvvquutu{|q{~yw|~z|~}zw}}~~zrnk{sonypuuw~tzx~ux|}{|w{{||w}}y~zqmou}sstzu||~|xxxjqup{{uttzsvvvt~}{s}{~yx{~{~~}{w~z}|yrnjuvpmxtpyw~vwzxwz||wyz{}~xy~|}zx|qlmq{vquywz}}zv~lovpw}vwt~twxxs{~{xx}}zy{}}~~|w|y~~{xrnjr~xqmyvp{x}wwz~yxz{|wxy{|}xx|z|ywznkkq{vsuzwz~zv}mpvqx{wxv}uxyxs|}v{~}zy|}}~~zv~v~|{vqmju}vpp|rux{}~vy}xxy{{~|vwz{}~~vyz{||{vztkjluzstvxzw|wnuus|zyw{yxy{vu|s}{|~y}~|~~}wx{wyzqojmzztowzmx~~~zv~~}{{xyz~}xww{{wwyy~}~}yyu{mkkqztsu}vyzyptvs{}z{y}yz{xs~}u}}|x|}}~~~xu~u{~|qoklw{wpu{mv~}u}}z|xx~y|}yvvzywvxx~|}}wzt|mkjqxtsu}ux{{ruvu|~||{~zz{xr~}v}}~y|}}~~wu|t{~|poklwzwqvylv~~}u}~z|xx}z|~~zvvzx~wuvv|||}vzt~nljowtrt|tt}yuuwuz}~||{{zr{~yy~y||}zt{s~~|qolksyytu{nyv||~vz~}zyyz|y~~~}~xtxx|~zuuty}z~wxwvulkltvstwyyu~z~uwvw~}~|{|vtw~~~z|~~~~~vu~~yt{znojmuzytywnw~}~v|}~{|yx|y{~}~}}|wtyx~}wutt{{z|tyuypljmuutt{v~ty}wxwy~~}|}uv}x}z{}}~tu~}wuzxnminuzyv{up~x~~v||}{|yx}y{}|~|}~|wtxw~{}vurry{z|syvwrljmtuuu{w~uzxxwy~|~wu~y|z{~~~~vs{|}yr|}{nmilsy|w{ypw~~vz|}|}zx{zy}~|{}}~}~~xsvwz}}xwuspv{x{uuyswmkksuvvzz{z|zzywy~~}~yu~{zz{}}~}wry{|{tyxuxy}~xy~|vxyz|{|{{z}~{|z{zz|~}zs{|xomjgnsrvpqvp}vljjqrsrv}sust~~yv~yxz}{z~y{~wqss{~y{{ynnlrx{wzy~x{{~|x{y{xuwnqtu{{wspts|yst}~wqrlmvwxxrywwtolpuvxxvv}~}z~{{}~~vztginu~|qgb`eirrsqgq|prwxyv~~{xxyz}}{|xy{uru}z}}~zuwx|}uyyvrz~~z}|ywxuw|w~{uqnqu{wzwqvutvonx{~vwv|zy}~z~xzvknsu{w|~somru~t{~|~ywxqmpx|u}}|wslmmpzsuqgrwslmiemqqsnpuoxvmjkrswy}v~xzy{~}|{~~{|~wysgimpwu}ysorszzyuiq{qovv{wqy~}yzxxx|}{z{xwzusu{~u|}z}wxy{s}zzxry~~}|~|xx~uvx~|vqtv~z{{qtutvpnv{~uxt|zy|{|yzznmqrvuwztptv~uyy}}{wyuopsyst~x|ztpqpyzsvjiuvolmfgopsrmstrysmkortz{xvt}}y{|}~|~|wyrhklput}|vuvwvlw|spwwytpw|{|y{yww{}{yywwzusu{|t{}xyyz|ty{{zrw}~~|zy~|utv|}vwxzurvuwtot}~{wvwy}|yy~~}y{x|tlpquxv~ystvz|u~{}zyvuvpoqwxq{}z}|utst}ytujmvtnnnfjpqtqouusxuomrsx}vy~u|yxz}|}z}xsvqhknsxxxvxx{px~wqw{xwouzz}}x~}zxvz{}}{wwuxtsuz}v{x}{yzzyu|yzwwqkmoknoifjlqxz|tsvx}vt~~}xy}s|z}zwtvwzyy~w{~{vwwxzvv~zxqiinp{yvsnv~|~{xvz{~}~}ty{{|{xxxx|zy{}{xpr}xwnovx}|z}|}~vruuxuv~vw}}yvvsw~xvuqut~xwutxx~{}{~}wyx{{xz~|voqtxyzrzx{{~z}|xx|~~~|{~wopnx}ppppplkopknnggjkrwyulv{vow~~{{||}||y|{~~|~~{~~tv{vno}~z~uo||tnrxtqolpryxtzstuzxz|w{~sw||}|{z}zxvv|}uutt~xvyz}zyz~zy}wswx}y~}}|zx}w|xurtr|~}zxvqsoprt{|uwzx||x{wtuvyxx{|}uqtu|vx{uq~~y{~}z}~}zvtyyqooq{~|}|}yuuwrnrlhkkqwyukqyuoo~|{{z~|}||}w||{~}}~{~}wx|wplxz{~{p}wpryuspmpqwzs||rrtyxz|~~vz~st||}|zx}{yxw|}wrssx}||y|}~||~w}zuxx}y}~z}{~y{{sw~~wyw|}zxstqqssz|vsyzz{wy~ytutxxxz|}yrtuzyw|wpxzz}|{|~~}{yuwzvnnpry|~~{{}srummnowzzqlwwsmr~~zyz~y}}}}zvz||}|}|uy{vpkyyy}tzrtzwvrorsy{t{|tqsyx{}~|uz~sr{}}{yw|{yxv{}xqsuv{{~~{}}}x|}wyy}{|y~{yz|y}wuzzz|{|{uttrtsx|ztt|{zxv}}vvtwzyz|z}wtvwu{}ytp~}|z|~|~}}y}zxuswwsmouv{}}}tuwpqqs{}zppzxtmt~|zyz}y~||}}}x{}|~{{yx|~|xxwvjqztkltsrqnqrx~xyztuzz|}|tv~wpxzqv~vy~||}yt{vuvy}z{{}{y~|v|yihtyw}|~|x{z{}yx{xz{}}w}zwz~xurrvvxx}ywrvzy{wvq|~~v|zz~}rjuvromghtux|~~|xsytttv~ut}|xrr}yvuty{~}z}umsy}{{~}wy{y~yqmlx}tngqtqx|zuuu{}~}uwunv|vorxvwsqtv|}x{ysv{{{v~vt||wuru~vz}~}vkkty{{vpt{|~~}w|unptqw~yvsorqrstzzxqu|ywy}zxv{{|{~{vwqqpw~wy~~|}~~{~wronw}yu~zwwy}yxw||{yx~toussru|}zqr{xuoq~|~~xxuvz{~zw~{y}zqmuzxx|}xxqolq|yw|z}~wvtw|}yuxtr{{spwxxwrtuy~{x|~vtz{~|uyyru~{yzurpw|}y|||~ynjqtz~}ts{|~}x}zrrxtv}yuqtsstuzzysrxvx~{yvzz{{|~vxtspu~~|z}}{{{z~yrnjqw|yxwx~{zx}~~}zzqwwwuw~~{rs{yvqp|||}}xxuuxy}yv{}z||sntw{{w~~|ywtqnnw}}~{~~zxvv|}~xyyvy~yswzyzuuwx~}zy}{sw{|~wu~|tqyx|~wtou}ty{wvx}wy{yzw{uw~}|xz{}~y|~yttxuqvvvxx}}}~wz~~}}|st|tqt|{zwv{{{vs|}}{vvv~tvsloyzz{{{y~|~qqxxww}{s{{xuquwyzwzz{xr}yx{}{w~vsswyyzwtuut|}zytqqqvxxuwxqv}xy{qoy|xyxywz}wyzzw|~w{~zyvx~}zrinwursw|ztojmorsrvvwvq|ywytqtvsrwvsiemooppy~{|xwvuxy}}xz~vy}~|~~|~}qlxvmkr}|xywvsrzwnnpqosy||vzt{phmvz|~}{}yu|}x~x|}}{w}wpvxvurw~zzussprsvxsu{|~|wmpwkcilfcgvwv}wrtrsstxwxsr{vzywz~{|yu||uz|twz{}~|{nouomr~~y{}zystvlimsssyzxrpxyvtpt}~~}~x|v{~w}|tor~{||xyvwvwzyzuw}y}}zqkeemw{}||{{tqrutrvrrwwyxvyy~~{|~~~~~~xxsw~}uty}z}}w{~}zx~~{rtuqz|}uouyqx}|ytwwwyy}~~}x||~|upmov{z|yzy{wstx{xz{y~{v}~}xuslqzvsx{vz{{~{xz}~|yzx{yux{}}}~ywzsv{}vx}wqv~xz{}}}~{}y{}}wronsyx||||zxy|~{vy}vqv~xy~y|~~}zwvorytmo{}~y}}~~yvv}}{~}wvzv{~~{|zyzxzxvunu|trz}|ztuvxzy}~zzzuqoorwtuyy||{~{{}||||}|~}wy~xy|}~}yrrzywtqpt{vqrqstsvvzyuuzyyvy{|~~~xsxtty}~}ww~yv|~|vwyzz}ywx~|wxwy||~ywwvyxyvqz|xyz{tpwxrmny|w{}}wvxy}}||yzwtz|tou~}|zy}|{|zz|y|zyy}|wqsqstuxwxwrx~zomuwxwsuvxxw}xz|}ytsprxrqssvvw{~~|{utyxpmxyw}wtzyuqnlmxyrqoprpqqtursv|{y{~~||{zworutx~|y{~~ztpomryvvy{z{y|{{zwwwxzz{sqyxvuy~|xrs{xtos}|xz|zsstuxxzyx{yy~xs{}|{yprwvzz}yw{}xstrsssvstuqu||rnvy{zvwwxxuy}}vu~{vuss|{x{{}zz~{{y{zyxtvvuz~{u{}v{|wsy{xuqpnu|vronpnnnpsqrv}|}yx}}~~{}}wrqooyzwz{~}~xxy|~~~zzzy{z{vntxvtv}}{vv~~yst}}|~zyzy}}}|x{zz~~x~|{x~~~~~}~|{yyyqsxz|z~zxz~yuurrqqsqtutxvtz}}z|{}|{}|}|yywz~|yywwxvvrqrrtxzvt}zu}~{wz~{xwts~xuruututwvtv{~{|{{z{{~~~~|xy~ytpoomvzwxy~~}}}}{}}vz{{|vw~}yuu~~~}}}~{}|}~}v{|}}~}}|~}z{}|{~}~~{{xzwprvxx{}|yzyzzx{zz{~uv|~~~~}xvupv{wuuy{z|}~}||}xw|~yxvtvuurnppqruupw||~zxws|}yuxxwywzyvx|{{|~{tvz|{~}{||~}yuv|yuqpqnu{xxx}~z|~~yx~ytsz~yy|wvvuxyzyvyyzz{zsv~}}x~~yz|z{yzyvwyxv{}~|ttx|{|~~~|~~yv{}}||{|yy{~zy~{{}yvxtv~}zxz~|~~}|}{x}{xty}{{xyzyytuvvwvxtrz{{vt}zsqsqrqqsqrtz{vyy{}|}~|~~~}}~~xz~|x{|xsrrntzxwv|~wv||y}|yuu|{xsry~||}||z}}~|y{{|{z{tt{}~{|xy||~|}|||yz{}wz}}~xuw}}|{{y{xy|||z~|ux{~~~~}z~{vvupv{yvtyyy{|~yz||}xv{}z}{yxvxwywtvxzyx{vw~}~z{vu~wvxwxwwxuvx}|ux{wx}|}zz{|}~{wy}ytrtosz{xvz||~~||}yw}~zvrv{|wzzxxvwxyyuwy{{wzwty}|y}y|{{{z{xxz~vv}|~}|z|xsty|~~~zy{~~~~}~{}}}|~zwy~yustpsz|yvz||~~~{}|}xux|}wywvtqrrrrnqsvwuywv|}}xu|~wtvuuutursuz{rt{}~}~z{}~}~~}~}zy{}}wsqv{xuqrrou|}zy~}{~{yuw|zvqpuy}xuxxywwyy|zx{}|{}yz~y{xxxwwuvtrsu|~touz|}z|~}~{~}yx{~}~{~{xy{~|}}||z|}~~}z{~ywxtt{~|wy|{}}{}}zvv||xtqtx}{uvvwvsuuwwtwy}{x{zz~||vt{}ututvuvvtwy}ux}z{~~}~~|{~}z{}}}~~~}zvw~{vtuqqw{{vx||~}|}|yy{wtwz~xyyzzwyy{zxz|}y{yy|~|}xv}yxxwyxyxuxz{sw}}~|}}xvv~}~~}}~~}z{zxyusx|{vvyxzz}~}w}~{{}xuwy~~wvvwwsttuurtwzytuvux}|x}zwxvxwwwtvy~~uv~}zx~~~}{yy|~|}~}~}||~yutz}zuqrpmpuxtrvwy{}~|zx}}yuux|{wxy{xxxy{xxz|}xuvuvy~z{yyxwxwwttvyvouz}}z||||y{yvsu~~|~}}|~tx{x{{~|{utzwruxz{yzwyyv}us|}z|zwy{~zunpumoqtwxz}ys{z}~|~~qt}opo{z~}}uz~~zx~uwy{uw~u{|stu{~w}{}|}tp{zwxzxx|~vz{}tqrsu~}untz|uvmwx||wms{z~|tzyx{{}~}r}||y||xwvywx~wy~~z~~ssgiou~qwxvxq}wy{upy}{}xwyuvnr{yz~wssyt|yzy|y~}v||{}z{xv~|}|~~w~|zy~yw~zwrxqqy~|~zulvwruulorwvruoks|ws{}w~{~}x}}xx|ys}|vswspyy{v~~|zxz~v}xwv|vrsv|{shpoimuqimoqrxptx}x~zyxz}zxvmx{}||{|}u{}wz|~|~~{y{nppmfs}|~}{yuz}qttwx||uz~wpwvyz}tx{xux{wo{xw||w~|xvxs|||zw}|{y||ylnw~~xrqrtuwlqx}vwwmmuuy}}zw{w|}||v|vv~~srrtrv|zuy{z}uyx{zsz~|~~|tz~voooksuqkpojhwupsusz{|{v|{{{yno|z}|}|zv{|vvt|{|pnsohjlwz{~syypp}~xuyrsusw{ywyso~~{zuu{{{}{xywuxz~~~u{wwx}x~}{|~}~|rlp{yz~x~yqo|yoqutyzumpzy}}xu~|{yy{z~wyv}~}ysw{rpsrrv|}~|}|~~~wuz{vwyr~vxyvzwturjuyvsxsoovxu|~rr{}~}}qwu{uz|uv{{~zz~|~|}~sxvthjnhhs~z}{xyyvyurvwtvytuux~yyvu{|vzzz|~zqs|yuy|y{|zy}yz~z{|~~}}rnruuwvxysp}zzvmr}y{|w{}wz~}xyz{}~w|ss}znnstylsuv}x~~~}~{sos~|rsv{x}}ut{tpstrqurtsurryy}z}vyutv{{~zz|~tr|}w{~{}|}x}tyyw|tilinpps}}zwzvz~}|xquut|{{tsvvsxzryxpv{xw||tq}~|~{{{z{|x{}y}|z||xztiovz}}y|yq}x~zsspr|{z~{{}}}}z~yzy~{|uzuwyvyvquxtv~~v{~zw{rqyxutw}~z{|uwxorusstswtmuxu~|y|}xr||yyssz|z}{}~~}~z|z}}}zypwvopmlllowvw}{}||}}~{}zou~zsxwu{{tz}svyu{|z}v|xv~y{||}v}y}x{|y~z~{}~yzvqnotx{z}|{vzs||~xvzy{x|}}{xu||w}zsxxuysprpmvut{}}}zy~~xtmiltvt|~yxswz{xu{wqutzws~}}|sw~wy||w~}wvvp|}{}{~{|}~v|zuwrpvrnjmnmnqzz{}}v{w}z}wuww{xrxxzwvzxxx~zvupx{yx{{}{~{~|w~|~~|}}z}tvtx{souu}v{y~}}tw|x~}||}zwyty}z}~sw{zztqytoqpyxw~zyuy}|x~|x|{{{wuqouvyv|~z}{|y~qt~wxzxsyyx~~}svw|~z}~wqsntwxv{y|y~|zznomrvolwuttkqz|}||{s}}wxtn{}rtou|ww~}~v{}ysz|x|x|~{|{ttxtx~{z~~z}utpotuv~~z|}|zys|~~~}{~yx~x}}~utzuqtrjswnnqy~xz{}t~}~yp|x|vovtpv}w{us}~vyu~}{u|vv|tym|ypst~|wz~x{xq{qwtv}{z}s{z}wzuxst}|totkxqoolwloxxsxz{}wys{v|ruwsrzvo||ypxtw{}z|~|z}y~~s~~wz|}y}vw~|vynp||~|z}{~v|}||}xu{y{{|y||ys}wqrxrosttu{~}}y~u|}~tyywxuzyzurroxytt{xuy|}zzu~|qu}{z~y|xv{~|}{~}~{tszvhr~~~|{}y~y~~wv}|xs{upmvungprppzsu}wy{w{|}|wo|{syxvu~~{~}tsytu}}w{z}{tz|tzww|~{}zv}vz|tuqpmuzwzvxy{}wxy{yvxyspvrpsylnqwqy~}yx~xtwumxurszw~|yuvpjqzuw}yyyzur}|w|~ypw~|~{}zt|wv{}~}~}z{z~}|}~~||xy{y|{wwxojtzuovplkowu~~}|v{u}wt|vw|{ys}yz|wv~{}wruu}x|~}z~y}wx|xsy}|ys|yzzyx{szzsmsvv}|{}}x}{v}zwt|xy|tx{wosslquvtu{}~||t}}wt~{~tttyy{~uwqgltvrux}{y}u|~{x{}~~|}~~}wxywryxwxy~{~|yzyy}}~~{z|wyvsyvoojtsrpjorqrvz~~zw~zu{~xxz{tu|yz}{}unu{{y}|||z}zv}}|z~yy|v|~{~|x{}ytvsnsno|{zs~|uy}~|{v}|{}vsyxtvrowsrmxwsv~|w~yxyzzwzv|{wvtv|vu|~{yuksrrnuwtrz|}|}xy{~|~{q{}{}|y~v{z{x|rlqrsoqohfsxljpso~nkw~~{~trw~uzz}uy~}zzvspvyrspu{{yy{x}w{wtxrx{u||{y}}vvywy|u|vzr}qmvyvq|rpw}{x|~syzzxw|w~vvz}qyvrmw|~{y}z~~{zy{}zq{|{|||}{{|}uyvqwqruwwm~seqwpu{odtttpwy|vsk|lryzytwuuyz{|yx~y~z{~ywz{tysgg|{yxzzwv}z~zvw~z{}}w||~}zv~sss|s~|u~wzrls~ocd|xmgtn`tgmitkkx|kbw|pmou}zsu{wy{||~uwuqwsfzvx~}txxw|y}z|{y{w}xx|~}|{|pwz~|z~wnuqzxo_{urpnkvlln}}wsulwzzrtvy~zwy}yxw~~w|}w~}t~v|~snrhrwlsz{wm|x{zn}||~}z{|{tuoswyxywtk{{|lsvrdotkqplgkpegyzvusnoorpwy~txy{~q}yy}~{~}xx|s}puput|~u}{|x|z|ty{z{x~zzy~||wwvvyuqqz}y|~zqnqlytpszkgtvqowx~xtk}xs|xwxtqv~}}|u|~~~}|}vss|wt|uumiivxt~vyz}v{{~}~y~}||~y{snzplp|z|u}ywur}nhgy{rfsjgokrkosv|}lfr|trs~ytxxt|}|{}~}}y{x|~unvjs{~t~wy||z}zvs|~|~u|smtxtxy}y}wy|mj~zpopsqnouyx}ssm}wu}s}zz{yx|~z~}yw~|yw~zx|v}usvyklrmnz|w|{ysr}~}|u}|~||v}vtrmuqvxuv}}}tmrxsjrzzulnhntgor{ztvxpgqvzv~tq}}o{zzz~x{{tzqvtkvr}~}yq}||~vrw~|}y~{|~|}z{zxwttwwwqq~w~}zzxuvt|~zzujn{yusw{umwu~|vyysrx}z~~w~u}{y~zy}rp}xto{|vhght~~|y}{wz~~~}v{||~}{|wr|opwulpzyxy{{~}}wqllu|zowklnqusmssnlxz|xwzv~zrw}~z~|w~y~w}z~wqsokz~x}|{~{~z{|~|||v~~t||}~~x{||}w~ww|olpxpsu{}t~unxwttxrvrwuw~x}ku{}p~{|||y|{}{v~|~zw~u}t~surtxwposot}{~xxqz}}v{}{~|}}{uupsjtrquvxy}zquyrsz~qqpkzqotq~yz|}ljt~||~}syx~|yzx~vxtwvqv{~psyz}q}|}vrw~z}~}~}}y~~|}y{t~xyyyxnqqvuzwx|~sxyz~toq~zzx~xttvy}|zrz|z|txwz{|~zx}yqoqrnnxxlknv||}y}tz~~|{~x~zz~vxoyowjrwyrw}x~}tpmpt|}|xmunzyzsx{vsrxys}r|{}yv~t|vx|uv|wwov|zy~|{~y{y|~yvz}{{}||vx~~w{vxtr{uxnlnxsrvz{z}vty}zz|szwzxt~nw{t|{~~|w~vyy|}yyy|~svv}tquuwwxtutw|zxyw~||{{~x}}}}}}zvz{}}s|vospkusrqvywzyzyz~xxqt}twvs{~~ymmy}~x|x}}}}wz|}~{uzsqrw{xvry|~~zy|}~|tuyxyw{|}|x~xw}zyx{rv}p|yzx|x}portu{zzwv||u}vvw}zzry|w}u{yz~}y}tz{}~|ysttrmplpxvnkqs~y~|~tz{}~~||yz~s}x}|y{{rwu|tvplsyyxy{xs{xssvvuywr~{{zzxvx}}~s|vzz}~ywu|{|u}|xzyqx|}uqv{~~|{~~}~~~}{~}|yu~t}uw~z~{z~xvtzvyurtnxztnklttsv~u|{zx{~yy||yzuu~y{vv{zt~z{~}{zwx}vywwx~suvvrvyxwwz{}x|yz}~y~yyy}~~z{|rt{|z{nxynsplsutqvz~{x~}}~ww}|yyzt~vor|~vxx{yz~}{|}y{rzspt{u{wx||}{|~}wvww{u|y}{}}}{vu{y}zwx{vumwy|osxu{}u{xttppttz}yw~}}~|~z{~~~zs}~|w|zzz}{vv{|~~~||~stpurnmos|xrqvw~~}}xyz|~z~~}wwx~z}}}px|~|{vxxxuxuovyruxmnt{|}}ww}~uvwyzxv}|}|yz}y~~{z||xvv}}z{~ytxz}ytsz|~xvy}}z{~~~~{~|}~}zwuz~|t{qu}w}xzzv~xxsmw{tvqosot}snkptxz|xz|{{|}~xxx}y~|x|vw|zy~{yxtyvzrtxz~wwwsqy}}z~{|{yw||y|~yy~~wux~~yt{y|~qnt|}}wvqryptqmrvxuy{~wy~~~zv~yqtzxww}z|}~}xz|wwxsyuu{vt~szqx{zz|ywvutu{yxyx{~rouzzuy}{}yytxssx|xrsrekjjmpvz|tw{w}yz}tqyzzyv}|{zz{wy|up|zuzwpnnw|ttu}~{{|yz|{zz{qjlw|rrqpoojhpuvwsqmoorpmrxgrwjmtz}{ux|wusy||~~{z{~}z{mdipw{|~|w}qpsvwuv{{z{vu{|wuv{}~yvx~~}zyvxttnjt{{z~xz{{zqozxxwtqxvxwsrwy|{zpps}|uz}roquy}qntru|{xv~~vyxsvy{v}{}yx}}y}os{xzxrqlr||}~}}|t{ztxtnijruvyqvywpebdmqurngljjmokikottvw{x{wy|~}~xwy~y}qrrkmnnnj{||u|{ru}{y~uqupu}{{|wvvxtx}z}{|zwxzy}t|ukqtvxuxovyswos|{rt|{|~zvwy{w|{w|smlqwz|ywvomytw|w}ssz}|w|~zy|srvvoormqu~{z{|yzvqnjks{wvljqrspmefhmkmmtigvmjqvx~zstw{{ww{|}|~xuoqxtqtkovyytx|yzzzs|wuw~|ztuu{~~|xys~x{|~{xuups|suxooy||z{x{z~{qqyx{xxu||{~yy|}v|{wv{s~usvv{{spuwt|vy|x|xvvy|w}|{}yz~vyyln{wx}wtry~~z|xskpupsxqopyxxuwzypjfhntvtoisoirvppotx{z}}z|{y{|y{zz|~||~}{qinphswuny~zwtzzx~|ttvrx}~xwvzwy~x~}u}}wvvz|}wuzuvypsvv}u{vtzwzpqz}~xs||~zxuuw~~z|{zz}|xv}}~~vv||}uwvpv|{|xyzw~w{qv~zux}~}~|~xwvy{zszww}so|~wntvuy~{~xzupv{}|wqqv||z|}{{vvx~~~{|~~|v|~||~|yoks|xvpmnkkxzxttw}~~|rrz{}u~~}{}x{z}{sqry{yspw~}vx{y}uuz{rtxz||~{|yz}wvv{{tpty{zuo{wtvyw}|zv||}~|{z}{z|{yzysr~zv}wtv|||{|yniqsvx{~vqst|}wjqsojmtoorpkjgjowwvpr{tl{}xvv{||}y~~yz~~}}{trnpry{zzv|wtx|yyyzwoouptzzysvwvy}yv}y|}{|zt|~zrooty~~{xx|~~~ux{y~z~~ss|||{trry~{zx{~~vt}uot{xuosw~|x{vw{{zx}}w{}{wpuxwv~~}~~x}~z}rprkkt~zzzzywxslpwnlmijrsupjglruzystxrvx|}}yyz||}ywz}||}ysv{xwsqnsx}ww~|vuu{~{vwpmtw~}uo{{xw||{~~|{~{zz~xvy{|zxssu{||vy~xxy~||u{xy||xxwxwxy{~wvy}|{rvvstywy{~|ywv}{~}~{||zuuxwzzwq|{xyyy~z}z||vttxslmvy~~ywwzwpwsqnoutoqomljkpvvvsw{vn{}yz}}}~zwyv{}~|wwuspqqtx~}yw|~}{{xtpwttwywvyvsx}zv~}{}{}|{}qxz{|{trprv}~}{y~~zy~}z}uu|z}|}yvsuw~|~~vyunx{zuruzzy|zxz~z}|s{zuv{ywsuzyx~xy|~~||~z}x{x{zqotmks~}yzuy{}z~wrtvz}zpqzmmuzy}~zyxz}xx~}wrqvspkotz|xwvoz~ytouuusplmrw|~v}~z{}y{uuyxpxxtsxvy~xrxzxz}}}y{~}xx|}xu}ytwwtvxrz~{w{z{|}~zqrrsstpru~}~}wuyuqrxxwxxxvwtu|~v}~}xspvuu{snpuvvtwvwwyw}~tz|~~{w~uswzvopvxzz||~}zzwtrtsrrvxyyz}}|w}|vt|yuuvutwxtt}wpt}w}{z{}}~{v{|vwtuuvuxzzz{~y}}v|{vtwwzxrq{~}yx|}~~w~}xuwyrx{rlntx|zvqxurz}|{|{w|{zwvstrv{|wy~{wrz{{uuv{}zy~{~}~~zwy}}{wz|yrnnpuxytuy{x|{~zs|~{zxqwxpmsy|}}{|y{|wsvxwz|t~|z~|vx~ysvyysppxzvrvy~wt}~}~}uw}yoruxyvrortxz~z~}|w}}tv{wvy}|~v~yvtwy|{{wswutsvyzxzyry~yxu{~|wz{qppsuwsst{{wyvsw|z{yywzxyzy~{zx{~xwvtu{yx{|zxwuvvxw{{oz|vz}~zvyxxrw{vqvzwqsxyzz}}~}vqpmkmosqprrv}{{{~|~{y}{||{wutw|}~~xz}~xxw{|~zz{vr}|vutvvvuxwxwzzurwvv}zy|}vv~~yz{}v{yxttsrswyy{}zy{vx{~{x}}y}{}~{}~xx}wvvxxzwy{|zzuquxwtttxz~{xw}|}ysu}zyxyz|~xqqx|y}{{zy|~tnrmgjxx||zuuxxsx{|trx~z}|ry{pgpwxxwusttvx}{xr}xpw}}z{sz}z|||x{pkkmpr}|xrpnmjoqmpqjinqttyz{xp|llvuty}|~{uv~}z{xy|{~~~{txsoq}wtuwvrqrxwqmv}uz~~ovwt|vpllnopszvps~{tou{}}~}z{wy{|~rrxwv}~{ztrvzty{xuy|}{{}~}}yqswuw~vustqigmlpz~zwvz{~yvy~|wuy|}~}|yzx~~tvxqmt}|{{wuv|~wnu~zzyxsnmvutz{z~xy{xxw{|yrt{xtpqx{xvyyz{}}}~}}}rnxywx}}vsxyxy}{vsy{}{}yz}~{yxwmjlqx|y|}yvwu{ysquvxyzxvqr}~{y}||swvmfr|~}||vss||pmx}}{z}~~}{~|zx{}}xw~{z~|umsy|zxuvuvvz{{zyzwszywy|wyxtwzzzz}{vswwyzytz}}~|tqz{~}}wqt{{|yz|~z~}}~|}{tlt~upy|yyumktwqpt}~}y|}~|||~zw}}~}|}}~{wnt{xs|}}x{~x|}wyxslpsqpt{~|z{yyz|{vpoopvttx}|~}|}}~~~z}~}|vxwtt{{y~~}{xyxzujhtvpov}zyutxwrv}}xst{}|wxxyttxzv{{|{w{{|~~|}}v|xxvts|~y}{{wxvpjovuuvw{}vwuuttqpqtvvwuuy~z{{~~~zxz{yxzzx}~{yy{}xw{~}}|}xz|{{|slmuzxvy~}ywoovxompuxtnmsuurxy}}{}~ww~{snw}~}}{{}|xxzxv|{zuz~~}yz}xwtsrsqrqqrvrnruvuwwwwyvvy}z|}zz{z{zyrqtzxz{|~|}}{wrpx}~||}trwxvrsuyvqnswvor{{vvxwwwyy|zur~|ty}}~|zzzwvuvspq}|z~|{wvtplnw~|{yzwtxyurvyyvwwyy{uqv~zxzxtv|xsrttx~wuxyxywwy}{ww}}~{uv~yvz{|y|~}zu{ztz}}{{|yxxz{zpn}uosuwxwtrsqqu|xtx|x~}~~}~{~~xwtsoptxy~wy~~~yty|yuurpx|vpqqrstpqsxxxz{y{|~}x~}|||wttwwpioy{|~yt}~xwxxyxwuvqlpyupqwzxttz~}|}ysu{}}~|||z~~~}ytwxxutqsy|{||v{zxwuuuusvxwtwyz{|{~~}|~~~~}{wtu||||}|~|{|}x}}~~}zzwurrv~|xxwuz~zyz~{wxz}}}}~~zuy~w{~|~}|}|zy{|y}|}~~|x|}|}}|xsptstuuv|}~~~x|{xv}~zuvvwz|yz~~|z{~}{~|z}~zzz}wy|vrsrtporvttx~~xr}~uuwy{zwvwuvx~}{}|yty{{z~}z{|~|yywzyxsrponmkqx{~}~{|xvz|wvuwyywy||zz}{y{z{uqt~~z{~z|~yw}yyyyvtsutrswzwqnz~{~~|||xtv~}zz~{vx{}|z{{x{|}|~}zw}{xx}~y|}{|{|{zvvx|{vtvz||yvz}}~|{~}~~}~||wz~}}|{}|{yyvuu{{wx}~~zytx}~z|}}zx}|y{yxwxwuyzv~}{{{z~~~{z}yy|yvwxzy{xus{{{~{|{zyzwyy{~zy~~wx}{zxz~{y}}|~~~}xuw{xy}}zuw~xuw|xzy{|xwz|{x{~zux||y~~}~x~~~|}~{y~{}~zuz~{vtuvxutx||~xx{zz}}|xxvutwz~~|~zw{~~zy}}~|xy}}}{yxyvxxxvutsttrs}}|{}yvwxxvxz~zvu}{~~ztu}y{}~~}}z|{z~z|wwzyvwyvuuvuvx|{wppy~zuy}}~}zvvz||z|}{|{x||xurrz}zvyyz{}||{|{|{yvww|}ysw|~ytswzzxywuv~~|}{uz|~~}~{zy|yt{zwyx{yxuuqoszxw|x{|y{zyxywvy}zqv|{{zzyywvvvuu|}yz}}{|}~~ytt{|}|xsw{{xwwvwwwuy{{z|{|zx|{x{zuvy|}|xz|yxxzz{xtsyzuu{}|}~xtz~{vttsuuwz{srvywy|~yy~{{}|}wz~~~}|yzy{zx|yvw}}zwwvyxuqw{zy{~~{|}~zuvwxuttrrrtw||z||z}|}|~~~~|~||{|{{ywx{wyzyzzywwuttupqz}z|}{wy|}wttvvvy|~zwv~~|ut~yz{~~~}}~}zx~|xwx|}vuxwvuvuwz}{xrpt}~~|xvw~|yx|~}}}z||yxsuz~zyyz{|}}{z{{{{|{xvvuy|zsu{}xsty||{||yx~yx}|~vw~yyy{|{xxwsrw|z{~z}{{|yzxxwwuvy|~~yrt{~||||{}{zz|zx{}{}{vv|}}}}vuy{yxxwxxywz}~yxy|}~{x}yy|{utwz{|{w{{{z{{{zvsw|xst{{||}}~qwtsrv}{y}umsvoeeijs~|ywp}v}}|~xvkx}ko{w}xzyu}}zxiswkkqskgmrzpxq{ttyo[Xm{q}x{z~w|}}prwquyz|szxwkgssrzupt|z|vhjgvz~{ypuroqZgxjefafY`ige^hi{rry{rjv|v~|sztjxtwvqbms|turvxzw~}~~{ovvrihcXfxyqfgkghmgrqiy{oqwz|rlyshqm|~qoxlizmvqxzsmgs|tv||tmzz{xxy|s~tw|ze\Zbhmsu}misi_zxgpmn|piusvt[dwm{}v|ztrvpzqu{y|msvfewy~{xxr{z|kz~xyuwkitg`nnw|}w}qpobjurlktqjcnrcfb`lix|zue|pxrx|ztmtmhut{x|vq{|y{~uw{w}wnlvjc_rxmvpawt^`^ctjfxvst]e|qswpjt~zxv{|nqzg\fbY}{u{~t|zw}wyjssmwkyy|tqocjqkicprpnnwldlkrgi|yptroiz~xus}|npxdjjq{y|{y~zq}vpoj{{x{yf^Xkpn~~gjlcidptdnxfzptskl}||xyyptwvq|vy~tkem_`~yp{w|~}u{|ww}{qzzmxrjjVbvjuyxwo~vsqq|snuru`_qop}|lnmgjzvn|zz|}prkoql{tz~|r|u}p{yumy~yxvxgfY`c\g}{mnhs|qu~w_prnlh]pokuzzs}w{}xwxry|yvrxm]rr`nzuuzw~}|zw{uso~{mt}uxdVmw|}~x|moxrtjrsekqrk[he`jwpxqnfx{|vvthplodcr{~{uy}yzyrxz~~y~rhifojbo|{snhwwiqz{q^pxmnnlgxljq|}}y{{x~vznina[oj\ejv{~}~w~ytr{~ppqobcdj~y|zpoyttvko{v|qgkspfrwqy~~y{yxqy|z{ytzthrsjji\wy~|uu{}~|uuqyoougbSRnq~|t|t|ytmv|mzqowpmgtvo|un|ynv}trxl|rvpsrladtq{vourvwn~~tz{skcs[Y`bVo|xyw~|x|y}|uv|tjvi_`Xzqrzsspzq~~~tqzwrwvkmtpx{|xuv~qv}ws~t|zxn|qn{qkThhfpi}}}ww~irfajc[hie~quz}{p|{w~wvyngtbmpkvz|v~yv~{~yrtrxjdmeidgcr}v{ckjiwfxwuc_vjvsqwx~|{~vylt[ov^]^um{ywxyvv}zukekichljo}{wpsywe|wstdpnnoplm}~zskspmfwklxqkYsvyvnrsqnw~{|vpsmeindjyqr}qz}yzwpfnpmpvsuv}qowpl|xs}gvzotciolrvzy{~xpup}}smz{}wnxea]Pog_cwruz|x{uvynovrnxv|ovqxqsp|xqyyuqcdilkw}pxtxv{q|q~qvcu~e]\kZaoojex~}rxxej[g}kim{~p~s|~~vnuyww{tpsphqyw}|t}z|{ntz~}yqrlxzjxzs_]neha`uz}|qqd}|ibisboz{qnytsy~|~~yuyulsn^cZsz{|z{u|~|}zy~ylvwwghphhf`Xcp{~xusn|zl{xffxrtnly{|||xwji^vvb[dpcs}y}yptnutzzzwladgfflikvzz~xpu}}pmyssspgnx~}zpmjohtvdnvpa]sv{}{tri|tlsyypsxww{trtncggbkvnrv}x}xjkorqyyyusmunynw}ofwwrl`lppv{x}|vz|rqzxnqmxvuxw}wo||rvkkb\otnwx}}yu~my|vpoq~x}yutvztmjjxycef\bkx{z~wyrn~xt{w}xvxztjfil`trS\a_thnurldfxuo|wy{{z~n{rowo{xtuol{zjpwew|xurz|tswzywxjox}xvrr}pjti`g_hr^]pqu}ryuqyxqietxtxw}~wwtmqniskcbklk{zywnzxz~xyomruillkYkuW^k_qfb{ux~qkfbtpbswsyw}wyuvmhtsglq\mwq|xw{z|}~uxxl{}yus{ht}rtrgjbmygesokturxn{zuqo|~nnxzxmsmt|fiod_Zgeepyxy|z}{u~~x{|~y~yz|t|tmuxsht[igfsaox{ywyushi|ooyvq}l}u|~vmepwh]k_bxqp{y~wr{~vk{bruuz|uxy}u~~ptqpzn^ee`gdgt~|wqzvn}qxz}pqjizjn}qh]ehdqtvs|xux{vswux}x~~vy|vi{q^ijfpYajmeceuyurkwmmkx~v|rx}|wsin~wjwmizqmt}u}|~xqyw}{fiux{}zr|~tokkusbdfZX[Umno{uuzgqng|~zzw{q|o|wlhkxikkictrt~|{}}}}{xw{xvuxwns~e[gfslbookZY_y~~}sykswheextuw~|vr~tlr`nzrkssimoy{zzowz~lv~{|tx}w}vwuu|segldaa^pdhzvxvruurkzz{{{o{to~uhegsdfdfenudgx|wt~xzwnup|vvvv}~xttqfos|uanppa_]l{prtr{ppo~u|}z}slryksferoko{rqmq~~y}nkvymmw~{st|}tzut{~spnj\]Tch^kmfkw~||o||{yts}lovifiyxhjagj{uhr{trlddffn{umt|sz}oyuvs}vztw||}xk{qxuwmrwueluwytoiiputx{vknz|wyxz|vxos}{z|~ut|swkxhoaTakc\orrr~znoytcYzdeorbhsmmovy}~qhwwl~}wopy{{|ytr|tshtryhrmp{sro~{xxvrcmqe[qwywy~}|nwz}||~yry|uy{wo_``Zqoiirlkfjeukiia`cci~lp{wm}_osm{|g[rvxswmoyt~|zm|e`loh}j||uu{jkrg|{ja|tmplyp{}|qkk{zsyy~}tntQP^YW{vptlvyfwmmVaeeYmldr{hyrm_hl{x[c~wutmqt|~wjpZgyjhymswv~t~mkeqrdbv|lolwmuuqg{t{{~wqzyypt|UQclVkt}|rjg~iruoY_nj^]uao~ppqq^zmdu|cZ|xrkzisz~j~y[h}zevmynv~yql{odpvdzjt}|ummxq~|qtg|x}zy~umy|xrs|VSfnXfnvnjktwrZapk^]t_jwjnqp_}sjy~c[}zskxhoy}o{^k~jxm{m}v~y}olwqaio^tqt}yomxuw~oxexwyywxpz|z~xzp`Rco^bmswnrp}teXrldX}adqmetsas~lwnVr~ulygopxwzeeznvqw{sv}{}{~vtrj_aj_czzm|prpnshoxzy||zvvt{~{zznxsRWii[oj~x{h|wtZetj]cr]enaorob|ta[zzsjxlozxu|bn}p~m}}p|w~wl}ziroZ`fXo~yu}tpx}os}fvzwxw||ws|xx~~}v|kxjOZkhbwn|ivv[iuj^jo\dj]rrnc}w^[yyslwqq}vw{co{rm}zs}x}ukyvfnlU[bWq{u}xqxlv~hv|wywx~}zuw|w{w~}n|wirkNVhlfukzwcezncex\`i_isrb~wgUrytjtxwt{gl{suytzzxwsjthbwUU`\hoxzonx~{op{|x{q{~}~yvzz}ssmpvojjoTSepjsyvn`{sib`^hcbvtdvxoSjzvm|x~tx{}mkyt}tr~zvz}vf~th^{yVS`agp{}nqr|z|qo}zyv{~ox~~yt|xy|qokioigjnTUepns}vpd|tjf`^gaeutdxylTmzwo{wu|~~lnz~tyyq}}vt{ph|oc`~rSUb`qq~zsjuyz|nszzs}~woz{su}sxrwr`igfengS[jouoyzin{pgqu]ag]urqe|^[vyus|xo{{lt}w~oyw|wzsxzfruh]m`Q[cbw~~p{jyv|woyysu{nu~~{qywu|n|~jh_fbejmXWdnlvyvj}vmkb`haktve{|iWpzxq~}ysy{}orzyxsozvoyykkui_ghQWb^}~|tgusz{pw~xqwy}or}qyxt}m~{~ij]daejnYWajhy{ynxpndaicnuxf|~jXqzwq|}|xsx{}rtzyyun{uozxnjtj`flRU_\x}x}irryqu|xq|rppzxruxrypvzjzu[d`eio_V]gf~u{o{{snoahgf{xmovYizzt|xqs||xsy}vsns{s}tveqkc`yyZR[\hzsvnuv}try|w~xm~ymutyqwr~u~kzwhd]``flkXYbhlzy|p|vquidjduvzf|i[r||x|~}wxy|uu{{{unzt}myundnf_e~oVT]\w~}xqrtyrszz|tmsmvtvqvoqvk{yqh~`^^aemhX[ejrxz|r{uqxjeidzuze}g\s}~||~yzy|vu{{}tnzt{kwsmaib]d}rYV__{~}wusuysryz{u}ltmuu~xpsnqxh|{trb{cZ]^dkl\[ens{qzvrvqfjfsw{iro[m}~ux}{ty|yuorwky}mr_da]^v{bX_cr{sqvw~yquzwoow|{lq{z}~qqpvxsgrou^rmX\\ciob]fqttwwstxhihm{zpiu]i}wy~~~tw|yqtzrv}ltkq^``\^s}h\ags|trvw{qtyvnpt|{lq{z~}~ppouxo}fo|mt]onX\\cjod_hswtwwtwyjjiry{njs^mw{}ux|zuq~~qv}gyyjo[_][ax|f^di||stxzwptxxxtkz}uls~wz|{{opn{nvsctrom[ueY[^fmncblu~}sxuurikjv}ewibv}~xuz{rqurwrjloeZ^Z]lsaahozwxx~~rqwvkvo|}mowz~{}z}qoou}yj{dk{yjr[koZ\]djpf`grwwxwv||mll{x}mkp`ow~}vy|x|pxpu}vfpmkZ]Z]i}we_fl~z|xy~spvvl{l}~mmuy~y|z~topu}{h{dizyjs\jq[\]djof^dot~yxwx~ooo~yolqaov~vy}z|qypu|vfrln[\Z]h{xf^djxyz}uptw~n~i|~nlr|yxwzx}xoqsyg{~hev|jsbau`\]bhoi_`jqxxyx}toqw|zfydi|}xyw}}syxqsx{gw|hr`[Z[cu}m`bhy}w{{{|qqvw|nosvjnvqzxxyrpqzsisakypkpXnm\]^floe_dnv|zxzrrsypkqcpvwy}z{rxosy}yn{vhZRXi{tk}tgfsyw{tZmcwk\eUaqz{mdgttxsuiwu{uzoew~xz~lRfaufZiWbr~~pjnsx~wt{kw}met{jxncs~crmnfxfn|snnpr}|}nttfUkytl}sgufqnojnuzttvv|~xdI_v~jpoy[khYds~{{~z~}mqr}rjuu~oihlj{jmnfPg|s~pls~|xyymkicK_{w|zgmrY^hzwtvvso|~~}|zs|vpuzyg|szkcclm{kmpoXe~v}w{mr|z}tt~~ghklVd{ss|_bi{xswuqkxxwvv{xp{wpuxwf}ryibdoplnss\gv}x{mpzy|qr~}hinoZeztq{^aizwsutmjxvvtuyuq|xswxwf|rzh`dqqmqwt_jt}v|ipz|xpt~}fkro[lxpxu\blzrtushlwtqvytx|pt}xxx{pjwsz|d`hst~mt{s`qs~r|hq}zsnx}}uhqwk^syl|k]eryqusodtutm~ttxwj{x}x}hwqv~sa`oq|ur{~lcxxw~qmjtx|y~op~zloxxee|{|r|abiysrtrhhyrqnptwnj}|wf|qvi_ftpqz{flt|u{{fo{u|skx{}|xnz}pap|p~laivyquqlcyurkrsupdw}gzpt~ra`qm|vzngxwz~rikux{}yzzkr}u~nzyei||yzxchr|qtrpbrzqnrwptrejqmvpv~f]lpp|v{iqw~r|shr||s~uqj{u{zvuqeqs~lfn|xqtpietrjprsk`uh|np|s``rjz~wtkxx{q}kmw~}u|}ztz~}ko|pp}~ljx~w}iktttsodprpm|msqec~{hzlom^fri}wqp}v}|szh}~~yuzxktjmwwv}|}iq|net~}zo{qkqyw|}|sbnymnzz{tmswWgsb_fpuzqo}u~yl[p~XG]jqtvs}os}o|jhvrqrtq}jsa]|olox|iq~zsg}y{~rfhttpUad}x_]bo]Zngp`hksru{yypgwfsoctx~}z}{Qklfzdefvtsu}|xuwywvig_]a_ttyrfarkehptlm~uvwroqwjorqvqafrim~xpo}qxy`^zsy}zw{p{|q~w|}ppwmhTcury{umzjcvsXjjdqiyju}rqV}l}xzhlqs}yx~|y|wpprdgimvswmrxyvw}ztzff_Xo^[{zpvsiynhsmrzxwrtkehzqasuxwvxkqr]rrr~zx|lrslcqu||twvz~~w{vvu|xxphaefbwsoxmmxq^txdjdj~vwtvwepupuw}x~nojrwuzytxdhx_g{tvo|zuv~{y|zyncZgiahwuzorpwper{rsn{}tjerpmmf{sf~v|w]hrosoxwxwuhkyaq{v{}yrw~ytvprzuvnbbod]jzuwzfjmalcmyrw~xr_rgn}nm}zmrll~~}~{eorjdjnmzzw~|{}}z{ljrebwlmo{~rwzjnwwrk|wwppujiuoifqssvwewvdlktw~uhrulgi}x{|~~}}rv}lr}|~ugqgj`\vttyxj~ncbq|pn|ch}xwnkjnwy}qfqwzys~y~vuxkfr`U}{nsx|wvz}wpgsyseYkyq~uoxqztwsznippyabvg`tvr~ffbrxt|tuzmvmenjpxvy{xwuz|z{qlhkn]eprxtumi}|ouuhx{v}v_exdpzsntoxr||{vjildhqlef|v{z}~}rha[ZXdsnTgvr{us~u}yquwpo|taoy~}Yta_ms}}{}{wsi|z{{wkktt~~qhm{wpjuq`xu|Uun^bdnrqv}~xl||lxwx{wz|puzrwrfmxnW|y{uxvt{|lzwoup{wkllyux}ox|vy{rfSyljcbjquq_gjfvigptnbdasvy}nvytp|n{yvolo`ww~pqvozqtzzrilex}~tof`iapmqpno`[we[cdglh{yb[`Vevv}}nu}~vv|wtqsogqh}}~ym|nv{~ztnujt~ljkkank~x{snnm[rn[blnqgoza[b[[tx}}qo|tzu~norvjqp|}|zy~pot~wqxum~hgjphnrw|snjo_ju^`ptulg~c[bbYp|}~~vk|ry}ulmpwost}|~{yq|suzwovwn|eehojqvw}upkr`jvabrvvld}aZacZo}~wl|rx}u~}ikovquw}}|zr|twxvovwoxccfnkuyx}wrou`mvaervujez_Yaa[r~tm{q{}v|~ygjntpyx}~}otz{{qnwuq|q`cgmkxy{ytsv_urbjwxufir[Zb_^u~qrxp~zy}}{|rfjnrq{x~ou~vnnvpw|}z}h`chjoy{~{tzoc|jemxuo`vfX\aZfy~}m~tu~u{y{}jhkp~|}~tz{zxtxyz|plnxt~weasipm{soovwiVUbY_]Yimjltor{zkj{sybg||V]gpotk}{}}uuzsxpo|xoq``jrrutqqjs{~uj^xf_cgbp{|}rfgfo_ZZadW`jqpvmv~yx~}|~ttsrxxytvslooxoepw{y}t}zzzp}wz|vpjaXowdcj]^Zadoc`kqqpof}|kggp|z}||uyz|v~vv|wzxrxqtuZUae^cuuzvxwtrWrxkjxqo}{}drmhksg_mu}~}|_^ruwjk`ervzmpu~oWbkX]`cpy|kxz~]xx|trz||xqmmmt|p_gp|rsk}jku{|yqiYlwwz}gh{umlYTirpaaLTaggZbx~|{sQkpao{guutyqtv}kfrxpthdfqxwxu{~}vw~my~wvzr^cuvkgi_agmwzaemdzm~zoqzz|xhissptnsuq{ylu}rwurzii[`uwky|u{py}esy|nKWbdkttbw|c\Qvxpuy{{gbzngo|tlxv~~yknzpet|ne\e~}r~xswmyvyxmy~v~yxvqw{mc``euqtwiklmzznrwnflinplsg\bilxpjp|qrz~z~vuzzlv}jsy~}~umx|fj|tzsrp_Jgzp|j]u}iluouiyu}z~qZ^oei|xmqd~vxshh|ytrpqalersmyw}sq|zl~{~qv{vli|noackv|ruoqiokmciqrrc[j}|sopihgozq}vqv}yomzt~xlqxs{llgr~f}wwyz}}xiQT}xvvy~xfbsslsmqhly`po`errxunmoo|olxvgx}l[atun{}{y|{ul~wz~y||skkh``bwdk|tplokkywlqyuu{p]^n}~}kohw|}~}qtbevwx{x{}exutmx~|{x|}ung~cZivxvztt}kwthgmx~{lrtojbbs\d|uwwk_vthu{qpxtkjnih|}n|z|{nm~y}tcS`ljmivztiizuqryvjqbvokvsy~~spr~}z}iyws|~|tqysgp|~xt{ui{tq|~{sx}{y{|vxzg^jbxw|rq{u]~zwwk{}q]Xv}i_kvzomyvnt{}{o|{swZhjp~}|wl~w|z}z{wwnjyjhrwz{svz~||zysmpv{ogmsak|yrljuxws~|u}~ot}mVWtzt{v|y}{xxvz{rlsut`Vgnny}fpskqrpfk~vtfhxxpo}wmy{vthx}}toqmhpnmyr}z~szyupk~{wwymghgXjef}zx}ttvwtomjqofdknv}qqu{orzkkwpvuldnzod]{~{yxz~yw}xezwnpbclvlcpx{r~ykl}sifyqrjo}swvrux|zy{yx}}zv{y|xslglxnm~{}{s{~~wv~{xywyxpnoqoZYlsu}|yx~dz}on[l{uoin{jgnmfhxsyll}va`q|t~}{y|{n|s~uzsbdfaZSeqso}~x|xzvlopnppkjmlryz~}spvv}ww}zig|cdlo{y{z{|}tz}~~yy|}cmwae`\frzuqz}rm~wgcgcWXu|tzvnkuun{eihgcdxw~x}|u}n}}|ru~~soxx|twxi\^m}~~~}{zzyy~mqngmxtr}|v{~~q|pwxwzokv|jc`\go}}{y{zyquqvv{~{qrstutudRyzx|zngs~xr[d~{}{qptlwzuq{z}|uu{hjwcs~|xzxssfp{vu}t{~yto^us_[^oyx}}|rsz{{vpjkxw|u{|xnhsvojmrz}tr}z|rgkvxzmt~~}gy}rotoc\^nta[hnpwpsw{rkn_mrpvzz}}|zz|xry}zz}yljnrokvn|zwzmu{xxt|vsyqqz|}znjikjggj`d}y|{quuskgpx{wlkopt}|qv{qq{|{{vsudmxtuv}o|~~{{y{||stqlpmjiq~pnffwwntwv{y~ux|{vmq{kzu}{|yr{z{zpy|lmrnf[_zwpt}{{juw{y|}{lw{ytpmrtkb[asz{{|xtz|xlwywpw}~q|{{|{~~tmmonegv}lv{|w{zndqyx{xvy{rmv}trrse_eezxzz{jdrwuz}ux~||qv~~riu|spmjhu~pq`\xs~wvqu~|uyqwqfzrgl^\ZW^ckwy}sn}rl~~|~}|x~}tvuxxssqppsoinrt}xpzxvspwyuo~~y~uxpwommjmpme[]ousz}zu{|kcmsjhtwsonoyvh}wllz~xpr|vztx}~~~~zwyw}s~zvpjkmp|~pf`^^`nj`rko}{}~~xjmvvvx|rv{z}{y{|{kg{vjh_ionos}}z{|tw{{~{{z|{vlr|zwyyjgwlfjh`cu~{y}~{}~tqokjp}zzqowqu~uu|omonnkjrsry|p}nv{rruvx}}upqv|zp{pp|ztka[dlo{outoq|sp{wnptrplo|ulda\[p}v~zxxsw|z}xr|}rp{yrljmkklh]`pr|x||vljxyxxx~xwzwtwwttttgo}kgvz{{|xuz{qhhu~||zutwz{sop_kxmgdcp~xgj{wwyurqw{xqhj~|}ykv~}~{uh]l~{qvu|xx|{}zlsyszrieb`[`nvre]hrt~rznr~|z}{y|zyqv~zslmqqqmhky|yrxs~uquy{sr~tlmvywsifgeehkjYg}~zyxtpqwwuxsrurywgl~ymsmcusny}yxyzus||rvsonlprdfjip|sil^`ntsou|vu}y{|yyxnt||wzwomy{xwlecebemuyvx}~z{|~nn{|~wtvtv~tar}rrvsqsqojefdhw~{v~}uzsrunnzy~wrolr}|~{}~zqkiuzzruz{yywwrsy~{rv}}vq|xpt}}xola^gt|zmX`uurts|}xhqzaTW_mqt{wusqb_pvvxrrmnqtpnpx~xwulhq}rvxpz}sof{{v|wy{voactwudW[b]V[cdr}rv{z}ns~pko}w}zqy|zqmtlmsuvw}zxv{{y}urqgp~rlr}~sif^{zqwukc\agkliqt]}r{ofrz~|lgnw~mdrrikqupmw~xku~ykZ{lq~}ww~}y~}{zedfdgbpxyr`t{mpZv~opv~ncsustzuornxrm{ytvxrt|~z~}nkkqyulu|v}vlnwlx~~zyt|zyxuk^bsk\Zdnzsgnpimzvvbe~sl^ksxzpn~zhnyyuds|}nuf~khexwxwutoqv|~~vowxhVnnoqpnrghh{tngnuwmozulrlco{skntymdhlx{ywsksm^p}wx|zqpp{r|xsr|no[TUc|~~ji~rl_qwzvgawr`doqm\ey|utmyoeuvt}qvvysmnz{{vx~pkmu~~q|pz{vvq]Yxvxy{xuxulrpejtvld`}moi^djxzvusy|x|wk~qvpp{oehqqlswl}{tx}y~z}|kfnin[fyq~kistn]fxpmmkenzrc_{uyxour{tz~z~wyslpbxtqzyz~i}ywljt|}{zx`gx_ejr|}}ksotgaado~~mlhjnqpybq~t~te{}qqz~viwyymps{uiov{w~~y}vuz}eQ]vrfcx~zwobozjgnv}fj`uzeos|~xqs~w}ox|srpzvdnroqw}urj~ou}}xm}y~~|wsppneneo~}zzzxbf}mxox}vpeXothn{~nt}w{xy{pkzvovonocro|ogvsswsv|~rzwqjlxy{yep}ifnwx{{zlZUVejkmiinqz~pcuv||rvvxtl{w{vh]tulot}dbo~qxvloljty|~}ypwso{zxhs|sfYcqZbty{k^blccgadilkeftvgxxzxvy~row}oafn[oqmz~sdhqzw~vnsjyximoYfr_p}fh~w}ovxsgrrmr{{gRioqqgqu{krys||tu{vbr{odz{]wjn}}rvztx}r{dtxymv^jkd~k[dvj~zzw|xls}pYsrktpooqseuyzrsricvpgpfkmelyrwrst{xh}vuzzuqzrtom]gxgnzw|ymklyrt~xxsatpnmu}uf|phxop}xtxwnuvoxwvgrwixzz|}sy|{tk~xyw~s{gxpdhfe]rtdn~zqkqrwo|ytqlgsfh~qntxq}tgvvwbqwjmpujrrn|uufm}}zus|x{|oxmjpic\lkewypjou|ltypf^vqjlxmyytq~r|uz~hlylqopk{wk{w}zohrv|~vju~s~~ykswjkecaswm{sekvwz}ulndxtlfvtroytwozvv}{jiyquoicmylw|mpw~|j}zyw}x|{oiqehid^e|r}|vinv~sq|ut`ovroxux{mywrryyxnhqnmhlop|~}}rnry~y~n~}v|wt~~{mctcgmedf}szkjn{wt|ukn^pzrkqtuzouwtsn}vz{re}~rsslljttux{x~~iy~~zyt~yh^yudcfdgg~p}zqrwzrznl]u~xoszy{t|~x}oqyxvianmkgimt~zoov~}}y{lx~~|zy|~zh_zugliebh~vznozvr||uoj[x{|ou|szpvv{tu|y|{ki|oujhhvx~|vv{wx|tgw}z}~u|ywq`eqajchcwv}tvwy}zrsiiywh}~uss}yumx{yxsfrrlsfjc~~~{z{|{q{iuwyq|u{txu|}u\fyvbZ_qpmkpzxvsslgdo~wqyztx|~ysznhwsflstyeqvce]t~}uy~zgmpr|u|uslliqik~n^L]nqvifv~~qm}gekttvsxttfn{t[RT`}yqtzwoysthm|}ny{x{tkxporo}uiaa[^p}uzz|xvk`qvnv{~|w~yjxtmr{tgwsadgsz~vyxoe_fsyxpvbayjflmdVKrnneku{x}rqywnz}x}nklxxrttpquyhergg_y}uwy|sy{tlswy|rs|oqmhbaYOSZhx~x}ymy~trtsik|{o~vrrpxtthy{txkkhjptpm{tu{}w{}ezzws~~{wd}ukfwhm{{iOUwzjfir|vjvsgjnxwwvhv}|k]r~cYcpm]k{v{wx~rwup{}z{x~kgejswmioqqpjceTjrmjmvyyufo{rx{}~vwzvopwx{i[ufnoggp}w~uzxo|lhu~~nr~kylgelyjqetnT[`ikmr}yxzzxy{ixy~wxlvpw|q`pzqqgsjksrcby|vzxuf~yvxz|byvmgauvyr\ThlbYarwqo}vxz{qmpty|{~suutkgfotysrsjgpu}rf~t{}ok}x}puzp}}sqpqmjjnheitxqbZh]jp}y|}vn\sqq{tx~md|ujij}|liVxsgsx~uzsqqwrtuznurscdki\Wkmoferecjwxo}~|~|wvuy|~}gd|mshq~yl[Rjspvn~xz~mvjv~|{vtbusehb_^inl^Vdlqywvx|{sosoy|wuqmqpmomwumjjmrtr}s|krxwyzumf~zyu|zvv{q_Wnzrjd`gbalbwtpy~|~~uhgt{yur{yrpolhgeplW\komdccjx{|{tqej{~s~~~lyzzrr}~xptu{zx}skq|z{wsqebswXe{h[U\ctr_|rjntutp{mh}tj|}tmmnfdq|rklqpx{st{{ynj{yXkp]VV]ajz|e{}yz|z~grur}opwzzpei}tkkx|vty{xoxuy}xtt_~xw}wlp{~wppvuTWz\ebSgtvvhVWl~aZdt{ti_tm}uns}jgsnry}y}bdluz}zw~~utf|sop{s}~z|ofnwofmh{rp~m}{y}~wxuoya~wvqyu||snnlxkRwcbXTvz|w_O`|_`o{chvizwldhp~tdmgfhtgh~~{r~ye|}xrpiquz{zyv_|tqdzclyrtxyw~||yu{kexw|ytpt~yutqjo{Wht`gNi|~~nUQrm\evsZv}fpheisfkuljhozwmrr~{wxsxlyvtnlj|r}{|xxojp|gnoc}vvq{zyxz~}s}snazttpz}}wtsnxtTvmcdPn||zfPX|i^iv|j]|{ugqeglx{frvmkl|xtqqv{{uwpssxuplkmv~z{wzinq|fplbvvqyx|y{{|qwrmcyqqp|xusnzuUuqejQh{}ygRXzn`et}yj]}vzdsgglx~lp{onm{{|zmzq}ywsp~{lvpijkzyzxxpfs|qfqbx~tu~y~{|z}}oqviixqpp{xssoxwVqxdqVay|zjTWyscdsyxk]t|buihnxpprpq~|{~kr|wvq{q}{mxohko}{zy|nht~sepc|}tt}{x{v{ylm~rliwoprzttr}uWythqTezvfS\qbgtz~sgbywudsjkp~~ov~rrvuvq{vz{so|{nqx|umjkv|zzydyrkjzhiyv}su}syzupioxixwl~uqr{xvuydeiuhVo{{n[Up|iamtzvl_xp~etzpkovtn{uwwjs}vwmspw}jzqjlr}zz~ojv~ycx}nf~}t}~s{~u{|s|sghyxjr|fystz{{}nbqrrWgx~{o`Vjnbjrwukarp|kjtmovvl~z{vlu|~vyln~}ou}g~~tnnt||sjy}}ct|me|}s{}s{}~{szzs|sfhw}tgqwezuv~~nkqxtZjx}ym^Yplckquribzo~hlupqwpj~ylu{{vxln}zmvzf|uoqx~soz~dqylg|}syyr|yxu|zrzteet}}tgnxc{zuv}wjyv{^fw}zoaZlqeiptqiduq{oeyrrwshvxq{{z}w}|mjwymq}ewyrrx}kyki{zngutu}zs~pv}uy|tuzjbkw||tifygqxxw|krn_oz{th]c}{jfnqsmfhq}bs}urt{zkklv~ywtim{}sjzsi}}wrvryzfo}ujk}tx}|vt~zt~uu~zvqw~tealvzwofmyb}wyynvjbpyxpe_kzkiorqkfpxtz`v}vsv~yko{nw~~wxqglxyok|ol{wswq}y}fp}|sim|tx}{uu|ws~v~t}~x}upv}sd_hrwumenyd|x{{ovpcnwxqfal}minqpkfp{q|aoxux|oo}lx{}vvtghrz|wojzuhz{vyvv~kjw~|tkk}uvz}zts|~kw{ysv||tnrzte]douuogk|huz}|qwxelvwriclqknqqlhp~o}cizvyrq~mzz|uu}tffowyumk{uhy~y|uymkv|zskmvvz{xsty|iwzusty~}yrmrysd]cmrqkfo{gzytzufnvwqheqplorpkhvwsz`o~yx{}qtynx{ytv~}pegovvpjn}mo{}~{rzkmvyvoks|uvyytr{xpnzw~kwty|ytmmuwl_]emplfgxshzynjrvtmfk}yonqrnjnn|may}yzvo}|p|~x~|utzvgdkrvrkixwfv|~x|rjrwvqkowuxyurvwyg}|u{qu}sw{ytnlrwpb\bimkfetvew~wkputohkz~ropqnknpzt_s~zz~vnx{twx|{usyxichospjhvweu~{|ukqvvpkowuvwuqvvyg{yr|{qv}tvyxsmlrwob\agkhcdtscvxmrvuojo~sqrrolso|rau{{~smx~szy{~zttzwjdhnpmhiwudt|xlputpkpwtvvtqwuzfwwpx{sstuywsmkqwqc\_figccqsb{tprwvqlo}wrrspnsrzybo|{~~ums|ztz{~~{usyzmefknmhgsxewt{}postqmn{{uuutqtzyimyorx~lwsvwtnkouui^]affcajvfkyxxquwtonw}ussroo|~qldx~{|zpn|~qy|~|xsu|~uhdglmiekyofs}{opssomsxtuurqyszerunvytotrttplkpvrf]]aedacptbtv{wruvsoq||uttrprytify}~yop~{pxz|zuru|}sgcfjkgemzmh~t|{opsrnmtwtttqqzq~xcp~tnu}xsnsprrojiouqe]\`cc`cqudtw{{suvspq|vtsrps{rmcu|rp}|q|xxzxuqsz}uicdgigelyqft}qnqqolqytsrpovrw}ei{wortxhtopqnjhmtsh^[_ccacoxgo|xuuvtqr{xtsrpr}ppcs~tr||rzxvxwsqsy}ujcdfowyxrv{yvw}y|{yqppmlrvpoonoxrz~hox|zp|x{zymgeirqmuvbmokx{xx||znj]]`afu}utuutz}xpj|}ywn|}~viber|{{{fd~wkxvxuponlmvxttuuzvvqv}nwktzy{{yx{}pfdiosuzsf}ms~~~t{wyxsnign}vqppps~s}hp}~l^[`kv~l{histrsx}~xpqzyluzuvqpnkjp~wrpppr~uhky}yvw{~}}{|yt~}yolotx|s~}|jnxq}wst}}phedceo}hqu_`qxxvwxwrpv{|zs}~}{|}pd`cipwsvt||y~}}|yx}~|{{}{iiwzvsrtuuxvvojvqqrqpsy}xmd`behmxyi|qq}zz}owxr~|}{wtxxrooosukozy{ma\^bis{{w}{}~vnnu}wktyotpppmlq~xrooor~t}jix}zxwtpq|}~{s}}~~}|snoqswx~}mpxk}|||~wmhfegr~jpxc_oyzywvqkho{z|q}|||z|uicbdgn{yw{x}z|{}}~|{~|zz{|ufqz{wtrpmluxjxuqqqprv||tjcabdgo~pqpw|wztnquyz}|zzxrpopwu|js|uw~}yka]^bgr~}woou|}riwvnsoppoouyronoss|jgv}}{ywsnoy~}r~|}}}upoprwz}zkk|jv{}unjhiqplzm^hv{zyxtnikxwqz{{zyz}ynebcflv{|{yz}u}|}|{|}zxx|vidqxxvutroq}}~ylurqqqsy}{rhcbdglvmw}qx{wzzlmox}||{z|ztqpr{~v}kr~yz}~~|zxz}|uja^`fny}|zrpu{|qgrzn~zrponnt}tpoos~twncny|{{{yvu{~{xvy{~}{|wrrw|}|{yqfg}u{~~tkks{r|il{yqnt~nfxlh~yrpooptz|yqjhjns{z~wtu{|u~}||}xvysxlbmy~}wuzpyq{vrrssv|ypkkmpuqxoqzvqrx}~ukpzvqvtrxjq~xxwxv|wtsrsw|}wojjnsz{u}}rpty|}}{tih|ttyrpnmnt~{yww~xst|qqztrqrv{~{smjlosz}rzrzwsuz}~}{ujev{t|yxutx~|~p{kfs~zrotyr~~w}}}|yy{~svks~wlftzo~|tponns|zxvuypxmep||vsxzs~|yvuux}~wpllnqxrsx{}zt||vsomowzshq}~xqlpzxnw|q{usrsv{~}voklos{vtx|{~~xngu}p}}uqqprw}||}|{s~{vx~rkx|{}xplklov{o}qu}xstx|}}ztjfu{p~wuutuywris~xpmq|yr|}w~}yxy{s|mpyzohv|nyzqnmmot}{vtuw|qxpen{yrnr}zqyz}zz|{sonnqwrvy~zszzurqpsy|z}{lr}~woiktwmsym~xustw{~yrnmnqwt}wu}{rjrrvxrqqsw}{|~uw|tsyvkmxwqmmnrzpqvpw}}vtvz|}|xpgj~ur{trrsv|~|~{qmw~wolr}wsyy}zxy{ypylpx~zol{|oz{rmlmou~{utux~qyrfmy~xqmoyxqyx~~xsqqtyr{xv~zsz{||tpnmpv~~{|spz{skiozzon|pr|wuuvz}wqooqu}wr}rwxnm}rvrqrt{~}~}syvpqxzpkuyxrpprw|ltrnu|{usux{{zumgntt|urqqt{|tnwxqns}wu}y}yxxzz{z~zurty|~wolyqv}yxuqrx|vtv|z~rtyji||u{v~xspoopxqpyry~ytuy~zu|x{~sgfo{|{|}xpx~ukjtxuun~}~zvsxt~|nqz|}yom{ot{yvtty~zy{}widn}ulgo}xju|{|~xromottyyu{{wx|zs{tp|~|yvv{~zy{|tpyyqmox|~yzzrmkp}~|oqw{}|wrqswz{zvnfj{qhu~|wuttw}{xx|z|rxxpklt~tsxqzustxz~|ro}xhq~~~~~~xttw~~qztgku{|zungekvwt~yn}ysooqzxxv|}z{~yqp~rx~}}uu|}vnhgn{ys{xjv|xwy{ztollnuzp}xtz~yvw{zro{mp}}{{|}{{~{ss|xqmq{~wzsnlnuyy|{urruxzzwphguzinz{usstw|}yxzxrs|zrjfkvzv|p}~zwwyx~~{rq~|jny{wwz~}xwy~rzxjksz|zunfcfp|xuzmz|vsqsxzt~y}{yz}yrrtw~{{~vt{~wofbfqz|mv{z{}|wroopu|~um{{uz}xuvy}~xqo{op{}xvw{}{~{vszzrmmuw}}vropt{yr~}xtsuxzzxrkhrokv{wrqsw||z{zywpu}|ulfelxz}~~}yxy|svux{wuux{|{wpkrynx{{~|zz~{u{ps|yqiehpx|zpp|ofu~ywx|}}pksnu|~zyz~~x{|~~}yvt{{ncbm{{yqv{uqpu~xlt{rsy}}zwx{vnr}w|~rmmpu}~}~~|{qs|zrorz{{v~}~zwx|}rq~|~xutvwxxwurwrw}xxz~sfq}{|umfabis|}vu~~lo}|}vgmxorz}zyzzvw~zr|xspqu}|}u}yvw}}vwwq~}upnorw{wiaqtqv|~}zuux}}vonwpq~~wsqsw~yv~zsnlpw~}{uzxhh}{~}xuuvwusqoowxnvyusuy}sz}sls~|tkc`bis{|x{~sxqxz~{{{zywtpr~~mo}~||~}twvsz}vrqv}{yx{}xvw|whmzpqx}~zvuuwyzzxuwz~||tnwzojs~woklqv{||ytryupwuyuy~|vqoopsuuuuvz}vssu{}vwy~xtuzytvtit{xxz~{mgvvqu{~zxwy{||{yx~~~|}~uz}qqyyrmmqv{~~|xtw~qyvlvphkrz{xutsqnjhn}po{|wssu{y{xztsv{|y|uz}|vlw}tu{|yxyz{{yvtyxs{|vtux}||os}zmiow||ytnjjnu{~~zwzuzwxw{~~{xvuuutronv}{{z{uz~ojpx}}zvtux}~zrmox~tho|{}~tly}uw||yxxyzxvrr{v{tpwzuux}{wz~ot{xz}qhrtjinruvvutrrsttspkjpxv~zyzz~y~}xwz}xzrv|qy|qoruvwvutstwyzyvqmqxs|zussv{|zz~vxtmpxztonqv{~~|xtvwyv{zvy}}{yyzzyvrpt{zutv{ts}tjkry}|xrljlpuxywsonv~okyzmn|x||zzz||zwtvx~qnt{|upmntzzw|xls|{{}~pjxvmmquxyyxvuuuvvtpljrxw{~wz{z}~z|ts}}~rurnptwwwuqommnnnmjhlz~u{|yxz~~}wt{qqxzurrv{|yy~u|w~utvxyxvtqooqsuusqr|}|{xx{~}xot~slpw}}ysonptxzyvrnpzzlm}|pu{wy~}{{{|}{yw{~~}wsot{|wroorvyzyvrqwxlrqitxnmrwz}}|zxwwwwvspov}x}~yyxspquz}~}zxyvq|{npwpquxzyxurpooppomknztv}|}|wwsw}zvssw{|{w{}vtpruwxwusponooomllu|x}}zor~vopw}|wsqsvy{zxtqt}sp~~sw|wx|~~|xurqrstttu{~~~sq||sqv|}xtrsvy||{wtu}ult}pn}xooswz{{zywvwwwvtru{|{z||wssuxzzxuqpvwkpuoz}rpsvyzzyvtrrrsrpnpy{x}}x~|uv{}ysooqtwyywvv}xzvnx}qmoruvvusqppqqqomow}y{rwzrrx}~zurqsvyzzwtrwrrwpz}xz}}zwuttttrpqxz~zrvyqqv{~~|xuux|}zx}sr~wowtoptvxxwutrrrqpnkltz}uz{ssy~|xtsuxz{yvsqvzlmz{t~ytvy{{{yvtrqrrqomnw}y~ysz}vv{~yvtvy{|{yvv|ssxpysmnprsssqppqrssqopy||tx{rquz}}zurpqtvvuromr|zmm{xowxrswy{{zxutsstsqoovz}uy~trv{~{xwy|~}zy}wr|ukovoorvxyyxwvuuuurpotx|wwwrty}~|yustvy{{yvtv~ultxxxvxz{{zxusqpoomjhlwvvyu~yrrvz|{xussuxz{zxvw~ruxs~uoprtuutsqqqqrqpnpz}wpt~ypnrw{|{xuuvxzzyvsrv~plw{qu|tsuxyzywusrrrrqopv}vuysuy}|ywxz|~}|ywxynrsoz}qmmprtttsrqrrrrpnpz}y}w{wuw{~~|yvuvxyzxvsrw~qlv}sv~usuxz{{ywussrrpnnsxxwu~{tswz|{xusstwyzyxwyuvwpxunnprsttrqpopponmowx}}tuuprvyyxuqppsuvusqpt}tlttuyuvyz{zywusrrrqppu|{}spyzsrvz}~|zyy|~}{|ts~smvtmmorstttrqqqqqporz{}uxyuvz|}{wtssuvvuspos||ohp~wzxuvxyyxvtqponnmlmswxww~wwz}~}zwuuwz{{zxx{}rv{omyzojjloqrrrqqqrrrrrw}vry{sqswxxvsppqsttsqprz{pp|xs}|utvxyyxvtrqpponmqz{u{ytzxux{~~|zxwy{}}|zyzur{tnvwpoqsuvvutssssrqqt|zyrv~upquwxvtrrsuwwvtstz~qmv|u|wuvwxxwvsqoonmlknv~vz{uzzvvy||{xvvwyz{zxwx~tpxxqxxpmnpqrrqponnoonnqz{yrwuqsvyyxvtstvwwvtst{~qnxxr{|tqrtuvutrqppponnr{y~~uqwwstwz{{yxxy{}}|{y{vr{vpxyqnoqstttsrqqqqpor{x|{uzyssvxyxvtstuvvusqrx|ojr}v|yvwyzzzyvtsrqpnmowwzztwztsuwxxvutuwyzzxxyzsyxps{qllmoppponmmmmmmov{|~vv{sqsuwwutssuvwvusswwmo{ww}wvwxyyxvtrqponmmr|yv}zrpy}urtwz{{zyz|~~|~~}}yy|{|{{z}~}|~|||xyyxxwywwyz|}{z|}}~~~}}zxw}~xy{{|~}~}~~~{}}urttuuyzz|zwxx{}zx}~}}|yxy~~~zz}{vvz~}~}{}~{xyywtttuuuz||~~~zrpuz}{{~~~~{}~|yxyz}xv{}~}|~~}{{zxvwyvvwuuvzzz||}xwvw~|xvz|}}ywz|~|}{y||yzzzxvvvwxyxx|}~}yys|{xy|~|~}||}~|z{wx~{{{z|zusuutrqsuz~||}~zww}~yw~}{{|{||vuy~~~}}~~}{}zwvyy|~{~}|ystz~|xvt}|y}uy|xvtvxz{xz~{xz}y|zu{vvzvtrvz}}{}|~}xy|uxuu|z{}z|}|{x~}}|yyz||}|zwvzuy{y}yz|}vu|{xy~~}~~}z{}}|yty~{{zwuuwxzzxxx~w|}{}}~{ywx|wz~}}yvy|zxwz}}{}}~z}~}}|z|}{{}~{~~~xuu|~|zwsxzyzwx|zz{xusx}z}}}~}xz~~}{y|yxzyx{{{|}}}{}}}~|x{zwwsuz}}{{yxz~{~~{xyxwzzwxww{yy|zyvu|~|{y|~}|~~|~|x}wv}~~}}yvvutxxv{}{w{~}|yu}{xuwxz|}|yz|xwvxz|}~}}x}}~~|zy|~|{~z|~~~~}~|uxyvzxxuvvvyxxvw}z|~|zuy}~{{|~{xxyz|yy{{z|}}|z}y{}wyvussuy}}~{ut~~yz{}~zxvzzxyv{{yy{zy{}~}~{{~~yzz}~~{}{y|~}~}zuwz~~~z}~~}{yyxyz{}{x|~}{x|}}y{xz{wxyxzwxwvz{}{z|~}~~z~|{y}}~~{z|yywvx{wvzuuywvzz}}}{yvw{z~{|}zz{{|}yz|}}}z{||{}}yuuwtsxz|{{uux~|z}{vz{xwxywz|~||{v|}|{~~~}{z{yy{}{zy|}{{|~wvsuwz~~|~|~}}v~~yy~~|{{{~|{x{vx|wtvuvtrwz|}~}y|}~|{|}}}~~~yvw~~~~}{~y{z{zxxuvsvxxyww{}|}~xuv|~~z{~}~}{y||~|z~z|{wvxyywrutuwv~~~yvxy~|~~z|x|{vtwyzxv}{yyy}|~~|xzxy{~}z|~zwyy{}~{|tpry~}}|{|z~}}~xvzz~~}z|~{}zzvxwvvsrvzzz|z{y~}}{{~zy}z~}}xwy{z}~}~z~~~}yw{xyvuxtrtvx{}~{}~{yyyxy~~}||yz{}|x~{}}}xv{{xwxvxsrttyy}yv{~~~|~{z~|~yyz{zxv|~~xy|}|~}{~~{y{}yxwz||xy}}||wvtvyz~{}{{|~~{z~~~yx|~|xzz{|wuuwwwuwzxy~{z{~}|~~}}y|y}~yuw}}}|~{yxy{v{yyxswwwvtx~||}|yxz|}~}~|}|{}~|~}y~~z}{ywvwwtsvvtssz|yywwz}yyy{~}|zyz|{{y~{zvz|}|}~|{|{yx|~{yyy}{|~zust{|}~|}~}{zx|~}{|w||x{{ywtuxutu{{|zy|||}|{y{z{zy|wutyy~}~}}|zyy|{uxwutwz~~z{~|wuv}~||~|~}{||}}y|}xxwxxutrttxuuwt{}uw~}}~}x{~~|zy{{}|{zyw{~~~z|zyyz}~yy{z{wvxy}}}}xsw~{}y~}}}~{~|y|{~~~~~~{szywvtuuuyvsw|~xx{z~}z}{xwxxz{}wtsvz}}~~}}z~x{xw{xwtuxz~~~xtuz}~}}~z~zy|yz~~xvxyzzxvrvwrytvzz{|}|}{x|}}xx}w}{vtwy~~~|~{xy||xxyz{{{y{||}~}||zuuw|~}~y}y|{~{xz~~xwyxvywtrtxzwwz~~xw{||{~}~|yxz|||{xww~z|{|z~||yuxyxzzxwy}~}~|xst}~~~|}~~|xx~~}{z}{vtvutuvtvvtusv||vvz~}z|~{{{z}~zy{y}~~~{yxz~~~~}z{~|xvwywxzxz|}z}vttr||~~~}~}z~~y}{~yvtvuxxwvuyyuvw}}xx{y}~|z}}~|zxw|~~~~{zwvzx|~}z{|}ywsuvxyz{|~~{}}z}~zww{~|z~}~{}~~zy}yz}~~~yxtutrxuuvsssy}}xwx{z{}|zx}}z}y{~~zvwy}{{y{}}|zwyxw~}|{z~{|wvrt||yv|~}|~|{~}~z}|{yyvxyvvvwxx|z}~}|xv}y}}{wz~}yxz{{zyuxwz}~{||||}}}zzzz|~z|zz~~~z~}}||~~||w{uuwzzz||}ywy|}|{~|uy{{}|{|}}~}{y}{y~}}y}zv~xvxw|}}~|}~{|vu||~|xwyx|~{x||{{yz{~}{|~{x|~~{xzwyxw~~z{{z}~{|x|{y{{|~|~z|~}|}zy~xz{y{~|z~}{|}{y}|yz}xszu{~ywwuy|yv|yvusuv{z{yzwu{~zxz~y}|wxzuvxuwxwz~}{|}zyy{wxz}{~{z||sy~x{}z}}yyzy|}|{zz{~|||{szz|}}{xxxuwx}xty~wuwuvvw~}uxwvu}~yxywy~uy{wzw{xwyz|~~yywvvtz~x~z|{{yz|~z}w{yxyxyz~}}~~}zz{{y{ssz}zxyxxz}~xwxsttttszv~vysz~~yxz}z{yw|t{xwyy{||}|zzxwu{w{||zzww~zzxy}yxyxy{||||zz|}z~{}vw{z~zxxyxz|{vw|wwwwwx~uztyv{yz{zxwy|v~w{ywxz{~~}xwvurt{~yzyy{yzz{|v|zwxyz|~~}{~}~~y{~zrz|~~{xxyy||x{~xutuusx}zwy{tuz}zvx~z~}vy|vx{yyz||~z}yxxwuzy}{}{z{w|}xxzzxvwxz~|||{{||z}~}~~{~~{tw|{~{yzz||ww}ywwxwy~u{u{vzyy{{zv{zy~w|yvvyz~||xvvvsw}z{~||xx~z|}x{xz|{wyzz~}~~~|z}~}|y}|tsy~~zwyy{zy{wuvxuw}|u~wytw~~{uuy}}~v|}z{v}|xy{|~{~~zwxxuz{x|zy}}y{wz~}{|yuvxy~~~}~~||}|{z{{{}}ztrz}{|}~~zw{xwyxxv}sxx}yvx}{~yx~wx~zwuxy|~|~zwwxuv}~{v|}~ywz}z|x{{w}yw{z}}~~}|{~~|~{z}}zsw{zzy{z~{z|ywxzw{yv|xyuw~~{ttz~|~|v}}z|w}yz||~z}~zvwwu{zy}~{y~~{{~x{|z}zuvxx}~~~|}~{|yy~|yz}}yvqz}~~~}}x{~zwxyx~zxxx}xx~~|ww|}}~w~zzy}{wwzz~|||ywwxv{~wy~xw}|z||w~~|}y}yy{{|}}~|}~{}}|z~~zxz|ztz}||{|{{z~{yy{y}yv~w}vv|~{vuz|}|v{zx{z|}~zyzvuvuy|y{~|z|}z|{|yz~|xvxy|~~y{|z{wy|wvy|}~~zxtw}~zy}yxyx{}wuzvy~~}zx{|}z{x{~}zxzz||y~~{xvxwz|x~}{wzzz}}xyzy~~{yz{~~}~|}}{y{~|y{~zww{~~|{xz|}}}}z~|z|{~yuw~xuz|xvz{{~|w{zy}{}}}yx~zvtvv{}{|~|z~|z|~z~}yz|yxyz}{{{{z~yv{yuux{|}|ywu|~z{}yyzzyzzwxv{~~|yz}|}zz{z}zz{{~yz|ywwxz~x~~yy{y|}yx{~z{}z{{~~}}}}{yz{~{yz~zvvy|}}~{yx{{~{~|}}yvw~yvz|xw|{{~~{x}{y}||~|xy~yvuvx}z~~~|||z}|{z|~z|yyz|~x|~z|zvx{vtvz||}zwux}|{|zz{~|y~v{vx}~~}{yy~~|~}{}yz~|{{|~~zy~~{yxyzw{~{z}yz~}zx}|}y~{{{~~|~~{{|{x}}zyy{wuxz|||zxwyz|~~~|vz{}wx}|ywy|z}|y|{z}~||}~xw}|xvvx{{z|~}|~{||{xz{|{z{}}{zz}y||xvyyttwz{|{yvu{}||}{|}{{~w{vx|~~|zyz~|~}|}yz~}||}~yx||zyz{w|}|{~yz}}zy~{y~|{{}~{~~|~~y|xz~~|zxy||wuvyz{{zxwy}|~}x{xzwz~~{xx~{{~~{z{z~}}~{wx~{xwxz~{~}~}}}{|}{{|yy|~|{{|~}z{z|y{~zwvy~xtsvy{{{yww}~||}}~|{w}vw{}~~}{z{}}~||y|{}}}~zwz~}|{{}zz|{}{y{}}zy}zzz~||~}|~~{y}yw{}~}|zxxz~}yuuwy{{{zyz|}{xz{~xx||zy|}{}~{z}{|{~}~|wvz}zyy{~~}~~~}|{}~|{~w~|y~~|||}|x{{yy|}zvuw}zustwyz{yxx}}||{x|xwz|~}}|{|}}~||y}z~}}}ywy}~}||~{||{~{y{}~{yy~yyz~~||||xxwz|}|zxwwz~{wtuwy{{{zz|}~y|y~|xy}~{z{||}|z|~|y~~~~yvw}|zz{~|~~~~}{}~~|{}x{~x|}|}}~|x|zzwz|~}{yvuw|{vstvxyyxwx~~}||x|ywz|}}}|{}}}~|~~y|{~}}}{y~}~z{||~~}~~{|z}{|~}|{}|z{}zy}{wz}}~|}~|}|{|{{|~|wv{y~~}|~|{||zz{|~~|}~~~}}{yy}}}}~{}}}~{z~{x{~~~~w{}z~~~{~}y|zzy{~{{~}~{}}{|{{~~~~}{{}~}~|zz}{y{{}}y~~}{~{x|{}{zz{}|{zxxy|zyx|~~z~|}}|~}~{}}~}~~|}}~z~}{~}}{~~{{~{|~~}{yw}zsvxvtsx}~{~zy|z~~}~~|~~~}}{|~~}|}~|~~~|~||wy|y|~|zz{uxzxuuz{~zy{y}}{y{|{z{y~|~~~~~~y~|{~||}~~xz}z|~~~~}~~|{zz{vy|zww{|{z}z~|{|~|{|z~}~~}|~~~{~}}}|~~z{|}}zzx~{tuyxtsw{~z~|y}z}}}~~~|}{~~}||}~~~}~~|}|}~yw~||{{z~~wuz{wtxz~z|}x{yz~{z{{|yzy|}~}|{{~|~}zx~}|~}|~~~~{|zxw{}yvz|{}~y}z||{|}|yzy|~|}|~}}~|}~{}{z~~~}~zzz~|vuy{wtxy|y}}y}{}}}~~}z{z~}}}~}}~~~~~~~}}|{~~~yz|{|||ww||wvyz|x}{x|y|~z{||{xyx~}}~~}{}|}}}~}}~x}~|~~|~~}{|{wz|wyz|{{y{|{{}}|zxxz|{|}~}{~|}}~z}||{}~}zx~xx~|xyyyy{{z{}~||||z{y}~wz{~z}}~wy||~~}~~zz~zx~|}~~~|y}|z~}~uv||z{z{|}||z|~~}|~}}|}~}}yz~{|{|{}~}~y{w~{|~|xvzxz{|{vy{||}|z|~||~}z~|{{}}|wvz}|{}|}~~|}||yy}~||{xx|yq{{wxsrrstuz~{}|zzz{||}|z~}||||{}~~}}}{}~~}yzwz{w~}~}yvy|{zw{yvxyxz{wuuy}~}|||{yy|{~{|}~{}{|~~}~}}}z{z}|yxw}~ys{zytz{wtvxy{zvw~{zyy|}{zz~|~yz|~{{~|}}~~~~|w~|||u}~}||zx}yvxz|{vwyyxwxzz{~}z~|y{}zu{~{{}}|~}}~~}y}~|~~{}yxzx|zy}~z{w}ux|{vrtuwzzww{~~~~{{{zx}|{zz~{uz|}}{}|~}zsz}x|{{{~zv|}~xu|~}{xutz}{zz~}zw}|z}w|{z|yux}|~}~}|~|{yzx{zz~}|xy|z}y{{yyxzywvx~~||~}~|{}y~~{|||}{zz}}~|~~|{}}zw~vwuy}|{~}ywxx{~~zxuw{xxyz~|{{w}}z}{||}y}yx~~~~}}{~}~~~}{~}tx{yz}}}{}yywy}{{yyyw{|z{|{x~{{|yz}wwzyx{~|{|}}}|{||~wv{z{z}~}zzz}||ztv|{zwwz|}~{}z|}|z~z|||~{|{|}}}{|zvwz{|{wzx|~}~}z|~~|z|||~z~}}{||}zwwux}~wy}zx|yw~~}{~{}~}zv|~~~~~|}}|}y}~z|~{|~~}y}~{||y|}}|xzxz}~~{~{w}{y~}~}~uvz{yv|~yz}}|{y{y}~}}{{|xy}}z}}~~w|~{y}y|{|}yzz{~}{}}z{~~}|}z|}{yy|wx|yyyvwwy~}~z|}y|zx~~}{{~}~|~}||~z|{{|z|zxzz{}}}|}~}{x{~~{}|yzywzwuvw{}|y}yz{wz}|~~|}|~|z}~~{}z|}{|xy|{|{}~z~|xw~|~~|zzwzz{{wvwx{}zy|wyzwy~|}{~}}{|{|}||z~}}}{|||||z}|{~{xx~}|~}}|x|{{ywwxy||{z{w{zx|}~{y~~~}}~~z|}||z~}~}{z|~}~{|{~~{xx|}~{{w{zzyvwxz{{~{{|x}ywz}{}zy~~{{{||||y}|}}{{~~~{}||yww|}}{w{zzyvvxz{z~{z}x{ywz~|{~{y|}{{|{}{~z|~||}|{~||xwu~|}}~}zz|{zvvwy{{~~|x}yz|wy{}|~{z{~}z~||}{{}}~~~}}{vxw}|}}xwzxxuttwzz}}y{{|xz{|{|zz|{wt|~}{~}{}~z{~}~}|}|~|xxu|~{~}{}~zy|||xwwz~}{|}{}zvw~~zwwu}}||{x|{z}~z|}}~|{~~|yxu|~|~}~~{z{}~}xxy~|z}~{}}~~~~~}yvvu}~|~}|{y~||~~z}}}~~|x||||}~~|}~|}}yx||{{xz}yxu}~}|xyx|}~}}~wxz}|||{|y{y|}{|~y{{}~}|y}~|{xz{~}~yyxz~~|yw{|xsv{~{{xwyx||wzwy{}~|}~}}{|~z}}}{zy~}}{y~{tsy|~zz{z|y}|~|vtvy}{vrzz{}~}~{zz{~}}||}~}|~}~||ux|wquw~|{||ywuxyx|}}}}{|}~uz~}}~||xz||~}y|~zzyvsz{}vuwvuz||~xy}{{{|yx~{}zz|vx}~|~~{|~~|}z}~~~z}{~{{ww{|z{~{{vwwuuuttvyu|{xyzyz}~~xz|~~|}}~~|z|{{}}~|~}}}{|yx}~~|yvy~{vnry}~{z{x}z}zz|{|}y}}~y|zzyz~}{{}}}~}}||~~zxztw}~|z}yxyw{ywtv{y|x|~yvzyw}}}~}|{{~vu}~}zzz|}z~~}~}~}}~~}{{~~~xwzyzxx|}|tux{{x|z|{|yyx{}~y~{}~|~|{}|{vwz}|{~~~}z||{}|~xw~}}wtyyzyy||}z|}|~yy}}||y~{}~}{}}~}~~~~zwxsw}~~}yyzzy|wtux{zyw{~yyxx~}~~|~z|}~xu}~~~}~||{}~z|~}{vswwyzx~~}~|xz}|xxy{zvx}}}}y}~w~~|ywzy|~~|zy}~{~}~|~}}{tv|~|~~}~~{uxy}}{zyyyv{}~|~{|}z~}~{|}{~}z{|~|}|~~~||wyzvw}~|}z{{|yy}~{z}~z|{{{}}}{z|z{z}~}|}|}~~}|}z}~x}~wwtuvyyz}~~}{x}wz}~zux|}~{{z}}|y|yw|~|~~}~yx}{yy||~{{{z~{xvyx|~|x{|w|}~|wwwyxz|~~||}}zx|~{ty~z~~~}~|}~}~x{{|~}}}}|wz~}|}}}}z||~zz|{z{|}~~~}{|zw|~~~{{~|}~~~~~~|z{}}}~~|}{{y{}z}|}~|{}{{{~|z~~|yxwy|}|{~}~yz}zz~|~}}~|~~~~|~}y~~}{|{zz}~~|~}~zzwx}~~~{y}zy|}z{yuy~~|}~{}|xxw~~}|zz||z}{{}~|||~}~~}zx{|{~}}~}~}{}~zy{}zuw}|y~~|}}~|xx{}~~y|}{y{~|~|z~~}|~|yz|~~~}|}}~yw{{}||}}}~~~{||y{~~yx|}~}z~~|~{xz~}zx{~~}{|zz{{}||}~z||{{|~~}}}}~}}}~|{}yv{}|~}{|}}~~|~}}{}zvy|xwwz}~z|}{z~}zyx{~|z{yxy|~xx~zwy}~~~||~~{~~~}|~|xvwxx{|{{xy|zy{}~}{}~|}~}~zy{|{ywx{~~}~~~~{}~}~~~{z|~}wvxyxz~}|~{~~~|{{~~|{|||~}{|{zy|}~~~~~~}|}{}~~}{yyzyz}}xx~{z~}{{|~~|~~zy~}}~|{zzz|~yy}{z{|~~{||}|{~{y{||~zwwy{~~~~}{y{~{}~~}zz|~~~|}~~}~}{{z}~~~~~~~{}~|}}}~~}xtuy~~|~}|{z|}~||{}~~|||~~~}yx}~|{|}~|~~|~}~}|{}~|zyyz|}~~~~|}~~{{~~~~~|{}}|{||zz{|~~}}}|zxy{~~}~~~}~}zx{{zz{|}}~}|{|}}~~~}|{~}~}~}{zzy{}{{}~zy}|~~~}~~~|yvvz|xvz|z{~}~~}}~~}}}~}{y}|xxz}~~}}}|~~||~}|}|{z}{ww{}}}}}}~~~~}{~~{}~~~~~{y{~~}{{{|~~~}~~{}}}~~z{}xwz~{{{||{{z|~}}|~}~}{z|{xwz|{yz{|{{~}}}~}{~~}}~~|yvvz|}{zyzz{}||~}{{~}{{|}~~}yvyz{zy{~~zz}~~~~~~~~~~}zy|~|yww{}{|~~||~~~}|}z}~{{}}|}~~}{~~}}||||}}{wvvy|~~|~|}}z{~}{yz}~~~z{~~~~}{{}~|yxz{{{{zx{~}|~|||}~~~}~~{{}|yyz|~~|}}z|~~~~~|xx{}}}|yvvy|}}}~}}}}z{~}~~{z|~zyxy{}~}}~~~~|~~}~~|{~}zyy{}|ywvvy{~~~~|~{|{|x{z}|{zxy~|{}{}{|}}~|}|}~~}}|~zuv{}|{wwx{{|~~||zww||}}z~~}zz~}~||yzxz{|}|~}|xxy~~xv~~}~~~}y}{|||~|}}|yvxwy{}~||{~}zx|y~}{}~}~}{|~~~z{{z~|x{{}}~|{{}~~~}|}~~}|zyvtyx{|~yz|{{}|{~~{{{~}~~}~z~~~|~~{z{{}{yz}~|~zz~z}~{}{}}}{||{|z{|}|xwy}zwx{|}z}{yyy}y{z|}{}|{~y}~}|{{z|}~||{}|~~{zyyyy}~|{yw|}zzvz~}||~}z{~{||zy{~}{~|}}~~{|~~|~~yy~~|{vx~~||{z|~~y~z||}xzw{}z|{{{}|~~{y|~~~~|y{}}}~|x{zz}~}|~zyxzw|{{}|||~{{}|x|}|{|}}}}~zuy~|}}~|{}||{|}{~{|}}~~~}~~|y|}}~xt{}}xxzy||~~zyx~|{{|xzwz}~z|~~~|~~~~~|yxzy{~}~{|zw|||}{yyw~zz}}~~~{|z|}x|~~}~|}~zx{~z~~}}{zx}}~~{x|y|{||zyw{~~~|~z{|{||}~y|{~}||}zuw~~}}~~}}{}~|}}|~~~|{ywzz~}y{z}~~~|~}}{ux~}|}}~y|~x{}~y|zyywyx{|}}}}~{~~~|xw|~}}|~~|zz}|zyyx{z~}}}z}}z|z}{~|z{{~}{|y{}}z}~}|}~}zxx~}y~~{{~|}~|}z{~}|}}ztvyy|}zsw~{}zyxwz{~|{{}~}{~{{||}zz~~}~~{}~}yx|xz}|y||~z~}z{yzwswzz~~~~{x{}}~~}~}}|~~|}~|zxx}{~~||~}yz{x{|x|z{{~}{zuuvz~|~~}y}zx{}~}yz{z{zx|{x|}}~~z{}y{}}{||}{xwsv|~wvvy~~~zvxy}|z}{{|yy{}}yx~{z~{wyx{~~}}zyz|}~~}zyxwzy}yuv{}}{|}~}|zxxx{}|{{{}{|}~}y{|{xx{~}|{{}}z~zz{~~|yxxwx{xx|}|~}~zz~~|xxy{z}z|{yx|}|~|zzx{~|||~}}|~~~|}{|}~zyyxwwxyz~||}{ww|~||wxx}{}yxy}~yxy{|}}~~||{}z|}{{y}~}~~zz|}~{xyv{~y{z}||~~zx|zxxvw{}}z|y{~~~||~}}yyxyx~~{z|}|z|~~}|{|}~}zwxw~yw|~{~~z{~yxyy{|{~|}{|~~~~}}|zxx|~}}|}}}}~~}~y}~~{}zvuv|z{z~}}~~y{~}}{ywy|~}y|w}~z|{}z{|yxz}}~}~|}}}{}~~{~}y{|~~|wusw~|zyz}}|~|wy|zzyw{|~}x}~x{}}{}~}{xxz}}|~}~}~~}|}|~||}{y|~ytsw}{z~|}{xy}xxzx{|{~x|}~~~||}{zx{{{{}~~~~|~}~~z|z{vz}|~|ysvx~~|{~~{x}{xwvw}~zyz{y}|zzzx{xyyz~}~|~~~|~{}{}}zxx||{|{zxvy|}~|}}|~|z~{y|zz{y|z}}{z|||~uw|}~z|~~z}}}}}~~}~w}}|{}}{||roq}}x}}~x{|yytw{yzzzzwzz}}x}{x{y{}~~z}~|z~}{|{yx}xw{|~z|zyxsov|~x{|~}yxz}|}|yy}|}||{z~}||{yz{ywvy~{y~}}}z~|}|y{z|}}|}}{ysuz{~z}}~~{{x}y||w||w~~}}}}}{yz|z~{z~~}}~|}}|~{yw{zyyxwy}}~xxyvq|}}zxy}{xuwz{xzyv~{{{|~{{{xvx~|~{|}}zw}||xy~{{{tz~yytvsu}~{}|x||||{~z~zzyz}}~}|}y}z{~~~~~|z~zzzz~zwz~}xvss~wuz~yy|}xvw~zzww{yzuy{~{|yu|~{{}}|~x{z~z{wzyxywvz}|x{wtvy}~~~w{{{y|}|{||~vz{y~~x{}zwwx~{}{z}|~~}{{}z||zz~}ywu{||xx{~~}~yy~y|y{z}zz{{yx|}~~|z}~{~|}{{wz{{y{y~yxwuvv{xxyy}zvy{zv{}}|x|~z{}~{zzv|zywxz|{{xx}~|{z~|y{{xwz|~~|~xtx~{{{xrvz~|{yvvz|~}|z}~z||{}~~~{y~}z}{}|x|~}~|~z}~~z~}{|z}}{y}}yywuy}|{yyz~{xv|y|}~}}yz|y{y}zwxx{yzwuyyx{~~yx}|x{}{wzutvxywwvwzwz}}|}{~}|{zz~~{zyz~~~}zzz|~~|w|~|~wyy{}|{uwwz~}z|~{~~|z{z|{{~{}}~~~|}}|yywzxww{~~|y{}{}~~~}}y{~zywx}}}}|}z}{w{~|}~~||~~}{|}~~~}zyy}|xu{}~zzz~zxwz~z~}{}{xxzz~}|}~}~||~{|~~y{{|~{~z}~~{}{}~}zyxxy{{|~yx}}z{z{{||~}{|zwxzzvy~}{}}|~y{|~~~}{vw{zy|}|{{}~~|z||{{~}|}~{~~{}|}~}zwvw|~}|y|yyxz{xx||xy|~yxvy{xvw}}~}yz}|~}}~{yy}}}~}~|}}{|}zz~~|~}{}~}|}{}}|~|{~~||~}zzxxy}~zzx}}|zxwvxxzzz|~yxx}~zyz}~y{}}}}~}}~|yw}||||{}|~|{{z{{~}z}{{~~~~|~}y}~~}~zww|}|z}|zxxyy{z{ywy{y{z}|yy}~z}~{y{~}}zz|{~{~~}~~|z{{}~|}}||||||{{|~~{~~}y{~}zyx}~}~xz}}|zywxxzzy{}z{zz}{zz~{z|{yx~}|}}}~{|{}{|~{|~||{{|~}~|~}|}||~||~~}~~}||z~}~}{{z}|{yy}}|zzy{zx{{zzyx{z}{yz~y|}|z{~|}{|~{{||~|~}~}~}{||~|~{||}}|}}}||}~}}||}{z~~~{zzz~zz|||||yzz{|{z{zz{x}{wz{}}zxz~|}~|~{yz~~{}~{|||~~}}|}}}~}}~|}}{}~}}~}|{~||~~~~~}{{||}|zy}}}}|yyyy{|{|zxzy{}zx{{|}yy~}|}||~~|zz|~|~}~}}~}~~||}}}{~~|}~}{}~|~|{|~}~~}|}||||~}{{~}|||zzzz||{|yxz{}|xy{}}{{~~}}}}|xy|}~~|}~|{{}}~~||{~}}|~~~~|~||{|}~|}~~~~}{{|}~{z}}}~|yy{{z{{|zy{z||zx|||y{}}|~~~~~~~yxz}~}}||~}}~~~}|~~~~}}||}~}{}~{{}~}~}}}||}~~}{}~}zyyzz||}{yz|}}{y{|||{~|||~~}~~~}~|xy|~}~~}}~}}~}|{}}|~~~~}~~||~~{{}~|}~}~~}|}~~||~~~}{{{{z{{{zy{|}~|yy~~}~|{}~}}}~~~~~|xy~~~~}~~}~~~~}||~}~~~}}~}~~~~}|}~~~{z|~}{}}|}|}~|{zzz{||{{||}}{y{}{|z|}{|}~~~~}}{yy}~}~~~~~~}|~~}}}}}}~~~~}}~~~{y{~~||~~}~~}|{{{||{zy{}}~|yz}{~{{}}{|~~~~~~}zy{~|}~}||}~}}}~~~}~}~~~|{|}~~{z{~~|{~~~}~~}|{{{{{{{|}}~~{y|}}|{|~}|}~~~~~}|zxz~}}}}~~}}|~}}~}}}~}~~~~}|||}~{y{}}|{~~~}~|{{{{|||{{}}~}{z~|}|{|~|{|~}}~~}}}{zz~}}~~~||~~}}~~}~}~~~~}{z{|~~}zz{~}|~~}|||{{{{{{}~{z}}}}{{}~}|}~}~~~~~}|{yz~~~}}||~~~}~~~~~}~}~~}{z{{}~}{z{~~}|~~}}||{{||||}~~}{z~~}~||}}||~~}}~~~~}||{z{~~}}~}}~~~~~~~}~}~}|{z{{}~~}|zz|~~}~}|||||||}~}{|}}~||}~||}~}}~~~~~~}|||zz|~~}|}~~~~~~}}~}}~}{zzz|}~~}}{z{}~}~~}}||||||}~|{}~}||~~}|}~~}}~~~~~~~}||zz|~~}}||~~~~~~}}}~~}{zzz{|~~~~~}}{zz|~~|}}}}|}}}}~~}{~}}||~~}||~}||}~~~~}|{zz{~~}~~}}~~~~~}}~}}}~~}{zzz{|}~~~~}|{zz|~~|}~}}}}}}}~~|{~}~||}~}}|}~~}}}~~~~~~~}||{{|~}}~~}|~~~|~~}}}~}|zyyz|}~~~~~}|{zz}~}||~~}}}}}~}|}~~||}~}}}~~}}}~~~~~}}|{z{~~}}}~}}~~~|}~}|}~}|zyyz{|}}~~~}}|{zz{}~|{~~~~~~~~~}}~}||~~}||}~}|}}}~~~}}||{{z|~~~~~~~}|zzz|~}{|~}{y|~}{zyzzz{{{{{zzyxxy{}}||~~||}~~~~~}|}~~~~}|{||}}~~~~}|zxz}}}~}||~}z{~~|zyyxyz{||{zxwwxyz{||||||{zz{}}}~~~~}}~~}}~~}}~}{ywwx{~~~|yy{~~~}}}}}}}~~~}|{{|~~~~||~|{|~~}|||}~~}|zyyz{|}~~~~~}}}~}~||{{{|}|zyy|~~{z|~}{yyz|}~~}|||}~~}}}}}}}|{{zzz{}~}~{{}~|zz{~~|{zzzzzyxvuuuvwxyzzzzyyxxz|~~||~~~~}~~~~~}}|{zyxxy{~~||~~}}|||}}~~~~}|{z{|~~}|{|}{{}~}}}}~~~|{zzzz|}~~~~}}~~~~}}|}}}}{zy|~|{|~~|zzz{|}~~~~}}|||}~}|||||||{zzzz{}~}}z{}}|zy{}~||{{{{yxvuuvwxyyzzzzyyyyz|}}{|~~~~~~}}|{zyyxxy{~}~~}}}}}~~~~}|{zz{}~}~|z|~~|{{}~}}~~~}|{zzz|}~~~}}~~}}~~|zz|}{|~~}{zz{|}}}}}}||||}~~}|||}||{{zyyz{~~}{{}~}zyy{}~~~~~}{zxwvwwxyyzzzzyyxyz|}|{{~~~~~~~~~~}}|{zzyxxxz}~~~~~~~~}{zz{|~}}}{z}~}{{{}~~~~~~|zz{{}~~}}~}{}~|{zz{}~|{}}{{{{|{{{zyyxxyz|~~}|||||{{zyxxwxz}~~~{{}~|zyxy{}}|}}zxwxxyz{|||{{zyyz|||zy|}~~}~~{{}}|~}|}}~~~~~~}}||{{zyxxxz|~|~~~}|{{{}}~~|zz{~}zyxxyyzzzzyyyz{}~}|{~~}~~~~|}~|{z{|}}}}}}}|}}~~~~{z}~|{zzz|~~|}~~|yy{}}|{{{}~~}}}~~|zzzzz{{{{{{zzz|}~}|~~~}~}}}{zyz{||||||{{{{}~~}|{{zzzzzyyyz{}~}~}|{{|}~~}|{zz{}}||{{{{{{zzyyz{}}|{{}~~~~}~~|{{||}~~~~}}}}~~~~~}~}{|~~|{zz{|~~||}~|zxy|}}|{{|}~~}}}~~|{{{{||||||{{{|~~|}~~~~~~|{zzz{{{{{{{zz{|}~}{{zzzzzyyyyz{}~}~~||||~~~~~~}{zzz{}~}}}}|||{zzzz|}}|zz}~~~~}~~~}|||}}~~~}}}}~~~~}~~}~~{z|}|{z{|~}|{|~}{yy|~~|{{{|}~~}}}~~}}}}}}}}||{|}~~|}~~~|{zz{{{{{{zzzz{}~~}|{zzzzzzyyyyz|~~}}~}||}~~~~~~~~~~}||{zz{|~~~}}|{{z{|}}|zz}}}~~}}}~~}||}}~~~}}}}}~~~}}}~~~~}{yxwxyz|}~~|{|}}{yyz}}}}}}||~~~}||{zz{|~~~||~}}|yz|}~~|{{{|||}|||||}~}|{zyyyyyyzzz{|~~}|}}|{{|}~{z{~zxwxy{}~~}}|{{|}~~}{{}~}}~~~}}~~~~~|{zzz|~}||}~~}|{{|~~zxxz|~~}{zyyy{|~}||}~~|zz|~~|{zyz|~~|zyyz|~~}}~~||}~}{zyxxyyyyyyyyyz{}~~}|{{zzzzyyxxxy{}~~~~}zyyz|~~~~~}}}|||||{{zzz{|}}|zy{}}||}~~~~~~~~~~}|zyyz{}~~}|{yxxxz}|{}~~}{zyyyz|}~~}||}~~|zy{}~}|zzz{}~|zyxy{}~}}~||~~}|zyyyyyzzzyyyz{|}~}|{zzzzyyxxxxz|~~}}~}{yyy{|~~}}}|||||{{{{{|}}}{yz}~|||~~~~~~~~~~~}|zyyz{}~}|{zyxxxz}~}~~}|{zyz{|}~}||}~~|zy{}~}|zzz{|~}}}|{||}~}~~|zyxxy{}}~~~|zxwwwwwwxxxyyz|}~|{{zzyyyyyyz|~{}|{|~~}}~}zxvx|~~~~}||||{{{{z{{|~~~|{{~~}~{xx{}{{{|}~~~}||~~}{zz|||{{{|}~}}~~}~~}}}~}|{{{{|~~zxwwwwxyz{{{||}}~~~~~~}}~~~~||}}||||}~~~}|{{|~~|}}{{{|}}}}}}}}}~~~}|{zzyyzzzzzz{|~~}~}{|}~{z|~~}~~|{{{||}~~}{{}~}}~~~}|{}~}}~~}}||}~}{}}{zzzz{{{|}~~}||{{zzyyyz|}~~}}~~~~~}||}~}}}~}|~~~|{{|~~{{|~~||}~~~}|{zyxyyz|}~~~|}~}||{{{{{{{|}~~}}}}}}}~~~|||}~~|zyz|}}|{zyyzz{|}~~}||||}~~|{}~}}~~}~}|}~}}|{{{{{|}~~}|{zzyyxxxwxxy{}~~~}}~~~~~~~}~}|{{{|~~|}~}||}}~~~~~~~}}}}|{zyyxyy{|~~}}~}~~}||||{{{|}~~~~~}}}}~~~}|{{|}}{yxy{|}|{zzzz{|}~~~}|||}~~||}~}~~||}~~}~}||{{{|}}~~}|{zyyxxxwwwxyz|~~}}~~~~}~~}|{{{|}}}}|{|}}}~}}}}}}}||{zzyyyyz|}~~}}~~~~}}}|||||}~~~~~~~~~~~}|{z{|}|zxxy{|}|{zzzz{||}~~}}|}}~}}~~~~|||~~|}~~}|||||}~~}{zzyxxxxxxxyz|}~~}|}~~~~~~}}|{{z{{}~~~||||}}~}}}}}}}||{{zyyyyz{|}~~}}~~}~~~}}}}~~~~~~~~~~~|{{{|}}{yxy{}}}|{zzzz{||}~~~}}}}}~~~~~~|||~~|{|~}}}}~~}|{zyyxxxxxxyz|}~~}||}~}~~}~~}|{{z{{|~}||}}}~}}}}}}}||{{zyyyyz{|}~~}}~}~~~~~~~~~~~~~~~~~~~|{{{|}}{yxy{}}}|{{zz{{||}~~~~}}}}}~~~~|{{|~~}||~~~~~~~}|{zyyyxxxyyz{}~~~|||~~}}~~}~~}|{{z{{|}}||}}~~~}}}}}}||{{zzzzz{|}}~~}}}~~~~~~~~~~~~~~~}}~~~~~}|{{|}}|zyyz|~~}||{{{||}}~~~~}}}}~~~~|{zz{}~}||~~~~~~~}|{zzyyyyyyz{|}~~}|}~~}~~~}~~}|{{{{{|}~}||}}~~~~}}}}}}||{{zzz{{|}~~}||}~~~~~~~~~~}||}~~~~~~~~}|{{|}~}{yyz|~~~}}||||||}}~~}}}~~~~~}|{zz{}~~~||}~}|{{zzzyzz{|}~~}||~}}}~~}~~}|{{{{{|}~~}}~~~~~~~~~~}}||{{zz{{||}~~}||~~~~~~~~~~~~~}|||}~~~~~~~~~~~~}||}~~|zyz|~~}||||||}}~~~~}}}~~~~~~~~}|zzz{}~~}|{|~~}|{{zzzz{{|}~~}||}~}}}~~}~~~~~}||{{z{{|}~~~~~~~~}}||{{{{{||}~~~}|||~~}}~~~~~~~~~}|||}}~~~~~~~~}}}~~~~~}~~~|zz{}~}}|||||}}~~~~}}}~~~~~~~~~}|{zzz|}~~}{{|~~}||{{{{{|}~~}||~~}|}}~~}}~~~~~~~~~}||{{{{{||}~~~~~}||{{{{||}}~~~}|||}~~}~~~~~~~~~}}~~|{{{|}}}~~~~~~~~~~~~~~~~|{{|~~}}}}}}}~~~~~}}~~~~~}}}~~~~~}|{zyyz{}~~}|{|~~}}||{{|||}~}||}}||}~~~}}~~~~~~~~~~~~~}}||{{{{{|}~~~~~}}|||||||}}~~}||||~~~~~~~~~~~}}~~|{zz{||}}}}~~~~~~~~~~}{{|~~~}}}}~~~~~~~~~~~~}}}}}~~~~~~~~}|{zyyyz|}~~}||}~}}||||}}~~}}}~~}||}~~~}}~~}}}}}~~~~~~~}}||{{{{||}~~~}}||||||}}~~~~}|{{|~~~~~~~~~~~}}}~}|zzz{{||}}}}}}~~~~~~}||}~~~}}~~~~~~~~~~~~}}}}}}}~~~~~~}||{zyyy{|~~}|{|}~|{zyxyyyz{|}}|{{{}~~~}}||}~~}|||||}}~~~}}}}}}~~{xy{}~~}}|||}}~~~~~}|~~}}}~~~}|~}zyyyz|}}|{zz{}~~~}}}||}}~~~}}~~~~~}}}~~~|zyy{~~}{{zyyyyyyyz{{||||{zz{}~~~}}}~~}~|zyy{}~}}||{{zzzyyyyyyyyzz{||}}}}}}}}}~~}|||}~~}||{{|}~~~}{zyxyz{|||{{{{{{{||}}~}|||~~|{zzyz|~~}|{zzzzzzz{{|||{{z{|}~~~}}}}~~~~~~~~~~~~~~~}{yyz|~~~}||{{{zzzyyyyyzzz{{||}}}}}}}}}~~}}|}}~~~}|{{{{{}~~~~}|{zyyz{|||||{{{|||}}}~~~}||}~~}{{zzz{}~}||{{{{{{{|||||{{{|}~~~~}}}~~~~~~}}}}~~~~~}{zyy{}~~}}||{{{zzzzzzz{{{|||}}}}}}}}~~~}}}}}~~}||{{{{{|}~|{zz{{|||||||||||}}}~~~}|||~}|{{zz{{}~}}||||||||||||{{{|}~~~}}}}~~~~~~}}}}}~~}|{zyz{}~~}}||{{{{{{{{{{|||}}}}}}}}~~~~}}}}}~~~}||{{{{{|}~~}||||}}}}}||||||}}}}~~~}}||}~~}|{{zz{|}~~~}}}}}}}}||||||}}~~}}}}}~~~~~~~~}}}}~~~}|{zzzz|}~~~}}||||{{{|||||}}}}}}}}}~~~~}}}}}~~~}||{{{{{{|}~~}}}}}}}}}}}}}}}}}}~~~~~}}|}}~~}}|{{zz{|}~~~~~}}}}}||||}}}~~}}}}~~~~~~~~}}}}}~~~~}|{{zzz{}~~~}}}}||||||}}}}}}}}}}~~~~~}}}}}~~}}||{{{{{{||~~~~~~~~~~}}}}}}}~~~~~~}}}}}~~~~}||{{{{{||~~~~~~~~}}}}}}~~~~~~~~~~~~~~}}}}}}}~~~~}}||{zzz{|}~~~}}}}}}}}}}}}~~~~~~~~~~~}}}}}~~~}||{{{{z{{{|}~~~~~~~~~~~~~~~~~~~~~~~}}}}}~~~~}}}||{{{{{||}~~~}}}~~~~~~~~~~~~~~~}}}}}}}~~~~~}}||{{{zz{|}~~~~~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~}}||{{{{{{{{|}~~~~~~~~~~~~}}}}}}~~~~}}}||{{{{{{||}~~~~~~~~~~~~~~~~~~~~}}}}}}}~~~~~}}}||{{zz{{|}~~~~~~~~~~~~~~~~~~~~~}}}}}~~~~~}}|||{{{{{{{{|}~~~~~}}}}}~~~~~}}}}||{{{{{{||}~~~~~~~~~~~}}}}}}}~~~~~}}}||{{{zz{{|}~~~~~~~~~}}}}}~~~~~~}}}||{{{{{{{{{|}}~~~~~}}}}}}~~~}}}}||||{{{{{{||}~~~~~~~}}}}}}}~~~~~~}}}}|||{{{{{{{||}~~~~~~~~~}}}}}~~~~~~}}}|||{{{{{{{{{|}}~~~~~~}}}}}}~~~~~}}}|||||{{{{{{||}~~~~~~~~}}}}}}}}~~~~~~}}}}||||{{{{{{{||}~~~~~~~~~}}}}}~~~~~~~~~}}}||||{{{{{{{{{||}~~~~{wvtss{xxronoqptorqzxmhvywpc`]chq~yiinx{{ujpvrdaeq~yhs}m{xzrtu~{tt{~z~hivvuvkggcfjj[douzytmwsu{~yuvsVhpZ^hpr}z|u{xyz{w|zus|q~~uw{~sx|w~kp|sw{vtqyy|y{yx~pan~{t~zvt|y~}r|hn~y}z}~ypYZbwy|ndmmc^[igejphcrovybmu~yutqjmtzoqgiswvtmsu{}pfo|uzzwqmyzvx}{x}szkd_h}~xrktkwst{wrzx}}nwrpbgrb`hpmvoum]jX]biddkefxeshpsp}ogbivtshvdojhrf~u|x|r}pw{t{y|}|u{qmuu~tj}y}nmzyqxoqwss|~rv||rzy~lficgk{{zuvjj]ixhcijmivmqteuuiqto|pnbrk`eqkln|y|rt|hvqpqvrzv~ux|zuu~dgelh|qotsxk}}zzz~vszz~imWbu^Xlozmutq_kmfenqhpvkdujehzibm`m|j]_voZx~utwzit|zo~zx}{{~}|usyxY`~wwz~uzzuwvtywwsqyz}y{|yvn}neihdg|t{|bpiausdWizclysb|{vueywtlxhekjddxsp||{potnvttms{s|xtwspggomqx{qop{wt|nz}z~}s~y}~zmp{bVd\qyyv{mtxmqprgkrudptql|phrrjhvgd`fxdY[^smtwz|~vx{x|~{|w}}ip|{gvuvng|rys}t{|q}{|y}wy~qou|vz|~{~}|y|{thgfdel{|tzuskjpd`ksnqzxfk{xrrmwlitmflkeox~z}|x}uq~jlteo|s~yowurcql]bqrq~|{rn~rqw{~|z~xywy~{sqncbe^bbh{sy|gn|repnopqrksxrmyvjp{kkoZlraT^wrn|~{szq~{}xxzwyrhegxkf{yg~yvtxvuxt}xwu~|uz|j~uxs}yv|xh_rncv}x~qsuk{g]upx}yf{{ryleyoagl`cnrpvwz{rwmsllxs}~}x{yutwanbkjvzz{ow~}x|w~{{{v{qnqsXeeW\ig~}|{nzxtolylmxunoufl|llrsifeopacixtv~szyz~z{sjybdpo`apt~lyr{}}txqxyzt{uwqx~}~}}{yurigzppn_ijmq~v}riluqhx{sygo|yprqfqjagent|zvz|xjxs|y}tvwp{{xmmtogshbryptyrs~w{uzxz{px~p}{{zwz}}wx|pffgdmkf^is|||vq||~xytuu}oloohtve`_Wpvgak}{~{wzt~tz|yyumrsj\mnk_q|wquyy}y|vor}ppyxxp}|z~x~o{zy|kgjpmd`ixko~yrtutgr{jswtl}y~or}kdquor{vxyuyvowp|yu|}{wzpnoskabd[pps}v{uwx~{{~{|~zxzz|yruwnw~kjlh`fccYrtpuz|u}svxrw|xsyvt|iivmkY]gustu{{{|w||}zypqy|kiaolekpkp~yzx}z{ssqlu~tn~ymqu}|p|okm{rpvyykv|kXcvxusg|pm{}s{svw{sdppjdkunpxo{}{|xxwnlzps|wzzzopwrrfhainbsxzv{~txxz{|rz|{uywz}|syuzxtytcufnjc`Y`jisuyv~xx{yx}xvnrvy{z{lvvpouijjlwzz}|~~||s{{t}kmuiedehnf]q}w{{v||syt{vzw{~yy{j|vvutsgm{sjsyka\dqdZio~x|~w~~qy~zynxvnttvhfukp~~tt~}q|zt{}}wo~{joz{{okorqormcatw~{vqv{xsopzx~uu|}}y{sqzoszv{twhmuolrafophjfgm||~ww|vutyry}mik`ozmrbo}}yx~zws}yzsoqq}xr^bbutbhuw|vvxtxsjk}tw|xw~ww~uys}~v~skmvkjwlnZ`f][jtrlqq}t}~v{twz~xr~snss}ryx}{z|w|xx{yswqttymllsrcddhkculx||r~v~~v}zztsy{}z~zy~xt{|uyqxpmrwoddc\qffndihnoly|wzxz~ntrckrpwoluxtvzvy|ytxprioysndbiuutx~{wt{qiq{|y{rphzxxxzz~x}}}vrinz{qlgnyqsocfbloaYmr{uuv}v}zxwyuihrtsrj~x}z}xvuou{~x{qgp~ulcnmniffci{zx~{wq|xxp{x}}sywuvwy|~z|}p{tw}pon|wplnajhWcabqpf`fjr~z{w}tkquq{rzoz}}}}~yru~smhmnkqfg_dkr~p|xzv|}uzwu|xzw~{qjq{}~r|uynvnjtkokklokbnsaiWdpkjqx{urzvwhmuss}s|{y{}zr{}~softyfjlqtnofmhlvyztw{|}zz}r|xwm{uw{pjpqw|{zyuosvihpxvmnkqsqrmaZeffregvnsvuv||zvt|~~qxx~roxhy{v}{u|~s~||s}x}{{xxvt~yv{{~ttvq~oxvw}||xvlmunzsyz{xss}p{zpdfpliunj`c`no_`hdmjgnkj{{{xxr{{~}pqy|}|suutqv{whssqxuttxzx}}w||x}~~~rcgwvs^Vc[`eecgz|}||}{sy}glvglkmmhrq{{quxuu|combny~zys}~rufff`jcgptnhmem}iuqghegcly{|yyz|wy|twstvzoyoguvyuszvy|pm]e]]\^nuvjapbZ`ktv{ow}}st|zstu~{zw}rrdhrkost||}xy~zz{tuwploxtsnglhmh__iwhjkZomhigiryu|strv|uzyvjmb`hgo}wyyzrmkjp|qryr~{vnlkiatryqpjtzv}womvhjkqripuytummys~|roqsxxwpginvwu{~y~z|x}slmcplkyx|xjc]acck_aadciragncd[Zmw{}zsvtmt{{ymryzz{~|~|zr\fbZY`myvwpo{wvspttgswluqjlqyzuvrwokrssvu}vyvrumfmtjouwy~wxoxouvuywzmv~w~}~w|uus~{{{rlpopsooinkgfad~~{}{|uv}yzqwurqmlt}~xpjq~|w~zpokWdh_e`h|xnipzxrpdq{ulsibkj_igdu~}vkkmpwrngivxztt|lovefpuruz~zvxzglnmjkw{xxpwofhcakkgkf`hupopw{|vsvw|~zz~{pskofgr{y|}yxy~u}~t{w}yy|z}xtwqtnna[^^djf~{}|vvrinmmrvt|z~xm|}xtw{l}|juqu~~yz}m|x|rgqg^iji}{uplkojeohktdhtklqvoqpq|wuvy|r|xy{uvmp}fji]__ddny{uw~~~{|~wrlc[_b\_v{}}ztuxty}syuywr]d{{ol{hpymkr}wu{qpsgqls~ynsu{~rt}v}{zw}~z}ywq}z}~~|ppzy{ywvsui]]qxuhmus{yws~{{{tszwxgrzrt{~{xx{{cfzylljnzzusty~txuumob]cqnv~vurpxzz}}kgcc^mqhoal~mqrtqwrgiluz~qjkutwfamjcgkhyyponrofhZaa_hnu}{}rxxzpps_aZYlsrz~mwtolmtv~x|v}{w}oa_^b{|sl`ejigggdnlYlk\`achswwy{xx~mvaT[XYbrw}ytulz||||}stxr{y{ve_ljl}{wpsuvzyijebj[\^VZ[d^e}xzyz}~}yr}~}pulhjeigk{r~{kv}~|}|twvsss~orokjlvwleoxptmgluwgnjW_XY]\ew~~}|w}xkpa[jb^dfwxvwojnx~|xwwz~ss}v~n`cebdqwzyxkqtqhbacsgal\]eihgqw{|}~|voqweac`]]hy~~|}x~tvvwzz{rv}smeh]aqzuuwxoanpkebhYTWYfaasvwyz~~yyzzjhfdd]czvuv}~|spsqrtv~wjkjehnw}wqsynsvpprocmfQbf\^`gr~ux}pjti`hdYfnx}yvvwzwy}zvy{x{sfc^\[i~{zplrmgehini]ihbfeccisv{~v{xrg]\^Z[gz|}|wx{{qzuz{v}z{{}nic]ljk~|x{{vikne\`_TZ^^fbd}~y|ws{xoynekmkmoq~uvvxuqxxvsv~~sty^`mkmqz~smlz|opmlttjon]\d^]chtyy}ulobWZhjes}~vusv~|zzv{|utji[W[bioq{xrurlfefehib_`elsv}{vprhceZgihpuvy~z}vjlnofmwnxyy~x|uru|||yyu~|wx{xuvxxvqem{jftpkptw{{aansuvxypx}|}u{z{|~nu|jpxy{~|{vpyxyz{rjx~rtz{|zuuv{q{{~}ndvy{ustpejjhj_ficiq|vu{}zxvwwtoku}wvu}~~ykao}{}}tt|wmpvwxw{{{qrdfzyrqihnshgn]_fchhhmvyvwnv~}vvtryynsvlz~wqs~tx~wxrz}vyryzphj}{oheko^eh^dntttvryxjfntqvs}zx|wn{ux}}xqoowwfkvutvtmuytnyx|urmie`_xvmysg^ddVX\grilvxzzwz||wussuurt~xq{z~~pvsmhfrwz~p{vimquy{}|whebhrx{ysfkmW]ngegikmmkpwvv~wzrqvnjxvp{v{~}{uomsq~~|u{{|z~|~z}x|}qrofeu|{vtssibkihllkknmjyximvpt~~|~z}}y|yvruvrprttwjax~||~zlptrzuz}~wrrph]Xcvuy}p`ceZ[bdki`oz{xqr|wr}}~}vqowwo}w~|rxwkf`fnnntsmtxwssw{zxwm^dmki{vrxm_ejb``agmnkrwmuzt|xwovwlmyxuyxz{xpsxm}~}|~uy~~sy|}~y|~xxxkmqiirqx}wuooriegkoqppsoqsfjpgq{yxzqz|vwuqmlkmqgowv}{|wmuzryvu|vkjgb]\eos{ylhpf^_acmlepusru|y|}{xvvx~ky}{}~}zqjwwhbhrlsr}~mnsstvy~z}|~~}zyvsk]bojmqtwk`esldfiljgcppux}}wqkuuosysnxzxsmjtym~~x{}{}x||wx|z|zsmjrocdbgw||||sowkfpsspnorogprouqx}~~~sztnlgcefb`\Zk{|n|zxyvxwypjuqjnqpox{}|xr|}sv{|{{l_s{oqwqx{y{|{|~r\k|xtqnjrsikw{y}y|vZl}zz{}}xwgn~rmljd[n{{r~tpqyskkkkkhgtud|x~xohZ[|rkpleuxuxz|}{y~|p|xrzxxzy~z{}tvwvsnknheyzdblbq{oh_TW\aa`lrokkoilxoxygm}wpnlic]V\jt~}}qinr|~ugaaXUVTT]jgo||~{{{ypnjd\Z``j~}}|zxz{{wty|}yuxtknuy~ytopmighghiarrhlfeimysnz~}vqoh]QP\ac|}qtvf^hf^`bcf^]r~}vr{xwwpmvzp{xvz|~wvrgYOMVX]vyussrhfsi^q|tpomc`qzqmx|skglsow{s|}tqpprrop}}{{qbajml~y}zwxywsttmu~}vvvlcnogedcgkci~~}uvxyusunm~zske]Zdgovopihnfivxuwxpy~w|{yxtojffjgsw~yztp{yytvun}xxndb`\Z]fhbaowq~~mrwngeccg`^q}vzz|nqtvvnyvhgeb_]\_`STmqiruhbqssznww{zxsqng^W^j\_smutllpthbp}}o{|uqvwjj}zswtmkinzxw{wvof][_YUcu~wsonpnekm^ds}}ywtjhsturmwzusw|su}zxy|{|~z}wnpg[__W_p~|~~{{~}qv~}vmuxmjkhbcgcfkkww{|rrvxxztkuwqnniam{spwrku{|~tqvrz{qllkf_[[`_Uc|}sv{swv}{umiinibpvlhfedbfsl\mz{qy}pihkphdsryup|znihnj]`nuns~qnmliffi`Yev{wyyvutpg^jrbeyx}wv}xsojd]Zakhmxkjrlcgh\^luzyvsuxos~zwv|zv~ztronv}wy||xmgc]XX^]_q~zwwwvqmow~~zqnxxqwwnosqsywy~wsqnllmornn~seegcffnuyzussstwpl|ztmnysihhe_\dnmm{wxy|~vu}zxricbkmdu|uyukgkrmjs|w{~pxnmmg`[\fhfu}~|tsqywo{{lptogdiux{}snkiiffqxmu}xxnflj\W^hgj{vr{vjd`_b]Vdvxsjhhgdejegv}{~{yvsokgky|~yyusvld_ZTPOT`jxxnrwponju}zwtrvzxwwuqljioxx}wsrpor{}|ypuma[Y[dklz~{wtrrurep|zwskclskfmrgcls}zxvtrpqwvp~|xqnkaUYgliq~tz{yvvy~ywynq}wlihe`\`rzp~stztmmsvmq~ryvuqkhmy{w{rmlrsilzzs}vlyshfea\[bkjozxz{qs|{uifkhdfpvw|smhedddhnpu~|tlnoffoop~}}rryrhc`\[a`bntwooonpoflz~~{}xwtojijjhq}z}xw~zt{yokgaZWW_cbs~xonwwoeix}yqoqqwwrnnryvuyvtsst~wp~kflfXSU\_Zatslwmt{rjiopd`lwup|{uutsrswtkq}vspkbZdrhdu~w|}{}xr}mlwrhdca]Z[fpkj}{~y||rsvx~vrolhehomjq~{vqryyqvxs{z|}|tkifcbfjdft~{wuvzwmpwqooeaegipspz~yusqqspjs~|~xvoeekiifdo|zwt|ymgea[Zahebm|yommopf`lw}xu}{{ytrtyuit|y}wu~z{{qsgec^XVYa`]l{svzilz|rn|vjotqlkqwsp|}uqooons~}nu{miok_\cidfu~yorzopyukggjmfanxswvtuutvwomy|~}tolhd_Z`kijxww{~zz~vokhda`cjnsyyry~yxums}zyzwtrolhefksst}xtqsy|wrs~|y~wmpmd][\ageftxw{pnvsihldY]enuty{wsqonpsml~|~yutod_jtrlqy~}||}{tgjrk`[YWSS\nsly~{srwxpp|znzvsrplkoyzqxyttz{ss~zlstd^]ZVW]hkm{~{yrnw{qr}|zuliu|ymflomow{vrqqpt{~}~yx|qh^\b`\dmnu~~~~}|vmpwqiecadihlx|}slklmrsmq~|{x~yqolighlpu}sv||yuy}wlgeb^\_gpr|zrpz|sq|~zrlt|nehieaagryy}vromnv~qy}ymqtjaahrtr|~}||~rx}wlfnpe_afaW]n{|~wtrporwrnzyyvpnle]_qyprwv|~~{lpvmc_]ZWXar{v|}v{}vswuou{py{trpmlnv~|z}vqpu{vs{|||plpqk`YVTTYciis|}~}tnvzwuoffknt||}ytponorvtwxx|rle\Zbegkjr~z{zqkrvmfcb_`hppouvstwztnvwqw~smkiedhnnhtxu}||wrvjgfb_^dkjix}yrnwuho{~{tjl||ibgjgeirxvy}zxwxyp~~qhmpgadllhn|tq}zojqrh`^ad_Xbpqq~{vutsuwojs~ywuqlhnyvs|wv}z{~tsuld`]ZWX]fmos|~z~|uns}~{|zxvsqmkmswrrx}~wspqw}{vrv||}spph`[[_fhdjy{r{wqx|tqtodcksyws||wtrqqsupjvyw~tlidYU`jiaao}~vx{qkigcckwuioz|yssvulju~vkwzplkheekqnemxzz}yo{ykec`]^djgeq{{{yvtzzwn`bjhd[Xet|vxylkmou}~|z~yuw}~ec{wvw}gmwk_ahddddllr|wxm{|zytmsx{~{wurruxsvsr{zvt{}|n[r}~wwqwycurglnttlnz~uzpolry}|Zgwxjhmmmjohkvnyx}umysh~rlx~tjppsgfpnn`irr~zu}|v|~vmutsoqt~}zyk`s{ec]\TWR[xwkqo{xxyvgszyxvyqqszzzp}{~s{rx}y|ulrrdrut}pvzyxwr}{qnygejkmtsfY\cakkdnoqziirwtlqvsmby~nnxln{oulr|srrafeUntzuyytyybgieoo|}zefmaa`X`WYg_^jaPl~mu|ryxzpy}}qlit|z|{r|lzxtvssx~{yyx|w{nuov{~{ssdmuip}uxo_wyxnbcqks|{}r~skz|svttvgxohma`}xq|{}is~pxzx}wruors^Zpqw~~{xhgcZdhda`rqtjgnghkjvmzwqyjWwnolt}wsyrgv|uotwsyvw{vuzy~{zpaT^fchnup~piso\ozfkh]lnj|{p~qtortkt~{~v{lhwrjv~{lplkj}{pzt{vx}txkeooa|z~hc|xovuinzpvifjbhomuq}v}zxwymhxngqq]pnf]\uvnt{x|~v|}~~~|z{nYnkbinn~ztblg`_]khbq{ph|upxzlzr}|ywq}ysmippdkx}xoupx{lrkps}|y|wslngtyxsumdc^npdq|xeZtynu}}z}x{|pwlqulqlx|t~xocskdlyr{pt|{wuxtq}h]a[kwtuixlfjjpgfurpb`odgakwrys~tonpkwz}yzhr{z}wzzqv{yz~~yy~vu{|fchY``lrw~ryx{jkvmnyvaq~kgci~{rm~psqwmvwyuvumuz{twskrszz{|~zx~s}vvqvzwye_mssmwuvvsvqtnbtm]ddo{t{v{m}~tuwou{{rlcduydwy{ssvwpvsty}z|}{n|tmh]bfqocwxr|qopsvrym_qyzt}~~wq||rnrowspurm|{oknyvm~|ux~u~vnv{xy|kmmz\\dfnk|xqtxdaanqlxvcsvs}|~wyuls{wdpwyls{uieflglu~|uwrx}vx}{twy|fff^aeu~}ormnqgmpab_dkr{{yruywrujtuqsalorpfzyyx}t~t|y{v|mhlis{wljrjrv}zpzqwjfz|viictlisi||y||usvsqf}yxtlgclonxustqs|vtyn~mlmm{lkXP``mwz|mpqvpvrnxzq`[z{u}rostt{|yxyqotya^\qt~v|{nvz||xutxuo^l|}qTWer{wspv{kicjwgdoxpxozoymgkfluz{ysgfepvusvvzq{~|suk{{plpwnae^b]l}u|z|}otntoss_edl~~~x~nquuwpuhkha^lxz~vvz{q~}zvvywtnpzhgf[mc^_mz|toompw~tqrkoqty{~zwv}nu}ti{~}mqmgomrjw{ypthxz~rpzyytstypqumol`Vgocim}{ut}t{{kdgopz|{wrsfy}mjm}pi~~a^wy~vry|~|t|thjxu|yyuvigjlmrh]^npjw}tqmzfotwx{wt||uxxmuxjjfgzl_eogq|{z|}x~}{yxxyx}{yq}zkrtqng^Vbgpp}{tpisvx|sdq{w}yv{utuzlqvtoyp\a`gtqu|yz}uu{~|~{rjoum{~zik|qig_mqlyw{}qstrq`ao|oxz~vuzqsrtpqyuurmbkrol~t|{|ryxwztm~zz_mxuhd^jocs}p|vjjkc|lqrx}|{~z~|psxiru{vcerpqmy}|v~n}su|vzi~|}}rtxogmppbXebdmlxz{zt{|oos{urv{wnfxyququxm{q[bqhc~tzv}{}tq~wylmwp\rwbicSca]ywz}{zuucfzww|||stlpfuxgy~ssg|nfrxrvyryu~un{wv|hptuneslhh\acdu|~~xyzipnebzvr~owsutlrztsnuib^ewwz{tzo{q|h{tuypmcnnfloskQbicm}wyu}vmpmwxuu{~zmn{khqp`v}\Y_aw{wy}|~vxtvvwwx{onobf]gn_k|po^Pr|u~|s|z~ytdyuzzzyox{{drrtwtmti^mny}|txur~n}~hnruz~ptyhnihh`grjaTfmqt{w{rsm{wxzqkx~xvkqwnkesvmkekphl~ozwx}poppp\hla`l[Nilakn~zx}}w~xpnou}qvobx{dc^fwsgoss{mv|}s}{y}yntxz~{vxxvwju~ttaZrsoreXauo|xrwro^pysvyy~zpm{mmnnvkbgfqn|{x~nuxmnpnpx~zzmgpjkadmgkeic[gpz~|o~wsr|{qyzmnopndwweYc_hx~~u~xsgux}|s{xlbh^[qj\^Zdd\_x~{{xsu|uy~~ttwogu{pjy|wufo|nptsp~yx{|w|mhtqs~}yloyxsj`kmgmcb][kox}x|tsztgt{wu~yztxqgolnnn{mhcoyrv}xr{usrzu}s|rskprdcill[aca^iipz|rxyr{~~{lxypefcc|}g[Ujwsprwy|xsvx}v||s~ke^jrh_iyuoWTmvz~|{~~lt|ttxvxzvnjddd_ffiojnqrxwx{z{ywwx|{|olvwyzzk`ggc_[XN_[aui`\i~~xy{utwyyzs|p{pu|fsz|{so~zvyz}t[_b[dfn_juo{zrexxlcWS_}}guyuu}x}jxziz~lkxr~ivtuutteg|naef{z~qqsu|utp{wr{olnk~w{udx|rwszo{yplyztr~~tkUkqxuurvjvaXbZccfTW^in\rwzxvmntvqf}yxvx|}|abwniwvpqqqyv~yw}pqwxzuky}h|}uy_knlptogot{xpy{r{yqww~~suwzjuxgh^Odbehlc`_nfrwpwuegkrs[vuwumn{|ttnlw{|shktqdxph~xym~zroilvmpz~|}tpprv~zp~|xys||vsuy{trk|qnxrpXhdffdbZVbx\ekfpkcdlps^w}||u~kj}u~~u{ssnegsxlzv~tuyv{gyzt{|~u}iiuovqtoig{hxy~s{|lgktpi|lp~v}mlfRaYb]]_XRhsXmms|ttx}~xby}|txzopoam{~t{{tpqsx|y}{qtmq|~sswxy|ymv^ihosozup{v}vy|yz}|lyop|rzp{|wtxfq\`egneplbfgaqi{ooqvtwbgvovsp}w}rzntvz{xujsuh||tv{~vzvuvo~r}{y{vtovus}sqylwwvx}vu}pju}ki{kwy~s{odieoanna^vfUk`tkhrttxjgv~{|txmoyvouywxjs}s~tsmzmkziuxwwlppyk{znlqf}s|~~n|ticrwbcsfw~r|ocedk\ml`^vcYrg~rr}{}mj{szyv{wsqqgksq~ut{vxy|p{yoiymj|r{~{v}yimmyj{ws~k|y}xwyouwogumevpt{xy|ilivgt|pfyrYrkxuh|ywvma|{lruutym{spoyy{{}ypuu~vxz|{zpxxgtvn~{~|~~xvruxs}gx~}tsx}~tsif~v_frg{y~|pkurgzvjhx^]qg{em|wvvhoyw|{}ytvpjqws}w~ozzr}~{~}y|zpgzjdqkpy}sor}kyxnyb|yv||{||xunywj_c}p]goj|wjgvlfvsimzaewrlw{xltxwxzwwtppifnuv{uvy{~zv}{tuujd{mjvrx~~}oir~my~vkvy}|zz|xvwq|sotzlce|glto~mjzwkz{ro|jbwqpl~xtklxuwwxtyp|nlr}~~x~|z~wy|xrz{{{w~ogswjstp}{~}{nttuv~kusyzz}}yx{|}utsoyj`_z~gipmw|phywhtxqlxj_tr}qi{vqr|}{|v{p}jhoz}{~vzzz|z~u}|{zxszwhaotiopo~xiqor}}t|~kww~|tvv{vyoxmmjsc[_|zhlqq~}kh|qjvzst|jj}{rr{upvz{xyvsvotffq{~yyzxt||u~qvoyoed|spttw}si~~q{|zpw|~~}wt|{o||ussmqlrpa^ptmqsr|l}qv}}u}vgu}~zky|spp{|zzwzszu}oio{}{~v{~{o{|}yxyvxxhdq{ooro{~|m{tw}z|nyt~|}|rvx~~qyx{rroomq`[fwiknm|{ozsty}vzvhq~~{nuwtu{~wzxzpejv{~~|~xw~s|}xvvusvd`lymlom|z~mzruzzznzyxlq}{pyvxqlmljk[Ygujjnn~p}svzz~xmw}svvtv~}}}z{txvxobhsx~}}xrxz~vtruptdblroqp{zyq{vx|sz{z~|}~}mq|y}oq~txprjqjo`\e}nklnuz|~|{|}pt{wpvst{||}{|wwzy{ggrx}}|{xyuq}zp|rwjcgtnlpo{}z{v~xzus}v~z{sku|vzn{|zywhqlmg[\nskgmj~zzy|}vpzyvsww{~|wzz{rbhrvz|}~|w~sy}yurwqrb`j~qkmntw~{z|wxw}sx~w}~~}mhsy~~xvnzwxtvxdohkaY]rsljpm|{|yv|zuww}||}yvwxy}qbjsx{x~qw{~xrpuqpbbmunppvx|{~~z|yvz|v~|z{{{|pit{s~uit}vvtsyfnlle[_qxpkqmz}}zu|x}pvvz{}}~{xxy|~{hity|zwts|{{nwrrfah}womqm}~{~}}}{xuw||zuz{||{~~ugqx||r{~yjs|yvwrxhjlkeZ]lzqjpmzy|{zv{wryy}}~}{www{zzhhsw{~~xvt|z|xlspoc`g|xonqo~}zzw~vy{|w{}~xzw|{}rfpv{{suirz~uttprehjjaZ_pzrmsn|{{zqxyz|{~vwtxzyxgluz|z|qv~{~x{rnrsmbdpwosovxy}{~y{w|us|yz}{||{xxv~y}mjtx{zr|}lkt|zswrrngkmi^]g{wnppq}~~z~vvxyy||{||wyu~y}phrwz}vvs|||zxpqtrebj|zqopm|zzx~|x}uyu~ys{z}~|}~w{t}xzqfoswxr~rks{|twupohglk^[bt~womol|}~z~z~v~||}~w{s}xzqfptwxzzt{}|wvqntqeanz}t{zjswtmqznxrnxy}veb^qz}xz~}xu}xd[d~njhezpive^eoutk{}nic\ixt~{u{yjlluuwrzm]a~mgawum}sjs|xosuvx~uy{n}{j^U]vpkrripgsyvpx~~u{{h`hztjzzlyrdju{{r|hio|wxrpyj||x}~|zxxggkrzyw_PWrqsu_deST_gjbfus~yvrzzkinq|wz~o}nwx|~yqp_V[cm~vy|yzln|{ruuf^kqpwnfsjjntmjmsmkpdqhd`TY^dp|x}z~{{kpo\Zgxr|vgmx~}ux}yvwdbRPXdr}yqwwsfjovstvrjqicqdY^n}xlsqqzkgs{~xyqx{khswu~wsz{}v{wtkpu{|vuaghWYjrvq]ajnldorr~}wv}omzuelnvxwzyq|ox|x}|}xvqygg]Zcmx{x{~t}quy}hluiev{vuthrnlpvpkmsojsclk_cZ]djr|~~y}|qxhdrcanwww}nkyz|||}vgp_ZYVao{{u}{wyjjmssstshkn\ll^bpzmoxpyugq||y~|~lm|rl{}|{~u~}}~}w~qvpszuho^Zk`bq~yuyhbnrrjqup|xqxqfuxgostyvx}s{tw}z~}{~t{sgsb`a`it}z|you{yvusbiwko~z{wpuqqvyqlnsjlp`mg]b_cin{zzx{~~|pmt^irek{}y{|lp|}{~||szfgiW[\`lw}w~rkmpsrtslesbbsecl}tn~}tszlkx}|w~x|rizwky{{y|}}{yyrtuty~~zqdo`Xmdcpz}ofqusmwup}xqwsgwxfoos{xz|uyvxz{}{}s}pisb_bbgoy|zwujyumw{}v}~xzy}}szrgo{rtsnouvp}tqvw~||zyu~~tvvxvvtncqh_wpip}}l_pz^Ufji}~}z}{r|nhtedkjlntptughkprwxvozt|~~wzpvux~{}x}o`awra[^\Xg`]ndennnnot~|{|z|{vlytf~yquvfgim{}vkz}}|w|y}~z{y~{zubmz}snhfdaWdgWnpefox|}yqfsvnfjoixwz{u}x{|xzogkw{xvlvuhwuktwuut|wx{ma]hhw}~}xpkrmj}pjmdlwzsmmprnhisy|z{rv|kqwhkrppp|pn}}zxyxqnrs~y~||~v}~{vsldan{tmvfama\eec`dz|u}xwx|tjx~|{xxwxxtkywh}~uws~{pfgpm}y{{|x{}y~||yvgl|xuqggtafwmhluy|zteiunhalkl{~wx}w|~vx~vz~ywurlhwvuro|rnyzwtz~z{luqg`cqs~{spwpp}oomdnvxpkjmmmfjz{~{yxpxzjrwilvtrq||pr~{zzzsrvt}{{~w~ypljjiewxq{pdok^glgddw}u{yttwvlu}{yvutuvums|kuzymtwokiy{||y}}|x}y{uruoo}ywunhvmdvvnnux{|vgdrmjdjtpw~||zs}xxy{yvysooqsl|zuzo{{ow{wx~zvdlnhhh{ztyzr}urqfktwpjghkpkj~~|xvnw|lnxljwwsq~}yx{vv|{{{uuyux{yzx{pigglnkxxpjtmblojek}xu|vsswtm|~}xwusrtuqmyxl|{|skwroko}~~x|{}zyty|vv|{tqsuo|}{wouxiq{tqux{}zmcookhdrrrz||r|zv{|zrzyrnnpvmt~w}~~{vpmls|qt|wy{zzvi]jmjjfu|{twpzz~{qv}ytrtw}sp|yul_foorqb_mlgdryovwrruyst~zowqdw|~~{npuuohegktmmquzvholffv}u|~zyz|tsvzxupjfehn|vt~{wy}zp}}|vsluroi`gqvtrrvzwvzr~{~v}ypm{}tzzutvystxplnvyxz|~}rivxrpgotqw|~qvywwx}v|~l_krvvmwyovxtstwqmr}{xtnlmoq|~}~~~shswrqiq{y{{|yy{ppuxtomnqypg}{~ulx}v~~}yofaafnzksxpndfplnuxrrxvutu{lkqurnkloxtg}wjs{v}v|y{|yy{ypzvy}|ytqqst{~vkonnqx{}{tolmstbl}uw{sgjwpmzvu}vmp}wt|zwvyzsvz}uomnqwtp|yuqhpvrv{v|~}}~{pb^nrr}|xxxrlz|mx}uusoihhikv~}}}~tjtyrsjlyww}xwzyy{zvz}zuqqt{xgy|xnwz{zume_`dlzrq{tqlbkpkmrzukrttttw}}sjmstpmlnt|mmxlk{|{|{}}zuv||zvqqruz~{{{{~ifklqx~|upopwucpvv~|ulbkvpoztrytms|tt}|yy{}wyz{yunjkpwvs|wvlq{vuxsy~x}sj_\q|}~{z{yqwrszronjdcfjow}}}||~~xlr|vvrlx}vwztnux}wy}|xusv}otxnuvmdYY^biszxots{rluplpw{nu|~qosg~vt|~}x}ln}~usqjpyklrkknnnpuy{zvnimmiu{ttyzyvxyspqxxej~xt|}w|llo~}|{xtrtx{|zrs}xuskq}{zrhq~mghihghlu}pl||tikxskt{z|u~|z{~|w{}uldbfv|{xyx}}lnu~zofdj|ygdjgfrsmw~}{{}|uw}ommcaejosxwxxxxuuuvwofquooh`ij^]g{}~|{}{s||zyv|tmrxt|v|uka]huphihpwxyy{{sjswolgan{xx~{g`smWVktlmsy{yvvz|u}~ulk{}{xohw}st~~mlpnmqzyn{|nijnprw{pZZbj~yc`ZU]a]k}}||}}xutw{zwz}jqrkq~nu~w|znfrytu}wnv|~ypcXSVbv|yohsxrsohpyrouttxwvuv|}s`RY``lwz}|}z~{}vttpidbfoxwze]^_eo}zyzyvtswoqwx|{wridszyz}~vshdfijknqwyqy|ywllvpgixyy}{{~~~n^bnmr~}yprutyxvwuoifgks|wwxqoij|qpnjikrvsuuspnnu|tn~uhiwvsx{wrkb^^equq~}~xnsyojp}xlostttw}t|legkpv|~~|uk\Ubheky}~}y}ypzx|{yxrqssollpv}tmooory|yyqkzyw{}{ui\ajd_akqjly|z|yxqjif`YUV[fqw~tu~xxrkt|tqz~swyyy{{n]Xfos{stspj`\[XSQT^k{ys||ywmkushfoo_Z`diovvuwxxww{yr|{t|zqpokfcgp~yu}yojbWYc`\gyyoquvvvx{~o{tnpsutssw}yjjzyx{zuk\Tanns{~z|~~yuvzw|~ndgkpwwsqmjimwyqzyxxvpeX[jlikqytjmrv{slu~zyzxoeb`]WV[ft~{{z~|~}{xri\Vftvz~uoqqlb^^^ZVW\dnqnwzrw}u|}xwohotkemrd^cglsz|~sy|nb]XSMMTbt{z|tpj`bke]bkbW[_bflry}q~tprtuvvx~|pswssqkbW[o{~yxyxvw|slrna_gdY]gpy}{wtrtytealrkfillh]Yfplknuzqlsyuyxrvzulijjecemy{vws~|zvnaZguwz}~zwxwqlovqe``_[XX]fpsr~|}yt}~~ysyvqxxkglptyypz}mda^YUV\hwzxtlcgngbjnb_dhikosyvu~ww{~w~~||zsg_jwwx~{vvvrmknt|~|}rtyoglk]X\_aekt}{|}oipzxqpqpj_Wajgeis~x}z|rlkje`_cmyzrv}wrz{omruwz|tx}rmkg_SLWdfirxnnwtgabca__cktwu~zqs{vovxmjostvzyqu~}{t}~rowvg_^^[VTV^jrt|xrhirrmtyojoqqqrty~vy}xladppmpx~||}{wssw~~~{rmstkindZ\`acfluzx|zzxpddormloyy~x}|nihfb]\`iqquz~}vv{|{z{~xru}|snlibVQ\edelyxwrkkliecekrsp|~ypnuumoqe_beeehnv}}|rhkvvrrwwmszn_[[YUSU]itwxpt{usxsikpqrsvz~}~sorz{rmmke[Zhrrtzwvslmonllov~|{ukgmnghj`\bgjmrzyw}}{{ypfhtupoqz~v{y~|tkrxkhiigdelv~|wpr||y~|qqwyz{}yss{yqnlg]Wborsy~wyljjhebdjrupu~|v|}vpu{usvmehmpru{|y}}}{tjkx|yxz|oo{ud\\][Z]fr~|spwwrtqfejlmnpsw}~|}wv|}tqoi_Zdpqptzztznmonlknu~}|wogjojij`Z`fkpv~yuwwrpme\`nrpqu}~xlp{sc\]][[^gt}}~yqr{{y|vnqx|~upouyrkigbZ\kvvy~~qo|}nfhjjijox}z~{zvqielolmja`glorw}zz{qmw|||ohtyj^[\\Z\bmy}}~wsy}}|qntwxxy|~~}|{z|wrqu}|tomh_Zdpqpsyxsprqonqy}{vnfhnkig\X^cgjou}~{vmekutpnpxv{~tjr|qc^__]]`isxv~|uxuqv{||}wpmmtumfc_VPYehhlsx~okkjgeflssov{~yunhjqqpmb_diklou{~ypjr{zvux~xlbjsgXTUUUV[equv}uw}}|xmjostssux|~ysrsz~wqnjb\dopmot~w~tprrpnpv||vz{zsohaailki`\bhkmot{{wnfktsmklt~{|wlqzjddda`bhptqw~xwvptyyxvwy{~}}zsrqv}yqmiaY]iljjnx|{yommkhhlrtoo|~{vokpwwulcekmnnpuz}ysihquqlloy{~qitzm`\\[YY^fnnn{~wyzomrvusstvz{wuu}{toja]enmjjoztzqkkkigiouvqv{vphdjonj^X]befhmsz|xpgjtvqoot~~}ply}ocaa`_`fntrs|vps{~wmnuyzz{}~tomkqvphe`WV`jjikq|zwpppnlnsyxsv|wslhnuwrgaeknnorv{}|umoz}xutw|uibmti\XYYXZ`jrrs{z~rouyyxxz|~{vurv~{rlg_Zbmnllow|{|rqqonotz|wv{~~vqle_aimj_WZbgjmqx~{tkkuzvsrt|{phoxpb]\\\]clsts|xu}|pmswxxxz|~}|}|rlkgksrjd_XU_knnpu~zxshchgksijpw~{v~{ozlkh_grstlnqmmrvrpvw{qlkr}yovspox{~vsyytuxulpuwyiwlfhhw~~~ww}yy{}xsr}slhcb^jstslprjimsnu}yxxyzxz|yt|z{vv~u|s[_yrju}}wzymmnsov~~|xyx~mhmllgm}|kryytoxz|xrvqggkqt{w~~x|{}}unqtuh[q~||wv|qebfhgsst|vsuwunrxkamqrmenyxy~{tym~zuz{|vvzkefryxqvx|tg^_eod`{nfbigmoinnfglwupr~voimwyuiept}u~zwwtq~rtuwtt~vhfn|}w~qs}wh`[ln]q|~}y}uibjmlmbbg`^`qzxoyyzyxqx|{pflp|r|}wx~j}ztquvtqy}lggzyyyp}wm_^pd_{}{wwqgdpkkb[`a\X_qyvkyzz~ws{|ohnqwwv~um~y|quyvpp|vhep~vzpxzr]cl\jz}~xtx}odjoieZ\caZS^n{rnwu{rt}ylhjst}sotu~w|}upsrifx}vv{p}zn\hj[sx|}vszxndnoj`W\caYTbnzlrts|rv~vihjy~s}~~xplyt~zxsxohh~|||wyr~xjZjg]yy~wvypfqpk`Y^ecYTcnxhtus{~sxvhgiz}t}xytnn|v}{ux~nih~{}}xwq~}wiXhf^|~u|}wv~zqfpqmb\`fdZTdmugtvty}~sxvfdgyu{}vvqkp}y{{yz~pjh}}x|yyq|}vkYgjaz~rx{tty|shnsnd\`fh^Tajrhmzuy}xxykcfsuwytoodyu|||ytlhvvy~zqw~tp[cnevqt{trvvkjsoh^afkdV^hnjgwx{|w}pcdnwuw|rjlbs|}~zugmz~{|qan}svv|xrvy{qg]aolwwp{~{ocswc^n|icfy}v|vrmitxtlpx{fdgdkxogxruojl|~|phft{vrp~|khvf`^\mymupmgluzwwxmhYUhxrvhu{ur|~tc\h{oa]ejrvvgj^mnl{|n}tjivppkr|qeidbn{uv}qisyzijiea_]Zpsd{iXymxlpto`tzqplfxyykzpceqy|wnt|xpht}iflvzxhpbcV`rzyxwz}tzimxmrmelz|tl}is{eytl_Z^n|tme^euuxr~vrxts|~~|pjym}|plusep{~nlofkvvyx{gXV`fizqkfacjqgj{{u}op]b|jinzwjpj_bzq{tvwsxx{ysx{zptqf`^gwqh^X_nmn~ruoltvwwre\wkunaxpo}mdbh~ypbr|qqsloywsv}ja_lpvyytu~{|~{fmnlp{qe|fdW`qw|zyysyv|pv~pxtfly}ukyyj~wgumlb_\f}{zpgbhvsrz~quxqz~tizr{{olnvfnx}likedoskxse^fknzyopjgovrk|{fsx_^}lflwxh~sje^|s{{|{uysx|zy|~vuz}s{vrkeaasvtna]^lolxwzs~poy{xp`oumzewprwqzgdbotmbxxnokimzrp{ngfqr}~z|u{}z|uar{hinz|vggu`dUkqs|~|{wwoqwuinuzqo{vq~wjumoge_hxwzphfjvvx{tqt{ouviz}wz}mkhz{vhfurzmfda`hxlsrhjoqz}srrkrw{r|hv||jYqtcio}|winwej]zvx{xxvv|ysv}}tw|{~qzsskg`gr{xnqfa_equ{xuvy{lq|xnaupqtn~qzttvfdbtwwiewpzskgfgntvsmmrt~|z}u{~z}v`qvcfjtuncbo^aYmzx||zwqrrwnlmuss}{y}zlsqrnjcfqy}stoiiiu}v~h}zusu|ur~wgyquuoucouurYT_elpzyu{xrkvjmjg}zts{vbrxomorcqtmi`x_[ru}zx|xthwn}nonkorlf]lnZZaqhp{u~x}psjyxphxxen{}oykmxwruutl{xrkgfclkrxt}zrrrwzvlyvvzhrmmkp^aqnc^ck[et{}y{{}fv}nrtluy}tuzzvxiqqgmfueccohn{zt||zwk~ws|em}xtsttjtudcrtr``^Xkhri~zw|ymoxomutnytz{rwuzkndimqpbltd]q{{}qz}y}stqzv|}{nesxgdgtyhgW][iryxnqp~xzxknv~~voxm{q}x`iwrisqbis}zop}xuwt|zxvoxvs||rppp`_i`n`nhUYjztv~uttmqs|spz~{yiltqk^n}pkih]ltr{p{zozz}vw~nl|~|||rut{n`nltckjaWVnyohty~{|wunn}~~qu{||yquyymerukrqyhahkym~{t|w{tzu}x|gjoeocsqjllgZWlrkqr~|qw}}xpr||t{||wnxsfly}msrdlfyukmyr}{nl}|xmh~zz}zoznxjemigalrdWZgqhsz~|}wyhhzpuxhqwqox}p{luf`prrjs}~|s}w~trwrr~~z||xruulnrlbk~obamveof~|wkwy}{wxyzqsslbqomlrxmjnkgr~uv}~vu{xu{||jksmbXjwpfff\N]ghjj|z~vpv||zz~zzyexnvll{ugUczxjgntww}{~}oovyxw|xvpryvlcjheipqXf_adaw{{v}x{tz}r|uvmmznrpopxxm^dmntwnz|{to~|yt~z{qmzx|rtxneflmijlbnc`ffgn{|pz{{}wypqqklizbior{~t{~yq{t|{v|d}te[auk`^i`PYnji\yrv{~mw~{qwnwqxrvxcZhht}tgqvpysffksj|p{hy~zvwwqfq~{tnnuuxhjvqiijqtyx~xstwkj~}ustxttqlv}q}{j||}{y}YeY]oi`}y~}pzzu|}wn||q{zwz{~mgk[|dbpgV[iehfSn`nhwodlyszt~ojlevxnpvhh`klhw|{vnprndptw~f~{}ptpZWl~~rmvrxltxzrkhyyxy|{z|u~zw{znf^cuxtwbbupqefkxxexyy~qosv^bte`conjfi|wjkuqzx~|{qpoqffl|zdul{{}{uxw{pjW{|{|{ntsud|phky~lt~tpvwnrz{z|xrwtj]w|~~sl}xooj|vgbYi`n}raq{njs}yocqsn}v}p\Zs{}zkhhxtt~xlsqwwmmwzuttypalvom{uq|~pnx~|myut}vzz{{v{vx|~~yz|}vwdanvz|y}fljt}wiq{l\_clnfoxm`pgkdYc{ugdge]j}qx}}ykc{nv~~|}nujusr|ieq}rsktfzvwyirpslbmz{|ygvv{wphaY`Pb{x{~|tm|ypgcxsnnqtqe_x}uv|qojczlowzxojzyxqrkkw}zssr|t|wwwvuvkuyddknvw}zllu|{q{q{wzvwk|u|z{{l~xnilne\hwzz{|znrlcbn~xiikpx}pbbm|{osmg]dhmbl~vs~~ymc{~zosxsm|~qu~tu]opds{x}q~qjk|}y}zz~psylnw}touvtk\fcgywzs{mgu{hasvrh^k~pgkge^gvpsejumlkz}pnyp|q{xzw|w{xy|xznbnkkk}{{{zkax|nfjihvsx~}wwvqx||t`sQcjm_RWy|}m\d}setotoYosrtx|y_\etzokct|xopxxy{rptx|h|ipekp{{irnmeassztiw}lyn||z~syvfxqt}}~|}{ttkaclgghmecpnyypb}}}via}|r^~vkhhonm}adrhkgeadtwsnx~uwwx}k|kltqfpk_Zhu}xmwlr{z}ooq}s|z~nctyvq{r|rhae[srcab_`kvx{w~tsu{myywvr`d{}je}}wqrv{{cSmndnxq~z}vt{qu|oukfhhl~`fujj]dv}smq{z|zkns~yqqtwoq|{~w|ixgZV`mzuaZ__\avx~wt~tjw~zsfuz^w|tolqr}kaeusc_isy|vpy~qnzvtp}l`jpp{wecglqu|skxwsx|ztr}h~u~ml~qzsvwt~qoab`e{p^ekgeky~vl{ot{zob~~rcmkhonvvj`q}raYan~{{}{z}w{kbbjjuyuecujhnwxyk{qmtw~mx|gxyzspllz|{~}xy|vjfgiblqm^bgfbf|{uslxssythuscsr~s[^~vhafgx~|yyy|}}u||xuvrl_dfprmenujghsu~of}sqd|p}w{ohsqpu~}rwutieihaliofdlijiosrsuzuetvyq~tmwyd\qyj_dbonvwz~w~rwqvysuqrnv}{kdilrlv}|yo|ns~xjot}y}pen~rmps{hr{snefdfikg\efekkvywx{txld|~~}twlmunc]|ugbhhw}{|}z}}ttyw~xppmjswyifllqpy}s}rwjt~uisvwttnthrs|}hpwpmcefhll_Vacfit|xqtw}~uuupwtqn`^xj_Z]iy|kwrpusws|rc`cgt~lnks~{~}}qfxypvvmis}yxopuwvrzvsv}|~}~ttwk|cijlrsi[X`[V^\gddvxjpsoz|qll[]t{nhelmy~xzqn|ywqdz|pebgmwwebdfv{}ourltqr}knt~wvrurv|{yv|yxv|x~u~ppnili_UYkmjrq{}xws}wvr~{no{zzyzwr{b_lynkzsz{qqt{xsj|}h\_qy}s\XY\hn}zwln~{nztacpukghry~}o|wo|yqha^mqiY[ifeklxzwqmwwhmxqqmx|qqzjjs|}z|y}|qqo{~qnochsgpqddnnqf[bemps~|mnuv~qqyrvylqyztuy~~|uystsxu}oyx~|pq{rojfja[Wjursv|~v}vxw{qs}ysy|xybgttz}~ytupzwol}xxi[[t~p[VY]cev~woq~~muzr`fmplhir|}kzll|{oa\Zmsn^\baelsywtk{qvmvwpon|n{tjjx{z{|~|z~h|xwopfcqhgsxnbkpsncbhnqq|zy}mqt|ultyzj~sqrrrtz}y~wuuvrsw}sptuz{ll}wqllfkd_\grrru{~zw{~z|z~u}|ehtzuv{}n}}ry|xjizwzn^Zpu^UV]_^nywt~kwovghljkjipjxxsju|ucYVgvsh\]]dkuxsqfnx{tvxtqm{xqngq{wx{{~}rnu|rtlbpmfkx}tlepstiffnspw~n{xzoqxsgv{sjssmlruwv}vqsvmuums|rnwvfpxpniiflc``joqqszyzxuzr||hlwtry~l|~vw|wffxs}xukX[eghhmipzop}uyz|u{z}vnl^giW_kuwhtqbjww|yyjmpgp}z|tlq{uuyocW`ku{iqr}qg^\imxcSbimxtwf`}xipyv{t}lsmgzvpcZV^k|~ocegx{}rrn}uhjokq|eq{yyzj`bgZ[lvy{{pjulils~wuqkzv}nwy{}mbcisvrbqyrnfd|t}~{||{j]]\`huwl|zoenmmyqrpwmfrz}|saky|pvkjpkioiiUVswpov|i|p|y`f{wzv}xu{|{~~xddprww}zpzoZWbiuslbPhyupuzw}huxhpxxz}ywwxventh|rk\W_pyxghkzxoujozu~rj{|krwmhr~{zkgvewytyeY]gwtrnptlm^py|{|k`giz~|wbtxtljp|xyv}z{l`bdku}u{onst{rtyeo{|z{|~{bYisxlqmfdhjpqh^Rk}~w{nqvi`w|}||yxx~~|{h^kqw~yoqvweX]kzseVk~wy{}|dixfr|}y{yvwtscpmewwg`^l}}sffjxz}siqxgw~{|ll~n{zou|{irqdw~j_bcrzpnkulmgauxwttyhgjo}}{pjfuunhjx}sz}v{rqklgnyzytuw}yuzrk|vtwkZdox}qqlqibchpxn\Rh{~xywqytcw~x{{vz|}||~peovzxtjwx}n^]gw{f\q}|yfd}~kuxttrsppbqg^}xwodivxnik~t~}xwlm{tiz~olswupy|{|mwqbzsiidjxwrlv}olpcuzwqimsqwwyvjrevwpignxp}w{z}~g{runlt|}|~}y}~ws|~ro{xfbov~ynyisqhcfhw{fT_v}}x~q|wzrlvru|vy~v~yt{w}oejtxwr~uy{yuhtwhoqt~rjwrwuceqjuo\iyy~~~z}yn^wsrmqxkuswovtry}ryinXU]`ijozwz~|x|_xylis}xxv~zia~{xqklxiw}wwnu|ytzyv}zqgq}lqmchy}sjWXUr{|vnpzswqzvfuj[lpbs~wmnow|rxywakpuqt~tot{}}wwzu~}|rlbfb`t|{k`bj[Xlx{s[amaqez}umgoxq|oszu|x{|rmo_eqkxzz|yzvu~zsupnx~mgZgq~~qu}n}gulW[kqsk`nzvmlgjtq{tuyxon|ns|~ssxxogho~{yuzzwxt~vy|jafi\c|{~kvsrwsfs{rmnemmjwlpbhpz~osyvz}sy}jfktzxhjrolw}}vtvy|z|x}||zpptrg`|yupvxrddz}{|v{fcb`pgz~ynb`nyut{p\iot}|~stnyrq{owo|y}{p}wux{tl|vrlymvdgshpssrv|ovejirpdj|spuzvjjysqifltys|zvtukywwnfiukwo}|~|zt|rw||sllkdXWqttriqoecnzzlXaadshxqjt|ny{mgkn{rswvwolvggq{vyy{~ww|v}y|~yh[ZV_]aymzzx{nt|ln_mxpkefd^\u}jvl}|zxx|khlctmvyvyz|w~v~rky|uz|ryblhaov{sxpmil{}rownec_bc_mwvqgasx}~xpn|rnijhaaww~p|}|xz}owpsz|vk^\rrf^i|x~}rfh|{zhhcaufmvxwmls|}vozwvryrskc_dkzs||}so|||{zw|z~ukmtsjZZZ]}|xyw|mhqyjbqqi[Sdyxoluxvpx{|oxwn|kr^ktj~z{yvwyoloaR[[acjzzsmzqfbwgt~~fiwou}|~rinrxu{{}xu}{y|yo{}jn}zyvziwiafekkpyvpvriVk|~vxwtiuqr}~zinmr{ywnsnuvyiko{xmfimelsbqi}x}yx}~xqx|{xqihzoiWJ\ql]bZUsqe`}v{{ks}oqzwux{jouhibn}yktzbr}qhrtw~rtx~jcjqt{wj{sbm{edfbUstu{}{u~gijgpxynsyppku{~kvyw}}yyxhTd}w}sstz~zx|o^pp`^onxx_twa`lsm]hsm||yvfhp}s{u|hkyx}qsznqyykqqWgzxqt|ps|zvpsxp|}xslxk^vvnhyp]cc`ioqz~qtdohcacsv~{}zitqz|la~}r{{vywkkx|xt{x|{|xy}qwtt}y~tZ\|z}|zqshbgrpnnh|x|ohlxslrqvwzqvnj|in{mm}zruf~xccs}}~zvzyl{z~}nzw~qsj`]urqbkkcc]hpshouuwv`ouieq}ujpqlfzvv}z}onmyhox|~zyko{zzxyyiz~}qlmhckluulnvompppyjizpjo~vabzk\gvq}}tjjrt{||z{ttjen{}{x~~tl{y~wqvoyyxja_\y}|~odzskdpm_^ojj\eqmet{u|xm~|u~|{|yxjxoljk}h^vt_^gbm~~x|uyyzoyrldly|zlzyje~{fbhak}{tod|shop{t}ukuqprpgmuuqtmz|kndsuihpnsmx{yibYazyxytuluypw}~~oh~vltpyxpwsswvnv}yvj]^rqhZZw{jhmhs|v|xmy{{qnlguvq`\wuv}{ixthttx{~}~u{wob]`\k[inYSYXcyyuw{d~|tptqbejqnmszr|ruoszsejt{bcu~|Uvaczzyuqqx~nywsh`cu|~l[eyrfa_wupyUv^^be^`mxyj~wvprx~zejtuw}p|ys|{g{ijqxqpnxl~yusq~|xswy|qh~yzh]wg`^edmvm`vckodfdsoic]ux{wyuh}u}rww|jiikyoozcn}nt}{x~kqobwx~umrdimeqgbgjiS]slhqpbZvlXVZSrzuwxfz{y{z||xvuen{p}{lnsz|uvt|muztfv|pnstcdoo~~vvv{hYqohdnni]ul`fp[euutyj~|{yo{|upqutovh|z}~w{o}syrwtyjq~~rppid[hltryppnl[rygcv|dd{m\_gZ^{pfxu}|y|jbhmsmxz}wzu~nqtt{wv{qskeqzxhvxzx~nosydqsd_pzq_fwdhqf`|yw~mjur|{{qy{}{louvjx|z|zk|{vx|ywwmwxl_dnskwwuukmqt]q}shvzeev\Weaa|x}op}nm{z{s{silqroxx~y~s~qrut|ovx{tte`jsqj{{~xv}zfzwpepup^il\bj\c{~{yx}zjwzvzu{}zxnemxxt{y~s}zmprxqwqufcekgkx|xqujg|rkcyyiYozg^^[Rhyz{xg{z|{}||{zvsbemtmvy||sy|yxsoipprzork]cntkx~~xtv}dsuofoyiYZvn`bkaax}x{nn{tw}t~|u}thpyzs{{|~p{n}zpttwmy~~|v{kfkpjjox|zx|gdtlid{zk^u{e\aa\oyxxzl}uzzy}}}ziilpksr{xw~k}|ttxrtms}tsi`fkibnq|{xw~}k~wsdqxnal~laccXdwywls}}zy}|xzkemunkor~zvvxp|pstn|{otfcjnhnht~}dlxqtkqa\ssb`n\Yxjr|qe{{t}rtnpm\isygcbr~}~~lwpj}{u}r`\jtnjuv~~~atttfauzZrxlsf`^l|kwuzxxs~oqsniniezjrx~x~x{~}k|uknr|~tqup]fl`hchvz{houi_asiXelcjww|p{~w{h{ptpt]qxb`mut{|wu||~pw|zvsqknepn__rv~ni~hl{scgsekfbem{kw}xy{i{kgnhkf_myxpxvvqquu}pv|x}zzwxxwjiyoZ`insvkuywzl~shem|gpmolory~tirzqixs}~q]Z^kmiw~utv|t|usxsc}vntx|rU\Zdkeu|}z}v}}bksnkelswjbbtt~v~kvs{~ltbfekxvwy~}trxsymillt}{y~tn^ufTWaikmortsmdhw}`^d\x|nvwywwz|q~nmtqteR_wvnw}~yuz}~ot|s}{hgovwk`db[]dq|wv|ucyyjngyqccgstq~vxrv|ngltrpccmqw{ut|rgzqblwwfxunmqvX^[gwzxp|jd`gvufnkaiyvppvwtntqytiwyohVorlpx}wzzu{}s}yxrwvepp^Zb_a\Uiudo~qsy{{ck_{}wp|uyi}x}jcbkonhgstwyxrpxrwxnj|pn~ys~zynemeWXfuz|w||{wwdp}pzdiooiah{|p{u}zxig}ylfgzqjvrzu~vmrry~~s}z~vfuodhgaW^jac\rwvz{yuqyvgckw|}zzqmty}usysym`~qm]Ynsuqg|zshy|ssz~vvtcytjj[TQ]kpqs~~puuhjnqgewplh{t}~w|iqxwsrkgkbbk}~yms|v`q~vwzvv{vs~||{vwwfT^nXTk|szxnyuqjhp|izi~dwtb\wbelQdU_rxyy{yvvypswxgmz|{pxleqxnej^b~mp[c~x|q{}{wUyzquvz{~}p|xqvz}~hdWjswvaP[|~jlxvz~|xs|u}y{srbg^y_oZcbWlwvrxusmkiurukbxs|{r}x{ly}atvs|wmnzqyn{shoxyzmxjz~z}}xp|m{mvbPWcdggdPHd{}}twy|pv{lzy~|vuy~odipkkvnrYodXx{|}{rwy{{zzusoXt}pwqXg{vibgfexukgtg||}oeovk|wvt]qywrz~|~lg~j{eaV]yo^\{}st}rwy|k{v~{v~ksviY^h^gsnprZkdesr|xzrpljtj`t}v{qp{~gav|la_auymdvg{~}xxsr~vdsx{jl~zx\gkg]RPdtn[Twxkqky|yysi||ztzncgfhttjvahjeyuzrufefkzymty~us}~zwfiyxidf^igqxi~srtonyxgxlxurt|wn~xz\ilobXYc}|hat}mmkpw~~uv{}}vrvsiZYacqmZeT_`Yy}~urwljvu\rrmy~}ck~{g`hkpnsosn|nlikyqnuq|{{d`z{p[mg`UROdulYc{opnhtxzsmw}z~|pkb^mxtnayno{|oo~{lfdvjgy}qermw~hWgviSWbbhihkx}{uu~~~jwtsytua|bntib`Yevp]\xxqpsvx}~svm~|~ssusid]WhpmffSekiz~s|}phelmi{vyo~||yuho|e^bgljlg{wozuryxkw{l~}nuzq_u|vagrj[QR_yw`^{wx~|unqmwxxuimxzvqvu}nmzjbh{fam~zwsxymw_awzldknvrlww|vx^vzqUT\iumhvtjn}vuyyqxxtx{{wdkztwztvdcw}{ovmjowlnwxwm}xZbSc|dbcftodsujjww}s}v}vwypep~tu|iiyz{|ypnrkonyscnoywslpt||oxutl`obsskctogr~|uxtny{xxfnipvmrjgjntiq}ocyt{zlda_h]ny|u~|zwyjs|r{}svusi]Rphsz}x|~vtyulnurzskvu}mdhcf^SYnyszxw}}s~~zp{lkq|hjx{{w|{}y|~wkny|yonh]Odwv~opvs|{~hmgrxouluyj_lysnw}md|}u{|v|j^^^mqmvkr~~x}qlpt~{~zroknla\utzos~vzzpjij|osvks\\bbif^c{xwz~vivrzwu{kgfiyo~wx||xqkt|~z}rc^_cde|~zy|mg{zy}~htlqudliklkixykm|t}|}y|~xxbYbgquw{{zxus}mxz|z{j^^ckignzw{vs{sppsnublsw\Ucjneaixuttpo}|}kdvsghs~|y~}{|lejzugv~jUV`jio}ynpstz}fl]lr`hnxzvo{|uru{ys|xst}ynre^\emz}x~tt}~v|zuwprbZXbo}}|qqz{nwytprorl_~tlfP^ellihtnsyyrt}tsim~vmiszpx|tsw}tfi~wfRRYciqzlhzuv~lvekxbgnsv{wxww~~y}vvvz}zqdidejv|xwtn|x|}wr{tu~}vvubXZht||xx~w{xuszyzhZylhcUiuxphe}sknqrot~{zobj{sjcm}~~~vw{vvw|wloikqp[Qcnu~pr|n~tto{|cj|w\kvw}~ts}s}ozzutrsokm]dvy{|~xyxu{ztz}w~~zv]Zemaoxz}o~{vyivwmjqitqnWc}vps|}ttwxpt}{c^h|tdqoqzw~qgv||~~qrZdkr]Vecjzstrxroxghut_typtvs~~}p}ywwxuuyhokn\cqms}}yy~v}ww}vs}zu_^_i]lsq}q|z|ipxmozgtyrZ\{vpq~}uyxsr~}o\eowgpqn{v|xww|qq|yyrsaYgnkXcegt}}rxs~xm|rropdnur{u||vuzwq~pwjhoph[kmpzs~}t|rviyuzia`ba\orwqzfw~wkz|jw|nVh|usy|}}y{zsv{}}}{{}j`dmohtswu|{{p}|n}~}zupnn]Walj^hjq{|rw{o{n~xysi]txv}yyyzxwnprgemrk]jowrwutzdyqsqh_accZju~s{hq}xntrt|u]d}wvy~}xx}zyuy}v|p}wpacgngqzw~{~nw~ov}{zxevsgdUYbmfitywrxnrutz|p]c{{p|~y}lwwkk`emthgtz~uzuwyxxzlkw}mxwii^`dh`dww{wxqm|ypqyszwed~}vxxx~{zy~yxwzk{~pqebemjl}z}{{qq~ns}}y~{ycpqcdXX_ljky~wtxmrvy|u]d||~oyz~|{~}}lythjaclumkwrrv~u|wlr{}yvvhypff_`glcgyxuy}pwxoxyw|sbp}v{uw{{}uz}|uh~tnnedjnhpwx|zo|zvi}~uy}y}ndtdc_Y[glhr~}vxrm|u~j]w{ywz{~w}~wrs|jkebhtrjr|}{zupsk|o|~t~||pqsmg`olahipnfrv{}sitklttxuv}zsy}tugqxgb|iZe\lw}z{{}ulg~xt~nxjtnu{hskSmm]isqrz}p~vktxdcypu|~p{zrvstnohfttpkqzzz||yuvsxq|z~j~xxwoicqpeihllZlnmyyq|qmrovtp}}v~rptzka}s^f[_rhr|{{ukv|u~|tpxpptsywtYhvbeieasvww~sr{qax{epqur}u|r{uurhnslhftprp~rwzywuos~~u{shjumilgo`\ndbs|qyzswsptor{zv|{}r}sy|fjlbe\jnan}}x~|yr~{qwyv||svmxst}~u^nwgli`[mko{|vxt}of{ejgswwv{twvkpslgbqnolvu{yvwypn~{|}uyrovnglinb[lf_eodx}{yyrvojxxzu|~sur{mjvbd]iqgfxy}s{~}u|utwv}r}wzyqvzr{{fg|rlod[cldqrz|vy{iuqjii|{y{x|{{txpktnndhrknm||}yys{ks}xz}|v|zsu{ngjgk]ald\ehb}~{wynmwsxtxtrwiqtfc^npffyxzxn~x~stxwyn~{|yzxq~yr~|xcfzuspd[dkcmlw{vwvk~qlhq}w|zzz~zxylkrlpenpkmm}}y~|xx{{|lzzxz|~wyzzs{ykkjhe[ih`Yb_g{|~xlrrsvsx~rtpkmiamtjbmyvwko}~}yrwuxnv|uy~wzs{ux}{lcuytuka_kbfgluqx}vyn|xoko}vy{y||y}rkqkphmslokwwpz}{y~nx|vr{~{yz}w}|nmnlh]jh`V\Ya{zswz~pnptvwx}}x`rsqt{tfatsewyk]R^dgr~|nuzy~w~hthZt~w}{~pgz{|Xcgo~}xp|whsxhuwlf]ksppg_~vlflndpdawuqq||t~twv}}lwzvninYXjtzz~{y{tl{{xix}ut}mexrdfs}eokowkfhz~|w~tlljs~}spvzqp{soqpmkjibTRnd\j]x{fr}sxxzfkvxzkg]k^^xg|ytz~y}|~|jdukzi~kx~nsieurrn|k_eaegUOoh`xkx|yq~sz}}~|srle|~mqkmq\KfbTm\e|`xqqxynooqrbjtnsfga`]Rpdprw|s{y{|ipyyu|uvd}zkomnxjQhq^vrlvw}hn}rq}~|{q}h}xxt{uW^q\gnZ}qpssutvv}sgwvzujlanZguh{t~wtz}w~{hinr~gt}z}wyyktghsx{xudkgot\Spebzf{w}rtz{zyumjvbtusrymQakUgdZ|gtn{n~kkoovh]lkria`YjTioh~vvu~ovt}oyxyk|mu|~|nspx{b[vhhzf~uw~{{qz|n|otvzx~{uytpnl~|zxw[gu`llarswstrvv|tcqrxqeg]qak|m{zr|w}{ljqq|ir}|symmdqqj{uz{{hmjnufXrpfxjyx}y|tx~~|nzyshif}kzuurws[bsahh^}qjuz~m}qmporn\gioj\_Wlai}m~|t~}us|{qxw|pqhy{u}rurtwh_xskxk}t{wvspyqqyt|w}~~wlxxpkkqt|{}vbrzjtjlqvztxvxwzqcoovkabaubx}txv~z{|{}jvsyto{vy~~luw|sefc{k|~y~sqpoqq_g{nrsmy|xzt~~~xz~lotvuefbxuw{zwvvfdynkm`z{eu~ouxqqqps`bilm[\Ylhdovxu~twvt|}~lkgzztyyvvvkf}wrvkryvsqm}~s|u}rs||~{}{~pktvzigev}t~sk{swi}m{yy{wyw{lfprveb]mocr~w~{~|}}toyznp{}}}rltuwfb_qqi{wwttsjf|xsuks}yvsxyx~|}z}njqstca_rpl~|xnkwrpf}~g|v|pt}vqsqtfcjmo^[Xjg`}l{xxwvwysz|~mhevvm}{xql}wvlq~|u}~rmkzzn~}xss~yw~xzx{phpqvgbaqwj{u}{pr{zwy{y|thprvha_kp`~swz}}{wq||qty|w{|{|wipqvj`^hta~||~z{uriywxnyxxzsrws}|yv|xzwxgilpk]]bvew|{sz}zpqzkwxny}wrvsvffjnl\[]pah}m|y~}zw{upuwxhdexro~zts|xpr~|}}sz{rjjx{l}xuuv~{wq}ttutsdjlqi__jzizw~}t~|t}y}z|lmpsoa`fwdsr~~}zu}wwq||s{}v{wzmhlnn_\`tjny|vss}xrt~x~wppt{{x}xr~utusscfhmf[\gyh}wtw{nwwnx~wqvrvheill][_sgls~|~|xz{{|~vkppthbfxzq{xv}txw~{w~qtuzrhiun}yu}xnurjv|zrnsnrdcgkj^`kq}u~}t|w}x|pjnpqcaeynqxzx{zwrz{yqs|rvsunchimb]`tumxwv}vyz|zuyz~vmp~uyzswrkx|zpoqnpabdid[^m|m|v{pwtmx}urwrvhfhlj^^gxgyx~}}v~{s||v|v{nilopddlt|zz|t~|tzy|wosuwjgluw{z}xvnstphkvvriploh_dfka_f|xxz~}vww~z|wjnnrgbfxsn|{|~}ytu{unnz{ynuqrobefja\autm{{}}zyy~xx}twx|slouzvs{smjvxvjonmm^acha[`rwm{xvxrp|~|rxvuugijof_assh|~~{yztr~t{xxwhjkpgbfyxp{xwzttx}}|oqqvlfj{wn||~zvpn}ungfrtrgolmk^ach`\bvto~~wwx}}|npptjdhytk{}~~{vs|tnmxzxnurqqcefja\`rqg~{zw|vu{tuvzrlo~qzwq|rkgrutjoolo``af`Z_psf~wztp{uz{w{lklpjbesue~x~vzuoy~~uwzuzlijnjber{j|u{vpyzzytopsrhhr~j|x~|}t}xkr|piahoqjhphof^ade]_iyjx~vz{||pqrtjho~ory|~utzrjkuwulvprqcdeh`^duni|~|v{us~z~tttxrlp~oyynz|pidlqqhkphpc_adc\`mxf~|y~wov}yww~vlmoofgp}kux~yyvnpy{xq{twuhijmfem}sr|vyrpz~~u~|zpoosmhmzziy|~|}{s~~tisxle_flmfhnfod^aee_dq}l}uyz|}qrstllstt}}|vq|qigpssjspnsdcdhcahwvj~v|upx~x}zxrsusnr~p~vxmq|pg`elnggpfnh^`ce`dp~n{}|yqs{~zux|{nnoqkkrtr~ztulktwvnxtrwjhjmihp|q~uyqmvz{s}yuyyy{rrxoyupowzzpfmvld^fnqms|w|wz}~z|ku}}~{tztr}rsvvopw~hrnv~{|tgbi{wqkpz}~~}}vywvy~wyz}||~sivpornoxvr|zyzzxuu~{oiwujcdnttr~xkc~tfnklwtx|wt{|yt}{|vtyyn~v}wt|{oyzoggqwyv}}|o}t{qplfau{ogiriXtnanwwqrttjppisljlv|{wyujtwlcajprnz|yzwz}zw{zdzsh|{}y|~w|qllv}vjcillgsroynlmpmlr|qazqestzxs~z{xzoimnlgw~~y|yk}r~x~y|{}||tr{~}y|q`wjas~}|~wx~wp|tkiorplyz~yw|zmu{~wxtt|}|{plswxu|~~}rt|ogiif`nu|ye|wfnuwqpqqgmnfqmkmuxsmqxmuzne`hnqmy{w{vxzxv{zdzwdo~~y}}w~xqotywv}wmorrlsxoytlmonlqzxarzdiu{y~r{}z~{xz}rikmmglxuoytv{yw}|}}{su|z{ffydaqv}|tp}|qiioqnn{v{|ur}vyzw~wuw~~zxspvyyw}~~rt~|yldffd`mtwjjnvysqqphloeookmsxtno~{qs{rhbgmpnw~vwwxwuy}dp|fjxxz~{vsxpnpyzxyvruwtr~vv}sppqnpw|kb{pcnyw~zs||}~}t|vkehiiftz{mrww{t}{}zsw|}}|yuv{x^ewdevy}vq}{qhglnmo~z}~wru~vxqztru}vqvz{you|xwtg]^`bao}}mzoltyvsrqiiqdjojlqyxsr}tp{wlegmqpvxzxwwtw||dg}mjz||pcaeghjzy~{xy}us~v}wqzwvz~xids|xwow}zsxruww}wnpxpe`dilm{|Xumdilortoszov{y|}reeqxywvuty|fsstywwyywyqsy|{lz~zwxzs]Ve}|stieefgluxeftlwytnvw{|wox{qigmqrw~ywuehxuspktpkwvw{y~sgdnyzsemr~uimsja]bfijz{z~|xwz}ng{~}ys|zr}~}{{|oa^hv~{yxywirwt~}x}{smnpf]|tjmk_higjquqlp{xtjlrh]W^gpuy~vuuuw~xhsvz}xjsw}{||snsvxzszwyl^X`m{zvuxzk[suadnvwyyvotqenpr{~ypt|ulhmprs}~ywxxh^~yqxulwxx{{|{ojnu}yz|tfnpu}}vkegjjkxws|{{yy}~ma~v{}ys{zq|~~{{tf_cku}~yjrs|w~y|yuuteVs{tvtfmomnsupjlwuskltmaZ]bhn|{xwvyxh~sqz{~}mrvv~wqswyzq~x|tf[\clwybo~jciqtuuuoqvikqpt}}upzypijoqrzz~|yz{t^g{zyw}qq{y{|zzxomqv||~|{v{ngoou{pkmonr}vv|{{z|}hd~sy}xt}xq}~}xz}sgadks~xlss{~w~xz}~{vtutgVny~{||mpussvyunmvurmiqod\]agm{}{yxy~juypu~yx}omuv{}uswyzvy~stuk_\ajtpf}{hgmttutqltpemqrx|ss{wojmprty}zxvyyl]t}}x{~qu|{}}}}yvnntzx~x{{spuhdnsz}rnopptzw|zyxz~|jewz}xt}zq{~~trxqgbgoyzz}or{~w{|~~zvvxzzurqrw|vfxztr~w{~{}wqwzrkjotyzupjrigwhipsromihqjfsx}uswhju|z{|wriholecir{xys~nhnuwutqmtujqy|ymgotvtssss}w|{|~s~lhry~xrrzzwtoxztnqw~|~yqquwtqnikrhjw}{ms|~x{vv{wvuv{zhoxz}xsllxvy{|}}rmqxrstspmjirlfrx|}vmpz{yx{{z|}w~xutty~~omypmy}~yz}xtlmtqjglqw|y{{z~{ypfgu~~ywvy}|md~xuz~}ytnjsvq~~wx|wslkrqkimt{}yyvvyxurolsshpx{|sqz}|}vxohdmvvyqmsrjfhmrx~zvuzrynpttrpmjprgoy|}sow|}}{yy|rf}|v{~zupx{u~z{}uqpw|wrntsgmuwz~xokstrrw{{|v~{wvvy~~pg}y{tovxs~zrpwunjlpu{~}{}~z}ujfnqt{|{|}mcztotz{ytokqqjuyz}xtnlsrlhkpu||z~~~zvspvwkqy{~xsy{xyt{|yskeknixwqvvnhilot{}ywy~xn~qpttrpmjqshmvy}wpuxvx}vgw}sv}|xsx{rx~{z|~{uqs{{ytvznluwy|||ztlorompvz||zzxwy~xgnuv||wqqxqrsifdbjuop}xyxow~z{|t|vw}y||}~nkyu{|xw{ry{wy}~|utzxrnpsw~ocjdjopponvwlr{~{~{vz}p|~uhfpw}ysx|wrqsux}{pwvtz{olzwidfinwyw}|}{jingjpx~}|ypy~|zwts{|}~soyz~{xv}{nrwvv{~~iXl|qhbei`bpvz{z}zuphijeacltz~sy}y}zslkqnp|xqr{z{z|yqypry|{{{z|t~vtsmlmq|zywxtzz|vt~rffjnpswyvu}|xx|zxuoonmtwsnnvzqupz}|rqqptysnwxytsxriry{~ujfemwzurw|sz}{yy{rm|styzxupknlhx}|vr}idgjkklnw|tx~||uoidmzyy~|{{zlyuqstpkfchpnw~wqqvusrnszyvvxz}}|}pmt|y{ywxzzp]^trdclv{zvqqvpmy|wuyzwupqvvqmnpt~zrmmuz||qpssrpoqyuls|{y}vngfotu~zqjb]chimsx|w{~ytpotz{qlwjhkkigegrupw|xyyrld`gou|z}~w|{~|||vtyunoqstuv|zmntuuvvroqsolfaejklprrt{zrzshz}pnqrojecimhr~y}|}xodZ]bgo}}zz{vh]n}qdbegeb^\ab[du}xxzvtpmrvustvy~{~}wrpt}~~|yxyy|~z}vonlhdbdmrnz|woimnjffghlurhmuvuuuwyvkg}{mjmonmmowxnr{~}{~|voqstw}t{{y{z{}|wvyyss}{vqljnl`_gknrvusuvqmhadggglsx~z|un}xyzwsolprioy|{~~}{xwx{tkbbefir~}|~~pbmvgacffdbbgjaanx}zz|{zzx|}|}~~{uzzx{ywx}~trqojd_`d]Zgw~tpsrmjklnu|upx|zwutuuodjyxzzxvux{szzrlnppuxuvtvvtpnntslvxy|wqljlqsnp{vrqvuiiqtuvvsqrtpnkfjpqrv|szsstsqootxpp~{{~|xvvwy}|~}|~v||yxzxrmd`dgjpzwfezrijnpoljlrnfnz|y}|z{}}~~vw}yuqqwwmoyz{~~~~sxolkhc_]afacs}vuyzxwwxz|w~}{|zpwxuvvutu{x~~xtlhlprv}~x{wxywursyyqu~|||~utyxtporxzus}|vqw||}}|z{}zvsmlqstw|qwzommkgb`ciedr}}}}w|~}{z}ux}xuurlha^emt|tm~qlmmkigiqsms{wy|zwvwwz~rrxxwvx~|yy~urz~~|}}{ytv|}owsiggfdbcjqnp}{xwxz|yzpyusssqoosxqiovxy|||~~yvpnty|{mpusuusqptzwrz|~||~wz}zwvw|z~|}}sv~}|}|wtnjmrstx{~y|wsxqqpmighopkq|x{~||}}{x||}|{|{{|yywrnjeiqv|xo~rkkkifdekldfqx|~}~~~~~yy~~{xvx{yrp~{yzusz~|xsuy{}}svmkjgdabimhl||yyy|utz{yvuvywpl|{|zxwx~ury}}}|zwwvrmicekot{xo}yrrsrppry|uvwu{{ywwz}|uuuqvyxxwutvxvtrnpuwx{rjxpihihfeflmehu}}|||~zw~yv|~|z{zuqlfglnqxtl{xrrsrpnpuukjt|}ytuxwvvwz~yx~|ywxyulj~}{|xv}}xx|}}}sxnkjgc`afha`lx~~~xmfu|tstsqqsz|touz{{{zxyyurnhgkmnsyu~wvwwutuz}uqx~}|~xrwywsqpqohbp}~~|wtnkmnnosx~zlo{ojjigddglhbiu~~tw}|~~ssyzyxvtrrpkhc_chlqyytvnmmkhhkqoghry}{wwzzxxyz~vw}~|{||xom}x~~yx|}}tiqrhefedcejnifp|z~~{qgoyvvvussx|wnqwzz{{{{|zvsnjmprv{wv|wvvusru{xoqz}xy{{zz{}ww}~|zxyyumm}y~}~~zwsnmprruy~xnw{plljhfglqkgp{~xns{{|{z{vv{}}|zxvvtolhdfkoszzsxpnnljilqqihqy~{z}}|{||yv|}{{|zrn~~vy~~~|yvrpswy|{nqxkdca^\]cigdlzz}xorzyywvvy|rqwz{zzxwxwspmilrvzuo~xpooonosz|uu~}{||ywwwy~{pmswwvvwzxqoy|~}zwsqtwy{wlrznjihfeflqmiq|}~~u|}|{xvvz}wmnsuuttrqrokiechnrxyyztrqommqwtmoy}yz||{{|}~tv|}|zyzztmp~}}{z~zvwz|~|niv|mda`^]]aikfht{qn{yyxvvy~~tptxxxwvuutpmjfgkpsy}wwtsqoorwxqnv~zxzywwvwz~zomswwuuvwtmm}}zwrqtvx{~qittiedbaadjnihr}~~to}~{{ywvw||slpttssqpppmjheejosyywsrpnnpvwplrz~~ywxyxwxy}vt{}}}ypny||yusvxy||ohsqgdca__cikednxxnjzywwvttw||snsxzz{{{{{xurmmpruy~|uwsrqoorwwokry~{y{{yyyz}~spvyzxvvuqigx|xsqrttvz~woy}rmljgginpjelu|~{qjv}zzzxxz~woquvvutssspmjfdgjmqw{{usqnmosvpiltz~}w{|{yumfo{{}zvrsvwx|wkl|{meca_]_digacmv}xnp~}}|{{vswz{zzyyywtrnjjmprw|zvvrqpnosxwolry}{yzzxxxx{{qouyyxvutofduxw~xrprqosw}~wpx{utsppquwphjquy}{x|zpt|vtsqqrw{xqqw{|~~zwrnmnoprv{vz~xuuwwwx|}vsx~|uliv|{|}}~~~{uw}|}~}ywyz{|~{tz{vtsqqswyuprywrx}vqsy}}}|{xqkn}}xwxwwy|~xvz}}zwtsuwy|}~urqpopswwroty}{usty{~}xvtqnkhfhjmorv{|xqtytrqppruxwrty~}ywy}}}xsu~~~~}{upmjijllheinqtvxz|}|{yvvxyz{|}}{|wt|{ywtsuy}||}{yvtvxz|~yvtuz}{|{ywuspkgkv}{vsrrrrtwywssvyz{{|}~}{zxvwy{}zwtsrruwvssw{~zvrppsy|~|wsplklnoqsvz}}zz~~zusz|wutsstuxxutw{}~{ywy}~~~|yuv~~~}|}{wspnnonkiknqsuwy{||zywwxzz{|}~|{}~}zvu|~zxvutux{{|~|zxwy{}~{wusty}{xvtqlkpx|yvttttuvxyvtuwxyyzz||{zywwxz{|~~~~~~~|zyyz{|zxz}{xusqrtwx{|xtrrrrstuxz{yy{}~~~}{xutx~|xvuuuuvxxvtvxz{|}~}zxvwz~}zz~}|{zyz{}~}zxvvtqnopqrsuwxzzyxwwxyzz{{|~}{z{}}}}|{xuuy}{zyxwxy{{|}{|}~~{xussvz}zwtpnqvywuttttuvwxvuuvwxxxyz{zyxwwxyz{|~~~}}}}{zz|~|ywusrsuvw{~{wvuuuuuvxyxwxz{||{zyvtuy}|zxwvvwwyzyxxz{}~~|zyz||z{~|{zyxwxyz|~|zywtrrrsstuvxxxxwvvwxyzz{|}}||}~~|zy|~}{zyyyyz{|~}}~~~}|{yvtrrtx|}{xtrrvxxwuuuuvwxyyxxyzz{{|}~~}|{{{|}}~~~~~~}}}~~~~~}|{|}~~~zwusrqrsuwz~|zxxxwwxxzzzz{|}}~}}{zyz}~|{zzzzz{{zyz{||}}}~~}|zyyz}~}|}~}|ywvvvvwxz}~}|zxwwwxxyyz||||{zz{|||}}~~~}}}~~~~~}|zz{~}{zyxwxy{|}~~~~}||{zyxwvutsrsvz~|zxxz|}|zzyyyzz{{zyzz{{{||}}}|{{{{|}}}~~~~|{{{{{|}~~~}~~|{{||zwusqqqruwz~~}}|||||}}|||}}}}}|{zyz}~~|{zzzzzz{zzzz{{{{{{{zyyxy{~~}~~~~}|{{zxvuuuvwy{~~|{{{{{{{|}}}|{{{{||||}}}}||}}}}}|{zyz{}}|zyxxwxyz|}~}||{{zyxwwvuuuvx{~|{|}~}|{{zz{{{{zzzz{{{{||||{{zz{{||||}}|{{zzzz{|}~~~~~~|{{||zxvutstuwy{~~~~~~~}}}}}}}}|{zz{|}}|{zzzzz{{zzzz{{{zzzzyyxxyz}~~~}||{zxwvvvvwxz|~}}||||}}}||{{{|||||}}}|||}}}}||{zzz||{zyxwwwwxyz|~}}|{zyxxwvutttuwz~~~~~}|{{{{{||{{{{{{{||||||{{z{{{{{{{{{zyyyyyyzz{|~~}{{{{zxvutssstvxz}~~~~~~~~}|{{|}}}|{{zzzzzzzzyzzzzzzzyyxwwwxz|~~}||{zyxvvuuuuvwx{}~~~~}}|||}}}}}}}||||||||{{zyzz{{zyxwwwwwwxy{}~}|{zyxwwvuutssstwy|~}}}}}|||{{{{{{|||||{{zzz{{{{zzzzyyxxxxxyz{|~~|{zyyywvutsrrssuwy|~~~~~}||{||}}|{{{{zzzzzzzzyyyyyyyyxxxxy{}~}||{zyxxwvuttsttuvxz|~~}}}}}}}}}}}||||||||{{zzzz{{zzyxxxxxyz{}~~}|{zyyxwvuuttsssstvx{~~~}}~}}}|||||||||||||{{{{{{{{{{{zzyyzzz{|~~}|{zyxwwvvtsrrrrstvx{}~~~}}}}~~}}||||||||{{{{{zzzzzyyyyz{|~~}|{zzyxwvuutssrsstuwy{~~~~~~~~~~~~~~~~}}}}|{{{{{{zyyxxxyz{|~~}}|{{zyxwvuutssrrrrssuwy|~~~~~~~~~~}}||||||{{{zzyyyyyz{|}~~}}|{{zywvvuutsrrqqrstuwz|~~~}}}}|||{{{{zzzyyyyyyyz{}~~}}|{zyyxwwvuttsrrrrssuvxz}~~~~~}}||{{{{zzyyxxxxxyz{}~~}||{zzyxwvuutssrrrrrrsuwy|~~~}}|||||{{zzzyyxxxyyz{|}~~}}||{zzyxwvuuttsrrrrrstvxz|~~}}}}||{{{zzzyyyyxxxxyz{|~~~}}}|{{zyxxwwvuutsssrssstvwy{}~~~~}}||{{{{zzyyxxxxxxyz{|~~~~}||{{zzyxxwvvuttssssssstvxz||~}~{~zvsmssqxy~~{|ywtoutwz{}y{|~~zxspww~ysqmoiihhkpy}qbeqcp|{|~shmy{{|{qnqs~}}zrttojbdflpv||{u{~qfcehhmnonoop}}rhfw}}}|z}ynnn{~ztrz|}|}xqovwyyyxq|}pep{zwxvtsw}}zz|{}~~}xr~~{{woqomsvwkbhv~|pk{xxyxo{x|wnx{kp|rqvwrks}|~xrugtuy|xrhm}{u{wov}}xms|tus\izsir|{||ulknonzyz{}}rtzn}wwvqxxmmtyyy{y}yzzizmz|}yz{ror`kfjrupeittljnmdbyohp{w}uw~vvxolrjrbryqpx|wgtpp{tw}wyx~~y~{rgo}z}y}jzldiu~ubszs~t~{{sxmnn{p~~udtyuhr~vqvn~~wnx|dhr|yryy}uytxxx|}z{tryhqimjjnqvr}}}zylyvrpy{~~~tysjvsghxvqsty|yqr~y~~oxihyzxxws|mcjk{ovv}~miqf^bmrpephns{jnz}rsv}|yxwymnfjjh~~qx|nzw~v|}~{{xzozwv|ofyvos}{|nlnsndlu~ttrrpxp|qt~u|lwvnjgv~spxzwm{mqluqowvpy}rvyx|u|{|trym}ssbVchpc_u}|pv{lrz{ry|sijtzzpv}{x|xw}vnlr|u~}y{vnpx{ppripyqsdqxlejssi^ff~~w}qes|xwsss{yo}}uznpw{yzsou{uyx}zol{xv{utvyv}zpxwnf~ompmhi~pvwjh~xul{~|xz~}hky{xih~}vvqngu{rrrwz}u}ucs|uos{ryt{~|x}r~q\jjcXWrqtxv~x{}~xtxz}~~xt|}qkoo}xy~yqtxuqvt|~w}ym_q~yx}{zudduvgialxt}rfcz|{yqpqsuxmzxn}xx{wt|{y|xqiqskfww~{y{vtop}{twly}~ikzwtnhbkzpqlvv~xx|tl{z}~rs{{}rx{rjwqhpusky{rmi|wdywz~}uwt}rsv{y}{{~s~oyxn`Vewrifdmtmxsk{{|r}sugx}yzxt~|z~{p{zt{okwypox{}~~|xw}zssvwzzztexrnmgjq|yyumuzro|tn}sn}~}}tzspqgal~z{~|wux}nkwzvfovtlntkq|qmdzzqywxzuzsz{~{|ywtzy|}yx|{nr|uunge|}uj~}||gpx|w~{|zrv{xz}|{us}~krtbdvr`Yaikcx}w{{~uww{|ssosqrv}m}}x|xjuw}{x}sm~wqvy~zor~xqxk~pqjhfo|wmp~yt{_y}yyq~rn|~}u~zwvv||pdfnruzo}}~~tyvuutrysnlthm~vgvyyux|ymw~uvyy}}|v{m|{}wntxvp|}xyy}qlv{v}jputxyz|}y|y}k|yuljzt_Z]]\e~wv~~{yqx}ux}}~lsvqoozx~}xymu|xsruu|zgq{vwyyu|{w{~|~rooy{uoovxpmw}y`xmolzvo{x}{zuty~yu{~vttpmkuxl~twm{t}}tv{kjqvjr{qqzw|~rhxy}vxnsw}~wzz}}|z~rvl}u}|uxvrxswkk~~xxxkekptxytxxx{xxzvy}ml~sihd^Yjqorswt~xusx{~mn}{su{zstzzxzyqrzrv~v|wtv{|wjwr}x~~|zystux{ulox}dsmturstplvsjmsx|w|yux}zqvzsuu_tyn{xuzy{{zlnussngu~uttdj|~}wwuw|vxywq~~}mxvxnq}uzy{q|{z|n}ostmnx|vw{|yz{}xm}sz|yrs|gekmgkwlkckvw{vy}|{u~t{t|{ppw~zx|sxwosyp~{s{lv}x{uztnykjmzz{}~vu}wwz|}~zl}mvtnsymbntmnnjnxzxy|{yxy{itzgnruy{~}}wy}~zxspstqwyyx{rny|wxq~~{z~mrz~xtzohyvwpuupmz}}|qy~~~uswoq|~wqpv|}|{xqswz~|{tlqtmgbcwurdmzz|yiw|y|xy~u{|zw~~{yz{~~r}{z{~wxzvo|ytz|vof~|ppqsu{}|xrwyhrx}ukowmcsphjlmr{~y}}|{zzvznrtoqnouv{{}tp}qllnuzn}ufsz{}wy}wx|z}ot~{w}{|}}}zq}nrknssnmz}y}u~|~qzxqr|}xzxppuxzpwvwyzswzutrmnnmsynrmo~~{{zsmty}~wy|z~zqxzls}}zxy{utu{|~{xr{~~yzn}wklotx}{tt}}yz|~p~uyrrvqfh{|wofcer}zry{zwz}iu{pwxtsvzyvyz}z{}zytq|}xx~jm~zwwk~}~z|z{zyvzvhu{}rghihiu{qt|~}{wyz}~{z~yns|xrvzm}x{||~}wyryznqwnkkn}yvheyzncgx~{{ys~~}xt{|}q{w{xu{~zv}~zx}x~|qvvxnqtmknswvrpruzxzqu~|qopqpu}wifnpx{syquw|}|~vzxprsw|~{rmpw~wp}||wtu{}xrynryyouzvy~}z}v|~~ww{|ovtvvqrngcl{{qljkv~~|{{}ypv}vt~jpuw|||~}zwu{you}yuqq{z|xoz|{{{tjgq{~y}y}z}w{|ls~~zooqru{y||}nx|~wl~~vwyxusqmkmqrrv{tu|}vtqnq~{ljpot}xofwzpqwz}p{}xtw{ytrstvxvpx|~x~{x|~w{qq{}|ti~|vuw{|xy|}yuysm}xzsiilnov}|slijlu||}v~|{xty{izuqvy|~xu{|wwtrs|yopx|zwmzynder~~}xvuux}zz{oqu{vsqonu|vu||~qvyw|py|sqsw|~wmhilorxqi~}~ttvvv{|wsopuurr{}}unr}x{yz}}xx~zplouxyvpiw~sw~{y|wrv~yupwy{zz|~|z{}~{vyz}|pyv{ynjhegs}ogehs}w{pw~~{y{~}zowvu}yz~~yvxz|~ywvtu}{rv{|~rs~znegqzzqwso{||z|}yrut}mimrv}|}~{~rnyvussuvtrpnklqrhwvxvqorzxuu{swznr}zqkmy~}y{n~|uomnrusltyyzxurww}ss}vy~tz|tsy|vux|zv{|xl~{zvmkjjkr{~zurons|tsynrz}||~uzx}~y~yrqvz||yswwttv{{||yzvo{}unnu{|yxxssymq~~}{|~~xqy}slrqltyx|zwz~~xs{uuwxywqkkostusej|usy|~~{xx|z|}jo}yxrlp{|zz{y{ytw}|wwyxtoieryx|sopty}~y|yl|ytz~ywxxvvvyz||qsutkhioyvnknvrqpemy}yw~~}}w|z{zzyustvwso}|~{xyzw{xzsx|rmq{{phpmi|zwx{{z~ztzyzvuoly|{zz}yyxz|xrlgeemxuusioux|}zwy{jnz|{~|ogr~yyyu~}}yv{|yuvxyxrjn|uuzspqv~nwxsvx|~r{|~|x}wnnrsv|z}zu{}}ulfl{i_dikkhcbjxyvvz{svzsow}xrssoz}tmjhiow{}}yppqbfxzxwwx{}}{vqnt||z}qhlz}|tnls}oy~vzzsmigghnrmx||zz{{yusuz}tns{odeost{{oktmk|{y{wrrswx}|~|xz{uuwy~wqnu{spu}}z}pq{xvx}~rlpojnz|{|{v~qhegp}}yw{~zsle`cksy}}||}zy|}v{z|}z~yww||qmr~}yyrfep{|upvvjqztoptx{{xsqqtw{{xwx~rinw~~unnv}wz{yz|vmhhmuyquy}xsrtx{{z{{z{smloxwqu}uhu}zxy{~~zwvy||x{{|ypmnt}{tqu|spu||~u~zxxyywutsnlws~}yst}zxslktrpy~~yoebgpw{{xsy~x}~~{~u}xuttw~|yy|yx~~rgkuxwy|ztxznrzsporvwtqpsz}~{vuvz~~xssvx{~xtv|}}w|y||olrvx{{syvx|ytsvxywtx}{~{{|tqu|}yyxwwoq|wvxyzyusty{|xzxy|vqnotyyurry}{vx~|w|w}}zyzzvqmns|w~{~|x|}{yx{~sjp}{rvyywtpmnsvxxvqqytpwt{uppu{~{tpr{||~ytrstu|snv|qt~{yxurnkknsy}}|xvz~~yqmoz}|xtwwu~{v{tsvuqxxy{}}}}|{{{{yvrpz|ww{{||ww|yqv{su~~zwxxvroorz|}{x{wwx{|xpkkp{vr|}x{ur}ytpnory}|y~|}zqotw{wz{tlinw~}wpnvsiu|uw~}ywzyxz|{uomoywpnt~zvx|ykm}||}}yrkhipy{z}{{|uoos}|zvux~~{ynyzsos}{q}~zvtw|~xrvw|yuw{zrtzuu{|~ql}yw}ysonorx}zz{z|xsz~zxx|~umimwwv|{~~rwxsqruz{ty~{||~|{|~zsos}wokmsy~~xvyqht~zy{zvz~||~~~}wquvolow~zsqty}yw|z{~ywuvzwt}xz|utx|zutw}|uqt~}yyqthtzyy|{xwwy{}|}yvuwyzvqxx}|vsqsxsnu~}}uxwqyxtx}{vwzysppt{|uy{xwxwspq~~~spsw}~yuvzpmuqxwspprvxywuttw||tz~wvyyxx~upqu}}{}yyzytomrxv{uollmoprssopzt{{vttuw|yrt|~~}xtsrpnouwxyolostsppruz}wnwysqtz{xxutv|wuyxsquzyroprssrrtx~yp{|||tommmnot{spptz~}xutssvz~vrtyxojpw{|{sq~y}xronoopv}zzwrs|xuuussxxuttsqprv{uv}z}{styxsqvxtsw}zsmlmoqsv~}vsrqpnmot{~wu||ywussw{ww{|{ytrqqrsuy~wx{xqnoqsvzyv|vz|yxwuss}zuuy~}xsoow}{x}yvroopty~x}yw{yzxsonmnpz~xtuy~}{|}xxy{{zy{|xrquwut{|u~xy|}~~}{xvsqpot~xqt~}|~{vtvxz{zy}~zwvspoqu|z~|y|~~~|{{xrosyy}}|}}{}~qkpvvtqquz}zvspnnqv|{w~~{zyywuy~|vrswyywtu|{}ww{yqlou|{t~}zzz{||zxwwwvuv}}}ysrvz{uqv~~ysoorw}}vy}|{{|rnrvtomt~}~}}~{y{}ywy{}}|{|~{uqrzrn{xz{|zwustvvtqs}|}~~}~z~}vrruxyyyy~~zusrtx{ysy~~~~}zwtsuwwqmx||yuvvrpswwv|z~ytqqswxro~{w}~yvwxyxwvy}~wqnosvxyy{~yrtyyvwwrt}~|xtqqrtttt{zy~~~vqoqsvxzy{|zxwxxtox~}wstvsmknt~}}{xy}}~~zqoty{{{~~|{zzywv|}zwsonnpqsxzwx}|ypllosvwy~}~zxy{yqp}xspoopqt|{{~xpklsxvpp|~|{zz{vnp}|z}yqnpsuw{z}~{}tmkmoqrty~{vu}|np|~|wqlklnpt~z}vomnprsuz~~xns}{~}xrlkottpw|wvx}~|}xnnsvsruyzouxvz|vpmllmns~yxy~{~ztomnoprv}~xzwpyyx|~xsonnoou}{~y|~xsooppqv}vpz{ssw{~|ummrspllu}|{||z{~z{|zxwxxxxy~{zqqty{tz~x{}yuromox||~{}~{wuz~~||}~~~~xroquz~|swxux}~zwuronu{{|}xy}}z{wqprvz~}upz{sv}zvxzxurpoot|~~z|{{|yzxssxxqp|wppsvyzzxvtrqopw{x~}z|~{~xsyyvvx||uow}soqsuwxxvqlmrvv|wz||yyupy|yy|~{rnw}xtrsvxxwuuutrpuxu{~{~}}{oq}|z}wqyvqprtuvuttttsqrz{{zwz~~}|~~qny||~~yqt{spqty|wolquuqpwvsy|}{|{}wms}zlj{uqqsstsrrsuusruzsvz|~}zz}|pt~}~~|uowztstttsrqrturlm{vrx{|~~|~~vnt|~~ypq}pnu{ztqpqsttssxyqtyz{|}zyz~rpy}~zsnv|vvvutrpooqrrrt}trz{y}}zy{}vx{|~~xpn{xvvwusqsvuoklqy|rtxxx{~~}}|xzupv{|~~zuv}~xvxxwutqonnoqru}yswyxx|{{xx|rqx|}slu{xxyxwtqonnoqsw~{v{{vw}~z~ywz~y~{pt{}}{qmxzxvsrvzxqllnpqwvvyxvx~~x|~zyz}~xqw}~rny}wvxyzxuqmklmnqx}uwywy}|{z|}~~}nkxxmo|zuuxyzxuplmqtrns}wyzzy|}~~|wz|{{}ss{wlq~rosy|{vrnmmmmpz{w{}|{~|}~|{||{zyz|qs|xnswsux{|zwspnmlmp{yrtz}~|{~|{{zz||snuypvwsuy{zuppsurmjnzxw|}|{~~}}}{ywy~wqw{st~spswyzzwtqomkkp{}uw~zz~~~~~{ywy~}uqx~vs|zrprvxyyvtqnlknxunwz{~|xvz|vv{ypoz~uu~xtwyvssvxvspnlmu~wu|{yx{~~}|}~}yutwztlq~}w{wqqsvxzyxvsqompy}vx~}zwy{|{z{|~~{wsu}uko|zwvrrtwyzytolnstv{|vqw~xx~ysv|}xvx{vlmyut~ztqrtvwvusrpnnt|zrqy}yz{{zyz}{xwzzqjrzwxqpqsuvutssqompy|wwx{|z{{zyy|{vuyvihu~wz}towyolnnmkhinw~~~~{vx}yxv|yvryxtuuz~wijxrlnopqv|q}xtrlzuwropt{{wru}s~}ywwuvlsuz|}||u}s|xt|yt~|y~{lbjxy|uuz{~x}}w{{wlz~ntpuy~|{y{}|v|z||ylrwpn}ywlmrpnl`_koeixytv}}||vv~z}wsf]bv}x{~{zz~sxw}}|y}{|vtlltu}~vsp{vvu~{topxljlrpzyu~nkvz||z~z{|yjlnxt||}prisqiwy}}tthmk~}vsrur{w}oszxwx|jy|{xwvu{|w{{}z|s{pt|v~rmyuv}|wpqvv}jfclmw|zz{}sw}xur|{~q}}y}tpgj{vrywx~|~~|{uz~xuyxx~{kuyslrknjsqfiq~}}suvumq{{prt{|~v~}rlmhohmrzv}tsowqzqnqx{}nzyt}tsyrllsuztz~szto~}}z}}z~{tlons}{ytntd`cryzs{o}|}unq~{x|~uefott~x}z~~yy~}{xtzpx}yy|tv|ktuqhmnopmqirzxzy|r~}|tuw~||vxy|~~{yz{w|ulsrecuwv||x|qip|tuxz}y{tzupyrmrukox}y~~vrrvsww|vfgmrnz~qnjsrkszstx{t|v|{v|}xr~~vjppeg{rv|vx~~xwx|~ynxuzw}~wtipusouvjfizslnwxz~zsz}wz{y}vp{{yyr}tjmh^s{p{||uss|yz|xmyz{xztsnkjsytx{wr}wr~|y~||x{rz~}{rysggntx}{rprkj{sw~}yvryrqz}{~{uwqqtek~tox{w|ywtvzw||y}|yvwwnn}zpnkmnroehqvvw}zz{yvvstz|vxv~}~{~ytvqilbbuy|z}{znsysvzuorqptrjs~~}tpz~}}xzxrv~{uu|vntzuow|{}powq}|vx||zxzst~wouzvornnrklx~ww|yu{{yx~|{xr||~{~wrx{qhisnemy~yzv}}{wls}|vw}|xy}~z~}qrxuiehrxps}z{yxxv{vvqzysu|~zvyxtzzux~|sxuy~vyy~}z~wz~zu|sqsuy~yt~~|wy{~z~~x{~rmw{y|vy}oruzsifmrkp}}rt~~rm|oux{~xt}zvphkturwuvzyyvtz{w|zsr{}}y~}|xxyyotxrlpvnhs}xuuz|w~}tnw~|~ynn}|swvtu}vz}uvy}z}{u}|~}pr{ytrxysvy}vv~xwyvzxwyuy}}{p{wrpmehsytwyx|~{{|yy~zzwwsw}try|~}}vvuqksxjz}u~~|xr}pty{|y{wx}~|}x}~{~~y|wwxxrilplm~z|szvy~xs}}vswyx|~vmtvwzw{z{}zw~x{s{s~|}qv~{twsomt||}}}wx}~zwo}xw~uw}~rqpqmmx}rlz}y{ww||utotyvv~||ww}uuw|xqr~ym}wuzxz~{|{st~}}{~w}|{}~z{x{zrs~}uvrpderzst{}~}}yxx~{~wt~ssyyqwws}oyut}z~{||~~~{}}zu||prroqw|r|{xx}~}tw}yw|ty~tuzrsvzxnpuvx}}~{{}||rwzsyqkz~stu{z~z{zryyy{vxx~~x}|zrq{xy~yvrxu}~~~~~w~}xxxpnkmjquqiuy~~}~sv}{wpw{|v~{t}ptz}~}v}x|vz|z|~x{{qowwzrt{s~y{~|zr|zx}xw|{rqzyx|wrhp}uz}|{~~{~y|}yyuy{szysrumqu}}{vtut~vt~wzvwzvxs{yx}}s~}~~w|y|}yvzpntvugiproy}~|||}xyx{zxuvvz}{{vv}xzvyvz|y}wx}{|yvv{|v|ynuvvwxt{|~{|q|zt{vv}wu{vspvxsxytnyz|xwtxxvux}qlstqyy{qp~}{v~||{uzw}}z||s~tv{}wy|zz|}~yv~ypr|wztpmgqwuowru}~|{}~v}~}r{xs|ws|yv{ypvw}x}||{~z}{~vyrs}sor|rlp~~~}{|x~|xywzvxqu{{{oswws~~xv~}~~~{~|zt}~xuzsou|suorvw~}zzw{}|ty~zzs{t~~yq~ytz|y~}}~vzzvw~xnqnunnttknyzzt~{zx||{z}zyv|x|sovtxz{pu|}~{}~x}|~~|zy~xtuxtjutrt}wx||}y}~~xps~y|zwxpw~uww{x{~}~}}v}}|~~zxut|xuuvjpt{~v}~x{xw|{zz|{|}}|u}|x}vty~~z|~v}yxuy|trsksqtrimqtvxp{xw|zzywz}xwzrqyvxrwxv||}y{}{vvzuz|nqotzsptvt}~}~vwzxy~|uzu|xx|xt~t~yv{|~vx|{{tzu|zpjops{v|ux~}}v|{xv}t{}uzzr{zxz{y}~||}y}z{{vnnuorrnqiszsn~xt|~yzwz{yyus}{yxwnvuz}w|~~~~{|z{|v}p|wqvpvprumlv}~~vzv{wzvu}ysp}szyzuz~|{~wx~|{|ruolpxrtus{v~{~|~|~xw}|z}yz~wzxx{vuwt}x{|~ttwrquslrmyvlr|tqzyy}xzx|{}yv}y|rouqvyv{sy||~|~{ytzvzxwvtzqsnjrqtxzrzy|{yv{{y}qy|uv~|ywr|w~zx}yv||}~|utmxwtruntvw~tt|~{z}~v|}y{zyqpx||~|z~}{{}tzryxroqmu{ntttquv}wtzu~{z~}|}z~}txsqtwqxrv||}|wv|s~wsz|wvvlrnuyrmonq{x~~z|{|qvxzw}|xt{xwzryxyyyy~~}{|rxzw|uqquqzsqtv|y}y~|wx~z}qsywx|zy|~~~}z|}|w}twuoqzsnvuwnsz{uxus|v~|vv~{|}~zzrzwtrtnx|z{u{|~|vxu{~vx}w{{xsxrzwprkimroztyzsw}wzw|sx{yz{qxrx}~{{}~~}}}x|~yuwxsrvplrr|qsy|}z}|{{yszt}}xvzv}~{|}y|{xzpxzwortxqn|vwxxu|wwzouux~~~}|wy|tzuqqyw{tyvx~|~|{}}|y}|xq{xy~uwxyzvvqiomlmplwx|yzy|xuvxvzuy{yvzr{~{zvw|z~|wz~x|xtzupklozlq}wwxy|y~|{|~{~w}x}xvy{xu}}z}{~~s|{zxptvolzuzztx{{{{oups~~}{~~v~utwwyxputy|~{|vwrusv|rzvv|}|tksjnokinozu~x|tx~vwxs}xxyyw||twwvw|y~}v{zz~sumkqsgrxsxpty|zx}z~~~|~w}zywsp~{~{z~}}}{ts}tlptu}sxzw{|wusmzxz}}}~}}u}v}}sqzvszw~~w||~zywttwrwwr}~~{tvnnsimklqwnu}|}|u~{utr}vw|vy~tyuqxvuwxzzv~xw{nnwrinrrtjsutxyx}~}~~|}}y|wppzwyz~~~~x~|}~s|rssuvvr|vw~vvtrtzs|zu{{|~~z|zsz{trr{y{v|}uyxustq{~{{yxwuxnoqistnipsysv{~wuvvu}sy~|xpztrust|}xxw~tq~toosqnhoqlvrrv{~}}u{}}~~}|~||ptxwqrxwx~|~{{{|{x{vx|yvvuw~swtptxz}ys|uvyuqt{|}y}|utx|xz}z~y}y~vyvtvzz|w|vxyxvvmwyomlrpjl|y}}x{xvwtwy~|z{xqzqqx{yvuz~|z{~z~uwqwtmikhpqissw}~~ts|txzz{~|yyw}yzrrxxqyut{}{~|{{|zwy{ywwwszooxw{}|{|r{xnoovxsvyv}}{{z|zyz||~}zy~yxvz{|}yz|qyzwxxx}{sulrsjioqy~v}zzzwvxx|}z{|z|tr}xzvrx}v|z~|~}szpnmkjkhppm{{{{yvyqu{sx{}}}{~py{vqpozvt~|}y}|{z|}~x{ywsplvrt~{|}}|}qsqlwuos{||}w}}{{{zwu|y~||}xx}x{{wtr{st{wy~}vlzxiknqrstx||x{z{~~xx~z{|{|}xyox{vsv}{{z}|~}}|}{r|yxztw|}s{}ztxz|~{~zxz{~olytsvvy}{{~{}wstuyx~wz~yty|s}~~{z|{{}~{wut~uwyxxwsyxsvw}~wv{u{wy|~~{wuut~|w~}rvzz{qsqlkrw|rv~~u|}v|~~|yw~tryw}z}zvy}z}{s~typikjkorxsw{xz||~{xvuz|wrxw~|||~zt||}{~qqttnjn|ymy~~~vtstuyx~vzwxx{}~po}{|{zupljmzuy~r|zwsx{wuzoryy~yywwww}{{yjqx}}truvpkw~tzxwy{{|ux||{wtrpxumx{x|yvytkx}uwvwvtsyopywy{}x|zy|urux|x{z}~{zzyrvvv}|tqvvnuwtxx|rvyz{{z}w}xz|zxwwwy~}vvvptw|xspqsyw}|||{uu|zurpvynuy{|yyxwz}z~yt{wq||zrsxw|royxxz{~yvusrqqr{tw~{}|~|zw}vyytx~|vrnswmuyz}vywquxz{zw~}z{}yx}}}wltsuz}wqqu|y~zqzx{|{{z{sp|~z{yxxz}}y}~{znu}|{{{zzy~w}zy~ws~wurqqpwypz~z|}{{www~t|}}{~ytpxunxyz~yz~rrrrqru~txz|{~~||vy}tmtsv{zurqrzru{|sz{w{{{zz|tsyw|zx}||{vwykottz{yz{zx}{{}yy~z|{yvrv|v|zyz{{}}z~}ur{}s{}~}|{rx|~{||tqrqpqr||v~xzy}zyyzzz{s|{{|xtrrzuq|~rw{rrrtvyy|st}z|xx|{{x|}xtxnjstx|xvsru|{z}}zt{{{zyvu{qo{~{{}x~~}xsppwmlx}~}|~{x~{{}zzzvrps{qp}}||~wttuytu~~|xx|sz~{}s{yqrqrsu{wv}|~{z|}|y{{{ywz~qrwy~{wusrx~v}~}vr|xsxzxrnyst{}{yy|~|{{upmrrhpy{ywvx{pz~|}}~zzwtpowxr}~y{yyvsomltso|~~|{zqz~{wrquxvpyyw|z{~z~wuwxwvw}ur{|{vsy|u|~xqzzrsuwxwy}qr{|}zx{~y|}{yvtvviq|zxvvx{}}}|uu~zvpqwwxvn{z|~z~zvrollsqly}~{v~{}}{yurpopuzw||{}|{~vtrpoov~qnw~zvz~tz}v}wuzwppu~~pt{}~{yxy|{|z{zwvtzujrx}|y{}zx~~}{rxxyzyvsryrny}}|z|}}|zvrmoxssxy~}~yy}}~~|{zpuzvpox}u}yytrponoywr|}x{zty~~rqrsuv{yx~~{yy~{x|ttuvwttsmw}y}~zxy{}sx|rqvywtzumvz}|{zyzz~|{yurqulkw|{x{~~~~~zwzxvsqpuypt~~zwroqu{uhs~u|vu~yspqtx}v~~}y~vsssstxtovxz}||}~{s{ztvwxxx}sry|}{zz{{x}y{ztmoz|lnv{~~~~}y}xxzwsppxvmv}|}}y{xtpmnvsny~}v~{wsrqppt}yw||||z~truxunpztszy{~~|xz}{||qsuwxx~uw|{|{y}||yw{uvwwvtxvjovx|}||uv}yyyxvuyvnz~||||}y}xqosurtpkv~|}s|}vsqporzws~}||~ztpnmnr{uq{|wx}tqqqru{vny}{}}|w{~yyunlqw|qluy|}|tz~xxxxwwy~sox||{~~|u~yxwuqnpullw}|xzxwtpnuyuz}}~}|}|qtwsljqyrtyy{~sqppprzxz~y|x{yyonnopqx{qu|}~||zxu~yssuwwuwxt}|yy{~}vz|pnqsrpurjr{}~}~{uz~yxwussxuny|y}x~|vrolkmurn|zzytqoqv{}rq}|y}r{yrljlov|tu}|v}{rqrsuxvw}{{|ws{wqqrqqrxwlq{}}}uw{xyvpnu}pv~|zz{}~zx{vuuqmknuoly|{z{wtqont{uv|~z}{z{~somkkluzsw|}x||{xqswwrpz}w}~|~y~|zsmnpqqu{uow|~}xzt{~vuwxxx{~tr{zy|}us~}utsqonrslvy}upqvxvyup{}~zys~yrolkkpwrr~~{uqpops|vy~~x}zxwpnnnqu{tir|~ty}zupqv}wqy}~}{||ry|uttsqquwlku{yx{zywttzzpu}~{}}}wz|urokjnzynqx~}~wwzvqou~yvz{yz~snmmmow|tt|~|~|x{srstuxtv{|}}}|}{vu}xrrtspnsurwzz}}yzrtxywx|vow}~}}~y}~xt|zusqnlotmkv}{}xusrrx}vw|y|x}rmkmqtvskr~wvzspqswvx~~{|wu}wpoopru|yosz}vw{vvvvvx}xnu}||}{sx{vsnilu}ujqzyz|{yvsrv|vr{~}||w}xqnkijovqpz}}zusrrt{|vz|{zw{tyyporrmjpzvt{~}ursuvw|vsz}~{z{{v~vqy{tqrrrrvypoy~xuzwvusrvzuy}}~~~xwrjkorqstmq}}wyzusqqs{~w{z{{~xv}xnjiijoxyrv}|{|ustux|zow~|z|x~yoq|zuniiov}xou}vw{wvvvvx}wow~{|~}}wrxxtrqonrxrp{|x{uspor|zsy~}z{txvqrrmiktxru}~vsrrtxy|}y{uz|ssyrkjklnszwot{w{|wxywtt||y~~{yyz{|yvu}}pjnqrruxqoy|~xw{wutrsxyqt}zy~}|vx}tpmllpxxsz}z}zrprvy{|us~~~x~tnywnihjnv{trz~ywuuvx{ww}}|z}~xopxuonnnoqwrjs~y{vqpuwpw{{{}~z~ypvyvsqomntwpr}|}xtrppt{zty|~z}x}~w{womlmpw~wosz~}~x}~xxzwrrz{y~~~||yx|vmlmnorxwnow{~}y{{zzyy{{sx{{z|{}zuov|vqomkkovtsz||~yrtxywyzss}}z~~yv~xruuomllpwysv}}~uqqqsx}tx~y~}uw}wpopqsvwpkw|v~yrqtzzsy~~~vnuytpoonosumkt{}|{yxvx}~vw~{{}|z~}|yst|zqlihkr|{nmw}z|{wrqu||ux~y}~}z~}~xy~xpmmmpu}{sx~~x{ztsstvzyqu{y~~|xrw}xsrqnkoyxswz|~ytwyzy}xs{}u|~xrox|upnlklquomw}~yvutv}|y~~{uz~~|uqwxojjnrstuor~xx~tpprv}xu|}}|zy|z{~vrxzroopru{}us{xu}yuuuuv{{rt}}~~~zts}~vojhlu}{onx{w}zxvux~~vz{}{zwrt{xokihjovvpu~~yuuux~uv}z~}||tx|unpvsqrpkjq|{v{~ysrsuw|xrx~~|yz~yt}|tpw{vrqqrty|us|~xwzvtrqsz{{~~||vv|wljnqrrutns}~|x~{wutvzy~}yzyx|}~~|wptyrkhhjmtzvqv}{}xwxz{}|tu~~|zy{zw}~{unnz~vlhiov}}tt|xwyvuuux|yqt|}~~}||wrw~{tqpoqu{xs{|y|tqprxyqx~~y~~unswusokimuxrs{~zvuvx~z|z{}tx{tnqvqkjklouyrls|~~{|zwss||x{~{yyz~zy~}{zyvwzummprsuzxpt}~|w}yutrsw{wrx|x~zz{~~xv}xqnnnry}wuz|~|zwtvzzz{zuy~|}{sou{pjhimsyvory}~~}zyyz|}v{~|}x}|wplsxrnlllnstop{vqrw~sqy|}~{{|}xw{~{onx{vspoosyxrv{xitxnmc^fc[cg`grlkxxq{w}~q}z||wyvaUsqmtpvzptmfwsxf[}cZVZd^]jijtrsikzvscsutmtuxxpyx{zxopsuekjQdpixzspsqMpwaMRVT_j[^}sp~tvriapof{fc}tgp}~pdwx}k{{sgavwdyv|r_mcU}mZ[XV`gT_xdqxxxz}tn~slktvir~yvraVp{typsrw`wqhoih|sofVV}|uwxtdgpap~rtikontzmeWTqsy|u|ohzi~]s}}flzvrvmiyrzxzlryyxy}]PUILx{u{dishx~xz|qsswbmde|zuvsioh}|hp}pltiw~v}}}ex|rmgzjzzlxsU_i\dvvw`ptmxwl|imrz[brcoxsubxiexgkpxm{wcvwfnqh~xt{|~tuvymow{VJcc[o|eyiawoowoh|jsr[zvq}ouq{sn~smkk|iandn~|s~g}~wiitugr|tkOdvivnq{brslqq_tegwuUfyg|s~remqyklsytfwucwn_ilcyrz~xxqu|qk{}|exrINmfj{|tfhkumrtggrdzd^~o|~{mxxr~q}orgqr`fo`}~tzqmv{wjqiwodznzUQqqnmrktnstj`v^syeSojo{qisso~p}fv{njiko`frfw~s~qi|vizin{TGcjful{vlqnttm_}_wmXnptvu{{w{myulpmnbgodq|wyn{w{|ym|awoe}|rv\Qkpnxt}rprwslbx\vygTdgmynqursvzfrpiqjicjogqw}xe{rl~|moxXK`ggl|t~tkrysmd~|az~kZbltt}{|~orxl~ylieklhl|~tty{zvv^qohy}ws}gSbmnp|~}mt|soev~bs}gZWem~|ov{uu~vqexhu{jddkmll}yzhoxo|w~umqiQUajhozlo~uqkqlomcW`r~x}{|enr~udejmjpnts}z}pzp]qkvtptaXbprpxjzrnmxug~q`YRiv~wvwrvztimnir{c`gnktmt}zxdmsy||yzrihgVUaqg{~fvtqqrzoufcV]zzvuzennzt_dlloxp|tnz~{m|{q\kp~~|zztilgZ\twu|izrqsrvn|p\^Mg{}z|whuwv|rhpik|e\gnk{t~suzr{mcq~ywrrpri^g`V[yo~nourvusvtj_`Svwj~wptqln}a_jnpuskq}onzqe_pyqsqruiame[jyjx~trwsouvw_]ZT|}jew{q|hwvkfmt[`moz|zox}o|utff|xz}cnflk\]j_Yv~}iwtt|osz{o]fYjevzw{o|rngyj[hps~rokw~miho^j}sphoiri]ipaipw}uswuhx{sb\aX{|iaw{utqrqngj|u_^nq~ypsxl}spmbsy{\ldkh[[mf^y~{r~utt|ksxk_e_q{cz~|syvrrqiy|i\kp{{ppiwygocr\p}~regkipc\lsctu~wuqzni|k`_`g~z`kwqpxmlqgqzo]dov{v|lumpetfj|||nh^idlaXbqajxz~wuqysime`el|hl||nplvnr{tbcnsys}jkxikekia{~~rr^kgog]cuhlvxvqwshkd^alwwghy|fnftmowscajq|~xnn{mrjmmf||yrl\gekbY_pdgk~wuqxrkjgadsyxlp~iohxssytfciq|z}wijshqfhje|}xwo`ijmf^csikhxvrwrnjg_bur{skm|cobtrqushafnyv|pktntqgqi}|tv|o`chhc[]lhfaxwwqvspnhebzt{wvrh{buwtvumcemwsyqglkou`njz|yvxrhbkig_`lplcrzyqwsrueg^wvsv{znz{os_jttqspgbhvp|klnqiktwwxpqj_dgd^[cmldcyrttszgkcorzyv{~lnetxsrrneesq~{mgflwsaquyv|n}yli`jec^clsob~zwpuqxqfhe~wsuzu}|yov`gsuoppkblts~lhjung}zyvtokj_`e`]]dmughyprqrikisuxz|}zqtbmxtoqrifurxrgajxzyemvrxlx|fl^ed`^dkv{i|vnsnw{gkk{uu}}z||}u~yq~l_kvompsilxtzrhbq}{{fyy~q{}g|rbgZb_]\el{xk}rmqm{wjotxx}{w~zw|janwonsvjtx|~x||la`s|zwwexsqskiff]d^_cntvyymnnpnkr|t{z||rwxxutbapskovsm{vuxkaj|}x|rq|nw|~fuw_f^]_[^fn|rrlnmz|mq}~v~|zty{{uq`iupmszqwyu}yoa_s{usziwrp}pk~hcg]b^_eowzylmlspmzs~x|{|outqrd_orjoxvrzx{w}wi`n~yt|ty{}|mzzweyp_e_]]Z`jq|mllqxnx{zz|susxqp_ktnmwztzu{s{wl_g{xovxs~x|nwzzhwubde^`[aiq~omlqynu}uyz|qqo|kr_eqnjtztws|v~xsdh{{qu}vxyrvyyitud`f^]X\fn||qmlq|ruv|~}tsokvdeqpktzwvqyv|vrchyxmr{xutswzwlvugai`^Y]gn}rnlt{qu}~vz}yrqlhudcnniswwqoy{|xuhn|ynt~~usvz}{tnvrh_h`[VZckyysnmv{tx~y}ytsmjwjdnpjswypnw{{uuimxxko{|vnyy{|tpwrl`he\YZdjyyxonv{vu{xzwqri~jqmainiouyqk{}~vyopyzmp{||l}zz|towpnacg[XV`fut~ons|zv|||{qvjxtmuefnlmuzxiwtwrnuynjtykwz~{yymxroh`jaZW^esv}toq~|w~||{}~oskmhrlahljrx}kwwxvxrvzukqvwn~}~u|mtulm`cgZWXakwt|oox|{~|{wptg~tkugcklowxm||{suusvumjuwlw}}~}uwukznmi_hcZY^hvwwor|}|}z}~mrikjlqacjlr{p{z~vtwwxwsknym~}|ypylqvhmd_h^YYamzwsnv~~z~zzlsft}ipqadlovrz|~|sruwwtojpuyo~|{ysovhurgmcai^\_hv}|qoy~wyxqknbxxeom_clqzxzwssxzyrnlwwuv~{yvmqpiyjgj_bg[]am{|x{nr~{}x}mohkrfth_gqv|~y|qqrzzunln~|qz|xvokrhquejh`ge^bhxvoywvwrjkbt}ziira_kt{~~y~urrx~{qmnxzyv|}|vqiokjwiekc`h^_dp~}z}pu~|yw~|mmdo}|tetk^erxz{{woqs}|skmq{|vyz{vshllgupdji`ff`fl}}~vs}u|tynmdhxyyflq`_mvy}{zprt~xlmp{{{y~|{xshjlftrdgkbbg^ciyz{t}w~uzrpeixy|liud]iux~{yopr|ylko|v|xy|y{shikestffmf`gadixyv}~x~uyspchvv{pgsf[ft~y~}zqru{mkpt{y|z}rgjjeusfdlg^e`bguty~z~v|vscmxw|shth[ft~x}wpqvzljp}p{w|zz}rfkgfxqgemh]dbcitr}}{}v|xsanwu{thriZbr}zxqrx}njqn~vz{{rfkhdxriclk]acahqo}x||wdnxu{yjpo]`p|~}yppv~oho|l|vuxytgihbwsjckn`^dchpo~}yz~zghxsx{ojqa\iy~{|~rquukmzssztvyzigj`qwlefof[bdfonu}}|y~oewtu{xipj]cu{|roq{ymiu}{~ksqv{mej`hxnhclm_]ggopp}}z|xdnwqw{phqc^k}{{ppvtlp}{rt{prxxfhe_rtkfeoi[biksm{|~|zngvssy{kloacu{rnpy|okv}yl}ulryndh^evoidhoe\hlstr}|{{|gkvosywhmmbf{~~pnq}zqo|||~orjrz|ife[julhcjod^lpxsx|}{|}wfpuot{whpnem~xllr~woq~xxqlis{vdg_]orjgdmoccqv|s{|{~{lhspnv{qgrkgu~~skoxurz~xsxxgjv~|mdeXeqnhegplajv|yx}}{}|zgpsoqy{lltkm|xmkr}zrt~w~{rmepzuef\\npjgflqgds|~u||}~|kmsomu|rfrqkwx}qkp{~vt}{ywxveiw}yjf`Wjolgfhpmcl{w}~~{rltqnr{zjmvnr}~~xtlmwxuz{uzu|ges{znfbWgnmiggnpfiyy~}}~~~{tmupnoy{lhvppx{~z|vnmw{v{{v}w}kdq{~|picWemligfkphfvy|~}}~}}wpwqpoy|pgutpuz|zzwpmw~{wzxt}xymdp{}{pkbXfmljggiojftw~~}}}|}}usxpoox{oettpqy}z|xroy}|y|vv}yyneq|}zqmaZhlkjgfhnier|u}}~}{~uwzqqpx|resvqmw||{xspy||y{su|{{v~pen{|zrpaZhkjkgggmlfo{t~}~}{}}ux{rppu{uenxrjs}zzuqx}}||st|zu|uhlz{zsscZgkhkggekmgkys{}~}~zwy~uprt{yjjyvkm}y~|vsv~|}}up{x|wwylht{yttjYdkhjjgeholivuzz~x~}zvxppqxzqft{pg||}xut||~zoyxy~szriozyvtq\`jhgjhfemojovv|z~}z}s~~sprt{xlk|yiq}{zzvuw}|~ss|u~wsxnirxusugYgifhjgfhqqmtuy}x~||wvypprvzuirsg~zyxvv{|{pzxwstukkuvsts]_jgfjjgeltqpuxwwzr|voqsx{tkypn|}wwvvw}~|xq|vypsskksrptmYcheejiggpwusv}w}v|x{p~snqsy{tm~nu~|}tuuwx~~}uu}t}|nsrjkroptfZggdekihitzwuxywyxy{tuyqosu{{ss|ozxptuxz~}}}szxutltoikplro]_iddhlhhn{}xtw|v|}v~zp|uopsv|zr{uw~{sqtwz}~~vw|t{mntmimmntf\gfcekkiju}ux{v{ww{}rxzrotu{}wu}tyxmsv{{~}~zu{uvsitrjilksj^dhcdhlijp~xsw}w}{x~st~toqtw}|u~u{~oqv{|}}~v{wtzipumgkjrn`cidcfliilz|pyzy{}y~ttvqqtv|}wzx~|qnu{~||}vzvt}|klwqhijpoadiebdjjhkw~o~}z|zz~ttxrptt{~ywy~}smu{||}w{ut|~mkxsihjqobgiecdijgju}n|{}{|swysqttz~zv|{~~tmv}|{}}w{st|}mkyuigjqlcjiebdhifju{l}|}|~syzsqssy~ztx|~}}vmv~|{}}x{rt{~ojyxkfkqldljfbdgifir{lz}}~ty|urstx~}tu|~}}yms}}z|}y{qsyrivznejpldmkfcceheho}}nw}~~uw~wsrtu}~vpz|~|}~pp{z|}z}uqy~wkp|sggpndkngdcdhgfmxrr~~}wtzurst{|pt{|{|vmw{z}~z}xov{|pjyykcmoehphebdfifjsxq~}{r|}wsrtvvn||z~z}pp|z|z}}srz}wko|sffohdnnfecdghhozty}}}|tu{vrsu{qs|~zy}zxmt|y~}zypv{}|rjtzmbklbiqiedceiikt~|w}z{}py}yuruw}n|||xzx{tmv{{{}vqy{}zpjvxjclg`kpheddfjjox|~~xzwo|~}yuswz|o~~zv{u|}rmxz|ytrzz{wnjvtfclbboofedehmms|}}~~vzps||xtty}vs~|wvwv~yop|{~{{rv{zztkmwocgg]hqkefdfjnpx}~}xu|ymz~||wtw{q}yuyry~unt}|zusxzyxqiquici_`opgefehmpu}||}~swnt{}yutzww}{uvtv}yqp{||}ysv{xyumluoegb\kqkefefjprz|}||ws~tp~}||wux~~t{wu~xsz~}uov}~{}styxxwqjqrhfd[gqnffeeiprx|}}}{zr{vp}~{}wtv}u{xu~zsz}~wqt}~~{tuyywxtkntlgc[eqohefdgnrvz||}z}syxq}|}yuv}v|yv~{sy|zss{~}{utwyvyvmltnib[eqoiefdflruxy~|~{~szws~|}ytu|w|yxzuz|{us{~{|~uuwyvywnltqk`\gqoiefcekqsuw}|~{u|wv|}yuu|}y|zyzvz|}wsy~~z}}uuwxvzypkssm_\gqpjeecdjqrru~|{wzwx}|zutz}y~zzzwz{~ztw|~y|}vvvxvy{skqupa[fpplfecdhpqpr~{~{zxwy~|{vuy~zz{zxyz}|vtz~zz}xvvwww{wllvteYcnpmhedcfnrpo|}||x|xw~||xuw~{|z|xyyzztw|{yywvvwv{zqirxlZ`kpokeecekrpmw}}~~{{zyt}~{{wv{|y}vyxx{}wsy|x~{xwvuvw|wkkwuaYfmomieedhosnp}}~~||zysw|{zwx}~|zxvywx}|tt{yzyywuvvy{shoymY_jnolgeeflsrmy}~~}}{}}ytp|{{yx{}y{svwuw~{st|~x}|yywtuvzyofqxgWbjmnlfffiovro~~}||z}|wpp~~{|zz~~x}zqwutwzrv}|y{zyvsuvzwlfsvbXekmnlghhlrxrt~~|{{y}{slt~{}{}{xsqwssy~wqxy|xzxusuwzshhwn[]ikmmjgijpvwp~~}{zzz~xllz~}|}}~y{xotuqt|}ts{~x|yzvstuxwneovdZdjlnmiikntysu~{y{y{}phs~~{~~{y}ppvrrxxqwx~x{xtruvxrhhvk\ajjmnkhkmrwvq~|yyzy}vgm~~||}zrnutpu~|ss|{zyzzurtvxulfrp`_hjknmhjmquwq~~}zy{y|zhj{~~}{}{totups|~urz|yzy{vrtwwwngoqb`gjjmmiimptuq{~~yx{z{~}ihy~}{~~~}uotvqs{wqw~|yzx{wssxwwpimqdbgjimmihmosspz~zy|{{~~jiz~~}{~}~tpuvqs{wqw|{zzx{wstxxxqkmoeehjilliglnqpo{~zy||{~ij{~~}z}}{~~~uquwrs{xquzz{zx{wstyxxsmmnggijhkkiglnomnz~zy|}{~liy~~}{|}z~~vquwsrzzrsxy{zxzxttyyxupmlhijjhjkigknnlmy~{y{~{}oiw~~}{{}y~}yruwuqx}uquw{{yyzutxzxwrnjikkjihkjhinnlkv~}yz~|{viq~}}|{~y{}}|stvvrt}xqruz{zyzwsuzxwuqigkmjjgjkihmomir~~zy|~z}~lly}~}|}{x~{usuutqx}upsv~~zyyyttxzwwtmdinmjihjkikpnjm}~~}yz}|ytjq{}}}}~x~||xrsttrs{zsprz|zyywtuyxvvrgclpkihhkkjoqmju~{xz}yyolt|~}}~~|x~z|trsssqt}yros~~{{yyvtuxvvvpbcopjigilmmrrmm}~}yxy{wyymmu|~}~|{|y|yqqrqrpu~yqot}{{xxusuwuuul^eqpjihkmoousnq}|xwyyuzslov}}~{}{x~spqqpqqx~wooy|{zwwssuvtvse\ksniihloprvqnz{zwwxwv~|onqx~}~|{yyzoqqppqs{|slq}{{ywvssuttwn]`qrljijnqrvuos}{xwwxuysoptz~~{|zw~ppqpoqqw~xnmy{{zwwtstvtwsb[ltojjhmqstwqpzzwwwvvxpqqw}}|zx{tnrpopqt{{qjt{{{xvussutvwhYetrkjijpstvsn{{zxwvwu}zrsqt{}~}~{yzwnsqoorsy}siq{{{yvwtstuvyk[bttmjiinstusny|zywvwv|{tursy}~~}|zzyosroorsx~~uin}{{|zvwuttvwym\`ruoiihmrtsrmx}zzxwxx|zvwtrx~}}|~}{|yptspossw~~tin}zz|zvxvttwyzl]bsvoihgmqsroly}{{ywxz~yxyurw}}}{~|xrutppstw}~shn|zz{zvxwuuxzzl^brvpihfkqspmky~{|zxx{}~yy{wru{}|{~|zsuvqpstu{ujly{y{{wxxvux{{n`apwrjhfjpsplkw~{}{xx|}~yz}yrty}|~{}}{tuwrprutyxljv}|x{{xwyvuw|{rb`kwtmhghnrqlkt|||yx{~}xy}|uqv||}{|~vtwtppttv}|pjrz|yz{zwyxuu{|vgafsvpjhglrsmkq~~{}zxy~}yv}~yqsy}||{wuuvqprutyulmvzzy{zxwzvux}ymbclvtnihipspknz~|||xw{~ytx}upt{}~{zuuutppttu{|qmrxzzy{ywxxuuz|sgbfpwrkihlstnlu~{|zww||ts|{rqv|}}}~vtturoqttv}ypotwzyzzyvxwtu|ynddhrvrkjjoutnp|~}z{yuw|xqs}yqqv}}yststpoqusw}wqquwzyzzxvxusv|vjdeisvqlklrwunu~|yzwtw|tnt~wpqx}}urtrroorusy~ursuwzxzywvwtsx{ofdglvvplmovxsp}}~}zyyuuyynnz|uos{wqssrqnpstt{ztttuxzxyxvvvrtzviefhowtomorxwqv~}~|yywtwzrjsyqpv~~{qqtrrporusw}}vvtsuzxxxwuwtsxymeghkuwrnoqvxsr|}}zxxtuzujn||uor{rptsqqoqttt{}yvwrsxyxyxvvusvzqfgiipwtooqtxuq}}|}|yyvuywkky~xppx~spttqrppstsx{zwxrquywxxwvvtvzsghjimvupoqswtq{~|}}zywvyxljwyqou}uoturrqprtsvzzxysosyxxyxvwuwzthhkjktvqoqqusqz|}}{yywzxmjwzrnt~}upuvrrqqrtruxyyzsnryxxyyvwwxzshjmjktuqnppsqp{|~~|zzy{wlkyzrms}}}tqvwssrqrsrtwyz{rmqxxxzzwxxzyrhknkjruqnpoqop{|~}z{z{wmkx|tmq{}~vqvxtrsqqsrrux{|tlovxwzzxxyzzsjkoliouropoonoz|}~z{{|wnkv}vmox}yqvxvrtrqrsqsw|}vlmtxwz{ywz{zskkpnjltspppnmnx~||{||ypkq~ypmt~}}|rsxwsssqqsqqt{~zokpwwx{zwy{{umjopkiqtrpqommu|~}z|}zqkny|tmpy}~~uqwxurtrqqrorw~uklsxwz{yw{{xokmqnjlttrqqmmr~~|~{z}|ulkr}yqms}~}}zrswwsstqprqos|{pjnuwx{zwy|zrklpqlioutsspmoy||}z{}yojmv~woow}|uqtwursspproovxmjovwx{ywz|wmjmqpkiqvvttpns{|~|x{|ukinw}vopy~~||sptvtqsrpprnpxwljpvvxzxvz{tjknrpkjsxwvvqpx~z||zx{zqgioy{uos||}wqptusqtqpqqmr}~sjkqvvyywv{xnhlproklvyyxvps}z|{xx|ujfls|yspv~{~rpqttrrsqpqomvzoilsvvyxvx{siiorrmjpy{zxtpz{z{zwzzoeiow~}wrs{||vqpstsqsrpppmq}tkjouuxyww{wlgnqspklv{|zwqv}y{zxy{rgfns|zuqw~~~{zrqqtsrrtpopnmyxoikruvyxwzzogkqrrmjqz|{xrr~zz{zy|viemrx|wrt{~|~srrstsrtqoonlvzqijpuvxyxy{qgjrssnjnx|{xsq~|z{zz|wjelrw~}xssy~}}utsstsrtronmks|sjinuvxyyz|rhjrssojkv||xsp|}z||{|xjflsv}~xtrw{|vutsttrsromlks|skhmuwxzz{|qhkstsojju|{vqo}~{|}}}wjgntv}~xtquy{xwusuussrnlkkt{skhmuwy{||{qhlttrojit{zupo}|}~~}xkgntv{~yuqsvzyxwtuutssoljjq~}tmhksxx{}}|riltuspjhpzzupo{}|~~ymhmtuzzvqqtx{yxtuutssoljjp|~vohjqwx{}~|tjksvsqkhmx{wpox|~zphlsvw}|xtpqv}}zyvtuussqliimwyrkhmuxy}}wljpvurniit{yrou|}|sijpuvz~~zvqpsz{zxttutrrnjhls}}uohjqwx{~ypimtwspkhny{vqs~|~vmhmsuv|}yuppu}{zvsuusrqlhioxzslgltwx||tljpvvrojjs|ztrz~}|zqiiotuw}|ytoqz{zytsuurrojhls{~xqjgmuvy}xojlrwuqnjlw}ztw{{vmhjptux}}zsor|{zwssutrrnihnu|}wpignuvy~}tljntwtrnlozzv|~z{~|rjgkquuy~~zspu~|{zyvrsutqqmhjqx}|unghouvz~zojkpuwtqnms~zy}}y|~wlhinruu{}xqp|zzzzxtqtusqpjhmuz~~yrkfjquw|}ukjmswwsqnox~z~zz}{phhkpttw}}uotzxz{ywrrutqqmhjsx{}|vohgmsuz~ymjlquxurpos}{}~}y|}tihjnruu{ypp}vy{{xtrturpoihpwy|}yrkfipuw}|qilotxwsqpqy}|~{{~wkgjmqttx~{royuw{|yvrtuspojhmvxz|ztmghntw|}sikpswxsrppu}}|~||~ylgknpttv}|tmvuu{}zwssutpokglvxy|{uoigmtw|}tjkpsvxtqpps{||~}ymglnpstu{|sluuu|~{xtsutpnjgluxx{{vpjhmtx}}tjlqtvwtqpoqxz{~~ylhmpqstt{{rluuu}|yutusomjgmvwvz{vpkhmty}}sjmstvwspoopvx{yminpqrtsy{rktvt}~zwtutpljgltwuy{xqlilsz}}tkmstuwtpponsw{~zoinqqrssw|slrws|{xuutpljgkswtwzysnijqy}}ullsuuvupponpuz~|qjlrqqssu|~vnozrw}zvttrmjhjqvutyzvqlinv|}xokquuuvrppnmrx~~~tlkqrqqssyyqnyss~~{xttsojiiotvswzxsojkrz|zqlnuuuvuqqpnov~}xnjmrqqrsu}~upsxpv}zwttqliilrutsxzvqmilu|{umlqvuuvtqrpmqz}{rkkprqqrsw{sqz~rpz|zusspjikotustxyupkinx{wplmtvuuvsrrpnt}}wnjkqrpqstzztuyppz~{yusroiimquurtxytojioxytnlnuvvvwtttqpx|}|skilqrpqsu|zvzunq{}{ytsrmikosutruxwrnhiqywpllqvwvwwuvtqr~~{~wnijnrqprtwxy{pmt}~|{wsspkinrtusrvxvqlhktxrmknsxwwxxvwtqv|{}qjilprppsu{|y}tmox~}|zurqmilqttsqswxsoihowtnkmqwxwwywwvrr~{}tkikosqpquxz}wnnt}~}{xsrojjpstsrquxvqlhltvnkmpuxwwxywwspy|{xliknrrpptw|{}zpnqz~~|ztqokiostsrpswwsoijrvojlptxxvwyxwspu}{{milnqspprvz|}{rnqx~~}{uqokinttrqprwxupkjpuojlqtxxvwyxvsos~|}niloqspoquy~|}|spqw~~{vqnkinttrqpqvyvqmkquoimrtwxvvxwurnr~||njnpqspoquw|~|~{sqrx~{vpnjintsqppqvywrnmqtminsuwxuuwwtplq}}okoqrspnptvz|||trrw~|wpmjintspoopuyxtpnqsnimsuwxuuwwsplo~~qkorrsqnosux{|ussu}}xqmjimttpoootyzuroprnimsvvxutvwtpln}~tlnrrrronruvy{wtsty~zsmkjlruqnooqwzxtqopnijqvvwwtuxvqmmw~ymmqsrrpnptuwz~zuttv~|vokjkpurnnoouzzvsponjhouwwxutwxsols~~rlosrrrnnruuy}vttsx~zsljknsupmnnpwzxuromjgjrvwwwuvywrnp{~~xnlqsrrpnptuvzzttst{}xpkjmputomnnrxzwtqnjhgmtwwxwvxzvqou}~tlmrsqrpoquvw}wssrt{|wokkorvsnmnnryzvtplgegnuxwxwwz{wrrz~|~{pknrsqqppsvvy}tssrt|{vnkmqtvqmlmnsyyusniddhpwxxyxy||vsv}|unlpsrqqpquww|xrsrrv~ysllosutollmovyxtrkebektxxxzy{}{uu|~||{qlmrsqqqqswwx|trsrrz|woknruurmklmqxyvtofachpwyxzzz}}xuy}{tnmqssqrqruwv}~wssrrv~}yrlmruusnkkmovyxuria`fmuyyyzz|}{ux~|}vpnossqqqqsvvyytssrt|~ztmlquutokjlnsyzwtla^dksyyxzz{}|vw}}xqoostrpqqruuwyvttrszzunlpuuspkjlnrx{yvnb]cjrxyxyzz{{vv~}yrqpstrpqqqstuzwuussx~zuokpuuspkjlnry|zwob]ckrxywxzzzzuvysrqttroqpprrtzyvvtsx~ztnlquurokilosy}|xoa]clrxywwyyyxtvyttsttroppppqs{zwvttw~~ztolquuqokikory}~ypb\bkqwywvyxxwtuzuutturoopoopr~{{ywutv|~zuplpuuqolijnqx}{sd\ajpvxwvxxwvtu~|vvvtuspoponoqz|{{xvtuy~{vrnotvromiimpu|~vi]^gotxxvwywvuu{~xvwuutqnppnmpv~{|ywutv||xspnsvtpnjikoryyn`\cmrvxwvyxwuuy~yvwwuusooqomns~{{{wvutx|zvrpqvvrnmiimqt|~tg\]gosxxvxzxvvx~|wvxvttropqnmpx|z|zwutuy|xtrqtwupmkhjnqv}zna[`kquxxwz{xwx}~~~}xvwxvtsqoqqons}zy{xvutuz{xtsruxtomjhjoqw~wj^[blqvyyx||yx{}}|yvuwxutsqprroov~xyzwuutuzzwtttwwsnlihjoqx~}sf\\dnswzyz~}zz|}}yvuvxxutsqqsqor|zwzzvuttvzyvuuvxvqlkhhlps{ymaZ_hptyzz||z}|}~}zutuwywttrqssppw~wxzxuuttxzwuvvwxtnkiginqv}}sf]\cmswz{{~{|~|}{ustwyyvtsrrtqosywzzvuusuyxvvxxxupkjhhlps{~wj`\`iquy{{}|{{~}~}vrtvyzxttsqsroq{{wy{xvutswxvvxxxwqljhgjory~zmb]^foswz{|}z|}~|~vqsvy{yussrrrooy~|xy{yvvtruxvvyyxwrljihjorw~|od^^enrvy{|~}z}}}|wprvy{zussrqqnnx~}yz|{wvtrsvvvyzxwrmjihjosx~|pd`_dmrtxz{|{y}~~{worw{|{usrqppmmx~}z{}|xwtqruuvz{xvrlijikoty~{oea`emqswzzzzw|{worx|}{vrrqoolmx~~{|}}ywtqptuvz|xvrmijikotx~|qfbadkqrvyyyyw{{xoqx|}|wrrqonllv~~}|}~zwuqoruvz|yurmijjkntx}|rgdbdjpquxyxxwz|~zpow|~}ysrqomlks~~}}}~|xvroptvy}{vsojijjmsw{}vkeccgnqswyxxwy~}}rms{~~|urrpmlkp}~}}}~~zwsonrux|}xtplhjjlpvy|xnfedekpquyywxx}vmox}~}ysrrollmw~|}}}~|xuqmnsw{~|vrnjikkmsxzzsjfedgmprxzyxy|~~{pkrz}~|vrsqnlmr}~|}}}~{wtolpuz~ztqlhikkouxyvngfeeioquz{yy{~~vlkt{~~{ussqmmow}{|}|}}zvrnlqw|yspkhikkouxwslgfffjprw||z{~|~|qjku|~}{uttrnnr||y{|||}yuqmlry~xrojgjjkpuvupjggfgkqsy}}{}{wmimx}~}zuutqnqw}}{y|||}|wtplmv|}uqmhgjjlrvtrmighghnrv|~}||||qiiq{~~|xuutoot}|~~xz|||}zurmkpy~yrokgijjnturnkhihgkpty~~|{ukhlw}~}{vuuqnrz~|zx|||}}wsokmv}}tplhgjjlrurnljijhinsv|}}{}xnhjr|~~|xuusopx|{x{}|}~ytplks|wpmjgjklpurnlkjkihkquz~}|}|zoijpz~~}yuusoow}|y{~}}~{tpljp{yqnjhillptsmllkkkhjosx}}{~||pjjoy~~}zvtsonv}z{~~}~|upliozzqnkiilmptslklllkhinrv{{z}|pkkpy~~|zusrnmw}{|~~|tokinzyqmkijmnqtrlkmmmlhhlquyzy{pllpy~|zurpmmw}|}~~|tojimyzqmljjnoqtrkkmnnmigkpswyx~~qllpw~~|{vrpmlu}}}~}vokhlw{snlkknpqtrkjmponjgiorvxx}smmou}~}{xspmls}}~~xpkijs~}unmlkmpqssmhlppolggmqswx{wnmnrz~}|zuqnlp|}}~~~{sliioz~ypmllloqrrnhjoqpnifjpruwz|pmnpv}~|{xromox|{}~~}wojilt|unlllmqrrojglqqpmhgmqsvy~vmmorx~}|{vqonsz{}~~{tljjoy~zrmllloqqplfhnrqokgiorux|}qlmosz~}|{uqoqx{x{}~}yqkjlr|}xplllloqpmiehpsqpkhkqtvzynkmpt{~}}{uqqt}~ww{}}~|xpkknt~|wolllkopnkfdjqtrpkimsux}~tlknqu|~}}{trsy~ztw{}~}{umklpx~ztmllklpolgdemstrojkpuv{xpkmorx}~~~yssv~}{uty|}|yrlkns||xplllknpmhdchqutrnjmsvx|slloqt{~~~|vsu|~{vrv{}~}zunknrx}ytmlmllpoidcenuvspkkpuv|~uoknpsx}~~~ystz{wssz}~~{wpkmqu}zvolmmlopjdbdjswtqmknsuyvqlnqrv|~~~zstz{vssy}~{wrlmpu{{wqmmnmoqkdbdirwurnjlqtwwrnnqrtz}}}{tsy{vsrx}{wrllptz{wrmnonoqkdadirwvrnjkoruwsooqsty|}|zsrz{ussy~zvrlmqty~zwsnopoprkcadirwvqnjjmqt~~wspprssx||{xrrz{ustyzvqlmqtx~}zwtopqqqrkcaeirxvqnjhkor}~xtrprssv{|{ysrz|ustx{vrmlqtw|}zwuqprrqrlc`dipwwrojhimpz~~yutqssstz{{yuqxurtw}|vrolptvz}{xvsprsrrne_chnvxtplhhkov}|utsrssrw{{ywsvwqru{~xsqmosuw{{xvuqqssrpg``fksxwqnjgins|~wttrssrtz{zxut}|rptw~{uronruvy|zwvtqrsrpkb^cinvyupmihkpy|ztttrssrv{{yxuyvopty~xsqoptvwy{xvusqssqnf^_ekrxxspmiint||vtusssrrx|{yxw~}qmquz}vrqpruwwzzwvurqssokc]_gmtyxsqmjkqy{||tstsssrsy}|zzzymlquz{urrqswwwyyvuurqsrmh`\`hnuzxtrnkmt~~z|xrsusssrt{}|{{~sjlrv|ytrsruwwvywuutqrspjc]]ckqxzwtrmlpy~|y}}trtusssrw}}||~zminsx~}vssttwwvwxvuusqsrmf_[_gntzzwtqmmu~yzwrsvtssrtz}||~rikqu{~ytsutvxvuwvuuurrsoga\]dlqx{yusnlq|~{y}ztsuvssrrw}}|~wkintx~{usuvvxwtuwuuvtrsqib]\ajpuzzwuqlnx}}y{{usuwtssru{|{}ymhmsw~~{vsuwvxwttvuuwustrkb^\`hotyzwuqlmv}y{{vtuxussrsy|{|{oilsw|}{vstwvwwtruuvwwtuslb^\_gosxzxtqlkt|z{{wuvxvsrqrwzz|{ojmtw}}zvsuxwwvsquvvxxuvtkb^]`hoswywspjjt|{|{xvwywsrqqvyy|{pkntx}|zvsuxwwvrptvwyzwvtla^^`horvywsojir~|~||{ywwzxsrpptxx{}rlmtx||yvstxxwvspruwy{ywumb^^`fnquxwspkhp~|}}||zxxzytqpprvwzummrwz}ywttxywvsppuvy{{xvpd^^`dlqswxuplhm|~}||~~zyxy{wrqpptwyxplpvy}~ywutwzxvtpnrvx{|ywrh_^_bipruxwrnjkv~}|}}zzyy{ysqpoqvx}{smmrxz{wvtvy{wurnotwy||xumb]_aemqswyuqmjp}}}|~{xyyz{wrqppswzvplotx|~ywvux{zwtpmpvx{}zwqg^]`bhortyytpmmx~|}|xxyyzzuqqppty~|snlouy||wvvwz}zvsomqvx{}yunc\]adjqsvzytqnq~}}~~||vxyyzztqqqqv{xqmlpvy}~{wwwx{}yurmlrvy{|xrj`[^aelrtx|zurqv|}}{yuxyy{ytrrqrx~tpllqwy}}yvxxz}}xtplmswy|{uoe][`chosuz}zust}||||vvyyz{xsrrqt{yqnlmtxz}|wvyz||vrnknuxz|yrja[]bekqtw}}xuux}}~{~~xuxzz{zursrrx~tpmlpwy||yvxz{~~xsokkrwy||umd][`dhotu{~{vvx|||~yuvz{{{xssrqt}wqolntxz|zvwz{}{tpljovx{}xof_[^dglrux}}xvx~~|~|~zvuz|{{ytrrqrzzsqnnsxyzzwvz|}|upmknuy{}zqg`]_dhlptw{|yvx~}~~~zwwz|||zvsrqrx{urqpswyyywvy|~}wqmlnty|}{sic`aeilpsvz{ywy~~~zwxz|}|zvsrqrx|wtsrtwxxxwvy|~~|wqnlouz|}{sjdbcgjmpruxyxwy~}yxy{}}|yvsqprx|xvttuwxxwvwy|~~{vqnmpuz}~{skecehkmoruwxxwz}zyz{}~|zwtrqrw}zxvuuwxxwvwy|}~|wsonpuy}~{umgefilnortwxxwz~zyz|}~|zxusqrv~{ywvvwwwwvwy{}~|xtpoptx|~|voighjlnpqtvwxxz~~{yz{}~}{ywtrru||zywvvwwwvvx{}~}zvrppsw{}}yrlihjlnoqsuwxyz~|zz{}~~|zxvtstz}{zxwwwwwvwxz|~}{xtqqruy|}zuokjklnpqsuwxyz}}zyz|}~}|zxvttw~~|{zxwwwwwvwy{}~}zvsqrtwz|{xrmkklnpqrtvxy{}~{yy{|}~}{zxvuv{}|{yxwwwwwwxz|}}|yusqrtwz{zvqmllnpqrtvxy{||yxy{|}}}|zywvx}}|{zyxwwwwwwy{}~}{xusrsuxz{ytpmmnoqrtuwy{|~}zyxz{}~}}|zyxx{~|{{zyxwwwwwxz|}~}zwusstvxzzwsonnoqrtuwy{|~~|yxyz|}~}}|zyxz}~|{{{zyxwwwwxy{}}}|yvtssuwyzxuroopqrtuvxz|~}zyyz{}~~}}|zyz|}{{{zyxxwwwwxz|}}|zxutttvxzywsqppqstuvxz{}}{yyz{}}~~}|{zz{}|{{{zyyxwwwxz{}}}{ywuttvwyyxurqqrstuvxy{}~|zyz{|}~~}}|{z{~}||||{zyxxwwxy{|}}{ywvuuvwyyxvsrrrstuvxy{|~|zyz{|}~~}}|{z{~~||||{zyxxwwxy{|}}{zxvuuvwyyxvtsrstuvvxyz|~}{zz{}}~~}}|{{{~~}||}|{zyxxxxy{|}|{zxwvvwxyyxvtssttuvvwyz{}}{z{|}}~~}}|{{|~~}}}}|{zyxxxxz{|}|{zxwwwwxyyxwutttuuvwwxz{}}{{{|}~~~}}|{{|~~}}}}}|{yxxxxy{|||{zyxwwwxyzywvuuuuvvwwxy{}~|{{|}~~~}}|{{|~~}}}}}|{zyxxxy{|||{zyxxwxxyzyxvuuuvvvwxxyz|~|{{|}~~~~}}|||}}}}}}}|zyyxxyz||||{zyxxxxyyyxwvvvvvwwxxyz|~}|||}}~~~}}|||}~}}}}}|{zyxxyz{|||{zyyxxxyyyyxwvvvwwwxyyz{}~||||}~~~~}}}|}~~}}}}}}|{zyyyz{||||{zyyxxyyyyxwwwwwwxxyyz{}}|||}}~~~~}}}}~~}}}}}}|{zyyyyz{|||{{zyyxxyyyyxwwwwxxxyyz{|~~}|||}}~~~~~}}~~}||}}}|{zyyyz{||||{zzyyyyyyyyxxwxxxyyzz{|}~||||}~~~~~~~~~~}|||}}}|{zzyzz{||||{zzyyyyyyyxxxxxyyyzz{|}~}|||}}~~~~~~~~~}||}}}||{zzzz{||||{{zzyyyyyyyxxxxyyzz{{|}~~}||}}~~~~~~~~~}|||}}}|{{zzzz{||||{{zzyyyyyyyyyyyyzz{{|}~~}||}}~~~~~}}||}}}||{zzzz{||||{{zzzyyyyyyyyyyzzz{{|}}~}|}}~~~~~}}||}}}}|{{zzz{||||{{{zzzzzzyyyyyyzzz{{||}~~}}}}~~~~~}}}}}}}||{zz{{|||||{{zzzzzzzyyyyzzz{{{||}~~}}}}~~~~~~~~}}}}}}}||{{{{{|||||{{{{zzzzzzyyzzzz{{{||}~~~}}~~~~~~~~~}}}}}}}||{{{{{|||||{{{{{zzzzzzzzzz{{{{||}~~}}~~~~~~~~~~~}}}}}}}|{{{{{||||||{{{{{{zzzzzzz{{{{{||}~~~~~~~~~~~~~~}}}}}}}||{{{||||||||{{{{{{zzzzz{{{{{{||}~~~~~~~~~~~~~~}}}}}}}||{{||||||||{{{{{{{zzzz{{{{{|||}}~~~~~~~~~~~~}}}}}}||{{||||||||||{{{{{zzzz{{{{||||}}~~~~~~~~~~~}}}}}}}||||||||||||||{{{{{{z{{{{|||||}}~~~~~~~~~}}}}}}}}|||||||||||||||{{{{{{{{{||||}}}~~~~~~~~~}}}}}}}}||||||||||||||||{{{{{{{{||||}}}~~~~~~~~~~}}}}}}}}||||||||||||||||{{{{{{{|||}}}}~~~~~~~~~~}}}}}}}}||||||}||||||||||{{{{{|||}}}}~~~~~~~~~}}}}}}}}}|||||}}|||||||||{{{{{|||}}}}~~~~~~~~}}}}}}}}}|||}}}}||||||||||{{{|||}}}~~~~~~~~~}}}}}}}}}||}}}}}||||||||||{||||}}}~~~~~~~~~}}}}}}}}}}}}}}}}|||||||||||||}}}}~~~~~~~~~~}}}}}}}}}}}}}}}}|||||||||||}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}}}||||||||}}}}}~~~~~~~~~~~}}}}}}}}}}}}}}}}}}|||||||}}}}~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}|||||}}}}}~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}||||}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}|||}}}}}~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~}}~~~}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/applications/SprPoP/dist/MUS/mus.idx b/applications/SprPoP/dist/MUS/mus.idx deleted file mode 100644 index 9da6442..0000000 Binary files a/applications/SprPoP/dist/MUS/mus.idx and /dev/null differ diff --git a/applications/SprPoP/dist/README.ru.txt b/applications/SprPoP/dist/README.ru.txt deleted file mode 100644 index e9ca085..0000000 --- a/applications/SprPoP/dist/README.ru.txt +++ /dev/null @@ -1,130 +0,0 @@ -=========================================================================== - SprPoP -- Prince of Persia для Sprinter Sp2000 -=========================================================================== - -Порт игры Prince of Persia Джордана Мехнера (1989) на Sprinter Sp2000. - -Графика взята из VGA-версии для MS-DOS, режим 320x256 с палитрой на 256 -цветов; музыка и звуковые эффекты - оригинальные оцифровки, играются через -канал CBL. В игре все четырнадцать уровней, заставка, экраны истории, -вступление с принцессой и Джафаром, финал и таблица рекордов. - - -ЗАПУСК ---------------------------------------------------------------------------- -Из командной строки DSS, на том диске, где лежит игра: - - sprpop - -Рядом с программой должны лежать её каталоги с данными: - - BG\ тайлы фона KID\ принц - GUARD\ стражи SKEL\ скелет - VIZIER\ Джафар SHADOW\ тень - SND\ звуковые эффекты MUS\ музыка - FONT\ шрифт TITLE\ заставка - PV\ история и вступление LEVELS\ четырнадцать уровней - -Без какого-нибудь каталога игра всё равно запустится, но то, что в нём -лежало, окажется беззвучным или невидимым. - - -УПРАВЛЕНИЕ ---------------------------------------------------------------------------- -Бег и лазание - - Влево / Вправо повернуться или бежать - Вверх прыжок или подтянуться - Вниз присесть или спуститься - Вниз + Влево/Вправо прыжок на месте - Shift подобрать предмет - Shift + Влево/Вправо осторожный шаг - Home или Вверх+Влево прыжок влево - PgUp или Вверх+Вправо прыжок вправо - Вверх на бегу прыжок с разбега - Shift в падении зацепиться за карниз - -Бой на мечах - - Влево / Вправо наступать или отступать - Shift удар - Вверх защита - Вниз убрать меч - (Shift достаёт его снова) - -По ходу игры - - Backspace или Esc пауза и меню - Пробел сколько осталось времени - T держать таймер на экране - F6 быстрое сохранение - F9 быстрая загрузка - Ctrl+A начать уровень заново - Ctrl+R вернуться к заставке - Ctrl+S звук вкл / выкл - Ctrl+M музыка вкл / выкл - Ctrl+V показать версию сборки - Ctrl+Q или F10 выход - -F10 работает везде, в том числе во время заставки. Любая другая клавиша на -заставке или на экранах истории пропускает их и начинает игру. - - -ЧИТЫ ---------------------------------------------------------------------------- -Читы оригинала; в этой сборке они включены по умолчанию. Выключить можно на -экране настроек. - - Shift+L следующий уровень - + / - на минуту больше / меньше - K убить стража - Shift+I перевернуть экран - Shift+W медленное падение - Shift+S вернуть одну единицу здоровья - Shift+T прибавить здоровья (до десяти) - [ / ] подвинуть принца на пиксель - -Двух читов оригинала пока нет: R (воскресить принца) и H / J / U / N вместе -с Ctrl+B (посмотреть соседнюю комнату). - -Отладочные клавиши этого порта, которых в оригинале нет вовсе. Все они под -Ctrl, чтобы не занимать простые буквы из раскладки выше. - - Ctrl+I бессмертие: выкл / в бою / почти во всём - Ctrl+P режим скорости - Ctrl+F остановить кадр и снова пустить - Ctrl+D отладочная строка: уровень, комната, скорость, звук - Ctrl++ / Ctrl+- перейти в следующую / предыдущую комнату по номеру - - -ФАЙЛЫ, КОТОРЫЕ СОЗДАЁТ ИГРА ---------------------------------------------------------------------------- - POP.CFG настройки - POP.SAV быстрое сохранение, POP.BAK - предыдущее - POP.HOF таблица рекордов - -Все они появляются рядом с программой, и любой можно удалить - игра просто -начнёт со значений по умолчанию. - - -ЧТО ЕЩЁ НЕ ГОТОВО ---------------------------------------------------------------------------- -Все четырнадцать уровней пройдены. Порт продолжает развиваться, так что -шероховатости ещё встречаются; те, о которых мы знаем, ведутся в списке -багов проекта. - -Против оригинала не хватает: чита R (воскресить принца), читов H / J / U / N -и Ctrl+B для осмотра соседних комнат и клавиши снимка экрана (F12). - - -БЛАГОДАРНОСТИ ---------------------------------------------------------------------------- -Prince of Persia придумал и написал Джордан Мехнер в 1989 году. - -Механика этого порта сверялась с SDLPoP (github.com/NagyD/SDLPoP) - это -самое точное прочтение того, как ведёт себя оригинальный движок. Графика -взята из версии для MS-DOS, звуковые эффекты - из её файлов DIGISND, музыка -- из записей саундтрека DOS-версии. - -Сам порт собран нашим компилятором sprinter-cc на основе SDCC для Sprinter -Sp2000. diff --git a/applications/SprPoP/dist/README.txt b/applications/SprPoP/dist/README.txt deleted file mode 100644 index 0599c43..0000000 --- a/applications/SprPoP/dist/README.txt +++ /dev/null @@ -1,134 +0,0 @@ -=========================================================================== - SprPoP -- Prince of Persia for the Sprinter Sp2000 -=========================================================================== - -A port of Jordan Mechner's Prince of Persia (1989) to the Sprinter Sp2000. - -The graphics are the VGA ones from the MS-DOS release, in 320x256 with a -256-colour palette; the music and the sound effects are the digitised -originals, played through the CBL channel. All fourteen levels, the title -sequence, the story screens, the intro with the Princess and Jaffar, the -ending and the hall of fame are in. - - -RUNNING IT ---------------------------------------------------------------------------- -From the DSS prompt, on the drive the game sits on: - - sprpop - -The game needs its data directories next to the program: - - BG\ background tiles KID\ the prince - GUARD\ guards SKEL\ the skeleton - VIZIER\ Jaffar SHADOW\ the shadow - SND\ sound effects MUS\ music - FONT\ text font TITLE\ title screens - PV\ story and intro screens LEVELS\ the fourteen levels - -If a directory is missing the game still starts, but whatever lived there -will be silent or invisible. - - -CONTROLS ---------------------------------------------------------------------------- -Running and climbing - - Left / Right turn, or run - Up jump, or climb up - Down crouch, or climb down - Down + Left/Right hop - Shift pick things up - Shift + Left/Right careful step - Home or Up+Left jump to the left - PgUp or Up+Right jump to the right - Up while running running jump - Shift while falling grab onto a ledge - -Sword fighting - - Left / Right advance or retreat - Shift strike - Up block - Down put the sword away - (Shift draws it again) - -While the game runs - - Backspace or Esc pause and open the menu - Space how much time is left - T keep the timer on screen - F6 quicksave - F9 quickload - Ctrl+A restart the level - Ctrl+R back to the title screen - Ctrl+S sound on / off - Ctrl+M music on / off - Ctrl+V show the build version - Ctrl+Q or F10 quit - -F10 works everywhere, including the title sequence. Any other key during -the titles or the story screens skips straight into the game. - - -CHEATS ---------------------------------------------------------------------------- -The cheats of the original, and they are on by default in this build. You -can turn them off on the Settings screen. - - Shift+L go to the next level - + / - one minute more / less - K kill the guard - Shift+I turn the screen upside down - Shift+W slow falling - Shift+S restore one hit point - Shift+T one more hit point (up to ten) - [ / ] nudge the prince one pixel - -Two cheats of the original are NOT here yet: R (resurrect the prince) and -H / J / U / N with Ctrl+B (look at the neighbouring room). - -Debug keys of this port, which the original has no equivalent for. They -all sit under Ctrl so that no plain letter is taken away from the layout -above. - - Ctrl+I immortality: off / in combat / almost everything - Ctrl+P speed mode - Ctrl+F freeze the frame, and unfreeze it - Ctrl+D debug bar: level, room, speed, sound, timer - Ctrl++ / Ctrl+- jump to the next / previous room by number - - -FILES THE GAME WRITES ---------------------------------------------------------------------------- - POP.CFG settings - POP.SAV quicksave, and POP.BAK the one before it - POP.HOF hall of fame - -All of them appear next to the program, and all of them can be deleted -- -the game will simply start from its defaults again. - - -WHAT IS NOT FINISHED ---------------------------------------------------------------------------- -All fourteen levels have been played through. The port is still under -active development, so rough edges do turn up; the ones we know about are -tracked in the project's own bug list. - -Missing against the original: the R cheat (resurrect the prince), the -H / J / U / N and Ctrl+B cheats for looking at neighbouring rooms, and the -screenshot key (F12). - - -CREDITS ---------------------------------------------------------------------------- -Prince of Persia was created by Jordan Mechner in 1989. - -The mechanics of this port were checked against SDLPoP -(github.com/NagyD/SDLPoP), which is the authoritative reading of how the -original engine behaves. The graphics come from the MS-DOS release, the -sound effects from its DIGISND files, and the music from recordings of the -DOS soundtrack. - -The port itself is built with sprinter-cc, our SDCC-based toolchain for the -Sprinter Sp2000. diff --git a/applications/SprPoP/dist/SND/snd.arc b/applications/SprPoP/dist/SND/snd.arc deleted file mode 100644 index 77fcd03..0000000 Binary files a/applications/SprPoP/dist/SND/snd.arc and /dev/null differ diff --git a/applications/SprPoP/dist/SND/snd.idx b/applications/SprPoP/dist/SND/snd.idx deleted file mode 100644 index 950900d..0000000 Binary files a/applications/SprPoP/dist/SND/snd.idx and /dev/null differ diff --git a/applications/SprPoP/docs/BUGS_OPEN.md b/applications/SprPoP/docs/BUGS_OPEN.md deleted file mode 100644 index 5bd5f4a..0000000 --- a/applications/SprPoP/docs/BUGS_OPEN.md +++ /dev/null @@ -1,1484 +0,0 @@ -# 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) | зацеп в падении срабатывает менее стабильно, чем в оригинале | **после всех уровней** | подозрение на клавиатуру ПОДТВЕРДИЛОСЬ частично: кража байт DSS устранена 2026-08-28, перепроверить | -| [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-28 (байты крал KEYSCAN DSS); ждёт полевой проверки | -| [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-28**: ветка удалена, сцена на единых часах по лучу | -| [PV-RENDER-BOUND](#pv-render-bound) | сцена с принцессой рисуется дороже бюджета: ~49 тиков/с вместо 60, музыка уезжает от картинки | производительность | **исправлено 2026-08-28**: кадр разложен на блоки по интервалу, удешевлять не понадобилось | -| [MUS-LEFT-TEAR](#mus-left-tear) | `pop_mus_left` (16 бит, пишет прерывание) читается из главного цикла неатомарно — возможен ложный «трек кончился» | **потенциальный** | открыт: хазард показан рассуждением, в прогоне не проявился | -| [CLIMB-VS-GUARD](#climb-vs-guard) | Кид подтягивается к стражу этажом выше: у нас удар засчитывается и убивает, в оригинале Кид просто срывается без урона; страж при этом способен провалиться сквозь пол вслед за Кидом | бой/физика | открыт: цепочка удара сверена — совпадает, расходятся входные данные; толкование звука ИСПРАВЛЕНО 2026-09-02 (звук 11 = промах, а не «любой укол») — главный подозреваемый снова ПОРОГ ДИСТАНЦИИ | -| [HP-BAR-RESTART](#hp-bar-restart) | после гибели и Ctrl+A на ОДНОЙ из двух страниц остаётся полоса HP по результатам боя | дабл-буфер/UI | КОРЕНЬ НАЙДЕН, фикс есть, ждёт проверки (2026-08-31) | - ---- - - -## 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-28.** Байты у нас крал сам DSS — см. -> раздел «Корень» ниже. Правка в `libc/irq/_irq_tramp.c`. Запись остаётся -> открытой до полевой проверки и решения по страховке-таймауту. - -## Корень (2026-08-28): скан-коды воровал KEYSCAN DSS - -Пришёл из чтения исходников DSS (`docs/sources/Estex-DSS/DSS/`, подсказка -пользователя). Обработчик прерывания DSS живёт в IM1 по `0x0038` -(`DSS-MAIN.ASM`), и его `INTx38_Handler` ПЕРВЫМ ДЕЛОМ делает `CALL KEYSCAN`, -а `KEYSCAN.RESCAN` (`KEYINTER.ASM`) вычерпывает FIFO SIO досуха. - -Наш трамплин проверял «есть ли клавиатурный байт» ОДИН раз, на входе в -прерывание, а хвост кадрового пути уходил в DSS (`jp 0x0038`). Значит -скан-код, прилетевший ПОЗЖЕ этой проверки, доставался DSS и уезжал в его -буфер — при этом **Rx-overrun не взводится**, потому что байт не потерян -железом, он просто прочитан не тем владельцем. Отсюда и загадка -исходного диагноза: бит залип, а `_kbdraw_overrun == 0`. - -**Измерено в MAME (сессия 2026-08-28, брейки + `totalcycles`):** - -| Что | Значение | -|---|---| -| окно от входа трамплина до чтения байта DSS | **738 тактов ≈ 34 мкс**, на КАЖДОМ кадровом прерывании (50/с) | -| из них пролог самого DSS (`jp 0x38` → `.Handler` → `call INTx38_Handler` → 11 `push` → `call KEYSCAN`) | 481 такт ≈ 22 мкс | -| цепочка кадровых хендлеров у SprPoP | пуста (`_irq_chain_n = 0`) — окно уже минимальное | -| расчётная частота на железе при удержании клавиши (typematic 30 Гц = 60 байт/с) | потеря байта раз в ~10 с | - -Последняя строка сходится с полевым наблюдением пользователя: залипание -стрелки ловилось на ур. 14, когда Кид долго бежит влево через несколько -комнат. - -**Пойманный случай (MAME, старая сборка).** Стоп на `IN A,($18)` внутри -`KEYSCAN` при `_kbdraw_active = 1`: DSS прочитал `A = 0x74` — make стрелки -«вправо», при том что `_kbdraw_pending = 2` (EXT), то есть префикс `E0` уже -разобрали МЫ. Посылка `E0 74` разорвана пополам между двумя владельцами -канала. Злейший подвид — кража `E0` у break'а `E0 F0 6B`: тогда мы гасим -бит в PLAIN-половине, а взведённый бит в EXT остаётся навсегда, и это ровно -залипшая стрелка `0x6B`/`0x72` из исходного разбора. - -**Правка.** Пока raw-канал открыт, кадровый путь трамплина НЕ вызывает -обработчик DSS вообще (приватный `RETI` вместо `jp 0x0038`) — окно ровно -ноль. Промежуточный вариант «проверить FIFO перед chain'ом» пробовали и -отвергли замером: он снимает лишь свою треть окна (738 → 481 такт), кражи -продолжались. Цена принятого варианта: пока открыт raw, у DSS замирает -кадровое обслуживание — опрос мыши и мигание текстового курсора (PoP не -нужно ни то, ни другое; зафиксировано в ``). - -**Проверка (MAME 2026-08-28).** До правки брейк на входе `KEYSCAN` с -условием «страница точно DSS + наш raw открыт» срабатывал мгновенно (50/с); -после правки молчит, а положительный контроль — брейк на нашем приватном -`RETI` — срабатывает сразу. ВАЖНО про методику: первый брейк без проверки -сигнатуры байтов дал ЛОЖНОЕ срабатывание — в W0 периодически лежит -драйверная страница DSS, и по адресу `0x0570` там своя команда. - -**Что осталось открытым.** Кража устранена, но потерять break всё ещё -может настоящий Rx-overrun SIO (редкий: FIFO вычерпывает `kbd_raw_poll` из -главного цикла). Значит вариант A из таблицы ниже — таймаут в циклах -«ждём отпускания всех клавиш» — остаётся разумной страховкой от КЛАССА -отказа «вечный клин», и его стоит сделать отдельно. - -## Исходный разбор (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): отложено до готовности всех уровней.** -> Механика работает, это вопрос ощущения, а не проходимости. - -> **Обновление 2026-08-28.** Гипотеза «дело в клавиатурном модуле, а не в -> физике» подтвердилась ЧАСТИЧНО: найдена и устранена кража скан-кодов -> обработчиком DSS (разбор — [KBD-STUCK-WAIT](#kbd-stuck-wait)). Украденный -> make Shift'а или стрелки — это ровно «нажал, а не сработало», и на -> железе при удержании клавиши такое случалось раз в ~10 с. Перепроверить -> ощущение зацепа на новой сборке ДО того, как лезть в физику. - -**Наблюдение (пользователь).** Вход на уровень 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 не включается — СНЯТ 2026-08-28 - -> **Закрыт удалением самой ветки.** Сцена с Джафаром переведена на ЕДИНЫЕ -> часы — кадры луча (`intro_pv_animated`): вторая шкала по насосу и гонка, -> которая её выбирала, убраны целиком. Насос как часы был нужен потому, -> что кадр сцены рисовался дольше своего интервала; теперь отрисовка -> разложена по интервалам (`pv_restore_bg`), и счёт кадров честен. -> Побочный эффект гонки — «кода реплики каждый прогон в другом месте» — -> исчез; проверено пользователем. Ниже — исходный разбор. - -**Замер (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. Сцена с принцессой не укладывается в свой бюджет кадра — ИСПРАВЛЕНО 2026-08-28 - -> **Причина оказалась не в цене кадра, а в том, ГДЕ отсчитывался интервал.** -> Кадр рисовался целиком, и только потом ждали vsync и ещё четыре — то есть -> отрисовка ПРИБАВЛЯЛАСЬ к делителю. Теперь она разложена на блоки, каждый -> из которых влезает в кадровый интервал, и после каждого ждём vsync -> (`pv_restore_bg` + раскладка кадра в `cut_run` / `pre_room_animated` / -> `intro_pv_draw_frame`); фон восстанавливаем только по картинке, 200 строк -> вместо 256. Удешевлять сцену не понадобилось: отрисовка ~2 кадра при -> бюджете 5. Подгонка `PV_MAGIC_LEAD` снята (0) — проверено пользователем: -> кода попадает в нужный кадр. Ниже — исходный замер. - -**Замер (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) это плюс. - - -## CLIMB-VS-GUARD - -Кид стоит рядом 2,6, страж — этажом выше на 1,6. Кид тянется подтянуться -на 1,6. - -**Оригинал (SDLPoP).** Страж машет мечом, Кид срывается обратно на 2,6 -**без потери HP**. - -**У нас.** Тот же замах убивает Кида на месте. Если Кид безоружен, это -мгновенная смерть (ветка «заколот без меча», HP разом в ноль) — и дальше -тело падает, что до 2026-08-31 давало отдельный симптом «мёртвый -вприсядку» (закрыт развилкой в `land()`, см. коммит того же дня). - -**Наблюдение сверх того (пользователь, 2026-08-31).** В этой же связке -поведение расходится сильнее: страж провалился с ряда 1 на ряд 2 ВМЕСТЕ с -Кидом — причём Кид ушёл в провал в колонке 7 (там дыра), а страж между -колонками 5 и 6, где на ряду 1 пол ЕСТЬ. То есть страж проваливается -сквозь целый пол. Это может быть тем же корнем, что и удар: обоим нужен -корректный ряд/колонка персонажа в момент подтягивания. - -**Что уже проверено статически (2026-08-31).** Вся цепочка засчитывания -удара сверена с оригиналом и совпадает ДОСЛОВНО: - -- гейт по кадру Кида (нулевой кадр и кадры выхода по лестнице пропускают - разбор целиком) — `pop_check_sword_hurting`; -- приоритет стража при встречном попадании (отметка «ранен» у Кида - снимается) — `pop_check_sword_hurt`; -- требование ОДНОГО ряда у бьющего и жертвы — `check_hurting`; -- кадры удара (укол/третий удар), диапазон дистанции (8 для безоружной - жертвы, 12 для вооружённой, верхняя граница 29); -- формула расстояния `pop_char_opp_dist` — совпадает с `char_opp_dist`. - -Значит расходятся не правила, а ВХОДНЫЕ данные: ряд, колонка или X -персонажа во время виса/подтягивания. Наиболее вероятный кандидат — -дистанция: висящий вплотную под кромкой в оригинале не дотягивает до -нижнего порога 8, а у нас пара пикселей переводит его через порог. - -**Как чинить.** Не гадать — замерить на живой сцене: в момент, когда -`Opp.action` становится «ранен», снять у обоих `curr_row`, `curr_col`, `x`, -`direction` и сравнить с теми же величинами в SDLPoP на том же кадре -(метод — lldb-трасса живого SDLPoP, как в -[[sdlpop_odd_pixel_char_x]]). Отдельно снять, почему страж теряет опору: -`get_tile_at_char` под ним в кадре провала. - -**Уточнение пользователя (2026-08-31, важное).** Оригинальное поведение — -Кид сорвался без потери HP — ВОСПРОИЗВОДИТСЯ и у нас: исход сильно зависит -от того, где именно стоит страж. То есть расхождение не абсолютное, а -пороговое, и это прямо подкрепляет версию про ДИСТАНЦИЮ: пара пикселей -переводит расстояние через нижний порог засчитывания удара (8 для -безоружной жертвы), и удар из «мимо» становится смертельным. Значит -искать надо не потерянную ветку, а сдвиг координаты/порога — сравнивать -`pop_char_opp_dist` в момент замаха при ОДИНАКОВОЙ расстановке. - -**ЗВУК КАК ИНДИКАТОР ПУТИ (пользователь, 2026-08-31) — лучшая зацепка.** -В той же сцене оригинал играет ВЗМАХ (звук 11, «клинок движется»), а у нас -слышен звук, похожий на упор Кида в стену (звук 8, `bumped`). - -Набор звуков проверен и НЕ виноват: в `assets/packed/SND/snd.idx` слот 11 -на месте и содержит собственный короткий сэмпл (1280 Б), слот 8 — другой -(1664 Б). Раскладка не сдвинута. - -**ПОПРАВКА 2026-09-02 — прежнее толкование звука было ОШИБОЧНЫМ.** - -Здесь стояло, будто оригинал играет звук 11 «сразу на кадре укола, ДО -проверки расстояния», то есть при ЛЮБОМ уколе, попал тот или нет. Это -описание НАШЕГО кода, а не оригинала: ровно так звук стоял у нас, внутри -ветки «не парировано» и до присвоения `Opp.action = 99_hurt`. В SDLPoP -(seg002:1039..1044) он стоит в ХВОСТЕ `check_hurting` и защищён условием -`Opp.action != actions_99_hurt`: - - if (Char.direction == dir_56_none) return; // Fix looping "sword moving" sound. - if (Char.frame == frame_154_poking && Opp.frame != frame_161_parry && - Opp.action != actions_99_hurt) - play_sound(sound_11_sword_moving); - -То есть звук 11 в оригинале — это индикатор **ПРОМАХА**, а не укола: на -попадании его нет (играет боль), на парировании нет (`Opp.frame` уже 161). -Расхождение найдено и исправлено 2026-09-02 по симптому «непрерывный взмах -клинка у безоружного Кида на подъёме» (звук опознан в живой сессии по -`snd_curr == 11`; указатели насоса при этом сошлись с `snd.idx` байт в -байт, то есть тракт звука был исправен). Заодно приехал и пропущенный -ранний выход по `dir_56_none` — тот самый, что автор SDLPoP подписал -«Fix looping sword moving sound». - -**Что из прежних выводов уцелело, а что нет.** - -* УЦЕЛЕЛО: наблюдение «в оригинале слышен 11, у нас 8» — это данные, и они - остаются. Арифметика приоритетов их подкрепляет: `snd_prio[11] = 0x12` - (18) против `snd_prio[8] = 0x4B` (75), меньше значит важнее, поэтому - взмах не мог быть заглушён упором в стену. При ТОГДАШНЕМ коде звук 11 - звучал на любом уколе, значит его отсутствие действительно означало, что - до кадра укола дело не дошло. -* ОТПАЛО: вывод «оригинал ведёт стража атакой с промахом, а мы — - столкновением» опирался на неверную посылку и больше не следует из звука - сам по себе. -* УСИЛИЛОСЬ: то, что в оригинале в этой сцене слышен ИМЕННО 11, теперь - доказывает, что укол у стража СОСТОЯЛСЯ и ПРОМАЗАЛ. А это ровно версия - про ПОРОГ ДИСТАНЦИИ (см. выше про пару пикселей и нижний порог 8), а не - про потерянную ветку. Прежняя «версия про столкновение» её вытеснила - зря — возвращаем дистанцию в главные подозреваемые. - -**ВНИМАНИЕ тому, кто вернётся к этому багу: диагностика по звуку с -2026-09-02 ЗНАЧИТ ДРУГОЕ.** Старые заметки прогона будут вводить в -заблуждение — переснимать сцену заново. Новая таблица: - -| слышно | что это значит | -|---|---| -| 11 (взмах) | укол состоялся и ПРОМАЗАЛ — поведение оригинала в этой сцене | -| 13 (боль Кида) | укол ПОПАЛ, `Opp.action = 99_hurt` | -| 8 (упор в стену) | укола не было вовсе, сработало столкновение | -| тишина | `check_hurting` вышел раньше: меч не вынут / ряды не совпали / кадр не 153-154 | - -На саму механику урона правка 2026-09-02 НЕ влияет (тронут только звук), -поэтому расхождение «удар убивает» остаётся открытым как было. - -**Решение пользователя:** отложено на будущее (2026-08-31) — поведение в -этой связке расходится широко, чинить нужно целиком, а не по одному -симптому. - - -## HP-BAR-RESTART - -**Симптом (пользователь, 2026-08-31).** Гибель на 2-м уровне, возврат к -началу по Ctrl+A: строка HP на одном из двух экранов остаётся отрисованной -по результатам боя. Замечание там же: «в начале уровня она отрисовывается -только в один экран». - -**Что уже известно.** - -Ctrl+A у нас — РЕСТАРТ УРОВНЯ (`sprpop_cold.c`, тот же путь, что пункт меню -`POP_MENU_RESTART_LEVEL`), а не возврат в заставку. - -Перерисовка полосы устроена счётчиком страниц, а не флагом: -`pop_hp_invalidate` ставит `hp_todo = 2`, и `pop_hp_draw` тратит по одной -странице за кадр (`src/pop_cdraw.c`). Все четыре холодных пути -(старт уровня, вход в комнату, быстрая загрузка, возврат из меню) -инвалидацию зовут — механизм на месте. - -**Главный подозреваемый — зона статус-строки.** Полоса HP делит строку со -статус-текстом, и пока текст висит (`pop_status_ticks != 0`), стирание -чистит ТОЛЬКО края — левее `POP_STATUS_L` и правее `POP_STATUS_R`, — -а середину не трогает, чтобы текст не мигал. При старте уровня текст как -раз висит («LEVEL 2»), поэтому всё, что от прошлой полосы попало в -середину, там и остаётся. Полоса стража рисуется справа налево от 314 и -при большом запасе HP заходит именно в эту незачищаемую зону. - -**КОРЕНЬ (2026-08-31).** Счётчик считает СТРАНИЦЫ, но тратился по КАДРАМ, -а это не одно и то же: между двумя вызовами отрисовки переворота может не -быть, и оба прохода уходили в одну страницу — вторая оставалась с -делениями прошлого боя. Фикс: проход тратится только когда -`gfx_get_draw_page()` отличается от страницы прошлого прохода (первый -проход идёт всегда). Полная чистка всей ширины при инвалидации добавлена -там же — старая полоса могла заходить под статус-текст, где щадящая чистка -её не трогала; текст сразу перезапрашивается, чтобы не пропал. - -**Что проверить при взятии в работу.** - -1. Значения `POP_STATUS_L`/`POP_STATUS_R` против реальной ширины полос: - при скольких делениях полоса стража (или Кида) заходит под текст. -2. Уходят ли оба прохода `hp_todo` в РАЗНЫЕ страницы на старте уровня — - если между ними нет переворота, обе перерисовки лягут в одну. -3. Возможное решение: на старте уровня чистить полосу во всю ширину - независимо от статус-текста (текст всё равно перерисовывается заново - через `pop_status_invalidate`), либо запоминать максимальную ширину - прошлой полосы и стирать по ней. diff --git a/applications/SprPoP/docs/KID_PLAN.md b/applications/SprPoP/docs/KID_PLAN.md deleted file mode 100644 index cc24ab3..0000000 --- a/applications/SprPoP/docs/KID_PLAN.md +++ /dev/null @@ -1,214 +0,0 @@ -# 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 deleted file mode 100644 index 9af3f17..0000000 --- a/applications/SprPoP/docs/PORT_PLAN.md +++ /dev/null @@ -1,555 +0,0 @@ -# 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 deleted file mode 100644 index a08fed1..0000000 --- a/applications/SprPoP/docs/PoP/APPLEII_RESOURCE_FORMAT.md +++ /dev/null @@ -1,283 +0,0 @@ -# Формат ресурсов 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 deleted file mode 100644 index 580e92b..0000000 --- a/applications/SprPoP/docs/PoP/MSDOS_RESOURCE_FORMAT.md +++ /dev/null @@ -1,301 +0,0 @@ -# Формат ресурсов 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 deleted file mode 100644 index 0d5c219..0000000 Binary files a/applications/SprPoP/docs/PoP/POP-DAT-FormatSpecifications.pdf and /dev/null differ diff --git a/applications/SprPoP/docs/PoP/POP-DAT-FormatSpecifications.txt b/applications/SprPoP/docs/PoP/POP-DAT-FormatSpecifications.txt deleted file mode 100644 index c2aa09a..0000000 --- a/applications/SprPoP/docs/PoP/POP-DAT-FormatSpecifications.txt +++ /dev/null @@ -1,2345 +0,0 @@ -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 deleted file mode 100644 index 572563f..0000000 --- a/applications/SprPoP/docs/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# `SprPoP/docs` — индекс - -Актуальность на 2026-08-26 (перенос из `applications/PoP/roomtest`). -Сюда взято только то, что ещё читают. Исполненные планы остались архивом -в `../../PoP/docs/`, закрытые задачи и баги — в `../../PoP/roomtest/`. - -## Входные точки - -| Документ | О чём | -|----------|-------| -| [`TASKS_OPEN.md`](TASKS_OPEN.md) | **Что берётся в работу сейчас** — начинать отсюда | -| [`BUGS_OPEN.md`](BUGS_OPEN.md) | Открытые баги и незакрытые оптимизации | -| [`../CHANGELOG.md`](../CHANGELOG.md) | **Что менялось по релизам** глазами игрока; версия и дата берутся из git-тега | -| [`impl_diff.md`](impl_diff.md) | **Осознанные расхождения с SDLPoP**: где сделано не дословно и почему. Новое расхождение — записью сюда, а не только комментарием в коде | -| [`keys.txt`](keys.txt) | **Целевая раскладка управления**, к которой подгоняем SprPoP | -| [`keys_plan.md`](keys_plan.md) | План перевода на неё: что совпадает, что переназначить, каких функций нет вовсе | -| [`../assets/orig/README.md`](../assets/orig/README.md) | **Откуда берутся исходные данные**: `make fetch` (SDLPoP + музыка), зачем нужен и почему необязателен `MSDOS/`, `SND_SRC` | - -## Производительность - -| Документ | О чём | -|----------|-------| -| [`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: разбор и архитектура | -| [`music_runtime_index_plan.md`](music_runtime_index_plan.md) | Будущее улучшение: runtime-индексы музыки и PCM-эффектов, смена аудионаборов без перелинковки EXE | -| [`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 deleted file mode 100644 index c4e2dbd..0000000 --- a/applications/SprPoP/docs/TASKS_OPEN.md +++ /dev/null @@ -1,1678 +0,0 @@ -# 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 — берётся в любой момент - -### TIME-SPEED. Часы бегут быстрее в FAST/FASTEST — СДЕЛАНО 2026-08-31, остался вариант 3 (RTC) - -**Наблюдение (пользователь, 2026-08-31):** «при режиме FAST/FASTEST время -начинает бежать быстрее — похоже, время мы считаем в наших логических -кадрах, и если они отрисовываются чаще, то и время быстрее». - -Так и есть. `pop_timer_tick` (`src/pop_timer.c`) уменьшает счётчик на -КАЖДОМ логическом кадре — ровно как оригинал. Но длина логического кадра -у нас зависит от режима скорости (`src/pop_pace.h`): - -| режим | обычный кадр | бой | -|---|---|---| -| NORMAL | 4 кадра луча (81,9 мс) | 5 (102,4 мс) | -| FAST | 3 (61,4 мс) | 4 (81,9 мс) | -| FASTEST | 3 (61,4 мс) | 3 (61,4 мс) | - -Разная длина кадра в игре и в бою — ПОВЕДЕНИЕ ОРИГИНАЛА (подтверждено -пользователем), и часы, идущие в бою медленнее, трогать не нужно. -Расхождение только в наших добавочных режимах: NORMAL повторяет оригинал, -а FAST/FASTEST ускоряют всё разом, включая ход часов — минута игрового -времени проходит примерно на треть быстрее реальной. - -**Варианты.** - -1. Оставить как есть: быстрый режим ускоряет игру целиком, это честно и - предсказуемо. Ноль работы и ноль риска. -2. Развязать часы от темпа: тикать не по логическому кадру, а по - накопленным кадрам ЛУЧА (4 кадра луча = 1 тик). Тогда минута остаётся - минутой в любом режиме, а в бою часы по-прежнему замедляются, как в - оригинале. Цена — счётчик-накопитель в `pop_timer_tick`. - -3. **Часы от RTC** (идея пользователя, 2026-08-31). Брать время из - часов реального времени, а не считать кадры вовсе. - -**Почему третий вариант интереснее, чем кажется.** Погрешность есть уже -СЕЙЧАС и без всяких режимов: логический кадр NORMAL — 4 кадра луча, а это -81,93 мс, то есть 12,2 кадра в секунду вместо ровных 12. Оригинальная -минута из 720 тиков проходит у нас за 58,99 с — почти на секунду быстрее. -За час игры набегает около минуты. Ни один из первых двух вариантов этого -не лечит: они выравнивают режимы между собой, но обе шкалы остаются -привязанными к лучу, а луч не кратен игровой секунде. - -RTC (`ESTEX $21 SYSTIME`, memory `sprinter_systime_dow`) даёт абсолютную -шкалу и снимает накопление полностью. Подводные камни, которые надо -решить при взятии в работу: - -- вызов ESTEX стоит дорого и клобберит регистры — читать раз в тик, не в - кадре, и не из горячего пути (memory `estex_bios_abi`); -- разрешение RTC — секунда, а тик игры — 1/12 секунды: нужен гибрид - «кадры внутри секунды, синхронизация по RTC на границе», иначе часы - задёргаются; -- пауза, меню и загрузка НЕ должны съедать игровое время — при часах от - RTC это перестаёт получаться само собой и требует явного вычитания; -- быстрая загрузка/сохранение обязаны сохранять смещение, а не абсолютное - время. - -**Решать пользователю.** Порядок по цене: вариант 1 (ничего), вариант 2 -(счётчик кадров луча, лечит только разбег режимов), вариант 3 (RTC, лечит -и накопление — но требует разобраться с паузами). - - -### SND-SPEAKER-38. Звук мигания надписи — СДЕЛАНО и ПРОВЕРЕНО 2026-08-31 - -**Постановка (пользователь, 2026-08-31):** «когда надпись Press Button -начинает мигать — в SDLPoP воспроизводится звук на каждое моргание, у нас -тишина». Решение выбрано там же: «проще синтезировать как PCM и добавить -в наш SND атлас». - -**Наш код НЕ виноват и правки не требует.** `pop_dead_prompt` -(`src/pop_status.c`) уже зовёт звук 38 на каждом появлении строки — ровно -как оригинал. Пусто в НАБОРЕ: в `assets/packed/SND/snd.idx` слот 38 имеет -длину 0, играть нечего. - -**Почему его нет.** У оригинала три параллельных набора звука, и номера -разложены по ним не подряд: - -| набор | что | номера | у нас | -|---|---|---|---| -| `DIGISND1..3.DAT` | оцифровка | 0–23, 44–49, 51 | берём, это и есть наш SND | -| `MIDISND1..2.DAT` | мелодии | 24–30, 32, 33, 35–37, 39–41, 43, 50, 52–56 | берём отдельно, как музыку в `MUS/` | -| `IBM_SND1..2` | ноты PC-спикера | 0–56 (весь диапазон) | НЕ берём вовсе | - -Звук 38 есть ТОЛЬКО в наборе спикера — ни оцифровки, ни мелодии для него -не существует, поэтому он и провалился между двумя нашими конвейерами. - -**Полная ревизия недостающего (сделана 2026-08-31).** Номера, которых нет -ни в оцифровке, ни среди мелодий: **31, 34, 38, 42**. Из них 31, 34 и 42 — -пустые заглушки в один байт, нот внутри нет. **Реально звучит ровно один -номер — 38.** То есть задача закрывает единственную дыру в наборе, а не -открывает семейство. - -**Формат ресурса** (канон — `docs/PoP/POP-DAT-FormatSpecifications.pdf`, -раздел «Internal PC Speaker»): заголовок 3 байта, из них байт 1 — темп в -долях на две секунды; далее тройки «частота в герцах (2 байта) + длина в -долях (1 байт)», нулевая частота = пауза; в конце маркер `12 00`. - -Звук 38 (`IBM_SND1/res10038.bin`, 17 байт) — нисходящий сигнал из четырёх -нот: 2500, 2000, 1500, 1000 Гц по одной доле, темп 72 → около 110 мс. -Для сверки разбора: звук 17 (мягкое приземление) — одна нота 49 Гц на три -доли. - -**Что делать.** - -1. В `tools/pop_pack_sound.py` — генератор PCM из нот: меандр на нашей - частоте вывода (`RATE`), амплитуда умеренная (сигнал короткий и резкий, - полный размах будет колоть ухо), пауза = уровень тишины `0x80`. -2. Брать из спикера ТОЛЬКО те номера, которых нет ни в оцифровке, ни в - мелодиях — сейчас это ровно 38. Правило важнее списка: если брать всё - подряд, синтез перекроет собой мелодии, которые мы играем из `MUS/`. -3. Дальше всё уже готово: звук ложится в атлас и индекс общим путём, - `snd.idx` пересобирается, EXE не меняется (раскладка читается с диска). -4. Проверка: `make resources` → слот 38 в индексе получил ненулевую длину; - в MAME дождаться мигания надписи после смерти — должен звучать сигнал. - -**Цена.** Один короткий звук в наборе (~2 КБ после выравнивания на блок), -кода в игре — ноль. - - -### 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). -- **KEYS-D — осмотр соседних комнат** (`H`/`J`/`U`/`N` + `Ctrl+B`, фаза D - плана [`keys_plan.md`](keys_plan.md)). Отложено 2026-08-27 по решению - пользователя: полезно игроку, но дорого. - - **Почему дорого.** У SDLPoP это три строки, потому что там `drawn_room` - влияет ТОЛЬКО на отрисовку, а физика ходит через `get_tile(room, col, - row)` с явной комнатой. У нас наоборот: карта коллизий загружается для - ОТРИСОВАННОЙ комнаты — `room_fg`, `lcol_fg`, `rcol_fg`, `above_fg`, - `below_fg` — это ОДИН комплект на программу, и описывает он `cur_room` - (видно в `pop_qsave_restore_room`). Уведи `cur_room` к соседу, не трогая - `kid_room`, — и Кид начнёт считать столкновения по чужим тайлам: - провалится сквозь пол или упрётся в воздух. - - Задел уже стоит: `kid_room` заведён отдельно, `update_kid_render_dx()` - умеет сдвигать спрайт на ±140 при расхождении. Но `enter_room_side()` - пишет обе переменные разом — это и есть незакрытая часть S3 straddle - ([`room_model_plan.md`](room_model_plan.md)). - - **Два пути.** (1) Честный, как в оригинале: игра идёт, пока смотришь. - Требует развести владельца карты коллизий — правка ядра, дни работы и - риск для того, что уже работает. (2) Смотровой режим С ОСТАНОВКОЙ игры: - пока ничто не тикает, рассогласование не проявляется. Нужна третья, - облегчённая «нарисовать комнату, не трогая состояние»: `enter_room_side` - попутно делает `pop_guard_leave`/`pop_loose_leave_room`, а - `pop_qsave_restore_room` перетирает карту и зовёт `pop_loose_reset`. - Оценка — 150-250 байт в банк 8 и несколько часов с проверкой. - - Рекомендация: путь (2) плюс запись в `impl_diff.md` (у нас смотровой - режим останавливает игру, у оригинала — нет). Главный риск не в - отрисовке, а в ВОЗВРАТЕ: оставить движок с чужой картой или подтёртой - историей heal — класс багов из memory `pop_seam_room_model`. - - Обходной путь, который уже работает: `Ctrl++` / `Ctrl+−` — телепорт по - номеру комнаты. Задачу разбора багов он закрывает, задачу игрока - «подсмотреть» — нет. -- **KEYS-R — воскрешение Кида** (`R`, остаток фазы C). Отложено - 2026-08-27. Не ещё один чит, а правка модели смерти: SDLPoP ставит - `resurrect_time = 20` и `Kid.alive = -1`, после чего об этом обязаны - знать четыре места — убывание счётчика (`seg003:512`), пропуск пик и - челюстей (`seg000:1245`), пропуск урона мечом (`seg000:876`), - восстановление позы в `play_kid` (`seg006:1352`: `loadkid`, полное HP, - стойка, `Char.x += 8`, `set_start_pos`). То есть окно неуязвимости, - размазанное по кадровой цепочке, и оно задевает гейт «мёртв», про - который предупреждает memory `pop_level_restart_scope`. Наш - `Char.alive` использует конвенцию оригинала (`-1` жив), так что - портируется, — но точечно и с проверкой каждого гейта. -- **KEYS-F12 — скриншот в файл** (фаза E). Помечено пользователем как - «возможно, когда-то в следующих версиях»: 80 КБ на кадр и запись на диск - посреди игры. - ---- - -## 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 deleted file mode 100644 index c19e5d5..0000000 --- a/applications/SprPoP/docs/full_game_plan.md +++ /dev/null @@ -1,367 +0,0 @@ -# От 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 deleted file mode 100644 index 3734abb..0000000 --- a/applications/SprPoP/docs/gates_spikes_plan.md +++ /dev/null @@ -1,279 +0,0 @@ -# Интерактивные объекты (кнопки/гейты/пики) + 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 deleted file mode 100644 index 6f4c5ad..0000000 --- a/applications/SprPoP/docs/host_tests_plan.md +++ /dev/null @@ -1,142 +0,0 @@ -# План: модульные тесты движка 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 deleted file mode 100644 index 1400f58..0000000 --- a/applications/SprPoP/docs/ideas_backlog.md +++ /dev/null @@ -1,183 +0,0 @@ -# Идеи и вопросы «на подумать» (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`). - -## Готовить следующий уровень, пока играет мелодия конца текущего - -**САМАЯ ДАЛЬНЯЯ ВЕРСИЯ.** Не полишинг и не порт: это улучшение ПРОТИВ -оригинала. Планируем, но не раньше, чем закроем уровни и полишинг. - -**Идея (пользователь, 2026-08-28).** Пока звучит мелодия конца уровня (и -трек заставки между уровнями), экран не меняется — значит в это время можно -успеть прочитать с диска следующий уровень, чтобы после музыки он появлялся -сразу, а не через секунду-другую загрузки. - -**Что УЖЕ сделано и мерено (2026-08-28).** Половина этого уже работает: -трек заставки играет ПОВЕРХ загрузки уровня — так же, как в оригинале -(`seg003:68-108`: `load_intro` возвращает управление, `load_level()` идёт под -музыку, и только потом `while (check_sound_playing())` на чёрном экране). -Насос это переживает: 500 подряд вызовов `pop_sfx_fill` через всю загрузку — -максимальный зазор 245 832 такта при дедлайне 251 000, ни одной пропущенной -порции. Осталась вторая половина: мелодия конца уровня досиживается на -ЖИВОМ экране пройденного уровня (как в оригинале, `play_level_2`), и только -после неё начинается загрузка. - -**Оговорки, найденные при разборе.** - -1. **Загрузку придётся разрезать надвое.** Сейчас `pop_level_switch` мешает - дисковую работу (файл уровня в EMM-страницу, атласы стража и тайлсета) с - экранно-палитровой (`pop_bg_load` переписывает записи палитры и атласы, - `pop_guard_load` физически применяет цветовые слоты, обе страницы - заливаются, `pop_pal_black`). Вторую половину НЕЛЬЗЯ выполнять, пока на - экране ещё живёт пройденный уровень — иначе палитра поедет прямо на - картинке. То есть в «музыкальное окно» можно вынести только диск, а всё, - что трогает палитру и VRAM, остаётся после. -2. **Бюджет кадра.** Ожидание мелодии идёт в игровом цикле (гейт - `pop_endmus_left` + `pop_music_busy()`), кадр там обычный, 80-100 мс. - Шаг подкачки должен быть такого же размера, как музыкальный — полстраницы - (~16 мс), а не страница целиком; иначе кадры на экране пройденного уровня - начнут дёргаться. -3. **Память.** Страницы следующего уровня придётся держать одновременно со - страницами текущего — проверить EMM-бюджет (memory `sprinter_emm_budget`), - тяжёлые тут не файл уровня, а атласы тайлсета и стража. -4. **Риск невелик, но он в самом хрупком месте.** Палитро-атласный обмен — - источник уже пойманных багов (`PAL-DUNGEON-STALE`, `PAL-L1-AFTER-INTRO`), - и трогать его ради экономии секунды стоит только на спокойную голову. - -**Выигрыш.** Одна-две секунды один раз на уровень. - -## Музыка одним постоянно открытым архивом (замер 2026-08-31, НЕ сейчас) - -**Идея.** Сейчас каждый трек — отдельный файл `MUS/mNN.bin`, и -`pop_music_load_begin` открывает свой на каждый запуск. Свести треки в -один файл и держать его `fd` открытым: вместо `chdir`+`open`+`chdir` -останется `lseek` к смещению трека. - -**Почему это стоит внимания — разложение окна старта музыки** (полный -замер и метод — `sound_plan.md` §9): - -| шаг | цена | уйдёт? | -|---|---:|---| -| ожидание `pop_music_service` в кадре | 22,6 мс | нет | -| `chdir` #1 | 46,1 мс | **да** | -| `open` файла трека | 34,5 мс | **да** | -| `chdir` #2 | 59,0 мс | **да** | -| чтение первых 8 КБ | 34,0 мс | нет | -| **итого** | **196,7 мс** | | - -Уходит **139,6 мс** — но это ВЕРХНЯЯ граница: появляется `lseek` к -смещению трека, которого сейчас нет вовсе (файл читается последовательно -с нуля), и его цена НЕ ИЗМЕРЕНА. Реальный выигрыш = 139,6 минус `lseek`; -померить можно тем же способом, `lseek` уже используется в -`pop_arc.c:89`. Остаётся ≈ 57 мс, то есть старт трека ускоряется -примерно вчетверо. - -**Где заметно.** Реплики PV-сцены идут встык (три трека подряд), плюс -каждый игровой джингл — смерть, зелье, подобранный меч. - -**Чем осложнено.** - -1. **`PBA1` под это не годится.** Размер элемента там `uint16` (≤ 64 КБ), - а трек — до 1,2 МБ (m56, 78 страниц); резать по страницам нельзя, - элементов вышло бы ~240 при `POP_ARC_MAX = 126`. Нужен свой индекс со - смещениями — то есть эта задача СМЫКАЕТСЯ с `MUS/MUSIC.IDX` из - `music_runtime_index_plan.md`: одно изменение раскладки, а не два. -2. **Постоянно занятый файловый манипулятор** — один из восьми (memory - `dss_fd_limit`, девятый `open` вешает DSS). Рядом свои открывают - `POP.CFG`, quicksave и загрузка уровня. -3. **`chdir` убрать нельзя** — он и есть половина выигрыша, но нужен - старым DSS (`POP_PATH_CALL`); экономия берётся не его удалением, а тем, - что открытие вообще перестаёт выполняться на каждый трек. - -**Оговорка.** Задержка старта музыки сама по себе НЕ является дефектом: -трек начинается на границе события, на слух это не сбой. У эффектов -такой задержки нет вовсе — они целиком в EMM (см. `sound_plan.md` §9.1). -Так что это оптимизация, а не исправление. diff --git a/applications/SprPoP/docs/impl_diff.md b/applications/SprPoP/docs/impl_diff.md deleted file mode 100644 index 2f8edc8..0000000 --- a/applications/SprPoP/docs/impl_diff.md +++ /dev/null @@ -1,686 +0,0 @@ -# Осознанные расхождения с 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` -кадров — труп должен успеть лечь. - ---- - -## Таблица рекордов: Esc отменяет запись, а оригинал уйти не даёт - -**Как в оригинале.** `show_hof` вставляет результат в таблицу ДО ввода -имени и крутит ввод, пока тот не вернёт положительную длину: - -```c -/* SDLPoP seg001.c:624 */ -while (input_str(&rect, hof[hof_index].name, 24, "", 0, 4, color, bgcolor) <= 0); -/* seg009.c input_str: Esc -> return -1; Enter -> return length */ -``` - -То есть **уйти нельзя ни по Esc, ни с пустым именем**: и −1, и 0 просто -начинают ввод заново. Запись в таблице остаётся в любом случае. - -**Как у нас.** Enter записывает имя (пустое подставляется как `PLAYER`), -**Esc отказывается от записи целиком**: таблица перечитывается с диска -(`hof_load`), `hof_save` не вызывается, экран показывается уже без строки -ввода. - -**Зачем.** Решение пользователя, 2026-08-27. Оригинальное поведение -означает, что случайно добравшийся до финала игрок обязан вписать имя, -чтобы вообще уйти с экрана, — на клавиатуре без цифрового блока и с нашим -raw-каналом это выглядит как зависание, а не как требование. - -**Чем платим.** Расхождение видно игроку: в оригинале таблица после -победы пополняется всегда. Плюс отмена стоит одного лишнего чтения -POP.HOF — откатывать сдвиг строк, который сделал `hof_insert_current`, -дешевле перечитыванием файла, чем обратным сдвигом (экран холодный, -50 мс там никому не мешают). - -**Что проверять при регрессе.** После Esc: таблица на экране прежняя (без -новой строки), POP.HOF на диске не изменился, следующий показ таблицы — -между credits и attract-demo — даёт тот же список. После Enter с пустым -именем строка называется `PLAYER` и сохраняется. diff --git a/applications/SprPoP/docs/keys.txt b/applications/SprPoP/docs/keys.txt deleted file mode 100644 index 3572819..0000000 --- a/applications/SprPoP/docs/keys.txt +++ /dev/null @@ -1,77 +0,0 @@ -* 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+Q / F10: 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 (optional - можно пока не делать). -* 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. (Both the main row and the keypad.) -* +: Increase remaining time by one minute. (Both the main row and the keypad.) -* 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). - -=== - -Sprinter-specific debug keys (not in the original; all under Ctrl so that no -plain letter is taken away from the layout above): - -* Ctrl+I: Immortality, cycling off -> combat only -> combat and minor damage. -* Ctrl+P: Speed mode. -* Ctrl+F: Freeze / unfreeze the frame (was 1 and 2). -* Ctrl+D: Debug bar (level/room/... in the top border) on/off. Same switch - as the Settings screen has; the shortcut does not write POP.CFG, - saving stays with Settings. - (The per-phase colour profiling of the border is a different thing - and stays a build option: make PROF=1.) -* Ctrl++ / Ctrl+-: Teleport to the next / previous room by number (ROOMNAV). - The plain +/- belong to the timer, as in the original; the debug - walk moved onto the same keys under Ctrl so muscle memory keeps. - -Note: Ctrl+B is deliberately NOT used here — the layout above reserves it for -"go back to the room where the prince is". - diff --git a/applications/SprPoP/docs/keys_plan.md b/applications/SprPoP/docs/keys_plan.md deleted file mode 100644 index b92386b..0000000 --- a/applications/SprPoP/docs/keys_plan.md +++ /dev/null @@ -1,170 +0,0 @@ -# План: перевод управления на раскладку `keys.txt` - -> **Состояние на 2026-08-27.** Фазы **A** и **B** сделаны (коммит 76f02e7), -> фаза **C** — три чита из четырёх (f8e96c0). Отложены и записаны на доску -> [`TASKS_OPEN.md`](TASKS_OPEN.md): `KEYS-R` (воскрешение), `KEYS-D` (осмотр -> соседних комнат), `KEYS-F12` (скриншот). Итоговая раскладка, включая наши -> сверхштатные клавиши, — в [`keys.txt`](keys.txt). - -Целевая раскладка — [`keys.txt`](keys.txt), она же раскладка SDLPoP. Здесь -разобрано, что у нас уже есть, что стоит не на той клавише и каких функций -нет вовсе, плюс порядок работ. Кода этот документ не меняет. - -## 1. Принципы - -**Раскладка — не самоцель.** Часть строк `keys.txt` — это функции SDLPoP, -которых у нас нет вообще (воскрешение, замедленное падение, осмотр соседних -комнат). Поэтому работа делится на две разные по цене части: *переназначить -клавишу* (минуты) и *реализовать функцию* (от часа до дня). Смешивать их в -одной фазе нельзя — иначе простые переименования застрянут за сложным. - -**Механику сверять с SDLPoP.** Для каждой новой функции — сначала найти её в -`assets/orig/SDLPoP/src/`, потом писать (правило проекта, memory -`pop_check_sdlpop_first`). Особенно для читов: `Shift+S`/`Shift+T` — это не -«прибавить HP», а конкретные `add_life`/`set_health_life`. - -**Кромка против удержания.** Игровые клавиши (стрелки, Shift) читаются как -удержание — это held-state, ради которого и заводился ``. Все -служебные и читерские — как НОВОЕ нажатие (edge), иначе одно касание -сработает столько раз, сколько кадров клавиша была внизу. В коде уже есть -оба образца: `read_user_control()` для первого, `intro_skip_requested()` и -`hof_*` для второго. - -**Где живёт обработчик.** Служебный слой — `pop_frame_ui()` в -`sprpop_cold.c` (банк 8), читы — `pop_cheat.h` + вызовы из главного цикла. -После переноса `pop_config`/`pop_hof` в банк 10 в банке 9 свободно 4436 -байт, в банке 8 — 1786; новые обработчики лучше класть в 8-й или 10-й, а не -в 9-й. - -**Ctrl-комбинации уже поддержаны:** `Ctrl+S`/`Ctrl+M` проверяют -`KBD_LCTRL || KBD_RCTRL` вместе с буквой (`sprpop_cold.c:518`). Тот же -приём годится для `Ctrl+A`, `Ctrl+R`, `Ctrl+V`, `Ctrl+Q`, `Ctrl+B`. - -## 2. Что есть сейчас - -### Движение и бой — совпадает, кроме диагоналей - -| `keys.txt` | у нас | статус | -|---|---|---| -| Left / Right / Up / Down | стрелки, `read_input()` | **есть** | -| Down+Left/Right — прыжок на месте | комбинация стрелок | **есть** | -| Shift — поднять предмет | `KBD_LSHIFT`/`KBD_RSHIFT` | **есть** | -| Shift+Left/Right — осторожный шаг | комбинация | **есть** | -| Up+Left / Up+Right — прыжок в сторону | комбинация | **есть** | -| **Home / Page Up** — то же самое | — | **НЕТ**: читаются только стрелки | -| Up на бегу — прыжок с разбега | | **есть** | -| Shift в падении — зацеп | | **есть** | -| Бой: Left/Right/Shift/Up | | **есть** | -| Бой: Down — убрать меч | | **проверить отдельно** | - -### Служебные - -| `keys.txt` | у нас | статус | -|---|---|---| -| `Esc` — пауза/меню | `KBD_MENU = 0x76` | **есть** | -| `Backspace` — меню | — | **НЕТ** (в оригинале это основная клавиша меню, Esc — дублёр) | -| `Space` — показать остаток времени | — | **НЕТ** (`Space` занят только в меню и вводе имени) | -| `Ctrl+A` — рестарт уровня | только пунктом меню | **НЕТ клавиши** | -| `Ctrl+R` — вернуться в заставку | — | **НЕТ** | -| `Ctrl+S` — звук вкл/выкл | `Ctrl+S` | **есть, совпадает** | -| `Ctrl+M` — музыка вкл/выкл | `Ctrl+M` | **есть, совпадает** | -| `Ctrl+V` — версия | `pop_build_info_show()` есть, зовётся на старте | **НЕТ клавиши** | -| `Ctrl+Q` / `F10` — выход | `F10` в двух местах | **половина** (см. §3) | -| `F6` / `F9` — quicksave/load | совпадает | **есть** | -| `F12` — скриншот | — | помечено опциональным, **не делаем** | - -### Читы - -| `keys.txt` | у нас | статус | -|---|---|---| -| `Shift+L` — следующий уровень | совпадает | **есть** | -| `K` — убить стража | совпадает | **есть** | -| `[` / `]` — сдвинуть Кида на пиксель | совпадает | **есть** | -| `+` / `−` — минута времени | у нас это **F7/F8**, а `+`/`−` заняты обходом комнат | **конфликт, см. §3** | -| `Shift+I` — перевернуть экран | у нас это **`U`** | **переназначить** | -| `R` — воскресить Кида | — | **НЕТ функции** | -| `Shift+W` — замедленное падение | — | **НЕТ функции** | -| `Shift+S` — вернуть одну единицу HP | — | **НЕТ функции** | -| `Shift+T` — добавить HP | — | **НЕТ функции** | -| `T` — показывать таймер | — | **НЕТ функции** | - -### Осмотр комнат — нет целиком - -`H` / `J` / `U` / `N` (соседняя комната слева/справа/сверху/снизу) и -`Ctrl+B` (вернуться к Кида) не реализованы. У нас есть отладочный **обход -комнат по номеру** (`+`/`−`, `ROOMNAV` в `sprpop.c`) — это другое: он -ТЕЛЕПОРТИРУЕТ Кида, а `H/J/U/N` только смотрят, не двигая его. - -### Наше сверх `keys.txt` - -`I` — бессмертие, `P` — режим скорости, `1`/`2` — стоп-кадр, `+`/`−` — -обход комнат. Всё это отладочное и в оригинале отсутствует; выбросить или -оставить — решение пользователя (§6). - -## 3. Конфликты, которые надо решить до кодинга - -**К1. `+`/`−`: время или комнаты.** `keys.txt` отдаёт их под ±минуту, у нас -на них обход комнат, а время висит на F7/F8. В `pop_cheat.h` это записано -как осознанный выбор («Numpad +/- уже принадлежат ROOMNAV»). Варианты: -вернуть `+`/`−` времени и увести ROOMNAV на другие клавиши; оставить как -есть и разойтись с `keys.txt`; либо развести по контексту (ROOMNAV — только -в отладочной сборке). Развилка требует решения. - -**К2. `U` занят.** Сейчас `U` — переворот экрана, а `keys.txt` хочет `U` под -«посмотреть комнату сверху», переворот — на `Shift+I`. Разрешается само, -если делать К5 и К7 одной фазой. - -**К3. `S` без Ctrl.** Звук у нас требует Ctrl, и это правильно, но -`keys.txt` хочет ещё и `Shift+S` (вернуть HP). Проверка обязана различать -`Ctrl+S`, `Shift+S` и голое `S`, иначе они будут срабатывать вместе. То же -для `T`: `T` (таймер) против `Shift+T` (добавить HP). - -**К4. Два выхода.** `F10` живёт в `pop_frame_ui()` (только в игре) и, с -коммита 808c2a5, в `kbd_idle()` (везде). Дублирование безвредное, но лишнее: -проверку в `pop_frame_ui` стоит убрать, оставив одну точку. Заодно повесить -туда же `Ctrl+Q`. - -## 4. Порядок работ - -**Фаза A — переназначения, функции уже есть.** Дёшево и не трогает логику. -- `Shift+I` вместо `U` для переворота экрана (К2). -- `Backspace` как основная клавиша меню, `Esc` оставить дублёром. -- `Ctrl+V` на существующий `pop_build_info_show()`. -- `Ctrl+Q` рядом с `F10`, убрать дубль `F10` из `pop_frame_ui` (К4). -- `Ctrl+A` на существующий путь `POP_MENU_RESTART_LEVEL`. -- Решение по К1 и правка `+`/`−`/F7/F8. - -**Фаза B — мелкие новые функции.** -- `Space` — показать остаток времени. Механика есть (`pop_timer`, статус- - строка); нужен показ на несколько секунд, как `text_time_total` оригинала. -- `T` — постоянный показ таймера (минуты:секунды) в статус-строке. -- `Ctrl+R` — возврат в заставку. Автомат это умеет (`POP_APP_EV_RESTART_INTRO`), - нужен только вход с клавиши. - -**Фаза C — читы, требующие правки игровой логики.** Каждый — сверка с -SDLPoP до кодинга. -- `Shift+S` / `Shift+T` — HP: аналоги малого и большого красного зелья. -- `R` — воскрешение Кида: сложнее прочих, поднимает мёртвого персонажа и - должен согласоваться с уже существующим гейтом «мёртв» (memory - `pop_level_restart_scope`). -- `Shift+W` — замедленное падение. - -**Фаза D — осмотр соседних комнат (`H`/`J`/`U`/`N` + `Ctrl+B`).** Самое -крупное: нужен показ комнаты, в которой Кида нет, с возвратом. У нас уже -разобрана модель `kid_room ≠ drawn_room` (memory `pop_seam_room_model`, -[`room_model_plan.md`](room_model_plan.md)) — это её прямое применение, и -именно поэтому фаза идёт последней. - -**Фаза E — `F12` скриншот.** Помечен опциональным; предлагаю не делать -вовсе: файл на 80 КБ и запись на диск посреди кадра. - -## 5. Открытые вопросы - -1. **К1** — кому отдать `+`/`−`: времени (как `keys.txt`) или обходу комнат? -2. Что делать с нашими сверхштатными клавишами (`I` бессмертие, `P` скорость, - `1`/`2` стоп-кадр): оставить как есть, увести под отладочную сборку или - выбросить? -3. Нужны ли `Home`/`Page Up` как дублёры диагональных прыжков — на IBM-клавиатуре - Sprinter они есть, но комбинация `Up+Left` уже работает. -4. Фазы C и D — делать ли их вообще: это читы и отладочный осмотр, к - прохождению игры отношения не имеющие. diff --git a/applications/SprPoP/docs/layout_plan_v2.md b/applications/SprPoP/docs/layout_plan_v2.md deleted file mode 100644 index d32a4ee..0000000 --- a/applications/SprPoP/docs/layout_plan_v2.md +++ /dev/null @@ -1,532 +0,0 @@ -# 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 deleted file mode 100644 index 877eb30..0000000 --- a/applications/SprPoP/docs/levels_12_15_plan.md +++ /dev/null @@ -1,462 +0,0 @@ -# Что осталось на уровнях 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 deleted file mode 100644 index 524175e..0000000 --- a/applications/SprPoP/docs/levels_plan.md +++ /dev/null @@ -1,209 +0,0 @@ -# План: от одного уровня к нескольким (загрузка, переходы, тайлсеты) - -Статус: план, 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 deleted file mode 100644 index e8b8d14..0000000 --- a/applications/SprPoP/docs/menu_settings_plan.md +++ /dev/null @@ -1,529 +0,0 @@ -# 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 deleted file mode 100644 index a0df25e..0000000 --- a/applications/SprPoP/docs/midtable_analysis.md +++ /dev/null @@ -1,275 +0,0 @@ -# Слои отрисовки: как устроен оригинал и чего стоит порт - -Разбор 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/music_runtime_index_plan.md b/applications/SprPoP/docs/music_runtime_index_plan.md deleted file mode 100644 index 40d3326..0000000 --- a/applications/SprPoP/docs/music_runtime_index_plan.md +++ /dev/null @@ -1,592 +0,0 @@ -# Аудио без перелинковки: runtime-индексы музыки и эффектов - -> Статус: **ОБЕ ЧАСТИ СДЕЛАНЫ 2026-08-31** — эффекты (SI0..SI4) и музыка -> (MI0..MI5). Разбор и результаты: `sound_plan.md` §10 (эффекты) и §11 -> (музыка). Ниже — исходный план; расхождения перечислены следом. -> -> Что в реализации разошлось с планом ниже: файл назван `SND/snd.idx` -> (единый basename с `snd.arc`, оба в нижнем регистре), а при отсутствии -> индекса поднимается ПУСТОЙ набор с блоком тишины — иначе встаёт музыка -> и игра непроходима (§10.5 sound_plan). Про `ticks60` в `PMI1` решено: -> поле в формате оставить даже если сценарии перейдут на ожидание звука — -> загрузчик волен не тащить его в память. -> -> Расхождения части I с планом ниже: -> * файл назван `MUS/mus.idx` (единый стиль с `snd.idx`); -> * пауза конца уровня переведена НЕ на runtime-тики, а на состояние -> «заявка/загрузка/звучание» (`pop_music_active`) — §11.2: это снимает и -> зависимость от набора, и враньё делителя `/4` в быстрых режимах, и -> расхождение с SDLPoP при выключенном звуке; -> * PV-сцена (§8 плана) — не структура времён через пять функций, а четыре -> якоря-статики (8 байт), от которых отсчитываются прежние выражения; -> * `pages` в PMI1 не хранится — считается из `blocks`. - -## 1. Цель - -Один и тот же `sprpop.exe` должен корректно работать с любым штатным набором -музыки (`flac`, `mp3`, `ogg`, `mt32`) без перекомпиляции и перелинковки. -Замена набора должна сводиться к замене файлов в `MUS/` вместе с описывающим -их индексом. - -Сейчас это невозможно: упаковщик печатает зависящие от набора значения в -`gen/pop_music_tbl.h` и `gen/pop_music_ticks.h`, после чего они становятся -частью EXE. Если заменить только `MUS/mNN.bin`, загрузчик продолжает верить -размерам старого набора, а сценарии — его длительностям. - -## 2. Подтверждённое текущее состояние - -После преобразования в PCM наборы `flac`, `mp3` и `ogg` имеют одинаковые -длины. Исполнение MT-32 отличается. Значимые примеры: - -| трек | FLAC/MP3/OGG | MT-32 | -|---|---:|---:| -| m32, конец уровня 4 | 438 тиков | 449 тиков | -| m41, обычный конец уровня | 732 | 685 | -| m50, принцесса ждёт | 831 | 867 | -| m53, реплика Джафара | 985 | 1044 | -| m56, финальная тема | 9865 блоков / 78 страниц | 10462 / 82 | - -Все обычные треки каждого из четырёх наборов по-прежнему помещаются в -`POP_MUS_PAGES == 20`. Только m56 требует кольцевого проигрывателя. -`uint16_t` достаточно и для самого длинного MT-32-трека: 10462 блока. - -Compile-time длительности используются не повсюду: - -- `pop_music.c` читает из `pop_music_tbl.h` наличие, число страниц и блоков; -- `pop_intro.c` использует длительности m50 и m53 для шкалы PV-сцены; -- `pop_kid.c` использует длительности m32 и m41 для паузы конца уровня, - включая режим с выключенной музыкой; -- остальные ожидания уже опираются на `pop_music_busy()` и автоматически - заработают с правильным runtime-числом блоков. - -## 3. Что должно остаться константами программы - -В индекс переносятся только свойства конкретного набора. Инварианты формата -остаются в коде, а заголовок IDX лишь подтверждает их: - -- PCM unsigned 8-bit mono; -- частота `CBL_FREQ_10K9`; -- один блок насоса — 128 байт; -- пространство оригинальных sound id — 0..56; -- `POP_MUS_PAGES == 20` для обычного загрузчика; -- размер кольца потокового проигрывателя. - -Это важно: IDX не должен обещать программе другой формат PCM, который насос -физически не умеет воспроизводить. - -## 4. Предлагаемый файл `MUS/MUSIC.IDX` - -Предпочтителен отдельный индекс набора, а не заголовок в каждом треке: - -- все сценарные длительности доступны сразу после старта; -- `mNN.bin` остаются простыми сырыми PCM-потоками; -- загрузчик трека меняется минимально; -- один индекс легко заменить вместе с набором; -- формат помещается в один 512-байтовый сектор DSS. - -Предлагаемая версия `PMI1`: - -```text -0..3 "PMI1" magic и версия -4 57 число плотных записей (id 0..56) -5 4 размер записи -6 7 log2 размера блока: 1 << 7 = 128 -7 1 формат PCM: u8 mono, CBL_FREQ_10K9 -8..15 0 резерв будущих версий - -16.. 57 записей по 4 байта: - +0..1 uint16 blocks длина PCM в блоках по 128 байт - +2..3 uint16 ticks60 длительность ожидания в тиках оригинала - -244..511 нулевой резерв до одного сектора -``` - -Нулевые `blocks` и `ticks60` означают, что трека с таким id в наборе нет. - -`pages` в IDX не хранится: это производная величина, и её дублирование может -разойтись с `blocks`: - -```c -pages = blocks / 128 + ((blocks & 127) != 0); -``` - -По умолчанию упаковщик вычисляет тики той же формулой, что сейчас: - -```text -ticks60 = round(blocks * 40 / 57) -``` - -Хранить `ticks60` отдельно всё же полезно: в будущем момент окончания -сценарной реплики можно будет уточнить независимо от технического хвоста PCM. -Генератор обязан печатать предупреждение, если явно заданные тики заметно -отличаются от длительности файла. - -## 5. Размещение индекса в памяти - -Загружать таблицу в обычный изменяемый `static` нельзя. Данные банковых -модулей сейчас попадают в общий `_DATA`/W2, а свободная куча составляет около -238 байт. Даже таблица из 228 байт практически уничтожит этот запас. - -Индекс следует держать в одной EMM-странице: - -1. `pop_music_init()` выделяет блок из одной страницы; -2. загружает туда первые 512 байт `MUS\MUSIC.IDX`; -3. проверяет magic, версию, размеры полей и формат PCM; -4. готовит страницу для безопасного временного отображения в W0; -5. сохраняет в W2 только номер блока/страницы и флаг готовности. - -Потеря 16 КБ EMM ради маленькой таблицы допустима: EMM у игры с запасом, а -W1/W2 — самый дефицитный ресурс. Не следует ради экономии страницы прятать -индекс в хвост `kid.ani` или звукового набора: это создаст ненужную связь -между независимыми ресурсами. - -Доступ к записи предоставляет банковая функция наподобие: - -```c -int8_t pop_music_info(uint8_t id, pop_music_info_t *out) __banked; -``` - -Она на короткое время отображает страницу IDX в W0, копирует четыре байта в -буфер вызывающего и сразу восстанавливает окно. Наружу указатель на EMM не -выдаётся. - -Индекс живёт до выхода из программы. `pop_music_free()`, который вызывается -между сценами, освобождать его не должен; для полного завершения нужен -отдельный shutdown либо освобождение в общем маршруте выхода. - -## 6. Изменения загрузчика музыки - -`pop_music_load_begin(id)` должен получать из runtime-индекса: - -- наличие трека; -- число блоков; -- вычисленное число EMM-страниц. - -Далее существующая архитектура почти не меняется: - -- `slot_blocks[]` уже хранит runtime-длину загруженного трека; -- `pop_mus_left` получает её при `play`/`stream`; -- насос сам останавливается на правильном блоке; -- `pop_music_busy()` автоматически отражает фактический конец; -- кольцевой проигрыватель получает правильные 82 страницы MT-32 m56 вместо - 78 страниц FLAC. - -При каждом открытии `mNN.bin` надо без дополнительного `open` проверить -фактический размер: - -1. `lseek(fd, 0, SEEK_END)`; -2. убедиться, что размер положительный и кратен 128; -3. сравнить его с `blocks * 128` из IDX; -4. вернуть позицию через `lseek(fd, 0, SEEK_SET)`. - -`lseek(SEEK_END)` уже реализован в libc поверх DSS `MOVE_FP $15` и -используется самой игрой для `POP.CFG`. - -Рекомендуемая политика несовпадения: - -- вывести диагностическое сообщение; -- считать фактический размер файла главным; -- пересчитать `blocks/pages/ticks60` в загруженной EMM-копии индекса; -- продолжить работу, если размер проходит ограничения. - -Так случайно забытый старый IDX не приведёт к чтению чужой EMM-страницы или -обрыву трека. При этом штатная поставка обязана всегда включать согласованные -IDX и PCM. - -## 7. Runtime-тики без 32-битного переполнения - -Если тики приходится восстанавливать из фактического размера, прямое -`blocks * 40` может переполнить `uint16_t`. Та же формула считается только -16-битной арифметикой: - -```c -ticks = (blocks / 57) * 40 - + ((blocks % 57) * 40 + 28) / 57; -``` - -Даже при `blocks == 65535` промежуточные значения остаются в `uint16_t`. -Это холодный путь, поэтому небольшая цена деления допустима. - -## 8. Перевод сценариев с compile-time на runtime - -### PV-сцена (`pop_intro.c`) - -Текущий большой `enum` смешивает два класса величин: - -- неизменные интервалы сценария; -- абсолютные точки, сдвигаемые длительностями m50 и m53. - -Надо сохранить относительные сценарные константы, а при входе в PV-сцену -один раз собрать локальную структуру `pv_timing_t`: - -```text -m50_end = MUS_2_START + ticks(m50) -wait_end = m50_end + 40 -... -dialog1_start = предыдущая фиксированная цепочка -exit_start = dialog1_start + ticks(m53) -anim_end = exit_start + 469 -``` - -Структуру лучше держать на стеке `intro_pv_animated()` и передавать нужным -helper-функциям указателем. File-scope изменяемая таблица снова попала бы в -W2. - -m50 уже загружен до начала PV. m53 начинает подгружаться задолго до своего -старта, поэтому метаданные обоих треков к моменту использования доступны. - -### Конец уровня (`pop_kid.c`) - -Вместо `POP_MUS_TICKS_32/41` редкое событие окончания уровня вызывает -банковый accessor IDX и вычисляет `pop_endmus_left` из runtime-тиков. - -Индекс используется даже при выключенной музыке: оригинал выдерживает эту -паузу молча. Поэтому нельзя заменять её одним `pop_music_busy()`. - -## 9. Генератор и сборка - -`tools/pop_pack_music.py` должен: - -1. как сейчас преобразовать выбранный набор в `mNN.bin`; -2. проверить кратность каждого результата 128 байтам; -3. собрать плотные записи 0..56; -4. записать `MUS/MUSIC.IDX` в формате `PMI1`; -5. больше не генерировать `pop_music_tbl.h` и `pop_music_ticks.h`. - -Makefile должен: - -- добавить `MUS/MUSIC.IDX` в `DISK` и staging; -- убрать музыкальные generated-header'ы из `GEN_H`; -- убрать их из зависимостей EXE; -- оставить `MUSIC_FMT` зависимостью только музыкальных ресурсов; -- гарантировать, что `make music-flac/mp3/ogg/mt32` заменяет и PCM, и IDX, - но не перелинковывает EXE. - -Критерий архитектуры: SHA/дата `sprpop.exe` не меняется при переключении -между четырьмя музыкальными целями. - -## 10. Ошибки и совместимость - -Игра не должна падать из-за необязательной музыки: - -- нет IDX — музыка отключена, игра продолжает работать; -- неверная magic/версия/формат — музыка отключена с диагностикой; -- записи нет — конкретный трек считается отсутствующим; -- файла нет — запрос трека завершается молча/с диагностикой, как сейчас; -- обычный трек требует больше 20 страниц — не загружать обычным путём; -- потоковый трек имеет больше 255 страниц — отвергнуть, потому что текущие - счётчики страниц восьмибитные; -- IDX и файл расходятся — применить политику §6; -- все файловые операции выполнять через `POP_PATH_CALL`, чтобы сохранить - работу на старых DSS с повреждением текущего каталога. - -Для отсутствующего/повреждённого IDX остаётся выбрать поведение немой паузы -конца уровня: нулевая пауза либо небольшой канонический fallback. Это не -мешает основной архитектуре, но решение надо принять до реализации. - -## 11. Этапы реализации - -### MI0 — формат и host-тест - -- вынести writer/reader `PMI1` в тестируемый код упаковщика; -- проверить magic, размеры, LE-поля, нулевые записи и padding; -- для всех четырёх наборов сверить `blocks/ticks` с фактическими PCM; -- зафиксировать тестом значения MT-32 m50/m53/m56 как отличающиеся от FLAC. - -### MI1 — runtime-загрузка IDX - -- `pop_music_init()`/shutdown; -- одна EMM-страница, загрузка и валидация; -- accessor одной записи; -- отказ без порчи W0/W3, EMM и файловых дескрипторов. - -### MI2 — loader/pump - -- заменить `pop_mus_tbl[]` runtime-записью; -- проверять реальный размер файла; -- обычный, немедленный и кольцевой пути должны использовать одну метаинформацию; -- удалить `pop_music_tbl.h`. - -### MI3 — runtime-шкала сцен - -- перевести m50/m53 в `pop_intro.c` на локальную runtime-шкалу; -- перевести m32/m41 в `pop_kid.c`; -- удалить `pop_music_ticks.h`. - -### MI4 — сборка и образы - -- добавить IDX в каждый музыкальный набор; -- исключить музыку из зависимостей линковки; -- собрать четыре HDD-варианта с одним EXE. - -### MI5 — приёмка в MAME/на железе - -- FLAC: title -> story -> PV, обычный конец уровня, конец уровня 4, ending; -- MT-32: те же маршруты, особенно m50/m53 и полный m56; -- музыка выключена: пауза конца уровня берётся из IDX; -- замена набора без пересборки EXE; -- повреждённый IDX, отсутствующий трек, несовпадающий размер; -- контроль, что не запущено более одного MAME и предыдущий экземпляр закрыт - перед новым прогоном. - -## 12. Критерии готовности - -- один бинарник запускается со всеми четырьмя наборами; -- ни один музыкальный generated-header не входит в сборку C; -- начало следующей сцены/уровня соответствует фактической записи; -- MT-32 m56 проигрывает все 82 страницы и не обрывается как FLAC-вариант; -- обычные треки не читают за пределами выделенного EMM-блока; -- IDX не расходует сотни байт W2; -- ошибочный набор отключает музыку безопасно; -- переключение `music-*` не меняет EXE; -- старые DSS продолжают работать через восстановление каталога приложения. - ---- - -# Часть II. Звуковые эффекты без перелинковки - -## 13. Цель и отличие от музыки - -Один и тот же `sprpop.exe` должен работать с разными наборами PCM-эффектов, -если в них сохранена исходная нумерация sound id 0..56. Замена набора должна -сводиться к замене согласованной пары `SND/snd.idx` + `SND/snd.arc`, без -генерации C-заголовка и перелинковки EXE. - -Набор состоит из двух согласованных файлов: - -- `SND/snd.arc` — обычный архив PBA1 с PCM-страницами; -- `SND/snd.idx` — описание раскладки эффектов внутри этих страниц. - -Отдельный IDX предпочтительнее расширения заголовка `snd.arc`: PBA1 остаётся -универсальным и не получает специального варианта только для звука, а -формат индекса можно независимо версионировать и проверять тем же способом, -что будущий `MUS/MUSIC.IDX`. - -Существующий формат записи менять не требуется: - -```c -typedef struct { - uint8_t page; - uint16_t off; - uint16_t len; -} pop_snd_ent_t; -``` - -Это ровно необходимые проигрывателю номер логической страницы, смещение в -ней и длина PCM. В текущем ABI SDCC/z80 запись занимает 5 байт; дисковый -формат обязан описывать эти пять байт явно (`uint16` little-endian), а код -должен проверять `sizeof(pop_snd_ent_t) == 5` на этапе сборки. - -## 14. Подтверждённое текущее состояние - -Сейчас `tools/pop_pack_sound.py` печатает раскладку в -`gen/pop_sound_tbl.h`, а `pop_sfx.c` включает её как -`static const pop_snd_tbl[57]`. Поэтому конкретные `page/off/len` становятся -частью EXE. - -В текущей сборке таблица занимает 285 байт в `_CODE`, по адресам -`0x52D8..0x53F4`. Это общий резидентный диапазон игры: при huge-модели -`_CODE`, `_DATA`, heap и stack совместно используют плоские 32 КБ -`0x4000..0xBFFF`; граница W1/W2 отдельного бюджета здесь не создаёт. - -Проверены оба имеющихся исходных набора DIGISND: - -| набор | PCM-эффектов | страниц после упаковки | отличия раскладки | -|---|---:|---:|---:| -| MSDOS 1.3/1.4 | 31 из 57 | 10 | эталон текущей сборки | -| SDLPoP 1.0/1.1 | 31 из 57 | 9 | отличаются 8 id | - -Максимальный эффект занимает 17 664 байта, или 138 блоков по 128 байт. -У SDLPoP эффект 48 содержит только 7 исходных сэмплов; это свойство самого -набора, а не ошибка runtime-индекса. - -## 15. Файл `SND/snd.idx` - -IDX занимает один 512-байтовый сектор и после короткого заголовка является -точным дисковым дампом 57 записей `pop_snd_tbl`: - -```text -0..3 "PSI1" magic и версия индекса эффектов -4 57 число плотных записей, id 0..56 -5 5 размер записи -6 7 log2 размера блока: 1 << 7 = 128 -7 1 формат: unsigned 8-bit mono, CBL_FREQ_10K9 -8 pages фактическое число PCM-страниц в snd.arc -9..15 0 резерв - -16..300 57 записей по 5 байт: - +0 uint8 page - +1..2 uint16 off, little-endian - +3..4 uint16 len, little-endian - -301..511 0 резерв до одного сектора -``` - -`pages` — фактический размер конкретного набора, например 10 для MSDOS или -9 для SDLPoP. Его не следует называть или трактовать как -`POP_SND_MAX_PAGES`: максимум 16 является compile-time-вместимостью -загрузчика, а IDX сообщает число реально нужных страниц и обязан укладываться -в этот предел. - -Загрузчик сверяет `pages` с числом элементов внешнего PBA1 и не доверяет -расходящимся файлам. `off` и `len` обязаны быть кратны 128; нулевая длина -означает отсутствие PCM для данного id. - -IDX загружается прямо в резидентный `pop_snd_tbl`, поэтому отдельной страницы -EMM для него не требуется. Цена решения — одно дополнительное открытие и -чтение 512 байт при старте звука; на фоне загрузки 9–10 страниц PCM это -приемлемый холодный расход. - -## 16. Размещение runtime-таблицы в памяти - -`pop_snd_tbl` становится обычным изменяемым внутренним объектом: - -```c -pop_snd_ent_t pop_snd_tbl[POP_SND_COUNT]; -``` - -Его следует определить в отдельном internal data-модуле и объявить через -`extern` в `_pop_sfx.h`, потому что таблицу заполняет холодный загрузчик из -банка 8, а читает резидентный `pop_sfx.c`. - -После снятия `const` SDCC перенесёт эти 285 байт из `_CODE` в `_DATA`. Это -не добавляет 285 байт к общему резидентному расходу: `_CODE` одновременно -уменьшается на тот же размер, а `_CODE` и `_DATA` последовательно лежат в -одном диапазоне `0x4000..0xBFFF`. Возможна лишь небольшая разница из-за -выравнивания, которую надо проверить итоговой map-картой и size-check. - -Банк 8 может читать индекс напрямую в этот буфер: весь диапазон -`0x4000..0xBFFF` остаётся доступен, пока банковый код исполняется в W3. -Горячие обращения `pop_snd_tbl[id].page/off/len` и ISR не требуют новых -маппингов или accessor-функций. - -## 17. Фактическое число страниц - -Инвариантами программы остаются: - -```c -#define POP_SND_MAX_PAGES 16 -#define POP_SND_COUNT 57 -#define POP_SND_BLOCK 128 -``` - -`POP_SND_PAGES == 10` больше не должен означать размер конкретного набора. -Вместо него появляется runtime-состояние: - -```c -uint8_t pop_snd_pages; -uint8_t pop_snd_page[POP_SND_MAX_PAGES]; -``` - -Загрузчик выделяет `mem_alloc_pages(pop_snd_pages)` и заполняет только -фактическое число элементов массива. MSDOS-набор займёт 10 страниц, -SDLPoP-набор — 9. Предел 16 оставляет запас будущим наборам без -перекомпиляции и увеличивает постоянный массив лишь на 6 байт относительно -текущего; ещё один байт занимает `pop_snd_pages`. - -Насос при переходе длинного эффекта через границу страницы сравнивает -`sfx_pg + 1` с `pop_snd_pages`, а не с compile-time-константой. - -## 18. Загрузка и валидация - -`pop_sfx_init()` должен выполнять следующую последовательность: - -1. открыть `SND/snd.idx` через существующий путь `POP_PATH_CALL`; -2. прочитать 16-байтовый заголовок, затем 285 байт записей прямо в - `pop_snd_tbl`, и проверить полный размер IDX; -3. проверить magic, версию, число/размер записей, формат и размер блока; -4. получить из IDX фактическое число страниц и проверить диапазон 1..16; -5. открыть `SND/snd.arc` через `POP_PATH_CALL`; -6. прочитать обычную таблицу PBA1 с ёмкостью `POP_SND_MAX_PAGES`; -7. сверить `PSI1.pages` с числом элементов PBA1; -8. проверить каждую ненулевую запись; -9. только после этого выделить фактическое число EMM-страниц и загрузить их. - -Для каждой записи проверяются: - -- `page < pop_snd_pages`; -- `off < 16384` и `off % POP_SND_BLOCK == 0`; -- `len % POP_SND_BLOCK == 0`; -- диапазон `page/off/len` не выходит за загруженный набор; -- переход длинного эффекта на следующие страницы не превышает - `pop_snd_pages`. - -При любой ошибке блок EMM и файловый дескриптор освобождаются, таблица не -используется, эффекты остаются выключенными. Набор без `snd.idx` считается -неполным; хранить в EXE старую таблицу как fallback не следует, иначе -зависимость бинарника от конкретного набора останется. - -## 19. Генератор и сборка - -`tools/pop_pack_sound.py` должен: - -1. как сейчас разобрать DIGISND, привести PCM к CBL_FREQ_10K9 и выровнять - начала/длины на 128 байт; -2. вычислить фактическое число страниц и отвергнуть набор больше 16; -3. сформировать плотные 57 записей `page/off/len`; -4. записать `SND/snd.idx` в формате `PSI1`, включая фактическое число - страниц; -5. собрать обычный `SND/snd.arc` из полученных страниц; -6. больше не генерировать `gen/pop_sound_tbl.h`. - -Чтобы число страниц не было захардкожено списком `s0.bin`..`s9.bin` в -Makefile, упаковщик звука предпочтительно должен сразу формировать -`assets/packed/SND/snd.arc` либо передавать упаковщику архивов динамический -список результатов без сохранения устаревшей десятой страницы. - -Стабильные тип и константы переносятся в обычный internal-заголовок. EXE не -должен зависеть от результата упаковки звука: цель выбора набора меняет -согласованную пару `snd.idx` + `snd.arc`. - -## 20. Этапы реализации эффектов - -### SI0 — формат и host-тест - -- writer/reader отдельного `snd.idx` в формате `PSI1`; -- проверка точного пятибайтового LE-формата записи; -- сборка и разбор обоих имеющихся DIGISND-наборов; -- проверки 10 страниц MSDOS, 9 страниц SDLPoP и отличающихся записей. - -### SI1 — runtime-таблица - -- изменяемый `pop_snd_tbl` в общем резидентном CODE/DATA-диапазоне; -- загрузка и полная валидация `PSI1`; -- удаление generated `pop_sound_tbl.h`; -- проверка map-карты: перенос CODE -> DATA не должен съесть heap. - -### SI2 — гибкое выделение страниц - -- `POP_SND_MAX_PAGES == 16` и runtime `pop_snd_pages`; -- фактический размер стека таблицы PBA1; -- выделение/освобождение 9, 10 и тестовых 16 страниц; -- runtime-гард перехода длинного эффекта между страницами. - -### SI3 — сборка без перелинковки - -- убрать фиксированный `SND_ATL` из десяти имён; -- сделать `snd.idx` и `snd.arc` согласованными результатами выбора набора; -- проверить неизменность SHA/даты `sprpop.exe` при смене набора. - -### SI4 — приёмка - -- проиграть короткий, обычный и переходящий страницу эффекты; -- проверить приоритеты и перебивание звуков; -- проверить музыку поверх общего CBL после runtime-загрузки эффектов; -- отсутствующий IDX и повреждённые magic/count/pages/page/off/len должны - безопасно отключать эффекты; -- перед каждым MAME-прогоном завершать предыдущий экземпляр и никогда не - запускать две копии одновременно. - -## 21. Критерии готовности эффектов - -- один `sprpop.exe` работает с MSDOS- и SDLPoP-наборами; -- замена согласованной пары `SND/snd.idx` + `SND/snd.arc` не требует - компиляции C и не меняет EXE; -- `pop_snd_tbl` больше не генерируется как C-код; -- загружается фактическое число страниц в диапазоне 1..16; -- таблица не расходует дополнительную страницу EMM и не увеличивает - суммарный резидентный CODE+DATA на свои 285 байт; -- горячий путь и ISR используют прежние прямые `page/off/len`; -- повреждённый индекс не приводит к чтению за пределами EMM-блока; -- файловые операции сохраняют совместимость со старыми DSS через - восстановление каталога приложения. diff --git a/applications/SprPoP/docs/palette_plan.md b/applications/SprPoP/docs/palette_plan.md deleted file mode 100644 index f25e914..0000000 --- a/applications/SprPoP/docs/palette_plan.md +++ /dev/null @@ -1,297 +0,0 @@ -# План: консолидация работы с палитрами + переход уровня через 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 deleted file mode 100644 index d522c7d..0000000 --- a/applications/SprPoP/docs/perf_backlog.md +++ /dev/null @@ -1,335 +0,0 @@ -# Оптимизация отрисовки — что НЕ сделано (замеры на 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 deleted file mode 100644 index 09188aa..0000000 --- a/applications/SprPoP/docs/perf_cyan_phase.md +++ /dev/null @@ -1,172 +0,0 @@ -# ЦИАН фаза (персонажи + передний слой) — анализ и оптимизация - -Рабочий документ: живёт между сессиями. Внизу **журнал правок** — каждая -запись с замером до/после. Сцена, рецепт воспроизведения и зонды — -[`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 deleted file mode 100644 index 00324c0..0000000 --- a/applications/SprPoP/docs/perf_green_phase.md +++ /dev/null @@ -1,327 +0,0 @@ -# ЗЕЛЁНАЯ фаза (слой фона) — анализ и оптимизация - -Рабочий документ: живёт между сессиями. Внизу **журнал правок** — каждая -запись с замером до/после. Сцена, рецепт воспроизведения и зонды — -[`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 deleted file mode 100644 index 24c59b0..0000000 --- a/applications/SprPoP/docs/perf_l11_room15.md +++ /dev/null @@ -1,238 +0,0 @@ -# Сцена и замер: факел + чомпер + страж, уровень 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 deleted file mode 100644 index 3a64012..0000000 --- a/applications/SprPoP/docs/perf_l13_room23.md +++ /dev/null @@ -1,271 +0,0 @@ -# Сцена и метод замера: каскад плит, уровень 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 deleted file mode 100644 index 18fbbc5..0000000 --- a/applications/SprPoP/docs/perf_registry.md +++ /dev/null @@ -1,871 +0,0 @@ -# Реестр оптимизаций: всё отложенное, в одном списке - -Собрано 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 deleted file mode 100644 index b5ed432..0000000 --- a/applications/SprPoP/docs/prng_alternatives.md +++ /dev/null @@ -1,148 +0,0 @@ -# Генераторы псевдослучайных чисел: запасные варианты - -Что сейчас стоит в порте, какие есть альтернативы и сколько на них реально -можно выиграть. Заготовка на случай, если упрёмся в бюджет кадра — -**сейчас менять ничего не нужно**. - -## Что стоит сейчас - -`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 deleted file mode 100644 index a5b671c..0000000 --- a/applications/SprPoP/docs/quicksave_plan.md +++ /dev/null @@ -1,328 +0,0 @@ -# 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 deleted file mode 100644 index c0f60fb..0000000 --- a/applications/SprPoP/docs/resident_budget.md +++ /dev/null @@ -1,111 +0,0 @@ -# Бюджет резидента 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 deleted file mode 100644 index ff5e341..0000000 --- a/applications/SprPoP/docs/room_model_plan.md +++ /dev/null @@ -1,86 +0,0 @@ -# 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 deleted file mode 100644 index f489af7..0000000 --- a/applications/SprPoP/docs/roomnav_skip.md +++ /dev/null @@ -1,65 +0,0 @@ -# Комнаты для отладочного телепорта (`+` / `-`) - -Считано скриптом прямо по `../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/sdlpop_audit.md b/applications/SprPoP/docs/sdlpop_audit.md deleted file mode 100644 index 2b047fc..0000000 --- a/applications/SprPoP/docs/sdlpop_audit.md +++ /dev/null @@ -1,755 +0,0 @@ -# Аудит расхождений с SDLPoP: Кид, стражи, seqtbl, отрисовка - -> Начат 2026-08-31. КОД НЕ МЕНЯЛСЯ — это только разбор. Задача: найти -> места, где наш движок может вести себя иначе, чем оригинал, и оценить -> вероятность того, что расхождение реально. - -## Как читать - -Ранги вероятности того, что расхождение ЕСТЬ и проявляется в игре: - -| ранг | смысл | -|---|---| -| **А** | гарантированное различие: код объективно разный, эффект понятен | -| **Б** | весьма вероятное: код разный, эффект вероятен, но не доказан | -| **В** | средневероятное: код разный, но эффект может гаситься другим местом | -| **Г** | маловероятное: различие есть в форме, эффект скорее отсутствует | -| **Д** | почти невероятное: сходство подтверждено, остаётся крайний случай | - -Ссылки вида `seg005:114` — строка в `assets/orig/SDLPoP/src/`. Наши -ссылки — `файл:строка` в `src/`. - -## Метод и охват - -Сравниваются НАШИ реализации с оригиналом построчно по функциям. Первый -проход (2026-08-31) охватил: - -* диспетчер `control()` (seg005:251) — целиком; -* `land()` (seg005:114) и `start_fall()` (seg006:1099); -* цепочку смерти: `control_kid` (seg006:1390), `play_kid` (seg006:1348), - `take_hp` (seg006:986), `control()` ветка `alive >= 0`. - -НЕ охвачено первым проходом (список для следующих): - -* интерпретатор `play_seq` и полный набор опкодов seqtbl; -* `frame_table` и модификаторы кадров; -* бой целиком: `control_with_sword`, парирование, `strike`, `hurt_by_sword`; -* ИИ стражей (`guard_ai`), особенности скелета, Тени, Джафара; -* `add_kid_to_objtable`/`add_guard_to_objtable`, порядок слоёв, `clip_char`; -* `check_bumped` — сверен ЧАСТИЧНО (находки 14, 15); `check_grab`, - `in_wall`, `check_bumped_look_left` — нет; -* `do_fall` целиком (проверен только вход). - ---- - -## Симптом, с которого начат аудит - -**Наблюдение (пользователь, 2026-08-31):** идёт бой, за Кидом провал на -этаж. Страж колет на последнем HP, Кид отшатывается назад и падает. -Кид умирает — но на экране он этажом ниже В ПРИСЕДЕ, как после мягкого -приземления. - -**Что говорит код SDLPoP.** Разбор цепочки: - -1. `start_fall` (seg006:1099) ПЕРВЫМ ДЕЛОМ убирает меч - (`Char.sword = sword_0_sheathed`) — для любого падения, независимо от - здоровья. Значит к моменту приземления меч уже в ножнах. -2. `land` (seg005:114) при падении на один ряд выбирает - `seq_63_guard_active_after_fall`, только если `charid >= guard` ИЛИ - меч вынут; иначе — `seq_17_soft_land`, то есть ПРИСЕД. Из-за п.1 для - Кида это всегда присед. -3. `land` НЕ смотрит ни на `alive`, ни на `hitp_curr` вовсе. -4. `control` (seg005:251) при мёртвом персонаже (`alive >= 0`) не - диспетчеризует ничего; он переводит в `seq_71_dying` ТОЛЬКО из - четырёх кадров стойки (15, 166, 158, 171). Присед в этот список не - входит. - -**Вывод:** по букве оригинала мёртвый Кид, застигнутый смертью в полёте, -тоже долетает, приземляется в присед и остаётся в нём — `control` его не -трогает. То есть наблюдаемое, СКОРЕЕ ВСЕГО, воспроизводится и в SDLPoP. - -Гипотеза «hp стал нулевым, поэтому спрятали меч» кодом НЕ подтверждается: -единственное место, где SDLPoP связывает `hitp_curr == 0` с чем-либо, — -`control_kid` (seg006:1395), и там взводится только `Char.alive = 0`. - -**Как проверить окончательно:** прогнать сцену на живом SDLPoP (он -собирается в `assets/orig/SDLPoP/`), где для этого уже есть наши врезки -`POP_TRACE`. До проверки считаю симптом НЕ доказанным расхождением — -ранг **В** (см. находку 5). - ---- - -## Находки первого прохода - -### 1. `land()`: лишний `determine_col()` — ранг **А** - -*Оригинал:* `land` (seg005:114) заканчивается тремя действиями: -`seqtbl_offset_char(seq_id)`, `play_seq()`, `Char.fall_y = 0`. Никакого -пересчёта колонки. - -*У нас:* `pop_map.c:715` (и в ветке смерти `:705`) после `play_seq()` -дополнительно вызывается `determine_col()`. - -*Чем грозит:* `determine_col` пересчитывает `Char.curr_col` из `Char.x`. -Оригинал делает это в другом месте и в другой момент — -`load_fram_det_col` (seg006:0144) перед разбором кадра. Лишний пересчёт -сразу после приземления может дать другую колонку, если `play_seq` -успел сдвинуть `x` первым же `dx`. Колонка — вход для проверок тайла, -пик и коллизий. - -*Замечание:* приём применён у нас системно (`pop_map.c` — 8 вызовов), то -есть это, вероятно, осознанная адаптация, но в `docs/impl_diff.md` она НЕ -записана. Нужно либо обосновать и записать, либо снять. - -### 2. `land()`: обнуляется ещё и `fall_x` — ранг **Б** - -*Оригинал:* в `land` обнуляется ТОЛЬКО `fall_y`, и притом в самом конце, -ПОСЛЕ `play_seq()`. - -*У нас:* `pop_map.c:697` и `:713` обнуляют пару `Char.fall_x = Char.fall_y = 0`, -причём ДО `pop_char_set_seq`/`play_seq`. - -*Чем грозит:* два отличия сразу. Во-первых, `fall_x` в оригинале -переживает приземление — если наш сброс лишний, теряется горизонтальный -импульс, влияющий на последующие кадры. Во-вторых, момент: если -`play_seq` читает `fall_y` (а он читает при обработке своих опкодов -движения), оригинал видит ещё НЕ обнулённое значение, а мы — уже ноль. - -### 3. `land()`: проверка пик до коррекции X — ранг **Б** - -*Оригинал:* сначала (внутри ветки «тайл под ногами не пика») делается -коррекция `Char.x = char_dx_forward(-3)` при `distance_to_edge_weight() < 3`, -и лишь ПОТОМ проверяется падение на пики, причём условие для пики ПОЗАДИ -использует `distance_to_edge_weight() >= 12`. - -*У нас:* `pop_map.c:664` — `fell_on_spikes()` вызывается ПЕРВЫМ, до -коррекции X. - -*Чем грозит:* `distance_to_edge_weight()` считается от `Char.x`, а -коррекция этот `x` меняет на 3 пикселя. У края тайла порядок решает, -попадёт ли персонаж в ветку «пика позади» — то есть умрёт он или нет. - -### 4. Смерть: у нас свой флаг вместо счётчика — ранг **В** - -*Оригинал:* `play_kid` (seg006:1348) после смерти ведёт СЧЁТЧИК -`Char.alive`, и по его значениям запускает музыку смерти (`alive == 6`) и -надпись «Press Button to Continue» (`alive == 7`), причём переход -задерживается, пока играет звук (`check_sound_playing`). - -*У нас:* `pop_ctrl.c` (`ctrl_kid_death`) взводит `pop_kid_dead` — сигнал -главному циклу на респавн; счётчика стадий нет. - -*Чем грозит:* момент респавна и порядок «музыка смерти → сообщение → -рестарт» могут отличаться, особенно если смерть застала персонажа в -длинной анимации (падение). Сюда же относится симптом выше: в оригинале -поза сохраняется, пока крутится счётчик. - -### 5. Мёртвый доигрывает приземление — ранг **В** - -Разобрано выше. Код у нас и в оригинале В ЭТОМ МЕСТЕ совпадает, поэтому -ранг не выше среднего: расхождение может сидеть не в `land`, а в моменте -взведения смерти (находка 4) — тогда оригинал успевает поставить кадр -смерти до приземления, а мы нет. Проверяется прогоном на живом SDLPoP. - -### 6. Диспетчер `control()` — ранг **Д** - -Сверен ветка в ветку (seg005:251 против `pop_ctrl.c:618`): совпадают и -порядок проверок (`bumped/freefall` → меч → charid → кадры), и границы -диапазонов кадров, и обработка мёртвого. Расхождений не видно; остаются -только опциональные `#ifdef`-фиксы SDLPoP, которых у нас нет намеренно. - -### 7. `JMP_IF_FEATHER`: у нас эффект только для Кида — ранг **Б** - -*Оригинал:* опкод `SEQ_JMP_IF_FEATHER` (seg006, play_seq) смотрит ТОЛЬКО -на глобальный `is_feather_fall`. Кто именно проигрывает последовательность, -роли не играет. - -*У нас:* `pop_kid.c:306` добавляет условие `Char.charid != CHARID_0_KID` — -для всех, кроме Кида, ветка «пера» не берётся никогда. - -*Чем грозит:* под зельем медленного падения любой НЕ-Кид, попавший в -последовательности `stepfloat`/`bumpfloat`, у нас пойдёт по обычной ветке -(с уроном), а в оригинале — по парящей. Практически это Тень (charid 1) на -уровне 4-6 и скелет; страж в эти seq попадает редко, но попадает через -`bumpfloat` при отскоке. - -*Замечание:* отличие ОСОЗНАННОЕ (в комментарии сказано «эффект достаётся -только Киду — как и сама физика пера»), но в `docs/impl_diff.md` не -записано, хотя правило проекта этого требует. - -### 8. `start_chompers` отложен до конца `play_seq` — ранг **Б** - -*Оригинал:* опкоды `SEQ_UP`/`SEQ_DOWN` меняют ряд и ТУТ ЖЕ зовут -`start_chompers()` — то есть внутри цикла интерпретатора, до разбора -следующих опкодов. - -*У нас:* `pop_kid.c:318-325` только взводит `chomp_pending`, а сам вызов -происходит после выхода из цикла (`:396`). Причина архитектурная и -описана в коде: seqtbl читается через окно W0, а `start_chompers` лезет в -другое окно. - -*Чем грозит:* два следствия. Во-первых, последовательность с ДВУМЯ -сменами ряда (`UP` `UP`, спуск/подъём по лестнице) в оригинале будит -чомперов в обоих рядах, у нас — только в конечном. Во-вторых, между -`SEQ_UP` и концом цикла успевают отработать `dx`/`dy`/`action`, то есть -оригинал будит чомперов с ДРУГИМИ координатами персонажа. - -Симптом «челюсти не заводятся» уже ловился в этом проекте (memory -`pop_chomper_needs_trigger`), и это место — кандидат в его причины. - -### 9. Набор опкодов seqtbl — ранг **Д** - -Сверены все пятнадцать кодов (`0xF1`..`0xFF`): совпадают и значения, и -семантика, включая проваливание `JMP_IF_FEATHER` в `JMP` и то, что -`SEQ_DIE` — пустышка в обоих движках. Отличия только в находках 7 и 8. - -### 10. Отрисовка: шаги те же, но разнесены — ранг **В** - -*Оригинал:* `add_kid_to_objtable` (seg008:1667) и его двойник для стража — -это строго упорядоченная цепочка: `loadkid`/`loadshad` → -`load_fram_det_col` → `load_frame_to_obj` → `stuck_lower` → -`set_char_collision` → `set_objtile_at_char` → `redraw_at_char` → -`redraw_at_char2` → `clip_char` → `add_objtable`. - -*У нас:* все звенья присутствуют, но распределены по слоям: `clip_char`, -`load_frame_to_obj`, `check_mirror`, брызги — в `pop_cdraw.c`; -`redraw_at_char`/`set_objtile_at_char`/`set_char_collision` — в `pop_bg.c` -(единый проход на всех Char, см. CLAUDE.md). - -*Чем грозит:* сам по себе перенос не ошибка, но ПОРЯДОК внутри цепочки -влияет на результат: `set_char_collision` и `set_objtile_at_char` готовят -данные, которыми пользуются `redraw_at_char` и `clip_char`. Если наш -общий проход выполняет их для ОБОИХ персонажей до отрисовки, а оригинал — -для каждого непосредственно перед его выводом, то при наложении Кида и -стража состояние на момент клипа будет разным. - -*Отдельно:* `stuck_lower` найден только в `pop_cdraw.h` — надо убедиться, -что он реализован, а не только объявлен. Если его нет, персонаж, -застрявший на границе тайла, будет рисоваться на пиксель выше. - -### 11. Порядок вывода Кида и стража — ранг **В** - -*Оригинал:* `draw_people` (seg008:1635) всегда ставит сначала Кида -(`draw_kid`), затем стража (`draw_guard`), а КТО ОКАЖЕТСЯ СВЕРХУ решает -`add_objtable` — таблица объектов упорядочена по позиции тайла. - -*У нас:* по CLAUDE.md порядок задаёт обход тайлов, «кто позже — тот -поверх». Это близко по смыслу, но не тождественно сортировке objtable. - -*Чем грозит:* при наложении персонажей (бой вплотную, страж перед Кидом) -верхний может оказаться другим. Проверять сравнением кадров боя вплотную -с эталонным SDLPoP (метод — memory `pop_pixel_diff_vs_sdlpop`). - -### 12. `hurt_by_sword`: ветка «сбит с уступа» не портирована — ранг **А** - -*Оригинал:* `hurt_by_sword` (seg002:911) при уколе ВООРУЖЁННОГО персонажа -выбирает одну из двух смертей по обстановке ПОЗАДИ: - -* тайл позади не пустой ИЛИ до кромки меньше 4 → `seq_85_stabbed_to_death` - (заколот на месте); -* иначе → `seq_81_kid_pushed_off_ledge` — отдельная последовательность - «убит и сброшен с уступа», которая сама отыгрывает падение замертво. - -*У нас:* `guards.c:1015` — ветки `seq_81` НЕТ вовсе, всегда `seq_85`. -Упрощение ЗАДОКУМЕНТИРОВАНО в комментарии (`guards.c:1011`): ей нужны -тайловые запросы от `Char`, а `pop_map` умеет их только от `Kid`. - -*Чем грозит:* именно тем, что наблюдал пользователь. Заколотый на краю -обрыва Кид в оригинале уходит в собственную анимацию падения с уступа; у -нас он получает «смерть на месте», продолжая при этом висеть в воздухе — -дальше им распоряжается обычная физика падения, и он приземляется этажом -ниже по общим правилам (а с убранным в `start_fall` мечом — в присед, -находка 5). - -*Как проверить:* поставить Кида спиной к обрыву с 1 HP и дать стражу -уколоть. В оригинале — падение замертво (кадры seq_81), у нас — смерть -на месте с последующим отдельным падением. - -### 13. `hurt_by_sword`: прижатие к полу стало безусловным — ранг **А** - -*Оригинал:* `Char.y = y_land[Char.curr_row + 1]` и `Char.fall_y = 0` -выполняются ТОЛЬКО в ветке выжившего удара (seg002:962, рядом с -`seq_74_hit_by_sword`). Смертельные ветки координату не трогают. - -*У нас:* `guards.c` — те же две строки стоят ПОСЛЕ всего `if/else`, то -есть выполняются и при смерти тоже. - -*Чем грозит:* персонажа, убитого в воздухе, мы принудительно ставим на -пол текущего ряда и обнуляем накопленную скорость падения. Дальше физика -обнаруживает, что пола под ним нет, и запускает падение ЗАНОВО — уже без -`fall_y`, то есть с другой высотой и другим исходом приземления. Это -вторая половина механизма из находки 12 и вероятная причина того, что -мёртвый Кид доезжает до нижнего этажа «своим ходом». - -*Как проверить:* тот же сценарий; в отладчике смотреть `Char.y` и -`fall_y` сразу после попадания — оригинал их не меняет. - -### 14. Отскок с мечом: нет `seq_64` — ранг **Б** - -*Оригинал:* при отскоке (`bumped`, seg004:328) живой персонаж с вынутым -мечом получает ОДНУ ИЗ ДВУХ последовательностей по направлению толчка: -толкнули вперёд — `seq_65_bump_forward_with_sword`, отбросило назад — -`seq_64_pushed_back_with_sword`. - -*У нас:* `pop_map.c` знает только `SEQ_65_BUMP_FWD_SWORD` (объявлен на -`:103`, используется на `:2231`); константы и ветки `seq_64` нет вовсе. - -*Чем грозит:* персонаж, отброшенный назад с мечом (страж у стены, Кид в -тесной комнате), проигрывает не ту анимацию — либо ветку без меча, либо -`seq_65`. Кадры разные, а вместе с ними расходятся и смещения `dx` в -последовательности, то есть итоговая позиция после отскока. - -*Как проверить:* бой вплотную к стене, толчок в сторону стены и от неё; -сверять кадры с эталонным прогоном SDLPoP. - -### 15. `check_bumped_look_right`: гейт по направлению — ранг **В** - -В нашей реализации (`pop_map.c:2148`) стоит ранний выход по -`Char.direction` с пометкой «(меча в руке у нас нет)». Пометка означает, -что ветка писалась до появления боя, а оригинал в этом месте учитывает и -меч, и `push_direction` (находка 14). Область `check_bumped_look_left` -не сверялась вовсе — её надо пройти отдельно. - -### 16. `control_with_sword` — ранг **Д** - -Сверен целиком (seg005:964 против `pop_ctrl.c:587`): гейт по `action`, -условие «пол под ногами loose ИЛИ страж видит Кида», пороги дистанции -(90 и −4), `seq_60_turn_with_sword`, ветка «соперник умер» с -`seq_92_put_sword_away`, разделение по `charid`. Совпадает. - -Отдельно отмечу: в оригинале сравнение дистанции сделано ЗНАКОВО-НЕЯВНО -(приведением к `word`), из-за чего ветка «соперник за спиной» вообще -достижима. У нас то же самое выражено явными знаковыми сравнениями — и -диапазоны совпадают, включая «вплотную за спиной» (−4..−1), где обе -реализации ведут бой, а не разворачиваются. - -### 17. `parry` — ранг **Д** - -Сверен целиком (seg005:1064 против `pop_ctrl.c:513`): список кадров -стойки, порог 32 для не-Кида, обработка кадров соперника (151/152/162, -особый случай 153 с отложенным `play_seq`), ветка стража по кадру 152, -ветка `frame_167_blocked` с `seq_61`, сброс автоповтора `control_up`. -Совпадает вплоть до порядка условий. - -### 18. `check_hurting`: звук «меч в движении» в других условиях — ранг **Б** - -*Оригинал:* звук 11 играется в САМОМ КОНЦЕ `check_hurting` (seg002) и -только при трёх условиях сразу: направление персонажа не `none`, его кадр -— укол (154), а соперник при этом НЕ парирует и НЕ ранен. Первое условие -добавлено в SDLPoP специально против зацикливания звука. - -*У нас:* `guards.c:1068` — звук играется в начале ветки укола, -безусловно, ещё до того, как определено попадание. - -*Чем грозит:* лишние срабатывания в двух ситуациях, где оригинал молчит — -когда удар парирован и когда он попал. То есть в самой частой части боя -звук звучит чаще, чем должен. Плюс отсутствует защита от зацикливания -при `direction == none`. - -*Как проверить:* бой с парирующим стражем; считать срабатывания звука 11 -на серии ударов и сравнить с эталонным прогоном SDLPoP. - -### 19. Остальной бой сверен — ранг **Д** - -Прочитаны целиком и совпадают: - -* `swordfight` (seg005:998) — включая ветку кадра 161, `sword_strike`, - побочные эффекты уборки меча (`offguard`, `guard_refrac`, - `holding_sword`), разделение `seq_93`/`seq_92`/`seq_87` по `charid` и - хвост (`parry` / `forward_with_sword` / `back_with_sword`); -* `sword_strike` (seg005:1037) — список кадров, выбор `seq_75`/`seq_58`, - `seq_66` после парирования, сброс автоповтора; -* `check_sword_hurt` (seg002:971) — включая ПРИОРИТЕТ СТРАЖА при - одновременном ранении и сброс `Kid.action` в бег, а также - `refractimer` по навыку; -* `check_hurting` в основной части — гейты по мечу, ряду и кадрам, пороги - дистанции (29), `min_hurt_range` 8/12 по мечу соперника, ветка - парирования с `justblocked` и `seq_69`. Единственное расхождение — - звук, находка 18. - -### 20. Стражи: «стена впереди» сужена до одного тайла — ранг **Б** - -*Оригинал:* `guard_follows_kid_down` (seg002:811) и соседние ветки ИИ -спрашивают `wall_type(tile) != 0`. Эта функция (seg006:1626) считает -преградой ПЯТЬ видов тайлов: ворота, верх двери с полом, верх двери, -зеркало, чомпер и собственно стену — с разной стороной блокировки. - -*У нас:* `guards.c:683` и `:685` сравнивают тайл напрямую с `TILE_WALL` -(тип 20). Ворота, верх двери, зеркало и чомпер преградой не считаются. - -*Причина:* `wall_type` реализована у нас (`pop_map.c:504`, таблица на -`:498`), но НЕ экспортирована — в `pop_map.h` её нет, поэтому `guards.c` -до неё не дотягивается. То есть это не пробел в портировании логики, а -следствие границы модулей. - -*Чем грозит:* страж, преследующий упавшего Кида, у нас шагнёт вперёд там, -где оригинал отступает — перед закрытыми воротами, верхом двери, -зеркалом и чомпером. Отсюда возможны и проход стража сквозь препятствие, -и падение туда, куда оригинал его не пускает. - -*Как проверить:* уровень с воротами (например, 3-й) — заманить стража к -закрытым воротам после падения Кида и сравнить, отступает ли он. - -*Замечание:* в `guards.c` таких мест ЧЕТЫРЕ (`:148`, `:683`, `:685`); -одно из них (`:148`) уже перечисляет три тайла вручную, то есть -расхождение частично компенсировано, но не везде одинаково. - ---- - -## Область: столкновение со стенами и падение внутри стены - -Заведена по наблюдению пользователя (2026-08-31): разбег, прыжок сделан -рано, Кид не долетел, врезался в стену и начал падать — но по X он -оказался ВНУТРИ стены и падал частично в ней. - -### 21. Фикс «скольжения сквозь стену» — ПОРТИРОВАН 2026-08-31 - -> Решением пользователя исправление взято в ТЕКУЩИЙ билд: играбельность -> важнее буквальности. Реализация — `glide_through_wall_guard()` в -> `pop_map.c`, вызывается из `do_fall` в ветке «ещё летим». Цена: +57 -> байт в банке 3, резидент и куча не изменились. -> -> Подтверждено host-тестом: в наборе `t_wall` число заходов в кладку -> упало с двух до нуля, остальные 15 наборов остались зелёными. -> -> Ниже — исходный разбор, по которому принималось решение. - -### 21a. Исходный разбор: было соответствие ванили — ранг **Г** - -*Оригинал:* в `do_fall` (seg005:37) есть блок `FIX_GLIDE_THROUGH_WALL` с -собственным комментарием SDLPoP: «Кид падает сквозь стены после разворота -в беге, особенно в невесомости». Блок опциональный — то есть в ВАНИЛЬНОЙ -игре этот баг ЕСТЬ, а SDLPoP его чинит по желанию. Рядом такие же -опциональные `FIX_JUMP_THROUGH_WALL_ABOVE_GATE` и `FIX_DROP_THROUGH_TAPESTRY`. - -*У нас:* ни один из трёх не портирован — мы намеренно повторяем ваниль. - -*Вывод по симптому:* «падение частично в стене» — с большой вероятностью -ОРИГИНАЛЬНОЕ поведение PoP, а не наша ошибка. Ранг Г означает: различия -с ванилью, скорее всего, нет. Но проверить стоит другое — не ХУЖЕ ли у -нас, чем в ванили (см. находки 22 и 15). - -*Как проверить:* повторить сцену на живом SDLPoP с выключенными фиксами -(они выключаемы в его настройках) и сравнить глубину захода в стену. - -### 22. `do_fall`: наш гард `curr_row <= 2` — ранг **В** - -*Оригинал:* в `do_fall` ветка «достигли нового ряда» выполняется БЕЗ -условия на номер ряда: проверка тайла стены с вызовом выталкивания, затем -`land()` либо переход на ряд ниже. - -*У нас:* `pop_map.c:909` — вся ветка обёрнута в `if (Char.curr_row <= 2)`. -Причина задокументирована (`:768`): наш `get_tile` за нижней кромкой -комнаты отдаёт СТЕНУ как сентинель, тогда как в оригинале там комната -снизу, и без гарда выталкивание срабатывало ложно, смещая падение на тайл. - -*Чем грозит:* гард гасит не только ложные срабатывания. Если персонаж -достиг `curr_row == 3` легитимно (падение между комнатами по вертикали), -у нас не выполнится ни выталкивание из стены, ни `land()`, ни переход -ряда — всё это ляжет на следующий кадр и другую ветку. Именно такая -комбинация (падение у границы комнаты рядом со стеной) даёт кандидата в -причины наблюдения пользователя. - -*Как проверить:* падение вдоль стены точно на стыке комнат по вертикали; -в отладчике смотреть `curr_row`, `Char.x` и факт вызова выталкивания. - -### 23. `bumped_fall` — ранг **Д** - -Сверен (seg004 против `pop_map.c`): откат X на 4 пикселя назад, обнуление -горизонтальной скорости в свободном падении, иначе `seq_45_bumpfall` с -проигрыванием, звук удара. Совпадает; у нас добавлен только флаг «стражи -услышали», который в оригинале ставится внутри звуковой функции. - -**Замечание по области:** глубина отката при столкновении — ровно 4 -пикселя в обоих движках. Если Кид вошёл в стену глубже (а при -недолёте с разбега скорость по X велика), одного отката не хватит ни там, -ни у нас — и дальше всё зависит от того, сработает ли выталкивание из -стены на следующем кадре. У нас его может съесть гард из находки 22. -Это главная зацепка по симптому. - -### 24. `in_wall`: не перезагружается кадр — ранг **Б**, ОТЛОЖЕНА - -> **Правка сделана и откачена 2026-08-31.** По букве оригинала находка -> верна, но практического эффекта показать не удалось: все 15 наборов -> host-тестов дали одинаковый результат до и после, включая специально -> написанный тест на заход в кладку (`t_wall`). При этом правка не -> бесплатна — добавляет маппинг окна и перезагрузку кадра на каждое -> выталкивание из стены. Платить за недоказанное не стали. -> -> Задача переехала в `docs/vanilla_vs_bugfixed.md`: вернуться к ней при -> работе над двумя режимами поведения, где появится сценарий, в котором -> кадр меняется перед выталкиванием. - - -*Оригинал:* `in_wall` (seg006) после выталкивания персонажа из стены -делает `load_fram_det_col()` — ЗАГРУЖАЕТ КАДР и следом определяет колонку, -затем перечитывает тайл. - -*У нас:* `pop_map.c` (`in_wall`) вызывает только `determine_col()`. -Пороги (`>= 8`), формулы смещения (`6 - d` и `d + 4`), условие по тайлу -впереди и финальное чтение тайла совпадают — расходится только этот шаг. - -*Чем грозит:* после выталкивания данные кадра (картинка, смещения, флаги -— включая «нужен пол» и «чётный пиксель») остаются от позиции ДО -коррекции, а ими пользуются проверки того же кадра: падение, клип, -коллизия. Это ровно область, где наблюдалось падение внутри стены -(находки 21, 22). - -*Как проверить:* недолёт с разбега в стену; в отладчике сравнить `Char.x`, -колонку и поля текущего кадра сразу после выталкивания. - -### 25. Таблицы кадров и seqtbl — ранг **Д** - -`frame_table_kid`, `original_seqtbl` и таблица смещений извлекаются -АВТОМАТИЧЕСКИ из исходников SDLPoP (`tools/pop_extract_kid_data.py` → -`gen/kid_data.h`), поэтому расхождение в данных маловероятно по -построению. Применение тоже сверено: используются все четыре флага кадра -(«нужен пол» 0x40, вес по X 0x1F, «тонкий» 0x20, чётный пиксель 0x80), а -байт клинка маскируется как в оригинале (`& 0x3F`, `pop_kdraw.c:31`). - -Не сверено: старшие два бита байта клинка (номер набора спрайтов) — у -Кида он нулевой, у прочих персонажей стоит проверить отдельно. - -### 26. Полнота автоуправления — ранг **Д** - -Из двенадцати функций `autocontrol_*` оригинала у нас есть одиннадцать. -Отсутствующая — тривиальная обёртка над общей логикой стража; у нас она -встроена в вызывающего. Расхождения нет. - -Не сверены ПОСТРОЧНО тела: `autocontrol_guard_kid_armed`, -`autocontrol_guard_kid_far`, `autocontrol_shadow*`, `autocontrol_skeleton`, -`check_grab`, `check_bumped_look_left`, `back_with_sword`, -`forward_with_sword`. - ---- - -## ИТОГ АУДИТА - -Проверено 26 позиций за пять проходов. - -| ранг | находки | суть | -|---|---|---| -| **А** | 1, 12, 13 | лишний пересчёт колонки в `land`; нет ветки «убит и сброшен с уступа»; безусловное прижатие к полу при смерти | -| **Б** | 2, 3, 7, 8, 14, 18, 20, 24 | `fall_x` и момент сброса; порядок «пики / коррекция X»; перо только для Кида; отложенные чомперы; нет `seq_64`; звук удара; «стена» сужена до одного тайла; нет перезагрузки кадра в `in_wall` | -| **В** | 4, 5, 10, 11, 15, 22 | флаг смерти вместо счётчика стадий; мёртвый доигрывает приземление; разнесённая цепочка отрисовки; порядок Кид/страж; гейт в `check_bumped_look_right`; гард `curr_row <= 2` в `do_fall` | -| **Г** | 21 | опциональные фиксы SDLPoP не портированы — соответствие ванили | -| **Д** | 6, 9, 16, 17, 19, 23, 25, 26 | сверено и совпадает | - -### Три узла, вокруг которых группируются расхождения - -1. **Смерть при активной физике** (1, 12, 13, 4, 5). Здесь все находки - ранга А. Общая причина: у нас смерть — это флаг, а физика продолжает - работать с персонажем как с живым. -2. **Границы модулей** (12, 20, 24). `pop_map` не отдаёт наружу то, что - нужно `guards.c` и работе с произвольным `Char`: тайловые запросы от - `Char`, `wall_type`, загрузку кадра. Ветки упрощались не по логике, а - по доступности функций. -3. **Момент побочных действий** (2, 8, 18, 24). Делаем то же самое, но - раньше или позже оригинала: сброс скорости, побудка чомперов, звук, - перезагрузка кадра. По отдельности мелочь, вместе — сдвиг состояния - на кадр. - -### Что делать дальше - -1. Проверить находки А и Б в MAME по сценариям из их описаний — начиная с - 12/13 (смерть на краю) и 24 (выталкивание из стены). -2. Те же сцены прогнать на живом SDLPoP: часть наблюдений может оказаться - ванильным поведением (как находка 21). -3. Подтверждённые осознанные отличия записать в `docs/impl_diff.md` — - сейчас там нет ни одного из найденных, хотя правило проекта требует. - - -Порядок по ожидаемой отдаче: - -1. **Бой** — ПРОЙДЕН. Находки: 12, 13 (ранг А), 18 (Б); совпадают - `control_with_sword`, `parry`, `swordfight`, `sword_strike`, - `check_sword_hurt`, `check_hurting` (кроме звука). Не сверены мелочи: - `back_with_sword`, `forward_with_sword`, `check_skel`. -2. **`play_seq` и опкоды** — самая опасная область: ошибка в одном опкоде - меняет все последовательности разом. -3. **Отрисовка** — `add_kid_to_objtable`/`add_guard_to_objtable`, порядок - слоёв, `clip_char`. -4. **`do_fall`/`check_bumped`/`check_grab`** — остаток физики. -5. **ИИ стражей** и особенности скелета/Тени/Джафара. - - ---- - -# Глубокое ревью находок А и Б: можно ли починить и чем платим - -> 2026-08-31. КОД ПО-ПРЕЖНЕМУ НЕ МЕНЯЛСЯ. Здесь только оценка. -> -> **Главное ограничение (требование пользователя): фикс не должен заметно -> замедлять игру.** Поэтому у каждой находки первым делом указана ЧАСТОТА -> вызова места, а уже потом сама правка. -> -> **Оговорка к ограничению:** для КРИТИЧНЫХ фиксов скорость — не вето. -> Если такой фикс всерьёз бьёт по производительности, он выносится в -> отдельный разбор, где ищется способ получить правильное поведение -> дёшево (иной момент вызова, кэш, предвычисление, перенос в холодный -> путь). То есть порядок такой: сначала решаем, критично ли поведение, и -> только потом — какой ценой его добиться. - -## Снятое препятствие - -Обоснование сразу двух упрощений (`guards.c:1011` — «нужны тайловые -запросы ОТ Char, а pop_map умеет только от Kid») **устарело**. Проверено: -`get_tile_at_char`, `get_tile_infrontof_char`, `get_tile_behind_char` и -`distance_to_edge_weight` в `pop_map.c` УЖЕ работают от `Char` -(строки 460, 465, 477, 561). Они лишь не выведены в `pop_map.h`. - -Так же обстоит с данными: `pop_char_set_seq()` ставит любую из 115 -последовательностей по индексу, то есть `seq_81` и `seq_64` доступны без -единого нового байта данных — таблица генерируется из оригинала целиком. - -То есть три находки (12, 14, 20) упираются не в архитектуру, а в четыре -строки объявлений. - -## Классификация мест по частоте вызова - -| место | частота | вывод | -|---|---|---| -| `play_seq` (находки 7, 8) | КАЖДЫЙ кадр каждого персонажа | правка обязана быть бесплатной | -| `check_hurting` (18) | каждый кадр боя, дважды | почти горячий | -| ИИ стража (20) | каждый кадр, пока страж активен | почти горячий | -| `land`, `in_wall`, `bumped` (1, 2, 3, 14, 24) | событие раз в несколько секунд | холодный, цена не важна | -| `hurt_by_sword` (12, 13) | момент попадания | холодный | - -## Разбор по находкам - -### 12 + 13 (ранг А) — смерть на краю. ТОЛЬКО ВМЕСТЕ, НЕ ПООТДЕЛЬНОСТИ - -> **Проверено на живой машине 2026-08-31 и провалилось.** Правка 13 была -> сделана в изоляции (прижатие к полу перенесено в ветки пережитого -> удара) — и сломала смерть: страж бьёт Кида, тот погибает, а вместо -> нормальной смерти идут вспышка, стопкадр и немедленный выход в -> заставку. -> -> Причина: у нас прижатие к полу работало КОМПЕНСАЦИЕЙ отсутствующей -> ветки «убит и сброшен с уступа» (находка 12). Убрав компенсацию и не -> добавив то, что она компенсировала, мы оставляем мёртвого персонажа с -> ненулевой `fall_y` и незакреплённой `Char.y` — физика продолжает вести -> его вниз, он проваливается за пределы уровня, и срабатывает аварийный -> путь. -> -> Вывод: обе находки — ОДНА правка. Оценка «чистое перемещение строк, -> риск низкий» была неверной; риск ВЫСОКИЙ, пока ветка 12 отсутствует. -> Порядок внутри правки: сперва добавить ветку `seq_81` (с экспортом -> тайловых запросов), убедиться, что смерть на краю отыгрывается ею, и -> только затем убирать безусловное прижатие. - - -*Место:* `guards.c`, `hurt_by_sword` — холодный путь. - -*Правка:* (а) перенести две строки прижатия к полу внутрь ветки -выжившего удара — это чистое перемещение, минус ноль байт; (б) добавить -ветку выбора `seq_81` по тайлу позади и расстоянию до кромки. - -*Что нужно:* экспорт `get_tile_behind_char()` и `distance_to_edge_weight()` -из `pop_map.c` в `pop_map.h` как `__banked`. - -*Цена скорости:* два межбанковых вызова (`guards.c` — банк 1, `pop_map.c` -— банк 3) в момент попадания мечом, то есть несколько раз за бой. -Незаметно. - -*Цена памяти:* банк 1 занят на 19,8 % (13 142 Б свободно) — места вдоволь; -банк 3 занят на 81,1 % (3 100 Б), но там прибавятся только две обёртки. - -*Риск:* низкий. Ветка симметрична существующей, данные есть. - -### 1 (ранг А) — лишний `determine_col()` в `land` - -*Место:* холодный путь. *Правка:* убрать вызов и проверить, не -понадобился ли он нам вместо оригинального `load_fram_det_col`, который -оригинал делает в другом месте цепочки. *Цена:* отрицательная (кода -меньше). *Риск:* СРЕДНИЙ — вызов мог компенсировать наш иной порядок -загрузки кадра; убирать только с прогоном сцен падения и приземления. - -### 2, 3 (ранг Б) — `land`: `fall_x` и порядок проверки пик - -*Место:* холодный. *Правка 2:* сбрасывать только `fall_y` и после -`play_seq`, как оригинал. *Правка 3:* перенести проверку пик после -коррекции X. *Цена:* нулевая, это перестановка строк. *Риск:* низкий, -но обе меняют поведение на краю тайла — нужны прогоны с пиками. - -### 24 (ранг Б) — `in_wall` не перезагружает кадр. Одна строка - -*Место:* холодный. *Правка:* заменить `determine_col()` на -`pop_load_fram_det_col()` — он УЖЕ экспортирован (`pop_kid.h:109`) и, что -важно, НЕ банковый, то есть вызов прямой. *Цена скорости:* одна -перезагрузка кадра при выталкивании из стены — доли процента кадра. -*Риск:* низкий; это возврат к оригиналу. - -### 14 (ранг Б) — нет `seq_64` - -*Место:* `bumped`, холодный. *Правка:* добавить выбор между 64 и 65 по -направлению толчка. *Цена:* нулевая. *Риск:* низкий. - -### 18 (ранг Б) — звук удара - -*Место:* `check_hurting` — дважды за кадр боя. *Правка:* перенести звук -в конец функции и обвесить тремя условиями оригинала. *Цена:* -ОТРИЦАТЕЛЬНАЯ — звук перестанет играть в двух случаях из трёх, то есть -уменьшится и число обращений к звуковой очереди. *Риск:* низкий. - -### 20 (ранг Б) — «стена» у стражей. Требует осторожности со скоростью - -*Место:* ИИ стража — вызывается каждый кадр, пока страж активен. - -*Плохой вариант:* экспортировать `wall_type` из `pop_map.c` и звать из -`guards.c`. Это МЕЖБАНКОВЫЙ вызов (банк 1 → банк 3) в почти горячем -пути — трамплин с переключением W3 на каждый шаг ИИ. Против требования -по скорости. - -*Хороший вариант:* завести копию таблицы `wall_type_tbl` (32 байта) в -rodata банка 1 и обращаться к ней напрямую — стоимость чтения байта, -ноль переключений банка. Дублирование данных здесь оправдано: таблица -константная и вшита в формат уровней. - -*Риск:* низкий, но нужно следить, чтобы копия не разошлась с оригиналом — -лучше генерировать обе из одного места или снабдить перекрёстным -комментарием. - -### 7 (ранг Б) — перо только для Кида. Правка ускоряет - -*Место:* `play_seq`, самый горячий путь. *Правка:* убрать лишнее условие -по `charid`. *Цена:* ОТРИЦАТЕЛЬНАЯ — из горячего цикла уходит сравнение. -*Риск:* средний: надо убедиться, что физика пера у нас применяется к -любому персонажу так же, как в оригинале, иначе анимация разойдётся с -физикой. - -### 8 (ранг Б) — отложенные чомперы. Чинить ДЕШЁВЫМ способом - -*Место:* `play_seq`, горячий путь. - -*Почему отложено:* `play_seq` маппит страницу байткода в окно W0 ОДИН раз -перед циклом (`pop_kid.c:284`) и снимает после (`:389`). Вызвать -`start_chompers` внутри цикла — значит снять окно, позвать, вернуть окно, -и так на каждый переход ряда. Это прямая деградация горячего пути и -против требования по скорости. - -*Дешёвая замена:* сейчас копится ОДИН флаг, из-за чего теряются -промежуточные ряды. Достаточно копить не флаг, а НОМЕРА рядов — один -байт-битовую маску (рядов всего 0..3) плюс запомненную колонку. После -цикла пройти по взведённым битам и разбудить чомперов в каждом. Цена в -цикле: одна операция «выставить бит» вместо присваивания флага, то есть -ноль. Разница с оригиналом останется только в МОМЕНТЕ побудки (после -цикла, а не внутри), но ряды перестанут теряться. - -*Риск:* низкий. Полное совпадение с оригиналом здесь недостижимо без -потери скорости — это осознанный компромисс, который надо записать в -`docs/impl_diff.md`. - -## Сводка: что делать в каком порядке - -| приоритет | находки | почему | -|---|---|---| -| 1 | 24 | одна строка, риск низкий, готовая экспортированная функция | -| 2 | 12+13 ВМЕСТЕ | порознь ломают смерть (проверено); нужен экспорт двух функций | -| 3 | 18, 8 | обе УСКОРЯЮТ или бесплатны; 8 — по дешёвому варианту | -| 4 | 2, 3, 20 | перестановки и копия таблицы; нужны прогоны | -| 5 | 1, 7 | риск средний: обе могут компенсировать наши отличия в другом месте | - -**Ни один фикс не требует переделки архитектуры и ни один не ложится на -горячий путь с накладными расходами** — при условии, что находка 20 -делается копией таблицы, а находка 8 — битовой маской рядов. diff --git a/applications/SprPoP/docs/shadow_render.md b/applications/SprPoP/docs/shadow_render.md deleted file mode 100644 index af0a39b..0000000 --- a/applications/SprPoP/docs/shadow_render.md +++ /dev/null @@ -1,160 +0,0 @@ -# Отрисовка Тени (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 deleted file mode 100644 index 96af4c6..0000000 --- a/applications/SprPoP/docs/sound_plan.md +++ /dev/null @@ -1,1286 +0,0 @@ -# Звук в порте 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). Разница -есть, и она влияет на выбор источника. - -### Оцифровка: по умолчанию SDLPoP, MSDOS — опция - -**С 2026-08-31 умолчание — SDLPoP** (`SND_SRC=sdlpop`): он выкачивается -автоматически (`make fetch`), а оригинального дистрибутива DOS у сборки -может не быть вовсе — и она обязана работать без него. У кого дистрибутив -есть, включает лучший набор явно: `make SND_SRC=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 фактически пустой: это звук насаживания на пики -(`spiked()`, seg005.c:245, кадр 177), и с умолчанием он не звучит — от пик -остаётся только шипение выдвижения (49 `spikes`). Звон клинков (10) на -секунду короче, а 11 просто другая запись. - -Всё остальное — включая уровни `LEVELS/res20xx.bin` — берётся из SDLPoP -всегда: `MSDOS/` нужен ровно для этих трёх звуков и ни для чего больше. - -### 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), курсор уходит далеко -за размер кольца — то есть подкачка успевает. - -## 9. ЗАДЕРЖКА СТАРТА ЗВУКА: ЗАМЕР (2026-08-31) - -Замерено в MAME на живой сборке: точка отсчёта — подстановка заявки -отладчиком при ОСТАНОВЛЕННОЙ машине, конец — условный watchpoint -`wpset 19FF6,1,w,{wpdata != 0}` на старший байт `pop_mus_left`, то есть -момент, когда трек реально зазвучал. Масштаб тактов откалиброван не по -памяти, а по аппаратным часам: период тика насоса CBL — ровно -`128/10937,5 = 11,7029` мс, измерено 245 748 и 245 778 тактов, отсюда -**21,00 МГц** (кадр луча — 429 973 такта, развёртка 48,8 Гц, не 50). - -### 9.1 Эффекты стартуют без задержки, музыка — через ~200 мс - -Разница принципиальная и вытекает из устройства: - -* **Оцифрованные эффекты диска не касаются вовсе.** `pop_sfx_init()` - вызывается ОДИН раз за запуск (`sprpop_cold.c`), вычитывает весь - `SND/snd.arc` в EMM и закрывает файл. Дальше `pop_sfx_play` только - номинирует, а `pop_sfx_tick` берёт `page/off/len` из таблицы в памяти. - Задержка = остаток текущего кадра + ближайший запрос CBL (≤ 11,7 мс). - Держать архив открытым НЕЧЕГО: после старта он не нужен. -* **Музыка читается с диска на каждый трек** — `mNN.bin` лежат - отдельными файлами, и `pop_music_load_begin` открывает свой. - -### 9.2 Разложение окна музыки - -От заявки (`pop_mus_req`) до звучания (`pop_mus_left != 0`), одна -загрузка по шагам: - -| шаг | цена | -|---|---:| -| ожидание `pop_music_service` в кадре | 22,6 мс | -| `chdir` #1 (вход в `POP_PATH_CALL`) | **46,1 мс** | -| `open` файла трека | 34,5 мс | -| `chdir` #2 (восстановление каталога) | **59,0 мс** | -| чтение первых 8 КБ (`bank_read_page`) | 34,0 мс | -| **итого** | **196,7 мс** | - -**Главное здесь: два `chdir` стоят 105 мс — больше половины окна, и -каждый дороже самого `open`.** Убрать их нельзя: старые DSS оставляют -рабочий каталог внутри последнего подкаталога, и без восстановления -ломаются относительные пути (ради этого `POP_PATH_CALL` и заведён). -Теоретический запас — снять ОДИН из двух (≈ 50 мс), но это риск для -других мест, которые полагаются на восстановленный каталог. - -Для музыки задержка непринципиальна: трек и так начинается на границе -события. Критична она была бы для эффектов — там её нет по построению. - -### 9.3 Разброс и режимы скорости - -Семь прогонов: **189,3 … 232,8 мс**, среднее 200,1. Величина -ФИЗИЧЕСКАЯ и от режима скорости не зависит, меняется только её пересчёт -в логические кадры: - -| режим | логический кадр (замерено) | окно в кадрах | -|---|---:|---:| -| NORMAL вне боя | 81,93 мс = 4,00 кадра луча | 2,31 … 2,84 | -| FAST / FASTEST вне боя | 61,4 мс = 3,00 кадра луча | 3,09 … 3,42 | - -Худший случай (232,8 мс в коротком кадре) — 3,79 логического кадра. -Пейсер при этом работает ровно как задумано: `pop_pace_n` заказывает 4 -кадра луча вне боя при NORMAL и 3 при FAST/FASTEST, замер это -подтвердил. Подкачка трека кадр НЕ просаживает. - -### 9.4 Следствие для паузы конца уровня - -Ждать окончания мелодии по одному `pop_music_busy()` НЕЛЬЗЯ: в этом окне -он ещё ложен, и уровень сменился бы до первой ноты. Правильный признак -— состояние «аудио-событие в работе», которое выводится из уже -существующих переменных без новых сущностей: - -```c -uint8_t pop_music_active(void) __banked /* банк 9 — там ld_busy */ -{ - return (uint8_t)(pop_mus_req || ld_busy || pop_mus_left); -} -``` - -Переход «запрошен → грузится → звучит» снаружи непрерывен: `service` -обнуляет `pop_mus_req` и тут же в `load_begin` взводит `ld_busy`, а -главный цикл между этими операторами не выполняется. - -Это лучше фиксированной задержки «на раскачку»: не нужна подобранная -константа, нет зависимости от режима скорости, а при провале загрузки -(нет файла, нет памяти) все три признака ложны и уровень меняется сразу, -вместо слепого ожидания. Заодно чинится расхождение с SDLPoP при -ВЫКЛЮЧЕННОМ звуке: `pop_sfx_play` возвращается рано при `!pop_snd_ok`, -заявка не кладётся, паузы нет — как в оригинале (`seg006:651` ставит -трек только `if (is_sound_on)`, а `seg003:387` ждёт -`check_sound_playing()`). Сейчас `pop_endmus_left` взводится -безусловно, и при выключенном звуке игра держит пройденный уровень -лишние 12 с молча. - -## 10. ЭФФЕКТЫ БЕЗ ПЕРЕЛИНКОВКИ: РАСКЛАДКА УЕХАЛА НА ДИСК (2026-08-31) - -Сделана часть II плана `music_runtime_index_plan.md` (этапы SI0..SI4). -Раскладка звукового набора больше не компилируется в EXE: пара -`SND/snd.arc` + `SND/snd.idx` меняется целиком, бинарник остаётся прежним. - -### 10.1 Что именно изменилось - -`gen/pop_sound_tbl.h` УДАЛЁН — упаковщик его не печатает. Вместо него: - -* `src/pop_snd_tbl.h` — рукописный: тип записи и инварианты программы - (`POP_SND_COUNT` 57, `POP_SND_BLOCK` 128, `POP_SND_MAX_PAGES` 16, - константы блока тишины). От набора не зависит НИЧЕГО из этого; -* `src/pop_snd_data.c` — резидентные данные: `pop_snd_tbl[57]`, - `pop_snd_page[16]`, `pop_snd_pages`; -* `SND/snd.idx` — дисковый индекс, формат `PSI1` (писатель и разборщик — - `tools/pop_idx.py`, покрыт `make test-tools`, 22 теста); -* `pop_sfx_cold.c` — чтение и полная валидация индекса. - -`POP_SND_PAGES` как compile-time размер набора БОЛЬШЕ НЕ СУЩЕСТВУЕТ. Его -заменили две разные величины: `POP_SND_MAX_PAGES` — вместимость -загрузчика (16, запас под чужие наборы), и runtime `pop_snd_pages` — -сколько страниц у ЭТОГО набора. Насос сверяет переход длинного эффекта -через границу страницы со вторым, а не с первым. - -### 10.2 Формат `PSI1` и почему запись ровно 5 байт - -Один сектор DSS: 16 байт заголовка (магия, число записей, размер записи, -log2 блока, формат PCM, число страниц) и 57 записей по 5 байт — -`page` (u8), `off` (u16 LE), `len` (u16 LE), — то есть побайтовый образ -`pop_snd_tbl[]`. Записи читаются ОДНИМ `read` прямо в резидентную -таблицу, без разбора по полям. - -Поэтому `sizeof(pop_snd_ent_t) == 5` в ABI SDCC z80 — часть ДИСКОВОГО -контракта, а не деталь компилятора. Ловится с двух сторон: статически -(`pop_snd_data.c`) и полем размера записи в заголовке, которое загрузчик -сверяет со своим `sizeof`. - -### 10.3 Цена в памяти - -Таблица была `static const` в `_CODE`, стала изменяемой в `_DATA`. -Замер до/после: - -| | было | стало | Δ | -|---|---:|---:|---:| -| `_CODE` | 23865 | 23582 | −283 | -| данные | 7128 | 7421 | +293 | -| куча резидента | 239 Б | 229 Б | −10 | -| банк 8 | 14824 | 15425 | +601 | - -Суммарный резидентный расход почти не изменился — в huge-модели `_CODE` -и данные лежат подряд в одном диапазоне `0x4000..0xBFFF`. Реальные -10 байт — это расширение `pop_snd_page` с размера набора до -`POP_SND_MAX_PAGES` плюс байт счётчика. - -### 10.4 Валидация: чему именно нельзя доверять - -Индекс — файл на диске, и битый индекс увёл бы насос за пределы -выделенного блока EMM. Проверяются заголовок целиком и КАЖДАЯ запись: -`page < pop_snd_pages`, `off` внутри страницы, кратность `off`/`len` -блоку 128, непересечение с блоком тишины и то, что длинный эффект не -выходит за последнюю страницу набора. - -Последняя проверка считается В БЛОКАХ, а не в байтах: байтовый адрес -конца (`page * 16384 + off + len`) не влезает в `uint16`, а 32-битная -арифметика на Z80 дорога. В блоках весь набор — не больше -`16 * 128 = 2048`, и всё считается шестнадцатью битами. - -Число страниц из индекса дополнительно сверяется с числом элементов в -`snd.arc`: расходящаяся пара означает, что файлы из разных наборов. - -### 10.5 Нет индекса — эффектов нет, но МУЗЫКА ОСТАЁТСЯ - -Первая версия просто возвращала ошибку, и это оказалось непроходимой -игрой: тишину в паузах насос льёт из первого блока набора, поэтому без -набора CBL не открывался вовсе — а вместе с ним вставала музыка, чьи -блоки отсчитывает тот же насос. Заставка ждала конца трека, который не -начинал играть, и `pop_music_busy()` не гас никогда. - -Теперь при отказе индекса поднимается ПУСТОЙ набор: одна страница EMM с -блоком тишины. Все `len` нулевые, поэтому `pop_sfx_play` уходит по ветке -«нет оцифровки» — эффекты молчат, музыка играет, игра проходима. - -Две тонкости этой заливки, обе выяснены на живой машине: - -1. **Заливается только первый блок (128 байт), а не вся страница.** - `gfx_w0_page_prepare` ставит в страницу IRQ/NMI-стабы, чтобы - прерывание при замапленном W0 нашло валидный код. Заливка всей - страницы затирает их, и первое же прерывание уводит процессор в - мусор — чёрный экран на старте. Насосу больше и не нужно: он берёт - ровно блок тишины, да и то через W3. -2. **Запись под `IRQ_DISABLE`** — 128 байт накрывают стаб RST38, и до - `gfx_w0_unmap` прерываний быть не должно. - -### 10.6 Грабли сборки: секундная гранулярность mtime - -Смена `SND_SRC` тихо давала НЕВЕРНЫЙ результат: `sdlpop -> msdos -> -sdlpop` перепаковывал `build/atl`, а в `assets/packed` оставался прежний -набор. Причина не в логике, а во времени: упаковка укладывается в одну -секунду с записью stamp'а, а `make` считает цель, сделанную в ту же -секунду, что и зависимость, актуальной. С паузой в секунду не -воспроизводится вовсе. - -Лечится двумя приёмами, и оба убирают время из решения: - -* смена варианта СНОСИТ stamp'ы своего семейства (`rm -f - $(STAMP)/sound-*`) — нет файла, значит правило обязано выполниться; -* упаковка, сборка архива и копия индекса делаются ОДНИМ рецептом: - внутри рецепта сравнивать нечего. - -То же лечение получила и музыка (`MUSIC_FMT`) — механизм общий. - -### 10.7 Что проверено на живой машине - -* таблица в памяти совпадает с файлом из образа ПОБАЙТОВО (285 байт); -* один и тот же EXE (sha256 не меняется) поднимает SDLPoP (9 страниц) и - MSDOS (10 страниц); -* СТРОГАЯ проверка: у двух реальных наборов совпадают 23 записи из 31, - поэтому сделан отладочный ключ `pop_pack_sound.py --order reverse` — - набор с обратным порядком укладки, где от штатного отличаются 30 из 31 - записей. Игра на нём звучит правильно, значит раскладка берётся с - диска, а не из памяти; -* битый индекс (испорченная магия) — эффекты выключаются, игра жива; -* отсутствующий индекс — музыка играет, эффектов нет, PV-сцена проходит; -* Ctrl+S в обоих режимах работает штатно. - -Маркер для ручной проверки: звук 51 `princess_door_opening` (дверь в -сцене с принцессой) лежит у наборов по РАЗНЫМ страницам — 8 у SDLPoP, -9 у MSDOS. А вот 4 `gate_closing` из той же сцены совпадает побайтно и -для проверки подмены не годится. - -## 11. МУЗЫКА БЕЗ ПЕРЕЛИНКОВКИ: ДЛИНЫ И ДЛИТЕЛЬНОСТИ УЕХАЛИ НА ДИСК (2026-08-31) - -Сделана часть I плана `music_runtime_index_plan.md` (этапы MI0..MI5, у нас -B0..B7). `gen/pop_music_tbl.h` и `gen/pop_music_ticks.h` УДАЛЕНЫ: длины -треков и длительности реплик читаются из `MUS/mus.idx` (формат `PMI1`). -Один и тот же `sprpop.exe` работает с любым из четырёх наборов записей. - -### 11.1 Где живёт индекс и почему именно там - -228 байт таблицы в W2 положить нельзя — там свободной кучи порядка двух -сотен. Поэтому индекс лежит в ОДНОЙ странице EMM, а в резиденте от него -остаются два байта (номер блока и страницы). Наружу указатель на EMM не -выдаётся: `pop_music_info()` подключает страницу в W0, копирует четыре -байта в буфер вызывающего и сразу снимает окно. - -**Данные в странице лежат со смещения `POP_MUS_IDX_BASE` (0x100), а не с -нуля.** Причина в `gfx_w0_page_prepare`: он делает страницу безопасной -для W0, записывая В НЕЁ переход на IRQ-восстановитель (0x38) и RETN для -NMI (0x66). С нуля эти байты легли бы прямо в записи (id 10 и 21) и -испортили индекс; со смещением стабы и данные не пересекаются, и работает -штатная защита от прерываний, а не запрет их. Тот же приём и по той же -причине — в `pop_config.c` (`CFG_BASE`). - -Числа страниц в индексе НЕТ: это производная от `blocks`, и дублирование -однажды разъехалось бы с ним. Считает `pop_music_pages()`. - -### 11.2 Пауза конца уровня: состояние вместо счётчика - -`pop_endmus_left` и `POP_MUS_TICKS_32/41` УДАЛЕНЫ. Главный цикл ждёт -`pop_music_active()` — «заявка лежит, идёт загрузка или трек звучит». - -Одного `pop_music_busy()` там мало: между заявкой и первой нотой проходит -190–230 мс (§9), и в этой щели он ложен. Раньше щель закрывал счётчик по -длине записи, но он тянул за собой две беды: зависимость EXE от набора и -жёсткий делитель `/4`, который врал в быстрых режимах (там логический кадр -3 кадра луча, а не 4). Три признака подряд закрывают щель без единой -подобранной константы, потому что `pop_music_service` обнуляет `req` и -тут же взводит `ld_busy`, а тот держится до записи `pop_mus_left`. - -Побочно исправилось расхождение с оригиналом: при ВЫКЛЮЧЕННОМ звуке -`pop_sfx_play` не кладёт заявку, все три признака ложны, и уровень -меняется сразу — как в SDLPoP, где трек ставится под `if (is_sound_on)` -(seg006:651), а ожидание это `check_sound_playing()` (seg003:387). -Прежний счётчик взводился безусловно и держал пройденный уровень лишние -12 секунд в тишине. - -### 11.3 PV-сцена: четыре якоря вместо compile-time шкалы - -Шкала сцены осталась абсолютной (переход на ожидание звука — отдельный -разговор, см. план), но точки, зависящие от длины реплик, считаются при -входе в сцену: `pv_m50_end`, `pv_dialog1`, `pv_exit`, `pv_anim_end`. -Остальные выражения не изменились — они отсчитываются от якорей. - -Восемь байт статики вместо протаскивания структуры времён через пять -функций (`pv_princess_step`, `pv_jaffar_step`, `pv_seq_period`, -`intro_pv_draw_frame`, `intro_pv_animated`), как предлагал план: сцена -одна на запуск, и переделка всех сигнатур того не стоит. - -Нет индекса — длительности нулевые, и сцена просто идёт без пауз на -реплики: ждать нечего, звука-то нет. - -### 11.4 Цена в памяти - -За обе фазы (эффекты и музыка) резидент не вырос, а освободился: - -| | до фазы A | после фазы B | -|---|---:|---:| -| `_CODE` | 23865 | 23544 | -| куча резидента | 239 Б | 256 Б | - -Банк 9 похудел на 118 Б (ушла `pop_mus_tbl` из его rodata), банк 11 — на -длительности реплик. Прибавка: 2 байта на страницу индекса музыки и 8 на -якоря PV. - -### 11.5 Что проверено на живой машине - -* `sprpop.exe` ПОБАЙТОВО одинаков для наборов flac и mt32; -* все 22 трека в индексах различаются, и контрольные значения совпали с - тем, что план предсказывал по замерам: m32 438→449, m41 732→685, - m50 831→867, m53 985→1044, m56 9865→10462 блоков (78→82 страницы); -* на mt32-наборе PV-сцена проходит целиком: m50 → m53 → m52 звучат по - длительностям mt32, хотя в EXE их больше нет; -* без `mus.idx` музыки нет, эффекты работают, игра проходима — сцены - проскакивают без пауз на реплики. - -**НЕ ПРОВЕРЕНО:** потоковый трек m56 на 82 страницах (у flac 78). -Кольцевой проигрыватель получает число страниц из индекса, и код к этому -готов, но живого прогона финала на mt32 не было — до него надо дойти в -игре. Это оставшийся пункт приёмки. diff --git a/applications/SprPoP/docs/status_line_text.md b/applications/SprPoP/docs/status_line_text.md deleted file mode 100644 index 75cd199..0000000 --- a/applications/SprPoP/docs/status_line_text.md +++ /dev/null @@ -1,218 +0,0 @@ -# Текст в нижней статус-строке (строке 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/docs/vanilla_vs_bugfixed.md b/applications/SprPoP/docs/vanilla_vs_bugfixed.md deleted file mode 100644 index 4a38b41..0000000 --- a/applications/SprPoP/docs/vanilla_vs_bugfixed.md +++ /dev/null @@ -1,275 +0,0 @@ -# Два поведения: VANILLA и BUGFIXED - -> Заведено 2026-08-31. Задача поставлена, работа НЕ начата. - -## Зачем - -У оригинального PoP есть баги, которые игроки знают наизусть и на -которых построены известные трюки. SDLPoP чинит их не молча, а -ОПЦИОНАЛЬНО: каждое исправление отдельным переключателем, по умолчанию -часть включена, часть нет. Мы до сих пор повторяли ваниль — сознательно, -чтобы порт вёл себя как оригинал. - -Задача: дать ДВА поведения на выбор, не размазывая условия по всему коду: - -* **VANILLA** — как в оригинале 1989 года, со всеми его багами; -* **BUGFIXED** — с портированными исправлениями SDLPoP. - -## Что уже известно (из аудита) - -Разбор расхождений — `docs/sdlpop_audit.md`. Оттуда прямо в эту задачу -переезжает следующее. - -### Опциональные фиксы SDLPoP, которых у нас НЕТ - -Все три относятся к прохождению сквозь препятствия и живут в `do_fall` -(seg005) и рядом: - -| фикс SDLPoP | что чинит | -|---|---| -| «скольжение сквозь стену» | Кид падает сквозь стены после разворота в беге, особенно под зельем медленного падения | -| «прыжок сквозь стену над воротами» | пролёт в тайл над воротами | -| «проваливание сквозь гобелен» | падение сквозь тайл гобелена | - -Ни один не портирован — это и есть наше нынешнее VANILLA. - -### Находка 24 — отложена сюда - -`in_wall` у нас пересчитывает только колонку, а оригинал перезагружает -ещё и данные кадра (`load_fram_det_col`, seg006). Правка сделана и -ОТКАЧЕНА 2026-08-31 по такой причине: - -* по букве оригинала находка верна; -* но практического эффекта показать НЕ УДАЛОСЬ — все 15 наборов - host-тестов дают одинаковый результат до и после, включая специально - написанный тест на заход в кладку; -* при этом правка не бесплатна: добавляет маппинг окна и перезагрузку - кадра на каждое выталкивание из стены. - -Платить за недоказанное не стали. Вернуться к ней имеет смысл именно -здесь: при работе над BUGFIXED появится сценарий, где кадр меняется перед -выталкиванием, и тогда эффект станет наблюдаемым. - -### Готовый детектор поведения - -`tests/host/t_wall.c` расширен с одной проверки до трёх. Ключевая — -`wall_stops_jump_from_left_side`: она НЕ требует нуля заходов в кладку, а -сторожит их ЧИСЛО (сейчас ровно два случая из четырнадцати стартовых -позиций). - -Это и есть переключатель ожиданий между режимами: - -* больше двух — правка сделала нас хуже ванили, регресс; -* ровно два — ведём себя как оригинал (режим VANILLA); -* меньше двух — кто-то портировал фикс; в режиме BUGFIXED тест должен - ждать нуля. - -То есть когда появится BUGFIXED, этому тесту понадобится ожидание, -зависящее от режима, — и он готов стать первым таким. - -## Переключатель уже есть — второго не нужно - -Уточнено 2026-08-31: в настройках игры ПЕРЕКЛЮЧАТЕЛЬ VANILLA/ENHANCED -СУЩЕСТВУЕТ (`docs/menu_settings_plan.md`), просто сейчас он жёстко -зафиксирован в положении VANILLA. Отдельную сущность заводить не надо — -эта задача про то, чтобы наполнить смыслом уже имеющееся положение -ENHANCED. - -**Наш «ванильный» билд уже не чистая ваниль.** Часть ванильных багов у -нас пофикшена по ходу портирования. Значит: - -* нельзя считать текущее поведение эталоном ванили — оно смешанное; -* при разделении режимов придётся пройтись по уже сделанным фиксам и - решить по каждому, остаётся он в VANILLA или уезжает в ENHANCED; -* и наоборот: отдельные исправления (например, падение сквозь стену) - вполне могут быть сделаны прямо в нынешнем «ванильном» билде, если - сочтём, что играбельность важнее буквальности. - -## Что предстоит решить - -1. **Что считать ванилью на практике.** Составить список уже сделанных - отступлений от оригинала и распределить их между режимами. -2. **Цена рантайм-проверки.** Условия попадают в физику и `play_seq`, то - есть в горячий путь. Если переключать в рантайме, проверка должна - быть дешевле самого фикса: флаг в резиденте, а не вызов через банк. -3. **Что считать умолчанием.** Оригинальное поведение честнее для порта, - но часть фиксов SDLPoP включает по умолчанию. -4. **Как тестировать оба режима.** Host-тесты гоняются одним прогоном; - для двух режимов нужен либо параметр сборки тестов, либо ожидания, - зависящие от флага. - -## Инвентаризация: что уже решено по каждому фиксу SDLPoP - -Составлено 2026-08-31 обходом кода. В движке эти решения УЖЕ приняты и -задокументированы прямо в комментариях — таблица лишь сводит их в одно -место, чтобы при разделении режимов не перечитывать исходники. - -| фикс SDLPoP | где у нас | что взято | -|---|---|---| -| `fix_feather_fall_affects_guards` | `pop_map.c:941` | **ФИКС** — перо действует только на Кида | -| `fix_exit_door` | `pop_map.c:1212` | **ФИКС** — ветка фикса вместо ванильного глобала | -| `FIX_GATE_SOUNDS` | `pop_trob.c:579` | **ФИКС** — условия через ИЛИ | -| `fix_sound_priorities` | `pop_sfx.c:196` | **ФИКС** — в SDLPoP включён безусловно, сравниваемся с исправленным | -| `FIX_STAND_ON_THIN_AIR` | `pop_map.c:1499` | **ВАНИЛЬ** — взяты 2 части из 3, третья ждёт патча `seqtbl` | -| `fix_painless_fall_on_guard` | `pop_map.c:1611` | **ВАНИЛЬ** — намеренно | -| `fix_jumping_over_guard` | `pop_map.c:1612` | **ВАНИЛЬ** — намеренно | -| `FIX_RETREAT_WITHOUT_LEAVING_ROOM` | `pop_map.c:3036` | **ВАНИЛЬ** — в SDLPoP выключен по умолчанию; трюк 35 сохраняем | -| `fix_skeleton_chomper_blood` | `pop_map.c:3177` | **ВАНИЛЬ** — кровь скелета в ванили есть | -| потеря HP скелетом при падении с двух этажей | `pop_map.c:676` | **ВАНИЛЬ** — баг оригинала, сохраняем сознательно | - -### Фиксы, которым нужна правка байткода - -`FIX_STAND_ON_THIN_AIR` не взят НЕ потому, что мы выбрали ваниль, а -потому что его нельзя применить наполовину: он состоит из трёх частей, и -третья — правка самого байткода `seqtbl` (смещения в последовательности -вставания). Взяв только первые две, мы получим вставание, уносящее -весовую точку в стену, то есть ХУЖЕ ванили. - -**Это выполнимо, и раньше здесь стояла неверная оценка** (уточнено -2026-08-31): байткод можно менять и у нас. Способов три: - -1. **Патч в рантайме** — после загрузки `kid.ani` в EMM-страницу - пропатчить нужные байты прямо там. Речь о единицах байт, страница уже - наша, и патч обратим — то есть режим переключается без пересборки - ресурсов. Это и делает фикс пригодным для VANILLA/ENHANCED. -2. **Патч в упаковщике** — готовить два варианта `kid.ani`. Привязывает - режим к файлам на диске, поэтому хуже: переключатель в меню перестаёт - быть чисто кодовым. -3. **Две копии в одной странице** — и это, пожалуй, лучший вариант - (решено 2026-08-31). `kid.ani` целиком около 4 КБ, а страница EMM — - 16 КБ, то есть обе версии байткода спокойно помещаются рядом в уже - выделенной странице. Переключение режима сводится к смене базового - смещения, патчить ничего не нужно, откат мгновенный. - -Для сравнения: у SDLPoP рабочая таблица и неизменная копия оригинала -существуют раздельно (`seqtbl` и `original_seqtbl`), причём вторая нужна -для сверки — то есть сама идея «оригинальный байткод отдельно, рабочий -отдельно» там уже заложена. - -Общее правило для BUGFIXED: фиксы, требующие правки `seqtbl`, доступны -через рантайм-патч страницы; закладывать это стоит сразу, чтобы не -упереться при первом же таком фиксе. - -### Вывод для разделения режимов - -Четыре фикса уже взяты, шесть позиций оставлены ванильными. Значит -нынешний билд — это не VANILLA, а «ваниль плюс четыре исправления». При -разделении: - -* взятые четыре надо либо оставить в обоих режимах (если считаем их - безусловными улучшениями), либо увести в ENHANCED и вернуть ванильное - поведение в VANILLA — второе честнее, но потребует обратной работы; -* ванильные шесть — кандидаты в ENHANCED; `FIX_STAND_ON_THIN_AIR` тоже, - но ему дополнительно нужен рантайм-патч байткода. - -## ВСЕ исправления SDLPoP и их статус у нас - -Полный перечень опциональных исправлений оригинала, какие есть в SDLPoP -(43 позиции), со статусом в нашем порте. Названия — идентификаторы -опций SDLPoP, описание — своими словами. - -Статусы: **ВЗЯТ** — портирован; **ВАНИЛЬ** — сознательно не берём, держим -поведение оригинала; **НЕТ** — не реализован, кандидат в ENHANCED; -**НЕДОСТУПЕН** — требует правки байткода `seqtbl` (см. ограничение выше); -**В РАБОТЕ** — решено делать сейчас. - -### Стены и препятствия - -| опция | что чинит | статус | -|---|---|---| -| `fix_glide_through_wall` | проход сквозь стену при падении после разворота в беге | **ВЗЯТ** 2026-08-31 — `glide_through_wall_guard()` в `pop_map.c`, точка отвязки для VANILLA | -| `fix_jump_through_wall_above_gate` | прыжок в тайл над воротами | НЕТ | -| `fix_drop_through_tapestry` | проваливание сквозь гобелен | НЕТ | -| `fix_running_jump_through_tapestry` | прыжок с разбега сквозь гобелен | НЕТ | -| `fix_turn_running_near_wall` | разворот в беге вплотную к стене | НЕТ | -| `fix_wall_bump_triggers_tile_below` | удар о стену срабатывает на тайл ниже | НЕТ | -| `fix_bigpillar_climb` | подъём на большую колонну | НЕТ | -| `fix_land_against_gate_or_tapestry` | приземление вплотную к воротам или гобелену | НЕТ | -| `fix_caped_prince_sliding_through_gate` | проскальзывание сквозь ворота | НЕТ | - -### Падение, прыжки, зацепы - -| опция | что чинит | статус | -|---|---|---| -| `fix_stand_on_thin_air` | стояние на воздухе после отмены падения | НЕТ — нужен рантайм-патч `seqtbl` (см. выше) | -| `fix_jump_distance_at_edge` | дальность прыжка у самой кромки | НЕТ | -| `fix_edge_distance_check_when_climbing` | проверка расстояния до кромки при подъёме | НЕТ | -| `fix_grab_falling_speed` | зацеп на слишком большой скорости падения | НЕТ | -| `fix_drop_2_rooms_climbing_loose_tile` | провал через две комнаты при подъёме на шаткой плите | НЕТ | -| `fix_infinite_down_bug` | бесконечное падение вниз | НЕТ | -| `fix_falling_through_floor_during_sword_strike` | провал сквозь пол во время удара мечом | НЕТ | -| `fix_safe_landing_on_spikes` | безопасное приземление на невыдвинутые пики | НЕТ | -| `fix_dead_floating_in_air` | мёртвый зависает в воздухе | НЕТ | - -### Бой и стражи - -| опция | что чинит | статус | -|---|---|---| -| `fix_painless_fall_on_guard` | падение на стража с высоты без урона | **ВАНИЛЬ** | -| `fix_jumping_over_guard` | перепрыгивание через стража | **ВАНИЛЬ** | -| `fix_skeleton_chomper_blood` | кровь скелета в челюстях | **ВАНИЛЬ** | -| `fix_push_guard_into_wall` | вталкивание стража в стену | НЕТ | -| `fix_guard_following_through_closed_gates` | страж идёт сквозь закрытые ворота | НЕТ | -| `fix_doortop_disabling_guard` | верх двери отключает стража | НЕТ | -| `fix_offscreen_guards_disappearing` | стражи пропадают за краем экрана | НЕТ | -| `fix_unintended_sword_strike` | непреднамеренный удар мечом | НЕТ | -| `fix_two_coll_bug` | двойная проверка столкновения | НЕТ | -| `fix_move_after_sheathe` | движение сразу после убирания меча | НЕТ | - -### Ворота, двери, плиты - -| опция | что чинит | статус | -|---|---|---| -| `fix_exit_door` | дверь выхода с уровня | **ВЗЯТ** | -| `fix_gate_sounds` | звуки ворот | **ВЗЯТ** | -| `fix_gate_drawing_bug` | отрисовка ворот | НЕТ | -| `fix_press_through_closed_gates` | нажатие плиты сквозь закрытые ворота | НЕТ | -| `fix_chompers_not_starting` | челюсти не заводятся | НЕТ | -| `fix_loose_left_of_potion` | шаткая плита слева от зелья | НЕТ | -| `fix_hidden_floors_during_flashing` | скрытые полы во время вспышки | НЕТ | -| `fix_retreat_without_leaving_room` | отступление без смены комнаты (трюк 35) | **ВАНИЛЬ** | - -### Зелья, перо, спецэффекты - -| опция | что чинит | статус | -|---|---|---| -| `fix_feather_fall_affects_guards` | перо действует и на стражей | **ВЗЯТ** | -| `fix_feather_interrupted_by_leveldoor` | перо прерывается дверью уровня | НЕТ | -| `fix_move_after_drink` | движение сразу после питья | НЕТ | -| `fix_quicksave_during_feather` | быстрое сохранение под пером | НЕТ | -| `fix_hang_on_teleport` | зависание при телепорте | НЕТ (телепортов у нас нет) | - -### Интерфейс и ввод - -| опция | что чинит | статус | -|---|---|---| -| `fix_one_hp_stops_blinking` | индикатор перестаёт мигать на одном HP | НЕТ | -| `fix_register_quick_input` | учёт быстрого ввода | НЕТ | - -### Отдельно: приоритеты звуков - -`fix_sound_priorities` в SDLPoP включён БЕЗУСЛОВНО (не опция), и мы -сравниваемся с исправленным вариантом — `pop_sfx.c:196`. Статус: -**ВЗЯТ**, вернуть ванильное поведение отдельным режимом было бы -дополнительной работой. - -### Сводка - -| статус | сколько | -|---|---:| -| ВЗЯТ | 5 | -| ВАНИЛЬ (сознательно) | 4 | -| требует патча `seqtbl` (выполнимо) | 1 | -| НЕТ (кандидаты в ENHANCED) | 32 | - -## Список кандидатов на BUGFIXED - -Пополняется по мере аудита. Пока: - -* три опциональных фикса SDLPoP выше; -* находка 24 (перезагрузка кадра в `in_wall`); -* находка 21 из аудита — общая рамка «мы намеренно повторяем ваниль». - -Не относятся сюда находки, где мы расходимся с оригиналом НЕ в его -пользу (ранги А и Б аудита): их надо чинить в обоих режимах, потому что -это не баги оригинала, а наши. diff --git a/applications/SprPoP/gen/bg_arc.h b/applications/SprPoP/gen/bg_arc.h deleted file mode 100644 index 581908e..0000000 --- a/applications/SprPoP/gen/bg_arc.h +++ /dev/null @@ -1,34 +0,0 @@ -/* 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 deleted file mode 100644 index f930261..0000000 --- a/applications/SprPoP/gen/guard_arc.h +++ /dev/null @@ -1,19 +0,0 @@ -/* 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 deleted file mode 100644 index db3d846..0000000 --- a/applications/SprPoP/gen/kid_arc.h +++ /dev/null @@ -1,67 +0,0 @@ -/* 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_data.h b/applications/SprPoP/gen/kid_data.h deleted file mode 100644 index 09debc0..0000000 --- a/applications/SprPoP/gen/kid_data.h +++ /dev/null @@ -1,77 +0,0 @@ -/* 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 deleted file mode 100644 index edb3299..0000000 --- a/applications/SprPoP/gen/pop_bg_atlas.h +++ /dev/null @@ -1,37 +0,0 @@ -/* pop_bg_atlas.h — раскладка атласов статического фона PoP. - * Сгенерировано tools/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 - -/* Палитры пока остаются отдельными файлами рядом с bg.arc. Имена - * видит только модуль загрузки, чтобы не размножать строки. */ -#ifdef POP_BG_ATLAS_NAMES -static const char *const pop_tile_pal[POP_TILESETS] = { "pop_tile.pal", "pal_tile.pal" }; -#endif /* POP_BG_ATLAS_NAMES */ -/* Пузырёк зелья: красный набор = 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 - -#endif diff --git a/applications/SprPoP/gen/pop_font.h b/applications/SprPoP/gen/pop_font.h deleted file mode 100644 index 1ef09fc..0000000 --- a/applications/SprPoP/gen/pop_font.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 deleted file mode 100644 index 8e394f4..0000000 --- a/applications/SprPoP/gen/pop_guard_pal.h +++ /dev/null @@ -1,36 +0,0 @@ -/* 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_shadow_atlas.h b/applications/SprPoP/gen/pop_shadow_atlas.h deleted file mode 100644 index 1000912..0000000 --- a/applications/SprPoP/gen/pop_shadow_atlas.h +++ /dev/null @@ -1,33 +0,0 @@ -/* pop_shadow_atlas.h — раскладка атласа ТЕНИ. - * СГЕНЕРЕНО tools/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 - -/* 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/pv_arc.h b/applications/SprPoP/gen/pv_arc.h deleted file mode 100644 index d7b9dae..0000000 --- a/applications/SprPoP/gen/pv_arc.h +++ /dev/null @@ -1,92 +0,0 @@ -/* 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 deleted file mode 100644 index 09a719a..0000000 --- a/applications/SprPoP/gen/shadow_arc.h +++ /dev/null @@ -1,41 +0,0 @@ -/* 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 deleted file mode 100644 index eca2b21..0000000 --- a/applications/SprPoP/gen/skel_arc.h +++ /dev/null @@ -1,13 +0,0 @@ -/* 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 deleted file mode 100644 index e02e7f2..0000000 --- a/applications/SprPoP/gen/title_arc.h +++ /dev/null @@ -1,29 +0,0 @@ -/* 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 deleted file mode 100644 index 8f30b67..0000000 --- a/applications/SprPoP/gen/vizier_arc.h +++ /dev/null @@ -1,14 +0,0 @@ -/* 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 deleted file mode 100644 index 5ff9578..0000000 --- a/applications/SprPoP/src/_pop_bg.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * _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 deleted file mode 100644 index cbd7e74..0000000 --- a/applications/SprPoP/src/_pop_config.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * _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 deleted file mode 100644 index 1cbe465..0000000 --- a/applications/SprPoP/src/_pop_draw.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * _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_file.h b/applications/SprPoP/src/_pop_file.h deleted file mode 100644 index 6c6485e..0000000 --- a/applications/SprPoP/src/_pop_file.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * _pop_file.h — внутренний файловый слой SprPoP для старых версий DSS. - * - * DSS до исправления относительных путей оставляет рабочий каталог внутри - * последнего подкаталога. POP_PATH_CALL восстанавливает абсолютный каталог - * приложения до и после операции. Это макрос намеренно: код исполняется в - * банке вызывающего, где доступен переданный указатель, и не расходует W1. - */ -#ifndef _POP_FILE_H -#define _POP_FILE_H - -#include -#include - -/* BANK10: получить каталог EXE через APPINFO B=1 и сделать его текущим. */ -int8_t pop_path_init(void) __banked; -int8_t pop_chdir_home(void) __banked; - -/* expression вычисляется только после успешного chdir. errno файловой - * операции сохраняется через завершающее восстановление каталога. */ -#define POP_PATH_CALL(result, expression) do { \ - if (pop_chdir_home() != 0) { \ - (result) = -1; \ - } else { \ - int _pop_path_errno; \ - (result) = (expression); \ - _pop_path_errno = errno; \ - (void)pop_chdir_home(); \ - errno = _pop_path_errno; \ - } \ -} while (0) - -#endif diff --git a/applications/SprPoP/src/_pop_kdraw.h b/applications/SprPoP/src/_pop_kdraw.h deleted file mode 100644 index f3988d2..0000000 --- a/applications/SprPoP/src/_pop_kdraw.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * _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 deleted file mode 100644 index ceb017e..0000000 --- a/applications/SprPoP/src/_pop_kid.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * _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 deleted file mode 100644 index 8bfabae..0000000 --- a/applications/SprPoP/src/_pop_level.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * _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 deleted file mode 100644 index e514f33..0000000 --- a/applications/SprPoP/src/_pop_sfx.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * _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_snd_tbl.h" /* тип записи и инварианты (НЕ генерируется) */ - -/* РАСКЛАДКА НАБОРА и его страницы — общие данные обеих половин, живут в - * pop_snd_data.c. Значения приезжают с диска (SND/snd.idx): холодная - * половина их читает и проверяет, горячая только пользуется. */ -extern pop_snd_ent_t pop_snd_tbl[POP_SND_COUNT]; -extern uint8_t pop_snd_page[POP_SND_MAX_PAGES]; -/* Сколько страниц у загруженного набора; 0 — набор не поднят. */ -extern uint8_t 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 deleted file mode 100644 index 2069088..0000000 --- a/applications/SprPoP/src/_pop_tile.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * _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 deleted file mode 100644 index 59e4ef1..0000000 --- a/applications/SprPoP/src/guards.c +++ /dev/null @@ -1,1219 +0,0 @@ -/* - * 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_81_PUSHED_OFF_LEDGE 81 /* заколот у обрыва — падает замертво */ -#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 и кадр - * «получил удар». - * - * СМЕРТЬ БЕЗОРУЖНОГО БЫВАЕТ ДВУХ ВИДОВ, и выбор между ними делается по - * обстановке ПОЗАДИ (seg002): если сзади есть опора или до кромки меньше - * четырёх — «заколот на месте» (seq_85); если сзади пусто и от кромки - * далеко — «сброшен с уступа» (seq_81), отдельная последовательность, - * которая сама отыгрывает падение замертво. - * - * Вторая ветка появилась 2026-08-31 (docs/sdlpop_audit.md, находка 12): - * раньше её не было, потому что считалось, будто тайловые запросы от Char - * недоступны — на деле они давно работают, не хватало объявлений. - * - * Замечание к сцене: падение первым делом убирает меч (start_fall), так - * что персонаж, сбитый в пропасть во время боя, к следующему удару уже - * безоружен — и попадает сюда же. */ -/* ПРИЖАТЬ К ПОЛУ СВОЕГО РЯДА — только для ПЕРЕЖИВШЕГО удар. - * - * В оригинале (seg002, ветка seq_74_hit_by_sword) эти две строки стоят - * ВНУТРИ ветки выжившего; смертельные ветки координату не трогают. - * - * У нас они долго выполнялись безусловно и работали СТРАХОВКОЙ за - * отсутствующую ветку «сброшен с уступа»: убитого в воздухе прижимали к - * полу, иначе он продолжал падать и выпадал за нижнюю границу, а игра - * уходила на рестарт, не показав тела (проверено на живой машине - * 2026-08-31 — правка в одиночку ломала смерть). - * - * Снято ПОСЛЕ появления ветки seq_81: теперь смерть у обрыва отыгрывает - * своя последовательность, и страховка больше не нужна. Порядок именно - * такой и важен — сперва ветка, потом снятие. */ -static void hurt_stand_on_floor(void) -{ - Char.y = (uint8_t)pop_y_land[Char.curr_row + 1]; - Char.fall_y = 0; -} - -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); - if (pop_tile_behind_char() != 0 || pop_dist_to_edge_weight() < 4) - pop_char_set_seq(SEQ_85_STABBED_TO_DEATH); /* есть опора сзади */ - else - pop_char_set_seq(SEQ_81_PUSHED_OFF_LEDGE); /* сзади обрыв */ - } else if (Char.charid == CHARID_0_KID && pop_immortal) { - /* ЧИТ, уровень 1 и выше: в боевой стойке удары не отнимают HP. - * Кадр «получил удар» оставляем — иначе бой перестаёт читаться, - * да и оригинал на выживший удар ставит ровно его. */ - pop_char_set_seq(SEQ_74_HIT_BY_SWORD); - hurt_stand_on_floor(); - } 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); - hurt_stand_on_floor(); - } - /* 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) { - 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(); - } - - /* СВИСТ КЛИНКА МИМО ЦЕЛИ (seg002:1039..1044) — в оригинале он стоит в - * ХВОСТЕ функции, после ОБЕИХ веток, а не внутри ветки «не парировано». - * Мы его туда и переносим; раньше он звучал раньше времени и без двух - * условий оригинала, отчего на подъёме Кида шёл непрерывный взмах - * клинка у безоружного (наблюдение пользователя 2026-09-02, звук - * опознан по snd_curr == 11 в живой сессии). - * - * Три отличия от прежнего кода, все из оригинала: - * - * 1. Ранний выход по dir_56_none — это ИМЕННО анти-зацикливание, автор - * SDLPoP так его и подписал («Fix looping sword moving sound»). - * Направление dir_56_none означает «персонаж выключен» (clear_char), - * и махать клинком ему нечем. - * 2. Условие `Opp.action != ACTION_99_HURT`: попали — играет звук боли, - * а не свист мимо. Поэтому звук обязан идти ПОСЛЕ присвоения выше. - * 3. Кадры перечитываются у персонажей, а не берутся из cf/of: ветка - * парирования только что записала Opp.frame, а play_seq в ней — - * Char.frame, и снимки выше уже устарели. */ - if (Char.direction == DIR_56_NONE) return; - if (Char.frame == FRAME_154_POKING && Opp.frame != FRAME_161_PARRY && - Opp.action != ACTION_99_HURT) - pop_sfx_play(11); -} - -/* 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 deleted file mode 100644 index 3dcaf14..0000000 --- a/applications/SprPoP/src/pop_app.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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 deleted file mode 100644 index 87c3dbb..0000000 --- a/applications/SprPoP/src/pop_app.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 deleted file mode 100644 index 1d40f06..0000000 --- a/applications/SprPoP/src/pop_arc.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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" -#include "_pop_file.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; - POP_PATH_CALL(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 deleted file mode 100644 index 20cd249..0000000 --- a/applications/SprPoP/src/pop_arc.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 deleted file mode 100644 index 89eca09..0000000 --- a/applications/SprPoP/src/pop_bg.c +++ /dev/null @@ -1,1187 +0,0 @@ -/* - * 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, НО только в ПОДЗЕМЕЛЬЕ. Условие - * `tbl_level_type[current_level] == 0` в оригинале стоит третьим - * (seg008:628) и у нас было потеряно ИМЕННО ЗДЕСЬ: статическая - * отрисовка (pop_room.c, draw_tile_base) его имеет, а оверлей — нет. - * Отсюда симптом «кнопка, на которую лезет Кид, залита кирпичом»: - * пока тайл рисует статика, кнопка правильная, но как только Kid - * встаёт под кромку, оверлей перерисовывает её поверх него уже - * спрайтом 148, а во дворце это другая картинка (кладку палас - * вообще рисует заливками, а не спрайтами). Уровень 4, комната 18, - * кнопка (0,4) — найдено пользователем 2026-09-02. - * - * Раньше здесь стоял голый 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 && !pop_palace) - base_id = 148; /* ТОЛЬКО ПОДЗЕМЕЛЬЕ (seg008:628) */ - 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 deleted file mode 100644 index c35bb14..0000000 --- a/applications/SprPoP/src/pop_bg.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - * 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 deleted file mode 100644 index 1f1ecc3..0000000 --- a/applications/SprPoP/src/pop_cdraw.c +++ /dev/null @@ -1,1085 +0,0 @@ -/* - * 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; /* сколько страниц ещё обновить */ -/* Сколько страниц ещё стереть ЦЕЛИКОМ. Обычное обновление щадит зону - * статус-текста (иначе он мигал бы на каждом изменении жизней), но после - * рестарта уровня в этой зоне остаются деления ПРОШЛОГО боя: полоса стража - * при большом запасе HP заходит под текст, и щадящая чистка их не трогает. - * Симптом — «после гибели и Ctrl+A на одной из страниц осталась полоса по - * результатам боя» (BUGS_OPEN, HP-BAR-RESTART). */ -static uint8_t hp_wipe; /* сколько страниц ещё стереть */ -static uint8_t hp_kid_prev, hp_kidmax_prev, hp_gd_prev, hp_gdmax_prev; -/* Страница, в которую лёг прошлый проход. Счётчики выше считают СТРАНИЦЫ, - * но кадр и страница — не одно и то же: между двумя вызовами переворота - * может не быть, и тогда оба прохода уходили в ОДНУ страницу, а вторая - * оставалась с делениями прошлого боя. Ровно этим полоса и переживала - * Ctrl+A (BUGS_OPEN, HP-BAR-RESTART). */ -static uint8_t hp_page_prev = 0xFF; - -/* Заставить перерисовать полосу на обеих страницах: звать при входе в - * комнату (фон перерисован целиком и стёр её) и при старте. */ -void pop_hp_invalidate(void) __banked { hp_todo = 2; hp_wipe = 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 == 2) идёт всегда, ему сравнивать не с чем. */ - uint8_t pg = gfx_get_draw_page(); - if (hp_todo < 2 && pg == hp_page_prev) return; - hp_page_prev = pg; - } - 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 (hp_wipe) { - /* Полная чистка обеих страниц: старая полоса могла заходить под - * текст. Текст при этом стирается тоже, поэтому сразу просим - * перерисовать и его — иначе строка уровня пропала бы. */ - hp_wipe--; - bar(0, HP_Y + POP_YOFF, 319, HP_Y + POP_YOFF + 6); - if (pop_status_ticks) pop_status_invalidate(); - } else 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 deleted file mode 100644 index 8049dc7..0000000 --- a/applications/SprPoP/src/pop_cdraw.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * 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 deleted file mode 100644 index 78eefe9..0000000 --- a/applications/SprPoP/src/pop_cfg.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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 deleted file mode 100644 index b12e3a7..0000000 --- a/applications/SprPoP/src/pop_char.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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 deleted file mode 100644 index f58cc3a..0000000 --- a/applications/SprPoP/src/pop_cheat.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 */ -/* ОДИН СКАН-КОД, ДВА ЧИТА: Shift+I — переворот экрана (toggle_upside - * SDLPoP, keys.txt), Ctrl+I — наше бессмертие. Различает их модификатор. - * Переворот раньше стоял на голом U, но U отдан осмотру комнаты сверху - * (фаза D, keys_plan.md); бессмертие — на голом I. */ -#define KBD_KEY_I 0x43 - -/* Бессмертие по кругу — Ctrl+I. Наш чит, в оригинале его нет; вынесен под - * Ctrl, чтобы не занимать голую букву (решение пользователя 2026-08-27). */ -#define KBD_CHEAT_IMMO KBD_KEY_I -/* 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 KBD_KEY_I -/* ВРЕМЯ — на +/-, как в оригинале (keys.txt). Раньше стояло на F7/F8, - * потому что +/- занимал отладочный обход комнат; теперь обход уехал под - * Ctrl (см. sprpop.c, ROOMNAV), и клавиши освободились. Принимаем и - * основной ряд, и цифровой блок — на клавиатуре Sprinter есть оба. */ -#define KBD_TIME_SUB_MAIN 0x4E /* '-' основного ряда */ -#define KBD_TIME_SUB_NUM 0x7B /* '-' цифрового блока */ -#define KBD_TIME_ADD_MAIN 0x55 /* '=' основного ряда ('+' без Shift) */ -#define KBD_TIME_ADD_NUM 0x79 /* '+' цифрового блока */ - -/* Наши сверхштатные, все под Ctrl, чтобы не занимать голые буквы. */ -#define KBD_KEY_P 0x4D /* Ctrl+P — режим скорости (pop_pace.h) */ -#define KBD_KEY_W 0x1D /* Shift+W — медленное падение (feather_fall) */ -#define KBD_KEY_D 0x23 /* Ctrl+D — отладочная строка вкл/выкл */ -#define KBD_KEY_F 0x2B /* Ctrl+F — стоп-кадр, вкл/выкл одной клавишей */ - - -/* Старое имя оставлено для обработчика 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 deleted file mode 100644 index 4c7dd56..0000000 --- a/applications/SprPoP/src/pop_config.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * 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" -#include "_pop_file.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; - - POP_PATH_CALL(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) - puts("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. */ - POP_PATH_CALL(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 deleted file mode 100644 index 91c5d64..0000000 --- a/applications/SprPoP/src/pop_ctrl.c +++ /dev/null @@ -1,889 +0,0 @@ -/* - * 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) ---------------------------------------- */ -/* Диагональные дублёры оригинала (keys.txt): Home = «прыжок влево», Page Up - * = «прыжок вправо». У SDLPoP это не отдельные действия, а ровно те же - * Up+Left / Up+Right, поэтому и здесь они просто ДОБАВЛЯЮТСЯ к стрелкам: - * заводить им ветку в диспетчере не нужно, и любой будущий разбор - * «откуда взялся прыжок» останется одним. */ -#define KBD_HOME (KBD_EXT | 0x6C) -#define KBD_PGUP (KBD_EXT | 0x7D) -/* Центр нумпада (KP5) — тоже «вниз», как просит раскладка оригинала: там - * весь блок 7/8/9-4/6-1/2/3 был управлением, и середина работала как - * приседание. Своей навигационной клавиши у KP5 нет, поэтому после - * kbd_raw_keypad_as_ext его голый код 0x73 переезжает в EXT-половину как - * есть — читаем оттуда и добавляем к стрелке «вниз». */ -#define KBD_KP5 (KBD_EXT | 0x73) - -static void read_input(void) -{ - uint8_t home = kbd_raw_down(KBD_HOME); - uint8_t pgup = kbd_raw_down(KBD_PGUP); - uint8_t l = (uint8_t)(kbd_raw_down(KBD_LEFT) || home); - uint8_t r = (uint8_t)(kbd_raw_down(KBD_RIGHT) || pgup); - uint8_t u = (uint8_t)(kbd_raw_down(KBD_UP) || home || pgup); - uint8_t d = (uint8_t)(kbd_raw_down(KBD_DOWN) || kbd_raw_down(KBD_KP5)); - 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; - /* НУМПАД НАМ НЕ НУЖЕН, И ЭТО ЛЕЧИТ ЗАЛИПАНИЕ СТРЕЛОК. Управление и - * меню читают только расширенные коды (KBD_LEFT = KBD_EXT|0x6B и т.д.). - * А при переполнении FIFO SIO теряется префикс E0, и make стрелки - * садится в PLAIN-половину карты как код нумпада — снять его нечем, - * потому что break придёт уже с префиксом. Дальше kbd_raw_any_down() - * навсегда «да», и всякое ожидание «пока ничего не нажато» виснет - * насмерть (BUG KBD-STUCK-WAIT; поймано на 14-м уровне, где почти весь - * путь идёт удержанием Left). Гасит биты kbd_raw_sync(), а его - * pop_ctrl_tick зовёт каждый кадр. */ - kbd_raw_keypad_as_ext(1); - 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 deleted file mode 100644 index 8964ae0..0000000 --- a/applications/SprPoP/src/pop_ctrl.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 deleted file mode 100644 index 7c61cca..0000000 --- a/applications/SprPoP/src/pop_cutscene.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 deleted file mode 100644 index d49303b..0000000 --- a/applications/SprPoP/src/pop_cutscene.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 deleted file mode 100644 index 4aaa18c..0000000 --- a/applications/SprPoP/src/pop_demo.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 deleted file mode 100644 index e4c0002..0000000 --- a/applications/SprPoP/src/pop_demo.h +++ /dev/null @@ -1,17 +0,0 @@ -/* 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 deleted file mode 100644 index 6be995e..0000000 --- a/applications/SprPoP/src/pop_draw.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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 deleted file mode 100644 index b96fa98..0000000 --- a/applications/SprPoP/src/pop_flow.c +++ /dev/null @@ -1,48 +0,0 @@ -/* 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 deleted file mode 100644 index 47022cc..0000000 --- a/applications/SprPoP/src/pop_flow.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 deleted file mode 100644 index 318e24a..0000000 --- a/applications/SprPoP/src/pop_geom.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * 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 deleted file mode 100644 index 67ef250..0000000 --- a/applications/SprPoP/src/pop_geom.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 deleted file mode 100644 index 099e852..0000000 --- a/applications/SprPoP/src/pop_guard.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * 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 deleted file mode 100644 index d747acf..0000000 --- a/applications/SprPoP/src/pop_guard.h +++ /dev/null @@ -1,393 +0,0 @@ -/* - * 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 deleted file mode 100644 index 35d7f63..0000000 --- a/applications/SprPoP/src/pop_guard_cold.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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 deleted file mode 100644 index a67613f..0000000 --- a/applications/SprPoP/src/pop_hof.c +++ /dev/null @@ -1,559 +0,0 @@ -/* - * 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" -#include "_pop_file.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); - POP_PATH_CALL(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(); - /* Неудача записи не меняет только что показанную таблицу: следующее - * открытие разумно начнёт с прежнего файла или с пустого списка. */ - { - int saved; - POP_PATH_CALL(saved, - 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(): подчерк той же гарнитуры сразу за текстом, - * мигающий сменой цвета на фоновый — у нас проще перерисовать. - * - * СТРОКА СТРОИТСЯ НА СТЕКЕ, И ЭТО НЕ ПРИДИРКА (поймано в MAME - * 2026-08-27). Литерал "_" лёг бы в _BANK10, то есть в окно W3, а - * pop_text_draw_mapped живёт в банке 9: трамплин на время вызова - * переключает W3, и указатель показывает уже в ЧУЖОЙ банк. Функция - * честно читает оттуда байты до первого нуля и рисует их знаками - * вопроса — экран заливало мусором на каждое мигание курсора. Стек - * лежит в W2, он виден из любого банка. */ - if (cursor) { - char cur[2]; - - cur[0] = '_'; - cur[1] = 0; - pop_text_draw_mapped(POP_TEXT_BIG_DARK, x, baseline, cur); - } - 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), 2 — отменён (Esc); 0 — продолжаем. - * *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 (uint8_t)(i == 28 ? 1 : 2); /* Enter : Esc */ - } - } - previous[i] = down; - } - return 0; -} - -/* Ввод имени в строку row. Пустое имя оригинал не отвергает, но пустая - * строка в таблице выглядела бы как свободное место — подставляем PLAYER. */ -/* Курсор мигает каждые шесть тиков 60 Гц (start_timer(timer_0, 6) в - * input_str) — в кадрах луча это HOF_BLINK. */ -#define HOF_BLINK POP_T60(6) - -/* 1 — игрок отказался вписывать имя (Esc). */ -static uint8_t 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 (;;) { - uint8_t done; - - kbd_raw_sync(); - done = hof_name_tick(entry, &length, previous, &changed); - if (done == 2) return 1; /* Esc — запись не нужна */ - if (done) { - if (!length) strcpy(entry->name, "PLAYER"); - return 0; - } - 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 сделан ею же. */ - (void)pop_pal_fade_out_skip(HOF_FADE); - page = pop_screen_begin(); - if (hof_page_build(page, row) == 0) { - pop_screen_present(page); - (void)pop_pal_fade_in_skip(HOF_FADE); - /* ОТКАЗ ПО Esc — РАСХОЖДЕНИЕ С ОРИГИНАЛОМ (docs/impl_diff.md). - * SDLPoP крутит `while (input_str(...) <= 0)`, то есть уйти без - * имени нельзя вовсе. Мы разрешаем: тогда таблицу перечитываем - * с диска (hof_insert_current уже сдвинул строки, и откатывать - * их дешевле всего именно так) и НЕ сохраняем. */ - if (hof_edit(row)) hof_load(); - else 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(); - (void)pop_pal_fade_out_skip(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 deleted file mode 100644 index dbe34be..0000000 --- a/applications/SprPoP/src/pop_hof.h +++ /dev/null @@ -1,21 +0,0 @@ -/* 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 deleted file mode 100644 index 2ffe184..0000000 --- a/applications/SprPoP/src/pop_intro.c +++ /dev/null @@ -1,1947 +0,0 @@ -/* - * 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_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. Раньше их - * печатал упаковщик в gen/pop_music_ticks.h, из-за чего EXE был - * привязан к набору; теперь они приезжают из индекса набора - * (MUS/mus.idx) при входе в сцену — см. ЯКОРЯ ниже. - * - * Всё, что стоит ПОСЛЕ реплики, сдвигается вместе с её концом — отсюда - * сложения от 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_MAGIC_LEAD = 0 -}; - -/* ПОДГОНКА ЗАКЛИНАНИЯ ПОД МУЗЫКУ СНЯТА (2026-08-28): теперь 0. - * - * История: сцена была render-bound — кадр рисовался дороже своего - * бюджета, кадровая ветка пейсинга догона не имела, и анимация шла - * ~49 тиков в секунду вместо 60 (замер 2026-08-27). Реплика Джафара - * при этом играет по РЕАЛЬНОМУ времени, поэтому кода приходила - * примерно на секунду раньше молнии, и жест целиком (замах, шаг - * назад, вспышка) сдвигали раньше сценария на подобранные на слух - * 100 тиков. - * - * Причину убрали: отрисовка разложена по кадровым интервалам - * (pv_restore_bg), сцена идёт по единственным часам — кадрам луча, — - * и подкачка трека оплачивается интервалом (pop_music_load_step). - * После этого подгонка стала вредной: молния била больше чем на - * секунду раньше коды (проверка пользователем 2026-08-28). Константу - * оставляем на месте — если запись другого набора (mt32/ogg) снова - * разъедется, крутить надо её, а не тайминги сценария. */ - -/* ЯКОРЯ ШКАЛЫ. Четыре точки, от которых отсчитывается всё остальное; - * считаются ОДИН РАЗ при входе в сцену из длительностей набора - * (pop_music_info). Держать их статикой, а не тащить структуру времён - * через пять функций: восемь байт против переделки всех сигнатур, а - * сцена всё равно одна на запуск. - * - * Нет индекса или трека — длительность нулевая, и сцена просто проходит - * без пауз на реплики: ждать нечего, звука-то нет. */ -static uint16_t pv_m50_end; /* конец «принцесса ждёт» (m50) */ -static uint16_t pv_dialog1; /* Джафар входит — начало реплики m53 */ -static uint16_t pv_exit; /* конец m53: Джафар уходит */ -static uint16_t pv_anim_end; /* конец всей сцены */ - -/* Ниже — те же выражения, что были в enum: биты сценария отсчитываются от - * своего якоря и от записи НЕ зависят. */ -#define PV_M50_END pv_m50_end -#define PV_WAIT_END ((uint16_t)(pv_m50_end + 40)) -#define PV_GATE_END ((uint16_t)(pv_m50_end + 88)) -#define PV_DOOR_END ((uint16_t)(pv_m50_end + 112)) -#define PV_TURN_START PV_DOOR_END -#define PV_WALK1_START ((uint16_t)(pv_m50_end + 152)) -#define PV_DIALOG1_START pv_dialog1 -#define PV_MUS_4_LOAD ((uint16_t)(pv_dialog1 + 54)) -#define PV_WALK2_START ((uint16_t)(pv_dialog1 + 176)) -#define PV_DIALOG2_START ((uint16_t)(pv_dialog1 + 416)) -#define PV_RAISE_START ((uint16_t)(pv_dialog1 + 696 - PV_MAGIC_LEAD)) -#define PV_STEPBACK_START ((uint16_t)(pv_dialog1 + 703 - PV_MAGIC_LEAD)) -#define PV_MAGIC_START ((uint16_t)(pv_dialog1 + 822 - PV_MAGIC_LEAD)) -#define PV_EXIT_START pv_exit -#define PV_MUS_4_START ((uint16_t)(pv_exit + 42)) -#define PV_GLASS_DONE ((uint16_t)(pv_exit + 210)) -#define PV_SLUMP_START ((uint16_t)(pv_exit + 273)) -#define PV_ANIM_TICKS pv_anim_end - -/* Собрать шкалу под НАШ набор записей. 200 — цепочка битов сценария от - * конца m50 до входа Джафара (40+48+24+40+48), 469 — хвост после его - * ухода; оба от записи не зависят. */ -static void pv_timing_init(void) -{ - pop_mus_info_t m50, m53; - - (void)pop_music_info(POP_MUS_STORY_2, &m50); - (void)pop_music_info(POP_MUS_STORY_3, &m53); - pv_m50_end = (uint16_t)(PV_MUS_2_START + m50.ticks); - pv_dialog1 = (uint16_t)(pv_m50_end + 200); - pv_exit = (uint16_t)(pv_dialog1 + m53.ticks); - pv_anim_end = (uint16_t)(pv_exit + 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; -} - -/* ВОССТАНОВЛЕНИЕ ФОНА ЗАДНЕЙ СТРАНИЦЫ — ДВУМЯ БЛОКАМИ ПО КАДРОВОМУ ИНТЕРВАЛУ. - * - * Персонажи и живой фон рисуются банком спрайтов и в ОЗУ-копию не оседают, - * поэтому «стереть прошлый кадр» = скопировать чистый фон из теневой копии - * соседней страницы. Раньше это была ОДНА копия на весь экран, и она стоила - * ~547 000 тактов = 1,27 кадра луча (модель blit_cost_model). Хвост кадра - * сцены ждал vsync ПОСЛЕ отрисовки, так что эти 1,27 кадра ПРИБАВЛЯЛИСЬ к - * пяти интервалам делителя: сцена шла 6+ кадров вместо 5, то есть ~8,2 fps - * вместо десяти (замер 49/60 тиков — docs/BUGS_OPEN.md PV-RENDER-BOUND). - * - * Лечение — не «удешевить», а РАЗЛОЖИТЬ по интервалам: каждый блок заведомо - * влезает в кадр, и после него честно ждём vsync. Тогда отрисовка живёт - * ВНУТРИ делителя, а не сверх него. - * - копируем только картинку (200 строк с POP_YOFF), а не все 256: - * сверху и снизу чёрная рамка, туда никто не рисует; - * - двумя половинами по 100 строк: ~219 000 тактов каждая, чуть больше - * половины кадра — запас двукратный. - * - * ТРАТИТ РОВНО ДВА кадровых интервала (по gfx_wait_vsync на блок) — это - * учтено в раскладке кадра у всех трёх сцен (см. вызовы). */ -#define PV_BG_Y0 POP_YOFF -#define PV_BG_H 200 - -static void pv_restore_bg(void) -{ - gfx_rect_t r; - - r.x = 0; - r.w = GFX_WIDTH; - r.h = PV_BG_H / 2; - r.y = PV_BG_Y0; - gfx_copy_page(&r, GFX_COPY_DIRECT); - gfx_wait_vsync(); - r.y = PV_BG_Y0 + PV_BG_H / 2; - gfx_copy_page(&r, GFX_COPY_DIRECT); - gfx_wait_vsync(); -} - -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) -{ - uint8_t back = (uint8_t)(gfx_get_visible_page() ^ 1); - - gfx_set_draw_page(back); - pv_restore_bg(); /* блоки 1-2: фон, два интервала */ - 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(); /* блок 3: актёры и декорации */ - 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 13; /* три интервала кадра + десять на молнию */ - } - return 3; /* сколько кадровых интервалов съели */ -} - -/* Сцена 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; /* кадров луча потрачено с начала сцены */ - 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(); - /* ЧЕМ МЕРИТЬ ВРЕМЯ — ЕДИНЫЕ ЧАСЫ: КАДРЫ ЛУЧА. - * - * Была вторая шкала — тики насоса CBL, и выбор между ними делался - * гонкой на старте сцены (одна выборка `pop_snd_tick` через кадр: - * успел ли диск раскрутить звук). Значит от прогона к прогону сцена - * шла то по одним часам, то по другим, и кода реплики приходилась - * каждый раз на другое место картинки (наблюдение пользователя - * 2026-08-28; баг SND-PACE-DEAD). Насос как часы был нужен потому, - * что кадр сцены рисовался ДОЛЬШЕ своего интервала и счёт наших - * ожиданий врал; теперь отрисовка разложена на блоки по кадровому - * интервалу (см. pv_restore_bg), и счёт кадров луча честен. - * - * Осталось учесть единственную работу, которая в интервал не влезала - * сама, — подкачку страницы трека: шаг читает ПОЛСТРАНИЦЫ (~16 мс, - * pop_music_load_step) и оплачивается ровно одним интервалом, как - * любой другой блок кадра. */ - 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; - pv_timing_init(); /* шкала под длительности НАШЕГО набора */ - 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(); /* как в оригинале — в конце отрисовки кадра */ - - /* ГАРАНТИРОВАННЫЙ ШАГ ПОДКАЧКИ, а не «если останется время». - * - * Раньше страница следующей реплики читалась в ПАУЗЕ кадра, и это - * верно ровно до тех пор, пока пауза есть. В этой сцене её почти - * не было: кадр дорогой, шкала работает по принципу «долг не - * догоняем», и цикл ожидания выходил сразу. Замер в MAME - * 2026-08-26: ОДНА страница за семь секунд, то есть одиннадцать - * страниц m53 не успевали за всю сцену — pop_music_play() на своём - * тике не находил готового трека, и реплики Джафара пропадали - * совсем (memory pv_music_stall_regression). - * - * Поэтому шаг безусловный — и оплачен как обычный блок кадра: он - * читает полстраницы (~16 мс), а следующий gfx_wait_vsync ставит - * нас на край интервала, который мы этим и заняли. */ - if (pop_music_loading()) { - (void)pop_music_load_step(); - gfx_wait_vsync(); - beam++; - } - /* Первый кадр собран под чёрной палитрой — теперь проявляем его - * целиком, вместе с принцессой и факелами. */ - if (!faded_in) { - faded_in = 1; - if (pop_pal_fade_in_skip(INTRO_FADE)) { result = 1; break; } - } - /* ПЕЙСИНГ ПО ОРИГИНАЛЬНОЙ ШКАЛЕ. Кадр сцены — 6/8/7 тиков по - * 1/60 с (pv_seq_period), в кадры луча переводит POP_T60, и цель - * КУМУЛЯТИВНА: нецелые 6,67 и 5,83 не копят ошибку. - * - * ДОЛГ НЕ ДОГОНЯЕМ: если кадр вышел дороже своего бюджета, цикл - * ожидания просто не сработает, но beam уже учёл потраченное — и - * следующая цель считается от той же общей шкалы. Наверстывать - * пропущенное нельзя: события соседних тиков (створка ворот, дверь - * покоев) слиплись бы в один кадр, и один из звуков пропал бы. */ - { - 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) (void)pop_pal_fade_out_skip(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; - 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; - 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); - pv_restore_bg(); /* блоки 1-2: фон, два интервала */ - 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(); /* блок 3: живой фон */ - gfx_set_visible_page(back); - /* Три интервала уже потрачены на блоки отрисовки — добираем до - * пяти (5/50 с = кадр катсцены оригинала). */ - for (hold = 3; hold < 5; hold++) gfx_wait_vsync(); - - /* Проявляем ПОСЛЕ первого кадра: иначе две секунды fade идут по - * пустой комнате, а часы и пламя возникают уже на свету. */ - if (frame == 0 && pop_pal_fade_in_skip(INTRO_FADE)) { result = 1; break; } - 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 = 6` тиков по 1/60 с (reset_cutscene, - * seg001:527, и его зовёт load_intro прямо перед сценой) = 100 мс, у нас - * 5 кадров луча по 50 Гц = те же 100 мс. - * - * РАСКЛАДКА ПО ИНТЕРВАЛАМ (иначе отрисовка прибавлялась к делителю, а не - * жила внутри него — см. pv_restore_bg): фон половинами 1-2, актёры и - * декорации 3, служебный блок (звук/подкачка музыки) 4-5. */ -#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) -{ - uint16_t i; - uint8_t hold; - - 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-копии; персонажи рисуются - * банком спрайтов и потому в неё не оседают. Двумя блоками по - * кадровому интервалу — см. pv_restore_bg. */ - pv_restore_bg(); /* блоки 1-2 */ - 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(); /* блок 3: актёры и декорации */ - gfx_set_visible_page(back); - - /* Блок 4 — служебный, и он НЕ добавляется к кадру, а живёт внутри - * оставшихся интервалов: подкачка страницы трека стоит ~33 мс, то - * есть меньше двух интервалов, которые всё равно надо переждать. - * Порядок как в сцене с Джафаром: шаг подкачки — безусловный (см. - * memory pv_music_stall_regression). */ - pop_sfx_tick(); - pop_music_service(); /* «встреча с принцессой» — трек, а не эффект */ - for (hold = 3; hold < CUT_FRAME_VSYNC; hold++) gfx_wait_vsync(); - 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_skip(INTRO_FADE)) cut_aborted = 1; - 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) aborted = pop_pal_fade_in_skip(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) aborted = pop_pal_fade_in_skip(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) aborted = pop_pal_fade_in_skip(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) aborted = pop_pal_fade_in_skip(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 || ev == POP_CS_EVENT_FADE_OUT) { - /* Fade — 2,13 с, и на стыке экранов их два. Пока он был глухим, - * эти секунды не пропускались ничем: пропуск работал внутри - * сценария, но не между его шагами. Прерывание здесь означает - * то же, что и в самом сценарии — уйти из заставки. */ - uint8_t cut = (ev == POP_CS_EVENT_FADE_IN) - ? pop_pal_fade_in_skip(INTRO_FADE) - : pop_pal_fade_out_skip(INTRO_FADE); - - if (cut) { pop_cutscene_abort(&cs); aborted = 1; break; } - } - 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 1; - 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) - (void)pop_pal_fade_in_skip(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(); - /* Прервали проявление — это не «показать картинку разом», а «хватит - * заставки»: маршрут тот же, что и у пропуска внутри сценария. */ - if (pop_screen_present_ltr()) { - pop_music_free(); - pop_sfx_pause(); - return 1; - } - } - 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 читается ЗДЕСЬ, под чёрным экраном и в тишине: - * дальше внутри сцены остаётся только пуск, а следующие две реплики - * подкачиваются по странице за кадр. */ - /* Глушение снято 2026-08-28: массовое чтение страниц насос не морит - * (замер на загрузке уровня — ни одной пропущенной порции), а трек - * титров может и должен доигрывать. */ - (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 — тоже - * полосами. Раньше обе картинки возникали мгновенно. */ - { - int shown = pop_title_logo_ltr(); /* 1 — прервано, -1 — нет архива */ - - if (shown == 1 || (shown == 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) -{ - /* ТРЕК ПЕРЕЖИВАЕТ СЦЕНУ — как в оригинале. load_intro (seg001:690) - * ничего не ждёт: гасит экран и возвращает управление, а звук играет - * дальше поверх загрузки уровня и первых секунд игры. Мы вместо этого - * ДОСИЖИВАЛИ трек на чёрном экране (27 — 10,7 с против 2,6 с картинки), - * то есть дарили игроку восемь секунд пустого экрана, и только потом - * глушили. ЗАМЕР 2026-08-28: снято вместе с глушением на загрузке - * уровня; если загрузка не укладывается в дедлайн насоса (251 000 - * тактов), вернуть оба места разом. - * - * Пропуск сцены по-прежнему обрывает и музыку: игрок нажал клавишу, - * чтобы идти дальше, а не чтобы дослушать. */ - if (aborted) { - 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; - - /* Глушение перед чтением трека снято 2026-08-28 (см. замер насоса). */ - /* МУЗЫКА ПЕРЕЖИВАЕТ СЦЕНУ. Треки заставок между уровнями длиннее самих - * сцен (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 -{ - /* Глушение снято 2026-08-28 (см. замер насоса). */ - /* 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 -{ - /* Глушение снято 2026-08-28 (см. замер насоса). */ - /* Экран гасим ПЕРВЫМ действием — как 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 deleted file mode 100644 index 105d960..0000000 --- a/applications/SprPoP/src/pop_intro.h +++ /dev/null @@ -1,29 +0,0 @@ -/* 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 deleted file mode 100644 index b4cdc3b..0000000 --- a/applications/SprPoP/src/pop_kboot.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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" -#include "_pop_file.h" - -/* Серии в архиве обязаны лежать подряд — так их кладёт список в Makefile, - * и только поэтому индекс считается сложением. Если порядок собьётся, - * сборка встанет здесь, а не рассыплется картинкой на экране. */ -#if (ARC_KID_SWORD - ARC_KID_KID0_V) != KID_PAGES -#error "обычная и зеркальная серии Kid должны иметь одинаковую длину" -#endif - -/* Таблица архива живёт на стеке вызывающего (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_PAGES) npages = KID_PAGES; - 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); - POP_PATH_CALL(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 deleted file mode 100644 index dcc6fdc..0000000 --- a/applications/SprPoP/src/pop_kdraw.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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 deleted file mode 100644 index f65b564..0000000 --- a/applications/SprPoP/src/pop_kid.c +++ /dev/null @@ -1,474 +0,0 @@ -/* - * 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 "pop_cutscene.h" /* POP_T60 — тики оригинала в кадры луча */ -#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). Отдан как есть, а не через аксессор: читается раз - * в кадр из другого банка, и вызов стоил бы дороже самого чтения. */ -atlas_t kidp[KID_PAGES]; -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-м оригинал молчит. Держать уровень на экране, - * пока трек звучит, — забота главного цикла: он ждёт - * pop_music_active(), и своей задержки здесь больше - * нет (была по длине записи из gen/pop_music_ticks.h, - * из-за чего EXE зависел от набора музыки). */ - 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 deleted file mode 100644 index 9f8d323..0000000 --- a/applications/SprPoP/src/pop_kid.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * 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 "kid_arc.h" /* границы серий атласов в kid.arc */ -#include /* atlas_t — набор спрайтов Кида (см. kidp ниже) */ - -/* Число страниц Кида берём из метаданных архива: зеркальная серия начинается - * сразу после обычной. Не дублировать литералом в загрузчике/хранилище. */ -#define KID_PAGES (ARC_KID_KID0_V - ARC_KID_KID0) - -/* Состояние персонажа — общий тип для Кида, стража и 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; -/* Задержка смены уровня на время мелодии конца (см. pop_kid.c). */ - -/* Загрузить 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 deleted file mode 100644 index 4edd4f4..0000000 --- a/applications/SprPoP/src/pop_level.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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 deleted file mode 100644 index 1031c0e..0000000 --- a/applications/SprPoP/src/pop_level.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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` относительно каталога EXE и выставить - * 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 deleted file mode 100644 index 0ecfb4a..0000000 --- a/applications/SprPoP/src/pop_level_cold.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * 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 */ -#include "_pop_file.h" - -/* Низкоуровневое чтение через 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); - POP_PATH_CALL(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] */ - 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] = (char)('0' + n / 10); - pri[13] = (char)('0' + n % 10); - - /* Старую страницу отпускаем ТОЛЬКО после успешной загрузки новой: - * level_read_file сам аллоцирует страницу, и если файла нет — остаёмся - * на текущем уровне вместо падения в пустой уровень. */ - pop_lvl_ok = 0; /* чтобы level_load_path не мешался */ - if (level_read_file(pri) != 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 deleted file mode 100644 index 02c5e6b..0000000 --- a/applications/SprPoP/src/pop_level_qsave.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Сериализация уровня для 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(); } - -/* Тайл ПОЗАДИ персонажа и расстояние до кромки — наружу, для боевой - * половины (guards.c): по ним оригинал выбирает, какой смертью умирает - * заколотый — на месте или сброшенным с уступа (seg002, hurt_by_sword). - * Обе давно работают от Char, а не от Kid; не хватало только объявлений - * (docs/sdlpop_audit.md, находка 12). */ -uint8_t pop_tile_behind_char(void) __banked { return get_tile_behind_char(); } - -/* расстояние до края тайла (для 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()); } - -int8_t pop_dist_to_edge_weight(void) __banked -{ - int d = distance_to_edge_weight(); - return (int8_t)(d > 127 ? 127 : (d < -128 ? -128 : d)); -} - -/* ---- ТОЧНАЯ коллизия со стеной (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 */ - /* seg005:0173 — ВСЯ развязка приземления (мягко/средне/разбиться) у - * оригинала заперта за `if (Char.alive < 0)`, то есть выполняется - * только для ЖИВОГО. Мёртвому телу отведена своя дорога (seg005 ветка - * else, loc_5F6C): добить HP, звук падения насмерть и seq_22. - * - * Развилки у нас не было, и труп шёл по живой дороге. Смертельно это - * не выглядело только из-за высоты: тело, сброшенное ударом с ОДНОГО - * ряда, набирает fall_y < 22 — урона нет, «последнее HP» не тратится, - * ветка «разбился» не выбирается никогда, и мёртвый Кид приземлялся - * в ПРИСЕД (кадр 109, seq_17). Отсюда симптом «убитого Кида уронили, - * а он сел этажом ниже, и только потом обнаружилось, что он мёртв». - * Цена проверки — один тест байта на приземление. */ - if (Char.alive >= 0) goto crushed; - 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 */ - crushed: - /* Сюда же приходит УЖЕ мёртвое тело (см. развилку выше); для - * него обнуление ниже — первое, живой путь его уже сделал. */ - Char.fall_x = Char.fall_y = 0; - 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 на несколько кадров). - * - * ВОЗВРАТ: 1 — зацеп СОСТОЯЛСЯ на этом кадре. Нужен вызывающему (do_fall): - * дальше по кадру идёт glide_through_wall_guard, и ему нельзя трогать - * персонажа, которого мы только что повесили на кромку (см. там же). */ -static uint8_t check_grab(void) -{ - uint8_t old_x; - if (!pop_ctrl_shift_held()) return 0; /* Shift не зажат */ - if ((uint8_t)Char.fall_y >= 32) return 0; /* падает слишком быстро */ - if ((uint16_t)pop_y_land[Char.curr_row + 1] > (uint16_t)(Char.y + 25)) return 0; - 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(); - return 0; - } - 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 */ - return 1; -} - -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; -} - -/* НЕ ДАТЬ ПРОЛЕТЕТЬ СКВОЗЬ СТЕНУ В ПАДЕНИИ. - * - * Порт опционального исправления SDLPoP (fix_glide_through_wall, seg005 в - * do_fall). В ванили personаж, падающий после разворота в беге, может - * оказаться внутри кладки и лететь «в стене» — баг оригинала, известный и - * воспроизводимый; у нас он ловится host-тестом (tests/host/t_wall.c, - * набор wall_stops_jump_from_left_side). - * - * ВЗЯТО В ТЕКУЩИЙ БИЛД по решению 2026-08-31: играбельность важнее - * буквальности. При разделении VANILLA/ENHANCED эта функция — готовая - * точка отвязки: достаточно не звать её в ванильном режиме - * (docs/vanilla_vs_bugfixed.md). - * - * Условие оригинала: персонаж внутри тайла стены, либо внутри верха двери - * (в обоих вариантах) при движении ВЛЕВО. Порог 8 и сдвиг на 15 назад — - * из исправления; они выбраны так, чтобы вытолкнуть на ту же дистанцию, - * что даёт выталкивание из стены при приземлении. Горизонтальную - * скорость гасим: иначе следующий кадр внесёт персонажа обратно. */ -static void glide_through_wall_guard(void) -{ - uint8_t t; - int d; - - determine_col(); - t = get_tile_at_char(); - if (t != TILE_WALL && - !((t == TILE_DOORTOP || t == TILE_DOORTOP_FLOOR) && Char.direction < 0)) - return; - d = distance_to_edge_weight(); - if (d < 8) return; - Char.x = (uint8_t)char_dx_forward((int8_t)(d - 15)); - Char.fall_x = 0; -} - -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) { - /* ЗАЦЕП ОТМЕНЯЕТ GUARD. seq_15 — «grab ledge MIDAIR», то есть после - * удачного зацепа Char.action == 3, персонаж уже подтянут вплотную к - * кромке, а под кромкой в оригинальных комнатах стоит КЛАДКА. Guard, - * позванный следом, видел ровно её (t == TILE_WALL, d == 10), считал - * это «пролётом сквозь стену», отбрасывал Кида на 5 пикселей назад и - * гасил fall_x — зацеп рисовался и тут же срывался. Ловилось на - * длинном прыжке уровня 3 (комната 7 -> кромка комнаты 2) и в - * attract-демо; регресс-набор — tests/host/t_glide.c. */ - if (!check_grab()) /* ещё летит — попытка зацепа */ - glide_through_wall_guard(); /* и не сквозь кладку (см. выше) */ - } 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 deleted file mode 100644 index 1208c8e..0000000 --- a/applications/SprPoP/src/pop_map.h +++ /dev/null @@ -1,290 +0,0 @@ -/* - * 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; - -/* Тайл позади АКТИВНОГО персонажа и расстояние до кромки его тайла. - * Нужны боевой половине (guards.c) для выбора смерти у обрыва. */ -uint8_t pop_tile_behind_char(void) __banked; -int8_t pop_dist_to_edge_weight(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 deleted file mode 100644 index afefd8c..0000000 --- a/applications/SprPoP/src/pop_menu.c +++ /dev/null @@ -1,803 +0,0 @@ -/* - * 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_music.h" /* pop_music_service — подкачка кольца, см. ui_wait_frame */ -#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 — ДВЕ КОЛОНКИ. В одну колонку раскладка уже не помещалась: - * после приведения клавиш к keys.txt (docs/keys_plan.md) их стало вдвое - * больше, а по высоте есть только 200 точек на всё вместе с заголовком. - * - * Слева — игра (бег, лазание, бой), справа — служебное и читы. Число строк - * в колонках разное и зависит от того, включены ли читы, поэтому высота - * блока считается в рантайме по ДЛИННОЙ колонке. */ -#define CTRL_TITLE_GAP 18 -#define CTRL_N_LEFT 10 /* игровые клавиши */ -#define CTRL_N_SYS 7 /* служебные */ -#define CTRL_N_CHEAT 5 /* читы: показываются, только если включены */ -/* Колонки сдвинуты вправо на 15 точек против первой раскладки: там слева - * оставалось 6 свободных точек, а справа 36, и блок выглядел прижатым к - * краю. Теперь поля 21 и ~20 — по обе стороны одинаково. */ -#define CTRL_L_KEY 21 -#define CTRL_L_DESC 77 -#define CTRL_R_KEY 179 -#define CTRL_R_DESC 235 -#define CTRL_STEP 13 - -/* 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); - - /* ЗВУК ЗДЕСЬ БОЛЬШЕ НЕ ГЛУШИМ (снято 2026-08-28). - * - * Был вызов pop_sfx_pause() с обоснованием «полностраничная accel-копия - * держит DI дольше половины CBL-буфера, иначе железо повторяет старые - * 128 байт». Обоснование устарело: лист, который зовёт gfx_copy_page - * (_bgi_scroll_rows_raw), режет DI бандами по 16 строк — это ~1,6 мс - * против дедлайна насоса 11,7 мс, и окно прерываний между бандами есть. - * Тот же полностраничный копир каждый кадр делают игровой цикл и - * катсцены, и звук там не рвётся. - * - * А цена была слышимой: pause закрывает CBL целиком (cbl_close), курсор - * трека остаётся на месте, и короткая мелодия начала уровня замирала - * ровно на время перерисовки меню — «тормозится на кусочек» (жалоба - * пользователя 2026-08-28). У оригинала такого нет и быть не может: - * там нет насоса, который надо кормить руками. */ - 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; -} - -/* КАДР МЕНЮ: перед ожиданием фронта — обслужить музыку. - * - * Насос CBL живёт в прерывании и главный цикл ему не нужен, НО потоковый - * трек (титульная тема играется КОЛЬЦОМ, pop_music.c) дочитывается с диска - * именно из pop_music_service: очередная страница попадает в слот, который - * насос уже прошёл. Пока открыто меню, главный цикл стоит, и без этого - * вызова кольцо опустошалось за несколько секунд — музыка замолкала до - * закрытия меню, а на перемещениях по пунктам это слышно как паузы - * (жалоба пользователя 2026-08-28). - * - * Место — рядом с ожиданием кадра: получается тот же темп 50 раз в - * секунду, что и в игровом цикле, и подкачка не чаще, чем нужна. */ -static void ui_wait_frame(void) -{ - pop_music_service(); - pop_wait_edge(); -} - -static void ui_end(uint8_t back) -{ - ui_wait_frame(); /* готовый кадр показать только на фронте */ - gfx_set_visible_page(back); - /* Парный к снятому pop_sfx_pause (см. ui_begin) вызов оставлен: он - * идемпотентен (открытый CBL возвращает 0 сразу) и чинит случай, когда - * в меню вошли при закрытом выводе — например сразу после загрузки - * уровня, которая глушит звук намеренно. */ - (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)) - ui_wait_frame(); -} - -/* Действие = 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; - ui_wait_frame(); - } -} - -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 строк общие, за - * ними идут читовые: их показываем, только когда читы включены. */ -/* ЛЕВАЯ КОЛОНКА — игра. Строка «IN A FIGHT» здесь заголовок, а не клавиша: - * ниже неё те же стрелки и Shift означают уже другое, и без разделителя - * список читался бы как противоречивый. */ -static const char * const ctrl_l_key[CTRL_N_LEFT] = { - "ARROWS", "UP", "DOWN", "SHIFT", "SH+ARROWS", "HOME PGUP", - "IN A FIGHT", "SHIFT", "UP", "DOWN" -}; -static const char * const ctrl_l_desc[CTRL_N_LEFT] = { - "RUN / TURN", "JUMP, CLIMB", "CROUCH", "PICK UP", "CAREFUL STEP", - "JUMP L / R", - "", "STRIKE", "BLOCK", "SHEATHE" -}; - -/* ПРАВАЯ КОЛОНКА — служебное, ниже читы. */ -static const char * const ctrl_r_key[CTRL_N_SYS + 1 + CTRL_N_CHEAT] = { - "BACKSPACE", "SPACE", "T", "F6 / F9", "CTRL+A", "CTRL+S M", "CTRL+Q F10", - "CHEATS", - "SHIFT+L", "K", "+ / -", "SHIFT+I", "SHIFT+S T" -}; -static const char * const ctrl_r_desc[CTRL_N_SYS + 1 + CTRL_N_CHEAT] = { - "MENU", "TIME LEFT", "SHOW TIMER", "SAVE / LOAD", "RESTART LVL", - "SOUND / MUSIC", "QUIT", - "", - "NEXT LEVEL", "KILL GUARD", "TIME + / -", "FLIP SCREEN", "HEAL / MAX HP" -}; - -/* Одна строка колонки: слева клавиша, справа что она делает. Пустое - * описание — значит строка была заголовком секции, и клавишу мы печатаем - * как есть, без второго столбца. */ -static void ctrl_row(int kx, int dx, int y, const char *key, const char *desc) -{ - pop_text_draw_mapped(POP_TEXT_SMALL, kx, y, key); - if (desc[0]) pop_text_draw_mapped(POP_TEXT_SMALL, dx, y, desc); -} - -static void controls_draw(void) -{ - uint8_t back = ui_begin(); - /* С выключенными читами правая колонка короче: вместо пяти строк идёт - * одна пояснительная. Блок центрируется по ДЛИННОЙ из двух колонок. */ - uint8_t cheats = pop_settings.cheats_enabled; - uint8_t rright = (uint8_t)(cheats ? CTRL_N_SYS + 1 + CTRL_N_CHEAT - : CTRL_N_SYS + 1); - uint8_t rows = (uint8_t)(rright > CTRL_N_LEFT ? rright : CTRL_N_LEFT); - uint8_t i; - int top = UI_CENTER_TOP(POP_FONT_BIG_ASCENT + CTRL_TITLE_GAP + - (int)(rows - 1) * CTRL_STEP); - int y0 = top + POP_FONT_BIG_ASCENT; - int y; - - pop_text_map(); - pop_text_center_mapped(POP_TEXT_BIG, GFX_WIDTH / 2, y0, "CONTROLS"); - y = y0 + CTRL_TITLE_GAP; - for (i = 0; i < CTRL_N_LEFT; i++, y += CTRL_STEP) - ctrl_row(CTRL_L_KEY, CTRL_L_DESC, y, ctrl_l_key[i], ctrl_l_desc[i]); - y = y0 + CTRL_TITLE_GAP; - for (i = 0; i < rright; i++, y += CTRL_STEP) { - const char *key = ctrl_r_key[i], *desc = ctrl_r_desc[i]; - - /* Заголовок секции читов: с выключенными читами он же и объясняет, - * почему список ниже пуст. */ - if (i == CTRL_N_SYS && !cheats) desc = "DISABLED"; - ctrl_row(CTRL_R_KEY, CTRL_R_DESC, y, key, desc); - } - 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); - } - ui_wait_frame(); - } -} - -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); - /* Звук не глушим: POP.CFG — десятки байт, а на практике HOF пишется - * под звучащий «won» и насос это переживает (проверка пользователя - * 2026-08-28). Прежняя осторожность «ESTEX может уйти в диск на много - * CBL-периодов» относилась к загрузке НАБОРА страниц, а не к записи - * одного мелкого файла. */ - rc = pop_cfg_save(); - 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); - } - ui_wait_frame(); - } -} - -static int8_t menu_quickload(void) -{ - uint8_t slots; - /* probe — это только open/close на D:, данных не читает; глушить насос - * ради него незачем (см. settings_finish). */ - slots = pop_qsave_probe(); - 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); - ui_wait_frame(); - gfx_set_visible_page(hidden); - - /* Теперь чистая hidden — источник для второй страницы. */ - gfx_set_draw_page(visible); - gfx_copy_page(&full, GFX_COPY_DIRECT); - if (entry_page != hidden) { - ui_wait_frame(); - gfx_set_visible_page(entry_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)) ui_wait_frame(); - - entry_page = gfx_get_visible_page(); - saved_page = gfx_get_draw_page(); - saved_bank = gfx_get_bank(); - getfillsettings(&saved_fill); - /* Звук НЕ глушим (снято 2026-08-28, вторая половина той же заплатки — - * см. ui_begin). Здесь под ней были снимок палитры, затемнение фона и - * первая полная перерисовка: копия режет DI бандами по 16 строк, а - * палитровое затемнение идёт и в титрах с катсценами, где музыку оно не - * рвёт. Цена глушения была слышна как пауза в мелодии ровно на - * появление меню. */ - 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); - } - ui_wait_frame(); - pop_pace_end(pop_pace_n(0)); /* игра стоит — обычный темп */ - } - - /* И на закрытии не глушим: menu_erase — те же две полностраничные - * копии, что и в ui_begin, с DI бандами по 16 строк. */ - 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 deleted file mode 100644 index 18efe13..0000000 --- a/applications/SprPoP/src/pop_menu.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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-клавиатуры и до первого игрового кадра. */ - -#endif diff --git a/applications/SprPoP/src/pop_music.c b/applications/SprPoP/src/pop_music.c deleted file mode 100644 index 8e3718f..0000000 --- a/applications/SprPoP/src/pop_music.c +++ /dev/null @@ -1,488 +0,0 @@ -/* - * pop_music.c — загрузка и запуск музыкального трека, БАНК 9. - * - * Здесь только холодная часть: выделить страницы EMM, вычитать в них куски - * m_.bin и выставить курсор. Сам вывод делает насос CBL в - * резиденте (pop_sfx.c): когда эффект не звучит, он берёт байты отсюда, а - * не из блока тишины. Контракт и мотивация — в pop_music.h. - * - * Имя файла собираем вручную, без sprintf: printf-семейство в резидент не - * тянем, а тут и формат-то из двух чисел. - */ -#include -#include -#include -#include -#include -#include /* gfx_w0_map/unmap — доступ к странице индекса */ -#include "pop_music.h" -#include "pop_sfx.h" -#include "_pop_sfx.h" /* pop_snd_ok — открыт ли вывод (см. pop_music_busy) */ -#include "pop_app.h" /* POP_APP_DEMO — в демке музыки нет */ -#include "_pop_file.h" - -uint8_t pop_mus_page[POP_MUS_PAGES]; - -/* --- ИНДЕКС НАБОРА ---------------------------------------------------- * - * - * Страница EMM с прочитанным mus.idx; 0 — индекса нет. В W2 от него - * остаются ровно эти два байта, сама таблица (228 Б) лежит в странице. - * - * Данные лежат со смещения POP_MUS_IDX_BASE, а не с начала страницы: - * gfx_w0_page_prepare пишет туда стабы прерываний (0x38 и 0x66), и с - * нуля они попали бы прямо в записи. Со смещением всё расходится, и - * страница подключается штатно — без запрета прерываний. */ -static uint8_t mus_idx_blk; -static uint8_t mus_idx_page; - -/* "MUS\mus.idx" — имя знает сам модуль (тот же приём, что в pop_arc.c: - * литерал лежит в rodata банка 9, и open() читает его, пока в W3 стоит - * этот банк). */ -static const char mus_idx_path[] = "MUS\\mus.idx"; - -int8_t pop_music_index_load(void) __banked -{ - uint8_t *w0 = (uint8_t *)POP_MUS_IDX_BASE; - uint8_t blk, page, ok; - int fd; - - if (mus_idx_page) return 0; - POP_PATH_CALL(fd, open(mus_idx_path, O_RDONLY)); - if (fd < 0) return -1; - blk = mem_alloc_pages(1); - if (!blk) { close(fd); return -1; } - page = mem_get_page(blk, 0); - if (!page || - bank_read_page(fd, page, POP_MUS_IDX_BASE, POP_MUS_IDX_SIZE) < 0) { - close(fd); - mem_free_block(blk); - return -1; - } - close(fd); - gfx_w0_page_prepare(page); /* стабы лягут вне данных, см. BASE */ - gfx_w0_map(page); - ok = (uint8_t)(memcmp(w0, "PMI1", 4) == 0 && - w0[4] == POP_MUS_COUNT && - w0[5] == POP_MUS_IDX_REC && - w0[6] == 7 && /* log2(128) — блок насоса */ - w0[7] == 1); /* u8 mono, CBL_FREQ_10K9 */ - gfx_w0_unmap(); - if (!ok) { mem_free_block(blk); return -1; } - mus_idx_blk = blk; - mus_idx_page = page; - return 0; -} - -int8_t pop_music_info(uint8_t id, pop_mus_info_t *out) __banked -{ - const uint8_t *rec; - const uint8_t *w0 = (const uint8_t *)POP_MUS_IDX_BASE; - - out->blocks = 0; - out->ticks = 0; - if (!mus_idx_page || id >= POP_MUS_COUNT) return -1; - gfx_w0_map(mus_idx_page); - rec = w0 + POP_MUS_IDX_HDR + (uint16_t)id * POP_MUS_IDX_REC; - out->blocks = (uint16_t)(rec[0] | ((uint16_t)rec[1] << 8)); - out->ticks = (uint16_t)(rec[2] | ((uint16_t)rec[3] << 8)); - gfx_w0_unmap(); - return out->blocks ? 0 : -1; -} - -/* Два слота: играющий и подгружаемый. Своей таблицы страниц слот НЕ - * держит — её и так хранит блок 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_half; /* 0 = впереди первая половина страницы */ -static uint8_t ld_pages; -static uint8_t ld_id; -/* Длина трека из индекса, снятая при открытии: лезть в страницу индекса - * на каждом шаге загрузки незачем — она не меняется. */ -static uint16_t ld_blocks; - -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 кадрах в секунду сцена встала бы на пять кадров. - * Шаг стоит 16 мс (полстраницы, см. pop_music_load_step) — меньше одного - * кадрового интервала, и подкачка растворяется в анимации. Кто может позволить себе паузу (титры, чёрный экран - * между сценами), зовёт pop_music_load — тот же цикл, но целиком. */ -static void load_abort(void) -{ - if (ld_busy) close(ld_fd); - ld_busy = 0; - ld_half = 0; - slot_release(ld_slot); -} - -int8_t pop_music_load_begin(uint8_t id) __banked -{ - char name[12]; - pop_mus_info_t info; - uint8_t pages, n; - - if (ring_on) pop_music_ring_stop(); /* потоковый трек и слоты не дружат */ - if (ld_busy) load_abort(); - if (id >= POP_MUS_COUNT) return -1; - /* Длину и число страниц знает ИНДЕКС НАБОРА, а не EXE: у mt32 те же - * треки другой длины (docs/music_runtime_index_plan.md). */ - if (pop_music_info(id, &info) != 0) return -1; - pages = pop_music_pages(info.blocks); - if (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 КБ), и платить его на каждую страницу — чистые потери. */ - POP_PATH_CALL(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_half = 0; - ld_pages = pages; - ld_id = id; - ld_blocks = info.blocks; - return 0; -} - -/* ШАГ = ПОЛСТРАНИЦЫ (8 КБ, ~16 мс), а не страница (~33 мс). - * - * Шаг вызывают раз в кадр из циклов, где кадр разложен на блоки по одному - * кадровому интервалу (20 мс): катсцены после перехода на блочную - * отрисовку, см. pop_intro.c/pv_restore_bg. Целая страница в интервал не - * влезала и незаметно растягивала кадр сцены; половина влезает, а цена — - * вдвое больше вызовов (11 страниц реплики = 22 кадра вместо 11, то есть - * меньше полусекунды). Игровому циклу это безразлично: там логический - * кадр 80-100 мс. */ -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); - /* Последняя страница короче — читаем сколько есть (вторая половина - * может дать и ноль байт); ошибку ловим по отрицательному коду. */ - if (!p || bank_read_page(ld_fd, p, ld_half ? 8192 : 0, 8192) < 0) { - load_abort(); - return -1; - } - if (!ld_half) { ld_half = 1; return 0; } /* дочитаем в следующий раз */ - ld_half = 0; - if (++ld_next < ld_pages) return 0; - - close(ld_fd); - ld_busy = 0; - slot_pages[ld_slot] = ld_pages; - slot_blocks[ld_slot] = ld_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] = ld_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]; - pop_mus_info_t info; - uint8_t i, page; - - if (id >= POP_MUS_COUNT || pop_music_info(id, &info) != 0) return -1; - /* Потоковый трек НЕ ограничен POP_MUS_PAGES: он и не помещается в - * память целиком, ради того и кольцо. Предел здесь — восьмибитные - * счётчики страниц (у mt32 финальная тема на 82 страницы против 78 - * у flac). */ - if (pop_music_pages(info.blocks) > 255) return -1; - pop_music_free(); /* обычные слоты кольцу не нужны */ - pop_music_ring_stop(); - mus_name(name, id); - POP_PATH_CALL(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 = (uint8_t)pop_music_pages(info.blocks); - ring_total = info.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 -{ - /* «ЗВУЧИТ», А НЕ «ЕСТЬ КУРСОР». Курсор трека двигает насос CBL, и пока - * вывод закрыт (pop_sfx_pause на загрузке уровня, Ctrl+S, показ заставки - * после Restart Game) pop_mus_left не убывает НИКОГДА. Вызывающий, - * который ждёт конца мелодии, в этом состоянии висит вечно — ровно так - * заставка после Restart Game вставала на экране Prince of Persia - * (поймано пользователем 2026-09-02). Корень того случая был в другом - * (CBL не открывался заново, см. pop_boot_sound), но ждать неиграющую - * музыку нельзя в принципе, поэтому признак спрашивает и про вывод. */ - return (uint8_t)(pop_snd_ok && pop_mus_left != 0); -} - -/* ТРЕК В РАБОТЕ — заявка лежит, идёт загрузка или он звучит. - * - * Одного pop_music_busy() там, где ждут КОНЦА мелодии, мало: между - * заявкой (pop_sfx_play кладёт pop_mus_req) и первым звуком проходит - * 190-230 мс — открытие файла и чтение первой полустраницы (замер и - * разбор — docs/sound_plan.md §9). В этой щели busy ещё ложен, и - * ожидание закончилось бы, не начавшись. - * - * Три признака подряд закрывают её без единой подобранной константы: - * pop_music_service обнуляет req и тут же взводит ld_busy в load_begin, - * а тот держится до самой записи pop_mus_left. Снаружи переход - * «запрошен -> грузится -> звучит» непрерывен, потому что между этими - * операторами главный цикл не выполняется. - * - * И наоборот: если трека нет или память кончилась, все три ложны сразу — - * ждать нечего, как и должно быть. */ -uint8_t pop_music_active(void) __banked -{ - return (uint8_t)(pop_mus_req || ld_busy || pop_mus_left); -} diff --git a/applications/SprPoP/src/pop_music.h b/applications/SprPoP/src/pop_music.h deleted file mode 100644 index 9a63c6d..0000000 --- a/applications/SprPoP/src/pop_music.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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 - -/* --- ИНДЕКС НАБОРА (MUS/mus.idx, формат PMI1) ------------------------- * - * - * Длины треков и длительности реплик приезжают С ДИСКА, а не из EXE: так - * набор записей (flac/mp3/ogg/mt32) меняется без перекомпиляции игры. - * Формат пишет tools/pop_idx.py, он же покрыт make test-tools. - * - * Индекс живёт в ОДНОЙ странице EMM, а не в W2: таблица на 57 записей — - * 228 байт, а свободной кучи в резиденте порядка двух сотен. Наружу - * указатель на EMM не выдаётся: pop_music_info копирует запись в буфер - * вызывающего, на короткое время подключая страницу в W0. */ -#define POP_MUS_COUNT 57 /* пространство id оригинала, 0..56 */ -#define POP_MUS_IDX_REC 4 /* blocks (u16) + ticks60 (u16) */ -#define POP_MUS_IDX_HDR 16 -#define POP_MUS_IDX_SIZE 512 /* сектор DSS */ -/* Индекс кладётся в страницу СО СМЕЩЕНИЯ 0x100, а не с нуля. Причина в - * gfx_w0_page_prepare: он делает страницу безопасной в W0, записывая в - * неё переход на IRQ-восстановитель (0x38) и RETN для NMI (0x66). С - * нуля эти байты легли бы прямо в записи (id 10 и 21) и испортили - * индекс; со смещением 0x100 стабы и данные не пересекаются, и работает - * штатная защита от прерываний, а не запрет их. Тот же приём и по той - * же причине — в pop_config.c (CFG_BASE). */ -#define POP_MUS_IDX_BASE 0x100 - -typedef struct { - uint16_t blocks; /* длина трека в порциях насоса по 128 байт */ - uint16_t ticks; /* та же длина в тиках сцены (60 Гц оригинала) */ -} pop_mus_info_t; - -/* Поднять индекс (один раз за запуск). 0 — ок, -1 — нет файла либо он - * не той версии; во втором случае музыки не будет, но игра работает. */ -int8_t pop_music_index_load(void) __banked; - -/* Запись трека в буфер вызывающего. -1 — индекса нет, id вне диапазона - * или трека нет в наборе (тогда поля нулевые). */ -int8_t pop_music_info(uint8_t id, pop_mus_info_t *out) __banked; - -/* Сколько EMM-страниц занимает трек такой длины. Считается, а НЕ хранится - * в индексе: дублирование однажды разъедется с blocks. */ -#define pop_music_pages(blocks) \ - (uint16_t)(((blocks) >> 7) + (((blocks) & 127) != 0)) - -/* Номера треков — нумерация оригинала (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; - -/* «Трек в работе»: заявка/загрузка/звучание. Для мест, где ждут КОНЦА - * мелодии, — busy там ложен в окне запуска (см. pop_music.c). */ -uint8_t pop_music_active(void) __banked; - -#endif /* POP_MUSIC_H */ diff --git a/applications/SprPoP/src/pop_pace.c b/applications/SprPoP/src/pop_pace.c deleted file mode 100644 index 3fd06ea..0000000 --- a/applications/SprPoP/src/pop_pace.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * pop_pace.c — фиксированный логический кадр по лучу. Зачем именно так, - * а не по счётчику кадровых прерываний — в шапке pop_pace.h (там же - * условие точности и замеры). - * - * Модуль НЕ банковый (EXTRA_SRCS): pop_beam_sample зовётся из банков - * рисования, а базовый код в W1 доступен им прямым call — трамплин на - * каждую выборку съел бы весь смысл. - */ - -#include -#include -#include -#include "pop_state.h" /* pop_idle — общий idle-хук */ -#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(); - /* ТОТ ЖЕ ХУК, что висит на gfx_wait_vsync, а не голый kbd_raw_poll: - * кроме плотного опроса FIFO (KBD-1) он проверяет F10, и без этого - * выход из игры в геймплее не работал — пейсинг по лучу через - * gfx_wait_vsync не ходит. */ - pop_idle(); - } 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 deleted file mode 100644 index 2daa201..0000000 --- a/applications/SprPoP/src/pop_pace.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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); -/* ЭТАЛОН ХОДА ЧАСОВ — делитель РЕЖИМА NORMAL для того же признака боя. - * Игровое время меряется им, а не фактическим темпом: иначе FAST/FASTEST - * ускоряли бы и часы (минута проходила за две трети минуты). При NORMAL - * фактический делитель равен эталону, поэтому его ход не меняется вовсе — - * включая замедление в бою, которое есть и в оригинале. */ -#define POP_PACE_BASE(fight) ((uint8_t)((fight) ? 5 : 4)) - -/* Счётчик кадров. 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 deleted file mode 100644 index 8f7ec8d..0000000 --- a/applications/SprPoP/src/pop_pal.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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 */ -#include "_pop_file.h" - -static void pal_file_load(const char *path) -{ - int rc; - POP_PATH_CALL(rc, gfx_pal_fload(0, path)); - 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"); - /* 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"); -} - -/* ЕДИНСТВЕННОЕ ОТЛИЧИЕ ФИНАЛА ОТ ИНТРО — цвет фона текстовой рамки. - * 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"); - 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"); -} diff --git a/applications/SprPoP/src/pop_pal.h b/applications/SprPoP/src/pop_pal.h deleted file mode 100644 index 2ef3968..0000000 --- a/applications/SprPoP/src/pop_pal.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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_path_bank.c b/applications/SprPoP/src/pop_path_bank.c deleted file mode 100644 index b49fc19..0000000 --- a/applications/SprPoP/src/pop_path_bank.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * pop_path_bank.c — каталог приложения и восстановление cwd, BANK10. - * - * APPINFO B=1 возвращает абсолютный каталог запущенного EXE вместе с диском. - * Получаем его один раз при старте. Буфер объявлен const намеренно: у - * Sprinter банковые страницы — RAM, а --constseg кладёт объект в BANK10, - * не расходуя дефицитную резидентную _DATA в W2. Записывает его только - * ESTEX во время pop_path_init(). - * - * ABI: публичные функции __banked; их вызывает POP_PATH_CALL из банка - * файлового клиента. После возврата трамплин восстанавливает прежнюю W3, - * поэтому переданный самой файловой операции указатель снова доступен. - */ -#include -#include - -#include "_pop_file.h" - -static const char pop_app_dir[256] = { 0 }; - -/* ESTEX APPINFO ($47), B=1: HL=буфер, CF/A=ошибка. */ -static int appinfo_dir(const char *buf) __naked -{ - (void)buf; - __asm - push ix - ld b, #1 - ld c, #0x47 - rst #0x10 - pop ix - jr c, _paid_err - ld de, #0 - ret - _paid_err: - call __errno_set - ld de, #-1 - ret - __endasm; -} - -int8_t pop_path_init(void) __banked -{ - const char *dir = pop_app_dir; - - if (appinfo_dir(dir) != 0 || !dir[0]) return -1; - return (int8_t)chdir(dir); -} - -int8_t pop_chdir_home(void) __banked -{ - return (int8_t)chdir((const char *)pop_app_dir); -} diff --git a/applications/SprPoP/src/pop_qsave.c b/applications/SprPoP/src/pop_qsave.c deleted file mode 100644 index 72ef0e9..0000000 --- a/applications/SprPoP/src/pop_qsave.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * 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" -#include "_pop_file.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; - int rc; - 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; - POP_PATH_CALL(rc, unlink("POP.NEW")); - POP_PATH_CALL(rc, bank_save_file(page,QS_BASE,"POP.NEW",size)); - if (rc != size) { - POP_PATH_CALL(rc, unlink("POP.NEW")); - return -1; - } - POP_PATH_CALL(rc, unlink("POP.BAK")); - POP_PATH_CALL(rc, rename("POP.SAV", "POP.BAK")); - POP_PATH_CALL(rc, rename("POP.NEW", "POP.SAV")); - if (rc != 0) { - POP_PATH_CALL(rc, 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; - POP_PATH_CALL(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); - - POP_PATH_CALL(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; - POP_PATH_CALL(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); - /* Звук не глушим (2026-08-28). Единственное место из снятых, где по - * диску реально едут 16 КБ (~33 мс, три периода насоса): если на F6/F9 - * появится скрежет — вернуть сюда pop_sfx_pause/pop_sfx_start, это - * лечится точечно и только здесь. */ - 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); - } - 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 deleted file mode 100644 index 564c296..0000000 --- a/applications/SprPoP/src/pop_qsave.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 deleted file mode 100644 index 511db08..0000000 --- a/applications/SprPoP/src/pop_qsave_coldio.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Холодная часть ввода-вывода 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 deleted file mode 100644 index cc4fc28..0000000 --- a/applications/SprPoP/src/pop_qsave_io.c +++ /dev/null @@ -1,34 +0,0 @@ -/* Резидентная часть 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 deleted file mode 100644 index d8e3018..0000000 --- a/applications/SprPoP/src/pop_redraw.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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 deleted file mode 100644 index 7ab303f..0000000 --- a/applications/SprPoP/src/pop_redraw.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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 deleted file mode 100644 index b9a14d6..0000000 --- a/applications/SprPoP/src/pop_room.c +++ /dev/null @@ -1,2011 +0,0 @@ -/* - * 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 "_pop_file.h" -#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; - POP_PATH_CALL(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 deleted file mode 100644 index 727f920..0000000 --- a/applications/SprPoP/src/pop_settings.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 deleted file mode 100644 index d2a4449..0000000 --- a/applications/SprPoP/src/pop_sfx.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * 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_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; - } - /* НАБОР ЕЩЁ ДОЧИТЫВАЕТСЯ. На старте программы звук открывается после - * ОДНОЙ страницы (в ней блок тишины), чтобы музыка заставки заиграла - * почти сразу; остальные приезжают позже (pop_sfx_init_begin/finish). - * Страницы под них уже выделены, но не прочитаны — эффект из такой - * страницы был бы шумом из чужих данных. Заявку на МУЗЫКУ выше это - * не трогает: её данные лежат отдельно. */ - if (!pop_snd_pages) 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 deleted file mode 100644 index aba1e82..0000000 --- a/applications/SprPoP/src/pop_sfx.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 - -/* ПОДЪЁМ НАБОРА РАЗБИТ НАДВОЕ — ради быстрого старта программы. - * - * Насосу, чтобы открыть CBL и заиграть МУЗЫКУ, нужен из всего набора - * ровно один блок — тишина, и упаковщик обязан класть её первым блоком - * СТРАНИЦЫ 0 (tools/pop_idx.py, проверка `_check_entry`). Поэтому: - * - * pop_sfx_init_begin() — индекс (snd.idx) + ОДНА страница (33 мс). - * После него можно звать pop_sfx_start() и - * играть музыку; эффектов ещё нет. - * pop_sfx_init_finish() — остальные страницы (~230 мс) и публикация - * pop_snd_pages: с этого момента есть эффекты. - * - * Пока набор неполон, pop_sfx_play() молча игнорирует эффекты (заявку на - * музыку принимает по-прежнему): страницы под них выделены, но ещё не - * прочитаны, и играть из них — это шум из чужих данных. - * - * ВЫВОД НИ ТА, НИ ДРУГАЯ НЕ ВКЛЮЧАЕТ — см. pop_sfx_start. 0 — OK, -1 — - * не получилось (тогда всё дальнейшее молча ничего не делает, игра идёт - * без звука; begin при отсутствии индекса поднимает набор из одной - * страницы тишины, чтобы не встала музыка). */ -int pop_sfx_init_begin(void) __banked; -int pop_sfx_init_finish(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 deleted file mode 100644 index 2c20673..0000000 --- a/applications/SprPoP/src/pop_sfx_cold.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * pop_sfx_cold.c — ХОЛОДНАЯ половина звука: поднять набор и открыть CBL. - * Отрабатывает один раз за запуск, поэтому живёт в банке 8; горячая - * половина (насос, запуск эффекта) — в резиденте, см. pop_sfx.c. - */ - -#include -#include -#include -#include -#include -#include -#include "pop_sfx.h" -#include "pop_arc.h" -#include "_pop_sfx.h" -#include "_pop_file.h" /* POP_PATH_CALL — старые DSS и пути */ -#include /* IRQ_DISABLE вокруг записи в W0 */ -#include /* gfx_w0_map — залить блок тишины */ - -static uint8_t snd_blk; /* блок EMM под весь набор */ -/* Раздельная загрузка (см. шапку pop_sfx.h): сколько страниц обещал индекс - * и сколько уже прочитано. 0 в want — begin не проходил либо поднялась - * только страница тишины (набора нет), и дочитывать нечего. */ -static uint8_t snd_want_pages; -static uint8_t snd_done_pages; - -/* --- ИНДЕКС НАБОРА (SND/snd.idx, формат PSI1) ----------------------- * - * - * Раскладка эффектов приезжает с диска, а не из EXE: так пара - * snd.idx + snd.arc заменяется целиком (SDLPoP <-> MSDOS) без - * перекомпиляции игры. Писатель формата — tools/pop_idx.py, он же - * покрыт тестом; проверки ниже повторяют его один в один, потому что - * доверять содержимому файла нельзя: битый индекс увёл бы насос за - * пределы выделенного блока EMM. - * - * Заголовок: 16 байт, дальше POP_SND_COUNT записей по 5 байт подряд — - * ровно в том виде, в каком лежит pop_snd_tbl[], поэтому читаются они - * ОДНИМ read'ом прямо туда, без разбора по полям. */ -#define IDX_HDR_SIZE 16 -#define IDX_BLOCK_LOG2 7 /* 1 << 7 == POP_SND_BLOCK */ -#define IDX_PCM_FMT 1 /* u8 mono, CBL_FREQ_10K9 */ -#define IDX_TBL_SIZE (POP_SND_COUNT * (int)sizeof(pop_snd_ent_t)) - -/* Имя знает сам модуль — литерал лежит в rodata банка 8, и open() читает - * его, пока в W3 стоит этот же банк (тот же приём, что в pop_arc.c). */ -static const char idx_path[] = "SND\\snd.idx"; - -/* Одна запись: ровно те проверки, что делает упаковщик. - * - * СЧИТАЕМ В БЛОКАХ, А НЕ В БАЙТАХ. Байтовый адрес конца эффекта - * (page * 16384 + off + len) не влезает в uint16, а 32-битная арифметика - * на Z80 дорога (memory avoid_32bit_arith_z80). В блоках по 128 весь - * набор — не больше POP_SND_MAX_PAGES * 128 = 2048, и всё считается - * шестнадцатью битами. */ -static int8_t idx_entry_ok(const pop_snd_ent_t *e, uint8_t pages) -{ - uint16_t start, end; - - if (e->len == 0) /* нет оцифровки — это музыка */ - return (e->page == 0 && e->off == 0) ? 0 : -1; - if (e->page >= pages) return -1; - if (e->off >= 0x4000) return -1; - if ((e->off | e->len) & (POP_SND_BLOCK - 1)) return -1; - /* Блок тишины неприкосновенен: насос льёт его, когда играть нечего. */ - if (e->page == POP_SND_SILENCE_PAGE && e->off < POP_SND_BLOCK) return -1; - start = (uint16_t)((uint16_t)e->page << 7) + (uint16_t)(e->off >> 7); - end = (uint16_t)(start + (uint16_t)(e->len >> 7)); - /* Длинный эффект переливается на следующие страницы — они обязаны - * быть загружены, иначе насос уйдёт в чужую память. */ - if (end > (uint16_t)((uint16_t)pages << 7)) return -1; - return 0; -} - -/* Прочитать и проверить индекс. Возвращает число страниц набора либо 0. */ -static uint8_t idx_load(void) -{ - uint8_t head[IDX_HDR_SIZE]; - uint8_t pages, i; - int fd; - - POP_PATH_CALL(fd, open(idx_path, O_RDONLY)); - if (fd < 0) return 0; - if (read(fd, head, IDX_HDR_SIZE) != IDX_HDR_SIZE || - memcmp(head, "PSI1", 4) != 0 || - head[4] != POP_SND_COUNT || - head[5] != (uint8_t)sizeof(pop_snd_ent_t) || - head[6] != IDX_BLOCK_LOG2 || - head[7] != IDX_PCM_FMT) { - close(fd); - return 0; - } - pages = head[8]; - if (pages == 0 || pages > POP_SND_MAX_PAGES) { close(fd); return 0; } - /* Записи ложатся прямо в резидентную таблицу: дисковый формат и - * структура совпадают побайтово (проверено статически в - * pop_snd_data.c и полем head[5] выше). */ - if (read(fd, pop_snd_tbl, IDX_TBL_SIZE) != IDX_TBL_SIZE) { - close(fd); - return 0; - } - close(fd); - for (i = 0; i < POP_SND_COUNT; i++) - if (idx_entry_ok(&pop_snd_tbl[i], pages) != 0) return 0; - return pages; -} - -/* НАБОРА НЕТ — НО НАСОС ВСЁ РАВНО ОБЯЗАН РАБОТАТЬ. - * - * Тишину в паузах насос льёт из ПЕРВОГО БЛОКА НАБОРА, поэтому без набора - * CBL не открывался вовсе. А вместе с ним вставала и МУЗЫКА: её блоки - * отсчитывает тот же насос (pop_mus_left убывает только в pop_sfx_fill). - * Заставка при этом висла намертво — ждала конца трека, который не - * начинал играть, и pop_music_busy() не гас никогда (поймано - * пользователем 2026-08-31 на образе без snd.idx). - * - * Поэтому при отказе индекса поднимаем ПУСТОЙ набор: одна страница EMM с - * блоком тишины. Эффектов нет — все len в таблице нулевые, и - * pop_sfx_play уходит по ветке «нет оцифровки», — а музыка играет. - * - * ЗАЛИВАЕМ ТОЛЬКО ПЕРВЫЙ БЛОК, и это не экономия: gfx_w0_page_prepare - * ставит в страницу IRQ/NMI-стабы, чтобы прерывание при замапленном W0 - * нашло валидный код. Заливка ВСЕЙ страницы затирает их, и первое же - * прерывание уводит процессор в мусор — чёрный экран на старте (поймано - * при первой попытке этого фикса). Насосу больше 128 байт и не нужно: - * он берёт ровно блок тишины, да и то через W3, а не через W0. - * Запись — под DI: 128 байт накрывают стаб RST38, и до unmap прерываний - * быть не должно. */ -static int8_t snd_silence_only(void) -{ - uint8_t blk, page; - uint8_t *w0 = (uint8_t *)0x0000; /* окно 0 — сюда ляжет страница */ - - memset(pop_snd_tbl, 0, sizeof(pop_snd_tbl)); - blk = mem_alloc_pages(1); - if (!blk) return -1; - page = mem_get_page(blk, 0); - if (!page) { mem_free_block(blk); return -1; } - gfx_w0_page_prepare(page); - IRQ_DISABLE(); - gfx_w0_map(page); - memset(w0, 0x80, POP_SND_BLOCK); /* 0x80 — тишина в 8-бит без знака */ - gfx_w0_unmap(); - IRQ_ENABLE(); - pop_snd_page[POP_SND_SILENCE_PAGE] = page; - snd_blk = blk; - pop_snd_pages = 1; - return 0; -} - -/* ПЕРВАЯ ПОЛОВИНА: индекс + страница 0 (в ней блок тишины). После неё - * звук можно открывать, и музыка играет; эффекты — после finish. */ -int pop_sfx_init_begin(void) __banked -{ - uint8_t blk, pages; - - if (snd_blk) return 0; /* набор уже поднимали */ - - /* СНАЧАЛА ИНДЕКС, ПОТОМ ДАННЫЕ: сколько страниц читать и куда какой - * эффект лёг — знает только он. Набора без индекса не бывает: - * держать в EXE запасную таблицу нельзя, иначе привязка бинарника к - * конкретному набору вернётся через чёрный ход. */ - pages = idx_load(); - if (!pages) { - /* Набор эффектов не поднялся — но звук как таковой поднять надо, - * иначе встанет музыка (см. snd_silence_only). Возвращаем -1: - * эффектов действительно нет, и вызывающий вправе об этом знать. */ - if (snd_silence_only() != 0) return -1; - pop_snd_want = 1; - return -1; - } - - /* Одним блоком: страницы блока идут подряд по логическому индексу, и - * таблица звуков адресует их именно так (page = смещение >> 14). - * - * Из АРХИВА, а не десятью файлами: `open` стоит 51,4 мс против 32,6 мс - * на чтение 16 КБ (замер 2026-08-25), то есть девять лишних открытий — - * это почти полсекунды на ровном месте. Таблица живёт на стеке, см. - * pop_arc.h. */ - { - pop_arc_t arc; - uint8_t tbl[POP_SND_MAX_PAGES * POP_ARC_REC]; - - /* Архив обязан содержать РОВНО столько страниц, сколько обещал - * индекс: расходящейся паре доверять нельзя — она означает, что - * snd.idx и snd.arc из разных наборов. */ - if (pop_arc_open_id(&arc, POP_ARC_SND, tbl, POP_SND_MAX_PAGES) != - (int8_t)pages) - return -1; - /* Блок выделяем СРАЗУ на весь набор: номера страниц отдаёт - * mem_get_page по индексу, и finish дочитает в те же самые. */ - blk = mem_alloc_pages(pages); - if (!blk) { pop_arc_close(&arc); return -1; } - pop_snd_page[POP_SND_SILENCE_PAGE] = mem_get_page(blk, 0); - if (!pop_snd_page[POP_SND_SILENCE_PAGE] || - pop_arc_read(&arc, tbl, 0, 0, - pop_snd_page[POP_SND_SILENCE_PAGE], 0, 0) != 16384) { - pop_arc_close(&arc); - mem_free_block(blk); - return -1; - } - pop_arc_close(&arc); - } - snd_blk = blk; - snd_want_pages = pages; - snd_done_pages = 1; - /* pop_snd_pages НЕ ставим: набор ещё неполон, и до finish эффекты - * запускать нельзя (страницы выделены, но не прочитаны). */ - pop_snd_want = 1; /* набор есть — звук по умолчанию включён */ - return 0; -} - -/* ВТОРАЯ ПОЛОВИНА: дочитать остальные страницы и разрешить эффекты. - * Идемпотентна; архив открывается второй раз (51 мс) — это дешевле, чем - * держать открытый манипулятор через всю заставку при лимите в восемь. */ -int pop_sfx_init_finish(void) __banked -{ - pop_arc_t arc; - uint8_t tbl[POP_SND_MAX_PAGES * POP_ARC_REC]; - uint8_t i; - - if (pop_snd_pages) return 0; /* набор уже полный */ - if (!snd_want_pages) return -1; /* begin не проходил либо только тишина */ - if (snd_done_pages < snd_want_pages) { - if (pop_arc_open_id(&arc, POP_ARC_SND, tbl, POP_SND_MAX_PAGES) != - (int8_t)snd_want_pages) - return -1; - for (i = snd_done_pages; i < snd_want_pages; i++) { - pop_snd_page[i] = mem_get_page(snd_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); - return -1; /* страницы не отдаём: тишина в стр.0 жива */ - } - snd_done_pages = (uint8_t)(i + 1); - } - pop_arc_close(&arc); - } - /* Число страниц ставим ПОСЛЕДНИМ: до этого момента насос обязан - * видеть 0 и не пытаться ходить по полузаполненной таблице. */ - pop_snd_pages = snd_want_pages; - 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 deleted file mode 100644 index 721dcb2..0000000 --- a/applications/SprPoP/src/pop_shadow.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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 deleted file mode 100644 index 7463e2f..0000000 --- a/applications/SprPoP/src/pop_shadow.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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_snd_data.c b/applications/SprPoP/src/pop_snd_data.c deleted file mode 100644 index cb8ac7e..0000000 --- a/applications/SprPoP/src/pop_snd_data.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * pop_snd_data.c — РЕЗИДЕНТНЫЕ ДАННЫЕ звукового набора. - * - * Отдельным модулем, потому что стороны у этих данных разные: заполняет - * их ХОЛОДНЫЙ загрузчик из банка 8 (pop_sfx_cold.c), а читает ГОРЯЧИЙ - * насос из резидента (pop_sfx.c) — в том числе из прерывания. Держать - * их в любой из половин значило бы тянуть чужую в резидент. - * - * Инициализаторов нет намеренно: crt0 зануляет _DATA (memory - * sdcc_static_storage_gotcha), а «набор не загружен» — это и есть нули. - * - * ЦЕНА ПЕРЕЕЗДА. Таблица была `static const` в _CODE (285 Б); сняв - * const, мы переносим её в _DATA — суммарный резидентный расход не - * меняется, потому что в huge-модели _CODE и данные лежат подряд в одном - * диапазоне 0x4000..0xBFFF. Реально добавилось 8 байт: pop_snd_page - * вырос с размера набора до POP_SND_MAX_PAGES (запас под чужие наборы) - * плюс байт самого счётчика. - */ -#include -#include "pop_snd_tbl.h" - -/* РАСКЛАДКА НАБОРА — приезжает из SND/snd.idx целиком, одним read'ом - * прямо сюда. Поэтому размер записи обязан совпадать с дисковым: если - * ABI когда-нибудь добавит выравнивание, сборка упадёт здесь, а не - * рассыплется звуком в рантайме. */ -pop_snd_ent_t pop_snd_tbl[POP_SND_COUNT]; -typedef char pop_snd_ent_is_5_bytes[(sizeof(pop_snd_ent_t) == 5) ? 1 : -1]; - -/* Физические страницы EMM, по индексу из pop_snd_tbl[].page. */ -uint8_t pop_snd_page[POP_SND_MAX_PAGES]; - -/* Сколько страниц у ЭТОГО набора (1..POP_SND_MAX_PAGES). Насос сверяет - * с ним переход длинного эффекта через границу страницы. */ -uint8_t pop_snd_pages; diff --git a/applications/SprPoP/src/pop_snd_tbl.h b/applications/SprPoP/src/pop_snd_tbl.h deleted file mode 100644 index 237d20b..0000000 --- a/applications/SprPoP/src/pop_snd_tbl.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * pop_snd_tbl.h — РАСКЛАДКА ОЦИФРОВАННЫХ ЭФФЕКТОВ: тип и инварианты. - * - * Раньше этот файл ПЕЧАТАЛ упаковщик (gen/pop_sound_tbl.h), и конкретные - * page/off/len становились частью EXE — из-за чего замена звукового набора - * (SDLPoP <-> MSDOS) требовала перекомпиляции игры. Теперь значения - * приезжают с диска (SND/snd.idx, формат PSI1 — tools/pop_idx.py), а - * здесь остаётся только то, что от набора НЕ зависит: устройство записи и - * пределы, на которые рассчитан загрузчик. - * - * ЗАПИСЬ ЧИТАЕТСЯ С ДИСКА ПОБАЙТОВО, прямо в pop_snd_tbl[]. Значит - * раскладка структуры в ABI SDCC z80 (uint8 + uint16 + uint16 подряд, без - * выравнивания = 5 байт) — часть дискового контракта, а не деталь - * компилятора. Расхождение молча разъехало бы весь набор, поэтому оно - * ловится статически (pop_snd_data.c) и проверяется загрузчиком по полю - * размера записи в заголовке индекса. - */ -#ifndef POP_SND_TBL_H -#define POP_SND_TBL_H - -#include - -/* Пространство id оригинала: 0..56. От набора не зависит. */ -#define POP_SND_COUNT 57 -/* Блок запроса CBL при 8 бит; страница EMM кратна ему, поэтому ни один - * блок не пересекает границу страницы (см. docs/sound_plan.md). */ -#define POP_SND_BLOCK 128 -/* СКОЛЬКО СТРАНИЦ ГОТОВ ПРИНЯТЬ ЗАГРУЗЧИК — вместимость, а НЕ размер - * конкретного набора: тот приходит в индексе (pop_snd_pages). Запас - * ради будущих наборов без перекомпиляции; цена — 16 байт таблицы - * страниц вместо 9-10. */ -#define POP_SND_MAX_PAGES 16 - -/* ПЕРВЫЙ БЛОК СТРАНИЦЫ 0 — ТИШИНА. Насос льёт её, когда играть нечего: - * без этого железо доигрывает по кругу хвост своего буфера (жужжание). - * Это инвариант ПРОГРАММЫ — упаковщик обязан оставить блок свободным, а - * загрузчик проверяет, что ни одна запись в него не залезла. */ -#define POP_SND_SILENCE_PAGE 0 -#define POP_SND_SILENCE_OFF 0x0000 - -/* page — индекс в pop_snd_page[] (физические страницы EMM), off — начало - * внутри страницы, len — длина PCM. Оба кратны POP_SND_BLOCK; len == 0 - * значит «у этого id нет оцифровки» (это музыка, см. sound_plan.md). */ -typedef struct { - uint8_t page; - uint16_t off; - uint16_t len; -} pop_snd_ent_t; - -#endif /* POP_SND_TBL_H */ diff --git a/applications/SprPoP/src/pop_state.c b/applications/SprPoP/src/pop_state.c deleted file mode 100644 index 096b2d3..0000000 --- a/applications/SprPoP/src/pop_state.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * 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_* (живёт здесь) */ - -/* F10 — НЕМЕДЛЕННЫЙ ВЫХОД, откуда угодно. Флаг взводит kbd_idle (sprpop.c), - * то есть он ловится везде, где программа ждёт кадр: в заставке, в титрах, - * в fade, в проявлении полосами, в меню и в игре. Держим его резидентным, - * чтобы взводить и читать без трамплина из любого банка. */ -uint8_t pop_quit_req; - -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 deleted file mode 100644 index 4ef6d3f..0000000 --- a/applications/SprPoP/src/pop_state.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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), а писучие статики банкового модуля наружу не видны. Ставит - * отрыв куска, снимает сам обход по факту пустой таблицы. */ -/* F10 нажата — выйти из программы; см. pop_state.c. */ -extern uint8_t pop_quit_req; - -/* Общий idle-хук: вычерпать клавиатурный FIFO и проверить F10. Живёт в - * sprpop.c (резидент), висит на gfx_wait_vsync и вызывается из цикла - * ожидания луча — обе точки ожидания обязаны звать ОДНО И ТО ЖЕ, иначе - * выход работает не везде. */ -void pop_idle(void); -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 deleted file mode 100644 index 55cd777..0000000 --- a/applications/SprPoP/src/pop_status.c +++ /dev/null @@ -1,619 +0,0 @@ -/* - * 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_music.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; /* заявка (пишут откуда угодно) */ -/* Постоянный показ таймера (клавиша T, keys.txt). Отдельно от - * pop_settings.show_debug_bar: полоса — это отладка, а таймер игрок может - * захотеть держать перед глазами. В POP.CFG не пишется намеренно — это - * переключатель «на посмотреть», а не настройка. */ -uint8_t pop_show_timer; -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; /* показанный РЕЖИМ полосы: 0 нет / 1 таймер / 2 всё */ - -/* ---- общее ------------------------------------------------------------- */ - -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 на каждом появлении */ - -/* Кадр, с которого пошёл отсчёт надписи. НЕ равен DEAD_SETTLE: оригинал - * не показывает «Press Button», пока звучит музыка смерти — ветка мёртвого - * (seg006:1351) на седьмом шаге просто выходит, если звук ещё играет, и - * надпись появляется только после него. Поэтому момент старта отсчёта - * заранее не известен и запоминается здесь. */ -static uint16_t dead_base; -static uint8_t dead_armed; /* всё отпущено — можно принимать нажатие */ - -uint8_t pop_dead_prompt(uint16_t frames) __banked -{ - uint16_t rem; - - if (frames < DEAD_SETTLE) { dead_base = 0; return 0; } - if (dead_base == 0) { - /* Ждём, пока домолчит музыка смерти — порядок оригинала. Пока она - * играет, надписи нет и отсчёт 288 не идёт; прервать ожидание можно - * Ctrl+A или быстрой загрузкой, они музыку глушат. */ - if (pop_music_active()) return 0; - dead_base = frames; - 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_base); - 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; - dead_base = 0; - pop_music_stop(); /* игрок ответил — доигрывать не заставляем */ - 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; - /* Три состояния верхней полосы: 0 — нет, 1 — только таймер (T), 2 — - * вся отладочная строка (Ctrl+D). Отслеживаем именно РЕЖИМ, а не - * булево: переход «таймер -> полоса» тоже обязан перерисовать всё. */ - uint8_t want = (uint8_t)(pop_settings.show_debug_bar ? 2 : - (pop_show_timer ? 1 : 0)); - - 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 if (want == 1) { - /* Только таймер: подписей нет, поле рисуется на своём месте у - * правого края — dbg_value стирает свою зону сам. Полную маску - * сюда пропускать нельзя, иначе dbg_draw позовёт dbg_labels. */ - dbg_scan(room); - if (dbg_dirty[page] & DBG_F_TIME) { - if (dbg_dirty[page] == DBG_F_ALL) - wipe(DBG_LEFT, DBG_TOP, DBG_RIGHT, DBG_BOTTOM); - dbg_draw(DBG_F_TIME); - } - 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 deleted file mode 100644 index a6d1d8e..0000000 --- a/applications/SprPoP/src/pop_status.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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; -/* Постоянный показ таймера (клавиша T); независим от отладочной полосы. */ -extern uint8_t pop_show_timer; - -/* Показать сообщение НЕМЕДЛЕННО, до конца кадра: строка выкладывается в - * видимую страницу прямо сейчас. Нужна там, где сразу за вызовом идёт - * длинная операция (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 deleted file mode 100644 index da9fdab..0000000 --- a/applications/SprPoP/src/pop_tile.c +++ /dev/null @@ -1,624 +0,0 @@ -/* - * 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 deleted file mode 100644 index 8de2a70..0000000 --- a/applications/SprPoP/src/pop_tile.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * 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 deleted file mode 100644 index f4daa3f..0000000 --- a/applications/SprPoP/src/pop_tile_cold.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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 deleted file mode 100644 index 1890fea..0000000 --- a/applications/SprPoP/src/pop_timer.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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; - -/* Накопленные кадры луча, ещё не сложившиеся в тик. Живёт между кадрами: - * при FAST логический кадр короче эталона, и остаток переносится вперёд. */ -static uint8_t tick_acc; - -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; - tick_acc = 0; -} - -uint8_t pop_timer_tick(uint8_t enabled, uint8_t may_run, - uint8_t spent, uint8_t base) __banked -{ - if (!enabled || !may_run || pop_timer_minutes == 0) return 0; - /* ХОД ЧАСОВ ОТВЯЗАН ОТ ТЕМПА. Тик стоит `base` кадров луча — столько, - * сколько их в кадре режима NORMAL. При NORMAL spent == base, и тик - * приходится ровно на кадр, как было всегда; в быстрых режимах кадр - * короче, остаток копится, и за то же РЕАЛЬНОЕ время выходит столько - * же тиков. Цикла не нужно: spent никогда не больше base (быстрые - * режимы кадр только УКОРАЧИВАЮТ) — значит не больше тика за вызов. */ - /* Пауза, меню и загрузка сюда не заходят вовсе, поэтому за время их - * работы кадры луча накапливаются мимо нас. Ограничиваем вклад одного - * вызова: иначе после меню часы прыгнули бы вперёд на всю паузу. */ - if (spent > (uint8_t)(base + base)) spent = base; - tick_acc = (uint8_t)(tick_acc + spent); - if (tick_acc < base) return 0; - tick_acc = (uint8_t)(tick_acc - base); - - --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 deleted file mode 100644 index 72ce854..0000000 --- a/applications/SprPoP/src/pop_timer.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 и при выключенном лимите - * вызывать можно — состояние останется неизменным. */ -/* spent — сколько кадров ЛУЧА стоил этот логический кадр (pop_pace_n), - * base — сколько их было бы при NORMAL (POP_PACE_BASE). Тик отсчитывается - * по base, поэтому режим скорости на ход часов не влияет. */ -uint8_t pop_timer_tick(uint8_t enabled, uint8_t may_run, - uint8_t spent, uint8_t base) __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 deleted file mode 100644 index 7b1664c..0000000 --- a/applications/SprPoP/src/pop_title.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * 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" -#include "sprpop_cold.h" /* pop_boot_sound/pop_boot_rest — фазы старта */ - -#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; - return pop_screen_present_ltr() ? 1 : 0; -} - -/* Тот же приём для композиции с логотипом и подписью Jordan Mechner: её - * show_title() показывает МЕЖДУ «свадьбой» и титрами (seg000:2045). */ -int pop_title_logo_ltr(void) __banked -{ - if (title_draw(TITLE_LOGO, 0) < 0) return -1; - return pop_screen_present_ltr() ? 1 : 0; -} - -/* Ждать ticks кадров луча, доливая трек. Тема заставки играет с ПЕРВОЙ - * страницы (pop_music_stream), остальные обязаны приезжать по ходу — шаг - * стоит 16 мс и укладывается в кадровый интервал. */ -static uint8_t title_wait(uint16_t ticks) -{ - while (ticks--) { - if (title_skip_pressed()) return 1; - if (pop_music_loading()) (void)pop_music_load_step(); - 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; - uint16_t first_wait = 0; /* такт 0 за вычетом времени загрузки */ - int prepared; - uint8_t cold_start = (uint8_t)(!title_shown && - !pop_settings.show_build_info); - - /* Игровые SFX не должны тянуться сюда из прерванной партии: заставка - * начинается с тишины, а через несколько строк её займёт тема титров. */ - pop_sfx_pause(); - /* Только мгновенность перехода: сам титул ещё не показан, - * и прерывать тут нечего. */ - if (!cold_start) (void)pop_pal_fade_out_skip(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; - - /* ЗДЕСЬ ЖЕ ДОГРУЖАЕТСЯ ВСЯ ИГРА — и это не случайное место. - * - * До 2026-09-02 pop_boot читал ВСЕ ресурсы перед первым пикселем: от - * запуска до проявления титула проходило несколько секунд чёрного - * экрана в тишине, потому что перед fade_in ещё и вычитывался целиком - * трек заставки (248 КБ). Теперь порядок обратный и совпадает с - * оригиналом (init_game_main грузит до заставки только мелочь, - * seg000.c:156): - * 1) картинка титула и fade_in — первое, что происходит; - * 2) звук: индекс + ОДНА страница набора, CBL (pop_boot_sound); - * 3) тема — с первой же страницы (pop_music_stream), не дожидаясь - * всех шестнадцати; - * 4) остальные ресурсы — под стоящим титулом и звучащей темой. - * - * Порядок «fade_in, потом тема» сохранён как у show_title (fade_in_2, - * затем play_sound(sound_54)). */ - for (step = 0; step < sizeof(title_scene); step++) { - if (step && title_draw(title_scene[step], 1) < 0) - break; - if (step == 0) { - uint8_t t0; - - if (pop_pal_fade_in_skip(TITLE_FADE)) { skipped = 1; break; } - (void)pop_boot_sound(); - (void)pop_music_stream(POP_MUS_INTRO); - /* ЧЕМ ЗАПЛАЧЕН ПЕРВЫЙ ТАКТ. Загрузка занимает около секунды, и - * без поправки титул простоял бы на первом кадре дольше - * оригинала, а сцена уехала бы относительно темы. Считаем - * потраченное часами насоса (85,4 Гц, идут от расхода буфера - * CBL — единственные, кому не мешает диск) и переводим в кадры - * луча сдвигами: 48,83/85,4 = 0,572 ~ 1/2 + 1/16. Без звука - * часы стоят, поправка выходит нулевой — и правильно, тогда и - * синхронизировать не с чем. */ - t0 = pop_snd_tick; - (void)pop_boot_rest(); - { - uint16_t spent = (uint8_t)(pop_snd_tick - t0); - - spent = (uint16_t)((spent >> 1) + (spent >> 4)); - first_wait = (spent >= title_wait_ticks[0]) - ? 0 : (uint16_t)(title_wait_ticks[0] - spent); - } - } - if (title_wait(step ? title_wait_ticks[step] : first_wait)) { - 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 deleted file mode 100644 index 0f15dd9..0000000 --- a/applications/SprPoP/src/pop_title.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 deleted file mode 100644 index 948a2d2..0000000 --- a/applications/SprPoP/src/pop_trob.c +++ /dev/null @@ -1,929 +0,0 @@ -/* - * 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 deleted file mode 100644 index 3be05ce..0000000 --- a/applications/SprPoP/src/pop_trob.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 deleted file mode 100644 index f2e3730..0000000 --- a/applications/SprPoP/src/pop_tune.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * 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 deleted file mode 100644 index bba7902..0000000 --- a/applications/SprPoP/src/pop_ui.c +++ /dev/null @@ -1,503 +0,0 @@ -/* - * 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" -#include "pop_music.h" /* pop_music_service — подкачка кольца в долгих фейдах */ -#include "_pop_file.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 -{ - int rc; - if (font_ready) return 0; - POP_PATH_CALL(rc, atlas_load(&font_at, "FONT\\font.atl")); - if (rc != 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) __banked -{ - gfx_w0_map(font_at.page); -} - -void pop_text_unmap(void) __banked -{ - gfx_w0_unmap(); -} - -uint8_t pop_text_width_mapped(pop_text_font_t font, const char *s) __banked -{ - 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) __banked -{ - 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) __banked -{ - 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) __banked -{ - 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 записи за вызов; одним буфером покрываем - * все четыре четверти палитры, не занимая постоянную память. - * sizeof(black) здесь нельзя: black — 16-битный указатель, и тогда - * обнулятся лишь первые два байта, а цвет заливки останется случайным. */ - for (i = 0; i < sizeof(pop_pal_buf); 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; - } - /* Fade — это 2,13 с, и всё это время главный цикл стоит. Кольцо - * потокового трека дочитывает pop_music_service, иначе музыка - * замолкает прямо посреди перехода (та же причина, что у меню: - * ui_wait_frame в pop_menu.c). */ - pop_music_service(); - 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. Поэтому у вызывающего после перехода на - * ЭКРАНЕ и в скрытой странице лежит одно и то же. */ -/* Возвращает 1, если проявление прервано НОВЫМ нажатием. - * - * Раньше функция была void и нажатие ГЛОТАЛА: полосы схлопывались, картинка - * появлялась целиком — и на этом всё, вызывающий о нажатии не узнавал. На - * заставке это выглядело как «клавиша срабатывает наполовину»: проявление - * прервалось, а в игру не ушли. - * - * Кромка считается от входа, как в сценах и в fade: клавиша, которой - * закончили предыдущий экран, ещё зажата, и принимать её за новое нажатие - * нельзя — иначе весь маршрут заставки схлопнулся бы сам собой. */ -uint8_t pop_screen_present_ltr(void) __banked -{ - gfx_rect_t stripe; - uint8_t visible = gfx_get_visible_page(); - uint8_t armed; - int x; - - kbd_raw_sync(); - armed = (uint8_t)!kbd_raw_any_down(); - 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) { - kbd_raw_sync(); - if (!kbd_raw_any_down()) armed = 1; - else if (armed) { - /* Прервали — дорисовать остаток разом, а не бросить полукадр. */ - stripe.x = 0; stripe.w = GFX_WIDTH; - gfx_copy_page(&stripe, GFX_COPY_DIRECT); - return 1; - } - stripe.x = x; - stripe.w = (uint16_t)((GFX_WIDTH - x < 4) ? GFX_WIDTH - x : 4); - gfx_copy_page(&stripe, GFX_COPY_DIRECT); - pop_music_service(); /* 80 кадров перехода — см. fade_run */ - gfx_wait_vsync(); - } - return 0; -} - -/* Нарисовать одну полосу и отпустить её страницу. Общая часть обоих - * загрузчиков ниже: они отличаются только тем, откуда взялся атлас. */ -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; - int rc; - - POP_PATH_CALL(rc, atlas_load(&at, parts[part])); - if (rc != 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 deleted file mode 100644 index b82f082..0000000 --- a/applications/SprPoP/src/pop_ui.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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 до рисования. - * - * ВСЯ ГРУППА __banked, И ЭТО ОБЯЗАТЕЛЬНО (крэш финала, 2026-08-27). Живёт - * она в pop_ui.c, то есть в банке 9, а зовут её и из ДРУГИХ банков — HOF - * лежит в банке 10. Трамплин SDCC выбирает по объявлению, а не по тому, - * где функция реально лежит (memory sdcc_banked_call_rules): без пометки - * получается прямой `call` по адресу банка 9, который в чужом банке - * попадает в пустой хвост (0xFF) — процессор ползёт по нему до 0x0000, и - * DSS убивает процесс с кодом 0x27. Собиралось это МОЛЧА, поэтому рядом - * появилась toolchain/check_bank_calls.py — она такое ловит на сборке. */ -void pop_text_map(void) __banked; -void pop_text_unmap(void) __banked; -uint8_t pop_text_width_mapped(pop_text_font_t font, const char *s) __banked; -void pop_text_draw_mapped(pop_text_font_t font, int x, int baseline, - const char *s) __banked; -void pop_text_center_mapped(pop_text_font_t font, int center, int baseline, - const char *s) __banked; -void pop_text_right_mapped(pop_text_font_t font, int right, int baseline, - const char *s) __banked; - -/* Снимок обеих (идентичных по контракту) палитр хранится в свободном хвосте - * 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 оригинала). Любая зажатая клавиша достраивает переход - * мгновенно. Страницы местами не меняются: содержимое копируется в - * видимую, поэтому после возврата обе страницы одинаковы. */ -/* 1 — проявление прервано новым нажатием (остаток дорисован разом). */ -uint8_t 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 deleted file mode 100644 index df45a23..0000000 --- a/applications/SprPoP/src/pop_vflip.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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; -} - -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 deleted file mode 100644 index f031fbd..0000000 --- a/applications/SprPoP/src/pop_vflip.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 - -/* Зеркало УЖЕ загружено из архива: таблица - * забирает его себе, и дальше он освобождается вместе с остальными. */ -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 deleted file mode 100644 index a3216ab..0000000 --- a/applications/SprPoP/src/sprpop.c +++ /dev/null @@ -1,1084 +0,0 @@ -/* - * 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" /* чёрные обе страницы перед сменой палитры */ -#include "_pop_file.h" /* cwd приложения для старых DSS */ - -#include -#include - -__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 возвращает - * «было ли что вычерпано». Приводить указатель кастом не хочется. */ -/* F10 (PS/2 set 2 — 0x09): немедленный выход. Проверяем ЗДЕСЬ, а не в - * каждом цикле ожидания: idle-хук висит на gfx_wait_vsync, то есть - * вызывается везде, где программа ждёт кадр — заставка, титры, fade, - * проявление полосами, меню, игра. Одна точка вместо десятка. */ -#define KBD_KEY_F10 0x09 - -/* НЕ static: этот же хук зовёт цикл ожидания луча (pop_wait_edge), иначе - * в геймплее F10 не работал бы вовсе — пейсинг по лучу не ходит через - * gfx_wait_vsync, к которому хук подвешен (поймано 2026-08-27). */ -void pop_idle(void) -{ - kbd_raw_poll(); - if (kbd_raw_down(KBD_KEY_F10)) pop_quit_req = 1; -} - -/* 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) и ассемблер падает. */ -/* Сколько кадров ЛУЧА прошло с прошлого игрового кадра. Часы считают - * именно их, а не ожидаемый делитель темпа: логический кадр не всегда - * укладывается в свой бюджет (дорогая сцена, загрузка звука), и часы, - * считавшие «по делителю», шли рывками — несколько секунд быстро, потом - * притормаживание (наблюдение пользователя 2026-08-31). Луч же идёт - * ровно, поэтому по нему время течёт равномерно. */ -static uint8_t timer_beam_delta(void) -{ - static uint8_t prev; - uint8_t now = pop_frame_tick; - uint8_t d = (uint8_t)(now - prev); - prev = now; - return d; -} - -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; - uint8_t freeze_prev = 0; - uint8_t dbgbar_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; - - /* До первой файловой операции запоминаем абсолютный каталог EXE. - * Без этого старый DSS теряет последний компонент cwd после OPEN пути - * с подкаталогом и следующий файл ищет уровнем выше. */ - if (pop_path_init() != 0) return 1; - - /* Загрузка ресурсов, палитра, первый экран — В БАНКЕ (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(pop_idle); /* вычерпывать FIFO, пока ждём кадр */ - if (!pop_app_dispatch(POP_APP_EV_BOOT_READY)) { - pop_shutdown(); - return 1; - } - /* Экран версии при запуске БОЛЬШЕ НЕ ПОКАЗЫВАЕТСЯ (решение пользователя - * 2026-08-27): единственная его точка — пункт ABOUT в меню паузы. - * Само состояние BUILD_INFO в автомате осталось: через него проходит - * маршрут BOOT -> TITLE, и убирать его ради одной пустой фазы незачем. */ - 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; - - /* F10 в любой точке — выходим, не начиная следующий маршрут. Проверка - * стоит В НАЧАЛЕ витка: заставку и титры прерывает ЛЮБАЯ клавиша (это - * штатный «пропустить в игру»), и без этой строки F10 успел бы уронить - * нас в загрузку уровня перед тем, как программа закроется. */ - if (pop_quit_req) break; - - /* 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; - /* Счётчик кадров смерти живёт СНАРУЖИ витка, поэтому переживает возврат - * на заставку. Ответ игрока кнопкой его обнуляет, а вот выход по - * таймауту (24 с молчания -> title, pop_dead_prompt вернул 2) уходит - * мимо этого сброса. Без строки ниже счётчик оставался израсходованным - * на всю сессию, и в следующей игре ПЕРВАЯ же смерть мгновенно уводила - * в title, не показав «Press Button to Continue». Сбрасываем на входе - * в игровой маршрут — это закрывает и любую другую боковую дорогу - * (выпадение за нижнюю границу, смена уровня), а не только таймаут. */ - dead_frames = 0; - - while (!quit && !pop_quit_req && (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 = (uint8_t)(kbd_raw_down(KBD_SPEED_MODE) && - (kbd_raw_down(KBD_LCTRL) || - kbd_raw_down(KBD_RCTRL))); - 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; - } - - /* Отладочный стоп-кадр: Ctrl+F — вкл/выкл ОДНОЙ клавишей. Раньше - * занимал две голые цифры ('1' заморозить, '2' продолжить); голые - * клавиши освобождены под раскладку keys.txt. В заморозке - * пропускаем весь tick/draw/flip: картинка стоит, можно спокойно - * разбирать позу Kid и окклюзию. */ - if (!is_demo) { - uint8_t fk = (uint8_t)(kbd_raw_down(KBD_KEY_F) && - (kbd_raw_down(KBD_LCTRL) || - kbd_raw_down(KBD_RCTRL))); - - if (fk && !freeze_prev) frozen = (uint8_t)!frozen; - freeze_prev = fk; - } - /* Ctrl+D — отладочная строка (level/room/...) в верхнем борте. Тот - * же переключатель, что пунктом Settings: пишем прямо в модель - * настроек, а строку по ней перерисовывает pop_status сам. В - * POP.CFG не сохраняем — это шорткат «посмотреть», сохранение - * остаётся за экраном Settings. - * - * На Ctrl+B вешать нельзя: keys.txt держит эту комбинацию под - * «вернуться в комнату Кида» (фаза D плана keys_plan.md). */ - if (!is_demo) { - uint8_t dk = (uint8_t)(kbd_raw_down(KBD_KEY_D) && - (kbd_raw_down(KBD_LCTRL) || - kbd_raw_down(KBD_RCTRL))); - - if (dk && !dbgbar_prev) pop_settings.show_debug_bar ^= 1; - dbgbar_prev = dk; - } - /* Читы — В БАНКЕ (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 - /* Обход комнат по номеру: Ctrl++ — вперёд, Ctrl+− — назад. Голые - * +/- отданы времени, как в оригинале (keys.txt); обход — наш - * отладочный чит, поэтому уехал под Ctrl на ТЕ ЖЕ клавиши: мышечная - * память сохраняется. Edge по nav_prev: одно нажатие = одна - * комната. Сама навигация — в банке (sprpop_cold.c): она нужна - * только по клавише, а место в резиденте дефицитно. */ - if (!is_demo) { - /* Ctrl входит в САМО значение nav, а не в условие блока: иначе - * при отпускании Ctrl блок не выполнялся бы, nav_prev застревал - * ненулевым и следующее нажатие глохло на кромке. */ - uint8_t ctrl = (uint8_t)(kbd_raw_down(KBD_LCTRL) || - kbd_raw_down(KBD_RCTRL)); - uint8_t nav = !ctrl ? 0 : - (uint8_t)((kbd_raw_down(KBD_TIME_ADD_NUM) || - kbd_raw_down(KBD_TIME_ADD_MAIN)) | - ((kbd_raw_down(KBD_TIME_SUB_NUM) || - kbd_raw_down(KBD_TIME_SUB_MAIN)) << 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))), - /* ХОД ЧАСОВ — по эталону NORMAL, а не по фактическому - * темпу: иначе FAST/FASTEST ускоряли бы и время. */ - timer_beam_delta(), - POP_PACE_BASE(Kid.sword == SWORD_2_DRAWN)) && - 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) { - /* КОНЕЦ УРОВНЯ ЖДЁТ МЕЛОДИЮ (порт seg003:387, play_level_2): - * - * if (next_level == current_level || check_sound_playing()) - * draw_game_frame(); ... // экран уровня живёт - * else { stop_sounds(); return next_level; } - * - * То есть пройденный уровень остаётся на экране — с анимацией - * факелов, идущим таймером и всем прочим — ровно столько, - * сколько звучит трек конца уровня; мы же уходили на смену - * уровня сразу и обрывали мелодию на первых нотах. - * - * «Звучит» — это pop_music_active(), а не busy: между заявкой - * на трек и первой нотой проходит 190-230 мс на открытие - * файла и чтение первой полустраницы, и в этой щели busy ещё - * ложен (замер — docs/sound_plan.md §9). Раньше щель - * закрывал счётчик по длине записи, но он же тянул за собой - * зависимость EXE от набора музыки и жёсткий делитель `/4`, - * враньё которого вылезало в быстрых режимах (там кадр 3 - * луча, а не 4). Состояние «заявка/загрузка/звучание» - * закрывает щель без единой константы. - * - * ЗВУК ВЫКЛЮЧЕН — ПАУЗЫ НЕТ, и это тоже верно: pop_sfx_play - * не кладёт заявку при !pop_snd_ok, все три признака ложны, и - * уровень меняется сразу. Ровно так в оригинале, где трек - * ставится под `if (is_sound_on)` (seg006:651), а ожидание — - * `check_sound_playing()` (seg003:387). */ - if (!pop_music_active() && 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 || pop_quit_req) 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 deleted file mode 100644 index 827e915..0000000 --- a/applications/SprPoP/src/sprpop_cold.c +++ /dev/null @@ -1,1504 +0,0 @@ -/* - * 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 /* cbl_shutdown — погасить железо звука на выходе */ -#include "pop_music.h" /* pop_music_busy/free — ждём трек заставки */ -#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; - static uint8_t restart_prev; - static uint8_t intro_prev, showtime_prev, timer_prev; - uint8_t e = (uint8_t)(kbd_raw_down(KBD_MENU) || kbd_raw_down(KBD_MENU_ALT)); - uint8_t ctrl = (uint8_t)(kbd_raw_down(KBD_LCTRL) || kbd_raw_down(KBD_RCTRL)); - 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_music_stop(); pop_qsave_request_load(); } - else if (mrc == POP_MENU_QUICKLOAD_BACKUP) - { pop_music_stop(); pop_qsave_request_load_backup(); } - else if (mrc == POP_MENU_RESTART_LEVEL) { pop_music_stop(); *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; - } - /* Ctrl+Q — выход (keys.txt). Вторая клавиша выхода, F10, ловится в - * kbd_idle (sprpop.c) и работает везде, включая заставку. */ - if (ctrl && kbd_raw_down(KBD_QUIT)) - return (int8_t)(pop_app_dispatch(POP_APP_EV_QUIT) ? 2 : -1); - /* Ctrl+A — рестарт уровня: тот же путь, что и пунктом меню. */ - /* БЫСТРЫЙ ВЫХОД ИЗ СМЕРТИ. Надпись «Press Button» у оригинала ждёт, - * пока домолчит музыка смерти (seg006:1351), — так же и у нас. Чтобы - * ожидание не было принудительным, три быстрых пути (Ctrl+A, обе - * быстрые загрузки и пункты меню) музыку ГЛУШАТ: оригинал при Ctrl+A - * делает то же самое (seg000:0617, stop_sounds). */ - { - uint8_t r = (uint8_t)(ctrl && kbd_raw_down(KBD_RESTART)); - - if (r && !restart_prev) { pop_music_stop(); *restart_level = 1; } - restart_prev = r; - } - /* Ctrl+R — вернуться в заставку. Тот же переход, что «Restart Game» - * пунктом меню: автомат сам разложит title/intro с первого экрана. */ - { - uint8_t ri = (uint8_t)(ctrl && kbd_raw_down(KBD_INTRO)); - uint8_t was = intro_prev; - - intro_prev = ri; - if (ri && !was) - return (int8_t)(pop_app_dispatch(POP_APP_EV_RESTART_INTRO) ? 1 : -1); - } - /* Space — «сколько осталось» (seg000:612). Печатать самим не надо: - * ставим тот же флаг, которым пользуется автоматическое объявление - * минут, и строку соберёт time_msg() из pop_status. 2 — перебить - * текущее сообщение немедленно, как делает оригинал по этой клавише. */ - { - uint8_t sh = kbd_raw_down(KBD_SHOWTIME); - - if (sh && !showtime_prev) pop_show_time = 2; - showtime_prev = sh; - } - /* T — постоянный показ таймера. Без модификаторов: Shift+T отдан - * «добавить HP» (фаза C плана keys_plan.md), и без этой проверки один - * тап сработал бы обеими функциями сразу. */ - { - uint8_t tk = (uint8_t)(kbd_raw_down(KBD_TIMER) && !ctrl && - !(kbd_raw_down(KBD_LSHIFT) || - kbd_raw_down(KBD_RSHIFT))); - - if (tk && !timer_prev) pop_show_timer ^= 1; - timer_prev = tk; - } - /* 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_music_stop(); 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 глушит - * звук первым действием. */ - /* Глушение снято 2026-08-28 (замер: загрузка уровня насос не - * морит — 500 вызовов подряд, максимум 245 832 такта при - * дедлайне 251 000). Прежнее обоснование — скрежет на палитре - * через BIOS ($A4 с запрещёнными прерываниями), пойманный на - * переходе без катсцены 2026-08-25. Если он вернётся, это - * единственное место, куда его надо возвращать. */ - /* Без 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(); - /* ПОЛОСУ HP — НА ОБЕИХ СТРАНИЦАХ. Старт уровня перерисовал фон, то есть - * стёр её, а сама она обновляется только при ИЗМЕНЕНИИ чисел. После - * Ctrl+A с выросшим за уровень максимумом (большой кувшин, чит Shift+T) - * это давало моргание: одна страница показывала старые деления, вторая - * новые (поймано пользователем 2026-08-27). invalidate ровно для этого - * и заведён — см. его шапку в pop_cdraw.c. */ - pop_hp_invalidate(); -} - -/* ---- ОТЛАДКА: обход комнат по номеру -------------------------------- * - * - * Нужен, чтобы пройти все комнаты уровня и собрать список багов отрисовки, - * включая недостижимые обычным путём. - * - * Здесь БЫЛ 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; -static uint8_t hp1_prev, hpmax_prev, feather_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); - /* Ctrl+I — бессмертие, Shift+I — переворот: скан-код один. */ - uint8_t i = (uint8_t)(kbd_raw_down(KBD_CHEAT_IMMO) && - (kbd_raw_down(KBD_LCTRL) || - kbd_raw_down(KBD_RCTRL))); - 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 = (uint8_t)(kbd_raw_down(KBD_CHEAT_UPSIDE) && - (kbd_raw_down(KBD_LSHIFT) || - kbd_raw_down(KBD_RSHIFT))); - uint8_t xd = kbd_raw_down(KBD_CHEAT_XDEC); - uint8_t xi = kbd_raw_down(KBD_CHEAT_XINC); - /* ±минута — на голых +/-, как в оригинале. С Ctrl те же - * клавиши означают обход комнат (sprpop.c), поэтому здесь Ctrl - * обязан быть ОТПУЩЕН, иначе сработает и то и другое. */ - uint8_t nomod = (uint8_t)!(kbd_raw_down(KBD_LCTRL) || - kbd_raw_down(KBD_RCTRL)); - uint8_t ts = (uint8_t)(nomod && (kbd_raw_down(KBD_TIME_SUB_MAIN) || - kbd_raw_down(KBD_TIME_SUB_NUM))); - uint8_t ta = (uint8_t)(nomod && (kbd_raw_down(KBD_TIME_ADD_MAIN) || - kbd_raw_down(KBD_TIME_ADD_NUM))); - /* C — записать POP.CFG сейчас (отладка MS2, до экрана Settings: - * там сохранять будет Apply/выход). */ - uint8_t cf = kbd_raw_down(0x21); /* C (PS/2 set 2) */ - /* ЧИТЫ ЗДОРОВЬЯ И ПЕРА (seg000:838, :845, :795). - * - * Делают то же, что соответствующие зелья, но НЕ через них: - * оригинал в этих ветках не зовёт stop_sounds, хотя ветка - * зелья зовёт (seg006:1871). Повторяем чит, а не зелье. - * Исключение — перо: там stop_sounds сидит внутри самого - * feather_fall(), поэтому остаётся. - * - * Shift+S делит скан-код с Ctrl+S (звук), поэтому Ctrl обязан - * быть отпущен; Shift+T — с голым T (таймер), но тот сам - * требует отпущенного Shift. */ - uint8_t shift = (uint8_t)(kbd_raw_down(KBD_LSHIFT) || - kbd_raw_down(KBD_RSHIFT)); - uint8_t h1 = (uint8_t)(shift && nomod && - kbd_raw_down(KBD_SOUND_TOGGLE)); - uint8_t hm = (uint8_t)(shift && kbd_raw_down(KBD_TIMER)); - uint8_t ff = (uint8_t)(shift && kbd_raw_down(KBD_KEY_W)); - if (pop_cheats && k && !cheat_prev) pop_guard_kill(); - /* Shift+S — одна единица HP, как малое красное зелье. */ - if (pop_cheats && h1 && !hp1_prev && hitp_curr != hitp_max) { - pop_sfx_play(33); - hitp_delta = 1; - pop_flash_color = POP_FLASH_RED; - pop_flash_time = 2; - } - /* Shift+T — поднять ПОТОЛОК HP, как большая склянка. Оригинал - * здесь условия «не полон» не ставит: потолок растёт всегда. */ - if (pop_cheats && hm && !hpmax_prev) { - pop_sfx_play(30); - pop_add_life(); - pop_flash_color = POP_FLASH_RED; - pop_flash_time = 4; - } - /* Shift+W — медленное падение, как зелье пера. */ - if (pop_cheats && ff && !feather_prev) { - pop_feather = POP_FEATHER_FRAMES; - pop_sfx_stop(); - pop_sfx_play(39); - pop_flash_color = POP_FLASH_GREEN; - pop_flash_time = 3; - } - hp1_prev = h1; hpmax_prev = hm; feather_prev = ff; - /* Бессмертие по кругу: 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 перерисовать */ -} - -/* ---- Запуск, смена уровня, завершение ------------------------------- * - * Три куска главного цикла, которые отрабатывают раз за игру (или раз на - * уровень). В резиденте от них оставались только вызовы — сам код здесь. - */ - -/* Фазы старта (см. шапку pop_boot): обе половины идемпотентны, а - * boot_rest_fail нужен, чтобы повторный вызов не пытался читать заново - * то, что уже не прочиталось. */ -static uint8_t boot_sound_done; -static uint8_t boot_rest_done; -static uint8_t boot_rest_fail; - -/* СТАРТ РАЗБИТ НА ТРИ ФАЗЫ — чтобы игрок увидел заставку и услышал музыку - * сразу, а не через несколько секунд чёрного экрана в тишине. - * - * pop_boot() — только настройки и графика: чёрный экран за ~0,1 с. - * pop_boot_sound() — индекс+первая страница звука, индекс музыки, CBL: - * после него титул уже показан, и тема заставки может - * начать играть, не дожидаясь остальных ресурсов. - * pop_boot_rest() — всё тяжёлое (эффекты, Кид, Тень, шрифт) ПОД стоящим - * титулом и звучащей темой; идемпотентно. - * - * ЧЕТЫРЕ ЗАГРУЗКИ ОТСЮДА ПРОСТО УБРАНЫ, а не отложены: атлас фона, страж, - * уровень 1 и игровая палитра — это дубликаты того, что и так делает - * pop_level_switch() на входе в ЛЮБОЙ уровень (а pop_new_game_load ещё и - * pop_pal_game_load перед ним). Оба маршрута в игру, LEVEL_LOAD и DEMO, - * упираются в pop_level_switch, так что второй раз читать их до заставки - * незачем — это ~0,77 с на ровном месте. Так же устроен и оригинал: у - * SDLPoP chtab_5/6/7 грузит load_lev_spr() на старте уровня, а init_game_main - * до заставки читает только меч, пламя, звуки и палитры (seg000.c:156). - * - * ПАЛИТРУ ТРОГАТЬ НЕЛЬЗЯ, пока на экране титул: и kid.pal (все 256 записей), - * и палитра тайлсета пишутся прямо в железо и перекрасили бы заставку. - * Поэтому в отложенной части нет ни одной палитровой операции — они все - * живут на маршруте входа в уровень, за чёрным экраном. */ -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(); - - /* FONT.ATL — В МИНИМАЛЬНОЙ ФАЗЕ, И ОТКЛАДЫВАТЬ ЕГО НЕЛЬЗЯ. - * - * Дело не в шрифте: СНИМОК ПАЛИТРЫ физически живёт в хвосте страницы - * шрифта (pop_ui.c, POP_FONT_SNAPSHOT_OFF — в W2 килобайта нет), и вся - * машинерия яркости начинается с `if (!font_ready) return`. Без него - * pop_pal_apply(DIM_BLACK) не гасит, а fade_in нечего проявлять — - * заставка возникает разом на полной яркости (поймано пользователем - * сразу после первой версии этой перестановки, 2026-09-02). - * Цена вопроса — 12 КБ, около 75 мс. */ - if (pop_menu_init() != 0) { - puts("menu font atlas load failed"); - return -1; - } - - pop_current_level = FIRST_LEVEL; - pop_cd_init(); /* метка «фон трогали» — пустые диапазоны */ - pop_guard_reset(); - 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) */ - if (!pop_settings.show_build_info) pop_pal_apply(POP_PAL_DIM_BLACK); - return 0; -} - -/* ЗВУК ВПЕРЁД ОСТАЛЬНЫХ РЕСУРСОВ. Насосу для тишины (а значит и для того, - * чтобы CBL вообще открылся) нужен ровно ОДИН блок набора, и упаковщик - * обязан класть его первым блоком страницы 0 — поэтому здесь читается одна - * страница вместо восьми (33 мс против 440). Остальные приезжают в - * pop_boot_rest, а до тех пор pop_sfx_play молча пропускает эффекты. - * - * Звать ПОСЛЕ того, как титул уже проявлен: чтение всё равно занимает - * полсекунды, и лучше, чтобы она прошла под картинкой. */ -int pop_boot_sound(void) __banked -{ - if (!boot_sound_done) { - boot_sound_done = 1; - (void)pop_sfx_init_begin(); /* эффектов может и не быть — не фатально */ - /* Индекс музыкального набора — одна страница EMM. Без него музыки не - * будет (pop_music_info вернёт -1 на любой трек), но игра работает: - * это необязательный ресурс, как и сами треки. */ - (void)pop_music_index_load(); - pop_settings_apply(1); /* CFG -> speed/cheats/CBL */ - } - /* ОТКРЫТИЕ CBL — ВНЕ РАЗОВОГО ГАРДА, и это не мелочь. Загрузка нужна - * один раз за запуск, а вот вывод к моменту следующего показа заставки - * почти наверняка закрыт: pop_title_show начинается с pop_sfx_pause, да - * и уровень оставляет CBL закрытым. Пока start стоял под флагом, - * Restart Game приводил к заставке БЕЗ музыки (поймано пользователем - * 2026-09-02), а заодно подвешивал ожидания «дослушать тему»: с - * выключенным насосом pop_mus_left не убывает, и pop_music_busy() - * истинен вечно. Идемпотентно и уважает Ctrl+S (pop_snd_want). */ - (void)pop_sfx_start(); - return 0; -} - -/* Шаг подкачки трека между кусками загрузки: заставочная тема играет уже - * с первой страницы (pop_music_stream), остальные обязаны приезжать по - * ходу — иначе насос уйдёт в ещё не прочитанную страницу. Запас после - * шага — 0,75 с звучания на каждые 8 КБ, куски загрузки короче. */ -static void boot_music_step(void) -{ - if (pop_music_loading()) (void)pop_music_load_step(); -} - -int pop_boot_rest(void) __banked -{ - if (boot_rest_done) return boot_rest_fail ? -1 : 0; - boot_rest_done = 1; - /* Заставку могли пропустить в первую же секунду — тогда звук ещё не - * поднимался. Идемпотентно. */ - (void)pop_boot_sound(); - - (void)pop_sfx_init_finish(); /* дочитать эффекты (музыка уже играет) */ - boot_music_step(); - if (pop_kid_data_load("KID\\kid.ani") != 0) { /* кадры+seqtbl в EMM-странице */ - puts("kid.ani load failed\n"); - boot_rest_fail = 1; - return -1; - } - boot_music_step(); - pop_shadow_load(); /* запечённый набор Тени — один раз на запуск */ - boot_music_step(); - if (pop_kid_load(KID_PAGES) != 0) { /* ВСЕ страницы: бег+стоп+разворот+присед+прыжок */ - puts("kid atlas load failed"); - boot_rest_fail = 1; - return -1; - } - boot_music_step(); - - /* Стартовая комната до title НЕ НУЖНА: LEVEL_LOAD всё равно выполняет - * pop_level_switch. Единственное исключение — явно включённый - * build-info: его фон по контракту является игровой сценой, поэтому - * только здесь читаются фон с уровнем и применяется игровая палитра. */ - if (pop_settings.show_build_info) { - if (pop_bg_load(pop_level_type()) != 0 || - pop_level_load_num(FIRST_LEVEL) != 0) { - puts("build-info scene load failed"); - boot_rest_fail = 1; - return -1; - } - pop_guard_load(); - pop_pal_game_load(); - 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); - } - 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; - - /* РЕСУРСЫ МОГУТ БЫТЬ ЕЩЁ НЕ ПРОЧИТАНЫ. Кид, Тень и шрифт грузятся под - * заставкой (см. шапку pop_boot), а её можно пропустить клавишей в - * первую же секунду. Идемпотентно: со второго раза это проверка байта. */ - if (pop_boot_rest() != 0) return -1; - - /* Граница 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) */ - /* ЗАМЕР 2026-08-28: глушение снято ВРЕМЕННО, чтобы измерить - * зазоры насоса на загрузке уровня (дедлайн 251 000 тактов). - * Если зазоры укладываются — снятие остаётся, и трек заставки - * доигрывает поверх загрузки, как в оригинале (seg001:690). */ - 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 на границе растра: впервые показать - * готовую комнату уже с окончательной палитрой уровня. - * Вторую страницу синхронизировать аппаратной копией. */ - /* ЖДЁМ КОНЦА ТРЕКА ЗАСТАВКИ НА ЧЁРНОМ ЭКРАНЕ — порт - * seg003:107: - * while (check_sound_playing() && !do_paused()) idle(); - * stop_sounds(); - * draw_level_first(); - * Порядок оригинала важен: загрузка уровня идёт ПОД - * звучащий трек, и только потом чернота до его конца. Тогда - * общее время черноты = max(трек, загрузка), а не их сумма, - * и трек не обрывается на полуслове. Раньше у нас было - * наоборот: pre_cut_finish досиживал трек, потом глушил - * насос и грузил уровень. - * - * ЗАМЕР 2026-08-28: 500 подряд вызовов насоса через всю - * загрузку — максимальный зазор 245 832 такта при дедлайне - * 251 000, ни одной пропущенной порции. Поэтому глушение на - * загрузке снято и музыка её переживает. - * - * Счётчик — страховка от вечного ожидания (потоковый трек, - * который никогда не кончится): ~20 с и идём дальше. */ - { - uint16_t mus_guard = 1000; - - while (pop_music_busy() && mus_guard) { - mus_guard--; - pop_wait_edge(); - } - pop_music_free(); /* порт stop_sounds() */ - } - 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_pal_game_load: она доплачивает слоты палитры по загруженным - * наборам (Тень, страж, тайлсет), и звать её раньше их чтения нельзя. */ - if (pop_boot_rest() != 0) return -1; - - 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(); - /* ЖЕЛЕЗО ЗВУКА ГАСИМ ПОСЛЕДНИМ ДЕЙСТВИЕМ, и pop_sfx_close выше для этого - * НЕ ДОСТАТОЧНО: он закрывает сессию, а bit7 порта CBL остаётся включён, - * пока его держит gfx_wait_vsync ради бита луча. Дальше main просто - * возвращается, а crt0 при возврате уходит прямо в ESTEX EXIT, минуя - * цепочку atexit (runtime/crt0_banked.s) — то есть vsync-ссылку снять - * уже некому. В результате мы уходили в шелл с включённым CBL, и он - * крутил свои 256 слотов: тихо до первой чужой записи в порт данных, а - * дальше она зацикливалась (поймано пользователем на железе 2026-09-02; - * в MAME не воспроизводится). */ - cbl_shutdown(); -} diff --git a/applications/SprPoP/src/sprpop_cold.h b/applications/SprPoP/src/sprpop_cold.h deleted file mode 100644 index b51384b..0000000 --- a/applications/SprPoP/src/sprpop_cold.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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; - -/* ---- Запуск / смена уровня / завершение ------------------------------ * - * Три куска главного цикла с частотой «раз за игру» или «раз на уровень». - * В резиденте от них остались только вызовы. */ - -/* СТАРТ В ТРИ ФАЗЫ — ради быстрого появления заставки; подробности и - * причины (в том числе почему часть загрузок убрана совсем) — в шапке - * pop_boot в sprpop_cold.c. 0 — OK, -1 — фатально (сообщение выведено). - * - * pop_boot() настройки + графика, чёрный экран; - * pop_boot_sound() индекс+первая страница звука, индекс музыки, CBL — - * звать, когда титул уже на экране: после него можно - * пускать тему заставки; - * pop_boot_rest() всё тяжёлое (эффекты, Кид, Тень, шрифт) под стоящим - * титулом; идемпотентно, страхуется вызовом из - * pop_new_game_load/pop_level_switch на случай, если - * заставку пропустили сразу. */ -int pop_boot(void) __banked; -int pop_boot_sound(void) __banked; -int pop_boot_rest(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 */ -/* МЕНЮ — Backspace, как в оригинале (keys.txt); Esc оставлен дублёром, - * потому что у SDLPoP он тоже открывает меню по умолчанию. */ -#define KBD_MENU 0x66 /* Backspace */ -#define KBD_MENU_ALT 0x76 /* Esc — дублёр */ -/* F10 больше НЕ здесь: он ловится в kbd_idle (sprpop.c) и работает везде, а - * не только в игровом цикле. Тут остаётся Ctrl+Q — вторая клавиша выхода - * из keys.txt. */ -#define KBD_QUIT 0x15 /* Q — вместе с Ctrl */ -#define KBD_RESTART 0x1C /* A — Ctrl+A, рестарт уровня */ -#define KBD_INTRO 0x2D /* R — Ctrl+R, вернуться в заставку */ -#define KBD_SHOWTIME 0x29 /* Space — сколько осталось (seg000:612) */ -#define KBD_TIMER 0x2C /* T — постоянный показ таймера. БЕЗ модификаторов: - * Shift+T в keys.txt — «добавить HP» (фаза C) */ - -/* Меню, 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 deleted file mode 100644 index de521a4..0000000 --- a/applications/SprPoP/tests/host/Makefile +++ /dev/null @@ -1,132 +0,0 @@ -# Модульные тесты движка SprPoP под ucsim_z80. Обвязка общая (testkit/), -# здесь — только сами наборы и список модулей, которые в них линкуются. -# Наружу тесты знают тот же единственный путь, что и само приложение, — -# корень тулчейна (см. ../../Makefile). Всё остальное считается от него. --include $(CURDIR)/../../local.mk -SPRINTER_ROOT ?= $(if $(wildcard $(CURDIR)/../../../../bin/sprinter-cc),$(abspath $(CURDIR)/../../../..),) -ifeq ($(strip $(SPRINTER_ROOT)),) -$(error Задайте SPRINTER_ROOT для host-тестов SprPoP) -endif -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_hang — зацеп обязан ДЕРЖАТЬСЯ (регресс glide_through_wall_guard). -OBJS_hang := $(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_death — смерть от меча: HP, признак смерти и, главное, координата с -# остаточной скоростью падения (находки 12/13 в docs/sdlpop_audit.md). -OBJS_death := $(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 deleted file mode 100644 index db81c9a..0000000 --- a/applications/SprPoP/tests/host/README.md +++ /dev/null @@ -1,121 +0,0 @@ -# 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 deleted file mode 100644 index 5e7921a..0000000 --- a/applications/SprPoP/tests/host/bank_stub.s +++ /dev/null @@ -1,25 +0,0 @@ -;-------------------------------------------------------------------------- -; 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 deleted file mode 100644 index 1974d6b..0000000 --- a/applications/SprPoP/tests/host/gen_kid_blob.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/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 deleted file mode 100644 index 62fdebf..0000000 --- a/applications/SprPoP/tests/host/golden/crouch.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Эталон трассы: 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 deleted file mode 100644 index 450567d..0000000 --- a/applications/SprPoP/tests/host/golden/fall_ledge.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Эталон трассы: 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 deleted file mode 100644 index 98ddae4..0000000 --- a/applications/SprPoP/tests/host/golden/jump_up.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Эталон трассы: 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 deleted file mode 100644 index d1df6d1..0000000 --- a/applications/SprPoP/tests/host/golden/loose.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Эталон трассы: 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 deleted file mode 100644 index d2c141c..0000000 --- a/applications/SprPoP/tests/host/golden/rjump_gap3.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Эталон трассы: 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 deleted file mode 100644 index 96c4fd6..0000000 --- a/applications/SprPoP/tests/host/golden/run_right.h +++ /dev/null @@ -1,26 +0,0 @@ -/* Эталон трассы: 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 deleted file mode 100644 index 8610c7c..0000000 --- a/applications/SprPoP/tests/host/golden/stand.h +++ /dev/null @@ -1,14 +0,0 @@ -/* Эталон трассы: 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 deleted file mode 100644 index c6c0891..0000000 --- a/applications/SprPoP/tests/host/golden/step_shift.h +++ /dev/null @@ -1,22 +0,0 @@ -/* Эталон трассы: 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 deleted file mode 100644 index 66f5dbd..0000000 --- a/applications/SprPoP/tests/host/golden/turn.h +++ /dev/null @@ -1,18 +0,0 @@ -/* Эталон трассы: 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 deleted file mode 100644 index 11f58aa..0000000 --- a/applications/SprPoP/tests/host/kidblob.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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 deleted file mode 100644 index b21db80..0000000 --- a/applications/SprPoP/tests/host/record_golden.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/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 deleted file mode 100644 index 9202153..0000000 --- a/applications/SprPoP/tests/host/scene.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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 deleted file mode 100644 index ba2dafd..0000000 --- a/applications/SprPoP/tests/host/scene.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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 deleted file mode 100644 index 0202e73..0000000 --- a/applications/SprPoP/tests/host/stubs.c +++ /dev/null @@ -1,423 +0,0 @@ -/* - * 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; } - -/* --- заглушки, без которых не линковались t_char/t_gate и прочие ------ * - * - * pop_chdir_home живёт в pop_path_bank.c (банк 10), а его тянет pop_kboot; - * kbd_raw_keypad_as_ext — из libc, её тянет pop_ctrl. Ни файловой - * системы, ни клавиатуры в хостовых тестах нет, поэтому обе — пустышки. - * До этого сборка tests/host падала на неразрешённых символах, и логику - * движка нельзя было проверить без эмулятора. */ -int8_t pop_chdir_home(void) __banked { return 0; } -void kbd_raw_keypad_as_ext(uint8_t on) { (void)on; } diff --git a/applications/SprPoP/tests/host/stubs.h b/applications/SprPoP/tests/host/stubs.h deleted file mode 100644 index ed71b8e..0000000 --- a/applications/SprPoP/tests/host/stubs.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 deleted file mode 100644 index 997ac99..0000000 --- a/applications/SprPoP/tests/host/t_app.c +++ /dev/null @@ -1,135 +0,0 @@ -/* 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 deleted file mode 100644 index 761648b..0000000 --- a/applications/SprPoP/tests/host/t_cfg.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * 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 deleted file mode 100644 index f213c60..0000000 --- a/applications/SprPoP/tests/host/t_char.c +++ /dev/null @@ -1,525 +0,0 @@ -/* - * 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 deleted file mode 100644 index e36ede6..0000000 --- a/applications/SprPoP/tests/host/t_cutscene.c +++ /dev/null @@ -1,46 +0,0 @@ -/* 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_death.c b/applications/SprPoP/tests/host/t_death.c deleted file mode 100644 index 1a81f6c..0000000 --- a/applications/SprPoP/tests/host/t_death.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * t_death — смерть от меча: что происходит с координатой и состоянием. - * - * Набор заведён ПЕРЕД правкой находок 12/13 (docs/sdlpop_audit.md), чтобы - * зафиксировать нынешнее поведение и поймать деградацию. История вопроса: - * правка 13 в изоляции уже ломала смерть — мёртвый Кид оставался с - * ненулевой скоростью падения, проваливался за нижнюю границу и игра - * уходила на рестарт, не показав тела. - * - * Проверяем ровно то, на что эти правки влияют: - * - HP и признак смерти; - * - КООРДИНАТУ по Y и остаточную скорость падения (в этом вся суть); - * - что смерть наступает при ударе не в боевой стойке (порт оригинала: - * «ранение вне боевой стойки означает смерть»). - */ -#include "tcheck.h" -#include "scene.h" -#include "stubs.h" -#include "pop_kid.h" -#include "pop_map.h" -#include "pop_guard.h" - -#define E 0 -#define F 1 -#define W 20 - -/* Ровный пол во всю ширину: ряд 2 — пол, выше пусто. Комната без краёв, - * чтобы смерть не смешивалась с падением. */ -static const uint8_t room_flat[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, -}; - -/* Площадка, обрывающаяся справа: колонки 0-4 — пол, 5-9 пусто. Кид у - * самого края спиной к обрыву — целевая сцена находки 12 (в оригинале - * такой удар отправляет в отдельную последовательность падения замертво). */ -static const uint8_t room_ledge[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, E, E, E, E, E, -}; - -/* Поставить сцену «страж бьёт Кида» и нанести удар. */ -static void strike_kid(const uint8_t *room, uint8_t col, uint8_t hp, uint8_t sword) -{ - sc_room(room, 1); - sc_kid_at(col, 2, 1 /* лицом вправо */); - hitp_curr = hp; - Kid.sword = sword; - pop_kid_dead = 0; - /* Признак «жив» обвязка ставит только стражу, поэтому Киду задаём его - * сами: обработчик удара первым делом отсекает уже мёртвого. */ - Kid.alive = -1; - /* Удар оформляется действием 99 «ранен» — так его помечает боевая - * проверка оригинала, а разбирает pop_check_sword_hurt. */ - Kid.action = 99; - pop_check_sword_hurt(); - /* Удар выставляет ДЕЛЬТУ, а HP меняет отдельный шаг кадра — как в - * оригинале. Без него hitp_curr остался бы прежним, и тест мерил бы - * не то. */ - pop_do_delta_hp(); -} - -TC_TEST(death_unarmed_hit_is_lethal) -{ - strike_kid(room_flat, 4, 3 /* HP */, 0 /* без меча */); - /* Удар не в боевой стойке смертелен независимо от запаса HP. */ - TC_EQ(hitp_curr, 0); -} - -TC_TEST(death_armed_hit_costs_one_hp) -{ - strike_kid(room_flat, 4, 3, 2 /* меч вынут */); - TC_EQ(hitp_curr, 2); -} - -TC_TEST(death_armed_last_hp_kills) -{ - strike_kid(room_flat, 4, 1, 2); - TC_EQ(hitp_curr, 0); -} - -/* --- КООРДИНАТА ПОСЛЕ УДАРА: суть находок 12 и 13 -------------------- */ - -TC_TEST(survivor_is_placed_on_floor) -{ - /* Пережил удар — оригинал ставит его на пол своего ряда и гасит - * скорость падения. Это единственная ветка, где он так делает. */ - strike_kid(room_flat, 4, 3, 2); - TC_EQ(Kid.fall_y, 0); - TC_EQ(Kid.y, pop_y_land[Kid.curr_row + 1]); -} - -TC_TEST(death_on_flat_floor_keeps_body_in_place) -{ - /* Смерть на ровном полу: тело обязано остаться в своём ряду. Если - * координата или скорость падения уедут, физика утащит труп вниз — - * ровно так ломалась игра при правке 13 в одиночку. */ - strike_kid(room_flat, 4, 1, 2); - TC_EQ(Kid.curr_row, 2); - TC_EQ(Kid.fall_y, 0); -} - -TC_TEST(death_at_ledge_keeps_body_in_place) -{ - /* Смерть у самого обрыва — целевая сцена находки 12. Сейчас мы - * ставим «заколот на месте» и удерживаем тело; в оригинале здесь - * своя последовательность падения замертво. Тест фиксирует НЫНЕШНЕЕ - * поведение: после правки 12+13 ожидание изменится осознанно. */ - strike_kid(room_ledge, 4, 1, 2); - TC_EQ(Kid.curr_row, 2); - TC_EQ(Kid.fall_y, 0); -} - -/* --- ДВЕ СМЕРТИ БЕЗОРУЖНОГО: на месте и сброшенным с уступа ---------- * - * - * Оригинал выбирает между ними по обстановке ПОЗАДИ: есть опора или до - * кромки меньше четырёх — «заколот на месте»; сзади обрыв — отдельная - * последовательность падения замертво (docs/sdlpop_audit.md, находка 12). - * - * Проверяем сам ВЫБОР: последовательности должны различаться. Сравнение - * с конкретным номером было бы хрупким — смещения приходят из таблицы, - * извлечённой из данных оригинала. */ - -static uint16_t seq_after_unarmed_death(const uint8_t *room, uint8_t col, int8_t dir) -{ - sc_room(room, 1); - sc_kid_at(col, 2, dir); - hitp_curr = 3; - Kid.sword = 0; /* безоружен: падение уже спрятало меч */ - Kid.alive = -1; - pop_kid_dead = 0; - Kid.action = 99; - pop_check_sword_hurt(); - pop_do_delta_hp(); - return Kid.curr_seq; -} - -TC_TEST(unarmed_death_with_floor_behind_differs_from_ledge) -{ - uint16_t on_floor, at_ledge; - /* Лицом ВПРАВО в середине сплошного пола: сзади (слева) опора. */ - on_floor = seq_after_unarmed_death(room_flat, 4, 1); - /* Лицом ВЛЕВО у кромки: сзади (справа) обрыв — колонки 5..9 пусты. */ - at_ledge = seq_after_unarmed_death(room_ledge, 4, -1); - TC_TRUE(on_floor != at_ledge); -} - -TC_TEST(unarmed_death_at_ledge_is_lethal_too) -{ - (void)seq_after_unarmed_death(room_ledge, 4, -1); - TC_EQ(hitp_curr, 0); -} - -/* --- ПРИЗЕМЛЕНИЕ МЁРТВОГО ТЕЛА --------------------------------------- - * - * Оригинал спрашивает у приземляющегося, жив ли он (seg005:0173): вся - * развязка «мягко / средне / разбиться» отведена ЖИВОМУ, а телу — своя - * ветка (добить HP, звук падения насмерть, seq_22). - * - * У нас развилки не было, и это не бросалось в глаза только из-за высоты: - * тело, сброшенное ударом с ОДНОГО ряда, набирает fall_y < 22 — урона нет, - * «последнее HP» не тратится, ветка «разбился» не выбирается никогда, и - * труп приземлялся в ПРИСЕД (кадр 109). Отсюда симптом «убитого Кида - * уронили, а он сел этажом ниже». - * - * Пара тестов держит обе стороны развилки: мёртвый обязан разбиться, - * живой на той же высоте — сесть, как и раньше. */ - -#define FRAME_109_CROUCH 109 - -/* Уронить персонажа с ряда 1 на пол ряда 2 и дать физике доиграть. */ -static void drop_from_row1(const uint8_t *room, uint8_t col, uint8_t alive, uint8_t hp) -{ - uint8_t i; - sc_room(room, 1); - sc_kid_at(col, 1, 1 /* лицом вправо */); - hitp_curr = hp; - Kid.alive = (int8_t)alive; /* -1 жив, 0 мёртв (соглашение оригинала) */ - Kid.sword = 0; - pop_kid_dead = 0; - Kid.action = 4; /* свободное падение */ - for (i = 0; i < 40; i++) pop_phys_tick(); -} - -TC_TEST(dead_body_falls_crushed_not_crouched) -{ - /* Мёртвое тело падает на ОДИН ряд: высоты не хватает ни на урон, ни на - * «последнее HP», поэтому до фикса оно уходило в мягкое приземление. */ - drop_from_row1(room_flat, 4, 0 /* мёртв */, 0); - TC_EQ(Kid.curr_row, 2); /* долетело до пола */ - TC_EQ(Kid.fall_y, 0); /* скорость погашена */ - TC_FALSE(Kid.frame == FRAME_109_CROUCH); /* НЕ присед */ - TC_EQ(pop_kid_dead, 1); /* пошли дорогой «разбился» */ -} - -TC_TEST(living_soft_land_from_row1_still_crouches) -{ - /* Обратная сторона развилки: живой с той же высоты обязан сесть, как и - * до фикса. Если этот тест покраснеет — развилка съела живую ветку. */ - drop_from_row1(room_flat, 4, (uint8_t)-1 /* жив */, 3); - TC_EQ(Kid.curr_row, 2); - /* Кадр не проверяем: присед — это ПОСЛЕДОВАТЕЛЬНОСТЬ, и к сороковому - * тику она уже ушла с начального 109 (первый прогон теста поймал это - * на кадре 107). Держим то, что действительно различает ветки. */ - TC_EQ(pop_kid_dead, 0); /* живого не разбило */ - TC_EQ(hitp_curr, 3); /* один ряд — без урона */ -} - -int main(void) -{ - sc_init(); - TC_RUN(death_unarmed_hit_is_lethal); - TC_RUN(death_armed_hit_costs_one_hp); - TC_RUN(death_armed_last_hp_kills); - TC_RUN(survivor_is_placed_on_floor); - TC_RUN(death_on_flat_floor_keeps_body_in_place); - TC_RUN(death_at_ledge_keeps_body_in_place); - TC_RUN(unarmed_death_with_floor_behind_differs_from_ledge); - TC_RUN(unarmed_death_at_ledge_is_lethal_too); - TC_RUN(dead_body_falls_crushed_not_crouched); - TC_RUN(living_soft_land_from_row1_still_crouches); - return 0; -} diff --git a/applications/SprPoP/tests/host/t_demo.c b/applications/SprPoP/tests/host/t_demo.c deleted file mode 100644 index 85d28c3..0000000 --- a/applications/SprPoP/tests/host/t_demo.c +++ /dev/null @@ -1,89 +0,0 @@ -/* 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 deleted file mode 100644 index 36a9aad..0000000 --- a/applications/SprPoP/tests/host/t_flow.c +++ /dev/null @@ -1,52 +0,0 @@ -/* 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 deleted file mode 100644 index 0340fdf..0000000 --- a/applications/SprPoP/tests/host/t_gate.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * 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 deleted file mode 100644 index 0702db5..0000000 --- a/applications/SprPoP/tests/host/t_geom.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 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 deleted file mode 100644 index 66d1c02..0000000 --- a/applications/SprPoP/tests/host/t_grab.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * 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_hang.c b/applications/SprPoP/tests/host/t_hang.c deleted file mode 100644 index 0ee13a6..0000000 --- a/applications/SprPoP/tests/host/t_hang.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * t_hang.c — ЗАЦЕП ОБЯЗАН ДЕРЖАТЬСЯ, а не срываться на следующем кадре. - * - * Регресс 2026-09-02. glide_through_wall_guard() (порт fix_glide_through_wall, - * взят в билд 2026-08-31) звался в do_fall СРАЗУ ПОСЛЕ check_grab. Беда в - * том, что seq_15 называется grab_ledge_MIDAIR: после удачного зацепа - * Char.action == 3 (в воздухе), персонаж уже подтянут вплотную к кромке — а - * под кромкой в комнатах оригинала стоит КЛАДКА. Guard видел ровно её - * (t == TILE_WALL, d == 10), считал это «пролётом сквозь стену», отбрасывал - * персонажа на 5 пикселей назад и гасил fall_x. Зацеп РИСОВАЛСЯ и тут же - * срывался — уровень 3 (комната 7, длинный прыжок через четыре пролёта) и - * attract-демо (комната 2, прыжок с места). - * - * ПОЧЕМУ ПРЕЖНИЕ НАБОРЫ ЭТОГО НЕ ПОЙМАЛИ. t_grab судит по «действие стало - * вис» — а вис-то наступал, он просто не жил. Отсюда мера здесь другая: - * вис обязан ДЕРЖАТЬСЯ подряд несколько кадров. Разница между «зацепился» - * и «зацепился и держится» — это и есть разница между багом и нормой. - * - * СЦЕНА (геометрия уровня 3, комната 7, приведённая внутрь одной комнаты, - * чтобы шов не примешивался): кромка на (0,0), под ней кладка (1,0), четыре - * пролёта (0,1..4), толчок с плиты (0,5), разбег справа. - */ -#include -#include "tcheck.h" -#include "scene.h" -#include "stubs.h" -#include "pop_kid.h" -#include "pop_map.h" - -#define E 0x00 -#define F 0x01 -#define G 0x04 -#define L 0x0B -#define T 0x13 -#define W 0x14 - -static uint8_t room[30] = { - F, E, E, E, E, L, F, T, F, T, - W, E, E, E, E, W, W, W, W, W, - W, E, E, E, E, W, W, W, W, W, -}; - -#define ACT_HANG_CLIMB 2 -#define ACT_HANG_STRAIGHT 6 -#define HOLD_FRAMES 3 - -/* Вис ДЕРЖИТСЯ: действие «вис» стоит HOLD_FRAMES кадров подряд. */ -static uint8_t hang_holds(uint8_t ledge, uint8_t nrun) -{ - uint8_t i, run = 0; - room[0] = ledge; - sc_room(room, 7); - sc_kid_at(9, 0, -1); /* лицом влево, у правого края */ - sc_run(SC_L, nrun); /* разбег */ - sc_run(SC_L | SC_U, 6); /* толчок в прыжок */ - sc_trace_clear(); - sc_run(SC_SHIFT, 30); /* полёт, зацеп и жизнь после него */ - for (i = 0; i < sc_len; i++) { - uint8_t a = sc_trace[i].action; - if (a == ACT_HANG_CLIMB || a == ACT_HANG_STRAIGHT) { - if (++run >= HOLD_FRAMES) return 1; - } else run = 0; - } - return 0; -} - -/* Сколько фаз разбега из 41 дают ЖИВОЙ вис. До регресса — все, кроме - * первой (слишком близкий толчок), то есть 34; при сорванном зацепе — ноль. */ -static uint8_t window(uint8_t ledge) -{ - uint8_t k, n = 0; - for (k = 6; k <= 46; k++) if (hang_holds(ledge, k)) n++; - return n; -} - -TC_TEST(long_jump_hang_survives_wall_under_ledge) -{ - /* Кромка-пол: под ней кладка — та самая, на которую срабатывал guard. */ - TC_TRUE(window(F) >= 30); -} - -TC_TEST(long_jump_hang_survives_on_gate) -{ - /* Кромка-решётка: тайл другой, кладка под ним та же. Проверяется - * отдельно, потому что зацеп за решётку идёт своей веткой can_grab. */ - TC_TRUE(window(G) >= 30); -} - -int main(void) -{ - sc_init(); - TC_RUN(long_jump_hang_survives_wall_under_ledge); - TC_RUN(long_jump_hang_survives_on_gate); - return 0; -} diff --git a/applications/SprPoP/tests/host/t_jaffar.c b/applications/SprPoP/tests/host/t_jaffar.c deleted file mode 100644 index 08bf622..0000000 --- a/applications/SprPoP/tests/host/t_jaffar.c +++ /dev/null @@ -1,349 +0,0 @@ -/* - * 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 deleted file mode 100644 index 24afb09..0000000 --- a/applications/SprPoP/tests/host/t_mouse.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * 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 deleted file mode 100644 index a3c91a0..0000000 --- a/applications/SprPoP/tests/host/t_phys.c +++ /dev/null @@ -1,380 +0,0 @@ -/* - * 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 deleted file mode 100644 index 2c96934..0000000 --- a/applications/SprPoP/tests/host/t_shadow.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * 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 deleted file mode 100644 index cd9e904..0000000 --- a/applications/SprPoP/tests/host/t_timer.c +++ /dev/null @@ -1,169 +0,0 @@ -/* 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, 4, 4)); /* Settings: unlimited */ - TC_EQ(pop_timer_ticks, POP_TIMER_START_TICKS); - TC_FALSE(pop_timer_tick(1, 0, 4, 4)); /* 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, 4, 4)); - TC_EQ(pop_timer_minutes, 2); - TC_EQ(pop_timer_ticks, 1); - TC_FALSE(pop_timer_tick(1, 1, 4, 4)); - 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, 4, 4)); - TC_EQ(pop_timer_minutes, 0); - TC_EQ(pop_timer_ticks, POP_TIMER_START_TICKS); - TC_FALSE(pop_timer_tick(1, 1, 4, 4)); - TC_FALSE(pop_timer_tick(0, 1, 4, 4)); -} - -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); -} - -/* --- ХОД ЧАСОВ НЕ ЗАВИСИТ ОТ РЕЖИМА СКОРОСТИ ------------------------- * - * - * Тик стоит `base` кадров луча — столько, сколько их в кадре режима - * NORMAL. Быстрые режимы только УКОРАЧИВАЮТ кадр (spent < base), поэтому - * за одно и то же реальное время выходит одинаковое число тиков. - * Наблюдение пользователя, из которого выросла правка: «при FAST/FASTEST - * время начинает бежать быстрее». */ - -/* Сколько тиков насчитается за `frames` логических кадров темпа spent/base. */ -static uint16_t ticks_over(uint8_t frames, uint8_t spent, uint8_t base) -{ - uint16_t before; - uint8_t i; - pop_timer_new_game(); - before = pop_timer_ticks; - for (i = 0; i < frames; i++) pop_timer_tick(1, 1, spent, base); - return (uint16_t)(before - pop_timer_ticks); -} - -TC_TEST(timer_normal_ticks_every_frame) -{ - /* NORMAL вне боя: кадр равен эталону — тик на каждом кадре, как было до - * правки. Страховка, что NORMAL не изменился ни на тик. */ - TC_EQ(ticks_over(12, 4, 4), 12); -} - -TC_TEST(timer_normal_fight_ticks_every_frame) -{ - /* NORMAL в бою: кадр длиннее (5), но и эталон 5 — часы по-прежнему идут - * по кадру. В РЕАЛЬНОМ времени это медленнее, и ровно так же ведёт себя - * оригинал, поэтому трогать нечего. */ - TC_EQ(ticks_over(12, 5, 5), 12); -} - -TC_TEST(timer_fast_keeps_real_time) -{ - /* FAST вне боя: кадр 3 при эталоне 4. За 12 кадров (36 кадров луча) — - * 9 тиков, ровно столько же, сколько NORMAL насчитает за те же 36 кадров - * луча. До правки было бы 12: часы бежали на треть быстрее. */ - TC_EQ(ticks_over(12, 3, 4), 9); -} - -TC_TEST(timer_fastest_fight_keeps_real_time) -{ - /* FASTEST в бою — самый короткий кадр против самого длинного эталона: 3 против - * 5. За 20 кадров ждём 12 тиков. */ - TC_EQ(ticks_over(20, 3, 5), 12); -} - -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); - TC_RUN(timer_normal_ticks_every_frame); - TC_RUN(timer_normal_fight_ticks_every_frame); - TC_RUN(timer_fast_keeps_real_time); - TC_RUN(timer_fastest_fight_keeps_real_time); -} diff --git a/applications/SprPoP/tests/host/t_wall.c b/applications/SprPoP/tests/host/t_wall.c deleted file mode 100644 index 662e432..0000000 --- a/applications/SprPoP/tests/host/t_wall.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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); -} - -/* --- ГДЕ КОНЧАЕТСЯ СТЕНА ПО X ---------------------------------------- * - * - * Колонка 5 — кладка (ряды 1-2). Тайл шириной 14, колонка c занимает - * [58 + 14*c, 58 + 14*(c+1)). Для колонки 5 это [128, 142). Персонаж - * НИКОГДА не должен оказаться внутри этого отрезка ниже верхнего ряда: - * там сплошная кладка, и «падение частично в стене» — как раз оно. - * - * Проверка отдельная от той, что выше: та смотрит КОЛОНКУ в конце сцены, - * а эта — X на КАЖДОМ кадре падения. Персонаж может кончить падение в - * законной колонке, успев по дороге пройти сквозь кладку. */ -#define WALL_X_LO 128 -#define WALL_X_HI 142 - -/* Сколько кадров трассы имеют X внутри кладки, будучи ниже верхнего ряда. */ -static uint8_t frames_inside_wall(uint8_t x) -{ - uint8_t i, n = 0; - sc_room(room14, 14); - sc_kid_at_x(8, 0, x, -1); - sc_trace_clear(); - sc_run(SC_L | SC_U, 6); - sc_run(0, 34); - for (i = 0; i < sc_len; i++) { - if (sc_trace[i].row == 0) continue; /* верхний ряд — не кладка */ - if (sc_trace[i].x >= WALL_X_LO && sc_trace[i].x < WALL_X_HI) n++; - } - return n; -} - -TC_TEST(wall_x_never_inside_masonry) -{ - uint8_t x, bad = 0; - puts_("inside x=177..196: "); - for (x = X_LO; x <= X_HI; x++) { - uint8_t n = frames_inside_wall(x); - put((char)(n ? ('0' + (n > 9 ? 9 : n)) : '.')); - if (n) bad++; - } - put('\n'); - TC_EQ(bad, 0); -} - -/* Симметричный случай: прыжок ВПРАВО через провал колонок 3-4 к площадке - * (0,5). Стартовая площадка — колонки 0-2 верхнего ряда; под провалом - * дно (ряд 2), по бокам кладка. Недолёт обязан кончиться на дне, а не - * внутри кладки: выталкивание из стены работает в обе стороны. */ -static char jump_right_from(uint8_t x) -{ - sc_room(room14, 14); - sc_kid_at_x(2, 0, x, 1 /* лицом вправо */); - sc_trace_clear(); - sc_run(SC_R | 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 'R'; /* остался на верхнем ряду */ - /* Ниже верхнего ряда законны ОБА провала — слева от кладки - * (колонки 3-4, недолёт) и справа (6-7, перелёт через площадку). - * Незаконна только сама кладка колонки 5. */ - if (col != 5) return '.'; - /* Вис на уступе площадки (0,5) — законное состояние, и колонка - * при нём как раз 5. Отличаем по действию персонажа. */ - { - uint8_t act = sc_trace[sc_len - 1].action; - if (act == 2 || act == 6) return 'h'; /* hang_climb / hang_straight */ - } - /* Осталось одно: персонаж НИЖЕ верхнего ряда, в колонке кладки и - * не висит — то есть находится ВНУТРИ стены. Именно так выглядит - * «падение частично в стене» (docs/sdlpop_audit.md, находка 24). */ - return '5'; - } -} - -/* ФИКС ПАДЕНИЯ СКВОЗЬ СТЕНУ ВЗЯТ — ЖДЁМ НОЛЬ. - * - * В ванили прыжок вправо через провал давал ДВА случая из четырнадцати, - * где Кид оказывается в колонке кладки, будучи в воздухе, — «падение - * частично в стене». Это баг оригинального PoP, для которого SDLPoP - * держит опциональное исправление (fix_glide_through_wall, seg005 в - * do_fall). - * - * 2026-08-31 исправление ПОРТИРОВАНО в текущий билд - * (glide_through_wall_guard в pop_map.c), и оба случая исчезли. Поэтому - * ожидание теперь НОЛЬ. - * - * Если когда-нибудь появится режим VANILLA без этого фикса, ожидание - * станет зависеть от режима: 0 в ENHANCED и 2 в ванильном - * (docs/vanilla_vs_bugfixed.md). Число 2 сохранено в имени константы - * именно поэтому — оно измерено, а не выдумано. */ -#define WALL_VANILLA_GLIDE_CASES 2 /* сколько их было ДО фикса */ -#define WALL_EXPECTED_GLIDE_CASES 0 /* сколько ожидаем СЕЙЧАС */ - -TC_TEST(wall_stops_jump_from_left_side) -{ - uint8_t x, bad = 0; - /* Плита колонок 3-4: [100, 128). */ - /* Плита колонок 0-2: колонка 2 — это [86, 100). */ - puts_("right x=86..99: "); - for (x = 86; x <= 99; x++) { - char r = jump_right_from(x); - put(r); - if (r != 'R' && r != '.' && r != 'h') bad++; - } - put('\n'); - TC_EQ(bad, WALL_EXPECTED_GLIDE_CASES); -} - -int main(void) -{ - sc_init(); - TC_RUN(wall_stops_undershot_jump); - TC_RUN(wall_x_never_inside_masonry); - TC_RUN(wall_stops_jump_from_left_side); - return 0; -} diff --git a/applications/SprPoP/tools/fetch_orig.py b/applications/SprPoP/tools/fetch_orig.py deleted file mode 100755 index 2f7617f..0000000 --- a/applications/SprPoP/tools/fetch_orig.py +++ /dev/null @@ -1,411 +0,0 @@ -#!/usr/bin/env python3 -"""fetch_orig.py — забирает ВНЕШНИЕ исходные данные в assets/orig/. - -Репозиторий SprPoP не хранит ни байта чужих данных: `assets/orig/` целиком -в .gitignore. Хранится вместо них ЗНАНИЕ, откуда эти данные берутся, — -таблица SOURCES ниже, и этот скрипт, который по ней всё выкачивает. - - python3 tools/fetch_orig.py # SDLPoP + музыка (чего нет) - python3 tools/fetch_orig.py --check # что уже на месте, ничего не качать - python3 tools/fetch_orig.py --list # печать таблицы источников - python3 tools/fetch_orig.py sdlpop --force --ref v1.23 - -Что откуда: - - SDLPoP (GPLv3, github.com/NagyD/SDLPoP) — распакованные ресурсы DOS-версии - (`data/`: спрайты, палитры, LEVELS/res20xx.bin, DIGISND*.DAT) и исходники - (`src/`), которые нам одновременно ЭТАЛОН МЕХАНИКИ. Тянем архивом ветки - или тега, а не git-клоном: в assets/orig/ не нужен вложенный .git, а - зафиксировать версию всё равно надо — пишем её в SDLPoP/.fetch.json. - - PoP1_DOS_music (popot.org, 43 МБ) — записи саундтрека DOS-версии в четырёх - наборах (flac/mp3/ogg/ogg_MT-32), из них `pop_pack_music.py` делает треки - для CBL. Ссылку берём ИЗ САМОГО SDLPoP (ReadMe.amigaos4, секция «AUDIO IS - SLOW/AWFULL»), чтобы она не протухла молча вместе с нашей копией; если - секцию не нашли — падаем на константу MUSIC_URL_FALLBACK. - - MSDOS/ — оригинальная поставка PoP 1.0 для MS-DOS. НЕ качается: это - копирайтный дистрибутив, качать его неоткуда и незачем. Он НЕОБЯЗАТЕЛЕН — - всё, что нужно сборке (уровни, звуки), лежит в SDLPoP. Если папка у вас - есть, её звуковые .dat можно включить явно: `make SND_SRC=msdos ...` - (см. assets/orig/README.md). -""" -import argparse -import io -import json -import os -import re -import shutil -import sys -import tarfile -import time -import urllib.error -import urllib.request -import zipfile -from pathlib import Path - -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) -import paths as P # раскладка каталогов SprPoP (tools/paths.py) - -# --- источники ------------------------------------------------------------- -SDLPOP_REPO = "https://github.com/NagyD/SDLPoP" -SDLPOP_REF = "master" # ветка по умолчанию = «последняя версия» -SDLPOP_API = "https://api.github.com/repos/NagyD/SDLPoP" - -# Секция «AUDIO IS SLOW/AWFULL» в этом файле SDLPoP держит ссылку на записи. -MUSIC_README = "ReadMe.amigaos4" -MUSIC_URL_FALLBACK = \ - "https://www.popot.org/get_the_games/various/PoP1_DOS_music.zip" - -UA = {"User-Agent": "SprPoP-fetch-orig/1"} -CACHE = P.ORIG / ".cache" # скачанные архивы; переживает make clean - -SOURCES = [ - ("sdlpop", P.SDLPOP, SDLPOP_REPO, - "ресурсы DOS-версии (data/) + исходники-эталон (src/)"), - ("music", P.MUSIC_ROOT, MUSIC_URL_FALLBACK, - "записи саундтрека DOS-версии, 4 набора (flac/mp3/ogg/ogg_MT-32)"), - ("MSDOS (необязательно, вручную)", P.MSDOS, "дистрибутив игры", - "оригинальные .dat; нужен только для SND_SRC=msdos"), -] - - -# --- мелочи ---------------------------------------------------------------- -def say(msg): - print("fetch: " + msg, flush=True) - - -def fetch_url(url, dest, expect_type=None): - """Скачать url в dest (через .part), с индикатором на одной строке. - - Если dest уже есть и его размер совпал с Content-Length — не качаем. - """ - req = urllib.request.Request(url, headers=UA) - with urllib.request.urlopen(req, timeout=60) as r: - total = int(r.headers.get("Content-Length") or 0) - ctype = r.headers.get("Content-Type", "") - if expect_type and expect_type not in ctype: - say(f"ПРЕДУПРЕЖДЕНИЕ: {url} отдал Content-Type {ctype!r}") - if dest.exists() and total and dest.stat().st_size == total: - say(f"уже скачано: {dest.name} ({total} Б)") - return dest - dest.parent.mkdir(parents=True, exist_ok=True) - tmp = dest.with_suffix(dest.suffix + ".part") - # Проценты — только в терминал: в перенаправленном выводе (лог сборки, - # CI) \r не схлопывается, и одна закачка даёт полторы сотни строк. - tty = sys.stdout.isatty() - got, t0 = 0, time.time() - with open(tmp, "wb") as f: - while True: - chunk = r.read(256 * 1024) - if not chunk: - break - f.write(chunk) - got += len(chunk) - if total and tty: - print("\r %s: %3d%% (%d/%d КБ)" - % (dest.name, got * 100 // total, - got >> 10, total >> 10), end="", flush=True) - print("%s %s: готово, %d КБ за %.1f с%s" - % ("\r" if tty else "", dest.name, got >> 10, - time.time() - t0, " " * 20 if tty else "")) - tmp.replace(dest) - return dest - - -def safe_members(names, root): - """Проверка архива на выход за пределы каталога распаковки.""" - for n in names: - p = Path(n) - if p.is_absolute() or ".." in p.parts: - sys.exit(f"архив содержит небезопасный путь {n!r} — распаковка отменена") - return root - - -def backup_dir(dest): - """Сложить текущий каталог в .cache архивом — перед заменой.""" - CACHE.mkdir(parents=True, exist_ok=True) - tar = CACHE / ("%s-backup-%s.tar.gz" % (dest.name, - time.strftime("%Y%m%d-%H%M%S"))) - with tarfile.open(tar, "w:gz") as t: - t.add(dest, arcname=dest.name) - say(f"старая копия сохранена: {tar} ({tar.stat().st_size >> 10} КБ)") - return tar - - -def swap_dir(new, dest): - """Поставить свежераспакованный каталог на место старого.""" - dest.parent.mkdir(parents=True, exist_ok=True) - old = dest.with_name(dest.name + ".old") - if old.exists(): - shutil.rmtree(old) - if dest.exists(): - dest.rename(old) - new.rename(dest) - if old.exists(): - shutil.rmtree(old) - - -# --- SDLPoP ---------------------------------------------------------------- -def sdlpop_head(ref): - """(sha, дата) верхушки ref на GitHub; (None, None) если API недоступен.""" - try: - req = urllib.request.Request(f"{SDLPOP_API}/commits/{ref}", headers=UA) - with urllib.request.urlopen(req, timeout=30) as r: - d = json.load(r) - return d["sha"], d["commit"]["committer"]["date"] - except (urllib.error.URLError, KeyError, ValueError) as e: - # Лимит запросов GitHub или отсутствие сети: не повод падать — - # просто не сможем сказать «у нас уже последняя». - say(f"GitHub API недоступен ({e}); версию не проверяю") - return None, None - - -def stamp_path(dest): - return dest / ".fetch.json" - - -def manifest(root): - """{относительный путь: sha256} по всему дереву, кроме служебного.""" - import hashlib - out = {} - for f in sorted(root.rglob("*")): - if not f.is_file() or f.name == ".fetch.json": - continue - rel = str(f.relative_to(root)) - h = hashlib.sha256() - with open(f, "rb") as fh: - for chunk in iter(lambda: fh.read(1 << 16), b""): - h.update(chunk) - out[rel] = h.hexdigest() - return out - - -def local_edits(dest): - """Файлы, изменённые ПОСЛЕ распаковки: (изменённые, добавленные). - - Считается по манифесту из .fetch.json. Нет манифеста — вернуть None: - происхождение копии неизвестно, и трогать её нельзя. - """ - base = read_stamp(dest).get("files") - if not base: - return None - now = manifest(dest) - changed = sorted(k for k, v in now.items() if k in base and base[k] != v) - added = sorted(k for k in now if k not in base) - return changed, added - - -def read_stamp(dest): - try: - return json.loads(stamp_path(dest).read_text()) - except (OSError, ValueError): - return {} - - -def fetch_sdlpop(ref=SDLPOP_REF, force=False): - dest = P.SDLPOP - sha, date = sdlpop_head(ref) - have = read_stamp(dest) - if dest.exists() and (dest / "data").is_dir(): - if sha and have.get("sha") == sha and not force: - say(f"SDLPoP уже последний: {ref} {sha[:8]} от {date[:10]}") - return - # ЗАМЕНА КАТАЛОГА — ТОЛЬКО ОСОЗНАННО. Наша копия SDLPoP не просто - # справочник: в неё внесены отладочные врезки (POP_TRACE — покадровая - # трасса Кида для сверки с портом, дампы спрайтов и палитры), и - # перекачка их сотрёт. Поэтому существующее дерево не трогаем без - # --force, а что именно потеряется — говорим заранее. - edits = local_edits(dest) - if edits is None: - note = ("происхождение копии неизвестно (нет манифеста .fetch.json) " - "— возможно, в ней есть ваши правки") - else: - changed, added = edits - if changed or added: - note = "локально изменено: %s%s" % ( - ", ".join(changed[:8]) + ("…" if len(changed) > 8 else ""), - "; добавлено %d файлов" % len(added) if added else "") - else: - note = "" - if not force: - say("SDLPoP уже на месте" + (f"; {note}" if note else "")) - say(" перекачать поверх: make FETCH_ARGS=--force fetch-sdlpop") - say(" (правки будут сохранены в бэкап, см. ниже)") - return - if note: - say("ВНИМАНИЕ: " + note) - backup_dir(dest) - # Тег и ветка лежат по разным путям codeload; пробуем оба. - urls = [f"https://codeload.github.com/NagyD/SDLPoP/tar.gz/refs/heads/{ref}", - f"https://codeload.github.com/NagyD/SDLPoP/tar.gz/refs/tags/{ref}"] - CACHE.mkdir(parents=True, exist_ok=True) - tar = CACHE / f"SDLPoP-{ref.replace('/', '_')}.tar.gz" - last = None - for u in urls: - try: - say(f"качаю SDLPoP {ref} <- {u}") - fetch_url(u, tar) - last = None - break - except urllib.error.HTTPError as e: - last = e - if last is not None: - sys.exit(f"не удалось скачать SDLPoP {ref!r}: {last}") - - tmp = P.ORIG / ".SDLPoP.new" - if tmp.exists(): - shutil.rmtree(tmp) - tmp.mkdir(parents=True) - with tarfile.open(tar, "r:gz") as t: - names = t.getnames() - safe_members(names, tmp) - # В архиве GitHub всё лежит в одном каталоге SDLPoP-/ — снимаем его. - top = os.path.commonpath([n for n in names if n]) if names else "" - t.extractall(tmp) - src = tmp / top if top else tmp - inner = P.ORIG / ".SDLPoP.unpacked" - if inner.exists(): - shutil.rmtree(inner) - src.rename(inner) - shutil.rmtree(tmp, ignore_errors=True) - - if not (inner / "data").is_dir() or not (inner / "src").is_dir(): - shutil.rmtree(inner) - sys.exit("в архиве SDLPoP нет data/ или src/ — раскладка репозитория " - "изменилась, чините fetch_orig.py") - # Манифест распакованного дерева: по нему следующий fetch отличит наши - # правки от нетронутых файлов и не сотрёт их молча. - stamp_path(inner).write_text(json.dumps( - {"repo": SDLPOP_REPO, "ref": ref, "sha": sha, "date": date, - "fetched": time.strftime("%Y-%m-%d %H:%M:%S"), - "files": manifest(inner)}, - ensure_ascii=False, indent=2) + "\n") - swap_dir(inner, dest) - say(f"SDLPoP: {ref} {(sha or '?')[:8]} -> {dest}") - - -# --- музыка ---------------------------------------------------------------- -def music_url(): - """Ссылка на архив записей: из ReadMe SDLPoP, иначе константа.""" - rm = P.SDLPOP / MUSIC_README - if rm.is_file(): - text = rm.read_text(errors="replace") - m = re.search(r"https?://\S*[Mm]usic\S*\.zip", text) - if m: - url = m.group(0).rstrip(".,)") - say(f"ссылка на музыку из SDLPoP/{MUSIC_README}: {url}") - return url - say(f"в SDLPoP/{MUSIC_README} ссылки не нашлось — беру запасную") - else: - say(f"нет SDLPoP/{MUSIC_README} (сначала `fetch_orig.py sdlpop`) — " - "беру запасную ссылку") - return MUSIC_URL_FALLBACK - - -def fetch_music(force=False): - dest = P.MUSIC_ROOT - if dest.is_dir() and not force: - have = [d for d, _ in P.MUSIC_FORMATS.values() if (dest / d).is_dir()] - if len(have) == len(P.MUSIC_FORMATS): - say(f"музыка уже на месте: {dest} ({len(have)} набора)") - return - say(f"музыка неполная (наборы: {', '.join(have) or 'нет'}) — перекачиваю") - - url = music_url() - CACHE.mkdir(parents=True, exist_ok=True) - zpath = CACHE / os.path.basename(url) - fetch_url(url, zpath, expect_type="zip") - - with zipfile.ZipFile(zpath) as z: - names = z.namelist() - safe_members(names, P.ORIG) - top = {Path(n).parts[0] for n in names if n} - if top != {dest.name}: - sys.exit(f"в архиве музыки ожидался один каталог {dest.name}/, " - f"а там {sorted(top)} — чините fetch_orig.py") - tmp = P.ORIG / ".music.new" - if tmp.exists(): - shutil.rmtree(tmp) - say(f"распаковываю {len(names)} файлов") - z.extractall(tmp) - swap_dir(tmp / dest.name, dest) - shutil.rmtree(tmp, ignore_errors=True) - got = [d for d, _ in P.MUSIC_FORMATS.values() if (dest / d).is_dir()] - say(f"музыка: наборы {', '.join(sorted(got))} -> {dest}") - - -# --- отчёт ----------------------------------------------------------------- -def do_list(): - print("Внешние источники SprPoP (assets/orig/ в репозиторий не идёт):\n") - for name, path, where, what in SOURCES: - print(f" {name}") - print(f" куда: {path}") - print(f" откуда: {where}") - print(f" что: {what}\n") - - -def do_check(): - ok = True - st = read_stamp(P.SDLPOP) - if (P.SDLPOP / "data").is_dir() and (P.SDLPOP / "src").is_dir(): - v = f"{st.get('ref', '?')} {(st.get('sha') or '?')[:8]}" - print(f" SDLPoP есть ({v}, {st.get('fetched', 'версия неизвестна')})") - else: - print(" SDLPoP НЕТ -> make fetch-sdlpop") - ok = False - have = [d for d, _ in P.MUSIC_FORMATS.values() if (P.MUSIC_ROOT / d).is_dir()] - if len(have) == len(P.MUSIC_FORMATS): - print(f" музыка есть (наборы: {', '.join(sorted(have))})") - elif have: - print(f" музыка ЧАСТЬ (только {', '.join(sorted(have))}) -> make fetch-music") - ok = False - else: - print(" музыка НЕТ -> make fetch-music") - ok = False - if P.MSDOS.is_dir(): - print(" MSDOS есть (необязательно; звуки оттуда — make SND_SRC=msdos)") - else: - print(" MSDOS нет (необязательно: звуки и уровни берутся из SDLPoP)") - return 0 if ok else 1 - - -def main(): - ap = argparse.ArgumentParser( - description="скачать внешние исходные данные SprPoP в assets/orig/") - ap.add_argument("what", nargs="*", default=["all"], - help="что качать: all (умолчание), sdlpop, music") - ap.add_argument("--force", action="store_true", - help="качать заново, даже если данные на месте") - ap.add_argument("--ref", default=SDLPOP_REF, - help=f"ветка или тег SDLPoP (по умолчанию {SDLPOP_REF})") - ap.add_argument("--check", action="store_true", - help="только показать, что уже на месте") - ap.add_argument("--list", action="store_true", - help="только показать таблицу источников") - a = ap.parse_args() - - if a.list: - do_list() - return 0 - if a.check: - return do_check() - - what = set(a.what) or {"all"} - bad = what - {"all", "sdlpop", "music"} - if bad: - ap.error("не знаю, что такое %s; есть: all, sdlpop, music" - % ", ".join(sorted(bad))) - if "all" in what: - what = {"sdlpop", "music"} - P.ORIG.mkdir(parents=True, exist_ok=True) - if "sdlpop" in what: - fetch_sdlpop(a.ref, a.force) - if "music" in what: - fetch_music(a.force) - say("готово; дальше — make resources") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/applications/SprPoP/tools/paths.py b/applications/SprPoP/tools/paths.py deleted file mode 100644 index 7dee7b6..0000000 --- a/applications/SprPoP/tools/paths.py +++ /dev/null @@ -1,150 +0,0 @@ -"""paths.py — единственное место, где инструменты SprPoP знают раскладку -каталогов приложения. Все упаковщики берут пути отсюда, поэтому папку -SprPoP можно перенести куда угодно: важно лишь, что рядом с tools/ лежат -assets/, gen/ и build/. - -Раскладка: - - assets/orig/ ОРИГИНАЛЬНЫЕ данные (SDLPoP, записи музыки и, - необязательно, MSDOS). В репозиторий не идут — - копирайт; выкачивает их tools/fetch_orig.py - (`make fetch`), см. 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 - -# --- источники звуковых эффектов ------------------------------------------ -# Контейнеры digisnd1..3 есть в ДВУХ поставках, и они не одинаковы: в SDLPoP -# лежит версия из репозитория (её и качает fetch_orig.py), в MSDOS/ — из -# оригинального дистрибутива 1.0, если пользователь его себе положил. -# ПО УМОЛЧАНИЮ БЕРЁМ SDLPoP: сборка обязана работать без MSDOS вообще. -# Ключ = значение SND_SRC в Makefile; регистр имён в поставках разный. -SND_SOURCES = { - "sdlpop": (SDLPOP_DATA, "DIGISND%d.DAT"), - "msdos": (MSDOS, "digisnd%d.dat"), -} - - -# Ноты PC-СПИКЕРА и МЕЛОДИИ — два других набора того же движка. Нужны нам -# по разным причинам: мелодии мы играем отдельно (MUS/), а из нот спикера -# добираем те номера, которых нет НИ в оцифровке, НИ среди мелодий — сейчас -# это ровно звук 38 (мигание надписи «Press Button»). В поставке SDLPoP -# наборы лежат распакованными каталогами res1XXXX.bin, в дистрибутиве DOS — -# контейнерами .DAT; поддерживаем оба вида. -SND_MIDI = { - "sdlpop": (SDLPOP_DATA, "MIDISND%d.DAT", 2), - "msdos": (MSDOS, "midisnd%d.dat", 2), -} -SND_SPEAKER = { - "sdlpop": (SDLPOP_DATA, "IBM_SND%d", 2), - "msdos": (MSDOS, "ibm_snd%d.dat", 2), -} - - -def snd_extra(kind, name="sdlpop"): - """(каталог, шаблон, сколько контейнеров) для набора нот или мелодий. - - kind — "midi" или "speaker". Набора может не быть вовсе (у пользователя - нет MSDOS, поставка неполная) — это НЕ ошибка: вызывающий просто - пропускает добор. Возвращает None, если источник неизвестен. - """ - table = {"midi": SND_MIDI, "speaker": SND_SPEAKER}.get(kind) - if table is None: - raise SystemExit("неизвестный набор %r; есть: midi, speaker" % kind) - return table.get(name) - - -def snd_src(name="sdlpop"): - """(каталог, шаблон имени) контейнеров оцифровки для источника.""" - if name not in SND_SOURCES: - raise SystemExit( - "неизвестный источник звука %r; есть: %s" - % (name, ", ".join(sorted(SND_SOURCES)))) - return SND_SOURCES[name] - - -def find_ci(directory, name): - """Путь к файлу без оглядки на регистр (FAT-имена приезжают по-разному). - - Возвращает None, если файла нет. Точное совпадение проверяется первым, - чтобы на case-insensitive ФС не читать каталог зря. - """ - p = directory / name - if p.exists(): - return p - if not directory.is_dir(): - return None - low = name.lower() - for e in directory.iterdir(): - if e.name.lower() == low: - return e - return None - -# --- выход ---------------------------------------------------------------- -ATL = APP / "build" / "atl" # промежуточные атласы, по набору в папке -PACKED = APP / "assets" / "packed" # раскладка диска игры -GEN = APP / "gen" # генерируемые заголовки -DIST = APP / "dist" # ручное содержимое дистрибутива (README и - # производные наборы, напр. тихий звук) - - -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 - - -def dist(name): - """Каталог внутри dist/ в раскладке диска; создаётся при обращении.""" - p = DIST / 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 deleted file mode 100644 index 6f394ba..0000000 --- a/applications/SprPoP/tools/pop_extract_font.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/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 deleted file mode 100644 index 585fbc6..0000000 --- a/applications/SprPoP/tools/pop_extract_kid_data.py +++ /dev/null @@ -1,171 +0,0 @@ -#!/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_idx.py b/applications/SprPoP/tools/pop_idx.py deleted file mode 100644 index 212c227..0000000 --- a/applications/SprPoP/tools/pop_idx.py +++ /dev/null @@ -1,230 +0,0 @@ -"""pop_idx.py — ДИСКОВЫЕ ИНДЕКСЫ аудио-наборов SprPoP. - -Зачем. Раскладка звукового набора (какой эффект в какой странице, по -какому смещению и какой длины) сегодня печатается упаковщиком в -gen/pop_sound_tbl.h и становится частью EXE. Из-за этого замена набора -(SDLPoP <-> MSDOS) требует перекомпиляции игры. Индекс переносит эту -раскладку НА ДИСК: пара `SND/snd.idx` + `SND/snd.arc` меняется целиком, -а бинарник остаётся прежним. - -Формат читают ДВЕ стороны — этот упаковщик и загрузчик на Z80, — поэтому -писатель и разборщик живут в одном месте и покрыты тестом -(tools/tests/test_pop_idx.py). Числа little-endian: у Z80 это родной -порядок, и разбор на нём — просто чтение байтов подряд. - -PSI1 — индекс ЭФФЕКТОВ, ровно один сектор DSS (512 Б): - - 0..3 'PSI1' магия и версия - 4 57 число плотных записей, id 0..56 - 5 5 размер записи - 6 7 log2 размера блока: 1 << 7 = 128 - 7 1 формат PCM: 8 бит без знака, моно, CBL_FREQ_10K9 - 8 pages сколько PCM-страниц в снд-архиве (1..16) - 9..15 0 резерв - 16..300 57 записей по 5 байт: page (u8), off (u16), len (u16) - 301..511 0 добивка до сектора - -Размер записи 5 — это НЕ произвольное число, а sizeof(pop_snd_ent_t) в -ABI SDCC z80 (uint8 + uint16 + uint16 без выравнивания). Загрузчик -читает записи ПРЯМО в резидентную таблицу, поэтому расхождение здесь -молча разъедет всю раскладку; на стороне C это проверяется статически. - -Нулевая `len` означает, что у этого id нет оцифровки (это музыка). - -PMI1 — индекс МУЗЫКИ, тоже один сектор: - - 0..3 'PMI1' магия и версия - 4 57 число плотных записей, id 0..56 - 5 4 размер записи - 6 7 log2 размера блока - 7 1 формат PCM - 8..15 0 резерв - 16..243 57 записей по 4 байта: blocks (u16), ticks60 (u16) - 244..511 0 добивка до сектора - -Числа страниц здесь НЕТ намеренно: это производная от `blocks`, и -дублирование однажды разъедется с ним. Загрузчик считает сам: - - pages = blocks / 128 + ((blocks & 127) != 0) - -`ticks60` — длительность в тиках сцены (60 Гц оригинала), нужная там, где -сценарий ждёт конца реплики. Считается из blocks отношением 40/57 — -обратным к тому, которым шкала сцены переводит тики в порции насоса. -Хранится ОТДЕЛЬНО, а не выводится в рантайме: так момент окончания -реплики можно будет уточнить независимо от технического хвоста PCM. - -Нулевой `blocks` означает, что трека с таким id в наборе нет. -""" - -import struct - -SECTOR = 512 - -# --- PSI1: эффекты --------------------------------------------------------- -SND_MAGIC = b"PSI1" -SND_COUNT = 57 # пространство id оригинала: 0..56 -SND_REC = 5 # sizeof(pop_snd_ent_t) в ABI SDCC z80 -SND_HDR = 16 -SND_MAX_PAGES = 16 # compile-time вместимость загрузчика -BLOCK = 128 # блок запроса CBL при 8 бит -BLOCK_LOG2 = 7 -PCM_U8_MONO_10K9 = 1 # единственный формат, который умеет наш насос -PAGE = 16384 - - -class IdxError(Exception): - """Индекс не разобрался: битый, чужой версии или противоречивый.""" - - -def snd_pack(entries, pages): - """Собрать PSI1 из 57 записей (page, off, len) и числа страниц.""" - if len(entries) != SND_COUNT: - raise IdxError("записей должно быть %d, а не %d" - % (SND_COUNT, len(entries))) - if not 1 <= pages <= SND_MAX_PAGES: - raise IdxError("страниц %d — вне 1..%d" % (pages, SND_MAX_PAGES)) - - out = bytearray(SECTOR) - out[0:4] = SND_MAGIC - out[4] = SND_COUNT - out[5] = SND_REC - out[6] = BLOCK_LOG2 - out[7] = PCM_U8_MONO_10K9 - out[8] = pages - for i, (page, off, ln) in enumerate(entries): - _check_entry(i, page, off, ln, pages) - struct.pack_into(" (записи, pages). Проверки те же, что у Z80.""" - if len(data) != SECTOR: - raise IdxError("размер %d, а должен быть сектор %d" % (len(data), SECTOR)) - if data[0:4] != SND_MAGIC: - raise IdxError("не PSI1: %r" % (bytes(data[0:4]),)) - if data[4] != SND_COUNT: - raise IdxError("записей %d, ожидалось %d" % (data[4], SND_COUNT)) - if data[5] != SND_REC: - raise IdxError("размер записи %d, ожидался %d" % (data[5], SND_REC)) - if data[6] != BLOCK_LOG2: - raise IdxError("log2 блока %d, ожидался %d" % (data[6], BLOCK_LOG2)) - if data[7] != PCM_U8_MONO_10K9: - raise IdxError("формат PCM %d — насос умеет только %d" - % (data[7], PCM_U8_MONO_10K9)) - pages = data[8] - if not 1 <= pages <= SND_MAX_PAGES: - raise IdxError("страниц %d — вне 1..%d" % (pages, SND_MAX_PAGES)) - - entries = [] - for i in range(SND_COUNT): - page, off, ln = struct.unpack_from("= pages: - raise IdxError("запись %d: page %d >= pages %d" % (i, page, pages)) - if off >= PAGE: - raise IdxError("запись %d: off %d вне страницы" % (i, off)) - if off % BLOCK or ln % BLOCK: - raise IdxError("запись %d: off/len не кратны блоку %d" % (i, BLOCK)) - if page == 0 and off < BLOCK: - raise IdxError("запись %d залезает в блок тишины (стр.0, off 0)" % i) - # Длинный эффект переливается на следующие страницы — они обязаны быть. - last = (page * PAGE + off + ln - 1) // PAGE - if last >= pages: - raise IdxError("запись %d выходит за набор: кончается в стр.%d из %d" - % (i, last, pages)) - - -# --- PMI1: музыка ---------------------------------------------------------- -MUS_MAGIC = b"PMI1" -MUS_COUNT = 57 # то же пространство id оригинала -MUS_REC = 4 -MUS_HDR = 16 -# Потоковый трек (финальная тема) заведомо длиннее обычных, и предел ему -# ставят не страницы загрузчика, а восьмибитные счётчики страниц в коде. -MUS_MAX_PAGES = 255 - - -def mus_ticks(blocks): - """Длительность в тиках сцены (60 Гц) по длине в блоках. - - Отношение 40/57 — обратное к тому, которым шкала сцены переводит тики - в порции насоса (57/40, pop_intro.c). Берём именно его, а не точное - 10937,5/128/60: длина реплики и ход шкалы обязаны считаться ОДНОЙ - арифметикой, иначе они разъедутся на длинных треках. Округление к - ближайшему: отбрасывание вниз теряло по тику на реплику. - """ - return (blocks * 40 + 28) // 57 - - -def mus_pack(entries): - """Собрать PMI1 из 57 записей (blocks, ticks60).""" - if len(entries) != MUS_COUNT: - raise IdxError("записей должно быть %d, а не %d" - % (MUS_COUNT, len(entries))) - out = bytearray(SECTOR) - out[0:4] = MUS_MAGIC - out[4] = MUS_COUNT - out[5] = MUS_REC - out[6] = BLOCK_LOG2 - out[7] = PCM_U8_MONO_10K9 - for i, (blocks, ticks) in enumerate(entries): - _check_mus(i, blocks, ticks) - struct.pack_into(" записи. Проверки те же, что у Z80.""" - if len(data) != SECTOR: - raise IdxError("размер %d, а должен быть сектор %d" % (len(data), SECTOR)) - if data[0:4] != MUS_MAGIC: - raise IdxError("не PMI1: %r" % (bytes(data[0:4]),)) - if data[4] != MUS_COUNT: - raise IdxError("записей %d, ожидалось %d" % (data[4], MUS_COUNT)) - if data[5] != MUS_REC: - raise IdxError("размер записи %d, ожидался %d" % (data[5], MUS_REC)) - if data[6] != BLOCK_LOG2: - raise IdxError("log2 блока %d, ожидался %d" % (data[6], BLOCK_LOG2)) - if data[7] != PCM_U8_MONO_10K9: - raise IdxError("формат PCM %d — насос умеет только %d" - % (data[7], PCM_U8_MONO_10K9)) - entries = [] - for i in range(MUS_COUNT): - blocks, ticks = struct.unpack_from(" MUS_MAX_PAGES: - raise IdxError("запись %d: %d блоков — больше %d страниц, счётчики " - "страниц в коде восьмибитные" - % (i, blocks, MUS_MAX_PAGES)) - if ticks == 0: - raise IdxError("запись %d: трек есть, а длительность нулевая" % i) diff --git a/applications/SprPoP/tools/pop_pack_arc.py b/applications/SprPoP/tools/pop_pack_arc.py deleted file mode 100644 index fd12db3..0000000 --- a/applications/SprPoP/tools/pop_pack_arc.py +++ /dev/null @@ -1,123 +0,0 @@ -#!/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 deleted file mode 100644 index d94f75c..0000000 --- a/applications/SprPoP/tools/pop_pack_bg.py +++ /dev/null @@ -1,616 +0,0 @@ -#!/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.", - " * Сгенерировано tools/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", - "", - "/* Палитры пока остаются отдельными файлами рядом с bg.arc. Имена", - " * видит только модуль загрузки, чтобы не размножать строки. */", - "#ifdef POP_BG_ATLAS_NAMES", - 'static const char *const pop_tile_pal[POP_TILESETS] = { %s };' - % ", ".join('"%stile.pal"' % p for p, _ in SETS.values()), - "#endif /* POP_BG_ATLAS_NAMES */", - "/* Пузырёк зелья: красный набор = 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, - "", - "#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 deleted file mode 100644 index 33b4d99..0000000 --- a/applications/SprPoP/tools/pop_pack_guard.py +++ /dev/null @@ -1,216 +0,0 @@ -#!/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 deleted file mode 100644 index 7e78394..0000000 --- a/applications/SprPoP/tools/pop_pack_intro.py +++ /dev/null @@ -1,353 +0,0 @@ -#!/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 deleted file mode 100644 index 682b1a8..0000000 --- a/applications/SprPoP/tools/pop_pack_kid.py +++ /dev/null @@ -1,209 +0,0 @@ -#!/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_arc.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")) - - 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 deleted file mode 100644 index 09920fa..0000000 --- a/applications/SprPoP/tools/pop_pack_music.py +++ /dev/null @@ -1,144 +0,0 @@ -#!/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 байт, и остаток «не полного блока» ему пришлось бы -досчитывать в прерывании. - -Длина трека уходит в MUS/mus.idx блоками по 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) -import pop_idx # формат дискового индекса PMI1 (tools/pop_idx.py) - -# Формат источника выбирается ключом --format (по умолчанию flac — он же -# лучший по качеству исходник). Каталог и расширение знает tools/paths.py. -FMT = "flac" -SRC_DIR, SRC_EXT = P.music_src(FMT) -OUT_DIR = P.atl("music") -OUT_IDX = "mus.idx" # дисковый индекс набора, ложится в MUS/ рядом с треками - -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} с") - - # ДИСКОВЫЙ ИНДЕКС набора: длины треков и длительности реплик. Пока - # печатаются и заголовки выше — переход на runtime идёт по шагам, и - # индекс обязан описывать ровно то же, что они (docs/music_runtime_index_plan.md). - # Тики считает pop_idx.mus_ticks — общая формула для обеих сторон. - if len(rows) != len(TRACKS): - print(f" ВНИМАНИЕ: упаковано {len(rows)} треков из {len(TRACKS)} — " - f"индекс описывает только их") - entries = [(0, 0)] * pop_idx.MUS_COUNT - for tid, _name, _chunks, blocks in rows: - entries[tid] = (blocks, pop_idx.mus_ticks(blocks)) - (OUT_DIR / OUT_IDX).write_bytes(pop_idx.mus_pack(entries)) - print(f"индекс: {OUT_DIR / OUT_IDX}") - print(f"итого {total / 1024:.0f} КБ в {OUT_DIR}") - - -if __name__ == "__main__": - main() diff --git a/applications/SprPoP/tools/pop_pack_shadow.py b/applications/SprPoP/tools/pop_pack_shadow.py deleted file mode 100644 index 968cb4b..0000000 --- a/applications/SprPoP/tools/pop_pack_shadow.py +++ /dev/null @@ -1,284 +0,0 @@ -#!/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 — раскладка атласа ТЕНИ.", - " * СГЕНЕРЕНО tools/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"], - "", - "/* 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 deleted file mode 100644 index c91a1fa..0000000 --- a/applications/SprPoP/tools/pop_pack_sound.py +++ /dev/null @@ -1,311 +0,0 @@ -#!/usr/bin/env python3 -""" -pop_pack_sound.py — упаковщик ЗВУКОВЫХ ЭФФЕКТОВ (оцифровка, chtab нет). - -ИСТОЧНИК. Контейнеры `digisnd1..3` — по умолчанию из `SDLPoP/data/` -(`--source sdlpop`), потому что SDLPoP скачивается автоматически и без него -сборки всё равно нет. У кого лежит оригинальный дистрибутив DOS, может -взять оцифровку оттуда: `--source msdos` (= `make SND_SRC=msdos`); наборы -НЕ одинаковы — версии данных разные. Разбор: -индекс по 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 Б) + snd.idx — -дисковая раскладка набора (формат PSI1, tools/pop_idx.py). C-заголовка -БОЛЬШЕ НЕТ: раскладка уехала на диск, чтобы замена набора не требовала -перекомпиляции игры. -""" -import argparse -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) -import pop_idx # формат дискового индекса PSI1 (tools/pop_idx.py) - -HERE = os.path.dirname(os.path.abspath(__file__)) -OUT_DIR = str(P.atl("sound")) -IDX = "snd.idx" # дисковый индекс набора, рядом с snd.arc - -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 load_bundle(dat_dir, pattern, count): - """id -> тело ресурса из набора любого вида. - - Наборы приезжают в двух видах: контейнером `.DAT` (дистрибутив DOS) и - распакованным каталогом `res1XXXX.bin` (поставка SDLPoP). Тело в обоих - случаях начинается с байта типа, так что дальше разбор общий. - """ - out = {} - for i in range(1, count + 1): - p = dat_dir / (pattern % i) - if p.is_dir(): - for f in sorted(p.glob("res1*.bin")): - try: - rid = int(f.stem[3:]) - 10000 - except ValueError: - continue - out[rid] = f.read_bytes() - elif p.is_file(): - out.update(dat_resources(p)) - return out - - -def speaker(body): - """(темп, [(частота Гц, длина в долях), ...]) или None — если это не ноты. - - Формат — канон Princed, «Internal PC Speaker»: заголовок 3 байта, из них - байт 1 — доли на ДВЕ секунды; дальше тройки «частота (2 байта) + длина - (1 байт)»; нулевая частота = пауза, 1 и 2 — маркеры; в конце маркер - `12 00`, который сам отсекается недобором до целой тройки. - """ - if not body or (body[0] & 7) != 0 or len(body) < 6: - return None - bps = body[1] or 1 - notes = [] - i = 3 - while i + 3 <= len(body): - notes.append((struct.unpack(" те же 8 бит без знака, что и оцифровка. - - Амплитуда НАМЕРЕННО маленькая: сигналы спикера короткие и резкие, и на - полном размахе они колют ухо рядом с оцифровкой, которая заметно тише. - Значение снижено вдвое по слуховой проверке в MAME (решение пользователя - 2026-08-31): на 44 сигнал мигания перекрикивал игру. - """ - out = bytearray() - for freq, beats in notes: - n = int(round(beats * (2.0 / bps) * rate)) - if n <= 0: - continue - if freq <= 2: # 0 — пауза, 1 и 2 — маркеры - out += bytes([0x80]) * n - continue - period = rate / freq - for i in range(n): - out.append(0x80 + amp if (i % period) < period / 2 else 0x80 - amp) - return bytes(out) - - -def main(): - ap = argparse.ArgumentParser(description=__doc__.splitlines()[1]) - ap.add_argument("--source", default="sdlpop", choices=sorted(P.SND_SOURCES), - help="откуда брать digisnd1..3 (по умолчанию sdlpop)") - ap.add_argument("--order", default="id", choices=("id", "reverse"), - help="порядок укладки эффектов в страницах: id (штатный) " - "или reverse — ОТЛАДОЧНЫЙ, для проверки того, что " - "игра берёт раскладку из snd.idx, а не из своей " - "памяти: смещения расходятся у ВСЕХ эффектов") - args = ap.parse_args() - - # Имена контейнеров в поставках отличаются регистром, и на Linux это не - # мелочь: ищем без оглядки на него (P.find_ci). - dat_dir, pattern = P.snd_src(args.source) - res = {} - for i in (1, 2, 3): - p = P.find_ci(dat_dir, pattern % i) - if p is None: - sys.exit(f"нет файла {dat_dir / (pattern % i)} " - f"(источник {args.source}); см. assets/orig/README.md") - res.update(dat_resources(p)) - print(f" источник оцифровки: {args.source} ({dat_dir})") - # У набора SDLPoP оцифровка беднее: `spiked` (48) там длиной 7 сэмплов, - # то есть звука пик фактически нет (разбор — docs/sound_plan.md). Если - # у пользователя лежит дистрибутив DOS, сказать об этом стоит вслух — - # молча отдавать тишину вместо эффекта нельзя. - if args.source == "sdlpop" and P.MSDOS.is_dir(): - print(" ВНИМАНИЕ: у вас есть assets/orig/MSDOS — там оцифровка полнее" - " (в наборе SDLPoP звук 48 `spiked` пустой).") - print(" собрать с ней: make SND_SRC=msdos") - - 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)} сэмплов") - - # ДОБОР ИЗ НОТ PC-СПИКЕРА (docs/TASKS_OPEN.md, SND-SPEAKER-38). - # У оригинала три параллельных набора звука, и номера разложены по ним - # не подряд: оцифровка, мелодии и ноты спикера. Берём из нот ТОЛЬКО те - # номера, которых нет ни там, ни там — иначе синтез перекрыл бы собой - # мелодии, которые мы играем отдельно, из MUS/. На поставке SDLPoP это - # ровно один номер: 38, сигнал под мигание надписи «Press Button» - # (31, 34 и 42 в наборе есть, но они пустые заглушки без нот). - midi_cfg = P.snd_extra("midi", args.source) - spk_cfg = P.snd_extra("speaker", args.source) - midi_ids = set(load_bundle(*midi_cfg)) if midi_cfg else set() - added = [] - for sid, body in sorted((load_bundle(*spk_cfg) if spk_cfg else {}).items()): - if sid in sounds or sid in midi_ids: - continue - sp = speaker(body) - if sp is None or not sp[1]: - continue - pcm = speaker_pcm(sp[0], sp[1], RATE) - if not pcm: - continue - sounds[sid] = pcm - added.append("%d (%d нот, %d Б)" % (sid, len(sp[1]), len(pcm))) - if added: - print(" из нот спикера: " + ", ".join(added)) - elif spk_cfg: - print(" из нот спикера: добирать нечего") - - # Раскладка: начало каждого звука кратно BLOCK, страницы по PAGE. - # ПЕРВЫЙ БЛОК — ТИШИНА. Он нужен насосу: когда эффект кончился, в CBL - # всё равно надо что-то отдавать, иначе железо доигрывает по кругу хвост - # своего буфера (жужжание). Держим тишину СВОЮ, а не просим у libc - # (CBL_UNDERRUN_SILENCE делает malloc), потому что куча в резиденте W2 - # тесная и её отказ ронял бы весь звук. - blob = bytearray([0x80] * BLOCK) - table = {} - # ПОРЯДОК УКЛАДКИ. Штатно — по возрастанию id; `--order reverse` кладёт - # те же эффекты задом наперёд. Данные и таблица при этом остаются - # согласованными (индекс печатается из table), но НИ ОДНО смещение не - # совпадает со штатным набором — только так и проверяется, что игра - # читает раскладку с диска, а не помнит её с компиляции: у двух - # реальных наборов совпадают 23 записи из 31, и подмена на них видна - # слабо. - order = sorted(sounds, reverse=(args.order == "reverse")) - for sid in order: - 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) - # Старые страницы СНОСИМ: у разных источников их разное число (SDLPoP 9, - # MSDOS 10), и лишняя s9.bin от прошлого прогона уехала бы в архив - # хвостом, которого нет в таблице. - for old in os.listdir(OUT_DIR): - if old.startswith("s") and old.endswith(".bin"): - os.remove(os.path.join(OUT_DIR, old)) - 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]) - - # ДИСКОВЫЙ ИНДЕКС. Та же раскладка, что и в заголовке ниже, но на - # диске: с ним пара snd.idx + snd.arc заменяется целиком, без - # перекомпиляции игры (docs/music_runtime_index_plan.md, часть II). - # Проверки внутри snd_pack — ровно те, что делает загрузчик на Z80: - # набор, который не пройдёт здесь, не пройдёт и там. - if npages > pop_idx.SND_MAX_PAGES: - sys.exit("набор занял %d страниц, а загрузчик берёт не больше %d" - % (npages, pop_idx.SND_MAX_PAGES)) - entries = [table.get(sid, (0, 0)) for sid in range(NSOUND)] - entries = [(off >> 14, off & 0x3FFF, ln) for off, ln in entries] - with open(os.path.join(OUT_DIR, IDX), "wb") as f: - f.write(pop_idx.snd_pack(entries, npages)) - print(" индекс -> %s (%d страниц)" % (os.path.join(OUT_DIR, IDX), - npages)) - - have = sum(1 for s in sounds.values() if s) - print(f"ИТОГО: {have} эффектов, {len(blob)} Б = {npages} EMM-страниц -> {OUT_DIR}") - - -if __name__ == "__main__": - main() diff --git a/applications/SprPoP/tools/pop_pack_title.py b/applications/SprPoP/tools/pop_pack_title.py deleted file mode 100644 index 5c1852b..0000000 --- a/applications/SprPoP/tools/pop_pack_title.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/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/pop_quiet.py b/applications/SprPoP/tools/pop_quiet.py deleted file mode 100755 index 0adf26b..0000000 --- a/applications/SprPoP/tools/pop_quiet.py +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env python3 -"""pop_quiet.py — тише в N раз: пережать ГОТОВЫЕ наборы звука и музыки. - -Берёт то, что уже лежит в assets/packed (SND/snd.arc + MUS/m*.bin), делает -тише и кладёт в dist/ в той же раскладке диска, ПЕРЕЗАПИСЫВАЯ то, что там -было. Оригиналы в assets/packed не трогаются никогда — значит скрипт -идемпотентен: сколько раз ни запусти, громкость упадёт ровно один раз. - -ФОРМУЛА. PCM у нас 8 бит БЕЗ ЗНАКА, тишина = 0x80 (см. cbl.h и -docs/sound_plan.md), то есть байт — это знаковое отклонение от 0x80. -Тише вдвое: - - out = 0x80 + (in - 0x80) / 2 = in / 2 + 0x40 - -Правая форма — та самая, что делается на Z80 двумя командами -(`srl a` / `add a,#0x40`), поэтому таблицу строим floor-делением: тогда -результат совпадает с ней БАЙТ В БАЙТ и его можно сверять с железной -реализацией. Тишина остаётся тишиной (0x80 -> 0x80) при любом gain, а -клиппинга не бывает по построению: |отклонение| только уменьшается. - -ЧТО ИМЕННО ПЕРЕСЧИТЫВАЕТСЯ. - - MUS/m*.bin — файл целиком: это чистый PCM без заголовка, загрузчик - читает его страницами с нулевого смещения (pop_music.c). - SND/snd.arc — ТОЛЬКО тела записей архива PBA1. Заголовок (512 Б) и - выравнивающий хвост между записями остаются как есть: - там нули упаковщика, а не звук, и пересчёт превратил бы - их в 0x40 — мусор на месте служебных байт. - *.idx — копируются без изменений: длины и смещения не поменялись, - меняются только сами отсчёты. Магию всё же проверяем, - чтобы молча не скопировать файл чужого формата. - -Запуск: - - python3 tools/pop_quiet.py # вдвое тише, packed -> dist - python3 tools/pop_quiet.py --gain 0.25 # вчетверо тише - python3 tools/pop_quiet.py --src DIR --out DIR -""" -import argparse -import shutil -import struct -import sys -from pathlib import Path - -sys.path.insert(0, str(Path(__file__).resolve().parent)) -import paths - -SILENCE = 0x80 # центр 8-битного беззнакового PCM -ARC_MAGIC = b"PBA1" # архив ресурсов, см. src/pop_arc.h -ARC_HEADER = 512 -ARC_SECTOR = 512 -SND_IDX_MAGIC = b"PSI1" -MUS_IDX_MAGIC = b"PMI1" - - -def gain_table(gain: float) -> bytes: - """Таблица перекодировки байта: 256 значений, готовые для bytes.translate. - - floor, а не округление к ближайшему: при gain = 1/2 это ровно - `in >> 1 + 0x40`, то есть побайтово то же, что сделал бы Z80. - """ - if not 0.0 <= gain <= 1.0: - raise SystemExit("gain обязан лежать в [0, 1]; громче делать нечем — " - "запас по амплитуде в исходнике уже выбран") - out = bytearray(256) - for v in range(256): - dev = v - SILENCE # знаковое отклонение - out[v] = max(0, min(255, SILENCE + _floor_div(dev, gain))) - return bytes(out) - - -def _floor_div(dev: int, gain: float) -> int: - """dev * gain, округлённое ВНИЗ (к минус бесконечности).""" - scaled = dev * gain - i = int(scaled) - return i - 1 if scaled < i else i # int() режет к нулю - - -def quiet_raw(src: Path, dst: Path, table: bytes) -> int: - """Файл целиком — PCM (музыкальный трек). Возвращает размер.""" - data = src.read_bytes() - dst.write_bytes(data.translate(table)) - return len(data) - - -def quiet_arc(src: Path, dst: Path, table: bytes) -> int: - """Архив PBA1: пересчитываем только тела записей.""" - raw = bytearray(src.read_bytes()) - if raw[0:4] != ARC_MAGIC: - raise SystemExit(f"{src}: не {ARC_MAGIC.decode()} — это не наш архив") - count = raw[4] - if not 1 <= count <= (ARC_HEADER - 8) // 4: - raise SystemExit(f"{src}: count = {count}, вне 1..126") - for i in range(count): - off_sec, size = struct.unpack_from(" len(raw): - raise SystemExit(f"{src}: запись {i} выходит за файл " - f"({off} + {size} > {len(raw)})") - raw[off:off + size] = bytes(raw[off:off + size]).translate(table) - dst.write_bytes(bytes(raw)) - return count - - -def copy_idx(src: Path, dst: Path, magic: bytes) -> None: - """Индекс переносим как есть — но убеждаемся, что он наш.""" - head = src.read_bytes()[:4] - if head != magic: - raise SystemExit(f"{src}: магия {head!r}, ожидалась {magic!r}") - shutil.copyfile(src, dst) - - -def peak(data: bytes) -> int: - """Максимальное отклонение от тишины — для отчёта «стало тише».""" - return max(abs(b - SILENCE) for b in data) if data else 0 - - -def main() -> int: - ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) - ap.add_argument("--gain", type=float, default=0.5, - help="множитель амплитуды (по умолчанию 0.5 — вдвое тише)") - ap.add_argument("--src", type=Path, default=paths.PACKED, - help="откуда брать готовые наборы (умолчание assets/packed)") - ap.add_argument("--out", type=Path, default=paths.DIST, - help="куда класть (умолчание dist/)") - args = ap.parse_args() - - table = gain_table(args.gain) - src_snd, src_mus = args.src / "SND", args.src / "MUS" - out_snd, out_mus = args.out / "SND", args.out / "MUS" - if not src_snd.is_dir() or not src_mus.is_dir(): - raise SystemExit(f"{args.src}: нет SND/ или MUS/ — сначала " - "`make resources`") - out_snd.mkdir(parents=True, exist_ok=True) - out_mus.mkdir(parents=True, exist_ok=True) - - print(f"тише в {1 / args.gain:g} раз(а): {args.src} -> {args.out}") - - arc = src_snd / "snd.arc" - before = peak(arc.read_bytes()[ARC_HEADER:]) - n = quiet_arc(arc, out_snd / "snd.arc", table) - after = peak((out_snd / "snd.arc").read_bytes()[ARC_HEADER:]) - copy_idx(src_snd / "snd.idx", out_snd / "snd.idx", SND_IDX_MAGIC) - print(f" SND/snd.arc {n} записей, пик {before} -> {after}") - - tracks = sorted(src_mus.glob("m*.bin")) - if not tracks: - raise SystemExit(f"{src_mus}: нет ни одного m*.bin") - total = 0 - worst_before = worst_after = 0 - for t in tracks: - total += quiet_raw(t, out_mus / t.name, table) - worst_before = max(worst_before, peak(t.read_bytes())) - worst_after = max(worst_after, peak((out_mus / t.name).read_bytes())) - copy_idx(src_mus / "mus.idx", out_mus / "mus.idx", MUS_IDX_MAGIC) - print(f" MUS/ {len(tracks)} треков, {total // 1024} КБ, " - f"пик {worst_before} -> {worst_after}") - print("индексы (snd.idx, mus.idx) скопированы без изменений — длины те же") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/applications/SprPoP/tools/render_room.py b/applications/SprPoP/tools/render_room.py deleted file mode 100755 index 6d4ca85..0000000 --- a/applications/SprPoP/tools/render_room.py +++ /dev/null @@ -1,853 +0,0 @@ -#!/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() diff --git a/applications/SprPoP/tools/tests/test_pop_idx.py b/applications/SprPoP/tools/tests/test_pop_idx.py deleted file mode 100644 index 6650421..0000000 --- a/applications/SprPoP/tools/tests/test_pop_idx.py +++ /dev/null @@ -1,244 +0,0 @@ -"""test_pop_idx — формат дисковых индексов аудио (tools/pop_idx.py). - -Индекс читают ДВЕ стороны: упаковщик на хосте и загрузчик на Z80. Тест -закрепляет ровно то, на что рассчитывает вторая сторона: точные смещения -полей, little-endian, размер сектора и НАБОР ОТКАЗОВ — потому что битый -индекс обязан выключить звук, а не увести насос в чужую память. - -Запуск: make test-tools (либо python3 -m unittest discover tools/tests) -""" -import os -import struct -import sys -import unittest - -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -import pop_idx as X - - -def empty(): - return [(0, 0, 0)] * X.SND_COUNT - - -class SndFormat(unittest.TestCase): - """PSI1: раскладка байтов, на которую смотрит Z80.""" - - def test_size_is_one_sector(self): - self.assertEqual(len(X.snd_pack(empty(), 1)), X.SECTOR) - - def test_header_fields(self): - d = X.snd_pack(empty(), 9) - self.assertEqual(d[0:4], b"PSI1") - self.assertEqual(d[4], 57) # число записей - self.assertEqual(d[5], 5) # sizeof(pop_snd_ent_t) в SDCC z80 - self.assertEqual(d[6], 7) # log2(128) - self.assertEqual(d[7], 1) # u8 mono 10937.5 - self.assertEqual(d[8], 9) # фактических страниц - self.assertEqual(d[9:16], b"\0" * 7) - - def test_entry_is_little_endian_at_fixed_offset(self): - """Смещение записи и порядок байтов — контракт с загрузчиком.""" - e = empty() - e[3] = (2, 0x1500, 0x0480) - d = X.snd_pack(e, 9) - at = X.SND_HDR + 3 * X.SND_REC - self.assertEqual(d[at], 2) - self.assertEqual(d[at + 1:at + 3], b"\x00\x15") # off LE - self.assertEqual(d[at + 3:at + 5], b"\x80\x04") # len LE - self.assertEqual(struct.unpack_from("> 1) + 0x40, f"байт {v:#04x}") - - def test_silence_stays_silence(self): - for gain in (0.5, 0.25, 0.1, 1.0): - self.assertEqual(X.gain_table(gain)[X.SILENCE], X.SILENCE) - - def test_no_clipping_and_range_shrinks(self): - t = X.gain_table(0.5) - self.assertEqual(t[0x00], 0x40) - self.assertEqual(t[0xFF], 0xBF) - for v in range(256): - self.assertLessEqual(abs(t[v] - X.SILENCE), abs(v - X.SILENCE)) - - def test_gain_one_is_identity(self): - self.assertEqual(X.gain_table(1.0), bytes(range(256))) - - def test_gain_out_of_range_rejected(self): - for bad in (-0.1, 1.5): - with self.assertRaises(SystemExit): - X.gain_table(bad) - - -def make_arc(bodies): - """Собрать PBA1 ровно так, как это делает tools/pop_pack_arc.py.""" - raw = bytearray(X.ARC_HEADER) - raw[0:4] = X.ARC_MAGIC - raw[4] = len(bodies) - blob = bytearray() - for i, body in enumerate(bodies): - off = X.ARC_HEADER + len(blob) - struct.pack_into("/. -/tests/*/*.exe - -# Клон исторических исходников VC служит локальным справочным материалом и -# сохраняет собственный .git; во внешний репозиторий его как gitlink не кладём. -/VolkovCommander/ - -# Собственный выход Commander и локальные пути к SDK/MAME. -local.mk -build/ -artifacts/*-local/ -.sprinter-cc-*/ -.resource-stamps/ -*.o -*.obj -*.dSYM/ -__pycache__/ -*.py[cod] -.DS_Store -._* -.vscode/ diff --git a/applications/Volkov/.python-version b/applications/Volkov/.python-version deleted file mode 100644 index e4fba21..0000000 --- a/applications/Volkov/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.12 diff --git a/applications/Volkov/LICENSE b/applications/Volkov/LICENSE deleted file mode 100644 index a738358..0000000 --- a/applications/Volkov/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Sprinter C Compiler contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/applications/Volkov/Makefile b/applications/Volkov/Makefile deleted file mode 100644 index fe4263d..0000000 --- a/applications/Volkov/Makefile +++ /dev/null @@ -1,221 +0,0 @@ -# Sprinter Commander — двухпанельный файловый менеджер. - -EXAMPLE := sprcmd -SRC_DIR := src -BUILD_DIR := build -MEMORY := big -ALLOCS ?= 3000 -HDD_IMG := $(CURDIR)/build/hdd/sprcmd.chd -MDVIEW2_DIR := $(CURDIR)/tests/fixtures/mdview2 -MDVIEW2_SRCS := $(wildcard $(MDVIEW2_DIR)/*.c) -MDVIEW2_HEADERS := $(wildcard $(MDVIEW2_DIR)/*.h) -MDVIEW2_TEST_EXE := $(CURDIR)/build/external/mdview2.exe -P5_COPY_FAULT_EXE := $(CURDIR)/build/tests/cperr.exe -P6_SORT_TEST_EXE := $(CURDIR)/build/tests/sortrt.exe -P7_SELECT_TEST_EXE := $(CURDIR)/build/tests/selrt.exe -P9_MKDIR_TEST_EXE := $(CURDIR)/build/tests/mkrt.exe -P10_RENAME_TEST_EXE := $(CURDIR)/build/tests/renrt.exe -P11_DELETE_TEST_EXE := $(CURDIR)/build/tests/delrt.exe -P19_OVERWRITE_TEST_EXE := $(CURDIR)/build/tests/p19/delrt.exe -BANK1_SRCS := src/sc_source_fs.c src/sc_sort.c src/sc_select.c \ - src/sc_copy_job.c src/sc_metadata.c src/sc_disk.c src/sc_tree.c src/sc_exec_ui.c -BANK2_SRCS := src/sc_draw.c src/sc_dialog.c src/sc_copy_ui.c \ - src/sc_file_ui.c src/sc_name.c src/sc_rename.c -EXTRA_FLAGS := --safe --max-allocs $(ALLOCS) -I include \ - $(foreach f,$(BANK1_SRCS),--bank 1=$(f)) \ - $(foreach f,$(BANK2_SRCS),--bank 2=$(f)) -EXTRA_SRCS := \ - src/sc_app.c \ - src/sc_command.c \ - src/sc_input.c \ - src/sc_path.c \ - src/sc_panel.c \ - src/sc_platform_screen.c \ - src/sc_platform_exec.c \ - src/sc_screen.c \ - src/sc_store.c \ - src/sc_theme.c \ - src/sc_video_system.c - -include $(CURDIR)/sdk.mk - -HEADERS := $(wildcard include/*.h) - -$(EXE): Makefile $(HEADERS) $(BANK1_SRCS) $(BANK2_SRCS) - -hdd-stress: $(EXE) - sh tests/make_p3_stress_hdd.sh $(CURDIR)/build/hdd/p3_stress.chd $(EXE) - -hdd-copy: $(EXE) - sh tests/make_p4_copy_hdd.sh $(CURDIR)/build/hdd/p4_copy.chd $(EXE) - -tests/p4_exec_child/p4child.exe: tests/p4_exec_child/p4child.c \ - tests/p4_exec_child/Makefile - $(MAKE) -C tests/p4_exec_child ALLOCS=$(ALLOCS) - -hdd-exec: $(EXE) tests/p4_exec_child/p4child.exe - sh tests/make_p4_exec_hdd.sh $(CURDIR)/build/hdd/p4_exec.chd \ - $(EXE) tests/p4_exec_child/p4child.exe - -hdd-p5: $(EXE) - sh tests/make_p5_hdd.sh $(CURDIR)/build/hdd/p5_stability.chd $(EXE) - -hdd-p5-enospc: $(EXE) - sh tests/make_p5_enospc_hdd.sh \ - $(CURDIR)/build/hdd/p5_enospc.chd $(EXE) - -hdd-p5-panels: $(EXE) - sh tests/make_p5_panel_edges_hdd.sh \ - $(CURDIR)/build/hdd/p5_panel_edges.chd $(EXE) - -$(MDVIEW2_TEST_EXE): $(MDVIEW2_SRCS) $(MDVIEW2_HEADERS) $(LIB) - mkdir -p $(dir $@) - $(PROJ_ROOT)/bin/sprinter-cc --memory small --max-allocs $(ALLOCS) \ - -o $@ $(MDVIEW2_SRCS) - -hdd-p5-viewer: $(EXE) $(MDVIEW2_TEST_EXE) - sh tests/make_p5_viewer_hdd.sh \ - $(CURDIR)/build/hdd/p5_viewer.chd $(EXE) \ - $(MDVIEW2_TEST_EXE) $(MDVIEW2_DIR)/README.md - -$(P5_COPY_FAULT_EXE): tests/p5_copy_faults.c src/sc_copy_job.c \ - src/sc_metadata.c src/sc_disk.c \ - src/sc_path.c src/sc_platform_exec.c include/sc_copy.h \ - include/sc_metadata.h include/sc_disk.h include/sc_path.h \ - include/sc_exec.h $(LIB) - mkdir -p $(dir $@) - $(PROJ_ROOT)/bin/sprinter-cc --memory big --safe --max-allocs $(ALLOCS) \ - -I include --bank 1=src/sc_copy_job.c --bank 1=src/sc_metadata.c \ - --bank 1=src/sc_disk.c -o $@ \ - tests/p5_copy_faults.c src/sc_path.c src/sc_platform_exec.c - -hdd-p5-copy-faults: $(P5_COPY_FAULT_EXE) - sh tests/make_p5_copy_faults_hdd.sh \ - $(CURDIR)/build/hdd/p5_copy_faults.chd $(P5_COPY_FAULT_EXE) - -hdd-p5-media: $(EXE) - sh tests/make_p5_media_hdd.sh \ - $(CURDIR)/build/hdd/p5_media.chd $(EXE) - -$(P6_SORT_TEST_EXE): tests/p6_sort_probe.c src/sc_sort.c src/sc_store.c \ - include/sc_entry.h include/sc_panel.h include/sc_sort.h \ - include/sc_store.h $(LIB) - mkdir -p $(dir $@) - $(PROJ_ROOT)/bin/sprinter-cc --memory big --safe --max-allocs $(ALLOCS) \ - -I include --bank 1=src/sc_sort.c -o $@ \ - tests/p6_sort_probe.c src/sc_store.c - -hdd-p6-sort: $(EXE) $(P6_SORT_TEST_EXE) - sh tests/make_p6_sort_hdd.sh $(CURDIR)/build/hdd/p6_sort.chd \ - $(EXE) $(P6_SORT_TEST_EXE) - -$(P7_SELECT_TEST_EXE): tests/p7_select_probe.c src/sc_select.c \ - src/sc_store.c include/sc_entry.h include/sc_panel.h \ - include/sc_select.h include/sc_store.h $(LIB) - mkdir -p $(dir $@) - $(PROJ_ROOT)/bin/sprinter-cc --memory big --safe --max-allocs $(ALLOCS) \ - -I include --bank 1=src/sc_select.c -o $@ \ - tests/p7_select_probe.c src/sc_store.c - -hdd-p7-select: $(EXE) $(P7_SELECT_TEST_EXE) - sh tests/make_p7_select_hdd.sh $(CURDIR)/build/hdd/p7_select.chd \ - $(EXE) $(P7_SELECT_TEST_EXE) - -hdd-p8-masks: hdd-p7-select - -$(P9_MKDIR_TEST_EXE): tests/p9_mkdir_probe.c src/sc_name.c src/sc_path.c \ - include/sc_path.h $(LIB) - mkdir -p $(dir $@) - $(PROJ_ROOT)/bin/sprinter-cc --memory big --safe --max-allocs $(ALLOCS) \ - -I include --bank 1=src/sc_name.c -o $@ \ - tests/p9_mkdir_probe.c src/sc_path.c - -hdd-p9-mkdir: $(EXE) $(P9_MKDIR_TEST_EXE) - sh tests/make_p9_mkdir_hdd.sh $(CURDIR)/build/hdd/p9_mkdir.chd \ - $(EXE) $(P9_MKDIR_TEST_EXE) - -$(P10_RENAME_TEST_EXE): tests/p10_rename_probe.c src/sc_rename.c \ - include/sc_file_ops.h $(LIB) - mkdir -p $(dir $@) - $(PROJ_ROOT)/bin/sprinter-cc --memory big --safe --max-allocs $(ALLOCS) \ - -I include --bank 1=src/sc_rename.c -o $@ \ - tests/p10_rename_probe.c - -hdd-p10-rename: $(EXE) $(P10_RENAME_TEST_EXE) - sh tests/make_p10_rename_hdd.sh $(CURDIR)/build/hdd/p10_rename.chd \ - $(EXE) $(P10_RENAME_TEST_EXE) - -$(P11_DELETE_TEST_EXE): tests/p11_delete_probe.c $(LIB) - mkdir -p $(dir $@) - $(PROJ_ROOT)/bin/sprinter-cc --memory big --safe --max-allocs $(ALLOCS) \ - -o $@ tests/p11_delete_probe.c - -hdd-p11-delete: $(EXE) $(P11_DELETE_TEST_EXE) - sh tests/make_p11_delete_hdd.sh $(CURDIR)/build/hdd/p11_delete.chd \ - $(EXE) $(P11_DELETE_TEST_EXE) - -hdd-p12-group-copy: $(EXE) - sh tests/make_p12_group_copy_hdd.sh \ - $(CURDIR)/build/hdd/p12_group_copy.chd $(EXE) - -hdd-p13-group-delete: $(EXE) - sh tests/make_p13_group_delete_hdd.sh \ - $(CURDIR)/build/hdd/p13_group_delete.chd $(EXE) - -hdd-p14-default-colors: $(EXE) - sh tests/make_p14_default_colors_hdd.sh \ - $(CURDIR)/build/hdd/p14_default_colors.chd $(EXE) - -hdd-p15-tree-copy: $(EXE) - sh tests/make_p15_tree_copy_hdd.sh \ - $(CURDIR)/build/hdd/p15_tree_copy.chd $(EXE) - -hdd-p15-tree-cancel: $(EXE) - sh tests/make_p15_tree_cancel_hdd.sh \ - $(CURDIR)/build/hdd/p15_tree_cancel.chd $(EXE) - -hdd-p15-tree-self: $(EXE) - sh tests/make_p15_tree_self_hdd.sh \ - $(CURDIR)/build/hdd/p15_tree_self.chd $(EXE) - -hdd-p16-group-tree-copy: $(EXE) - sh tests/make_p16_group_tree_copy_hdd.sh \ - $(CURDIR)/build/hdd/p16_group_tree_copy.chd $(EXE) - -hdd-p17-tree-delete: $(EXE) - sh tests/make_p17_tree_delete_hdd.sh \ - $(CURDIR)/build/hdd/p17_tree_delete.chd $(EXE) - -hdd-p17-tree-delete-cancel: $(EXE) - sh tests/make_p17_tree_delete_cancel_hdd.sh \ - $(CURDIR)/build/hdd/p17_tree_delete_cancel.chd $(EXE) - -hdd-p18-metadata: $(EXE) - sh tests/make_p18_metadata_hdd.sh \ - $(CURDIR)/build/hdd/p18_metadata.chd $(EXE) - -$(P19_OVERWRITE_TEST_EXE): tests/p19_overwrite_probe.c \ - src/sc_copy_job.c src/sc_metadata.c src/sc_disk.c src/sc_path.c \ - include/sc_copy.h include/sc_metadata.h include/sc_disk.h \ - include/sc_path.h $(LIB) - mkdir -p $(dir $@) - $(PROJ_ROOT)/bin/sprinter-cc --memory big --safe --max-allocs $(ALLOCS) \ - -I include --bank 1=src/sc_copy_job.c --bank 1=src/sc_metadata.c \ - --bank 1=src/sc_disk.c \ - -o $@ tests/p19_overwrite_probe.c src/sc_path.c - -hdd-p19-overwrite: $(P19_OVERWRITE_TEST_EXE) - sh tests/make_p19_overwrite_hdd.sh \ - $(CURDIR)/build/hdd/p19_overwrite.chd $(P19_OVERWRITE_TEST_EXE) - -hdd-p19-policy: $(EXE) - sh tests/make_p19_policy_hdd.sh \ - $(CURDIR)/build/hdd/p19_policy.chd $(EXE) - -.PHONY: hdd-stress hdd-copy hdd-exec hdd-p5 hdd-p5-enospc hdd-p5-panels \ - hdd-p5-viewer hdd-p5-copy-faults hdd-p5-media hdd-p6-sort hdd-p7-select \ - hdd-p8-masks hdd-p9-mkdir hdd-p10-rename hdd-p11-delete \ - hdd-p12-group-copy hdd-p13-group-delete hdd-p14-default-colors \ - hdd-p15-tree-copy hdd-p15-tree-cancel hdd-p15-tree-self \ - hdd-p16-group-tree-copy hdd-p17-tree-delete hdd-p17-tree-delete-cancel \ - hdd-p18-metadata hdd-p19-overwrite hdd-p19-policy diff --git a/applications/Volkov/README.md b/applications/Volkov/README.md deleted file mode 100644 index b66dfad..0000000 --- a/applications/Volkov/README.md +++ /dev/null @@ -1,112 +0,0 @@ -# Sprinter Commander - -Функциональный аналог классического двухпанельного Commander для Sprinter -Sp2000. Volkov Commander 4.05/4.99 и Dos Navigator используются как -поведенческие и архитектурные референсы; проект не является переносом их -DOS-кода. - -Текущее состояние: PoC 0.1 (этапы P1–P5) пройден в MAME / BIOS 3.06 / -DSS 1.71.57; аппаратный smoke-тест остаётся внешним контрольным пунктом. -Начат этап 0.2: готовы независимые для каждой панели сортировки по имени, -расширению, размеру и дате/времени в обоих направлениях, выбор записей через -`Insert`, `*` и маски `+`/`-`, создание каталога по `F7` и локальное -переименование файла/каталога по `F6` без overwrite, однообъектный F8 и -групповой рекурсивный F8 для выбранных файлов и каталогов, а также рекурсивный -F5 для выбранных файлов и каталогов. F5 сохраняет FAT date/time и атрибуты -`R/H/S/A` обычных файлов и поддерживает conflict-policy -`overwrite/skip/rename`, включая `overwrite all`/`skip all`, отдельную -защиту readonly и backup/rollback. Обход использует динамическую EMM-очередь. -Встроенный профиль имён уже различает белые каталоги, жёлтые `.EXE` и -светло-серые остальные файлы; загрузка пользовательских цветовых правил -остаётся задачей 1.0. -Командер -сканирует, хранит и сортирует каталоги, показывает явную виртуальную запись -предела DSS 1.71, безопасно копирует один файл через выделенную EMM-страницу -W3 и запускает `.EXE` с восстановлением режима, палитры, CWD и обеих панелей. - -## Сборка - -Из этого каталога: - -```sh -make -``` - -Результат: `build/sprcmd.exe`, конфигурация -`--memory big --safe --max-allocs 3000`. Значение `ALLOCS=3000` принято как -исходное для Commander и всех его проб; при диагностике его можно переопределить -явно, например `make ALLOCS=4000`. - -Основные тесты используют быстрый HDD `D:`: - -```sh -make hdd # только Commander -make hdd-stress # большой каталог P3 -make hdd-copy # набор успех/overwrite/cancel/zero для P4.1 -make hdd-exec # запуск child/restore/error для P4.2 -make hdd-p5 # 20 copy, граничные размеры, 100 refresh, lifecycle -make hdd-p5-enospc # реальный disk-full и cleanup temp -make hdd-p5-panels # точные границы панели 27/28 записей -make hdd-p5-copy-faults # детерминированная матрица ошибок copy/EXEC -make hdd-p5-viewer # запуск реального mdview2 и восстановление Commander -make hdd-p5-media # снятие/возврат hard2 во время F5 -make hdd-p6-sort # target-матрица и UI-сценарий сортировок 0.2 -make hdd-p7-select # выбор Insert/*, статистика и EMM cleanup -make hdd-p8-masks # тот же расширенный образ: редактор и маски +/− -make hdd-p9-mkdir # validator DOS 8.3, mkdir/error/CWD и UI F7 -make hdd-p10-rename # file/dir rename, no-overwrite, CWD и UI F6 -make hdd-p11-delete # confirm/cancel, file/empty-dir, guards и UI F8 -make hdd-p12-group-copy # legacy group fixture; current F5 also copies selected dirs -make hdd-p13-group-delete # legacy file-only group F8 regression -make hdd-p14-default-colors # built-in dir/EXE/file colours и restore палитры -make hdd-p15-tree-copy # recursive current directory, depth=16 и repeat guard -make hdd-p15-tree-cancel # Esc в 4-МБ файле и cleanup temp/traversal page -make hdd-p15-tree-self # target-inside-source guard до mutation -make hdd-p16-group-tree-copy # selected files + multiple directory roots -make hdd-p17-tree-delete # recursive group F8, depth=12, cancel confirmation -make hdd-p17-tree-delete-cancel # Esc после начала recursive group F8 -make hdd-p18-metadata # exact FAT date/time + R/H/S/A после F5 -make hdd-p19-overwrite # target backup/commit/rollback matrix -make hdd-p19-policy # UI overwrite/skip/rename/all/readonly/cancel -``` - -`make floppy` сохранён для smoke-проверки совместимости с FDD и не запускает -эмулятор. Подробнее: [политика тестовых носителей](docs/test-media.md). - -## Документы - -- [требования PoC](docs/commander-requirements.md); -- [архитектура](docs/commander-architecture.md); -- [roadmap](docs/commander-roadmap.md); -- [клавиши и команды](docs/commander-keymap.md); -- [результаты платформенных проб P1](docs/p1-platform-probes.md); -- [экранный backend и границы BIOS API](docs/p2-screen-backend.md); -- [результаты coordinate-screen probe P2.1](docs/p2-screen-probe-results.md); -- [результаты системной палитры P2.2](docs/p2-palette-results.md); -- [результаты двухпанельного skeleton P2](docs/p2-skeleton-results.md); -- [результаты файловых панелей P3](docs/p3-panel-results.md); -- [результаты копирования P4.1](docs/p4-copy-results.md); -- [результаты запуска EXE P4.2](docs/p4-exec-results.md); -- [результаты стабилизации P5](docs/p5-stability-results.md); -- [результаты сортировок 0.2/P6](docs/p6-sort-results.md); -- [результаты выбора записей 0.2/P7](docs/p7-selection-results.md); -- [результаты выбора по маске 0.2/P8](docs/p8-mask-results.md); -- [результаты создания каталога 0.2/P9](docs/p9-mkdir-results.md); -- [результаты локального rename 0.2/P10](docs/p10-rename-results.md); -- [результаты однообъектного delete 0.2/P11](docs/p11-delete-results.md); -- [результаты группового F5 0.2/P12](docs/p12-group-copy-results.md); -- [результаты группового F8 0.2/P13](docs/p13-group-delete-results.md); -- [результаты стандартных цветов P14](docs/p14-default-colors-results.md); -- [результаты рекурсивного F5/P15](docs/p15-tree-copy-results.md); -- [результаты рекурсивного group F5/P16](docs/p16-group-tree-copy-results.md); -- [результаты рекурсивного group F8/P17](docs/p17-tree-delete-results.md); -- [результаты сохранения метаданных F5/P18](docs/p18-metadata-results.md); -- [результаты conflict-policy F5/P19](docs/p19-copy-policy-results.md); -- [направление развития UI по образцу VC](docs/ui-reference.md); -- [инструкция и ограничения PoC 0.1](docs/poc-0.1-release.md); -- [носители интеграционных тестов](docs/test-media.md); -- [предел больших каталогов DSS и стратегия Commander](docs/dss-large-directories.md). - -Экран выводится системными координатными функциями ESTEX/BIOS без оконных -дескрипторов и без прямой адресации VRAM. Системные вызовы палитры разрешены; -их save/set/readback/restore и многоцветная сцена подтверждены пробой P2.2. diff --git a/applications/Volkov/artifacts/mame-p1/README.md b/applications/Volkov/artifacts/mame-p1/README.md deleted file mode 100644 index e288d21..0000000 --- a/applications/Volkov/artifacts/mame-p1/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# Артефакты MAME-прогона P1 - -Дата: 6 сентября 2026 года. - -Окружение: MAME 0.287 (`b0c4527c`), машина `sprinter`, BIOS `v3.06`, текущие системные -образы проекта. `p1probe.exe` и `p1child.exe` запускались с дискеты A:. - -Каталоги: - -- `directory/` — CWD, `*.*`, `*`, завершение F_NEXT с `ENOENT=3`; - `strict-enoent.png` снят после удаления временного допуска `EISDIR`; -- `emm/` — три страницы, граничные patterns и возврат free pages; -- `dskinfo/` — геометрия и свободное место текущего диска; -- `winrest/` — полный экран 80x32 и частичный вывод из offset `0x2000`; - `partial-phase-hidden.png`/`partial-stable.png` показывают неудачные raw- - атрибуты, а `partial-flash-phase.png`/`partial-contrast-stable.png` — - именованный цветной pattern. Повторная проверка декодированных RGB показала, - что два последних кадра пиксельно идентичны; P2.2 отдельно подтвердил - стабильность четырёх палитровых планов; -- `exec/` — оба вызова child и итоговые значения EXEC/WAIT; -- `exec-then-emm/` — повторный EMM-тест после двух EXEC в том же процессе; -- `keyboard-commander/` — навигация, F1–F10, Ctrl+R, Ctrl+PgUp, Alt+F1; -- `keyboard/` — дополнительная проба Ctrl+A, Alt+X и Shift+Tab. - -Нумерованные клавиатурные кадры соответствуют времени сценария: - -- `0000.png` — первые навигационные события; -- `0001.png` — продолжение навигации и начало F-клавиш; -- `0002.png` — полная базовая таблица до сочетаний; -- `0003.png` — сочетания, Esc и общий `RESULT: PASS`; -- `0004.png` — возврат в главное меню. - -Интерпретация результатов и ссылки на основные кадры находятся в -`docs/p1-platform-probes.md` и `docs/commander-keymap.md`. -Актуальная интерпретация палитры находится в -`docs/p2-palette-results.md`. diff --git a/applications/Volkov/artifacts/mame-p1/directory/menu.png b/applications/Volkov/artifacts/mame-p1/directory/menu.png deleted file mode 100644 index ef16fc7..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/directory/menu.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/directory/result-early.png b/applications/Volkov/artifacts/mame-p1/directory/result-early.png deleted file mode 100644 index 34cef07..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/directory/result-early.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/directory/result.png b/applications/Volkov/artifacts/mame-p1/directory/result.png deleted file mode 100644 index b0c2445..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/directory/result.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/directory/strict-enoent.png b/applications/Volkov/artifacts/mame-p1/directory/strict-enoent.png deleted file mode 100644 index 6d97dd1..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/directory/strict-enoent.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/directory/strict-menu.png b/applications/Volkov/artifacts/mame-p1/directory/strict-menu.png deleted file mode 100644 index ef16fc7..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/directory/strict-menu.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/directory/transition.png b/applications/Volkov/artifacts/mame-p1/directory/transition.png deleted file mode 100644 index b0c2445..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/directory/transition.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/dskinfo/menu.png b/applications/Volkov/artifacts/mame-p1/dskinfo/menu.png deleted file mode 100644 index ef16fc7..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/dskinfo/menu.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/dskinfo/result.png b/applications/Volkov/artifacts/mame-p1/dskinfo/result.png deleted file mode 100644 index b482e9e..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/dskinfo/result.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/dskinfo/transition.png b/applications/Volkov/artifacts/mame-p1/dskinfo/transition.png deleted file mode 100644 index b482e9e..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/dskinfo/transition.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/emm/menu.png b/applications/Volkov/artifacts/mame-p1/emm/menu.png deleted file mode 100644 index ef16fc7..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/emm/menu.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/emm/result.png b/applications/Volkov/artifacts/mame-p1/emm/result.png deleted file mode 100644 index ef38cbf..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/emm/result.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/emm/transition.png b/applications/Volkov/artifacts/mame-p1/emm/transition.png deleted file mode 100644 index ef38cbf..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/emm/transition.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/exec-then-emm/emm-result.png b/applications/Volkov/artifacts/mame-p1/exec-then-emm/emm-result.png deleted file mode 100644 index ef38cbf..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/exec-then-emm/emm-result.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/exec-then-emm/exec-result.png b/applications/Volkov/artifacts/mame-p1/exec-then-emm/exec-result.png deleted file mode 100644 index 8ae8136..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/exec-then-emm/exec-result.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/exec-then-emm/menu.png b/applications/Volkov/artifacts/mame-p1/exec-then-emm/menu.png deleted file mode 100644 index ef16fc7..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/exec-then-emm/menu.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/exec/case1-child.png b/applications/Volkov/artifacts/mame-p1/exec/case1-child.png deleted file mode 100644 index 0890c65..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/exec/case1-child.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/exec/case1-parent.png b/applications/Volkov/artifacts/mame-p1/exec/case1-parent.png deleted file mode 100644 index c157572..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/exec/case1-parent.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/exec/case2-child.png b/applications/Volkov/artifacts/mame-p1/exec/case2-child.png deleted file mode 100644 index 0890c65..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/exec/case2-child.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/exec/case2-parent.png b/applications/Volkov/artifacts/mame-p1/exec/case2-parent.png deleted file mode 100644 index 7c0ce18..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/exec/case2-parent.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/exec/menu.png b/applications/Volkov/artifacts/mame-p1/exec/menu.png deleted file mode 100644 index ef16fc7..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/exec/menu.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/exec/result.png b/applications/Volkov/artifacts/mame-p1/exec/result.png deleted file mode 100644 index 8ae8136..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/exec/result.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/keyboard-commander/sprinter/0000.png b/applications/Volkov/artifacts/mame-p1/keyboard-commander/sprinter/0000.png deleted file mode 100644 index 94ef339..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/keyboard-commander/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/keyboard-commander/sprinter/0001.png b/applications/Volkov/artifacts/mame-p1/keyboard-commander/sprinter/0001.png deleted file mode 100644 index 33e0c79..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/keyboard-commander/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/keyboard-commander/sprinter/0002.png b/applications/Volkov/artifacts/mame-p1/keyboard-commander/sprinter/0002.png deleted file mode 100644 index 6c3d05c..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/keyboard-commander/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/keyboard-commander/sprinter/0003.png b/applications/Volkov/artifacts/mame-p1/keyboard-commander/sprinter/0003.png deleted file mode 100644 index b6595ce..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/keyboard-commander/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/keyboard-commander/sprinter/0004.png b/applications/Volkov/artifacts/mame-p1/keyboard-commander/sprinter/0004.png deleted file mode 100644 index ef16fc7..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/keyboard-commander/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/keyboard/sprinter/0000.png b/applications/Volkov/artifacts/mame-p1/keyboard/sprinter/0000.png deleted file mode 100644 index 94ef339..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/keyboard/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/keyboard/sprinter/0001.png b/applications/Volkov/artifacts/mame-p1/keyboard/sprinter/0001.png deleted file mode 100644 index 33e0c79..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/keyboard/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/keyboard/sprinter/0002.png b/applications/Volkov/artifacts/mame-p1/keyboard/sprinter/0002.png deleted file mode 100644 index a56ed41..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/keyboard/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/keyboard/sprinter/0003.png b/applications/Volkov/artifacts/mame-p1/keyboard/sprinter/0003.png deleted file mode 100644 index 294adb0..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/keyboard/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/keyboard/sprinter/0004.png b/applications/Volkov/artifacts/mame-p1/keyboard/sprinter/0004.png deleted file mode 100644 index ef16fc7..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/keyboard/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/winrest/full-contrast.png b/applications/Volkov/artifacts/mame-p1/winrest/full-contrast.png deleted file mode 100644 index 8741942..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/winrest/full-contrast.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/winrest/full-current.png b/applications/Volkov/artifacts/mame-p1/winrest/full-current.png deleted file mode 100644 index e052adc..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/winrest/full-current.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/winrest/full.png b/applications/Volkov/artifacts/mame-p1/winrest/full.png deleted file mode 100644 index e052adc..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/winrest/full.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/winrest/menu-current.png b/applications/Volkov/artifacts/mame-p1/winrest/menu-current.png deleted file mode 100644 index ef16fc7..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/winrest/menu-current.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/winrest/menu.png b/applications/Volkov/artifacts/mame-p1/winrest/menu.png deleted file mode 100644 index ef16fc7..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/winrest/menu.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/winrest/partial-contrast-stable.png b/applications/Volkov/artifacts/mame-p1/winrest/partial-contrast-stable.png deleted file mode 100644 index 9ebdf88..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/winrest/partial-contrast-stable.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/winrest/partial-flash-phase.png b/applications/Volkov/artifacts/mame-p1/winrest/partial-flash-phase.png deleted file mode 100644 index 9ebdf88..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/winrest/partial-flash-phase.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/winrest/partial-phase-hidden.png b/applications/Volkov/artifacts/mame-p1/winrest/partial-phase-hidden.png deleted file mode 100644 index c1df3d1..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/winrest/partial-phase-hidden.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/winrest/partial-stable.png b/applications/Volkov/artifacts/mame-p1/winrest/partial-stable.png deleted file mode 100644 index c1df3d1..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/winrest/partial-stable.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/winrest/partial.png b/applications/Volkov/artifacts/mame-p1/winrest/partial.png deleted file mode 100644 index c1df3d1..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/winrest/partial.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/winrest/result-contrast.png b/applications/Volkov/artifacts/mame-p1/winrest/result-contrast.png deleted file mode 100644 index c07a8d0..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/winrest/result-contrast.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/winrest/result-current.png b/applications/Volkov/artifacts/mame-p1/winrest/result-current.png deleted file mode 100644 index c07a8d0..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/winrest/result-current.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p1/winrest/result.png b/applications/Volkov/artifacts/mame-p1/winrest/result.png deleted file mode 100644 index c07a8d0..0000000 Binary files a/applications/Volkov/artifacts/mame-p1/winrest/result.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0000.png b/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0000.png deleted file mode 100644 index e99a71a..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0001.png b/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0001.png deleted file mode 100644 index f3d4d23..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0002.png b/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0002.png deleted file mode 100644 index 1b49f0b..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0003.png b/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0003.png deleted file mode 100644 index 6ef9f1a..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0004.png b/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0004.png deleted file mode 100644 index 2d9d34e..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0005.png b/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0005.png deleted file mode 100644 index ab02a2e..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0006.png b/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0006.png deleted file mode 100644 index 4526c99..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0007.png b/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0007.png deleted file mode 100644 index a56f301..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-final/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0000.png b/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0000.png deleted file mode 100644 index e99a71a..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0001.png b/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0001.png deleted file mode 100644 index 894df1d..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0002.png b/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0002.png deleted file mode 100644 index 54c654f..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0003.png b/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0003.png deleted file mode 100644 index 947106b..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0004.png b/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0004.png deleted file mode 100644 index 76c6f0d..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0005.png b/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0005.png deleted file mode 100644 index 8b1adde..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0006.png b/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0006.png deleted file mode 100644 index afb278c..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0007.png b/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0007.png deleted file mode 100644 index a56f301..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-pass/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0000.png deleted file mode 100644 index e99a71a..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0001.png deleted file mode 100644 index c16fe10..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0002.png deleted file mode 100644 index 754d671..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0003.png deleted file mode 100644 index 522a10d..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0004.png b/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0004.png deleted file mode 100644 index e2da05e..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0005.png b/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0005.png deleted file mode 100644 index 8b1adde..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0006.png b/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0006.png deleted file mode 100644 index 06d6e0f..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0007.png b/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0007.png deleted file mode 100644 index a56f301..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename-run/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename/clean-dss-return.png b/applications/Volkov/artifacts/mame-p10/rename/clean-dss-return.png deleted file mode 100644 index a56f301..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename/collision.png b/applications/Volkov/artifacts/mame-p10/rename/collision.png deleted file mode 100644 index 2d9d34e..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename/collision.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename/dialog-default.png b/applications/Volkov/artifacts/mame-p10/rename/dialog-default.png deleted file mode 100644 index f3d4d23..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename/dialog-default.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename/group-guard.png b/applications/Volkov/artifacts/mame-p10/rename/group-guard.png deleted file mode 100644 index 4526c99..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename/group-guard.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename/renamed-focused.png b/applications/Volkov/artifacts/mame-p10/rename/renamed-focused.png deleted file mode 100644 index 1b49f0b..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename/renamed-focused.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename/unchanged-name.png b/applications/Volkov/artifacts/mame-p10/rename/unchanged-name.png deleted file mode 100644 index 6ef9f1a..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename/unchanged-name.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p10/rename/virtual-guard.png b/applications/Volkov/artifacts/mame-p10/rename/virtual-guard.png deleted file mode 100644 index ab02a2e..0000000 Binary files a/applications/Volkov/artifacts/mame-p10/rename/virtual-guard.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0000.png deleted file mode 100644 index 0d47c78..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0001.png deleted file mode 100644 index df2c5b5..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0002.png deleted file mode 100644 index b04d5cb..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0003.png deleted file mode 100644 index e23d66b..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0004.png b/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0004.png deleted file mode 100644 index 461f913..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0005.png b/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0005.png deleted file mode 100644 index bbecd05..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0006.png b/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0006.png deleted file mode 100644 index df2f531..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0007.png b/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0007.png deleted file mode 100644 index 4c8acbe..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0008.png b/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0008.png deleted file mode 100644 index a56f301..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete-run/sprinter/0008.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete/cancel-preserves.png b/applications/Volkov/artifacts/mame-p11/delete/cancel-preserves.png deleted file mode 100644 index b04d5cb..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete/cancel-preserves.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete/clean-dss-return.png b/applications/Volkov/artifacts/mame-p11/delete/clean-dss-return.png deleted file mode 100644 index a56f301..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete/empty-dir-deleted.png b/applications/Volkov/artifacts/mame-p11/delete/empty-dir-deleted.png deleted file mode 100644 index 461f913..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete/empty-dir-deleted.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete/file-confirm.png b/applications/Volkov/artifacts/mame-p11/delete/file-confirm.png deleted file mode 100644 index df2c5b5..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete/file-confirm.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete/file-deleted.png b/applications/Volkov/artifacts/mame-p11/delete/file-deleted.png deleted file mode 100644 index e23d66b..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete/file-deleted.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete/group-guard.png b/applications/Volkov/artifacts/mame-p11/delete/group-guard.png deleted file mode 100644 index 4c8acbe..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete/group-guard.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete/nonempty-preserved.png b/applications/Volkov/artifacts/mame-p11/delete/nonempty-preserved.png deleted file mode 100644 index bbecd05..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete/nonempty-preserved.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p11/delete/virtual-guard.png b/applications/Volkov/artifacts/mame-p11/delete/virtual-guard.png deleted file mode 100644 index df2f531..0000000 Binary files a/applications/Volkov/artifacts/mame-p11/delete/virtual-guard.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0000.png b/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0000.png deleted file mode 100644 index 382c532..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0001.png b/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0001.png deleted file mode 100644 index 03e8fd3..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0002.png b/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0002.png deleted file mode 100644 index 3fe3807..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0003.png b/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0003.png deleted file mode 100644 index 612df0a..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0004.png b/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0004.png deleted file mode 100644 index 12240d5..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0005.png b/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0005.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy-p16-regression/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0000.png deleted file mode 100644 index d685749..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0001.png deleted file mode 100644 index 8dfc702..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0002.png deleted file mode 100644 index f271128..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0003.png deleted file mode 100644 index c7b537b..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0004.png b/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0004.png deleted file mode 100644 index df0c174..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0005.png b/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0005.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy-run/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy/clean-dss-return.png b/applications/Volkov/artifacts/mame-p12/group-copy/clean-dss-return.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy/copied-and-skipped-dir.png b/applications/Volkov/artifacts/mame-p12/group-copy/copied-and-skipped-dir.png deleted file mode 100644 index f271128..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy/copied-and-skipped-dir.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy/passive-refreshed.png b/applications/Volkov/artifacts/mame-p12/group-copy/passive-refreshed.png deleted file mode 100644 index df0c174..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy/passive-refreshed.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy/repeat-stops-eexist.png b/applications/Volkov/artifacts/mame-p12/group-copy/repeat-stops-eexist.png deleted file mode 100644 index c7b537b..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy/repeat-stops-eexist.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/group-copy/selected-files-and-dir.png b/applications/Volkov/artifacts/mame-p12/group-copy/selected-files-and-dir.png deleted file mode 100644 index 8dfc702..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/group-copy/selected-files-and-dir.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0000.png b/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0000.png deleted file mode 100644 index cca9dcf..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0001.png b/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0001.png deleted file mode 100644 index 362fbe8..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0002.png b/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0002.png deleted file mode 100644 index 7a9a26d..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0003.png b/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0003.png deleted file mode 100644 index ee538ad..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0004.png b/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0004.png deleted file mode 100644 index 42d8c2f..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0005.png b/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0005.png deleted file mode 100644 index 191d7dd..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0006.png b/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0006.png deleted file mode 100644 index 5e501dc..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0007.png b/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0007.png deleted file mode 100644 index 7dde71d..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0008.png b/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0008.png deleted file mode 100644 index 34dc9b3..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0008.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0009.png b/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0009.png deleted file mode 100644 index b31a79c..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0009.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0010.png b/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0010.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p12/single-copy-regression/sprinter/0010.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/file-only-regression/before.png b/applications/Volkov/artifacts/mame-p13/file-only-regression/before.png deleted file mode 100644 index 7305a80..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/file-only-regression/before.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/file-only-regression/cancel-preserves.png b/applications/Volkov/artifacts/mame-p13/file-only-regression/cancel-preserves.png deleted file mode 100644 index 81f988a..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/file-only-regression/cancel-preserves.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/file-only-regression/clean-dss-return.png b/applications/Volkov/artifacts/mame-p13/file-only-regression/clean-dss-return.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/file-only-regression/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/file-only-regression/files-deleted.png b/applications/Volkov/artifacts/mame-p13/file-only-regression/files-deleted.png deleted file mode 100644 index d2c6ff9..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/file-only-regression/files-deleted.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/file-only-regression/group-confirm.png b/applications/Volkov/artifacts/mame-p13/file-only-regression/group-confirm.png deleted file mode 100644 index e7584a5..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/file-only-regression/group-confirm.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/file-only-regression/selected-files.png b/applications/Volkov/artifacts/mame-p13/file-only-regression/selected-files.png deleted file mode 100644 index dcc3d47..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/file-only-regression/selected-files.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0000.png b/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0000.png deleted file mode 100644 index 5d69c55..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0001.png b/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0001.png deleted file mode 100644 index 865a6cf..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0002.png b/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0002.png deleted file mode 100644 index 12501de..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0003.png b/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0003.png deleted file mode 100644 index 9a2f5a4..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0004.png b/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0004.png deleted file mode 100644 index ce3d3f2..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0005.png b/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0005.png deleted file mode 100644 index 7745055..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0006.png b/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0006.png deleted file mode 100644 index a56f301..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete-rerun/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0000.png deleted file mode 100644 index 5d69c55..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0001.png deleted file mode 100644 index ad71e67..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0002.png deleted file mode 100644 index 8425c0c..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0003.png deleted file mode 100644 index 5250617..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0004.png b/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0004.png deleted file mode 100644 index 7e3cf69..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0005.png b/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0005.png deleted file mode 100644 index a83a2ad..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0006.png b/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0006.png deleted file mode 100644 index da56269..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete-run/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete/cancel-preserves.png b/applications/Volkov/artifacts/mame-p13/group-delete/cancel-preserves.png deleted file mode 100644 index 9a2f5a4..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete/cancel-preserves.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete/clean-dss-return.png b/applications/Volkov/artifacts/mame-p13/group-delete/clean-dss-return.png deleted file mode 100644 index a56f301..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete/directory-only-refused.png b/applications/Volkov/artifacts/mame-p13/group-delete/directory-only-refused.png deleted file mode 100644 index 7745055..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete/directory-only-refused.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete/files-deleted-dirs-preserved.png b/applications/Volkov/artifacts/mame-p13/group-delete/files-deleted-dirs-preserved.png deleted file mode 100644 index ce3d3f2..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete/files-deleted-dirs-preserved.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete/group-confirm.png b/applications/Volkov/artifacts/mame-p13/group-delete/group-confirm.png deleted file mode 100644 index 12501de..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete/group-confirm.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p13/group-delete/selected-files-and-dirs.png b/applications/Volkov/artifacts/mame-p13/group-delete/selected-files-and-dirs.png deleted file mode 100644 index 865a6cf..0000000 Binary files a/applications/Volkov/artifacts/mame-p13/group-delete/selected-files-and-dirs.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p14/default-colors-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p14/default-colors-run/sprinter/0000.png deleted file mode 100644 index b018d31..0000000 Binary files a/applications/Volkov/artifacts/mame-p14/default-colors-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p14/default-colors-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p14/default-colors-run/sprinter/0001.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p14/default-colors-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p14/default-colors/clean-dss-palette.png b/applications/Volkov/artifacts/mame-p14/default-colors/clean-dss-palette.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p14/default-colors/clean-dss-palette.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p14/default-colors/file-types.png b/applications/Volkov/artifacts/mame-p14/default-colors/file-types.png deleted file mode 100644 index b018d31..0000000 Binary files a/applications/Volkov/artifacts/mame-p14/default-colors/file-types.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-cancel-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p15/tree-cancel-run/sprinter/0000.png deleted file mode 100644 index dcb026f..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-cancel-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-cancel-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p15/tree-cancel-run/sprinter/0001.png deleted file mode 100644 index dc0d6c6..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-cancel-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-cancel-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p15/tree-cancel-run/sprinter/0002.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-cancel-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-copy-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p15/tree-copy-run/sprinter/0000.png deleted file mode 100644 index 984a375..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-copy-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-copy-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p15/tree-copy-run/sprinter/0001.png deleted file mode 100644 index b012944..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-copy-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-copy-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p15/tree-copy-run/sprinter/0002.png deleted file mode 100644 index edead95..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-copy-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-copy-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p15/tree-copy-run/sprinter/0003.png deleted file mode 100644 index 3bbbd69..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-copy-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-copy-run/sprinter/0004.png b/applications/Volkov/artifacts/mame-p15/tree-copy-run/sprinter/0004.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-copy-run/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-copy/before.png b/applications/Volkov/artifacts/mame-p15/tree-copy/before.png deleted file mode 100644 index 984a375..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-copy/before.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-copy/cancel-clean.png b/applications/Volkov/artifacts/mame-p15/tree-copy/cancel-clean.png deleted file mode 100644 index dc0d6c6..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-copy/cancel-clean.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-copy/clean-dss-return.png b/applications/Volkov/artifacts/mame-p15/tree-copy/clean-dss-return.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-copy/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-copy/passive-refreshed.png b/applications/Volkov/artifacts/mame-p15/tree-copy/passive-refreshed.png deleted file mode 100644 index 3bbbd69..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-copy/passive-refreshed.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-copy/repeat-existing-target.png b/applications/Volkov/artifacts/mame-p15/tree-copy/repeat-existing-target.png deleted file mode 100644 index edead95..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-copy/repeat-existing-target.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-copy/self-target-refused.png b/applications/Volkov/artifacts/mame-p15/tree-copy/self-target-refused.png deleted file mode 100644 index f2497af..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-copy/self-target-refused.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-copy/success.png b/applications/Volkov/artifacts/mame-p15/tree-copy/success.png deleted file mode 100644 index b012944..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-copy/success.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-self-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p15/tree-self-run/sprinter/0000.png deleted file mode 100644 index edd4c1a..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-self-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-self-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p15/tree-self-run/sprinter/0001.png deleted file mode 100644 index f2497af..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-self-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p15/tree-self-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p15/tree-self-run/sprinter/0002.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p15/tree-self-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p16/group-tree-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p16/group-tree-run/sprinter/0000.png deleted file mode 100644 index eec5d44..0000000 Binary files a/applications/Volkov/artifacts/mame-p16/group-tree-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p16/group-tree-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p16/group-tree-run/sprinter/0001.png deleted file mode 100644 index 3d805e9..0000000 Binary files a/applications/Volkov/artifacts/mame-p16/group-tree-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p16/group-tree-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p16/group-tree-run/sprinter/0002.png deleted file mode 100644 index 451d1d0..0000000 Binary files a/applications/Volkov/artifacts/mame-p16/group-tree-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p16/group-tree-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p16/group-tree-run/sprinter/0003.png deleted file mode 100644 index a56f301..0000000 Binary files a/applications/Volkov/artifacts/mame-p16/group-tree-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p16/group-tree/clean-dss-return.png b/applications/Volkov/artifacts/mame-p16/group-tree/clean-dss-return.png deleted file mode 100644 index a56f301..0000000 Binary files a/applications/Volkov/artifacts/mame-p16/group-tree/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p16/group-tree/copied.png b/applications/Volkov/artifacts/mame-p16/group-tree/copied.png deleted file mode 100644 index 3d805e9..0000000 Binary files a/applications/Volkov/artifacts/mame-p16/group-tree/copied.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p16/group-tree/passive-refreshed.png b/applications/Volkov/artifacts/mame-p16/group-tree/passive-refreshed.png deleted file mode 100644 index 451d1d0..0000000 Binary files a/applications/Volkov/artifacts/mame-p16/group-tree/passive-refreshed.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p16/group-tree/selected-roots.png b/applications/Volkov/artifacts/mame-p16/group-tree/selected-roots.png deleted file mode 100644 index eec5d44..0000000 Binary files a/applications/Volkov/artifacts/mame-p16/group-tree/selected-roots.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p17/tree-delete-cancel/clean-dss-return.png b/applications/Volkov/artifacts/mame-p17/tree-delete-cancel/clean-dss-return.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p17/tree-delete-cancel/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p17/tree-delete-cancel/group-confirm.png b/applications/Volkov/artifacts/mame-p17/tree-delete-cancel/group-confirm.png deleted file mode 100644 index 608e4ac..0000000 Binary files a/applications/Volkov/artifacts/mame-p17/tree-delete-cancel/group-confirm.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p17/tree-delete-cancel/runtime-cancelled.png b/applications/Volkov/artifacts/mame-p17/tree-delete-cancel/runtime-cancelled.png deleted file mode 100644 index 1991252..0000000 Binary files a/applications/Volkov/artifacts/mame-p17/tree-delete-cancel/runtime-cancelled.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p17/tree-delete-cancel/selected-before-cancel.png b/applications/Volkov/artifacts/mame-p17/tree-delete-cancel/selected-before-cancel.png deleted file mode 100644 index c74ea62..0000000 Binary files a/applications/Volkov/artifacts/mame-p17/tree-delete-cancel/selected-before-cancel.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p17/tree-delete/before.png b/applications/Volkov/artifacts/mame-p17/tree-delete/before.png deleted file mode 100644 index 33a943e..0000000 Binary files a/applications/Volkov/artifacts/mame-p17/tree-delete/before.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p17/tree-delete/cancel-preserves.png b/applications/Volkov/artifacts/mame-p17/tree-delete/cancel-preserves.png deleted file mode 100644 index 20a457e..0000000 Binary files a/applications/Volkov/artifacts/mame-p17/tree-delete/cancel-preserves.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p17/tree-delete/clean-dss-return.png b/applications/Volkov/artifacts/mame-p17/tree-delete/clean-dss-return.png deleted file mode 100644 index a56f301..0000000 Binary files a/applications/Volkov/artifacts/mame-p17/tree-delete/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p17/tree-delete/deleted-keep-preserved.png b/applications/Volkov/artifacts/mame-p17/tree-delete/deleted-keep-preserved.png deleted file mode 100644 index 31f72e7..0000000 Binary files a/applications/Volkov/artifacts/mame-p17/tree-delete/deleted-keep-preserved.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p17/tree-delete/group-confirm.png b/applications/Volkov/artifacts/mame-p17/tree-delete/group-confirm.png deleted file mode 100644 index f852fc9..0000000 Binary files a/applications/Volkov/artifacts/mame-p17/tree-delete/group-confirm.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p17/tree-delete/selected-file-and-trees.png b/applications/Volkov/artifacts/mame-p17/tree-delete/selected-file-and-trees.png deleted file mode 100644 index f39b794..0000000 Binary files a/applications/Volkov/artifacts/mame-p17/tree-delete/selected-file-and-trees.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p18/metadata/clean-dss-return.png b/applications/Volkov/artifacts/mame-p18/metadata/clean-dss-return.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p18/metadata/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p18/metadata/copied-metadata.png b/applications/Volkov/artifacts/mame-p18/metadata/copied-metadata.png deleted file mode 100644 index f4349d2..0000000 Binary files a/applications/Volkov/artifacts/mame-p18/metadata/copied-metadata.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p18/metadata/passive-metadata.png b/applications/Volkov/artifacts/mame-p18/metadata/passive-metadata.png deleted file mode 100644 index fe2c52e..0000000 Binary files a/applications/Volkov/artifacts/mame-p18/metadata/passive-metadata.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p18/metadata/source-metadata.png b/applications/Volkov/artifacts/mame-p18/metadata/source-metadata.png deleted file mode 100644 index a3ca160..0000000 Binary files a/applications/Volkov/artifacts/mame-p18/metadata/source-metadata.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p19/overwrite-core/all-pass.png b/applications/Volkov/artifacts/mame-p19/overwrite-core/all-pass.png deleted file mode 100644 index c005e1a..0000000 Binary files a/applications/Volkov/artifacts/mame-p19/overwrite-core/all-pass.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p19/overwrite-core/clean-dss-return.png b/applications/Volkov/artifacts/mame-p19/overwrite-core/clean-dss-return.png deleted file mode 100644 index 469db83..0000000 Binary files a/applications/Volkov/artifacts/mame-p19/overwrite-core/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p19/policy/cancel-choice.png b/applications/Volkov/artifacts/mame-p19/policy/cancel-choice.png deleted file mode 100644 index ec02cb1..0000000 Binary files a/applications/Volkov/artifacts/mame-p19/policy/cancel-choice.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p19/policy/cancel-cleared.png b/applications/Volkov/artifacts/mame-p19/policy/cancel-cleared.png deleted file mode 100644 index e4b0b7f..0000000 Binary files a/applications/Volkov/artifacts/mame-p19/policy/cancel-cleared.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p19/policy/clean-dss-return.png b/applications/Volkov/artifacts/mame-p19/policy/clean-dss-return.png deleted file mode 100644 index a56f301..0000000 Binary files a/applications/Volkov/artifacts/mame-p19/policy/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p19/policy/overwrite-choice.png b/applications/Volkov/artifacts/mame-p19/policy/overwrite-choice.png deleted file mode 100644 index a0d541a..0000000 Binary files a/applications/Volkov/artifacts/mame-p19/policy/overwrite-choice.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p19/policy/overwrite-complete.png b/applications/Volkov/artifacts/mame-p19/policy/overwrite-complete.png deleted file mode 100644 index d366f0b..0000000 Binary files a/applications/Volkov/artifacts/mame-p19/policy/overwrite-complete.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p19/policy/readonly-reprompt.png b/applications/Volkov/artifacts/mame-p19/policy/readonly-reprompt.png deleted file mode 100644 index 0487799..0000000 Binary files a/applications/Volkov/artifacts/mame-p19/policy/readonly-reprompt.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p19/policy/rename-choice.png b/applications/Volkov/artifacts/mame-p19/policy/rename-choice.png deleted file mode 100644 index e18afd0..0000000 Binary files a/applications/Volkov/artifacts/mame-p19/policy/rename-choice.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p19/policy/renamed.png b/applications/Volkov/artifacts/mame-p19/policy/renamed.png deleted file mode 100644 index 2307e3f..0000000 Binary files a/applications/Volkov/artifacts/mame-p19/policy/renamed.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p19/policy/skip-all-choice.png b/applications/Volkov/artifacts/mame-p19/policy/skip-all-choice.png deleted file mode 100644 index 1477025..0000000 Binary files a/applications/Volkov/artifacts/mame-p19/policy/skip-all-choice.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p19/policy/skip-complete.png b/applications/Volkov/artifacts/mame-p19/policy/skip-complete.png deleted file mode 100644 index d55dbfe..0000000 Binary files a/applications/Volkov/artifacts/mame-p19/policy/skip-complete.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p19/policy/target-result.png b/applications/Volkov/artifacts/mame-p19/policy/target-result.png deleted file mode 100644 index 81ac855..0000000 Binary files a/applications/Volkov/artifacts/mame-p19/policy/target-result.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/README.md b/applications/Volkov/artifacts/mame-p2/README.md deleted file mode 100644 index 995b2d6..0000000 --- a/applications/Volkov/artifacts/mame-p2/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Артефакты MAME-прогонов P2 - -Дата: 6 сентября 2026 года. - -Окружение: MAME 0.287 (`b0c4527c`), машина `sprinter`, BIOS `v3.06`. -Программа `p2screen.exe` запускалась с дискеты A:. - -Каталог `screen/` содержит: - -- `full.png` — полный `WINREST` и сверка через `RDCHAR`; -- `partial-offset.png` — прямоугольник из EMM offset `0x2000`; -- `left-scroll-up.png` и `right-scroll-down.png` — оба направления scroll на - разных панелях; -- `stress-running.png` и `stress-final.png` — прогон 1000 операций и итоговая - сверка модели; -- `stable-phase-a.png`, `stable-phase-b.png`, `stable-phase-c.png` — три - идентичных кадра принятого варианта с системным атрибутом `0x30`; -- `flash-before-visible.png`, `flash-before-hidden.png` и - `flash-before-visible-again.png` — исторические диагностические имена. - Повторная RGB-проверка показала, что все три файла полностью идентичны; - прежний вывод о чередовании был ошибкой предпросмотра. - -Каталог `skeleton/` содержит именованные кадры первого запускаемого Commander: - -- `initial-left-active.png` и `tab-right-active.png` — старт и переключение - панели; -- `navigation-scroll.png` — PgDn+Down, cursor/top `28/2`; -- `quit-dialog.png`, `quit-cancelled-restored.png` — обе ветви первого - подтверждения; -- `reserved-command-dialog.png`, `message-closed-restored.png` — общий - message dialog и восстановление модели; -- `clean-exit-dos.png` — штатный выход; -- `repeat-second-launch.png`, `repeat-second-clean-exit.png` — второй процесс - после cleanup и его завершение. - -Это сохранённый одноатрибутный baseline. Каталог `skeleton-color/` повторяет -полный сценарий после подключения `sc_theme` и дополнительно содержит кадры -обоих красных quit-dialog. Актуальным результатом P2 считается -`skeleton-color/`. - -Каталог `palette/` содержит P2.2: - -- `phase-a.png`–`phase-e.png` — многоцветная сцена в разных моментах; -- `restored-dos.png` — возврат после восстановления сохранённых записей. - -В `phase-a.png` отличается только внешний индикатор Drive A в левой панели -layout MAME. Viewport Sprinter во всех пяти фазах пиксельно идентичен. Левая -служебная панель MAME в сравнение экранных тестов не входит. - -P2.1 не открывает BIOS-окна, не обращается к VRAM напрямую и не изменяет -палитру. Интерпретация и точные результаты приведены в -[`docs/p2-screen-probe-results.md`](../../docs/p2-screen-probe-results.md). -P2.2 проверяет разрешённые системные палитровые вызовы; результаты приведены -в [`docs/p2-palette-results.md`](../../docs/p2-palette-results.md). -Результаты приложения-skeleton приведены в -[`docs/p2-skeleton-results.md`](../../docs/p2-skeleton-results.md). diff --git a/applications/Volkov/artifacts/mame-p2/palette/phase-a.png b/applications/Volkov/artifacts/mame-p2/palette/phase-a.png deleted file mode 100644 index 3f35ed7..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/palette/phase-a.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/palette/phase-b.png b/applications/Volkov/artifacts/mame-p2/palette/phase-b.png deleted file mode 100644 index d41755f..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/palette/phase-b.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/palette/phase-c.png b/applications/Volkov/artifacts/mame-p2/palette/phase-c.png deleted file mode 100644 index d41755f..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/palette/phase-c.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/palette/phase-d.png b/applications/Volkov/artifacts/mame-p2/palette/phase-d.png deleted file mode 100644 index d41755f..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/palette/phase-d.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/palette/phase-e.png b/applications/Volkov/artifacts/mame-p2/palette/phase-e.png deleted file mode 100644 index d41755f..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/palette/phase-e.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/palette/restored-dos.png b/applications/Volkov/artifacts/mame-p2/palette/restored-dos.png deleted file mode 100644 index 815c5ef..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/palette/restored-dos.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/screen/flash-before-hidden.png b/applications/Volkov/artifacts/mame-p2/screen/flash-before-hidden.png deleted file mode 100644 index 2659006..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/screen/flash-before-hidden.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/screen/flash-before-visible-again.png b/applications/Volkov/artifacts/mame-p2/screen/flash-before-visible-again.png deleted file mode 100644 index 2659006..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/screen/flash-before-visible-again.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/screen/flash-before-visible.png b/applications/Volkov/artifacts/mame-p2/screen/flash-before-visible.png deleted file mode 100644 index 2659006..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/screen/flash-before-visible.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/screen/full.png b/applications/Volkov/artifacts/mame-p2/screen/full.png deleted file mode 100644 index 2e69c91..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/screen/full.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/screen/left-scroll-up.png b/applications/Volkov/artifacts/mame-p2/screen/left-scroll-up.png deleted file mode 100644 index f2beaf0..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/screen/left-scroll-up.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/screen/partial-offset.png b/applications/Volkov/artifacts/mame-p2/screen/partial-offset.png deleted file mode 100644 index bd3013c..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/screen/partial-offset.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/screen/right-scroll-down.png b/applications/Volkov/artifacts/mame-p2/screen/right-scroll-down.png deleted file mode 100644 index fd2b965..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/screen/right-scroll-down.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/screen/stable-phase-a.png b/applications/Volkov/artifacts/mame-p2/screen/stable-phase-a.png deleted file mode 100644 index 8e88ec8..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/screen/stable-phase-a.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/screen/stable-phase-b.png b/applications/Volkov/artifacts/mame-p2/screen/stable-phase-b.png deleted file mode 100644 index 8e88ec8..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/screen/stable-phase-b.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/screen/stable-phase-c.png b/applications/Volkov/artifacts/mame-p2/screen/stable-phase-c.png deleted file mode 100644 index 8e88ec8..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/screen/stable-phase-c.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/screen/stress-final.png b/applications/Volkov/artifacts/mame-p2/screen/stress-final.png deleted file mode 100644 index 8e88ec8..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/screen/stress-final.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/screen/stress-running.png b/applications/Volkov/artifacts/mame-p2/screen/stress-running.png deleted file mode 100644 index 903b2c5..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/screen/stress-running.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-color/clean-exit-dos.png b/applications/Volkov/artifacts/mame-p2/skeleton-color/clean-exit-dos.png deleted file mode 100644 index 815c5ef..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-color/clean-exit-dos.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-color/initial-left-active.png b/applications/Volkov/artifacts/mame-p2/skeleton-color/initial-left-active.png deleted file mode 100644 index 4471bd0..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-color/initial-left-active.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-color/message-closed-restored.png b/applications/Volkov/artifacts/mame-p2/skeleton-color/message-closed-restored.png deleted file mode 100644 index 3a4ba93..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-color/message-closed-restored.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-color/navigation-scroll.png b/applications/Volkov/artifacts/mame-p2/skeleton-color/navigation-scroll.png deleted file mode 100644 index f5b8efb..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-color/navigation-scroll.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-color/quit-cancelled-restored.png b/applications/Volkov/artifacts/mame-p2/skeleton-color/quit-cancelled-restored.png deleted file mode 100644 index d694559..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-color/quit-cancelled-restored.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-color/quit-dialog.png b/applications/Volkov/artifacts/mame-p2/skeleton-color/quit-dialog.png deleted file mode 100644 index b7a412c..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-color/quit-dialog.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-color/repeat-second-clean-exit.png b/applications/Volkov/artifacts/mame-p2/skeleton-color/repeat-second-clean-exit.png deleted file mode 100644 index 815c5ef..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-color/repeat-second-clean-exit.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-color/repeat-second-launch.png b/applications/Volkov/artifacts/mame-p2/skeleton-color/repeat-second-launch.png deleted file mode 100644 index 4471bd0..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-color/repeat-second-launch.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-color/repeat-second-quit-dialog.png b/applications/Volkov/artifacts/mame-p2/skeleton-color/repeat-second-quit-dialog.png deleted file mode 100644 index 2ff9148..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-color/repeat-second-quit-dialog.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-color/reserved-command-dialog.png b/applications/Volkov/artifacts/mame-p2/skeleton-color/reserved-command-dialog.png deleted file mode 100644 index cd5cdbb..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-color/reserved-command-dialog.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-color/second-quit-dialog.png b/applications/Volkov/artifacts/mame-p2/skeleton-color/second-quit-dialog.png deleted file mode 100644 index 05c1961..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-color/second-quit-dialog.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-color/tab-right-active.png b/applications/Volkov/artifacts/mame-p2/skeleton-color/tab-right-active.png deleted file mode 100644 index b57a0b3..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-color/tab-right-active.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0000.png deleted file mode 100644 index 3913e2c..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0001.png deleted file mode 100644 index f9297df..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0002.png deleted file mode 100644 index 610bab7..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0003.png deleted file mode 100644 index c4a5543..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0004.png b/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0004.png deleted file mode 100644 index d0705f2..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0005.png b/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0005.png deleted file mode 100644 index afc2ded..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0006.png b/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0006.png deleted file mode 100644 index f6ff3cc..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0007.png b/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0007.png deleted file mode 100644 index fc8d10f..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0008.png b/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0008.png deleted file mode 100644 index 815c5ef..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0008.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0009.png b/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0009.png deleted file mode 100644 index 3913e2c..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0009.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0010.png b/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0010.png deleted file mode 100644 index 66ca85d..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0010.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0011.png b/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0011.png deleted file mode 100644 index 815c5ef..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-repeat-run/sprinter/0011.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0000.png deleted file mode 100644 index 3913e2c..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0001.png deleted file mode 100644 index f9297df..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0002.png deleted file mode 100644 index 610bab7..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0003.png deleted file mode 100644 index c4a5543..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0004.png b/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0004.png deleted file mode 100644 index d0705f2..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0005.png b/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0005.png deleted file mode 100644 index afc2ded..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0006.png b/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0006.png deleted file mode 100644 index f6ff3cc..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0007.png b/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0007.png deleted file mode 100644 index fc8d10f..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0008.png b/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0008.png deleted file mode 100644 index 815c5ef..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton-ui-run/sprinter/0008.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton/clean-exit-dos.png b/applications/Volkov/artifacts/mame-p2/skeleton/clean-exit-dos.png deleted file mode 100644 index 815c5ef..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton/clean-exit-dos.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton/cleanup-between-runs.png b/applications/Volkov/artifacts/mame-p2/skeleton/cleanup-between-runs.png deleted file mode 100644 index 815c5ef..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton/cleanup-between-runs.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton/initial-left-active.png b/applications/Volkov/artifacts/mame-p2/skeleton/initial-left-active.png deleted file mode 100644 index 3913e2c..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton/initial-left-active.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton/message-closed-restored.png b/applications/Volkov/artifacts/mame-p2/skeleton/message-closed-restored.png deleted file mode 100644 index f6ff3cc..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton/message-closed-restored.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton/navigation-scroll.png b/applications/Volkov/artifacts/mame-p2/skeleton/navigation-scroll.png deleted file mode 100644 index 610bab7..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton/navigation-scroll.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton/quit-cancelled-restored.png b/applications/Volkov/artifacts/mame-p2/skeleton/quit-cancelled-restored.png deleted file mode 100644 index d0705f2..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton/quit-cancelled-restored.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton/quit-dialog.png b/applications/Volkov/artifacts/mame-p2/skeleton/quit-dialog.png deleted file mode 100644 index c4a5543..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton/quit-dialog.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton/repeat-second-clean-exit.png b/applications/Volkov/artifacts/mame-p2/skeleton/repeat-second-clean-exit.png deleted file mode 100644 index 815c5ef..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton/repeat-second-clean-exit.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton/repeat-second-launch.png b/applications/Volkov/artifacts/mame-p2/skeleton/repeat-second-launch.png deleted file mode 100644 index 3913e2c..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton/repeat-second-launch.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton/reserved-command-dialog.png b/applications/Volkov/artifacts/mame-p2/skeleton/reserved-command-dialog.png deleted file mode 100644 index afc2ded..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton/reserved-command-dialog.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p2/skeleton/tab-right-active.png b/applications/Volkov/artifacts/mame-p2/skeleton/tab-right-active.png deleted file mode 100644 index f9297df..0000000 Binary files a/applications/Volkov/artifacts/mame-p2/skeleton/tab-right-active.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/bigdir-partial.png b/applications/Volkov/artifacts/mame-p3/panels/bigdir-partial.png deleted file mode 100644 index 23a6cf5..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/bigdir-partial.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/ctrl-pgup-left-only.png b/applications/Volkov/artifacts/mame-p3/panels/ctrl-pgup-left-only.png deleted file mode 100644 index b66dea9..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/ctrl-pgup-left-only.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/dss-enum-limit.png b/applications/Volkov/artifacts/mame-p3/panels/dss-enum-limit.png deleted file mode 100644 index 3388fda..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/dss-enum-limit.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/dss-limit-clean-exit.png b/applications/Volkov/artifacts/mame-p3/panels/dss-limit-clean-exit.png deleted file mode 100644 index 815c5ef..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/dss-limit-clean-exit.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/dss-limit-quit-dialog.png b/applications/Volkov/artifacts/mame-p3/panels/dss-limit-quit-dialog.png deleted file mode 100644 index 4300ec7..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/dss-limit-quit-dialog.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/dss-marker-message.png b/applications/Volkov/artifacts/mame-p3/panels/dss-marker-message.png deleted file mode 100644 index b58e144..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/dss-marker-message.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/dss-marker-selected.png b/applications/Volkov/artifacts/mame-p3/panels/dss-marker-selected.png deleted file mode 100644 index cfe2ec1..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/dss-marker-selected.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/empty-parent.png b/applications/Volkov/artifacts/mame-p3/panels/empty-parent.png deleted file mode 100644 index 4919a72..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/empty-parent.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/enter-parent-return.png b/applications/Volkov/artifacts/mame-p3/panels/enter-parent-return.png deleted file mode 100644 index 9d6c1a3..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/enter-parent-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/first-clean-exit.png b/applications/Volkov/artifacts/mame-p3/panels/first-clean-exit.png deleted file mode 100644 index 815c5ef..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/first-clean-exit.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/independent-panel-state.png b/applications/Volkov/artifacts/mame-p3/panels/independent-panel-state.png deleted file mode 100644 index fb1fb02..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/independent-panel-state.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/left-testdir-parent.png b/applications/Volkov/artifacts/mame-p3/panels/left-testdir-parent.png deleted file mode 100644 index 88d4484..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/left-testdir-parent.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/navigation-scroll.png b/applications/Volkov/artifacts/mame-p3/panels/navigation-scroll.png deleted file mode 100644 index 97cd85b..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/navigation-scroll.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/quit-dialog.png b/applications/Volkov/artifacts/mame-p3/panels/quit-dialog.png deleted file mode 100644 index 1fa966e..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/quit-dialog.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/refresh-preserves-inner.png b/applications/Volkov/artifacts/mame-p3/panels/refresh-preserves-inner.png deleted file mode 100644 index dc34b34..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/refresh-preserves-inner.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/root-both-panels.png b/applications/Volkov/artifacts/mame-p3/panels/root-both-panels.png deleted file mode 100644 index 295c995..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/root-both-panels.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/scan-marker.png b/applications/Volkov/artifacts/mame-p3/panels/scan-marker.png deleted file mode 100644 index d34838d..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/scan-marker.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/second-clean-exit.png b/applications/Volkov/artifacts/mame-p3/panels/second-clean-exit.png deleted file mode 100644 index 815c5ef..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/second-clean-exit.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/second-launch.png b/applications/Volkov/artifacts/mame-p3/panels/second-launch.png deleted file mode 100644 index 295c995..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/second-launch.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/second-quit-dialog.png b/applications/Volkov/artifacts/mame-p3/panels/second-quit-dialog.png deleted file mode 100644 index 28da7c4..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/second-quit-dialog.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p3/panels/store-boundary-640-641.png b/applications/Volkov/artifacts/mame-p3/panels/store-boundary-640-641.png deleted file mode 100644 index b2d47e2..0000000 Binary files a/applications/Volkov/artifacts/mame-p3/panels/store-boundary-640-641.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0000.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0000.png deleted file mode 100644 index ae73fce..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0001.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0001.png deleted file mode 100644 index 2a0af2f..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0002.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0002.png deleted file mode 100644 index 6663f1e..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0003.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0003.png deleted file mode 100644 index c0030c7..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0004.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0004.png deleted file mode 100644 index 00442ab..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0005.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0005.png deleted file mode 100644 index c1f7d9c..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0006.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0006.png deleted file mode 100644 index bb29355..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0007.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0007.png deleted file mode 100644 index b17b305..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0008.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0008.png deleted file mode 100644 index 99dafb9..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0008.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0009.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0009.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-diag/sprinter/0009.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0000.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0000.png deleted file mode 100644 index 2004e42..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0001.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0001.png deleted file mode 100644 index 2a0af2f..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0002.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0002.png deleted file mode 100644 index 596ad61..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0003.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0003.png deleted file mode 100644 index 0dfce4b..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0004.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0004.png deleted file mode 100644 index ee4a8cd..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0005.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0005.png deleted file mode 100644 index 43c396a..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0006.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0006.png deleted file mode 100644 index c397f85..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0007.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0007.png deleted file mode 100644 index 1e96936..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0008.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0008.png deleted file mode 100644 index b7b89a1..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0008.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0009.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0009.png deleted file mode 100644 index c090a6b..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0009.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0010.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0010.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-final/sprinter/0010.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0000.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0000.png deleted file mode 100644 index 2622789..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0001.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0001.png deleted file mode 100644 index 2a0af2f..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0002.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0002.png deleted file mode 100644 index aadd8c4..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0003.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0003.png deleted file mode 100644 index 155e16a..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0004.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0004.png deleted file mode 100644 index 84a8c32..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0005.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0005.png deleted file mode 100644 index 5c38baa..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0006.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0006.png deleted file mode 100644 index 460081f..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0007.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0007.png deleted file mode 100644 index 4cffd00..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0008.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0008.png deleted file mode 100644 index fe8716e..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0008.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0009.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0009.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-fixed/sprinter/0009.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0000.png deleted file mode 100644 index c7a73e2..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0001.png deleted file mode 100644 index 2a0af2f..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0002.png deleted file mode 100644 index 5fd7c2b..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0003.png deleted file mode 100644 index 399137c..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0004.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0004.png deleted file mode 100644 index 68e5625..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0005.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0005.png deleted file mode 100644 index 5797306..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0006.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0006.png deleted file mode 100644 index c815ef3..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0007.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0007.png deleted file mode 100644 index bf947c3..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0008.png b/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0008.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-hdd-run/sprinter/0008.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0000.png b/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0000.png deleted file mode 100644 index b5bd5b9..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0001.png b/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0001.png deleted file mode 100644 index 2a0af2f..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0002.png b/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0002.png deleted file mode 100644 index 11f2c63..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0003.png b/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0003.png deleted file mode 100644 index a552fd3..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0004.png b/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0004.png deleted file mode 100644 index 55e321a..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0005.png b/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0005.png deleted file mode 100644 index de4c53f..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0006.png b/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0006.png deleted file mode 100644 index 33c8501..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0007.png b/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0007.png deleted file mode 100644 index 7130408..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0008.png b/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0008.png deleted file mode 100644 index a5e4d75..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0008.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0009.png b/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0009.png deleted file mode 100644 index c180e33..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0009.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0010.png b/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0010.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-openfix/sprinter/0010.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0000.png b/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0000.png deleted file mode 100644 index 6cd5004..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0001.png b/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0001.png deleted file mode 100644 index 2a0af2f..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0002.png b/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0002.png deleted file mode 100644 index 2ee778a..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0003.png b/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0003.png deleted file mode 100644 index 518d562..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0004.png b/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0004.png deleted file mode 100644 index e50d1d9..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0005.png b/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0005.png deleted file mode 100644 index 4905279..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0006.png b/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0006.png deleted file mode 100644 index 6086083..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0007.png b/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0007.png deleted file mode 100644 index f037bb8..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0008.png b/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0008.png deleted file mode 100644 index c63b73e..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0008.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0009.png b/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0009.png deleted file mode 100644 index fe75c00..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0009.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0010.png b/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0010.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy-precommit-regression/sprinter/0010.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy/bank-page-io.png b/applications/Volkov/artifacts/mame-p4/copy/bank-page-io.png deleted file mode 100644 index 6e0f2b0..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy/bank-page-io.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy/hdd-cancel-cleanup.png b/applications/Volkov/artifacts/mame-p4/copy/hdd-cancel-cleanup.png deleted file mode 100644 index 1e96936..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy/hdd-cancel-cleanup.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy/hdd-cancel-progress.png b/applications/Volkov/artifacts/mame-p4/copy/hdd-cancel-progress.png deleted file mode 100644 index c397f85..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy/hdd-cancel-progress.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy/hdd-clean-exit.png b/applications/Volkov/artifacts/mame-p4/copy/hdd-clean-exit.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy/hdd-clean-exit.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy/hdd-copy-success.png b/applications/Volkov/artifacts/mame-p4/copy/hdd-copy-success.png deleted file mode 100644 index ee4a8cd..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy/hdd-copy-success.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy/hdd-existing-refused.png b/applications/Volkov/artifacts/mame-p4/copy/hdd-existing-refused.png deleted file mode 100644 index 43c396a..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy/hdd-existing-refused.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/copy/hdd-zero-copy-success.png b/applications/Volkov/artifacts/mame-p4/copy/hdd-zero-copy-success.png deleted file mode 100644 index b7b89a1..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/copy/hdd-zero-copy-success.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0000.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0000.png deleted file mode 100644 index 79c189f..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0001.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0001.png deleted file mode 100644 index 048351a..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0002.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0002.png deleted file mode 100644 index 1a8538c..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0003.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0003.png deleted file mode 100644 index ee59798..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0004.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0004.png deleted file mode 100644 index fbd3fd2..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0005.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0005.png deleted file mode 100644 index ffade7a..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0006.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0006.png deleted file mode 100644 index 27bc305..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0007.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0007.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-83-pass/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0000.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0000.png deleted file mode 100644 index 63aa2fa..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0001.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0001.png deleted file mode 100644 index 495a2ab..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0002.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0002.png deleted file mode 100644 index 516e964..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0003.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0003.png deleted file mode 100644 index 7985f43..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0004.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0004.png deleted file mode 100644 index 8f4fd78..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0005.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0005.png deleted file mode 100644 index f0df585..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0006.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0006.png deleted file mode 100644 index fc0b552..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0007.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0007.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-final/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0000.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0000.png deleted file mode 100644 index c3f0023..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0001.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0001.png deleted file mode 100644 index fb5618c..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0002.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0002.png deleted file mode 100644 index 516e964..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0003.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0003.png deleted file mode 100644 index 2603413..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0004.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0004.png deleted file mode 100644 index 6095af7..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0005.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0005.png deleted file mode 100644 index 07ff09d..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0006.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0006.png deleted file mode 100644 index 1ef7243..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0007.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0007.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-pass/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0000.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0000.png deleted file mode 100644 index e56ee50..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0001.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0001.png deleted file mode 100644 index c900213..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0002.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0002.png deleted file mode 100644 index 516e964..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0003.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0003.png deleted file mode 100644 index 181c3c3..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0004.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0004.png deleted file mode 100644 index 658d774..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0005.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0005.png deleted file mode 100644 index 43eac69..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0006.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0006.png deleted file mode 100644 index 0d7edd9..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0007.png b/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0007.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-hdd-run-20260908/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0000.png b/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0000.png deleted file mode 100644 index 0978008..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0001.png b/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0001.png deleted file mode 100644 index 048351a..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0002.png b/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0002.png deleted file mode 100644 index 1a8538c..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0003.png b/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0003.png deleted file mode 100644 index 09bb86f..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0004.png b/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0004.png deleted file mode 100644 index 6fed65f..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0005.png b/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0005.png deleted file mode 100644 index 9ef735a..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0006.png b/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0006.png deleted file mode 100644 index 522af92..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0007.png b/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0007.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec-regression-cwd/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec/broken-exe-error.png b/applications/Volkov/artifacts/mame-p4/exec/broken-exe-error.png deleted file mode 100644 index fbd3fd2..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec/broken-exe-error.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec/child-mode-palette-cwd.png b/applications/Volkov/artifacts/mame-p4/exec/child-mode-palette-cwd.png deleted file mode 100644 index 1a8538c..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec/child-mode-palette-cwd.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec/clean-dss-return.png b/applications/Volkov/artifacts/mame-p4/exec/clean-dss-return.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec/commander-restored-both-panels.png b/applications/Volkov/artifacts/mame-p4/exec/commander-restored-both-panels.png deleted file mode 100644 index ee59798..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec/commander-restored-both-panels.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec/quit-after-exec.png b/applications/Volkov/artifacts/mame-p4/exec/quit-after-exec.png deleted file mode 100644 index 27bc305..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec/quit-after-exec.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p4/exec/unsupported-file.png b/applications/Volkov/artifacts/mame-p4/exec/unsupported-file.png deleted file mode 100644 index ffade7a..0000000 Binary files a/applications/Volkov/artifacts/mame-p4/exec/unsupported-file.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults-final/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/copy-faults-final/sprinter/0000.png deleted file mode 100644 index 96ab6f6..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults-final/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults-final/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/copy-faults-final/sprinter/0001.png deleted file mode 100644 index 7b5bf9f..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults-final/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults-missing-exe/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/copy-faults-missing-exe/sprinter/0000.png deleted file mode 100644 index 3ce4a0e..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults-missing-exe/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults-missing-exe/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/copy-faults-missing-exe/sprinter/0001.png deleted file mode 100644 index 79378f8..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults-missing-exe/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults-pass/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/copy-faults-pass/sprinter/0000.png deleted file mode 100644 index 96ab6f6..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults-pass/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults-pass/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/copy-faults-pass/sprinter/0001.png deleted file mode 100644 index 7b5bf9f..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults-pass/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults-readonly-openfix/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/copy-faults-readonly-openfix/sprinter/0000.png deleted file mode 100644 index 958029b..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults-readonly-openfix/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults-readonly-openfix/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/copy-faults-readonly-openfix/sprinter/0001.png deleted file mode 100644 index e2b2ceb..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults-readonly-openfix/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults-readonly/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/copy-faults-readonly/sprinter/0000.png deleted file mode 100644 index 205d997..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults-readonly/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults-readonly/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/copy-faults-readonly/sprinter/0001.png deleted file mode 100644 index 87c3aff..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults-readonly/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/copy-faults-run/sprinter/0000.png deleted file mode 100644 index c9f2633..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/copy-faults-run/sprinter/0001.png deleted file mode 100644 index 15329bd..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults-second/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/copy-faults-second/sprinter/0000.png deleted file mode 100644 index 7b5bf9f..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults-second/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults-second/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/copy-faults-second/sprinter/0001.png deleted file mode 100644 index 7b5bf9f..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults-second/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults-visible/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/copy-faults-visible/sprinter/0000.png deleted file mode 100644 index 32dcd7c..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults-visible/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults-visible/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/copy-faults-visible/sprinter/0001.png deleted file mode 100644 index 34ba3e2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults-visible/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults/fault-matrix-all-pass.png b/applications/Volkov/artifacts/mame-p5/copy-faults/fault-matrix-all-pass.png deleted file mode 100644 index 3ce4a0e..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults/fault-matrix-all-pass.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/copy-faults/return-to-dss.png b/applications/Volkov/artifacts/mame-p5/copy-faults/return-to-dss.png deleted file mode 100644 index 79378f8..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/copy-faults/return-to-dss.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/enospc-precommit/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/enospc-precommit/sprinter/0000.png deleted file mode 100644 index 494a63b..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/enospc-precommit/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/enospc-precommit/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/enospc-precommit/sprinter/0001.png deleted file mode 100644 index 69e0363..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/enospc-precommit/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/enospc-precommit/sprinter/0002.png b/applications/Volkov/artifacts/mame-p5/enospc-precommit/sprinter/0002.png deleted file mode 100644 index d3204d8..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/enospc-precommit/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/enospc-precommit/sprinter/0003.png b/applications/Volkov/artifacts/mame-p5/enospc-precommit/sprinter/0003.png deleted file mode 100644 index 7d82a67..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/enospc-precommit/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/enospc-precommit/sprinter/0004.png b/applications/Volkov/artifacts/mame-p5/enospc-precommit/sprinter/0004.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/enospc-precommit/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/enospc-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/enospc-run/sprinter/0000.png deleted file mode 100644 index 494a63b..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/enospc-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/enospc-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/enospc-run/sprinter/0001.png deleted file mode 100644 index 2b27e3e..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/enospc-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/enospc-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p5/enospc-run/sprinter/0002.png deleted file mode 100644 index e8a9344..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/enospc-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/enospc-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p5/enospc-run/sprinter/0003.png deleted file mode 100644 index 80e6e95..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/enospc-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/enospc-run/sprinter/0004.png b/applications/Volkov/artifacts/mame-p5/enospc-run/sprinter/0004.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/enospc-run/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/enospc/clean-dss-return.png b/applications/Volkov/artifacts/mame-p5/enospc/clean-dss-return.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/enospc/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/enospc/copy-progress.png b/applications/Volkov/artifacts/mame-p5/enospc/copy-progress.png deleted file mode 100644 index 2b27e3e..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/enospc/copy-progress.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/enospc/enospc-cleanup.png b/applications/Volkov/artifacts/mame-p5/enospc/enospc-cleanup.png deleted file mode 100644 index e8a9344..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/enospc/enospc-cleanup.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/enospc/quit-after-error.png b/applications/Volkov/artifacts/mame-p5/enospc/quit-after-error.png deleted file mode 100644 index 80e6e95..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/enospc/quit-after-error.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0000.png deleted file mode 100644 index f81507c..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0001.png deleted file mode 100644 index b87e161..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0002.png b/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0002.png deleted file mode 100644 index b87e161..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0003.png b/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0003.png deleted file mode 100644 index b0ceb44..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0004.png b/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0004.png deleted file mode 100644 index 6221f8f..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0005.png b/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0005.png deleted file mode 100644 index ae58397..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0006.png b/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0006.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change-copy/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0000.png deleted file mode 100644 index c0855aa..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0001.png deleted file mode 100644 index ad59aaa..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0002.png deleted file mode 100644 index ad59aaa..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0003.png deleted file mode 100644 index ad59aaa..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0004.png b/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0004.png deleted file mode 100644 index 8aac37c..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0005.png b/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0005.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change-run/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change/clean-dss-return.png b/applications/Volkov/artifacts/mame-p5/media-change/clean-dss-return.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change/managed-error.png b/applications/Volkov/artifacts/mame-p5/media-change/managed-error.png deleted file mode 100644 index b0ceb44..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change/managed-error.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change/recovered-copy.png b/applications/Volkov/artifacts/mame-p5/media-change/recovered-copy.png deleted file mode 100644 index ae58397..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change/recovered-copy.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/media-change/retry-progress.png b/applications/Volkov/artifacts/mame-p5/media-change/retry-progress.png deleted file mode 100644 index 6221f8f..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/media-change/retry-progress.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/panel-edges-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/panel-edges-run/sprinter/0000.png deleted file mode 100644 index de70a3f..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/panel-edges-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/panel-edges-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/panel-edges-run/sprinter/0001.png deleted file mode 100644 index 4060496..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/panel-edges-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/panel-edges-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p5/panel-edges-run/sprinter/0002.png deleted file mode 100644 index 647eea1..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/panel-edges-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/panel-edges-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p5/panel-edges-run/sprinter/0003.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/panel-edges-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/panel-edges/clean-dss-return.png b/applications/Volkov/artifacts/mame-p5/panel-edges/clean-dss-return.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/panel-edges/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/panel-edges/count-27-fits.png b/applications/Volkov/artifacts/mame-p5/panel-edges/count-27-fits.png deleted file mode 100644 index 647eea1..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/panel-edges/count-27-fits.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/panel-edges/count-28-scrolls.png b/applications/Volkov/artifacts/mame-p5/panel-edges/count-28-scrolls.png deleted file mode 100644 index 4060496..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/panel-edges/count-28-scrolls.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/panel-edges/counts-27-28.png b/applications/Volkov/artifacts/mame-p5/panel-edges/counts-27-28.png deleted file mode 100644 index de70a3f..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/panel-edges/counts-27-28.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0000.png deleted file mode 100644 index 7e36769..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0001.png deleted file mode 100644 index 11967ae..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0002.png b/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0002.png deleted file mode 100644 index 89d9f91..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0003.png b/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0003.png deleted file mode 100644 index 5075265..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0004.png b/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0004.png deleted file mode 100644 index 28a4dc9..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0005.png b/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0005.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0006.png b/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0006.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0007.png b/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0007.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-pass/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0000.png deleted file mode 100644 index 81014d2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0001.png deleted file mode 100644 index 11967ae..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0002.png b/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0002.png deleted file mode 100644 index 62a4b00..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0003.png b/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0003.png deleted file mode 100644 index 2e91bcf..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0004.png b/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0004.png deleted file mode 100644 index b937fee..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0005.png b/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0005.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0006.png b/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0006.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0007.png b/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0007.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-precommit/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0000.png deleted file mode 100644 index 79ba180..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0001.png deleted file mode 100644 index 11967ae..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0002.png deleted file mode 100644 index 11b5069..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0003.png deleted file mode 100644 index c6a7eb7..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0004.png b/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0004.png deleted file mode 100644 index fe62857..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0005.png b/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0005.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0006.png b/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0006.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0007.png b/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0007.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability-run/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability/cleanup-cycle-1.png b/applications/Volkov/artifacts/mame-p5/stability/cleanup-cycle-1.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability/cleanup-cycle-1.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability/cleanup-cycle-2.png b/applications/Volkov/artifacts/mame-p5/stability/cleanup-cycle-2.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability/cleanup-cycle-2.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability/cleanup-cycle-3.png b/applications/Volkov/artifacts/mame-p5/stability/cleanup-cycle-3.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability/cleanup-cycle-3.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability/copy-large-progress.png b/applications/Volkov/artifacts/mame-p5/stability/copy-large-progress.png deleted file mode 100644 index 89d9f91..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability/copy-large-progress.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/stability/twenty-copy-hundred-refresh.png b/applications/Volkov/artifacts/mame-p5/stability/twenty-copy-hundred-refresh.png deleted file mode 100644 index 28a4dc9..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/stability/twenty-copy-hundred-refresh.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0000.png deleted file mode 100644 index e20e03d..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0001.png deleted file mode 100644 index e99e281..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0002.png b/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0002.png deleted file mode 100644 index b13058f..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0003.png b/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0003.png deleted file mode 100644 index 2140158..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0004.png b/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0004.png deleted file mode 100644 index f6d1b58..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0005.png b/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0005.png deleted file mode 100644 index c6f1929..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0006.png b/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0006.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-openfix/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0000.png deleted file mode 100644 index e1c3be5..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0001.png deleted file mode 100644 index e99e281..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0002.png b/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0002.png deleted file mode 100644 index b13058f..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0003.png b/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0003.png deleted file mode 100644 index 2140158..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0004.png b/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0004.png deleted file mode 100644 index 58cda43..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0005.png b/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0005.png deleted file mode 100644 index 54d5a2e..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0006.png b/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0006.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-pass/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0000.png deleted file mode 100644 index e803714..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0001.png deleted file mode 100644 index e99e281..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0002.png deleted file mode 100644 index b13058f..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0003.png deleted file mode 100644 index 2140158..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0004.png b/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0004.png deleted file mode 100644 index 0e7c4bb..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0005.png b/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0005.png deleted file mode 100644 index ff7e047..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0006.png b/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0006.png deleted file mode 100644 index 0b317b3..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer-run/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer/clean-dss-return.png b/applications/Volkov/artifacts/mame-p5/viewer/clean-dss-return.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer/commander-restored.png b/applications/Volkov/artifacts/mame-p5/viewer/commander-restored.png deleted file mode 100644 index c6f1929..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer/commander-restored.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer/mdview-main.png b/applications/Volkov/artifacts/mame-p5/viewer/mdview-main.png deleted file mode 100644 index e99e281..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer/mdview-main.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer/mdview-pagedown.png b/applications/Volkov/artifacts/mame-p5/viewer/mdview-pagedown.png deleted file mode 100644 index 2140158..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer/mdview-pagedown.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p5/viewer/mdview-raw.png b/applications/Volkov/artifacts/mame-p5/viewer/mdview-raw.png deleted file mode 100644 index b13058f..0000000 Binary files a/applications/Volkov/artifacts/mame-p5/viewer/mdview-raw.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0000.png b/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0000.png deleted file mode 100644 index c468ad7..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0001.png b/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0001.png deleted file mode 100644 index 9fa4db6..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0002.png b/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0002.png deleted file mode 100644 index 4d43936..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0003.png b/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0003.png deleted file mode 100644 index 8593675..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0004.png b/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0004.png deleted file mode 100644 index a470121..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0005.png b/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0005.png deleted file mode 100644 index ee0350c..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0006.png b/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0006.png deleted file mode 100644 index 904a45e..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0007.png b/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0007.png deleted file mode 100644 index c492657..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0008.png b/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0008.png deleted file mode 100644 index ee59227..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0008.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0009.png b/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0009.png deleted file mode 100644 index 091349b..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0009.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0010.png b/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0010.png deleted file mode 100644 index cb6fa5a..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0010.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0011.png b/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0011.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-final/sprinter/0011.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0000.png deleted file mode 100644 index c468ad7..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0001.png deleted file mode 100644 index 9fa4db6..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0002.png deleted file mode 100644 index 4d43936..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0003.png deleted file mode 100644 index 8593675..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0004.png b/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0004.png deleted file mode 100644 index a470121..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0005.png b/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0005.png deleted file mode 100644 index ee0350c..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0006.png b/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0006.png deleted file mode 100644 index 904a45e..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0007.png b/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0007.png deleted file mode 100644 index c492657..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0008.png b/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0008.png deleted file mode 100644 index ee59227..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0008.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0009.png b/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0009.png deleted file mode 100644 index 091349b..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0009.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0010.png b/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0010.png deleted file mode 100644 index cb6fa5a..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0010.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0011.png b/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0011.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort-run/sprinter/0011.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort/clean-dss-return.png b/applications/Volkov/artifacts/mame-p6/sort/clean-dss-return.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort/date-ascending.png b/applications/Volkov/artifacts/mame-p6/sort/date-ascending.png deleted file mode 100644 index ee0350c..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort/date-ascending.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort/date-descending.png b/applications/Volkov/artifacts/mame-p6/sort/date-descending.png deleted file mode 100644 index 904a45e..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort/date-descending.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort/extension-ascending.png b/applications/Volkov/artifacts/mame-p6/sort/extension-ascending.png deleted file mode 100644 index 9fa4db6..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort/extension-ascending.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort/extension-descending.png b/applications/Volkov/artifacts/mame-p6/sort/extension-descending.png deleted file mode 100644 index 4d43936..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort/extension-descending.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort/independent-panels.png b/applications/Volkov/artifacts/mame-p6/sort/independent-panels.png deleted file mode 100644 index 091349b..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort/independent-panels.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort/name-ascending.png b/applications/Volkov/artifacts/mame-p6/sort/name-ascending.png deleted file mode 100644 index c468ad7..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort/name-ascending.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort/name-descending.png b/applications/Volkov/artifacts/mame-p6/sort/name-descending.png deleted file mode 100644 index ee59227..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort/name-descending.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort/name-reset-ascending.png b/applications/Volkov/artifacts/mame-p6/sort/name-reset-ascending.png deleted file mode 100644 index c492657..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort/name-reset-ascending.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort/refresh-keeps-mode.png b/applications/Volkov/artifacts/mame-p6/sort/refresh-keeps-mode.png deleted file mode 100644 index cb6fa5a..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort/refresh-keeps-mode.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort/size-ascending.png b/applications/Volkov/artifacts/mame-p6/sort/size-ascending.png deleted file mode 100644 index 8593675..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort/size-ascending.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p6/sort/size-descending.png b/applications/Volkov/artifacts/mame-p6/sort/size-descending.png deleted file mode 100644 index a470121..0000000 Binary files a/applications/Volkov/artifacts/mame-p6/sort/size-descending.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0000.png b/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0000.png deleted file mode 100644 index e9a4b88..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0001.png b/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0001.png deleted file mode 100644 index 05ff151..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0002.png b/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0002.png deleted file mode 100644 index a80968a..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0003.png b/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0003.png deleted file mode 100644 index c4d88dd..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0004.png b/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0004.png deleted file mode 100644 index 7c2056c..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0005.png b/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0005.png deleted file mode 100644 index 41d2986..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0006.png b/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0006.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-final/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0000.png b/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0000.png deleted file mode 100644 index 2ceefa9..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0001.png b/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0001.png deleted file mode 100644 index e661562..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0002.png b/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0002.png deleted file mode 100644 index d6624f3..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0003.png b/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0003.png deleted file mode 100644 index e87d078..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0004.png b/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0004.png deleted file mode 100644 index e906edd..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0005.png b/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0005.png deleted file mode 100644 index 0d7f85f..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0006.png b/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0006.png deleted file mode 100644 index 50b853f..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0007.png b/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0007.png deleted file mode 100644 index 31220ad..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0008.png b/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0008.png deleted file mode 100644 index 689feeb..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0008.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0009.png b/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0009.png deleted file mode 100644 index 32370bc..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0009.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0010.png b/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0010.png deleted file mode 100644 index b80da54..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0010.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0011.png b/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0011.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-regression-files-only/sprinter/0011.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0000.png deleted file mode 100644 index 89b5728..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0001.png deleted file mode 100644 index 1824d88..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0002.png deleted file mode 100644 index de40223..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0003.png deleted file mode 100644 index e191823..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0004.png b/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0004.png deleted file mode 100644 index 8176ab4..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0005.png b/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0005.png deleted file mode 100644 index 1b06831..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0006.png b/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0006.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select-run/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select/clean-dss-return.png b/applications/Volkov/artifacts/mame-p7/select/clean-dss-return.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select/insert-three.png b/applications/Volkov/artifacts/mame-p7/select/insert-three.png deleted file mode 100644 index a80968a..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select/insert-three.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select/inverted.png b/applications/Volkov/artifacts/mame-p7/select/inverted.png deleted file mode 100644 index e906edd..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select/inverted.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select/refresh-clears.png b/applications/Volkov/artifacts/mame-p7/select/refresh-clears.png deleted file mode 100644 index 41d2986..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select/refresh-clears.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select/sort-keeps-selection.png b/applications/Volkov/artifacts/mame-p7/select/sort-keeps-selection.png deleted file mode 100644 index c4d88dd..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select/sort-keeps-selection.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p7/select/virtual-entry-guard.png b/applications/Volkov/artifacts/mame-p7/select/virtual-entry-guard.png deleted file mode 100644 index 05ff151..0000000 Binary files a/applications/Volkov/artifacts/mame-p7/select/virtual-entry-guard.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0000.png deleted file mode 100644 index 77da0e5..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0001.png deleted file mode 100644 index 183ceeb..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0002.png deleted file mode 100644 index 9da86d6..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0003.png deleted file mode 100644 index 9d42acd..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0004.png b/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0004.png deleted file mode 100644 index ac7cb26..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0005.png b/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0005.png deleted file mode 100644 index 81d0fc7..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0006.png b/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0006.png deleted file mode 100644 index 60fd542..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0007.png b/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0007.png deleted file mode 100644 index 63dcd25..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0008.png b/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0008.png deleted file mode 100644 index 011fb04..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0008.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0009.png b/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0009.png deleted file mode 100644 index cbb846d..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0009.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0010.png b/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0010.png deleted file mode 100644 index 9a7ac8e..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0010.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0011.png b/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0011.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks-run/sprinter/0011.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks/b-unselected.png b/applications/Volkov/artifacts/mame-p8/masks/b-unselected.png deleted file mode 100644 index cbb846d..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks/b-unselected.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks/bin-selected.png b/applications/Volkov/artifacts/mame-p8/masks/bin-selected.png deleted file mode 100644 index 011fb04..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks/bin-selected.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks/cancel-preserves.png b/applications/Volkov/artifacts/mame-p8/masks/cancel-preserves.png deleted file mode 100644 index 9a7ac8e..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks/cancel-preserves.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks/clean-dss-return.png b/applications/Volkov/artifacts/mame-p8/masks/clean-dss-return.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks/default-dialog.png b/applications/Volkov/artifacts/mame-p8/masks/default-dialog.png deleted file mode 100644 index 60fd542..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks/default-dialog.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p8/masks/edited-bin-mask.png b/applications/Volkov/artifacts/mame-p8/masks/edited-bin-mask.png deleted file mode 100644 index 63dcd25..0000000 Binary files a/applications/Volkov/artifacts/mame-p8/masks/edited-bin-mask.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0000.png b/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0000.png deleted file mode 100644 index ea14768..0000000 Binary files a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0001.png b/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0001.png deleted file mode 100644 index e64dcf4..0000000 Binary files a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0002.png b/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0002.png deleted file mode 100644 index b10bf0f..0000000 Binary files a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0003.png b/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0003.png deleted file mode 100644 index 5da1402..0000000 Binary files a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0004.png b/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0004.png deleted file mode 100644 index 3685e64..0000000 Binary files a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0005.png b/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0005.png deleted file mode 100644 index 9dd7b05..0000000 Binary files a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0006.png b/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0006.png deleted file mode 100644 index ac23190..0000000 Binary files a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0007.png b/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0007.png deleted file mode 100644 index 8b2b3ab..0000000 Binary files a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0008.png b/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0008.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p9/mkdir-run/sprinter/0008.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p9/mkdir/clean-dss-return.png b/applications/Volkov/artifacts/mame-p9/mkdir/clean-dss-return.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-p9/mkdir/clean-dss-return.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p9/mkdir/created.png b/applications/Volkov/artifacts/mame-p9/mkdir/created.png deleted file mode 100644 index b10bf0f..0000000 Binary files a/applications/Volkov/artifacts/mame-p9/mkdir/created.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p9/mkdir/dialog-empty.png b/applications/Volkov/artifacts/mame-p9/mkdir/dialog-empty.png deleted file mode 100644 index e64dcf4..0000000 Binary files a/applications/Volkov/artifacts/mame-p9/mkdir/dialog-empty.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p9/mkdir/duplicate-error.png b/applications/Volkov/artifacts/mame-p9/mkdir/duplicate-error.png deleted file mode 100644 index 5da1402..0000000 Binary files a/applications/Volkov/artifacts/mame-p9/mkdir/duplicate-error.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p9/mkdir/entered-newdir.png b/applications/Volkov/artifacts/mame-p9/mkdir/entered-newdir.png deleted file mode 100644 index ac23190..0000000 Binary files a/applications/Volkov/artifacts/mame-p9/mkdir/entered-newdir.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-p9/mkdir/invalid-name.png b/applications/Volkov/artifacts/mame-p9/mkdir/invalid-name.png deleted file mode 100644 index 3685e64..0000000 Binary files a/applications/Volkov/artifacts/mame-p9/mkdir/invalid-name.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-ui/selected-footer-clean.png b/applications/Volkov/artifacts/mame-ui/selected-footer-clean.png deleted file mode 100644 index 4526c99..0000000 Binary files a/applications/Volkov/artifacts/mame-ui/selected-footer-clean.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0000.png b/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0000.png deleted file mode 100644 index 2ceefa9..0000000 Binary files a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0000.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0001.png b/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0001.png deleted file mode 100644 index e661562..0000000 Binary files a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0001.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0002.png b/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0002.png deleted file mode 100644 index f9fe8fa..0000000 Binary files a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0002.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0003.png b/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0003.png deleted file mode 100644 index 0349009..0000000 Binary files a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0003.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0004.png b/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0004.png deleted file mode 100644 index 1b20ed3..0000000 Binary files a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0004.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0005.png b/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0005.png deleted file mode 100644 index c43b1ed..0000000 Binary files a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0005.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0006.png b/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0006.png deleted file mode 100644 index 9929f6e..0000000 Binary files a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0006.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0007.png b/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0007.png deleted file mode 100644 index 3ea0119..0000000 Binary files a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0007.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0008.png b/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0008.png deleted file mode 100644 index 5f2b8a5..0000000 Binary files a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0008.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0009.png b/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0009.png deleted file mode 100644 index 4e60fc5..0000000 Binary files a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0009.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0010.png b/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0010.png deleted file mode 100644 index 4b863b7..0000000 Binary files a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0010.png and /dev/null differ diff --git a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0011.png b/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0011.png deleted file mode 100644 index 8f9b5c2..0000000 Binary files a/applications/Volkov/artifacts/mame-ui/vc-footer/sprinter/0011.png and /dev/null differ diff --git a/applications/Volkov/docs/commander-architecture.md b/applications/Volkov/docs/commander-architecture.md deleted file mode 100644 index 8e5a5eb..0000000 --- a/applications/Volkov/docs/commander-architecture.md +++ /dev/null @@ -1,678 +0,0 @@ -# Sprinter Commander: архитектура - -Статус: P1/P2 подтверждены; одностраничная модель панели P3 реализована, -проходит граничные проверки DSS - -Связанные документы: -[требования](commander-requirements.md), -[клавиши и команды](commander-keymap.md), -[план разработки](commander-roadmap.md), -[платформенные пробы P1](p1-platform-probes.md), -[экранный backend P2](p2-screen-backend.md), -[результаты P2.1](p2-screen-probe-results.md), -[результаты skeleton P2](p2-skeleton-results.md) - -## 1. Цели архитектуры - -Архитектура должна позволить получить небольшой технический PoC, не создавая -тупиков для полной версии. Главные ограничения задаются не исходным VC, а -платформой Sprinter: - -- Z80 имеет 64-КБ адресное пространство; -- W0 занят DSS/BIOS; -- стек обязан находиться в W2; -- полный список двух панелей не помещается вместе с кодом в near-память; -- код полной версии потребует банков; -- DSS имеет критический предел в восемь открытых файлов; -- текстовый экран удобно восстанавливать непосредственно из EMM-страницы. - -## 2. Принятые архитектурные решения - -### ADR-001. Новая реализация, а не перенос - -Commander пишется на C для SDCC. VC 4.05/4.99 используются как описание -поведения и состава функций, Dos Navigator — как источник архитектурных -идей. Зависимости от DOS interrupt API, сегментной модели x86, PSP, DTA, -TSR, overlays, XMS и EMS в код проекта не переносятся. - -### ADR-002. Лёгкий цикл событий вместо оконного framework - -Приложение имеет один цикл событий, набор символьных команд и явное состояние -экрана. Иерархия объектов наподобие Turbo Vision не используется. - -Причины: - -- меньше резидентного кода и данных; -- нет виртуальных методов и потоковой сериализации объектов; -- проще анализировать банковские вызовы; -- легче гарантировать освобождение ресурсов. - -### ADR-003. Целевой режим памяти — `big` - -PoC собирается с `--memory big --safe`: - -- W0 `0x0000–0x3FFF` — DSS/BIOS; -- W1 `0x4000–0x7FFF` — переключаемые банки кода; -- W2 `0x8000–0xBFFF` — HOME, DATA/BSS, heap и стек; -- W3 `0xC000–0xFFFF` — EMM-страницы данных. - -Если первоначальный skeleton временно собирается без банков, это не меняет -контрактов модулей. До принятия PoC должна быть проверена именно сборка `big`. - -### ADR-004. Панели, экран и рабочая область копирования хранятся в EMM - -Начиная с P4 PoC выделяет один блок из четырёх последовательных 16-КБ страниц. -P2/P3 использовали первые три страницы; четвёртая заранее резервируется текущим -`ScApp`, чтобы копирование не увеличивало W2 и не меняло схему памяти во время -операции: - -| Логическая страница | Назначение | Использование | -|---:|---|---| -| 0 | записи левой панели | до 640 `ScDirEntry`, 1024 байта резерва | -| 1 | записи правой панели | до 640 `ScDirEntry`, 1024 байта резерва | -| 2 | экранный буфер | первые 5120 байт | -| 3 | рабочая область copy job | первые 4096 байт в PoC | - -Допускается выделение четырёх отдельных блоков, если эксперимент покажет -проблему с последовательным блоком, но внешний интерфейс хранилища от этого -не меняется. - -### ADR-005. Запрещены долгоживущие указатели на W3 - -Указатель в диапазон `0xC000–0xFFFF` действителен только внутри функции, -которая явно владеет отображением W3. После выхода из неё указатель считается -недействительным. - -Ни `PanelState`, ни `ScApp`, ни длительная операция не содержат near-указатель -на содержимое EMM. Они хранят физическую страницу и смещение. - -### ADR-006. Экран строится как модель ячеек - -Каждая экранная ячейка представлена двумя байтами `(символ, атрибут)`. Формат -совпадает с подтверждённым форматом ESTEX `WINREST`: - -```c -typedef struct { - uint8_t ch; - uint8_t attr; -} ScCell; -``` - -Полный экран занимает `80 * 32 * 2 = 5120` байт. Renderer изменяет EMM-буфер, -помечает строки грязными, а backend выводит либо диапазон строк, либо весь -экран. Backend использует координатные ESTEX/BIOS-функции; основной пакетный -путь — проверенный `WINREST`. Прямой доступ к VRAM не используется. - -Renderer использует смысловые роли атрибутов (`frame`, `title`, `active`, -`passive`, `error`), а не числа палитры. Модуль `sc_theme`, подтверждённый -пробой P2.2, назначает ролям разные атрибуты, сохраняет затрагиваемые записи -четырёх планов и восстанавливает их при cleanup. Это не меняет формат -`ScCell` или API renderer. -Системные BIOS/ESTEX-вызовы палитры разрешены; запрещён только обход системного -экранного API через прямую адресацию VRAM и оконные дескрипторы BIOS. - -Дескрипторы BIOS `WIN_OPEN/WIN_CLOSE/WIN_*_WIN` не используются: BIOS хранит -только один описатель окна 0. Слово `WIN` в ESTEX `WINREST` означает область -экрана и не делает эту функцию частью BIOS window manager. - -### ADR-007. Источник файлов отделён от панели - -Панель не вызывает `ffirst/fnext` напрямую. Она работает через модуль -источника. В PoC реализован только `SC_SOURCE_REAL_FS`. - -Вместо таблицы виртуальных функций используется `kind` и обычный диспетчер: - -```c -typedef enum { - SC_SOURCE_REAL_FS = 0, - SC_SOURCE_SEARCH, - SC_SOURCE_ARCHIVE -} ScSourceKind; -``` - -Такой подход не создаёт near/far function pointers и упрощает размещение -реализаций по банкам. `SEARCH` и `ARCHIVE` до соответствующих этапов должны -возвращать внутреннюю ошибку `SC_ERR_UNSUPPORTED`, которая на границе -платформы при необходимости отображается как ESTEX `EUNKOP`. - -### ADR-008. Длительные операции являются jobs - -Копирование, а позднее удаление, поиск и подсчёт размеров, оформляются как -конечные автоматы: - -```text -INIT -> PREPARE -> RUN_CHUNK -> PRE_COMMIT -> COMMIT -> DONE - | | | - +-> CANCEL ----+-----------+ - +-> ERROR -----+-----------+ -``` - -Один шаг не должен надолго захватывать управление. Между блоками приложение -может обновить прогресс и проверить отмену. Даже последний успешно записанный -блок завершает только `RUN_CHUNK`: отдельный вызов переводит job через -`PRE_COMMIT` к атомарному rename. Поэтому `Esc`, полученный после отображения -`N/N`, всё ещё успевает закрыть и удалить temp до появления целевого файла. - -### ADR-009. Viewer, editor и setup сначала отдельные программы - -Основной Commander отвечает за панели и операции с файлами. Просмотрщик, -редактор и настройка первоначально запускаются через `EXEC` как отдельные -`.EXE`. Это уменьшает HOME, число банков и последствия ошибки компонента. - -После 1.0 они могут быть интегрированы банками, если измерения покажут -реальную пользу. - -## 3. Слои системы - -```text -+------------------------------------------------------+ -| app: main loop, mode, active panel, command dispatch | -+----------------------+-------------------------------+ -| panels/model | UI/dialogs/status | -+----------------------+-------------------------------+ -| sources | renderer/screen buffer | -+----------------------+-------------------------------+ -| jobs: copy/... | storage: EMM/page access | -+----------------------+-------------------------------+ -| platform: ESTEX, keyboard, exec, screen, filesystem | -+------------------------------------------------------+ -| libc + Sprinter DSS/BIOS | -+------------------------------------------------------+ -``` - -Зависимости направлены вниз. `platform` не знает о панелях; `storage` не -показывает сообщения; renderer не читает файловую систему. - -## 4. Предлагаемая структура исходников - -```text -Makefile -src/ - sprcmd.c точка входа и аварийная очистка - sc_app.c главный цикл и состояние приложения - sc_command.c преобразование событий в команды и dispatch - sc_input.c клавиатурные события - sc_screen.c EMM framebuffer и dirty rows - sc_video_system.c координатный вывод ESTEX/BIOS без оконных ID - sc_draw.c рамки, строки, числа, сокращение путей - sc_panel.c состояние и поведение панели - sc_panel_draw.c представление панели - sc_dir_store.c записи каталога в EMM - sc_sort.c сравнение и сортировка записей - sc_source.c диспетчер источников - sc_source_fs.c настоящий каталог DSS - sc_copy_job.c PoC-копирование одного файла - sc_copy_ui.c банковая оркестрация F5 и прогресс - sc_dialog.c сообщения и подтверждения - sc_path.c безопасная работа с путями 8.3 - sc_error.c нормализация errno/ESTEX ошибок - sc_platform_exec.c ESTEX EXEC/WAIT - sc_platform_screen.c wrappers WINREST/WRCHAR/SCROLL и screen mode -include/ - sc_app.h - sc_command.h - sc_glyphs.h raw-коды CP866 для рамок и стрелок - sc_input.h - sc_screen.h - sc_panel.h - sc_store.h - sc_source.h - sc_job.h - sc_path.h - sc_platform.h -banks/ - bank1_panel.c чтение/сортировка/обновление панелей - bank2_fileops.c копирование и диалоги операции -tests/ - ... target-тесты и данные для MAME -docs/ - ... -``` - -Граница файлов может уточняться по карте линковки. Нельзя объединять всё в -один большой модуль: SDCC/линкер должны иметь возможность не тянуть лишний -код и разнести холодные функции по банкам. - -## 5. Состояние приложения - -### 5.1. `ScApp` - -В W2 хранится единственный объект состояния: - -```c -typedef struct { - ScPanel panels[2]; - uint8_t active_panel; - uint8_t running; - uint8_t video_mode_saved; - uint8_t original_video_mode; - uint8_t emm_block; - uint8_t panel_page[2]; - uint8_t screen_page; - ScJob job; - int16_t last_error; -} ScApp; -``` - -Фактическое определение может отличаться, но следующие инварианты обязательны: - -- `active_panel` равен 0 или 1; -- обе панели ссылаются на разные EMM-страницы; -- `job` либо неактивен, либо является единственным владельцем своих fd; -- номера блоков и страниц обнуляются сразу после освобождения; -- cleanup можно вызвать после любой частично завершённой стадии init. - -### 5.2. `ScPanel` - -```c -typedef struct { - char path[256]; - uint16_t count; - uint16_t cursor; - uint16_t top; - uint32_t total_size; - uint8_t source_kind; - uint8_t phys_page; - uint8_t truncated; - uint8_t dirty; -} ScPanel; -``` - -Ограничения: - -- `cursor < count`, если `count != 0`; -- `cursor == 0`, если `count == 0`; -- `top <= cursor`; -- видимый курсор лежит в диапазоне `top..top+26`; -- `count <= 640` для всей линейки 0.1–1.0; -- `path` всегда завершён нулём. - -## 6. Хранилище каталогов - -Публичный интерфейс не возвращает указатель в W3: - -```c -int sc_store_get(const ScPanel *panel, uint16_t index, ScDirEntry *out); -int sc_store_put(ScPanel *panel, uint16_t index, const ScDirEntry *entry); -void sc_store_clear(ScPanel *panel); -``` - -Для пакетных операций допускается закрытая функция, которая отображает -страницу на короткое время. Она должна: - -1. Сохранить текущую страницу W3. -2. Подключить страницу панели. -3. Выполнить только вычисления над памятью. -4. Не вызывать ESTEX, renderer, банковский dispatch или пользовательский - callback. -5. Восстановить W3 перед возвратом. - -Сортировка PoC должна быть нерекурсивной и не использовать callback `qsort`, -чтобы избежать лишнего кода и банковских переходов. Предпочтительный первый -вариант — Shell sort над одной отображённой страницей. - -## 7. Чтение настоящего каталога - -Последовательность `sc_source_fs_scan()`: - -1. Проверить и нормализовать путь. -2. Сохранить имя записи под курсором, если панель уже заполнена. -3. Очистить счётчики панели, но не освобождать её страницу. -4. При необходимости добавить синтетическую запись `..`. -5. Выполнить `ffirst("*.*")/fnext` с одним общим 256-байтовым near-буфером. - Шаблон `*` запрещён: P1 и P3 подтвердили, что DSS считает его именем без - расширения и не возвращает обычные `NAME.EXT`. -6. Пропустить `.` и `..` от DSS. -7. Преобразовать `ffblk_t` в `ScDirEntry` и последовательно записать в EMM. -8. При достижении 640 записей установить `truncated` и немедленно закончить - scan: следующий `F_FIRST` сам переинициализирует глобальный итератор DSS. -9. На стабильном DSS 1.71 код 35 означает, что закончилась единственная - 16-КБ страница кэша каталога, а не весь каталог. Принять уже прочитанные - записи, установить `truncated` и добавить последней красную виртуальную - запись `>>> MORE...` с флагом `SC_ENTRY_DSS_LIMIT`. Она занимает один - слот, не является файлом и не участвует в файловых операциях. -10. Отсортировать записи: `..`, каталоги, файлы, маркер ограничения; внутри - обычных групп применить ключ и направление конкретной панели. -11. Найти прежнее имя и восстановить курсор. -12. Пометить панель и её экранные строки грязными. - -Переборы двух панелей никогда не активны одновременно. `ffblk_t` не хранится -в EMM и не является частью состояния панели. Причина и варианты снятия -предела DSS разобраны в [dss-large-directories.md](dss-large-directories.md). - -Флаг `SC_ENTRY_SELECTED` хранится в самой 24-байтовой EMM-записи и переезжает -вместе с ней при сортировке. Резидентная `ScPanel` содержит только счётчик и -32-битную сумму выбранных обычных файлов. `..` и виртуальные маркеры никогда -не выбираются; refresh в текущем срезе 0.2 явно сбрасывает выбор и статистику. -Одиночный `Insert` может менять флаг каталога, но все групповые операции -`+`, `-`, `*` сначала отбрасывают `FA_DIREC` и никогда не меняют ранее -установленный на каталоге флаг. - -Маски `+`/`-` используют ASCII case-insensitive matcher с `*` и `?` только -для файлов; специальный DOS-шаблон `*.*` совпадает также с именами файлов -без точки. Редактор -маски является общей координатной модальной поверхностью в BANK2: он рисует -поле в экранной EMM-модели, поддерживает insertion, Backspace, Delete, -Left/Right/Home/End и не создаёт BIOS-окно. - -F6/F7 используют тот же editor и один validator компонента DOS 8.3. Локальный -F6 не передаёт полные пути в DSS `RENAME`: отдельный банковый helper сохраняет -CWD в резидентный scratch, входит в каталог панели, проверяет отсутствие -target, вызывает `rename(old_basename, new_basename)` и восстанавливает CWD. -Перезапись существующей записи запрещена до системного вызова. - -F8 без выбора удаляет один объект после подтверждения: `unlink` для файла и -`rmdir` для пустого каталога. Непустой каталог и виртуальная запись не -запускают разрушительную операцию. При наличии выбора F8 одним -подтверждением сначала удаляет выбранные файлы верхнего уровня, затем -обходит все выбранные directory roots общей BFS-очередью, удаляет найденные -файлы и выполняет `rmdir` каталогов обратным проходом. При ошибке или Esc -уже выполненные действия не откатываются; CWD и EMM-страница освобождаются. - -Групповой F5 проходит EMM-store по индексам и передаёт `sc_copy_begin/step` -только записи с одновременно установленным `SC_ENTRY_SELECTED` и снятым -`FA_DIREC`. Между файлами долговременный указатель W3 не сохраняется. При -ошибке уже committed-файлы остаются, активный temp удаляется прежней job; -это первая зафиксированная политика частично успешной группы. - -F5 одного текущего каталога использует `sc_tree`: на время операции он -выделяет отдельную EMM-страницу с BFS-очередью до 1008 каталогов. Узел хранит -только parent-index, компонент DOS 8.3 и depth; путь восстанавливается перед -действием. Первый 256-байтовый диапазон страницы хранит исходный CWD. -Глубина ограничена 32, а путь — 255 символами. Файлы по-прежнему проходят -через единственную 4-КБ copy-page. Existing target не merge-ится; target -внутри source запрещается до создания root. Групповой F5 добавляет все -выбранные каталоги верхнего уровня как независимые roots той же очереди -после завершения выбранных top-level файлов. - -## 8. Renderer - -### 8.1. Интерфейс - -Минимальные операции: - -```c -void sc_screen_clear(uint8_t attr); -void sc_screen_cell(uint8_t x, uint8_t y, uint8_t ch, uint8_t attr); -void sc_screen_text(uint8_t x, uint8_t y, const char *s, uint8_t attr, - uint8_t max_width); -void sc_screen_fill(uint8_t x, uint8_t y, uint8_t width, - uint8_t ch, uint8_t attr); -int sc_screen_scroll_rows(uint8_t x, uint8_t y, uint8_t width, - uint8_t height, int8_t delta); -void sc_screen_mark_rows(uint8_t first, uint8_t count); -int sc_screen_flush(void); -``` - -Экранный буфер — источник истины. Диалог рисуется в тот же буфер поверх -панелей. Для PoC допустима перерисовка панелей после закрытия диалога вместо -отдельного стека сохранённых окон. - -### 8.2. Обновление - -- Dirty mask занимает 32 бита в W2. -- Изменение курсора помечает старую и новую строку. -- Прокрутка панели помечает строки 1–27 её половины экрана. -- При изменении `top` ровно на одну строку `sc_screen_scroll_rows()` сдвигает - одновременно EMM-модель и экранный прямоугольник, затем renderer строит - только открытую строку. PgUp/PgDn и большой скачок делают полный redraw - области панели. -- Смена каталога перерисовывает соответствующую половину и общие строки - 29–31. -- Полный `sc_video_present_full()` используется после запуска, возврата из - `.EXE` и крупных изменений. -- `sc_video_present_rect()` группирует соседние грязные строки. - -App-local WINREST wrapper проверен P1-пробой Commander: полный экран 80x32 и -окно 20x2 из offset `0x2000` восстановились корректно. P2.1 затем подтвердил -полную сверку через `RDCHAR`, оба направления прямоугольного scroll и 1000 -операций. Координатный backend принят как основной пакетный способ вывода; -ограничения и артефакты описаны в -[результатах P2.1](p2-screen-probe-results.md). - -## 9. Ввод и команды - -`sc_input.c` преобразует `getkey()` и `kbd_mod_state()` в `ScKeyEvent`: - -```c -typedef struct { - uint8_t ascii; - uint8_t scan; - uint8_t modifiers; -} ScKeyEvent; -``` - -Модификаторы хранят только Shift, Ctrl и Alt; состояния CapsLock, NumLock и -Rus/Lat не участвуют в выборе команды. - -P1 установил особенность DSS: при модифицированной клавише бит 7 raw -scan-кода установлен. Например, Ctrl+R даёт `93`, Ctrl+PgUp — `D9`, Alt+F1 -— `BB`. `sc_input` нормализует их в `13`, `59`, `3B` через `scan & 0x7F`, -но только после чтения compact modifiers. Таблица команд никогда не содержит -raw-коды с битом 7. - -`sc_command.c` превращает событие в `ScCommand`. Остальные модули не должны -сравнивать scan-коды непосредственно. Команды описаны в -[commander-keymap.md](commander-keymap.md). - -В MAME `kbd_mod_state()` в момент возврата `getkey()` сохранил активный -модификатор: compact Ctrl=`02`, Alt=`04`, Shift=`01`. Для переносимости -управляющие ASCII-коды (`Ctrl+R` и подобные) всё равно имеют явные алиасы, -не зависящие от live-состояния. - -## 10. Копирование как конечный автомат - -Состояния PoC: - -```text -IDLE - -> VALIDATE - -> CREATE_TEMP - -> OPEN_SOURCE - -> COPY_CHUNK <----+ - -> CLOSE_FILES | - -> RENAME_TEMP | - -> REFRESH | - -> DONE | - | -COPY_CHUNK -----------+ пока done < total - |-> CANCEL -> CLEANUP -> DONE - +-> ERROR -> CLEANUP -> DONE -``` - -Правила: - -- конечное имя проверяется до создания temp; -- temp создаётся в целевом каталоге с `O_CREAT|O_EXCL`; -- имя temp имеет форму `~SCxxxx.TMP` и перебирается с ограниченным числом - попыток; -- рабочая область находится в отдельной EMM-странице и временно отображается - в W3; 4-КБ массива в W2 и изменяемого буфера в банке кода нет; -- начальный логический размер блока — 4096 байт, чтобы между блоками часто - обновлять прогресс и проверять отмену; после измерений он может быть увеличен - вплоть до размера страницы; -- чтение выполняется существующим `bank_read_page()` прямо из открытого fd в - страницу; для записи нужен симметричный `bank_write_page()`, который сохраняет - и восстанавливает отображение W3; -- открыто не более двух fd; -- успешность `write()` определяется его библиотечным контрактом, а не - сырым DE ESTEX; -- при short write оставшаяся часть блока дописывается или операция завершается - ошибкой; -- после успешного закрытия temp конечное имя проверяется повторно, и только - затем temp переименовывается в него; -- FAT date/time задаются temp через `PUT_D_T` перед close; после rename - конечное имя получает исходные R/H/S/A через `ATTRIB`; -- ошибка после rename помечается как ошибка финализации уже committed-файла, - а не как полное отсутствие результата; -- любое завершение проходит через единый cleanup; -- после успеха перечитывается пассивная панель. - -P19 добавляет policy существующего файла. Safe overwrite сначала переводит -старый target в уникальный `~SBxxxx.BAK`, затем commit-ит temp. При отказе -второго rename backup возвращается на место вместе с исходными атрибутами; -после успешных метаданных нового target backup удаляется. `overwrite all` и -`skip all` — локальное состояние одного F5. Readonly не наследует молча -`overwrite all`, а повторно показывает choice-dialog. - -Для версии 0.2 этот автомат расширяется очередью файлов, обходом каталогов, -политиками overwrite/skip/rename и сохранением метаданных. - -## 11. Запуск дочерней программы - -Последовательность: - -1. Завершить или запретить активный job. -2. Сформировать проверенный абсолютный путь `.EXE`. -3. Запомнить активную панель, курсоры и ожидаемый CWD; временно сделать путь - активной панели текущим каталогом child. -4. Выполнить синхронный ESTEX `EXEC` с `B=1` и сразу сохранить его результат - до других вызовов libc. -5. Восстановить ожидаемые диск и CWD. -6. Повторно установить текстовый режим 80x32 и палитру Commander, не меняя - сохранённую палитру для финального shutdown. -7. Перечитать обе панели: внешняя программа потенциально могла изменить оба - их каталога. -8. Полностью вывести экран из screen page. -9. При ошибке показать её штатной строкой состояния Commander. - -P1 подтвердил контракт wrapper: C-сигнатура -`p1_exec(uint8_t path_mode, const char *path)` получает mode в A и path в DE, -затем перекладывает их в B и HL для ESTEX. `B=0` с коротким именем и `B=1` -с явным относительным путём работают; probe получил `0x5A`. В Commander -низкоуровневый `sc_platform_exec(path)` фиксирует `B=1`, принимает путь в HL -по `__sdcccall(1)` и возвращает код завершения как `int` в DE. - -### 11.1. Командная строка и `Ctrl+O` в 1.0 - -В VC 4.05/4.99 `Ctrl+O` переключает панели и user screen, а редактор `DosBuf` -работает вместе с prompt и историей. Dos Navigator дополнительно показывает -самостоятельный `TCommandLine` с горизонтальной прокруткой и историей. На Sprinter -родительская DSS-shell приостановлена, поэтому её нельзя превратить в интерактивный -фон простым скрытием панелей. - -Поэтому 1.0 использует собственный pseudo-shell: - -- однострочный prompt на экране панелей и полноэкранный режим делят один - редактор, историю и модель текущего каталога; -- `Ctrl+O` переключает режимы; возврат к панелям полностью строит их из - `ScPanel`, поэтому отдельная копия экрана панелей не нужна; -- builtin-команды минимум `cd`, смена диска, `dir`, `cls`, `pwd`, `help`; -- внешняя команда запускается через подтверждённый ESTEX `EXEC`; после возврата - pseudo-shell восстанавливает видеорежим, CWD и свою экранную модель; -- вывод и скролл идут через тот же безоконный screen backend; прямой VRAM и BIOS - window manager не используются; -- полноэкранные ячейки могут повторно использовать текущую screen page. До - фиксации поведения внешнего `.EXE` отдельной пробой проверяется `WINCOPY`. - -## 12. Ошибки и владение ресурсами - -Низкий уровень возвращает `0/-1` и устанавливает `errno`. UI не анализирует -Carry Flag или регистры DSS. - -Каждый ресурс имеет одного владельца: - -| Ресурс | Владелец | -|---|---| -| общий EMM-блок PoC | `ScApp` | -| физическая страница панели | `ScPanel`, без права освобождения блока | -| экранная страница | `sc_screen`, без права освобождения блока | -| рабочая страница копирования | `ScApp`/copy job, без права освобождения блока | -| fd источника и temp | активный `ScCopyJob` | -| исходный видеорежим | `ScApp` | -| временно отображённая W3 | текущая функция storage/screen | - -Cleanup выполняется в обратном порядке инициализации. Нельзя прятать -владение fd или EMM-блоком в локальном static-состоянии модуля. - -## 13. Предварительная карта кода - -HOME/W2 содержит только постоянно необходимое: - -- crt0, банковские трамплины и используемую libc; -- init/cleanup; -- главный цикл; -- ввод и базовый dispatch; -- EMM accessors; -- screen flush; -- критическую обработку ошибок. - -Предварительные банки: - -| Банк | PoC | Полная версия | -|---:|---|---| -| 1 | panel scan, sort, redraw model | панели, сортировки, режимы панели | -| 2 | copy job, operation dialog | копирование/перемещение/удаление | -| 3 | не нужен | меню, конфигурация, история | -| 4 | не нужен | поиск и дерево | -| 5 | не нужен | архивные/виртуальные источники | - -Номер банка является деталью сборки, а не публичным ABI. Банковские модули -не должны хранить долгоживущие payload-данные в локальном сегменте банка. -Краткоживущий `ffblk_t` является обычным near-буфером; закреплять его в хвосте -W1 бессмысленно, поскольку граница больших каталогов находится внутри DSS. - -После каждой сборки проверяются: - -- размер HOME вместе с DATA/BSS и heap; -- запас каждого банка до 16 КБ; -- отсутствие прямых вызовов в чужой банк; -- расположение трамплинов; -- стек W2 при всех вызовах ESTEX. - -## 14. Эволюция после PoC - -### Версия 0.2 - -- одностраничное хранилище остаётся ограничено 640 записями; -- выбранные файлы и маски; -- очередь операций; -- рекурсивный обход; -- local rename, mkdir, recursive group copy/delete выполнены; group move и - защитная policy остаются; -- транзакционная перезапись файлов и conflict-policy выполнены; directory - merge и delete/move policy остаются; -- атрибуты и время обычных файлов выполнены; метаданные каталогов — позже. - -### Версия 0.4 - -- командная строка, история и полноэкранный pseudo-shell по `Ctrl+O`; -- associations/user menu; -- внешний viewer/editor/setup; -- конфигурационный файл; -- переключение дисков. - -### Версия 0.6 - -- quick view и info source; -- дерево и поиск; -- фильтры и сравнение каталогов; -- подсчёт размеров каталогов; -- описания файлов. - -### Версия 0.8+ - -- `SC_SOURCE_SEARCH` как виртуальная панель; -- `SC_SOURCE_ARCHIVE`; -- протокол внешних helper-программ; -- мышь, помощь, локализация; -- измеренная оптимизация кэшей и renderer. - -### Версия 2.0+ - -- динамическое выделение EMM-страниц панели; -- предел 1024/2048 или отсутствие продуктового предела после измерений; -- межстраничная сортировка без полной копии каталога в W2; -- деградация при нехватке EMM и тесты частично выделенных цепочек страниц. - -## 15. Архитектурные запреты - -- Нельзя вызывать DSS при стеке вне W2. -- Нельзя держать одновременно восемь файловых дескрипторов. -- Нельзя сохранять W3 pointer в состоянии приложения. -- Нельзя менять W1 вручную из банковской функции. -- Нельзя вызывать чужой код при оставленной временной странице W3. -- Нельзя выполнять разрушительную операцию без известного cleanup-path. -- Нельзя смешивать вывод через `printf` с экранной моделью во время работы UI. -- Нельзя добавлять функцию DN только потому, что она присутствует в DN; - каждая функция должна соответствовать roadmap Commander. diff --git a/applications/Volkov/docs/commander-keymap.md b/applications/Volkov/docs/commander-keymap.md deleted file mode 100644 index f81ebf3..0000000 --- a/applications/Volkov/docs/commander-keymap.md +++ /dev/null @@ -1,324 +0,0 @@ -# Sprinter Commander: клавиши и команды - -Статус: PoC и первые команды 0.2 подтверждены в MAME - -Связанные документы: -[требования](commander-requirements.md), -[архитектура](commander-architecture.md), -[план разработки](commander-roadmap.md) - -## 1. Назначение - -Документ отделяет физические клавиши Sprinter от логических команд -Commander. Только модуль `sc_input.c` знает scan-коды, а только -`sc_command.c` знает таблицу их назначения. - -Панели и операции получают значения `ScCommand`, а не результаты `getkey()`. -Это позволяет позднее добавить мышь, переназначение клавиш и альтернативную -клавиатуру без изменений файловых функций. - -## 2. Формат события - -```c -enum { - SC_MOD_SHIFT = 0x01, - SC_MOD_CTRL = 0x02, - SC_MOD_ALT = 0x04 -}; - -typedef struct { - uint8_t ascii; - uint8_t scan; - uint8_t modifiers; -} ScKeyEvent; -``` - -Правила нормализации: - -- обычный символ записывается в `ascii`; -- расширенная клавиша записывается в `scan`, `ascii == 0`; -- при активном Ctrl/Alt/Shift DSS может установить бит 7 scan-кода; - диагностическая трассировка показывает raw-значение, а `sc_input` - передаёт командам базовый `scan & 0x7F`; -- левый и правый Shift объединяются в `SC_MOD_SHIFT`; -- левый и правый Ctrl объединяются в `SC_MOD_CTRL`; -- левый и правый Alt объединяются в `SC_MOD_ALT`; -- CapsLock, NumLock, ScrollLock, Insert и Rus/Lat не являются модификаторами - команды; -- управляющий ASCII-код имеет приоритет над live-состоянием модификатора; -- неизвестная комбинация преобразуется в `SC_CMD_NONE`. - -## 3. Подтверждённые расширенные scan-коды - -Значения уже объявлены в ``: - -| Клавиша | Scan-код | Макрос | -|---|---:|---| -| F1 | `0x3B` | `KEY_F1` | -| F2 | `0x3C` | `KEY_F2` | -| F3 | `0x3D` | `KEY_F3` | -| F4 | `0x3E` | `KEY_F4` | -| F5 | `0x3F` | `KEY_F5` | -| F6 | `0x40` | `KEY_F6` | -| F7 | `0x41` | `KEY_F7` | -| F8 | `0x42` | `KEY_F8` | -| F9 | `0x43` | `KEY_F9` | -| F10 | `0x44` | `KEY_F10` | -| End | `0x51` | `KEY_END` | -| Down | `0x52` | `KEY_DOWN` | -| PgDn | `0x53` | `KEY_PGDN` | -| Left | `0x54` | `KEY_LEFT` | -| Delete | `0x55` | `KEY_DEL` | -| Right | `0x56` | `KEY_RIGHT` | -| Home | `0x57` | `KEY_HOME` | -| Up | `0x58` | `KEY_UP` | -| PgUp | `0x59` | `KEY_PGUP` | -| Insert | `0x50` | `KEY_INS` | - -Все значения таблицы, включая `Insert` и `Delete`, подтверждены P1-пробой в -MAME. `F11` и `F12` в P1 не проверялись и остаются неподтверждёнными до -появления команды, которая их использует. - -Фактическая последовательность MAME для навигации была: -`Up=58`, `Down=52`, `Left=54`, `Right=56`, `Home=57`, `End=51`, -`PgUp=59`, `PgDn=53`, `Insert=50`, `Delete=55`. F1–F10 дали непрерывный -диапазон `3B..44`. - -## 4. Логические команды - -Численные значения enum не являются стабильным ABI до версии 0.2. - -```c -typedef enum { - SC_CMD_NONE = 0, - - SC_CMD_CURSOR_UP, - SC_CMD_CURSOR_DOWN, - SC_CMD_CURSOR_LEFT, - SC_CMD_CURSOR_RIGHT, - SC_CMD_PAGE_UP, - SC_CMD_PAGE_DOWN, - SC_CMD_HOME, - SC_CMD_END, - SC_CMD_PANEL_SWITCH, - SC_CMD_OPEN, - SC_CMD_PARENT, - SC_CMD_ROOT, - SC_CMD_REFRESH, - SC_CMD_CANCEL, - - SC_CMD_SELECT, - SC_CMD_SELECT_MASK, - SC_CMD_UNSELECT_MASK, - SC_CMD_INVERT_SELECTION, - - SC_CMD_HELP, - SC_CMD_USER_MENU, - SC_CMD_VIEW, - SC_CMD_EDIT, - SC_CMD_COPY, - SC_CMD_MOVE_RENAME, - SC_CMD_MKDIR, - SC_CMD_DELETE, - SC_CMD_MAIN_MENU, - SC_CMD_QUIT, - - SC_CMD_LEFT_DRIVE, - SC_CMD_RIGHT_DRIVE, - SC_CMD_SWAP_PANELS, - SC_CMD_TOGGLE_SHELL, - SC_CMD_QUICK_VIEW, - SC_CMD_INFO_PANEL, - SC_CMD_FIND, - SC_CMD_TREE -} ScCommand; -``` - -Дополнительные команды вводятся при появлении соответствующей функции, а не -заранее в обработчиках-заглушках. - -## 5. Клавиши PoC 0.1 - -### 5.1. Навигация панели - -| Клавиша | Команда | Поведение PoC | -|---|---|---| -| `Up` | `SC_CMD_CURSOR_UP` | На одну запись вверх | -| `Down` | `SC_CMD_CURSOR_DOWN` | На одну запись вниз | -| `PgUp` | `SC_CMD_PAGE_UP` | На 27 записей вверх | -| `PgDn` | `SC_CMD_PAGE_DOWN` | На 27 записей вниз | -| `Home` | `SC_CMD_HOME` | Первая запись | -| `End` | `SC_CMD_END` | Последняя запись | -| `Left` | `SC_CMD_NONE` | Не используется в одноколоночной панели PoC | -| `Right` | `SC_CMD_NONE` | Не используется в одноколоночной панели PoC | -| `Tab` | `SC_CMD_PANEL_SWITCH` | Переключить активную панель | -| `Enter` | `SC_CMD_OPEN` | Войти в каталог или запустить `.EXE` | -| `Ctrl+PgUp` | `SC_CMD_PARENT` | Перейти в родительский каталог | -| `Backspace` | `SC_CMD_PARENT` | То же, пока командной строки нет | -| `Ctrl+R` | `SC_CMD_REFRESH` | Перечитать активную панель | -| `Ctrl+F3` | `SC_CMD_SORT_NAME` | Имя; повторное нажатие меняет направление | -| `Ctrl+F4` | `SC_CMD_SORT_EXTENSION` | Расширение; повтор меняет направление | -| `Ctrl+F5` | `SC_CMD_SORT_DATE` | Дата/время; повтор меняет направление | -| `Ctrl+F6` | `SC_CMD_SORT_SIZE` | Размер; повтор меняет направление | -| `Insert` | `SC_CMD_SELECT` | Выбрать/снять текущую запись и перейти вниз | -| `*` | `SC_CMD_INVERT_SELECTION` | Инвертировать только файлы панели; каталоги не менятся | - -`SC_CMD_CURSOR_LEFT/RIGHT` сохраняются в enum для будущего многоколоночного -режима, но PoC их не генерирует. - -### 5.2. Функциональные клавиши PoC - -| Клавиша | Команда | Состояние | -|---|---|---| -| `F1` | `SC_CMD_HELP` | Зарезервирована, неактивна | -| `F2` | `SC_CMD_USER_MENU` | Зарезервирована, неактивна | -| `F3` | `SC_CMD_VIEW` | Зарезервирована, неактивна | -| `F4` | `SC_CMD_EDIT` | Зарезервирована, неактивна | -| `F5` | `SC_CMD_COPY` | Один файл/каталог либо группа выбранных файлов и directory roots | -| `F6` | `SC_CMD_MOVE_RENAME` | Локальный rename одной записи; move/group позже в 0.2 | -| `F7` | `SC_CMD_MKDIR` | Создать каталог; 0.2, выполнено | -| `F8` | `SC_CMD_DELETE` | Одна запись либо выбранная группа; каталоги группы удаляются рекурсивно после одного подтверждения | -| `F9` | `SC_CMD_MAIN_MENU` | Зарезервирована, неактивна | -| `F10` | `SC_CMD_QUIT` | Открыть подтверждение штатного выхода | - -PoC не должен выполнять разрушительное действие по неактивной клавише. - -### 5.3. Общие клавиши PoC - -| Клавиша | Команда | Контекст | -|---|---|---| -| `Esc` | `SC_CMD_CANCEL` | Закрыть сообщение или отменить job | -| `Esc` | `SC_CMD_NONE` | Обычный режим панелей | -| `Enter` | подтвердить | Диалог сообщения | -| `Y`/`N` | да/нет | Диалог подтверждения, если он появится | - -## 6. Контекстная обработка - -Одна клавиша может иметь разное действие в зависимости от режима. Приоритет -обработчиков фиксирован: - -1. Активная аварийная ошибка. -2. Модальный диалог. -3. Активный job. -4. Командная строка или полноэкранный pseudo-shell версии 1.0. -5. Активная панель. -6. Глобальные команды приложения. - -Примеры: - -- Во время копирования `Esc` запрашивает отмену, но `F10` не завершает - процесс немедленно. -- В диалоге `Enter` подтверждает кнопку, а не открывает файл под курсором. -- После закрытия диалога то же событие не передаётся панели повторно. -- `F10` во время job игнорируется с подсказкой отменять операцию через `Esc`; - подтверждение выхода открывается только после завершения или отмены job. -- В режиме панелей печатный символ передаётся однострочному редактору; - `Enter` выполняет непустую строку, а при пустой строке открывает запись. -- В pseudo-shell `Up/Down` листают историю команд, а не панель; `Ctrl+O` - возвращает панели с сохранёнными путями и курсорами. -- `Esc` сначала очищает непустую команду, затем закрывает полноэкранный режим; - событие не передаётся следующему контексту повторно. - -## 7. Нормализация управляющих ASCII-кодов - -На части клавиатур `Ctrl+буква` может приходить как ASCII `0x01–0x1A` без -надёжного сохранения состояния Ctrl. Поэтому ввод должен распознавать -следующие значения независимо от `kbd_mod_state()`: - -| ASCII | Комбинация | Команда | -|---:|---|---| -| `0x12` | `Ctrl+R` | `SC_CMD_REFRESH` | -| `0x15` | `Ctrl+U` | `SC_CMD_SWAP_PANELS`, после PoC | -| `0x0F` | `Ctrl+O` | `SC_CMD_TOGGLE_SHELL`, обязательно к 1.0 | -| `0x11` | `Ctrl+Q` | `SC_CMD_QUICK_VIEW`, после PoC | -| `0x0C` | `Ctrl+L` | `SC_CMD_INFO_PANEL`, после PoC | - -`Tab` (`0x09`), `Enter` (`0x0D`), `Esc` (`0x1B`) и `Backspace` (`0x08`) -обрабатываются как обычные управляющие ASCII-клавиши. - -P1 в MAME показал второй, фактически основной для текущего DSS путь: - -| Комбинация | ASCII | Raw scan | Base scan | `kbd_mod_state()` | Compact | -|---|---:|---:|---:|---:|---:| -| `Ctrl+R` | `00` | `93` | `13` | `0028` | `02` | -| `Ctrl+PgUp` | `00` | `D9` | `59` | `0028` | `02` | -| `Alt+F1` | `00` | `BB` | `3B` | `0014` | `04` | -| `Esc` | `1B` | `01` | `01` | `0000` | `00` | - -Таким образом, `sc_input` обязан поддерживать оба представления: сначала -управляющий ASCII-код, затем пару `(base scan, modifiers)`. Проверять raw -scan `93/D9/BB` прямо в таблице команд нельзя: базовый scan с модификатором -получается снятием бита 7. Скриншоты пробы находятся в -[`artifacts/mame-p1/keyboard-commander`](../artifacts/mame-p1/keyboard-commander/sprinter/0003.png). - -## 8. Предварительная карта полной версии - -Эта таблица резервирует привычные сочетания VC, но не является требованием -PoC. - -| Клавиша | Команда | Планируемая версия | -|---|---|---:| -| `Insert` | Выделить/снять выделение и перейти вниз | 0.2, выполнено | -| `Gray +` или `+` | Выделить по маске | 0.2, обычный `+` выполнен | -| `Gray -` или `-` | Снять выделение по маске | 0.2, обычный `-` выполнен | -| `Gray *` или `*` | Инвертировать выделение | 0.2, `*` выполнено | -| `F1` | Помощь | 0.4 | -| `F2` | Пользовательское меню | 0.4 | -| `F3` | Просмотр | 0.4 | -| `F4` | Редактор | 0.4 | -| `F5` | Копирование | 0.1/0.2 | -| `F6` | Перемещение/переименование | 0.2, локальный rename выполнен | -| `F7` | Создать каталог | 0.2, выполнено | -| `F8` | Удалить | 0.2, single и recursive group выполнены | -| `F9` | Главное меню | 0.4 | -| `F10` | Выход | 0.1 | -| `Alt+F1` | Диск левой панели | 0.4 | -| `Alt+F2` | Диск правой панели | 0.4 | -| `Alt+F7` | Поиск файлов | 0.6 | -| `Alt+F10` | Дерево каталогов | 0.6 | -| `Ctrl+R` | Перечитать панель | 0.1 | -| `Ctrl+F3..F6` | Name/ext/date/size, повтор меняет направление | 0.2 | -| `Ctrl+U` | Обменять панели местами | 0.4 | -| `Ctrl+O` | Панели / интерактивный полноэкранный pseudo-shell | 0.4/1.0 | -| `Ctrl+PgUp` | Родительский каталог | 0.1 | -| `Ctrl+\` | Корень текущего диска | 0.2 | -| `Ctrl+Q` | Quick view | 0.6 | -| `Ctrl+L` | Информационная панель | 0.6 | - -Shift-варианты функциональных клавиш не резервируются до появления -конкретной необходимости. Это предотвращает преждевременное копирование всей -таблицы VC без соответствующей функции. - -## 9. Нижняя строка клавиш - -Строка 31 содержит десять сегментов шириной по восемь символов. В PoC: - -```text -1 Help 2 Menu 3 View 4 Edit 5 Copy 6 Ren 7 Mkdir 8 Delete9 Menu 10 Quit -``` - -Это логическая схема, а не точная строка байтов. Названия сокращаются до -доступной ширины. Поддерживаемые действия (`F5`, `F10`) рисуются активным -цветом, остальные — неактивным. - -После добавления модификатора строка клавиш может временно показывать -назначения `Alt`/`Ctrl`, но это не входит в PoC. - -## 10. Проверки клавиатуры P1/P2 - -В P1 на MAME подтверждены F1–F10, восемь навигационных клавиш, -Insert/Delete, Esc, Ctrl+R, Ctrl+PgUp и Alt+F1. Отдельная проба также -подтвердила compact-состояния Shift=`01`, Ctrl=`02`, Alt=`04`. - -В P2 остаются поведенческие проверки уже внутри UI: - -1. `Tab`, `Enter` и `Backspace` во всех контекстах. -2. Одновременное удержание нескольких модификаторов. -3. CapsLock, NumLock и Rus/Lat. -4. Отсутствие повторного выполнения события после закрытия диалога. -5. Keyboard repeat на границах списка. -6. Приём `Esc` во время копирования большого файла. -7. Повтор P1-набора на реальном Sprinter. - -Любое расхождение с таблицей сначала оформляется отдельным репро и только -затем учитывается в Commander. diff --git a/applications/Volkov/docs/commander-requirements.md b/applications/Volkov/docs/commander-requirements.md deleted file mode 100644 index 699087d..0000000 --- a/applications/Volkov/docs/commander-requirements.md +++ /dev/null @@ -1,427 +0,0 @@ -# Sprinter Commander: требования к PoC и границы проекта - -Статус: требования PoC зафиксированы; gate P1 и ядро P2.1 пройдены в MAME - -Целевая платформа: Sprinter Sp2000, Z80, DSS 1.71.57 - -Инструментарий: SDCC 4.5, `sprinter-cc`, MAME 0.287 / BIOS 3.06 - -Связанные документы: -[архитектура](commander-architecture.md), -[клавиши и команды](commander-keymap.md), -[план разработки](commander-roadmap.md), -[экранный backend P2](p2-screen-backend.md), -[результаты P2.1](p2-screen-probe-results.md), -[результаты skeleton P2](p2-skeleton-results.md), -[результаты файловых панелей P3](p3-panel-results.md) - -## 1. Назначение документа - -Документ фиксирует проверяемые требования к первой работающей версии -файлового менеджера для Sprinter. Рабочее имя проекта — **Sprinter -Commander**, рабочее имя исполняемого файла — `SPRCMD.EXE`. - -PoC не является портом Volkov Commander. Это новая программа на C, -повторяющая привычную модель двухпанельного файлового менеджера с учётом -архитектуры памяти и API Sprinter. - -## 2. Референсы и правила их использования - -### 2.1. Volkov Commander 4.05 - -Используется как минимальный поведенческий образец: - -- компоновка двух панелей; -- клавиатурная навигация; -- назначение функциональных клавиш; -- общий сценарий работы пользователя. - -### 2.2. Volkov Commander 4.99 - -Используется как образец декомпозиции и источник функций для дальнейших -версий: - -- отдельные подсистемы панелей, экрана, клавиатуры и диалогов; -- отдельные операции копирования, удаления и изменения атрибутов; -- просмотрщик, редактор, поиск и дерево каталогов; -- описания файлов и архивные панели; -- разделение резидентного ядра и загружаемых частей. - -### 2.3. Dos Navigator 1.51 - -Используется только как архитектурный референс: - -- единый цикл событий и команды приложения; -- отделение модели панели от её представления; -- абстракция источника файлов; -- длительные операции с прогрессом и отменой; -- виртуальные панели для архивов и результатов поиска; -- история, конфигурация и внешние инструменты. - -Объектная система Turbo Vision, DOS overlays, XMS/EMS-код и прикладные -модули DN не переносятся. - -Исходный код, строки интерфейса и ресурсы референсов не копируются без -отдельного решения по лицензированию. Реализация проекта должна оставаться -собственной реализацией по описанному поведению. - -## 3. Определения версий - -**PoC 0.1** доказывает техническую реализуемость панелей, EMM-хранилища, -экранного буфера, копирования и запуска программ. - -**Рабочая версия** пригодна для повседневной работы с файлами и включает -безопасные групповые и рекурсивные операции. - -**Полная версия 1.0** соответствует основным возможностям VC 4.99, но не -пытается повторить весь набор утилит Dos Navigator. - -## 4. Пользовательский сценарий PoC - -После запуска программа переключается в текстовый режим 80x32, выделяет -необходимую память и показывает две панели. Обе панели первоначально -открывают текущий каталог запуска. - -Пользователь может: - -1. Перемещать курсор по активной панели. -2. Переключать активную панель клавишей `Tab`. -3. Входить в каталог клавишей `Enter`. -4. Возвращаться в родительский каталог. -5. Перечитывать содержимое активной панели. -6. Копировать один обычный файл в каталог пассивной панели. -7. Запускать выбранный `.EXE` и возвращаться в Commander. -8. Выйти по `F10` с освобождением всех ресурсов. - -## 5. Компоновка экрана PoC - -Экран имеет 80 столбцов и 32 строки. - -| Строки | Назначение | -|---:|---| -| 0 | Заголовки и текущие пути обеих панелей | -| 1–27 | Списки файлов, по 27 видимых записей | -| 28 | Итоги панелей: число записей и общий размер | -| 29 | Информация о текущей записи активной панели | -| 30 | Строка состояния, ошибок и прогресса | -| 31 | Подсказки функциональных клавиш | - -Левая панель занимает столбцы 0–39, правая — 40–79. Точные символы рамок -и палитра не являются контрактом PoC, но все символы должны корректно -отображаться в CP866. - -Renderer задаёт смысловые роли атрибутов независимо от конкретной палитры. -P2.2 подтвердил многоцветные планы в MAME: тема сохраняет исходные записи, -задаёт обе FLASH-пары и восстанавливает палитру при выходе. До аппаратного -подтверждения активная и пассивная панели всё равно различаются не только -цветом, но и глифами/маркерами. - -Полная перерисовка выполняется из собственного экранного буфера через -`sc_video`. Commander не использует дескрипторы окон BIOS: панели, меню и -диалоги — логические прямоугольники приложения. `sc_video` выводит их в -координаты глобального экрана через ESTEX/BIOS-функции без идентификатора -окна; прямой доступ к VRAM не используется. -Изменение курсора или одной строки панели не должно требовать повторного -чтения каталога. - -Прокрутка списка на одну строку может использовать прямоугольный ESTEX -`SCROLL 55h`, как `mdview2`, но рамка и соседняя панель остаются неподвижны, -а экранный EMM-буфер обновляется синхронно. Эта служба не является BIOS- -дескриптором окна; её аппаратная реализация исходниками `mdview2` не -доказана. Псевдографика задаётся raw-кодами CP866 из единого `sc_glyphs.h`. - -## 6. Функциональные требования PoC - -### REQ-BOOT: запуск и завершение - -- **REQ-BOOT-01.** Программа должна явно установить текстовый режим 80x32. -- **REQ-BOOT-02.** Перед изменением видеорежима и текущего каталога должны - быть сохранены исходные значения, если DSS позволяет их получить. -- **REQ-BOOT-03.** При штатном выходе освобождаются все EMM-блоки и закрываются - все открытые файлы. -- **REQ-BOOT-04.** Ошибка инициализации не должна оставлять изменённый - видеорежим или выделенную память. -- **REQ-BOOT-05.** Если обязательные четыре EMM-страницы выделить нельзя, - программа выводит понятную ошибку и завершает работу. -- **REQ-BOOT-06.** `F10` в обычном режиме открывает подтверждение выхода; - положительный ответ запускает штатный cleanup, отрицательный возвращает к - панелям. - -### REQ-PANEL: панели - -- **REQ-PANEL-01.** Панели имеют независимые пути, курсор и позицию прокрутки. -- **REQ-PANEL-02.** Активная панель визуально отличается от пассивной. -- **REQ-PANEL-03.** Каталоги отображаются перед обычными файлами. -- **REQ-PANEL-04.** Внутри групп записи сортируются по имени без учёта - регистра ASCII. -- **REQ-PANEL-05.** Для записи отображаются имя, размер, дата, время и - атрибут каталога/файла в объёме, который помещается в ширину панели. -- **REQ-PANEL-06.** После перечитывания каталог по возможности сохраняет - курсор на прежнем имени; если оно исчезло — на ближайшей допустимой - позиции. -- **REQ-PANEL-07.** Полный scan DSS использует `*.*`; шаблон `*` не считается - эквивалентным, поскольку не возвращает обычные имена с расширением. -- **REQ-PANEL-08.** Пустой каталог отображается корректно и остаётся - управляемым. -- **REQ-PANEL-09.** Родительский каталог представлен синтетической записью - `..`, кроме корня диска. -- **REQ-PANEL-10.** Записи `.` и `..`, возвращённые DSS, не должны - дублировать синтетическую запись. -- **REQ-PANEL-11.** Во всей линейке 0.1–1.0 панель хранит не более - 640 записей, включая синтетическую `..`. При превышении лимита - показывается предупреждение; запись за границу EMM-страницы - запрещена. Многостраничные панели и предел более 640 записей - относятся только к версии 2.0+. -- **REQ-PANEL-12.** Если DSS завершил `F_FIRST/F_NEXT` кодом 35 - (`TOO_MANY_FILES_IN_DIR`), уже прочитанные записи остаются доступны, а - последней добавляется красная виртуальная запись `>>> MORE...`. Она - сообщает о недоступном остатке каталога, не сортируется вместе с файлами - и не может быть целью файловой операции. - -### REQ-NAV: навигация - -- **REQ-NAV-01.** Поддерживаются `Up`, `Down`, `PgUp`, `PgDn`, `Home`, `End`. -- **REQ-NAV-02.** `Tab` переключает активную панель без изменения каталогов. -- **REQ-NAV-03.** `Enter` на каталоге открывает его в активной панели. -- **REQ-NAV-04.** `Ctrl+PgUp` и `Backspace` открывают родительский каталог. -- **REQ-NAV-05.** `Ctrl+R` перечитывает активную панель. -- **REQ-NAV-06.** Навигация внутри уже прочитанной панели не обращается к - файловой системе. -- **REQ-NAV-07.** Операция с активной панелью не должна неожиданно менять - путь пассивной панели. - -### REQ-FS: работа с DSS и путями - -- **REQ-FS-01.** PoC работает с именами DOS 8.3. Длинные имена не входят в - требования до отдельного подтверждения поддержки DSS. -- **REQ-FS-02.** Максимальный внутренний путь — 255 байт плюс завершающий - ноль. -- **REQ-FS-03.** Склейка пути и имени проверяет переполнение до обращения к - DSS. -- **REQ-FS-04.** Контекст текущего каталога DSS считается глобальным - ресурсом. После временной смены каталога модуль обязан восстановить - ожидаемый каталог приложения. -- **REQ-FS-05.** Одновременно приложение держит не более трёх файловых - дескрипторов; штатная операция копирования использует два. -- **REQ-FS-06.** Конец перебора каталога отличается от настоящей ошибки - `fnext()` по `errno` и контексту операции. - -### REQ-COPY: безопасное копирование одного файла - -- **REQ-COPY-01.** `F5` копирует выбранный обычный файл в текущий каталог - пассивной панели. -- **REQ-COPY-02.** Копирование каталогов и групп файлов в PoC запрещено с - явным сообщением. -- **REQ-COPY-03.** Если конечное имя уже существует, PoC отказывается от - операции и не изменяет существующий файл. -- **REQ-COPY-04.** Данные копируются блоками, а общий размер и позиция имеют - 32-битный тип. -- **REQ-COPY-05.** Поддерживаются файлы больше 64 КБ. -- **REQ-COPY-06.** Целевой файл сначала создаётся под уникальным временным - именем через семантику `O_EXCL`, после успешного закрытия переименовывается - в конечное имя. -- **REQ-COPY-07.** При ошибке или отмене временный файл закрывается и - удаляется. -- **REQ-COPY-08.** Между блоками проверяется `Esc`, а строка состояния - обновляет прогресс. -- **REQ-COPY-09.** Нулевой файл копируется корректно. -- **REQ-COPY-10.** В PoC сохранение времени и атрибутов файла не обязательно, - но должно быть предусмотрено в интерфейсе операции. -- **REQ-COPY-11.** Непосредственно перед переименованием временного файла - конечное имя проверяется повторно. Если оно появилось, операция завершается - без его изменения. -- **REQ-COPY-12.** Данные между открытыми файлами проходят непосредственно - через выделенную EMM-страницу, отображаемую в W3. Полноразмерный блоковый - буфер в W2 и изменяемый буфер внутри банка кода запрещены. -- **REQ-COPY-13.** Начиная с P18 успешно скопированный обычный файл получает - исходные FAT date/time и атрибуты readonly/hidden/system/archive. Время - задаётся temp до `close`, атрибуты — конечному имени после commit. -- **REQ-COPY-14.** Начиная с P19 существующий файл обрабатывается policy - overwrite/skip/rename; варианты overwrite-all и skip-all ограничены одним - F5. Overwrite использует backup в том же каталоге и rollback при отказе - commit, а readonly требует явного повторного решения. - -### REQ-EXEC: запуск программ - -- **REQ-EXEC-01.** `Enter` на имени с расширением `.EXE` запускает программу - через ESTEX `EXEC`. -- **REQ-EXEC-02.** Перед запуском Commander сохраняет логическое состояние - панелей и закрывает временные ресурсы операции. -- **REQ-EXEC-03.** После возврата восстанавливаются видеорежим, экран и - ожидаемый текущий каталог. -- **REQ-EXEC-04.** Активная панель перечитывается после возврата; пассивная - перечитывается, если запущенная программа могла изменить её каталог. -- **REQ-EXEC-05.** Ошибка загрузки `.EXE` показывается без завершения - Commander. -- **REQ-EXEC-06.** Обычный файл неизвестного типа в PoC не запускается. - -### REQ-UI: сообщения и состояние - -- **REQ-UI-01.** Ошибки не выводятся поверх структуры панели случайными - вызовами `printf`; они проходят через единый модуль сообщений. -- **REQ-UI-02.** Для каждой ошибки показываются действие, имя объекта и - текст/код ошибки, если они доступны. -- **REQ-UI-03.** `Esc` закрывает сообщение или отменяет текущую длительную - операцию; в обычном состоянии он не завершает программу. -- **REQ-UI-04.** Неподдерживаемые в PoC функциональные клавиши либо не - показаны, либо показаны неактивным цветом. -- **REQ-UI-05.** F6 local rename и F7 mkdir принимают только один проверенный - компонент DOS 8.3; `..`, маркер DSS и неоднозначная группа не передаются - файловой системе. -- **REQ-UI-06.** Rename обязан проверить отсутствие нового имени до DSS - `RENAME`, сохранить CWD и восстановить его после успеха или ошибки. -- **REQ-UI-05.** После закрытия сообщения восстанавливается экран из - логического экранного буфера. - -### REQ-MEM: память - -- **REQ-MEM-01.** Целевая конфигурация PoC — `--memory big --safe`. -- **REQ-MEM-02.** W2 содержит резидентное ядро, обычные данные, heap и стек; - банковские функции размещаются в W1; W3 используется только для EMM- - данных. -- **REQ-MEM-03.** PoC выделяет четыре 16-КБ страницы: две для панелей, одну - для экранного буфера и одну для рабочей области копирования. -- **REQ-MEM-04.** Прикладной код не хранит указатели в диапазон - `0xC000–0xFFFF` между вызовами хранилища. -- **REQ-MEM-05.** Любой модуль, временно меняющий W3, восстанавливает прежнюю - страницу до возврата и до вызова чужого кода. -- **REQ-MEM-06.** Повторное чтение каталогов не выделяет новые блоки. -- **REQ-MEM-07.** Освобождение частично созданного приложения идемпотентно на - уровне состояния приложения: каждый реально выделенный блок освобождается - ровно один раз. -- **REQ-MEM-08.** Рабочая EMM-страница копирования принадлежит общему блоку - `ScApp`; copy job не выделяет и не освобождает её на каждый файл. - -## 7. Формат записи каталога PoC - -Логический формат записи фиксирован как 24 байта: - -```c -typedef struct { - char name[13]; - uint32_t size; - uint16_t date; - uint16_t time; - uint8_t attr; - uint8_t flags; - uint8_t reserved; -} ScDirEntry; -``` - -На этапе реализации обязательна compile-time или build-time проверка -`sizeof(ScDirEntry) == 24`. - -Флаги PoC: - -- `SC_ENTRY_PARENT` — синтетическая запись `..`; -- `SC_ENTRY_SELECTED` — выбор обычной записи в версии 0.2; -- `SC_ENTRY_DSS_LIMIT` — виртуальная запись о недоступном остатке каталога; -- остальные биты должны быть нулевыми. - -В одной странице используется не более 640 записей; последние 1024 байта -остаются резервом и не входят в массив записей. Служебное -состояние панели хранится в W2, а не в начале EMM-страницы. - -## 8. Нефункциональные требования - -- **REQ-NF-01.** Исходный код и комментарии проекта ведутся на русском. -- **REQ-NF-02.** Сборка выполняется штатным `sprinter-cc` без отдельной - проприетарной цепочки. -- **REQ-NF-03.** После каждого этапа сохраняется отчёт размеров HOME и - банков; переполнение банка является ошибкой сборки. -- **REQ-NF-04.** Реакция на навигационную клавишу в уже прочитанной панели не - должна визуально зависать; целевой ориентир в MAME — менее 100 мс. -- **REQ-NF-05.** Операция дольше 500 мс должна показывать состояние или - прогресс. -- **REQ-NF-06.** Все воспроизводимые дефекты DSS или SDCC подтверждаются - отдельным тестом, `.asm`, дампом или другим артефактом. -- **REQ-NF-07.** Рабочая версия должна проверяться в MAME и на реальном - Sprinter; PoC может быть принят по MAME с обязательным последующим - hardware smoke-test. - -## 9. Что не входит в PoC 0.1 - -- удаление файлов; -- перезапись существующих файлов; -- перемещение и переименование пользователем; -- создание каталогов; -- рекурсивные операции; -- групповое выделение и маски; -- командная строка и история; -- встроенные viewer/editor; -- дерево и поиск; -- архивы и виртуальные панели; -- мышь; -- конфигурационный файл, темы и локализация; -- длинные имена; -- фоновые операции и многозадачность. - -## 10. Критерии приёмки PoC - -PoC считается завершённым, если одновременно выполнены условия: - -1. Программа стабильно запускается в MAME с двухпанельным экраном. -2. Обе панели независимо проходят дерево каталогов и корректно работают с - пустым каталогом. -3. Хранилище панели проверено синтетическим source на границах 640/641 без - выхода за EMM-страницу; на DSS 1.71 превышение его границы 512 физических - FAT-записей даёт управляемый частичный список с красным маркером. - Оба случая подтверждены MAME-пробами в - [результатах P3](p3-panel-results.md). -4. Сто последовательных перечитываний и переходов не меняют число свободных - EMM-страниц. -5. Копируются и побайтно совпадают файлы размеров 0, 1, 4095, 4096, 65535, - 65536 байт и более 1 МБ. -6. Отмена и ошибка записи не оставляют временный файл. -7. Существующий целевой файл никогда не меняется. -8. Тестовый `.EXE` запускается, завершается, после чего Commander полностью - восстанавливает экран и продолжает работу. -9. Все пути ошибок возвращают приложение в управляемое состояние. -10. На выходе восстановлен видеорежим, закрыты дескрипторы и освобождены - выделенные EMM-блоки. - -## 11. Граница полной версии 1.0 - -Версия 1.0 должна дополнительно включать: - -- групповые и рекурсивные копирование, перемещение и удаление; -- безопасную политику перезаписи; -- сохранение атрибутов и времени; -- независимую для каждой панели сортировку по имени, расширению, - размеру и дате файла в обоих направлениях; -- переключение дисков; -- командную строку, историю, интерактивный полноэкранный - режим псевдооболочки по `Ctrl+O` и ассоциации; -- настраиваемую подсветку имён по расширению; -- просмотрщик и редактор, допустимо отдельными `.EXE`; -- меню, диалоги, помощь и конфигурацию; -- быстрый поиск, дерево и поиск файлов; -- информационную и quick-view панели; -- базовую работу с архивами; -- мышь. - -Обязательные детали этих функций: - -- групповые `+`, `-` и `*` работают только над файлами и не меняют флаг - каталога, даже если он был установлен отдельным `Insert`; -- `Ctrl+O` не просто скрывает панели, а переключает между ними и - управляемым полноэкранным режимом командной строки; -- полноэкранный режим и однострочный prompt используют один редактор строки и - общую историю; внешние `.EXE` запускаются через ESTEX `EXEC`; -- синтетическая `..` всегда первая, каталоги остаются перед файлами, - а направление применяется внутри этих групп; -- пункт сортировки «дата» использует метку создания/модификации, которую - реально возвращает файловая система; DSS `F_FIRST/F_NEXT` сейчас даёт - метку last-write, поэтому отдельный выбор creation не имитируется - дубликатом даты и требует отдельной платформенной пробы; -- при отсутствии файла цветов каталоги и `..` имеют белый цвет, `.EXE` — - жёлтый, остальные файлы — светло-серый; -- предел 640 записей не снимается ни одной из этих функций. - -Многостраничное/динамическое хранилище панели, межстраничная сортировка и -показ 1024/2048 или неограниченного числа файлов не входят в 1.0 и -резервируются для 2.0+. - -Терминал, модем, CD-плеер, форматирование, восстановление дисков, электронная -таблица, игры и многооконный desktop Dos Navigator не входят в определение -Commander 1.0. diff --git a/applications/Volkov/docs/commander-roadmap.md b/applications/Volkov/docs/commander-roadmap.md deleted file mode 100644 index 0717a46..0000000 --- a/applications/Volkov/docs/commander-roadmap.md +++ /dev/null @@ -1,751 +0,0 @@ -# Sprinter Commander: план разработки - -Статус: рабочий план - -Связанные документы: -[требования](commander-requirements.md), -[архитектура](commander-architecture.md), -[клавиши и команды](commander-keymap.md), -[платформенные пробы P1](p1-platform-probes.md), -[экранный backend P2](p2-screen-backend.md), -[результаты P2.1](p2-screen-probe-results.md), -[результаты P2.2](p2-palette-results.md), -[результаты skeleton P2](p2-skeleton-results.md), -[результаты файловых панелей P3](p3-panel-results.md), -[результаты копирования P4.1](p4-copy-results.md), -[результаты запуска EXE P4.2](p4-exec-results.md), -[результаты P5](p5-stability-results.md), -[результаты F7/P9](p9-mkdir-results.md), -[результаты F6/P10](p10-rename-results.md), -[результаты F8/P11](p11-delete-results.md), -[результаты group-copy/P12](p12-group-copy-results.md), -[результаты group-delete/P13](p13-group-delete-results.md), -[результаты стандартных цветов/P14](p14-default-colors-results.md), -[результаты recursive copy/P15](p15-tree-copy-results.md), -[результаты recursive group-copy/P16](p16-group-tree-copy-results.md), -[результаты recursive group-delete/P17](p17-tree-delete-results.md), -[результаты сохранения метаданных/P18](p18-metadata-results.md), -[результаты conflict-policy/P19](p19-copy-policy-results.md), -[UI-ориентир VC](ui-reference.md) - -Текущее состояние: - -- P0 — завершена первая редакция документации; -- P1 — обязательные платформенные примитивы подтверждены в MAME 0.287; - проверка на реальном Sprinter ожидается; -- P2.1 — ядро координатного экранного backend прошло MAME 0.287; -- P2 — многоцветный двухпанельный skeleton прошёл полный MAME-сценарий, - включая scroll, диалоги и повторный cleanup; аппаратная проверка ожидается; -- P2.2 — системная многоцветная палитра прошла отдельный MAME-тест; - аппаратная проверка ожидается; -- P3 — реальные две панели, EMM-store с границей 640/641, сортировка, - навигация, синтетическая `..` и предел DSS 35 прошли диагностические и - интеграционные MAME-тесты; аппаратная проверка ожидается. -- P4 — безопасное однофайловое копирование через EMM/W3 и запуск `.EXE` с - полным восстановлением окружения прошли HDD-сценарии MAME; аппаратная - проверка ожидается. -- P5 — завершён в MAME: пройдены 20 последовательных copy со всеми граничными - размерами, 100 refresh, три lifecycle, ENOSPC, readonly, ошибки - read/rename/ENOENT, отмена перед commit, смена носителя, отсутствующий EXE и - реальный `mdview2`. Аппаратный smoke-тест остаётся внешней проверкой. -- 0.2 — начат: независимые сортировки name/ext/size/date и reverse, выбор - `Insert`, `*`, `+`/`-` с масками, F7 mkdir и локальный F6 rename - реализованы и прошли target/UI-сценарии MAME; следующим идёт расширение - файловой job и рекурсивная обработка каталогов. Групповые F5 и F8 уже - обрабатывают файлы и выбранные directory roots; для F8 каталоги удаляются - после файлов обратным проходом очереди. F5 сохраняет FAT date/time и - файловые атрибуты R/H/S/A. Conflict-policy overwrite/skip/rename и all, - readonly re-prompt, backup/rollback выполнены; P5/P18 повторно пройдены. - Также досрочно реализован стандартный fallback цветов имён без файла - конфигурации. - -## 1. Целевые результаты - -Проект делится на три продуктовых рубежа: - -1. **PoC 0.1** — доказана работа архитектуры на Sprinter. -2. **Рабочая версия 0.4** — Commander пригоден для повседневных файловых - операций. -3. **Полная версия 1.0** — реализован основной набор возможностей VC 4.99 с - адаптацией к Sprinter. - -Dos Navigator задаёт идеи для расширяемости, но не объём версии 1.0. - -Вся линейка до 1.0 включительно использует одну EMM-страницу на панель и -жёсткий предел 640 показываемых записей. Его снятие — отдельная архитектурная -задача 2.0+. - -## 2. Допущения для оценки - -Оценки приведены для одного разработчика, знакомого с текущим Sprinter -C-Compiler и имеющего доступ к MAME. Один рабочий день означает день -сосредоточенной разработки, а не календарный день. - -В оценку входят: - -- реализация; -- целевые тесты; -- анализ `.asm`/`.map` при платформенной проблеме; -- MAME-прогон; -- обновление документации. - -В оценку не входят: - -- исправление неизвестных дефектов DSS; -- разработка отсутствующего архиватора; -- длительное ожидание доступа к реальному железу; -- перенос стороннего editor/viewer; -- создание графического режима интерфейса. - -Ожидаемая точность оценки: - -- PoC: `-20% / +40%`; -- этапы после PoC: `-25% / +60%`; -- архивы и editor: оцениваются повторно после исследования доступных программ. - -## 3. Сводный план - -| Этап | Результат | Оценка | -|---|---|---:| -| P0 | Спецификация и архитектура | 2–3 дня | -| P1 | Проверенные платформенные примитивы | 3–5 дней | -| P2 | Запускаемый двухпанельный skeleton | 4–5 дней | -| P3 | Полноценная навигация по двум панелям | 5–7 дней | -| P4 | Копирование одного файла и EXEC | 5–7 дней | -| P5 | Стабилизация PoC | 3–5 дней | -| 0.2 | Безопасное файловое ядро | 5–7 недель | -| 0.4 | Рабочая оболочка и внешние инструменты | 4–6 недель | -| 0.6 | Поиск, дерево и расширенные панели | 5–7 недель | -| 0.8 | Виртуальные источники и архивы | 6–10 недель | -| 1.0 | Надёжность, производительность, релиз | 5–8 недель | - -PoC требует ориентировочно 22–32 рабочих дня. Полная версия 1.0 — примерно -29–44 рабочих недели с учётом PoC. После исследования P1 оценки должны быть -пересмотрены по фактической скорости разработки. - -## 4. Зависимости этапов - -```text -P0 documentation - | - v -P1 platform probes - | - +------> EXEC probe -------------------+ - | | - v v -P2 app/screen/input skeleton P4 EXEC - | ^ - v | -P3 panel/store/filesystem --------> P4 copy - | | - +------------------+-------------------+ - v - P5 PoC hardening - | - v - 0.2 safe file core - | - +----------+-----------+ - v v - 0.4 shell/helpers 0.6 search/tree - +----------+-----------+ - v - 0.8 virtual sources - | - v - 1.0 - | - v - 2.0+ dynamic panel store -``` - -0.6 допускается начинать после стабилизации интерфейса панелей 0.2, но релиз -0.8 требует законченных 0.4 и 0.6. - -## 5. Фаза P0 — проектирование - -### Цель - -Превратить общую идею Commander в набор контрактов, по которым можно писать -и проверять код без постоянного возврата к исходникам VC. - -### Работы - -- зафиксировать границы PoC и версии 1.0; -- определить компоновку экрана; -- определить `ScDirEntry`, `ScPanel`, события и команды; -- выбрать режим памяти и владение EMM; -- описать безопасное копирование; -- описать запуск дочерней программы; -- определить клавиши PoC и будущий keymap; -- составить acceptance tests; -- зафиксировать лицензионное правило для референсов. - -### Артефакты - -- `docs/commander-requirements.md`; -- `docs/commander-architecture.md`; -- `docs/commander-keymap.md`; -- `docs/commander-roadmap.md`. - -### Критерий выхода - -Нет неизвестного продуктового решения, мешающего начать платформенные тесты. -Непроверенные технические детали перечислены как задачи P1, а не скрыты в -предположениях архитектуры. - -## 6. Фаза P1 — платформенные пробы - -### Цель - -Получить минимальные воспроизводимые программы для всех примитивов, от -которых зависит PoC. - -### P1.1. Каталоги и пути - -Проверить: - -- `getcwd`, абсолютные и относительные пути; -- `chdir` между двумя каталогами и дисками; -- шаблон для перечисления всех файлов; -- возвращает ли DSS `.` и `..`; -- конец `fnext()` и значение `errno`; -- имя корня и переход к родителю; -- максимальную практическую длину пути; -- поведение при смене/отсутствии носителя. - -Результат: таблица фактического поведения и тестовый `.exe`. - -### P1.2. EMM - -Проверить: - -- выделение блока из трёх страниц; -- получение всех физических страниц; -- чтение/запись начала и конца каждой страницы; -- сохранение W3 вокруг `bank_read/bank_write`; -- повторное выделение/освобождение; -- понятную ошибку при нехватке памяти. - -Результат: подтверждённая схема трёх страниц PoC. - -### P1.3. Экран - -На базе существующего теста `winrest` проверить app-local wrapper: - -- полный экран 80x32; -- вывод с ненулевого offset; -- частичный диапазон строк; -- все используемые атрибуты; -- восстановление после смены видеорежима; -- сохранность IX и W3. - -Результат: утверждённый ABI `sc_platform_winrest()`. - -### P1.4. Клавиатура - -Записать фактические `ascii`, `scan`, `kbd_mod_state` для клавиш из -`commander-keymap.md`. Отдельно проверить быстрое отпускание Ctrl после -`Ctrl+PgUp` и `Ctrl+R`. - -Результат: тестовая таблица и список подтверждённых комбинаций. - -### P1.5. EXEC - -Проверить: - -- формат параметров ESTEX `EXEC`; -- требуется ли отдельный `WAIT`; -- возврат к вызывающей программе; -- состояние W1/W2/W3 после возврата; -- состояние видеорежима и CWD; -- коды ошибок отсутствующего и повреждённого `.EXE`; -- взаимодействие с открытым загрузочным fd multi-bank программы. - -Результат: app-local wrapper и отдельный repro. - -### P1.6. Информация о диске и метаданные - -`DSKINFO` подтверждён в P1. `ATTRIB`, получение и установка даты/времени не -блокируют базовый PoC и проверяются перед реализацией соответствующих -операций версии 0.2. - -### Критерий выхода P1 - -- Все обязательные wrappers имеют воспроизводимый тест. -- Необъяснённых регистровых соглашений нет. -- Известно минимальное число требуемых страниц и fd. -- Архитектура обновлена по результатам проб. - -Критерий достигнут для MAME 0.287 / BIOS 3.06. Подтверждены EMM, DSKINFO, -F_FIRST/F_NEXT, полный и частичный WINREST, клавиши PoC, EXEC обоих режимов, -WAIT=`0x5A`, восстановление W1/W2/W3 и повторный EMM-тест после child. -Фактические значения и скриншоты приведены в -[p1-platform-probes.md](p1-platform-probes.md). - -Расширенные отрицательные случаи не блокируют skeleton P2 и перенесены к -этапам, где появится использующий их код: - -- переходы между дисками, корень, длинный путь и смена носителя — P3/P5; -- исчерпание EMM — P5; -- отсутствующий/повреждённый EXE и multi-bank loader fd — P4/P5; -- ATTRIB и изменение даты/времени — версия 0.2. - -## 7. Фаза P2 — skeleton приложения - -### Цель - -Получить запускаемое приложение с двумя пустыми панелями, экранной моделью и -управлением, но без чтения каталога. - -### Работы - -1. Создать `Makefile`, `src/`, `include/`, `banks/`, `tests/`. -2. Добавить `ScApp` и init/cleanup. -3. Настроить сборку `--memory big --safe`. -4. Выделить EMM-блок и получить три страницы. -5. Зафиксировать прошедший P2.1 coordinate-screen probe: полный и частичный - `WINREST`, `RDCHAR`, оба направления прямоугольного `SCROLL`, 1000 - операций и сохранение W3. -6. Реализовать `sc_video_system` на `WINREST`/`WRCHAR`/`SCROLL`; не - использовать BIOS window descriptors и прямой доступ к VRAM. -7. Реализовать screen buffer и полный/частичный present через `sc_video`. -8. Добавить `sc_glyphs.h` с CP866-глифами и test рамок, стрелок и block- - элементов. -9. Реализовать прямоугольный scroll одной панели в обоих направлениях; - синхронно сдвигать экранную EMM-модель и проверять границы прямоугольника. -10. Нарисовать две панели и нижнюю строку клавиш. -11. Реализовать `ScKeyEvent`, `ScCommand` и главный цикл. -12. Реализовать `Tab`, навигационные команды-заглушки и подтверждение `F10`. -13. Добавить единый message dialog. -14. Настроить первый интерактивный MAME-тест со скриншотом. -15. Подключить прошедший P2.2 модуль темы: сохранить затрагиваемые записи - четырёх планов, задать одинаковые normal/FLASH-пары и восстановить их при - cleanup. Повторить тот же тест на реальном Sprinter. - -### Критерий выхода P2 - -- `SPRCMD.EXE` собирается в `big`. -- Экран соответствует схеме 80x32. -- `Tab` меняет активную рамку. -- Однострочный scroll не двигает рамку, соседнюю панель и общие строки. -- Все соединения рамок, стрелки и block-элементы отображаются ожидаемыми - CP866-глифами. -- Экран остаётся читаемым во времени с подключённым многоцветным профилем. -- Диалог F10 работает в обоих направлениях. -- Выход освобождает EMM; повторные запуски не уменьшают свободную память. -- В `.map` есть зафиксированный запас HOME и банков. - -MAME-часть этих критериев достигнута 6 сентября 2026 года. Текущий skeleton -имеет 7 276 байт heap в HOME, пока не использует W1-банки и дважды подряд -восстановил число свободных EMM-страниц после выхода. Отдельный `sc_theme` -сохранил/установил/восстановил четыре палитровых плана. Подробный сценарий и -кадры: [p2-skeleton-results.md](p2-skeleton-results.md). - -Аппаратный прогон остаётся внешним gate. Многоцветная тема P2.2 прошла MAME; -на реальном Sprinter она проверяется вместе с остальными критериями P2. - -## 8. Фаза P3 — панели и каталог - -### Цель - -Реализовать независимую навигацию по настоящей файловой системе. - -### Работы - -- `ScDirEntry` и build-time проверка размера 24 байта; -- EMM store get/put/clear; -- scan настоящего каталога; -- синтетический `..`; -- сортировка каталогов и файлов; -- panel renderer; -- cursor/top и постраничная навигация; -- вход в каталог, parent, refresh; -- сохранение позиции по имени; -- отдельные пути панелей; -- предупреждение о пределе 640 записей и отдельный красный маркер - ограничения DSS после ошибки 35; -- отображение ошибок носителя. - -### Порядок реализации - -1. Одна панель, маленький каталог. -2. Пустой каталог и корень. -3. Сортировка. -4. Прокрутка за 27 строк. -5. Вторая независимая панель. -6. Большой каталог и предел страницы. -7. Ошибки и восстановление CWD. - -### Критерий выхода P3 - -- Выполнены `REQ-PANEL`, `REQ-NAV` и `REQ-FS` PoC. -- Сто переходов/refresh не меняют EMM free pages. -- Каталог на пределе не повреждает screen page. -- Перемещение курсора не вызывает `ffirst/fnext`. -- После ошибки обе панели остаются управляемыми. - -## 9. Фаза P4 — копирование и запуск - -### P4.1. Копирование - -Порядок работ: - -1. Проверка выбранной записи и построение путей. -2. Проверка отсутствия конечного имени. -3. Генерация temp с `O_EXCL`. -4. Зарезервировать четвёртую EMM-страницу как рабочую область copy job. -5. Добавить симметричные операции `bank_read_page()`/`bank_write_page()` для - файлового обмена через W3 без полноразмерного буфера в W2. -6. Копирование логическими блоками 4096 байт; возможное увеличение блока — - только после измерения отзывчивости и скорости. -5. 32-битный прогресс. -6. Отмена через `Esc`. -7. Cleanup на каждой точке ошибки. -8. Повторная проверка конечного имени. -9. Rename temp и refresh пассивной панели. -10. Тесты размеров и побайтовое сравнение. - -### P4.2. EXEC - -Порядок работ: - -1. Запуск минимального дочернего `.EXE`. -2. Восстановление видеорежима и экрана. -3. Восстановление CWD. -4. Refresh панелей. -5. Обработка ошибки запуска. -6. Запуск существующего viewer как интеграционный тест. - -### Критерий выхода P4 - -- Выполнены `REQ-COPY` и `REQ-EXEC`. -- Большой файл копируется без 16-битного переполнения. -- Cancel/error не оставляют temp. -- Существующий файл не изменяется. -- После дочерней программы Commander продолжает принимать команды. - -## 10. Фаза P5 — стабилизация PoC 0.1 - -### Обязательная матрица - -| Область | Сценарии | -|---|---| -| Каталог | пустой, один файл, 27/28, synthetic 640/641, DSS >512 физических записей | -| Навигация | границы, repeat, две разные глубины каталогов | -| Файлы | 0, 1, 4095, 4096, 65535, 65536, более 1 МБ | -| Ошибки | ENOENT, EACCES/readonly, ENOSPC, смена носителя | -| Copy cleanup | cancel в начале/середине/конце, ошибка read/write/rename | -| EXEC | успех, отсутствующий файл, плохой EXE, возврат с другим экраном | -| Ресурсы | 100 refresh, 20 copy, повторные запуск/выход | -| Экран | полный redraw, dirty rows, диалог, возврат из EXE | - -### Артефакты релиза PoC - -- `SPRCMD.EXE`; -- Makefile с `floppy`, `hdd` и `run`; -- MAME-тесты и контрольные скриншоты; -- test files с известными размерами/checksum; -- отчёт размеров HOME/банков; -- список известных ограничений; -- инструкция запуска. - -### Release gate 0.1 - -Все десять критериев раздела 10 требований выполнены. Наличие известной -ошибки, способной повредить чужой файл или потерять EMM-блок, блокирует релиз. - -Release gate выполнен в MAME 0.288 / BIOS 3.06 / DSS 1.71.57. Сводка, -команды воспроизведения и ограничения зафиксированы в -[p5-stability-results.md](p5-stability-results.md) и -[poc-0.1-release.md](poc-0.1-release.md). Проверка на реальном Sprinter не -подменяется MAME и остаётся обязательным последующим smoke-тестом. - -## 11. Версия 0.2 — безопасное файловое ядро - -### Цель - -Сделать Commander пригодным для реальной работы с файлами без viewer/editor. - -### Функции - -- одна EMM-страница на панель и предел 640 записей сохраняются; -- `Insert`, `*`, `+`/`-` и маски выбора — выполнены; -- сортировки name/ext/size/date и reverse — выполнены и проверены в MAME; -- последовательная очередь выбранных файлов F5 — выполнена; -- рекурсивное копирование одного каталога и группы directory roots — - выполнено; traversal hardening для больших деревьев остаётся; -- F6 local rename — выполнено; межкаталожный/group move остаётся в работе; -- F7 mkdir — выполнено и проверено в MAME; -- F8 single file/empty-directory delete и recursive group delete выбранных - файлов и каталогов — выполнены, включая runtime-cancel; защитная policy - остаётся; -- cancel между файлами и блоками; -- overwrite/skip/rename и варианты all — выполнены для файлов, включая - backup/rollback и readonly re-prompt; merge существующих каталогов позже; -- проверка свободного места; -- сохранение даты, времени и атрибутов обычных файлов — выполнено и - проверено exact FAT-тестом; метаданные каталогов не входят в P18; -- защита readonly при overwrite — выполнена; delete/move policy остаётся; -- подсчёт итогов выбранной группы; -- переключение дисков минимум через диалог. - -### Внутренние работы - -- сохранить одностраничный store и добавить к `ScPanel` ключ и направление сортировки; -- расширить `ScJob` очередью и стеком каталогов; -- сохранить crash-safe temp/commit модель; -- добавить единый policy object для ошибок; -- определить поведение при частично успешной группе. - -### Критерий выхода 0.2 - -- Неделя dogfood без повреждения данных. -- Рекурсивные операции проходят дерево глубиной не менее 16 уровней. -- Рекурсивная job обрабатывает более 640 файлов суммарно в нескольких - каталогах, не превращая саму панель в многостраничную. -- Cancel оставляет уже завершённые файлы в определённом документированном - состоянии и не оставляет temp. -- Все разрушительные команды требуют подтверждения. - -## 12. Версия 0.4 — рабочая оболочка - -### Функции - -- однострочная командная строка; -- история команд и каталогов; -- интерактивный полноэкранный pseudo-shell по `Ctrl+O`; -- вставка имени/пути активной и пассивной панели; -- запуск `.EXE` с параметрами; -- file associations; -- пользовательское меню; -- F3 через отдельный viewer; -- F4 через отдельный editor; -- F9 и базовое меню; -- режимы панелей `Brief`/`Details` по образцу VC; -- конфигурационный файл; -- сохранение путей, sort mode и палитры; -- цветовые правила по расширениям; без файла: каталоги белые, EXE жёлтые, - остальные файлы светло-серые; -- `Ctrl+U`, `Ctrl+O`, root; -- полноценный drive dialog; -- краткая встроенная помощь. - -### Протокол helper-программ - -До реализации F3/F4 фиксируется небольшой контракт: - -- полный путь передаётся аргументом; -- код возврата различает успех, отмену и ошибку; -- helper не обязан восстанавливать экран; -- Commander всегда восстанавливает режим и перечитывает затронутую панель; -- для editor предусматривается признак изменения файла. - -### Критерий выхода 0.4 - -Commander можно использовать как основную оболочку DSS для навигации, -операций, запуска программ, просмотра и редактирования файлов. - -## 13. Версия 0.6 — возможности VC-класса - -### Функции - -- quick search по текущей панели; -- постоянные фильтры/маски панели; -- quick-view panel; -- info panel; -- размеры каталогов; -- сравнение каталогов; -- поиск файлов по имени; -- поиск текста в файлах, если скорость приемлема; -- виртуальная панель результатов поиска; -- дерево каталогов; -- история посещённых каталогов; -- descriptions (`description.ion`/настраиваемое имя), если формат выбран; -- изменение атрибутов и времени через UI. - -### Архитектурный gate - -Перед началом поиска должен быть стабилен интерфейс `ScSource`. Результаты -поиска не должны притворяться настоящим каталогом с помощью специальных -проверок по всему UI: они реализуются как отдельный source kind. - -### Критерий выхода 0.6 - -Поиск и дерево работают на объёмах, превышающих near-memory, а возврат из -виртуальной панели не теряет прежнее состояние настоящей панели. - -## 14. Версия 0.8 — виртуальные источники и архивы - -### Обязательные исследования - -- какие архиваторы реально существуют для Sprinter; -- можно ли получить листинг без распаковки; -- достаточно ли внешнего процесса и list-файла; -- какие форматы стоит поддерживать первыми; -- сколько памяти требует нативный decoder; -- разрешает ли лицензия включать выбранную реализацию. - -### Предпочтительная последовательность - -1. Внешний архиватор по шаблону команды. -2. Просмотр архива как read-only `SC_SOURCE_ARCHIVE`. -3. Извлечение выбранных файлов. -4. Копирование файла в архив, если внешний инструмент поддерживает. -5. Только затем — нативный decoder для одного востребованного формата. - -### Дополнительные функции - -- мышь; -- локализация строк через resource file; -- расширенная помощь; -- bookmarks; -- protocol внешних plugin/helper-программ; -- сохранённые наборы конфигурации. - -### Критерий выхода 0.8 - -Архивная и поисковая панели используют тот же panel UI без специальных -ветвей в renderer и навигации. Ошибка внешнего архиватора не повреждает -исходный архив. - -## 15. Версия 1.0 — релизная стабилизация - -К этому этапу уже должны быть закончены все назначенные функции 0.2–0.8. -Отдельно проверяются три обязательных релизных контракта: - -- командная строка и интерактивный полноэкранный pseudo-shell по `Ctrl+O`; -- сортировка каждой панели по имени, расширению, размеру и дате, по - возрастанию и убыванию; -- подсветка по типу файла с настраиваемыми цветами и заданным поведением - при отсутствии конфигурации. - -Предел панели 640 записей в 1.0 остаётся штатным документированным -ограничением. - -### Надёжность - -- проверка всех cleanup-path; -- защита от двойного освобождения; -- тесты нехватки EMM; -- тесты предела файловых дескрипторов; -- смена и извлечение носителя; -- read-only и disk full; -- повреждённые каталоги/архивы; -- отмена каждой длительной операции; -- безопасное обновление конфигурации через temp/rename. - -### Производительность - -- профилирование чтения больших каталогов; -- профилирование сортировок EMM; -- минимизация переключений W3; -- группировка dirty rows; -- размер блока copy по фактическим измерениям; -- размер HOME и каждого банка; -- время холодного запуска и возврата из helper. - -### Совместимость - -- MAME 0.287 / BIOS 3.06; -- реальный Sprinter Sp2000; -- DSS 1.71.57; -- floppy и HDD; -- минимум две конфигурации доступной EMM; -- CP866 и Rus/Lat состояние клавиатуры. - -### Документация релиза - -- руководство пользователя; -- полный keymap; -- описание конфигурации; -- восстановление после прерванной операции; -- ограничения файловой системы; -- лицензии и благодарности; -- руководство сборки; -- архитектурная памятка для добавления source/job/bank. - -### Release gate 1.0 - -- Нет известных дефектов потери или скрытой порчи данных. -- Все обязательные функции требований 1.0 реализованы; перенос любой из них - требует нового явного решения о составе релиза. -- Пройдены автоматические MAME-тесты и ручной hardware checklist. -- Размеры HOME/банков имеют документированный запас. -- Чистая сборка воспроизводима штатной командой проекта. - -## 16. Версия 2.0+ - -Первый архитектурный рубеж после 1.0 — просмотр каталогов, содержащих более -640 записей. Перед реализацией сравниваются два варианта. - -**Вариант A: потоковые логические страницы.** Рекомендуемый первый шаг: - -- в EMM по-прежнему находится только текущая страница и не более 640 строк; -- виртуальные `<<< PAGE n` и `>>> PAGE n` занимают места в этом лимите; -- при переходе каталог сканируется заново до требуемой логической позиции; -- каталоги и файлы отбираются двумя проходами, чтобы каталоги оставались - первыми во всём потоке; -- сортировка выполняется только внутри текущей страницы и так обозначается - в UI; -- изменение каталога между проходами сбрасывает просмотр на первую страницу. - -Текущий DSS API `F_FIRST/F_NEXT` не принимает ключ или направление -сортировки. Незавершённые итераторы двух панелей не сохраняются: каждый -переход заканчивает перебор либо начинает новый. - -Платформенная предпосылка варианта A: стабильный DSS 1.71 не умеет перейти -за 512-ю физическую FAT-запись и возвращает код 35. Потоковые страницы -становятся реализуемыми только после перехода на расширенный режим -`F_FIRST` (`B=0x80/0x81`) из экспериментальной ветки `beta_cdfs` либо после -эквивалентного исправления DSS. До этого `>>> MORE...` является только -информирующим маркером, а не переходом на страницу 2. Детали: -[dss-large-directories.md](dss-large-directories.md). - -**Вариант B: динамический EMM-store.** Он нужен, только если потребуются -глобальная сортировка и произвольный переход между страницами: - -- выбор модели 1024, 2048 или динамического предела по результатам замеров; -- выделение и освобождение динамической цепочки EMM-страниц; -- межстраничная сортировка или индекс без полной копии списка в W2; -- определённое поведение при нехватке EMM на части каталога; -- нагрузочные тесты многостраничных списков и отмены во время scan/sort. - -Для обоих вариантов виртуальные записи имеют отдельный тип и не участвуют в -выделении, файловых операциях и сортировке. Точный номер версии после 1.0 -фиксируется отдельным release gate; базовый релиз 1.0 от этого не зависит. - -Прочий backlog, не влияющий на релиз 1.0: - -- нативные архивные codecs; -- FTP/serial/network source при наличии транспорта; -- disk image source; -- сравнение и синхронизация деревьев; -- макросы; -- очередь нескольких jobs; -- background copy, только если появится безопасная модель многозадачности; -- интеграция viewer/editor в банки Commander; -- дополнительные режимы экрана. - -DOS-специфические функции DN — CD player, modem terminal, disk recovery, -spreadsheet, games и desktop окон — остаются отдельными приложениями. - -## 17. Правило изменения плана - -После каждого release gate обновляются: - -1. Фактическое время этапа. -2. Размер HOME и банков. -3. Число обязательных EMM-страниц. -4. Открытые платформенные риски. -5. Состав следующей версии. - -Новая функция не добавляется в текущий этап без одного из решений: - -- она устраняет блокирующий риск текущего release gate; -- она дешевле сейчас из-за уже изменяемого интерфейса; -- другая функция того же объёма явно исключается из этапа. - -Такой обмен фиксируется в этом документе, чтобы PoC и 1.0 не расширялись -неуправляемо. diff --git a/applications/Volkov/docs/dss-large-directories.md b/applications/Volkov/docs/dss-large-directories.md deleted file mode 100644 index 17292ef..0000000 --- a/applications/Volkov/docs/dss-large-directories.md +++ /dev/null @@ -1,100 +0,0 @@ -# DSS: большие каталоги и `F_FIRST/F_NEXT` - -## Вывод - -Стабильный DSS 1.71 не может перечислить каталог за пределами первых 512 -физических FAT-записей. Это ограничение внутреннего 16-КБ кэша DSS, а не -размера `ffblk_t`, адреса DTA, W2, W3 или EMM-хранилища Commander. - -Commander хранит до 640 элементов в EMM, но на стабильном DSS источник -может закончиться раньше. Код ошибки 35 принимается как частичный успех: -полученные элементы сохраняются, а последней добавляется красная виртуальная -строка `>>> MORE...`. - -## Подтверждение исходниками DSS - -Исходники находятся в `../../docs/sources/Estex-DSS` и закодированы в CP866. -Для чтения комментариев они просматривались через `iconv`, без изменения -оригинальных файлов. - -Стабильная ветка `master`, релизный срез `6078563`: - -- `DSS/defines.inc`: `DIRPAGE.buffer = 0xC000`; -- `DSS/FS/FAT.asm:LOADDIR`: за один раз читается ровно `0x4000` байт; -- размер `FAT_DIRECTORY_RECORD` равен 32 байтам; -- `SEARCH.Custom` начинает с `IX=0xC000` и прибавляет 32; -- после 512 записей IX переходит через `0xFFFF`, а - `SEARCH.error_too_many_files` возвращает - `DSS_Error.sys.TOO_MANY_FILES_IN_DIR`; -- `DSS/API/Find.asm:F_NEXT` отдельно проверяет `IX==0` и направляет выполнение - на ту же ошибку; -- комментарии `TODO` прямо требуют record index и загрузку другой части - каталога размером более `0x4000` байт. - -В системном `SYSTEM.DOS`, реально используемом MAME, найдены обе характерные -последовательности стабильной реализации: проверка `LD IX,0` и цикл -`ADD IX,32` до переноса. Бинарник на HDD и `dss171u.img` совпадают по SHA-256. - -## Почему старый тест показывал 682 - -Один ранний экран `P3DIR` показывал `count=682, errno=0`, но поле `last` -содержало `B0000599.DAT`. Проверка физического порядка тестового FAT-образа -показала: - -- записи 0 и 1 — `.` и `..`; -- `B0000599.DAT` — запись 511, то есть 512-я физическая запись; -- запись 512 уже имеет другое имя. - -Следовательно, DSS завершил реальный перебор точно на системной границе, а -старый диагностический `.EXE` исказил локальный счётчик и `errno`. Этот экран -не является доказательством чтения 682 записей. Исправленная проба хранит -счётчик и контрольные имена 511/512/513 в статической памяти. - -Матрица `tests/p3_enum_matrix` независимо получила для всех проверенных -вариантов `count=512, end_errno=35`: обычный wrapper, wrapper и цикл в W1, -чтение W3 и запись в EMM. - -## Семантика Commander 0.1–1.0 - -При `fnext() == -1 && errno == 35`: - -1. Ошибка не откатывает уже прочитанный каталог. -2. `panel.truncated` устанавливается в 1. -3. В конец добавляется `ScDirEntry` с флагом `SC_ENTRY_DSS_LIMIT` и именем - `>>> MORE...`. -4. Маркер всегда сортируется после файлов, рисуется красным и занимает один - из 640 слотов панели. -5. `Enter` и файловые команды не трактуют маркер как файл или каталог. - -Число видимых файлов может быть меньше 510: в 512 физических записей входят -`.`/`..`, удалённые записи и служебные записи длинных имён, которые DSS или -Commander отфильтровывают. - -## Расширенная ветка DSS - -В `origin/beta_cdfs` существует незавершённая реализация больших каталогов: - -- `B=0x80` — короткое имя и обход без старого предела; -- `B=0x81` — DOS-имя и обход без старого предела; -- хранится record index; -- при окончании страницы вызывается `LOAD_NEXT_DIR_PART_TO_DIR_CACHE`; -- старые режимы `B=0/1` намеренно ограничиваются 512 результатами для - совместимости. - -Это хороший upstream-прототип для версии 2.0, но не контракт стабильного DSS -1.71. До отдельной сборки и тестирования Commander не должен зависеть от -него. - -## Следствие для логических страниц Commander - -Предложенные `<<< PAGE n`/`>>> PAGE n` реализуемы без дополнительной памяти, -если DSS способен продолжить физический перебор после первой 16-КБ части. -На стабильном DSS повторный `F_FIRST/F_NEXT` снова остановится на тех же 512 -записях, поэтому настоящее переключение на PAGE2 требует одного из вариантов: - -1. стабилизировать расширенный режим `B=0x80/0x81`; -2. добавить эквивалентный API DSS с record index/offset; -3. как нежелательный запасной путь — реализовать raw FAT source вне DSS. - -Выбор делается после релиза 1.0; текущий красный маркер уже имеет отдельный -тип и сможет позднее стать переходом без изменения формата `ScDirEntry`. diff --git a/applications/Volkov/docs/p1-platform-probes.md b/applications/Volkov/docs/p1-platform-probes.md deleted file mode 100644 index 8819a85..0000000 --- a/applications/Volkov/docs/p1-platform-probes.md +++ /dev/null @@ -1,324 +0,0 @@ -# Sprinter Commander: платформенные пробы P1 - -Статус: MAME 0.287 / BIOS 3.06 — `PASS`; проверка на реальном Sprinter ожидается - -Связанные документы: -[требования](commander-requirements.md), -[архитектура](commander-architecture.md), -[клавиши](commander-keymap.md), -[roadmap](commander-roadmap.md), -[экранный backend P2](p2-screen-backend.md) - -## 1. Назначение - -Программа `p1probe.exe` объединяет платформенные проверки, которые должны -быть завершены до реализации UI Commander. Она не является ранней версией -Commander и не определяет его пользовательский интерфейс. - -Исходники находятся в `tests/p1_platform/`. - -Вторая программа, `p1child.exe`, используется только для проверки ESTEX -`EXEC`, возврата к родителю и кода завершения. - -## 2. Ограничения безопасности - -- Пробы не создают, не изменяют и не удаляют файлы. -- Каталожная проба выполняет только `getcwd`, `chdir(".")` и - `ffirst/fnext`. -- EMM-проба пишет только в выделенный ей блок из трёх страниц. -- Screen-проба пишет только в отдельную выделенную EMM-страницу. -- Дочерняя программа меняет видеорежим, но не пишет на диск. -- Проба не открывает файловые дескрипторы приложения напрямую. -- MAME не запускается целями `make` и `make all`. - -Первый MAME-прогон выполнен 6 сентября 2026 года после отдельного согласования. -Все запуски использовали автоматически пересоздаваемый `mc.img`; исходные -дисковые образы не изменялись программами-пробами. - -## 3. Состав - -| Файл | Назначение | -|---|---| -| `p1probe.c` | меню, общие сообщения, последовательность тестов | -| `p1_sys.c` | app-local wrappers DSKINFO, EXEC, WAIT, WINREST | -| `p1_emm.c` | выделение трёх страниц и проверка границ | -| `p1_dir.c` | CURDIR/CHDIR/F_FIRST/F_NEXT и DSKINFO output | -| `p1_screen.c` | полный и частичный WINREST с ненулевым offset | -| `p1_keyboard.c` | вывод `ascii/scan/live modifiers` | -| `p1_exec_test.c` | два режима EXEC и проверка состояния родителя | -| `p1child.c` | дочерний EXE, меняющий режим на 40x32 и возвращающий `0x5A` | -| `mame_dump_keys.lua` | вывод карты полей AT-клавиатуры MAME | -| `mame_p1_keyboard.lua` | воспроизводимый ввод стрелок, F1–F10 и сочетаний | -| `Makefile` | сборка обоих EXE | - -## 4. Сборка - -```sh -cd applications/Volkov/tests/p1_platform -make -``` - -Диагностическая программа собирается в режиме `small --safe`. Это сделано -намеренно: P1 проверяет платформенные ABI и не должен одновременно отлаживать -банковую topology Commander. Сборка `big --safe` является критерием P2. - -Дочерняя программа собирается как `tiny --safe`. - -### Результат текущей сборки - -`p1probe.exe`: - -- `_CODE`: 11 062 байта; -- данные: 2 741 байт; -- файл EXE: 11 835 байт; -- heap после статики: 17 429 байт; -- стек: 1 279 байт. - -`p1child.exe`: - -- `_CODE`: 3 907 байт; -- данные: 849 байт; -- файл EXE: 4 453 байта; -- heap после статики: 10 092 байта; -- стек: 1 279 байт. - -Компиляция выполнена без ошибок. Значения относятся к текущей диагностической -сборке и не являются бюджетом Commander. - -## 5. Статическая проверка ABI - -### 5.1. DSKINFO - -Сигнатура: - -```c -int p1_disk_info(uint8_t disk, P1DiskInfo *out); -``` - -Сгенерированный вызов передаёт `disk` в A, `out` в DE. Wrapper сохраняет IX, -а после RST сохраняет: - -- A — sectors per cluster; -- HL — total clusters; -- DE — free clusters; -- BC — bytes per sector. - -Размер `P1DiskInfo` проверяется typedef-assert и равен семи байтам. - -### 5.2. EXEC - -Сигнатура намеренно имеет 8-битный аргумент первым: - -```c -int p1_exec(uint8_t path_mode, const char *path); -``` - -Для этого порядка SDCC передаёт `path_mode` в A и `path` в DE. Wrapper -перекладывает их в B и HL соответственно. Вариант -`p1_exec(const char *path, uint8_t mode)` был отвергнут после просмотра -сгенерированного ASM: второй 8-битный аргумент SDCC помещал на стек, а не в -DE. - -На успехе A сохраняется как `p1_exec_exit_code`; на ошибке код из A проходит -через `__errno_set`. - -### 5.3. WINREST - -Wrapper повторяет уже подтверждённую раскладку `tests/winrest` и `mdview2`: - -```text -A row -L column -SP+2 height -SP+3 width -SP+4 physical page -SP+5..6 offset in 16-KB page -``` - -IX сохраняется, перед RST устанавливается в `0xC000 + offset`. Сгенерированный -вызов проверен для полного экрана `32x80` и окна `2x20` с offset `0x2000`. - -## 6. Меню `p1probe.exe` - -| Клавиша | Проба | Автоматическая оценка | -|---|---|---| -| `1` | Каталог и F_FIRST/F_NEXT | да, плюс наблюдаемые коды | -| `2` | EMM: три страницы | да | -| `3` | WINREST full/partial | только завершение; изображение оценивается визуально | -| `4` | Клавиатурные коды | сбор наблюдений | -| `5` | EXEC и возврат | да для режима B=1 | -| `6` | DSKINFO текущего диска | да | -| `A` | Пробы 1, 2 и 6 | да | -| `0`/`Esc` | Выход | — | - -## 7. Ожидаемые проверки - -### 7.1. Каталог - -Проба дважды перечисляет каталог по маскам `*.*` и `*`, печатает первые 20 -записей и итоговые значения: - -- число записей; -- наличие `.`; -- наличие `..`; -- `errno` последнего `fnext()`; -- совпадение результатов двух масок; -- сохранение CWD после `chdir(".")`. - -Фактический результат DSS различается: `*.*` вернул обе записи с расширением, -а `*` завершился как пустой список с `ENOENT`. Поэтому `*.*` принят как -обязательный шаблон полного scan; совпадение масок не предполагается. - -MAME-прогон установил фактическое завершение перечисления: `fnext()` -возвращает ошибку с `errno=3` (`ENOENT`). Код `0x0F` из старого описания DSS -для этой операции не подтверждён. Проба после измерения принимает только -`ENOENT`, чтобы возможная смена поведения не осталась незамеченной. - -### 7.2. EMM - -Проверяются: - -- `mem_alloc_pages(3)`; -- три различных физических номера; -- первые 64 байта каждой страницы; -- последние 64 байта каждой страницы; -- независимые patterns страниц; -- восстановление `mem_info.free_pages` после освобождения блока. - -### 7.3. Screen/WINREST - -Первый вызов должен показать полный экран 80x32 с рамкой и цветными строками. -После клавиши второй вызов должен наложить окно 20x2 в позиции row 14, -column 30. Данные второго окна лежат по offset `0x2000`, поэтому тест -одновременно проверяет корректность IX. - -Тест не открывает окно через BIOS: ESTEX WINREST здесь только копирует -прямоугольник в текущий текстовый экран. Повтор со снимками в `t=20` и `t=24` -без промежуточных вызовов подтвердил, что фон вне прямоугольника сохранён. - -Первая версия pattern использовала арифметически полученные атрибуты -`0x11..0x17`. Верхняя граница `0x11` была обычным blue-on-blue, а часть -остальных сочетаний выглядела неоднозначно в разных моментах палитрового -цикла. Замечание о синих символах на синем фоне подтвердилось; raw-арифметика -атрибутов в UI запрещается. - -После замены на именованные `COLOR(LIGHTBLUE..WHITE, BLUE)` полный экран и -частичное окно были видимы на кадрах `t=20` и `t=24`. Повторная проверка -исходных PNG установила, что их декодированные RGB-пиксели полностью -идентичны. Прежнее сообщение о чередовании P2 было ошибкой встроенного -предпросмотра, а не поведением MAME. - -Отдельная проба P2.2 сохранила, установила, прочитала обратно и восстановила -шесть атрибутов во всех четырёх планах; многоцветная сцена осталась стабильной. -Строка P1-SCR-03 ниже подтверждена как `PASS`. Детали — в -[результатах P2.2](p2-palette-results.md). - -### 7.4. Keyboard - -Для 24 событий печатаются: - -```text -ascii, scan, полное kbd_mod_state, compact Shift/Ctrl/Alt -``` - -`Esc` печатается последним и завершает сбор. В автоматическом MAME-сценарии -использован следующий набор из 24 событий: - -- стрелки, Home/End, PgUp/PgDn; -- F1–F10; -- Insert/Delete; -- Ctrl+R; -- Ctrl+PgUp; -- Alt+F1; -- Esc. - -### 7.5. EXEC - -Выполняются два случая: - -1. `B=0`, `P1CHILD.EXE` — наблюдение short-name/PATH semantics. -2. `B=1`, `.\\P1CHILD.EXE` — обязательный случай PoC. - -Дочерняя программа: - -- переключается в 40x32; -- показывает свои W1/W2/W3 и CWD; -- ждёт клавишу; -- возвращает `0x5A`. - -После возврата родитель проверяет: - -- результат непосредственного EXEC; -- результат WAIT; -- восстановление W1/W2/W3; -- восстановление CWD; -- возможность вернуть 80x32 и продолжить работу. - -Ошибка случая B=0 записывается как наблюдение и не проваливает PoC. Ошибка -B=1 проваливает тест. - -## 8. Выполненный порядок первого прогона - -1. `2` — EMM. -2. `6` — DSKINFO. -3. `1` — каталог. -4. `3` — WINREST. -5. `4` — клавиатура. -6. `5` — EXEC последним. -7. Повторно `2` в том же процессе после EXEC, чтобы проверить EMM. -8. Возврат в меню; независимые запуски дополнительно подтвердили повторный - старт программы. - -Такой порядок сначала проверяет неразрушительные примитивы и оставляет -наиболее сложное переключение контекста на конец. - -## 9. Таблица runtime-результатов - -Окружение MAME: `mame.arm` 0.287 (`b0c4527c`), машина `sprinter`, BIOS `v3.06`, запуск -`p1probe.exe` с дискеты A:. `PENDING` в аппаратной колонке означает только -отсутствие прогона на реальном Sprinter и не отменяет результата MAME. - -| ID | Проверка | MAME 0.287 / BIOS 3.06 | Реальный Sprinter | Артефакт | -|---|---|---|---|---| -| P1-DIR-01 | CWD и `chdir(".")` | PASS | PENDING | [результат](../artifacts/mame-p1/directory/strict-enoent.png) | -| P1-DIR-02 | `*.*` против `*` | PASS: `2` против `0`; полный scan использует только `*.*` | PENDING | [результат](../artifacts/mame-p1/directory/strict-enoent.png) | -| P1-DIR-03 | Код конца F_NEXT | PASS, `ENOENT=3` | PENDING | [строгая проверка](../artifacts/mame-p1/directory/strict-enoent.png) | -| P1-EMM-01 | Блок 3 страницы | PASS, страницы `EF/F0/F1` | PENDING | [результат](../artifacts/mame-p1/emm/result.png) | -| P1-EMM-02 | Границы страниц | PASS | PENDING | [результат](../artifacts/mame-p1/emm/result.png) | -| P1-EMM-03 | Счётчик после free | PASS, `221 → 221` | PENDING | [результат](../artifacts/mame-p1/emm/result.png) | -| P1-SCR-01 | Полный WINREST 80x32 | PASS, визуально | PENDING | [контрастный экран](../artifacts/mame-p1/winrest/full-contrast.png) | -| P1-SCR-02 | Offset WINREST | PASS, row 14 / col 30 / `0x2000` | PENDING | [фон и окно вместе](../artifacts/mame-p1/winrest/partial-contrast-stable.png) | -| P1-SCR-03 | Цветные атрибуты в двух моментах цикла | PASS, RGB кадров идентичен; P2.2 подтвердил 6 атрибутов × 4 плана | PENDING | [кадр 1](../artifacts/mame-p1/winrest/partial-flash-phase.png), [кадр 2](../artifacts/mame-p1/winrest/partial-contrast-stable.png), [P2.2](p2-palette-results.md) | -| P1-SCR-04 | Raw-атрибуты `0x11..0x17` | OBSERVED, raw-арифметика запрещена | PENDING | [неудачный исходный pattern](../artifacts/mame-p1/winrest/partial-phase-hidden.png) | -| P1-KBD-01 | Базовые scan-коды | PASS | PENDING | [таблица](../artifacts/mame-p1/keyboard-commander/sprinter/0002.png) | -| P1-KBD-02 | Ctrl/Alt modifiers | PASS | PENDING | [сочетания](../artifacts/mame-p1/keyboard-commander/sprinter/0003.png) | -| P1-EXE-01 | EXEC B=0 | PASS | PENDING | [результат](../artifacts/mame-p1/exec/result.png) | -| P1-EXE-02 | EXEC B=1 | PASS | PENDING | [результат](../artifacts/mame-p1/exec/result.png) | -| P1-EXE-03 | Exit/WAIT `0x5A` | PASS / PASS | PENDING | [результат](../artifacts/mame-p1/exec/result.png) | -| P1-EXE-04 | Восстановление W1/W2/W3 | PASS, `F3/F2/FF` | PENDING | [результат](../artifacts/mame-p1/exec/result.png) | -| P1-EXE-05 | EMM после двух EXEC | PASS | PENDING | [контроль](../artifacts/mame-p1/exec-then-emm/emm-result.png) | -| P1-DSK-01 | DSKINFO | PASS | PENDING | [результат](../artifacts/mame-p1/dskinfo/result.png) | - -### 9.1. Зафиксированные значения - -- DSKINFO: 1 сектор на кластер, 512 байт на сектор, 2847 кластеров всего, - 2814 свободно. -- EMM до выделения: 256 страниц всего, 221 свободна; после освобождения - снова 221. -- Оба режима EXEC: `rc=0`, `errno=0`, непосредственный exit=`5A`, WAIT=`5A`. -- CWD до и после child: `\`; страницы W1/W2/W3 до и после: `F3/F2/FF`. -- Модифицированная клавиша приходит с установленным битом 7 scan-кода: - Ctrl+R=`93`, Ctrl+PgUp=`D9`, Alt+F1=`BB`. После `scan & 0x7F` получаются - базовые коды `13`, `59`, `3B`; compact modifiers равны `02`, `02`, `04`. -- Esc: `ascii=1B`, `scan=01`. - -## 10. Условия завершения P1 - -MAME-часть P1 завершена: все обязательные строки имеют `PASS`, фактические -клавиатурные коды перенесены в keymap, завершение `fnext()` уточнено, -`EXEC B=1`/WAIT и EMM после child подтверждены, артефакты сохранены. - -Проверка на реальном Sprinter остаётся желательной аппаратной валидацией, -но не блокирует переход к P2 и реализацию platform API PoC. Если реальное -железо даст расхождение, оно оформляется отдельным репро и не маскируется -адаптацией UI. diff --git a/applications/Volkov/docs/p10-rename-results.md b/applications/Volkov/docs/p10-rename-results.md deleted file mode 100644 index 6d1ead5..0000000 --- a/applications/Volkov/docs/p10-rename-results.md +++ /dev/null @@ -1,77 +0,0 @@ -# Sprinter Commander: локальный rename 0.2/P10 - -Дата: 8 сентября 2026 года. - -Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`. - -## Реализованный срез F6 - -`F6` переименовывает одну текущую обычную запись внутри активного каталога. -Поддержаны как файлы, так и каталоги. Диалог начинает с текущего имени и -использует общий редактор; новое имя проходит тот же строгий validator одного -компонента DOS 8.3, что и F7. - -Это пока локальный `Rename`, а не полный VC `Move/Rename`: - -- межкаталожный move в пассивную панель ещё не выполняется; -- если в панели есть неоднозначная группа, команда явно отказывается от - операции; единственная выбранная текущая запись допустима; -- `..` и маркер предела DSS никогда не передаются файловой системе; -- совпадающее без учёта ASCII-регистра имя считается no-op. - -Низкоуровневый `sc_rename_local()` учитывает подтверждённое ограничение DSS -1.71: `RENAME` принимает два basename относительно CWD. Helper сохраняет CWD, -временно входит в активный каталог, проверяет отсутствие target через -`F_FIRST`, выполняет `rename()` и восстанавливает CWD. Таким образом -существующая запись не перезаписывается даже при иной семантике конкретной -версии DSS. - -## Проверка - -`RENRT.EXE` вызывает тот же helper и проверяет: - -- rename файла с побайтно неизменным содержимым; -- отказ при существующем target и сохранность обоих файлов; -- rename непустого каталога с сохранностью вложенного файла; -- CWD после успеха, collision и ошибки входа в каталог. - -После UI-сценария внешний checker извлекает HDD и подтверждает окончательные -имена и содержимое: - -```text -PASS: file/dir rename, no-overwrite, CWD restore и UI F6. -``` - -Контрольные состояния UI: - -- [диалог с исходным именем](../artifacts/mame-p10/rename/dialog-default.png); -- [`FINAL.BIN` создан и остался под cursor](../artifacts/mame-p10/rename/renamed-focused.png); -- [неизменное имя обработано как no-op](../artifacts/mame-p10/rename/unchanged-name.png); -- [существующий target отвергнут](../artifacts/mame-p10/rename/collision.png); -- [`..` не переименовывается](../artifacts/mame-p10/rename/virtual-guard.png); -- [неоднозначная группа не превращается в rename cursor](../artifacts/mame-p10/rename/group-guard.png); -- [штатный возврат в DSS](../artifacts/mame-p10/rename/clean-dss-return.png). - -## Размер - -Конфигурация `--memory big --safe --max-allocs 3000`: - -| Область | После P9 + UI footer | После P10 | Изменение | -|---|---:|---:|---:| -| `_CODE` W2 | 9449 | 9463 | +14 | -| данные W2 | 3256 | 3256 | 0 | -| свободная куча W2 | 2143 | 2129 | -14 | -| BANK1 | 6477 | 6477 | 0 | -| BANK2 | 8303 | 10008 | +1705 | -| `SPRCMD.EXE` | 43026 | 43040 | +14 | - -В BANK2 остаётся 6376 байт, в BANK1 — 9907 байт. Межбанковый аудит чист. - -## Воспроизведение - -```sh -make hdd-p10-rename ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p10_rename.chd \ - tests/mame_p10_rename.lua artifacts/mame-p10/local 65 -tests/check_p10_rename_hdd.sh build/hdd/p10_rename.chd -``` diff --git a/applications/Volkov/docs/p11-delete-results.md b/applications/Volkov/docs/p11-delete-results.md deleted file mode 100644 index f354f7e..0000000 --- a/applications/Volkov/docs/p11-delete-results.md +++ /dev/null @@ -1,71 +0,0 @@ -# Sprinter Commander: однообъектный delete 0.2/P11 - -Дата: 9 сентября 2026 года. - -Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`. - -## Реализованный срез F8 - -`F8` удаляет одну текущую обычную запись только после модального -подтверждения: - -- обычный файл удаляется через `unlink()`; -- пустой каталог — через `rmdir()`; -- непустой каталог сохраняется и даёт управляемую ошибку; -- `N`/`Esc` отменяют команду без изменения носителя; -- `..` и маркер предела DSS не передаются файловой системе; -- в срезе P11 неоднозначная выбранная группа отклонялась; начиная с P13 - выбранные файлы удаляются группой, а выбранные каталоги пропускаются. - Это исторический контракт P11/P13; начиная с P17 каталоги выбранной группы - удаляются рекурсивно. - -Рекурсивное и групповое удаление намеренно не имитируются циклом вокруг -текущего cursor. Они будут реализованы общей файловой job с определённой -политикой частичного успеха и cancel. - -## Проверка - -Target-проба `DELRT.EXE` проверяет удаление обычного файла и пустого каталога -по полным путям, отказ для непустого каталога, сохранность соседнего файла и -неизменность CWD. UI-сценарий затем проверяет обе стороны подтверждения и -защитные ветки. После выхода дерево извлекается из CHD и проверяется -побайтно: - -```text -PASS: file/empty-dir delete, cancel, guards, nonempty и CWD. -``` - -Контрольные состояния: - -- [подтверждение удаления файла](../artifacts/mame-p11/delete/file-confirm.png); -- [cancel сохранил файл](../artifacts/mame-p11/delete/cancel-preserves.png); -- [файл удалён после подтверждения](../artifacts/mame-p11/delete/file-deleted.png); -- [пустой каталог удалён](../artifacts/mame-p11/delete/empty-dir-deleted.png); -- [непустой каталог сохранён](../artifacts/mame-p11/delete/nonempty-preserved.png); -- [`..` заблокирован](../artifacts/mame-p11/delete/virtual-guard.png); -- [неоднозначная группа заблокирована](../artifacts/mame-p11/delete/group-guard.png); -- [штатный возврат в DSS](../artifacts/mame-p11/delete/clean-dss-return.png). - -## Размер - -Конфигурация `--memory big --safe --max-allocs 3000`: - -| Область | После P10 | После P11 | Изменение | -|---|---:|---:|---:| -| `_CODE` W2 | 9463 | 9496 | +33 | -| данные W2 | 3256 | 3256 | 0 | -| свободная куча W2 | 2129 | 2096 | -33 | -| BANK1 | 6477 | 6477 | 0 | -| BANK2 | 10008 | 10942 | +934 | -| `SPRCMD.EXE` | 43040 | 43073 | +33 | - -В BANK2 остаётся 5442 байта, в BANK1 — 9907 байт. Межбанковый аудит чист. - -## Воспроизведение - -```sh -make hdd-p11-delete ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p11_delete.chd \ - tests/mame_p11_delete.lua artifacts/mame-p11/local 67 -tests/check_p11_delete_hdd.sh build/hdd/p11_delete.chd -``` diff --git a/applications/Volkov/docs/p12-group-copy-results.md b/applications/Volkov/docs/p12-group-copy-results.md deleted file mode 100644 index ee74acb..0000000 --- a/applications/Volkov/docs/p12-group-copy-results.md +++ /dev/null @@ -1,78 +0,0 @@ -# Sprinter Commander: групповое копирование файлов 0.2/P12 - -Дата: 9 сентября 2026 года. - -Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`. - -Примечание: документ фиксирует исторический срез P12, где directory roots -ещё пропускались. Начиная с P16 текущий F5 копирует их рекурсивно; fixture и -checker P12 обновлены как регрессия нового контракта, а исходные кадры ниже -оставлены для истории этапа. - -## Контракт первого группового среза - -Если в активной панели нет выбора, F5 сохраняет проверенное поведение -однофайлового PoC. При ненулевом выборе F5: - -1. Считает выбранные обычные файлы и отдельно выбранные каталоги. -2. Последовательно копирует только файлы в порядке текущей сортировки панели. -3. Для каждого файла использует прежний crash-safe `temp -> commit` и общую - 4-КБ EMM-страницу W3. -4. Проверяет Esc между блоками и файлами. -5. При cancel/error удаляет только незавершённый temp; уже завершённые файлы - сохраняются. -6. Останавливается на первой ошибке и сообщает `copied/total`, число - пропущенных каталогов и errno. -7. Перечитывает passive panel один раз после завершённых копий. - -Выбранные каталоги намеренно игнорируются до появления рекурсивной job. Это -не меняет их флаг выбора и согласуется с правилом: групповые `+`, `-`, `*` -работают только над файлами. - -Overwrite по-прежнему запрещён. Повторный запуск поверх уже скопированных -файлов останавливается на первом `EEXIST`; существующие targets не меняются. - -## Проверка - -HDD-fixture содержит выбранный вручную `ADIR` и три файла размером 10, 4097 -и 0 байт. После первого F5 внешний checker подтверждает три побайтно точные -копии, отсутствие каталога и temp. Второй F5 проверяет управляемую остановку -на существующем `A.BIN`. - -```text -PASS: 3 selected files copied; selected directory skipped; no temp. -``` - -Контрольные состояния: - -- [каталог и три файла выбраны](../artifacts/mame-p12/group-copy/selected-files-and-dir.png); -- [скопировано 3/3, один каталог пропущен](../artifacts/mame-p12/group-copy/copied-and-skipped-dir.png); -- [повтор остановлен на EEXIST](../artifacts/mame-p12/group-copy/repeat-stops-eexist.png); -- [passive panel перечитана](../artifacts/mame-p12/group-copy/passive-refreshed.png); -- [штатный возврат в DSS](../artifacts/mame-p12/group-copy/clean-dss-return.png). - -## Компоновка и размер - -Конфигурация `--memory big --safe --max-allocs 3000`: - -| Область | После P11 | После P12 | Изменение | -|---|---:|---:|---:| -| `_CODE` W2 | 9496 | 9496 | 0 | -| данные W2 | 3256 | 3256 | 0 | -| свободная куча W2 | 2096 | 2096 | 0 | -| BANK1 | 6477 | 6477 | 0 | -| BANK2 | 10942 | 12256 | +1314 | -| `SPRCMD.EXE` | 43073 | 43073 | 0 | - -Пробный перенос обработчика в новый BANK3 увеличивал EXE ровно на 16384 -байта при 2823 байтах полезного кода. Поэтому P12 оставлен в BANK2: там ещё -4128 байт запаса, а размер файла не вырос. Межбанковый аудит чист. - -## Воспроизведение - -```sh -make hdd-p12-group-copy ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p12_group_copy.chd \ - tests/mame_p12_group_copy.lua artifacts/mame-p12/local 57 -tests/check_p12_group_copy_hdd.sh build/hdd/p12_group_copy.chd -``` diff --git a/applications/Volkov/docs/p13-group-delete-results.md b/applications/Volkov/docs/p13-group-delete-results.md deleted file mode 100644 index 8f11f44..0000000 --- a/applications/Volkov/docs/p13-group-delete-results.md +++ /dev/null @@ -1,78 +0,0 @@ -# Sprinter Commander: групповое удаление файлов 0.2/P13 - -Дата: 9 сентября 2026 года. - -Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`. - -Это исторический контракт P13. Начиная с P17 выбранные каталоги не -пропускаются, а удаляются рекурсивно после одного общего подтверждения; новый -контракт и актуальные артефакты описаны в -[p17-tree-delete-results.md](p17-tree-delete-results.md). Текущий P13 fixture -оставлен как регрессия file-only группы. - -## Контракт первого группового среза F8 - -Если в активной панели есть выбранные записи, F8: - -1. Считает выбранные обычные файлы и выбранные каталоги отдельно. -2. Если выбранных файлов нет, не открывает подтверждение и сообщает, что - рекурсивное удаление каталогов ещё не реализовано. -3. Показывает одно подтверждение для всей группы файлов. -4. После подтверждения проходит EMM-store в порядке панели и удаляет только - выбранные записи без `FA_DIREC`. -5. Останавливается на первой ошибке. Уже удалённые файлы не восстанавливаются; - итог сообщает `deleted/total`, число пропущенных каталогов и errno. -6. После хотя бы одного успешного удаления перечитывает активную панель. - -Выбранные каталоги не удаляются и не обходятся рекурсивно. Это согласуется с -контрактом групповых `+`, `-`, `*`: групповые файловые команды отделяют файлы -от каталогов. При отсутствии выбора сохранён однообъектный F8 из P11: файл -удаляется через `unlink()`, пустой каталог — через `rmdir()`. - -## Проверка - -HDD-fixture содержит два выбранных файла, один невыбранный файл, выбранный -пустой каталог и выбранный непустой каталог. Сценарий сначала отменяет -групповое подтверждение, затем повторяет операцию с подтверждением. После -refresh отдельно выбирается только каталог: F8 обязан отказать без диалога и -без изменения носителя. - -После чистого выхода MAME дерево извлекается из CHD и проверяется побайтно: - -```text -PASS: selected files deleted; selected directories and KEEP preserved. -``` - -Контрольные состояния: - -- [два файла и два каталога выбраны](../artifacts/mame-p13/group-delete/selected-files-and-dirs.png); -- [одно подтверждение группы](../artifacts/mame-p13/group-delete/group-confirm.png); -- [отмена сохранила группу](../artifacts/mame-p13/group-delete/cancel-preserves.png); -- [файлы удалены, каталоги сохранены](../artifacts/mame-p13/group-delete/files-deleted-dirs-preserved.png); -- [directory-only группа отклонена](../artifacts/mame-p13/group-delete/directory-only-refused.png); -- [штатный возврат в DSS](../artifacts/mame-p13/group-delete/clean-dss-return.png). - -## Компоновка и размер - -Конфигурация `--memory big --safe --max-allocs 3000`: - -| Область | После P12 | После P13 | Изменение | -|---|---:|---:|---:| -| `_CODE` W2 | 9496 | 9496 | 0 | -| данные W2 | 3256 | 3256 | 0 | -| свободная куча W2 | 2096 | 2096 | 0 | -| BANK1 | 6477 | 6477 | 0 | -| BANK2 | 12256 | 13267 | +1011 | -| `SPRCMD.EXE` | 43073 | 43073 | 0 | - -В BANK2 остаётся 3117 байт. HOME и размер EXE не выросли; межбанковый аудит -чист. - -## Воспроизведение - -```sh -make hdd-p13-group-delete ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p13_group_delete.chd \ - tests/mame_p13_group_delete.lua artifacts/mame-p13/local 58 -tests/check_p13_group_delete_hdd.sh build/hdd/p13_group_delete.chd -``` diff --git a/applications/Volkov/docs/p14-default-colors-results.md b/applications/Volkov/docs/p14-default-colors-results.md deleted file mode 100644 index 77cc493..0000000 --- a/applications/Volkov/docs/p14-default-colors-results.md +++ /dev/null @@ -1,62 +0,0 @@ -# Sprinter Commander: стандартные цвета имён P14 - -Дата: 9 сентября 2026 года. - -Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`. - -## Реализованный профиль без конфигурации - -Renderer выбирает атрибут обычной строки по типу записи: - -- каталоги и синтетическая `..` — белые; -- файлы с точным регистронезависимым расширением `.EXE` — жёлтые; -- остальные файлы, в том числе без расширения, — светло-серые; -- виртуальный маркер предела DSS остаётся красным; -- cursor активной панели сохраняет отдельную контрастную cyan-подсветку. - -Цвет применяется ко всей строке записи, поэтому имя, размер и атрибуты не -расходятся визуально. `SC_ATTR_DIR` и `SC_ATTR_EXE` переиспользуют уже -сохранённые палитровые атрибуты frame/title; новый атрибут потребовался только -для обычного файла. - -Настраиваемые правила по расширениям и файл конфигурации ещё не реализованы. -Текущий профиль является обязательным fallback при отсутствии такого файла. - -## Проверка - -Отдельный HDD содержит `SUBDIR`, `APP.EXE`, `ARCHIVE.ZIP`, `README.TXT` и -`NOEXT`. [Контрольный кадр](../artifacts/mame-p14/default-colors/file-types.png) -показывает все три класса одновременно в активной и passive панелях. После -F10 [палитра DSS восстановлена](../artifacts/mame-p14/default-colors/clean-dss-palette.png). -Внешняя проверка извлечённого CHD подтверждает, что визуальный тест не изменил -fixture: - -```text -PASS: colour fixture preserved. -``` - -## Компоновка и размер - -Конфигурация `--memory big --safe --max-allocs 3000`: - -| Область | После P13 | После P14 | Изменение | -|---|---:|---:|---:| -| `_CODE` W2 | 9496 | 9503 | +7 | -| данные W2 | 3256 | 3268 | +12 | -| свободная куча W2 | 2096 | 2077 | -19 | -| BANK1 | 6477 | 6477 | 0 | -| BANK2 | 13267 | 13388 | +121 | -| `SPRCMD.EXE` | 43073 | 43080 | +7 | - -Двенадцать байт DATA/BSS — сохранённые RGB трёх компонент для четырёх -палитровых планов нового атрибута. В BANK2 остаётся 2996 байт; межбанковый -аудит чист. - -## Воспроизведение - -```sh -make hdd-p14-default-colors ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p14_default_colors.chd \ - tests/mame_p14_default_colors.lua artifacts/mame-p14/local 36 -tests/check_p14_default_colors_hdd.sh build/hdd/p14_default_colors.chd -``` diff --git a/applications/Volkov/docs/p15-tree-copy-results.md b/applications/Volkov/docs/p15-tree-copy-results.md deleted file mode 100644 index 033d6a0..0000000 --- a/applications/Volkov/docs/p15-tree-copy-results.md +++ /dev/null @@ -1,117 +0,0 @@ -# Sprinter Commander: рекурсивный F5 одного каталога 0.2/P15 - -Дата: 9 сентября 2026 года. - -Статус: основной и отрицательные сценарии `PASS` в MAME 0.288 / BIOS 3.06 / -DSS 1.71.57 на HDD `D:`. - -## Реализованный контракт - -Если в активной панели нет выбранной группы и cursor стоит на обычном -каталоге, F5 рекурсивно создаёт одноимённое дерево в каталоге passive panel: - -- обход breadth-first, без рекурсии C-стека; -- каждый файл проходит прежний crash-safe `temp -> commit` и общий 4-КБ - copy-buffer в выделенной странице W3; -- одновременно открыты не более source и temp; -- пустые каталоги создаются; -- existing target не объединяется и не перезаписывается; -- Esc проверяется между каталогами и на каждом 4-КБ шаге файла; -- при cancel/error уже committed-файлы и созданные каталоги остаются, temp - текущего файла удаляется; -- CWD процесса восстанавливается и passive panel перечитывается после - изменения target. - -Начиная с P16 выбранные каталоги также добавляются как несколько roots в ту -же очередь. Этот документ фиксирует более узкий single-root срез P15. - -## Память обхода - -`sc_tree` на время одной операции выделяет одну дополнительную EMM-страницу. -Первые 256 байт сохраняют исходный CWD. Остаток содержит 1008 узлов по 16 -байт: `parent index + DOS 8.3 component + depth`. Абсолютный путь строится -перед операцией по цепочке parent; фиксированный предел глубины — 32, а общий -предел пути остаётся 255 символов. - -4-КБ данные файла не смешиваются с traversal queue и не размещаются в W2. -W3 отображается только внутри `bank_read/write` и `bank_read_page/ -bank_write_page`; вызовы сами восстанавливают прежнюю страницу. - -До добавления root проверяется, что каталог passive panel не совпадает с -source root и не лежит внутри него. Это запрещает саморастущее копирование. - -## Проверка - -Fixture включает пустой каталог, файлы размером 0, 15, 16 и 4097 байт и -цепочку из 16 вложенных уровней. MAME сообщил точный итог: - -```text -Tree copied: 4 files; 19 dirs. -``` - -После выхода source и target извлечены из CHD и сравнены как полные деревья: - -```text -PASS: recursive tree copied exactly; depth=16; no temp. -``` - -Повторный F5 остановился до изменения файлов: DSS вернул `EISDIR` (`errno -15`) для уже существующего target-каталога. Контрольные состояния: - -- [source TREE и пустой target](../artifacts/mame-p15/tree-copy/before.png); -- [успех 4 files / 19 dirs](../artifacts/mame-p15/tree-copy/success.png); -- [repeat остановлен на existing target](../artifacts/mame-p15/tree-copy/repeat-existing-target.png); -- [passive panel перечитана](../artifacts/mame-p15/tree-copy/passive-refreshed.png); -- [Esc удалил temp большого файла](../artifacts/mame-p15/tree-copy/cancel-clean.png); -- [target внутри source отклонён до mutation](../artifacts/mame-p15/tree-copy/self-target-refused.png); -- [чистый возврат в DSS](../artifacts/mame-p15/tree-copy/clean-dss-return.png). - -Отдельный cancel-fixture начал копирование `BIG.BIN` размером 4 МБ и подал -Esc до commit. Target root остался как определённый частичный результат, -файл и temp отсутствуют, source не изменён: - -```text -PASS: tree cancel removed active temp; source intact; no BIG commit. -``` - -Self-target fixture расположил passive panel в `SOURCE\TREE\INNER`. Операция -остановилась с `errno 14` (`EINVAL` отображается libc на родной DSS -`EUNKOP`) до первого `mkdir`; `INNER` остался пустым: - -```text -PASS: target-inside-source refused before mutation. -``` - -## Компоновка и размер - -Конфигурация `--memory big --safe --max-allocs 3000`: - -| Область | После P14 | После P15 | Изменение | -|---|---:|---:|---:| -| `_CODE` W2 | 9503 | 9503 | 0 | -| данные W2 | 3268 | 3524 | +256 | -| свободная куча W2 | 2077 | 1821 | -256 | -| BANK1 | 6477 | 9687 | +3210 | -| BANK2 | 13388 | 14547 | +1159 | -| `SPRCMD.EXE` | 43080 | 43080 | 0 | - -256 байт W2 — единственный `ffblk_t` traversal engine. Крупная очередь -динамическая и в эти числа не входит. BANK1/BANK2 имеют соответственно 6697 -и 1837 байт запаса; межбанковый аудит чист. - -## Воспроизведение - -```sh -make hdd-p15-tree-copy ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p15_tree_copy.chd \ - tests/mame_p15_tree_copy.lua artifacts/mame-p15/local 60 -tests/check_p15_tree_copy_hdd.sh build/hdd/p15_tree_copy.chd - -make hdd-p15-tree-cancel hdd-p15-tree-self ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p15_tree_cancel.chd \ - tests/mame_p15_tree_cancel.lua artifacts/mame-p15/cancel 48 -tests/check_p15_tree_cancel_hdd.sh build/hdd/p15_tree_cancel.chd -tests/run_mame_hdd.sh build/hdd/p15_tree_self.chd \ - tests/mame_p15_tree_self.lua artifacts/mame-p15/self 48 -tests/check_p15_tree_self_hdd.sh build/hdd/p15_tree_self.chd -``` diff --git a/applications/Volkov/docs/p16-group-tree-copy-results.md b/applications/Volkov/docs/p16-group-tree-copy-results.md deleted file mode 100644 index 8a2714b..0000000 --- a/applications/Volkov/docs/p16-group-tree-copy-results.md +++ /dev/null @@ -1,72 +0,0 @@ -# Sprinter Commander: рекурсивный group F5 0.2/P16 - -Дата: 9 сентября 2026 года. - -Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`. - -## Реализованный контракт - -При групповом F5 Commander сначала последовательно копирует выбранные файлы, -а затем добавляет все выбранные каталоги текущего уровня как независимые -roots в одну traversal queue P15. Для каждого root действует тот же контракт: - -- breadth-first обход и пустые каталоги; -- crash-safe temp/commit для файлов; -- cancel между файлами, каталогами и 4-КБ блоками; -- no-overwrite и self-target guard; -- один динамический EMM-блок очереди на всю группу; -- один refresh passive panel после операции. - -Если top-level файл даёт ошибку или cancel до запуска обхода, directory roots -остаются `unprocessed`. При ошибке одного tree root операция останавливается; -уже committed-файлы и созданные каталоги сохраняются. - -Это не меняет семантику `+`, `-`, `*`: групповые команды выделения по-прежнему -игнорируют каталоги. Каталог может попасть в F5-группу только после отдельного -`Insert`. - -## Проверка - -В active panel вручную выбраны `ADIR`, `BDIR` и `ROOT.BIN`; `KEEP.TXT` -оставлен невыбранным. Два root-каталога содержат по файлу, а `ADIR` — ещё и -пустой вложенный каталог. UI сообщил: - -```text -Tree copied: 3 files; 3 dirs. -``` - -Внешний checker сравнил target с точным ожидаемым множеством и подтвердил -отсутствие `KEEP.TXT` и temp: - -```text -PASS: selected file and two directory roots copied exactly. -``` - -Контрольные состояния: - -- [два каталога и файл выбраны](../artifacts/mame-p16/group-tree/selected-roots.png); -- [группа скопирована](../artifacts/mame-p16/group-tree/copied.png); -- [passive panel перечитана](../artifacts/mame-p16/group-tree/passive-refreshed.png); -- [чистый возврат в DSS](../artifacts/mame-p16/group-tree/clean-dss-return.png). - -## Компоновка - -`_CODE` W2, DATA, BANK1 и размер EXE не изменились относительно P15. BANK2 -вырос с 14547 до 14918 байт (+371); остаётся 1466 байт. Итоговая сборка: - -```text -_CODE 9503; DATA 3524; heap 1821 -BANK1 9687/16384; BANK2 14918/16384 -SPRCMD.EXE 43080 -``` - -Межбанковый аудит чист. - -## Воспроизведение - -```sh -make hdd-p16-group-tree-copy ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p16_group_tree_copy.chd \ - tests/mame_p16_group_tree_copy.lua artifacts/mame-p16/local 60 -tests/check_p16_group_tree_copy_hdd.sh build/hdd/p16_group_tree_copy.chd -``` diff --git a/applications/Volkov/docs/p17-tree-delete-results.md b/applications/Volkov/docs/p17-tree-delete-results.md deleted file mode 100644 index e2cd426..0000000 --- a/applications/Volkov/docs/p17-tree-delete-results.md +++ /dev/null @@ -1,101 +0,0 @@ -# Sprinter Commander: рекурсивный group F8 0.2/P17 - -Дата: 10 сентября 2026 года. - -Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`. - -## Контракт - -Если в панели есть выбранные записи, F8 показывает одно подтверждение для -всей группы. После подтверждения: - -1. выбранные обычные файлы верхнего уровня удаляются в порядке панели; -2. выбранные каталоги становятся корнями одного BFS-job; -3. содержимое каталогов перечисляется через `F_FIRST/F_NEXT`, файлы удаляются - по мере обхода, а найденные подкаталоги добавляются в очередь; -4. после полного scan очередь проходится в обратном порядке, поэтому каждый - дочерний каталог удаляется раньше родителя; -5. исходный CWD восстанавливается, динамическая EMM-страница освобождается, - активная панель перечитывается. - -Очередь та же, что у recursive F5: одна динамическая 16-КБ страница, 1008 -узлов по 16 байт, максимальная глубина 32. В W2 дерево не хранится. `Esc` -проверяется между шагами; уже выполненные удаления при ошибке или отмене не -откатываются, что явно отражается в статусе. - -При отсутствии группового выбора однообъектный F8 сохраняет прежний контракт: -файл удаляется, а каталог должен быть пустым. - -## Проверка - -Fixture содержит два выбранных дерева и выбранный файл, а также невыбранные -`KEEPDIR/SAFE.BIN` и `KEEP.TXT`. Первое дерево имеет пустой каталог, четыре -файла размеров 0/1/16/4097 и цепочку глубиной 12. Несколько файлов в одном -каталоге отдельно проверяют продолжение `F_NEXT` после `unlink` текущей -записи. - -Сценарий сначала отменяет общее подтверждение и убеждается, что выбор и -панель не изменились, затем подтверждает удаление. После выхода содержимое -CHD извлекается и проверяется побайтно: - -```text -PASS: selected file and two directory trees deleted; KEEP preserved. -``` - -Отдельный runtime-cancel fixture выбирает `TOP.BIN` и дерево из 180 -подкаталогов. `TOP.BIN` удаляется до начала обхода, затем Lua подаёт `Esc` во -время заполнения очереди. После отмены дерево остаётся доступным, завершённое -удаление не откатывается, `KEEP.TXT` не меняется: - -```text -PASS: runtime cancel kept remaining tree; completed TOP delete remains. -``` - -Чтобы `Esc` не ждал завершения длинной серии `F_NEXT`, delete-итератор -возвращает управление вызывающему коду после каждого добавленного каталога. - -Контрольные кадры: - -- [исходное дерево](../artifacts/mame-p17/tree-delete/before.png); -- [два каталога и файл выбраны](../artifacts/mame-p17/tree-delete/selected-file-and-trees.png); -- [общее подтверждение](../artifacts/mame-p17/tree-delete/group-confirm.png); -- [отмена до mutation сохранила группу](../artifacts/mame-p17/tree-delete/cancel-preserves.png); -- [после удаления остались только KEEP-объекты](../artifacts/mame-p17/tree-delete/deleted-keep-preserved.png); -- [чистый возврат в DSS](../artifacts/mame-p17/tree-delete/clean-dss-return.png). - -Runtime-cancel: - -- [выбранное дерево и верхнеуровневый файл](../artifacts/mame-p17/tree-delete-cancel/selected-before-cancel.png); -- [подтверждение](../artifacts/mame-p17/tree-delete-cancel/group-confirm.png); -- [частичный результат после Esc](../artifacts/mame-p17/tree-delete-cancel/runtime-cancelled.png); -- [чистый возврат в DSS](../artifacts/mame-p17/tree-delete-cancel/clean-dss-return.png). - -## Компоновка - -P17 остаётся в двух банках и не увеличивает образ EXE: каждый дополнительный -банк добавил бы 16 КБ независимо от фактического заполнения. Текущая сборка -`--memory big --safe --max-allocs 3000`: - -```text -_CODE 9503; DATA 3524; heap 1821 -BANK1 10562/16384; BANK2 15904/16384 -SPRCMD.EXE 43080 -``` - -В BANK2 остаётся 480 байт, поэтому следующий функциональный этап потребует -осмысленной перекладки модулей в BANK3. Межбанковый аудит чист. - -## Воспроизведение - -```sh -make hdd-p17-tree-delete ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p17_tree_delete.chd \ - tests/mame_p17_tree_delete.lua artifacts/mame-p17/local 58 -tests/check_p17_tree_delete_hdd.sh build/hdd/p17_tree_delete.chd - -make hdd-p17-tree-delete-cancel ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p17_tree_delete_cancel.chd \ - tests/mame_p17_tree_delete_cancel.lua artifacts/mame-p17/cancel-local 50 -tests/check_p17_tree_delete_cancel_hdd.sh \ - build/hdd/p17_tree_delete_cancel.chd -``` diff --git a/applications/Volkov/docs/p18-metadata-results.md b/applications/Volkov/docs/p18-metadata-results.md deleted file mode 100644 index 94e583f..0000000 --- a/applications/Volkov/docs/p18-metadata-results.md +++ /dev/null @@ -1,89 +0,0 @@ -# Sprinter Commander: сохранение метаданных F5 0.2/P18 - -Дата: 10 сентября 2026 года. - -Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`. - -## Контракт - -Одиночный, групповой и рекурсивный F5 сохраняют у каждого обычного файла: - -- FAT date/time, включая двухсекундную точность; -- атрибуты readonly, hidden, system и archive. - -Date/time задаются вызовом DSS `PUT_D_T` на открытом temp-файле после -последней записи и до `close()`. Атрибуты задаются через `ATTRIB` только -после атомарного rename temp в конечное имя. Такой порядок не делает temp -readonly до commit и сохраняет прежнюю защиту существующего target. - -Если ошибка происходит до rename, общий cleanup закрывает дескрипторы и -удаляет temp. Если `ATTRIB` или восстановление CWD завершаются ошибкой уже -после rename, `ScCopyJob.committed` не позволяет выдавать результат за -отсутствующий файл: UI сообщает `File committed; finalization failed`. - -Метаданные каталогов на этом этапе не переносятся. - -## Проверка - -Fixture создаёт `MSRC/META.BIN` размером 4097 байт с контролируемым payload, -датой `2001-02-03`, временем `04:05:06` и набором атрибутов `R/H/S/A`. -Подготовленный raw FAT проверяется до MAME, чтобы текущая дата сборки не могла -дать ложный положительный результат. - -После F5 извлекается конечный CHD и собственным FAT16-reader проверяются обе -32-байтовые directory entries и цепочка кластеров: - -```text -PASS: F5 preserved exact FAT date/time and R/H/S/A attributes. -``` - -На экране обе панели показывают одинаковые размер, `RHSA` и -`2001-02-03 04:05`; секунды и payload подтверждает raw-проверка. - -Контрольные кадры: - -- [исходные метаданные](../artifacts/mame-p18/metadata/source-metadata.png); -- [файл скопирован с теми же полями](../artifacts/mame-p18/metadata/copied-metadata.png); -- [пассивная панель после переключения](../artifacts/mame-p18/metadata/passive-metadata.png); -- [чистый возврат в DSS](../artifacts/mame-p18/metadata/clean-dss-return.png). - -После P18 повторно пройдены: - -- P5 copy-fault matrix: `ALL PASS`, CHD-check `PASS`; -- P16 recursive group F5: UI корректен, все выбранные деревья и файлы - совпали побайтно. - -## ABI DSS - -`sc_metadata_set_datetime()` раскрывает packed FAT date/time в поля -`PUT_D_T $18`. Низкоуровневый leaf получает handle в `A`, near-указатель на -семибайтовую структуру в `DE`, загружает год в `IX` и сохраняет caller IX. - -Для `ATTRIB $16` сгенерированный `.asm` подтвердил важную особенность -`__sdcccall(1)`: второй однобайтовый аргумент banked-wrapper лежит на стеке, -а `E` занят номером банка. Поэтому публичная banked-функция вызывает -локальный raw-leaf с обычным ABI; leaf снимает однобайтовый аргумент и адрес -возврата явно. Оба пути переводят `CF/A` в `errno`. - -## Компоновка - -Сборка `--memory big --safe --max-allocs 3000` остаётся двухбанковой и не -увеличивает EXE за счёт пустого третьего банка: - -```text -_CODE 9503; DATA 3531; heap 1814 -BANK1 11011/16384; BANK2 16145/16384 -SPRCMD.EXE 43080 -``` - -В BANK2 остаётся 239 байт. Следующий заметный UI/policy-модуль следует -размещать с осмысленной перекладкой в BANK3. Межбанковый аудит чист. - -## Воспроизведение - -```sh -make hdd-p18-metadata ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p18_metadata.chd \ - tests/mame_p18_metadata.lua artifacts/mame-p18/local 48 -tests/check_p18_metadata_hdd.sh build/hdd/p18_metadata.chd -``` diff --git a/applications/Volkov/docs/p19-copy-policy-results.md b/applications/Volkov/docs/p19-copy-policy-results.md deleted file mode 100644 index eccb5d1..0000000 --- a/applications/Volkov/docs/p19-copy-policy-results.md +++ /dev/null @@ -1,129 +0,0 @@ -# Sprinter Commander: conflict-policy F5 0.2/P19 - -Дата: 10 сентября 2026 года. - -Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`. - -## Поведение - -Если конечное имя файла существует, F5 показывает единый choice-dialog: - -- `O` — безопасно заменить только этот файл; -- `S` — пропустить только этот файл; -- `R` — ввести другое DOS 8.3-имя; -- `A` — заменить этот и все следующие конфликтующие файлы текущей группы; -- `N` — пропустить этот и все следующие конфликты текущей группы; -- `Esc` — отменить текущую операцию. - -Policy `all` живёт только до завершения одного вызова F5 и распространяется -на top-level группу и файлы рекурсивной job. Readonly target не заменяется -молча даже после ранее выбранного `overwrite all`: для него снова появляется -диалог `Read-only target`. Групповые `+/-/*` по-прежнему работают только над -файлами; выбор каталогов через `Insert` не менялся. - -`Rename` меняет только имя target, проверяет один компонент DOS 8.3 и не -переименовывает source. Если новое имя тоже занято, policy запрашивается -повторно. - -## Транзакционная замена - -Существующий target не удаляется перед копированием. После полной записи и -закрытия нового temp выполняется последовательность: - -1. выбирается отсутствующее имя `~SBxxxx.BAK` в том же каталоге; -2. readonly временно снимается только после явного разрешения overwrite; -3. старый target переименовывается в backup; -4. новый temp переименовывается в target; -5. новому target назначаются исходные date/time и R/H/S/A; -6. backup удаляется. - -Если шаг commit нового temp не проходит, старый target переименовывается -обратно и его атрибуты восстанавливаются. Заранее существующие backup-имена -пропускаются, а не перезаписываются. Ошибка после необратимого commit -помечается `ScCopyJob.committed`, поэтому UI не сообщает, что target якобы -отсутствует. - -## Проверка - -Отдельная target-проба прошла на реальном DSS-пути: - -```text -PASS default still refuses existing target -PASS overwrite commits through backup -PASS explicit readonly overwrite -PASS pre-existing backup name skipped -PASS replace policy accepts vanished target -PASS distinct destination name -PASS rename failure rolls original target back -PASS EMM restored -ALL PASS -``` - -CHD-check побайтно подтвердил заменённые файлы, rollback исходного payload, -сохранность заранее созданного `~SB0000.BAK` и отсутствие новых backup/temp: - -```text -PASS: transactional overwrite, readonly, collision and rollback. -``` - -Полный UI-сценарий выбрал три файла для `overwrite all`, получил отдельный -readonly prompt, затем применил `skip all` к двум файлам, скопировал -`REN.BIN` как `NEXT.BIN` и отменил конфликт `CANCEL.BIN`. Итоговая проверка: - -```text -PASS: overwrite-all, readonly prompt, skip-all, rename and cancel. -``` - -Контрольные кадры target-пробы: - -- [ALL PASS](../artifacts/mame-p19/overwrite-core/all-pass.png); -- [чистый возврат](../artifacts/mame-p19/overwrite-core/clean-dss-return.png). - -UI policy: - -- [выбор overwrite all](../artifacts/mame-p19/policy/overwrite-choice.png); -- [повторный readonly prompt](../artifacts/mame-p19/policy/readonly-reprompt.png); -- [три файла заменены](../artifacts/mame-p19/policy/overwrite-complete.png); -- [выбор skip all](../artifacts/mame-p19/policy/skip-all-choice.png); -- [оба файла пропущены](../artifacts/mame-p19/policy/skip-complete.png); -- [rename-conflict](../artifacts/mame-p19/policy/rename-choice.png); -- [`NEXT.BIN` создан](../artifacts/mame-p19/policy/renamed.png); -- [отмена конфликта](../artifacts/mame-p19/policy/cancel-choice.png); -- [диалог полностью убран после Esc](../artifacts/mame-p19/policy/cancel-cleared.png); -- [итог пассивной панели](../artifacts/mame-p19/policy/target-result.png); -- [чистый возврат в DSS](../artifacts/mame-p19/policy/clean-dss-return.png). - -В процессе визуальная проверка нашла и устранила stale-dialog: после -одиночных conflict `error/cancel/skip` теперь выполняется полный redraw, а не -обновление только двух нижних строк. - -После итоговой правки повторно пройдены P5 copy-fault matrix и P18 exact FAT -metadata. - -## Компоновка - -EXEC-модуль перенесён из почти полного BANK2 в BANK1. Его строковые статусы -теперь всегда копируются в W2 BSS, поэтому наружу не уходит указатель на -bank-local literal. Это позволило оставить приложение двухбанковым: - -```text -_CODE 9503; DATA 3561; heap 1784 -BANK1 14374/16384; BANK2 15665/16384 -SPRCMD.EXE 43080 -``` - -Межбанковый аудит чист. В BANK1 остаётся 2010 байт, в BANK2 — 719. - -## Воспроизведение - -```sh -make hdd-p19-overwrite ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p19_overwrite.chd \ - tests/mame_p19_overwrite.lua artifacts/mame-p19/core-local 180 -tests/check_p19_overwrite_hdd.sh build/hdd/p19_overwrite.chd - -make hdd-p19-policy ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p19_policy.chd \ - tests/mame_p19_policy.lua artifacts/mame-p19/policy-local 200 -tests/check_p19_policy_hdd.sh build/hdd/p19_policy.chd -``` diff --git a/applications/Volkov/docs/p2-palette-results.md b/applications/Volkov/docs/p2-palette-results.md deleted file mode 100644 index c5bd447..0000000 --- a/applications/Volkov/docs/p2-palette-results.md +++ /dev/null @@ -1,81 +0,0 @@ -# Sprinter Commander: результаты палитровой пробы P2.2 - -Дата: 6 сентября 2026 года. - -Статус: `PASS` в MAME; проверка на реальном Sprinter ожидается. - -Окружение: MAME 0.287 (`b0c4527c`), машина `sprinter`, BIOS `v3.06`. -Исходник пробы находится в `tests/p2_palette/p2palette.c`, кадры — в -[`artifacts/mame-p2/palette/`](../artifacts/mame-p2/palette/). - -## 1. Проверенный контракт - -Проба использует только публичные системные функции -`text_pal_get_color()`/`text_pal_set_color()`: - -- сохраняет шесть применяемых атрибутов во всех четырёх текстовых планах; -- задаёт normal-paper и flash-paper одинаковыми; -- задаёт normal-ink и flash-ink одинаковыми; -- читает обратно все 24 записи и сравнивает каждый RGB-компонент; -- показывает одновременно шесть контрастных цветовых сочетаний; -- перед выходом восстанавливает каждую сохранённую запись. - -Проверенные сочетания: white/blue, yellow/green, white/red, -white/magenta, black/lightgray и black/cyan. Runtime-результат: - -```text -Readback of 6 attrs x 4 planes: PASS (0 mismatches) -``` - -## 2. Стабильность изображения - -Пять кадров одной неизменной сцены сняты в разных моментах. После -декодирования PNG область экрана Sprinter во всех кадрах пиксельно идентична. -Единственное различие между первым и остальными кадрами — 42 пикселя в -прямоугольнике `x=0..13, y=42..44`: это внешний индикатор обращения к -`Drive A` в layout MAME, а не видеовыход Sprinter. Его состояние в экранных -тестах не учитывается. - -Файлы `phase-b.png`–`phase-e.png` идентичны целиком. Их SHA-256: - -```text -5eda33a6289dcd0afbb10573b70bcd701c428d6ed8f2aaae55d16c8d32dbe9cb -``` - -Артефакты: - -- [phase-a.png](../artifacts/mame-p2/palette/phase-a.png) — горит индикатор - Drive A; -- [phase-b.png](../artifacts/mame-p2/palette/phase-b.png), - [phase-c.png](../artifacts/mame-p2/palette/phase-c.png), - [phase-d.png](../artifacts/mame-p2/palette/phase-d.png), - [phase-e.png](../artifacts/mame-p2/palette/phase-e.png) — стабильная сцена; -- [restored-dos.png](../artifacts/mame-p2/palette/restored-dos.png) — возврат - в DSS после восстановления палитры. - -## 3. Исправление прежней интерпретации - -Ранее встроенный предпросмотр двух PNG ошибочно показал исчезновение части -цветных строк. Проверка исходных файлов опровергла это: - -- `partial-flash-phase.png` и `partial-contrast-stable.png` из P1 имеют - идентичные декодированные RGB-пиксели; -- все три исторически названных `flash-before-*.png` из P2.1 также полностью - идентичны; -- текущая P2.2-сцена различается только внешним LED дисковода MAME. - -Следовательно, гипотеза о нестабильных FLASH-планах была артефактом -предпросмотра. Она не является ограничением BIOS, libc, MAME или Commander. - -## 4. Принятое решение - -Многоцветная тема разрешена для Commander. Модуль `sc_theme`: - -- владеет точными RGB смысловых атрибутов; -- сохраняет только затрагиваемые записи всех четырёх планов; -- устанавливает одинаковые normal/FLASH-пары; -- восстанавливает исходные записи при любом штатном cleanup. - -До аппаратного прогона `PASS` относится к MAME 0.287 / BIOS 3.06. На реальном -Sprinter требуется проверить те же цвета и возврат исходной палитры после -выхода. diff --git a/applications/Volkov/docs/p2-screen-backend.md b/applications/Volkov/docs/p2-screen-backend.md deleted file mode 100644 index 522ae87..0000000 --- a/applications/Volkov/docs/p2-screen-backend.md +++ /dev/null @@ -1,270 +0,0 @@ -# Sprinter Commander: экранный backend P2 - -Статус: ядро P2.1 и многоцветная тема P2.2 прошли MAME; аппаратная проверка, -восстановление после child и измерение времени ожидаются. - -Связанные документы: -[архитектура](commander-architecture.md), -[требования](commander-requirements.md), -[roadmap](commander-roadmap.md), -[результаты P1](p1-platform-probes.md), -[результаты P2.1](p2-screen-probe-results.md), -[результаты P2.2](p2-palette-results.md) - -## 1. Решение - -Commander не использует систему оконных дескрипторов BIOS (`WIN_OPEN`, -`WIN_CLOSE`, `WIN_COPY_WIN`, `WIN_RESTORE_WIN`). Текущий BIOS хранит только -один описатель с идентификатором 0; открытие нового окна уничтожает прежнее -состояние. Диалоги, панели и меню поэтому являются прямоугольниками внутри -собственной модели экрана Commander, а не окнами BIOS. - -Целевой backend выводит в координаты полного экрана через системные функции, -но не открывает и не переключает BIOS-окна. Основной пакетный примитив — -ESTEX `WINREST`; для специальных операций доступны координатные `WRCHAR`, -`CLEAR` и `SCROLL`. Прямой доступ к VRAM не используется. - -## 2. Что уже доказано - -P1 и `examples/mdview2` подтвердили формат логического буфера: - -```text -(char, attr), по строкам, stride = width * 2, без padding -``` - -`WINREST` корректно выводит полный экран, отдельную строку и прямоугольник из -ненулевого смещения EMM-страницы. Эта функция ESTEX не создаёт BIOS-окно и не -использует его идентификатор; слово `WIN` здесь означает прямоугольную область -текущего экрана. - -`mdview2` — полезный образец полноэкранного приложения: состояние документа -принадлежит программе, строки материала выводятся из собственного кэша, а -status/menu рисуются координатными примитивами. Commander сохраняет этот -принцип, но выносит способ вывода в отдельный backend. - -## 3. Почему используем системный координатный вывод - -- DSS/BIOS уже знают реальное устройство текстового режима, знакогенератора, - активной screen page и системной палитры. -- `WINREST` выводит сразу прямоугольник из EMM, поэтому не требуется RST на - каждую ячейку. -- Координатные ESTEX-функции не используют идентификатор глобального окна. -- После возврата из child достаточно снова установить 80x32 и вывести всю - экранную модель. -- Один путь работает и в MAME, и на реальном Sprinter без предположений о - внутренней раскладке VRAM. - -## 4. Граница между оконными и безоконными функциями - -Запрещены функции управления глобальными окнами, где передаётся window ID: -`WIN_OPEN`, `WIN_CLOSE`, `WIN_COPY_WIN`, `WIN_RESTORE_WIN`, `WIN_MOVE_WIN`. -Они зависят от единственного поддерживаемого описателя 0. - -Разрешены: - -- ESTEX `WINREST 5Ah` — прямоугольник `(row, col, height, width)`, EMM page - и source address, без window ID; -- ESTEX `WRCHAR 58h`, `CLEAR 56h`, `SCROLL 55h` — абсолютные координаты - активной screen page; -- BIOS `LP_SET_PLACE`/`LP_PRINT_*`/`LP_CLS_WIN*` — только как пакетные - оптимизации для глобального экрана, без открытия новых окон. - -Системные вызовы чтения и записи текстовой палитры также разрешены. Их -отсутствие в P2.1 — способ изолировать экранный backend, а не архитектурный -запрет. Выделенный модуль темы использует их для полного save/set/restore; -контракт подтверждён P2.2. - -Чтобы renderer не зависел от текущего cursor/place, основным остаётся -`WINREST`. Если применяется BIOS `LP_*`, backend всегда сам устанавливает -place непосредственно перед операцией и не оставляет его частью состояния UI. - -## 5. Палитра и FLASH - -У Sprinter атрибут — индекс в четыре текстовых палитровых плана: paper, ink, -flash-paper и flash-ink. Сигнал FLASH переключает пары глобально. Чтобы тема -Commander не мигала, для каждого используемого атрибута normal-paper и -flash-paper задаются одинаково, как и normal-ink с flash-ink. - -Первый pattern P1 с raw-атрибутами `0x11..0x17` действительно содержал -blue-on-blue. Это ошибка выбора атрибутов, а не нестабильность палитры; -арифметическое построение атрибутов для UI поэтому по-прежнему запрещено. - -Прежняя гипотеза о пропадающих цветных строках была вызвана неверным -предпросмотром PNG. Проверка декодированных файлов показала, что именованные -цветные кадры P1 и три диагностических кадра P2.1 пиксельно идентичны. - -P2.2 отдельно проверил шесть цветовых атрибутов: - -- сохранены все 24 исходные записи (6 атрибутов × 4 плана); -- установлены одинаковые normal/FLASH-пары; -- обратное чтение дало `PASS (0 mismatches)`; -- пять временных кадров имеют одинаковый viewport Sprinter; -- перед выходом исходные записи восстановлены. - -Единственное изменение между двумя полными PNG находилось в служебной панели -MAME: 42 пикселя LED `Drive A`. Элементы layout вне viewport Sprinter в -экранных сравнениях не учитываются. Подробности — в -[результатах P2.2](p2-palette-results.md). - -## 6. Интерфейс backend - -Минимальный интерфейс не раскрывает ESTEX/BIOS остальным модулям: - -```c -int sc_video_init(void); -void sc_video_shutdown(void); -int sc_video_present_full(uint8_t emm_page); -int sc_video_present_rect(uint8_t emm_page, uint16_t offset, - uint8_t row, uint8_t col, - uint8_t height, uint8_t width); -int sc_video_scroll_rect(uint8_t row, uint8_t col, - uint8_t height, uint8_t width, - int8_t delta); -``` - -`sc_screen` строит `(char, attr)` в EMM и вызывает backend. Он не вызывает -`WIN_OPEN`, `WRCHAR`, `WINREST` или BIOS непосредственно. Единственная -реализация PoC — `sc_video_system.c`; выбор конкретного системного примитива -остаётся внутри неё. - -## 7. Вертикальный текстовый scroll - -`mdview2` использует эффективную схему для перемещения на одну строку: - -```text -Down: scroll(rect, UP) → нарисовать новую нижнюю строку -Up: scroll(rect, DOWN) → нарисовать новую верхнюю строку -PgUp/PgDn/Home/End → полный redraw области -``` - -Его viewport — `(col=0, row=1, width=80, height=30)`. Вызов libc -`scroll()` обращается к ESTEX `SCROLL 55h`, который сдвигает прямоугольник -активной screen page на одну строку. Он не использует идентификатор окна -BIOS и совместим с решением не применять window descriptors. - -Исходники `mdview2` и контракт ESTEX не доказывают, что операция реализована -аппаратным регистром видеоконтроллера: для документации это **системный -прямоугольный scroll**, пока не появится измерение или исходник DSS. Возможный -глобальный аппаратный сдвиг начала экрана для Commander всё равно неудобен — -он одновременно сдвинет рамки и обе панели. Реализация -`sc_video_scroll_rect()` делегирует прямоугольник ESTEX; это не создаёт -зависимости от BIOS-окон. - -Для Commander эта оптимизация применяется только когда `top` панели -изменился ровно на один. Прямоугольники — внутренности панелей без рамок: - -```text -left: col=1, row=1, width=38, height=27 -right: col=41, row=1, width=38, height=27 -``` - -Точные размеры уточняются renderer-геометрией, но центральный разделитель, -заголовок, footer и пассивная панель не должны входить в scroll rectangle. - -Экранный EMM-буфер остаётся источником истины. Поэтому -`sc_screen_scroll_rows()` сначала сдвигает соответствующие `(char, attr)` в -модели и строит открытую строку, затем вызывает `sc_video_scroll_rect()` и -выводит эту строку. Системный scroll без синхронизации модели запрещён: -после диалога или EXEC полный redraw иначе вернул бы старые строки. - -Fallback при любой неопределённости — present всех 27 строк активной панели. -P2.1 должен проверить четыре границы прямоугольника, оба направления, верх и -низ списка, 1000 последовательных scroll и неизменность соседней панели. - -## 8. Псевдографика и CP866 - -`mdview2` подтверждает стандартный light box-drawing набор CP866/CP437: - -| Имя | Код | Глиф | Имя | Код | Глиф | -|---|---:|:---:|---|---:|:---:| -| `SC_G_H` | `C4` | ─ | `SC_G_V` | `B3` | │ | -| `SC_G_TL` | `DA` | ┌ | `SC_G_TR` | `BF` | ┐ | -| `SC_G_BL` | `C0` | └ | `SC_G_BR` | `D9` | ┘ | -| `SC_G_TM` | `C2` | ┬ | `SC_G_BM` | `C1` | ┴ | -| `SC_G_ML` | `C3` | ├ | `SC_G_MR` | `B4` | ┤ | -| `SC_G_X` | `C5` | ┼ | | | | - -Стрелки для подсказок, если понадобятся: Up=`18`, Down=`19`, Right=`1A`, -Left=`1B`. Это управляющий диапазон ASCII, поэтому такие байты нельзя -передавать через API, интерпретирующий CR/LF/BEL/Esc. Они кладутся как raw -glyph непосредственно в `ScCell`. - -Правила Commander: - -- все коды сосредоточены в `sc_glyphs.h`, чисел `0xB3` по renderer-модулям - быть не должно; -- исходная рамка строится из глифов, а не из UTF-8-литералов; -- строки UI хранятся в CP866 или проходят явную build-time конвертацию; -- border test рисует все 11 соединений и четыре стрелки; -- атрибут каждого глифа задаётся через `COLOR(fg, bg)`; -- при координатном raw-выводе символы `<0x20` считаются глифами, а не - командами. - -Для PoC достаточно одинарных рамок. Двойные рамки VC-класса добавляются -после проверки полного набора соединений, чтобы не смешивать несовместимые -single/double junctions. - -Расширенный набор, уже подтверждённый таблицей конвертации `mdview2`, можно -зарезервировать для прогресса, отметок и будущих тем: - -| Имя | Код | Глиф | Назначение | -|---|---:|:---:|---| -| `SC_G_SHADE_LIGHT` | `B0` | ░ | светлая заливка | -| `SC_G_SHADE_MEDIUM` | `B1` | ▒ | средняя заливка | -| `SC_G_SHADE_DARK` | `B2` | ▓ | тёмная заливка | -| `SC_G_BLOCK` | `DB` | █ | полный блок | -| `SC_G_HALF_UP` | `DF` | ▀ | верхняя половина | -| `SC_G_HALF_DOWN` | `DC` | ▄ | нижняя половина | -| `SC_G_HALF_LEFT` | `DD` | ▌ | левая половина | -| `SC_G_HALF_RIGHT` | `DE` | ▐ | правая половина | -| `SC_G_BULLET` | `F9` | ∙ | маркер | -| `SC_G_CHECK` | `FB` | √ | отметка | -| `SC_G_SQUARE` | `FE` | ■ | квадрат | - -`mdview2_help.c` рисует рамку этими кодами через `wrchar()` и BIOS fill- -функции. Commander заимствует таблицу глифов и геометрию, но обращается к ним -только через `ScCell`/`sc_video`: текущий cursor/place не становится частью -модели интерфейса. - -## 9. Coordinate-screen probe P2.1 - -Отдельный `tests/p2_screen/p2screen.exe` выполнен в MAME 0.287 / BIOS 3.06: - -1. Режим 80x32 и одна EMM-страница экранной модели — `PASS`. -2. Полный экран одним `WINREST` с полной сверкой через `RDCHAR` — `PASS`. -3. Прямоугольник 20x2 из offset `0x2000` — `PASS`. -4. Две рамки и таблица CP866-глифов — `PASS` визуально и по `RDCHAR`. -5. Левая панель вверх и правая панель вниз — `PASS`; рамки и соседние клетки - совпали с моделью. -6. Синхронизация EMM-модели после системного `SCROLL` — `PASS`. -7. 1000 чередующихся однострочных scroll с итоговой полной сверкой — `PASS`. -8. Сохранение W3 вокруг `WINREST`, `RDCHAR` и `SCROLL` — `PASS`. -9. Один системный атрибут стабилен в трёх временных фазах — `PASS`, кадры - пиксельно идентичны. -10. Шесть атрибутов и четыре палитровых плана — `PASS` отдельной пробой P2.2. - -Остались отдельные проверки, не нужные для начала skeleton: - -11. Повторить полную инициализацию после child, изменившего видеорежим. -12. Сравнить время полного, однострочного и scroll-вывода. -13. Повторить принятый сценарий на реальном Sprinter. - -Подробные значения и кадры приведены в -[результатах P2.1](p2-screen-probe-results.md). - -## 10. Критерий выбора - -Системный backend принят для реализации skeleton по результату MAME, потому -что: - -- совпадает с EMM-моделью по символам и атрибутам; -- изолирует палитру в модуле темы и сохраняет отображение W3; -- частичное обновление не затрагивает соседние клетки; -- scroll прямоугольника не двигает рамку и пассивную панель; -- все псевдографические соединения совпадают с CP866; -- базовый системный атрибут стабилен во времени; -- код не обращается к `RGADR`, `RGMOD` и VRAM-банкам напрямую. - -До аппаратного принятия backend остаются прогон на реальном Sprinter и -повторная инициализация после EXEC. Многоцветность подтверждена отдельным -критерием P2.2 и не выводится из результата P2.1. diff --git a/applications/Volkov/docs/p2-screen-probe-results.md b/applications/Volkov/docs/p2-screen-probe-results.md deleted file mode 100644 index 0d29213..0000000 --- a/applications/Volkov/docs/p2-screen-probe-results.md +++ /dev/null @@ -1,81 +0,0 @@ -# Sprinter Commander: результаты экранной пробы P2.1 - -Дата: 6 сентября 2026 года. - -Статус: `PASS` в MAME; проверка на реальном Sprinter ожидается. - -Окружение: MAME 0.287 (`b0c4527c`), машина `sprinter`, BIOS `v3.06`. -Исходники пробы находятся в `tests/p2_screen/`, сохранённые кадры — в -[`artifacts/mame-p2/`](../artifacts/mame-p2/README.md). - -## 1. Проверяемая граница - -`p2screen.exe` проверяет системный координатный backend, а не интерфейс -готового Commander: - -- экранная модель 80x32 хранится в EMM как пары `(char, attr)`; -- прямоугольники выводятся ESTEX `WINREST`; -- фактические клетки читаются ESTEX `RDCHAR`; -- области панелей сдвигаются ESTEX `SCROLL`; -- BIOS window descriptors не открываются; -- к `RGADR`, `RGMOD` и VRAM-банкам программа не обращается; -- палитра в принятом варианте теста не читается и не изменяется. - -Сборка: `--memory big --safe`. - -| Область | Значение | -|---|---:| -| `_CODE` | 4 458 байт (`0x8100..0x926A`) | -| данные | 581 байт (конец `0x94AF`) | -| EXE | 5 004 байта | -| heap после статики | 9 809 байт | -| стек | 1 279 байт | - -## 2. Runtime-результаты - -| Этап | Результат | Артефакт | -|---|---|---| -| Полный `WINREST` 80x32 и полная сверка `RDCHAR` | PASS | [full.png](../artifacts/mame-p2/screen/full.png) | -| Частичный `WINREST` 20x2 из offset `0x2000` | PASS | [partial-offset.png](../artifacts/mame-p2/screen/partial-offset.png) | -| Scroll внутренности левой панели вверх | PASS | [left-scroll-up.png](../artifacts/mame-p2/screen/left-scroll-up.png) | -| Scroll внутренности правой панели вниз | PASS | [right-scroll-down.png](../artifacts/mame-p2/screen/right-scroll-down.png) | -| 1000 scroll, синхронная EMM-модель, итоговая сверка `RDCHAR` и W3 | PASS | [stress-final.png](../artifacts/mame-p2/screen/stress-final.png) | -| Финальная сцена в трёх временных точках | PASS, файлы идентичны | [phase A](../artifacts/mame-p2/screen/stable-phase-a.png), [B](../artifacts/mame-p2/screen/stable-phase-b.png), [C](../artifacts/mame-p2/screen/stable-phase-c.png) | - -SHA-256 каждого из трёх финальных кадров: - -```text -dabeb114f8a7574462aeeb0731363608c1afa940da66d518f734335b36fe8f3b -``` - -Полная сверка после каждого функционального этапа сравнивала все 2560 клеток -экрана с EMM-моделью, включая рамки, пассивную панель и общие строки. Поэтому -`PASS` scroll означает не только появление новой строки, но и отсутствие -побочного сдвига соседней области. - -## 3. Граница палитры и исправление диагностики - -P2.1 намеренно принят с одним системным атрибутом `0x30` и не вызывает -палитровые функции: так координатный backend проверяется отдельно от темы. - -Первичная визуальная интерпретация диагностических PNG ошибочно сообщала, что -в одной временной фазе часть цветных символов исчезает. Проверка самих файлов -показала обратное: три `flash-before-*.png` имеют одинаковые SHA-256 и -одинаковые декодированные RGB-пиксели. Два именованных цветных кадра P1 также -пиксельно идентичны. - -Отдельная проба P2.2 затем подтвердила save/set/readback/restore всех четырёх -планов и стабильную сцену с шестью атрибутами. Подробности и исправление -ошибочной гипотезы приведены в -[результатах P2.2](p2-palette-results.md). - -## 4. Что ещё не проверено - -- повторная инициализация backend после child, сменившего видеорежим; -- измерение времени полного present, одной строки и системного scroll; -- тот же сценарий на реальном Sprinter; -- интеграционный прогон многоцветного `SPRCMD.EXE` после подключения темы. - -Эти пункты не блокируют реализацию экранной модели и skeleton P2. Аппаратный -прогон остаётся gate перед признанием backend полностью платформенно -подтверждённым. Многоцветная тема уже прошла отдельный MAME-gate P2.2. diff --git a/applications/Volkov/docs/p2-skeleton-results.md b/applications/Volkov/docs/p2-skeleton-results.md deleted file mode 100644 index d524927..0000000 --- a/applications/Volkov/docs/p2-skeleton-results.md +++ /dev/null @@ -1,98 +0,0 @@ -# Sprinter Commander: результаты двухпанельного skeleton P2 - -Дата: 6 сентября 2026 года. - -Статус: многоцветный skeleton `PASS` в MAME; файловая система и аппаратная -проверка ожидаются. - -Связанные документы: -[архитектура](commander-architecture.md), -[roadmap](commander-roadmap.md), -[экранный backend](p2-screen-backend.md), -[результаты P2.1](p2-screen-probe-results.md) - -## 1. Реализованная граница - -`build/sprcmd.exe` — первая запускаемая форма Commander, а не очередная -платформенная проба. В ней уже разделены: - -- `ScApp`, инициализация, главный цикл и идемпотентное состояние cleanup; -- три EMM-страницы: левая панель, правая панель и screen model; -- `sc_screen` — единственный владелец модели 80x32 `(char, attr)`; -- `sc_video_system` — `WINREST` и прямоугольный `SCROLL` без BIOS-окон; -- renderer двух панелей, status и строки функциональных клавиш; -- нормализованные `ScKeyEvent` и `ScCommand`; -- навигация по синтетическим P2-записям, включая системный scroll; -- единая модальная поверхность для сообщений и подтверждения выхода. - -Синтетические строки нужны только для проверки cursor/top и scroll. Они не -являются подменой каталога: `ffirst/fnext` skeleton не вызывает, настоящий -источник файлов добавляется в P3. - -## 2. Сборка - -Команда: `make`, конфигурация `--memory big --safe`. - -| Область | Значение | -|---|---:| -| `_CODE` | 7 129 байт (`0x8100..0x9CD9`) | -| данные | 443 байта (конец `0x9E94`) | -| EXE | 7 675 байт | -| heap после статики | 7 276 байт | -| стек | 1 279 байт | - -Банков кода skeleton пока не требует. Контракты уже рассчитаны на `big`, а -холодные функции будут переноситься в W1 по мере появления P3/P4-кода. - -## 3. MAME-сценарий - -Окружение: MAME 0.287 (`b0c4527c`), машина `sprinter`, BIOS `v3.06`. -Сценарий воспроизводится файлом `tests/mame_p2_skeleton.lua`. - -| Действие | Наблюдение | Результат | -|---|---|---| -| Первый экран | две многоцветные панели 80x32, левая `[ACTIVE]`, cursor/top `0/0` | [PASS](../artifacts/mame-p2/skeleton-color/initial-left-active.png) | -| `Tab` | `[ACTIVE]` и `>` перешли на правую панель | [PASS](../artifacts/mame-p2/skeleton-color/tab-right-active.png) | -| `PgDn`, `Down` | правая панель показывает строки 002–028, cursor/top `28/2`; сосед и рамки неподвижны | [PASS](../artifacts/mame-p2/skeleton-color/navigation-scroll.png) | -| `F10` | красный диалог подтверждения поверх модели | [PASS](../artifacts/mame-p2/skeleton-color/quit-dialog.png) | -| `N` | обе панели и тема полностью восстановлены из EMM | [PASS](../artifacts/mame-p2/skeleton-color/quit-cancelled-restored.png) | -| `F1`, `Esc` | общий message dialog открылся и восстановил экран | [PASS](../artifacts/mame-p2/skeleton-color/reserved-command-dialog.png), [после закрытия](../artifacts/mame-p2/skeleton-color/message-closed-restored.png) | -| повторный `F10`, `Enter` | палитра восстановлена, штатный выход в DSS | [PASS](../artifacts/mame-p2/skeleton-color/clean-exit-dos.png) | -| второй запуск после выхода | приложение снова инициализировало EMM и тему | [PASS](../artifacts/mame-p2/skeleton-color/repeat-second-launch.png) | -| второй cleanup | возврат в DSS без сообщения об утечке EMM | [PASS](../artifacts/mame-p2/skeleton-color/repeat-second-clean-exit.png) | - -## 4. Проверка ресурсов - -В начале `sc_app_init()` сохраняет `mem_info.total/free`, затем выделяет один -блок из трёх страниц. `sc_app_cleanup()` освобождает блок, повторяет -`mem_info()` и возвращает ошибку, если оба значения не восстановились. При -ошибке основной модуль печатает отдельное сообщение и возвращает код 4. - -В MAME этого сообщения не было ни после первого, ни после второго процесса. -Второй процесс успешно выделил тот же объём и полностью отрисовался. Таким -образом, проверка включает и внутреннее сравнение счётчиков, и повторный -запуск с чистого состояния. - -## 5. Экран и палитра - -Skeleton не открывает BIOS window descriptors и не обращается к VRAM -напрямую. Панели и диалоги — прямоугольники в собственной EMM-модели. - -Системные BIOS/ESTEX-вызовы палитры **не запрещены**. Первый зафиксированный -skeleton намеренно отображал все semantic attributes системным `0x30`, чтобы -изолировать проверку приложения. После успешной отдельной пробы P2.2 добавлен -`sc_theme`: разные смысловые атрибуты, save/set всех четырёх планов и restore -при cleanup. Renderer и формат экранной модели при этом не изменились. - -Интеграционный MAME-сценарий прошёл с белыми/жёлтыми/светло-голубыми -элементами на синем фоне, чёрным на cyan/lightgray и белыми красными -диалогами. Второй запуск подтвердил повторную инициализацию темы после её -восстановления первым процессом. - -## 6. Следующие работы - -- повторить палитровую пробу P2.2 на реальном Sprinter; -- повторная инициализация `sc_video` после child, сменившего видеорежим; -- измерение full/row/scroll present; -- аппаратный прогон P2.1 и skeleton; -- P3: заменить синтетические строки настоящими независимыми каталогами. diff --git a/applications/Volkov/docs/p3-panel-results.md b/applications/Volkov/docs/p3-panel-results.md deleted file mode 100644 index 0c88c2d..0000000 --- a/applications/Volkov/docs/p3-panel-results.md +++ /dev/null @@ -1,146 +0,0 @@ -# Sprinter Commander: результаты файловых панелей P3 - -Дата: 7 сентября 2026 года. - -Статус: `PASS` в MAME для навигации, независимых панелей, EMM-store на -границе 640/641, сортировки и явной обработки предела каталога DSS 1.71. - -Связанные документы: -[требования](commander-requirements.md), -[архитектура](commander-architecture.md), -[roadmap](commander-roadmap.md), -[большие каталоги DSS](dss-large-directories.md) - -## 1. Проверенная граница - -В P3 синтетические записи заменены настоящим источником `F_FIRST/F_NEXT`. -Каждая панель имеет собственные путь, cursor/top и физическую EMM-страницу. -Запись панели занимает 24 байта, поэтому одна 16-КБ страница вмещает -жёсткий продуктовый предел 640 записей и оставляет 1024 байта запаса. - -Реализованы и проверены: - -- шаблон перечисления `*.*` и имена DOS 8.3; -- синтетическая `..` в некорневом каталоге без дублей `.`/`..` от DSS; -- каталоги перед файлами и сортировка имён без учёта регистра ASCII; -- вход в каталог, возврат через `..`/`Ctrl+PgUp`, refresh и независимость - пассивной панели; -- навигация `Up/Down/PgUp/PgDn/Home/End` без повторного scan; -- частичный результат при ошибке DSS 35 и отдельная виртуальная запись - `>>> MORE...`. - -## 2. Сборка - -Основная команда: `make`. - -Конфигурация: `--memory big --safe --max-allocs 3000`. - -| Область | Значение | -|---|---:| -| `_CODE` | 7728 байт (`0x8100..0x9F30`) | -| данные | 2281 байт (конец `0xA819`) | -| свободная куча W2 | 4839 байт | -| стек | 1279 байт | -| `_BANK1` — scan/sort | 2299 / 16384 байт (14,0%) | -| `_BANK2` — renderer | 2902 / 16384 байт (17,7%) | -| EXE | 41305 байт | - -`check_bank_calls.py` не нашёл прямых вызовов в чужой банк или публикации -указателей на данные банка. - -## 3. Прямая диагностика DSS - -Образ содержит `BIGDIR` из 680 обычных файлов. Исправленная проба `P3DIR` -получила: - -```text -count=512 -errno=35 -511=B0000598.DAT -512=B0000599.DAT -513= -``` - -Это подтверждает границу стабильного DSS и отсутствие продолжения за ней. -Конкретные последние имена отражают физический порядок записей, созданный -`mcopy`, и сами по себе не являются контрактом теста. - -Артефакт: [экран P3DIR](../artifacts/mame-p3/panels/dss-enum-limit.png). - -## 4. Диагностика общего пути Commander - -Проба `P3SCAN` использует те же `sc_source_fs`, `sc_store` и `sc_sort`, что -основная программа. Получен результат: - -```text -scan: rc=0 errno=35 count=512 trunc=1 -first: .. attr=10 flags=01 -last : >>> MORE... attr=00 flags=04 -``` - -Ошибка 35 сознательно остаётся в `errno` как причина усечения, но scan -возвращает успешный частичный снимок. Красный маркер занимает последний -элемент снимка и сортируется после файлов. - -Артефакт: [экран P3SCAN](../artifacts/mame-p3/panels/scan-marker.png). - -## 5. Интеграционный сценарий Commander - -Исторические артефакты ниже сняты в MAME 0.287 с FDD `A:`. Текущий -регрессионный сценарий предельного каталога переведён на HDD `D:`: -`make hdd-stress` создаёт `build/hdd/p3_stress.chd`, а -`tests/mame_p3_stress.lua` сначала переключает DSS на `D:`. FDD-сборщик -`tests/make_p3_stress_floppy.sh` оставлен только для smoke-совместимости. - -| Действие | Подтверждённое наблюдение | Результат | -|---|---|---| -| Корень | обе панели независимо открыли один реальный каталог | [PASS](../artifacts/mame-p3/panels/root-both-panels.png) | -| Вход и `..` | левая панель вошла в `TESTDIR` и вернулась в корень | [PASS](../artifacts/mame-p3/panels/left-testdir-parent.png), [возврат](../artifacts/mame-p3/panels/enter-parent-return.png) | -| `Ctrl+R` | после refresh сохранён выбор `INNER.TXT` | [PASS](../artifacts/mame-p3/panels/refresh-preserves-inner.png) | -| `Tab` и навигация справа | пути и cursor/top панелей не смешиваются | [PASS](../artifacts/mame-p3/panels/independent-panel-state.png) | -| `Ctrl+PgUp` слева | изменена только активная левая панель | [PASS](../artifacts/mame-p3/panels/ctrl-pgup-left-only.png) | -| Вход в `EMPTY` | единственная запись — синтетическая `..`, count=1 | [PASS](../artifacts/mame-p3/panels/empty-parent.png) | -| Вход в `MANY`, `PgDn`, `Down` | cursor/top меняются до 28/2 без rescan, рамки и соседняя панель сохраняются | [PASS](../artifacts/mame-p3/panels/navigation-scroll.png) | -| Вход в `BIGDIR` | показан частичный снимок, count=512 и признак `entries+` | [PASS](../artifacts/mame-p3/panels/bigdir-partial.png) | -| `End` | выбран последний красный `>>> MORE...`, info показывает `` | [PASS](../artifacts/mame-p3/panels/dss-marker-selected.png) | -| `Enter` на маркере | выведено сообщение о недоступном остатке DSS; переход или EXEC не выполнен | [PASS](../artifacts/mame-p3/panels/dss-marker-message.png) | -| `F10` | открыт диалог выхода поверх текущего состояния | [PASS](../artifacts/mame-p3/panels/dss-limit-quit-dialog.png) | -| `Enter` в диалоге | выполнен cleanup и возврат в `A:\>` | [PASS](../artifacts/mame-p3/panels/dss-limit-clean-exit.png) | - -Скриншот фиксирует только состояние изображения в конкретный момент и не -используется в одиночку для вывода о причине возможного дефекта. Подозрение -на ошибку должно дополнительно подтверждаться соседними кадрами, ожидаемым -состоянием сценария и, при необходимости, отдельной диагностической пробой. - -## 6. Синтетическая граница EMM-store - -Проба `tests/p3_store_probe/p3store.c` не вызывает DSS и поэтому достигает -продуктового предела, который нельзя получить из реального каталога DSS -1.71. Сборка выполнена с `--max-allocs 3000`. - -```text -entry=24 capacity=640 bytes=15360 reserve=1024 -write: count=640 first=1 middle=1 last=1 -guard: put640=1 get640=1 tail=1 -cleanup: free_after=222 restored=1 -PASS: store boundary is intact. -``` - -Проверены записи 0, 319 и 639. Запись и чтение индекса 640 отклонены, -байты в начале и конце 1024-байтового резерва не изменились, EMM-страница -освобождена без утечки. - -Артефакт: -[граница 640/641](../artifacts/mame-p3/panels/store-boundary-640-641.png). - -## 7. Итог и следующая граница - -Функциональный gate файловых панелей P3 закрыт в MAME. Аппаратный прогон на -реальном Sprinter ещё нужен, но не блокирует начало P4. - -Следующая работа PoC: - -1. копирование одного файла через отдельную EMM-страницу в W3; -2. безопасный временный файл, обработка ошибок и cleanup; -3. запуск `.EXE` через ESTEX с восстановлением экрана после child; -4. регрессионный MAME-сценарий для копирования, файлов больше 64 КБ и EXEC. diff --git a/applications/Volkov/docs/p4-copy-results.md b/applications/Volkov/docs/p4-copy-results.md deleted file mode 100644 index b9cf986..0000000 --- a/applications/Volkov/docs/p4-copy-results.md +++ /dev/null @@ -1,98 +0,0 @@ -# Sprinter Commander: результаты копирования P4.1 - -Дата: 7 сентября 2026 года. - -Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:` для -однофайлового F5, прогресса, отмены, запрета overwrite и нулевого файла. - -Связанные документы: -[требования](commander-requirements.md), -[архитектура](commander-architecture.md), -[roadmap](commander-roadmap.md), -[носители тестов](test-media.md). - -## Реализация - -- F5 берёт обычный файл активной панели и каталог пассивной панели; -- каталоги и виртуальный маркер DSS явно отклоняются; -- source и уникальный temp открыты одновременно — не более двух fd; -- блок 4096 байт находится только в четвёртой EMM-странице `ScApp` и - временно отображается в W3 функциями `bank_read_page()` и - `bank_write_page()`; -- счётчики размера/позиции 32-битные; -- temp создаётся через `O_CREAT|O_EXCL`, на ошибке или Esc закрывается и - удаляется; -- перед commit конечное имя проверяется повторно; -- commit вызывает DSS `RENAME` двумя basename в каталоге пассивной панели и - восстанавливает глобальный CWD; -- после успеха пассивная панель перечитывается. - -UI-оркестрация и форматирование прогресса вынесены из резидентного W2 в -`sc_copy_ui.c` (BANK2). Полноразмерного статического буфера нет ни в W2, ни в -банке кода. - -## Найденная особенность DSS RENAME - -Первый HDD-прогон перенёс все 131109 байт во временный файл, но завершился -`errno 16` (`EINAME`), а валидатор CHD подтвердил отсутствие результата. -Причина установлена не по одиночному кадру, а по сочетанию: - -1. позднего прогресса `122880/131109`; -2. отдельного итогового кадра с `errno 16`; -3. извлечённого состояния HDD; -4. исходника `DSS/API/Rename.asm`: операция переименовывает записи текущего - каталога, а полный новый путь не является допустимым новым именем. - -Исправление ограничено commit-фазой: job сохраняет CWD, временно входит в -target-dir, передаёт два коротких имени и восстанавливает CWD на всех обычных -путях возврата. - -## Матрица MAME - -Образ создаёт `tests/make_p4_copy_hdd.sh`, сценарий выполняет -`tests/mame_p4_copy_hdd.lua`, итог проверяет `tests/check_p4_copy_hdd.sh`. - -| Сценарий | Результат | -|---|---| -| `LARGE.BIN`, 131109 байт | скопирован, passive refresh показывает файл; содержимое побайтно совпало | -| `EXIST.TXT` | F5 отказал до изменения существующего целевого файла | -| `CANCEL.BIN`, 4194341 байт | виден промежуточный прогресс; Esc удалил temp и не создал target | -| `EMPTY.ZRO`, 0 байт | создан корректный пустой файл и показан passive refresh | -| cleanup/exit | временных `~SC*.TMP` нет; возврат в `D:\>` | - -Артефакты: - -- [успешный файл больше 64 КБ](../artifacts/mame-p4/copy/hdd-copy-success.png); -- [отказ overwrite](../artifacts/mame-p4/copy/hdd-existing-refused.png); -- [прогресс до Esc](../artifacts/mame-p4/copy/hdd-cancel-progress.png); -- [состояние после отмены](../artifacts/mame-p4/copy/hdd-cancel-cleanup.png); -- [успешный нулевой файл](../artifacts/mame-p4/copy/hdd-zero-copy-success.png); -- [чистый выход на HDD](../artifacts/mame-p4/copy/hdd-clean-exit.png); -- [низкоуровневый read/write EMM-page probe](../artifacts/mame-p4/copy/bank-page-io.png). - -Финальный файловый валидатор сообщил: - -```text -PASS: LARGE/EMPTY скопированы; EXIST сохранён; cancel/temp отсутствуют. -``` - -## Размер сборки - -Конфигурация: `--memory big --safe --max-allocs 3000`. - -| Область | Размер | -|---|---:| -| `_CODE` W2 | 8795 байт | -| данные W2 | 3161 байт | -| свободная куча W2 | 2892 байта | -| стек | 1279 байт | -| BANK1: scan/sort/copy job | 4327 / 16384 байт | -| BANK2: draw/copy UI | 4411 / 16384 байт | -| `SPRCMD.EXE` | 42372 байта | - -`check_bank_calls.py` — чисто. Общий `make size-check` — 67 программ без -роста; отдельно отмечается уже существующий новый тест `hello3`, не связанный -с Commander. - -P4.1 закрыт для требований PoC `REQ-COPY-01..12`; запуск `.EXE` и вся -стабилизация PoC продолжены в отчётах P4.2 и P5. diff --git a/applications/Volkov/docs/p4-exec-results.md b/applications/Volkov/docs/p4-exec-results.md deleted file mode 100644 index 22f040b..0000000 --- a/applications/Volkov/docs/p4-exec-results.md +++ /dev/null @@ -1,100 +0,0 @@ -# Sprinter Commander: результаты запуска EXE P4.2 - -Дата: 8 сентября 2026 года. - -Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`. -Выполнены требования `REQ-EXEC-01..06`: запуск по `Enter`, возврат в -Commander после изменившей окружение программы, обработка плохого EXE и отказ -для неподдерживаемого типа файла. - -Связанные документы: -[требования](commander-requirements.md), -[архитектура](commander-architecture.md), -[roadmap](commander-roadmap.md), -[носители тестов](test-media.md). - -## Реализация - -- `Enter` запускает только обычную запись с расширением `.EXE` без учёта - регистра; каталог, маркер предела DSS и иной тип файла не передаются EXEC; -- полный путь строится в резидентной памяти, ESTEX `EXEC $40` вызывается с - `B=1` и синхронно возвращает код завершения в диапазоне `00..FF`; -- перед запуском сохраняются текущий диск и 256-байтный CWD; -- child получает каталог активной панели как CWD, что позволяет программам - без аргументов открывать соседние файлы; затем CWD Commander возвращается; -- одновременный copy job запрещён; пути временно используют уже существующие - строковые поля `ScCopyJob`, поэтому EXEC не добавил два новых буфера в W2; -- после child восстанавливаются диск/CWD, режим 80x32 и восемь записей - палитры Commander во всех четырёх planes; -- повторная установка темы не перезаписывает снимок исходной системной - палитры, который нужен штатному cleanup; -- обе панели перечитываются, затем экран полностью строится из EMM-модели; -- успешный статус показывает код завершения, ошибка загрузчика — имя и - `errno`; Commander остаётся управляемым. - -Низкоуровневая обёртка находится в `sc_platform_exec.c`, а холодная -оркестрация и форматирование статуса — в `sc_exec_ui.c` (BANK2). - -## Интеграционный child - -`tests/p4_exec_child/p4child.exe` намеренно создаёт неблагоприятное для -родителя состояние: - -1. создаёт `EXECLEFT/CREATED.TXT` и `EXECRGHT/PASSIVE.TXT`; -2. оставляет CWD в `EXECLEFT`; -3. переключает экран в 40x32; -4. заменяет используемые Commander цвета ярко-зелёной/пурпурной палитрой; -5. ждёт `Enter` и возвращает код `5A`. - -Имена каталогов теста строго соответствуют 8.3. Промежуточный вариант -`EXECRIGHT` оказался VFAT long name и был виден DSS как `EXECRI~1`; отказ -`chdir("EXECRIGHT")` был ошибкой fixture, а не доказательством дефекта DSS. - -## Матрица MAME - -Образ создаёт `tests/make_p4_exec_hdd.sh`, сценарий выполняет -`tests/mame_p4_exec_hdd.lua`, итоговое содержимое проверяет -`tests/check_p4_exec_hdd.sh`. - -| Сценарий | Результат | -|---|---| -| `P4CHILD.EXE` | child показал `active=0 passive=0` и CWD `\\EXECLEFT` | -| возврат из child | Commander снова в 80x32 с исходной темой; статус содержит `5A` | -| refresh активной панели | появился `CREATED.TXT`, размер 19 | -| refresh пассивной панели | появился `PASSIVE.TXT`, размер 27 | -| `BROKEN.EXE` | показано `Cannot execute BROKEN.EXE (errno 17)`; приложение продолжило работу | -| `NOTE.TXT` | показано имя объекта и явный отказ: поддерживаются только `.EXE` | -| cleanup/exit | диалог работает; системная палитра восстановлена; возврат в `D:\>` | - -После остановки MAME CHD был извлечён и оба marker-файла сравнены побайтно: - -```text -PASS: child создал active/passive markers с ожидаемым содержимым. -``` - -Артефакты: - -- [child после изменения режима, палитры и CWD](../artifacts/mame-p4/exec/child-mode-palette-cwd.png); -- [Commander после восстановления и refresh обеих панелей](../artifacts/mame-p4/exec/commander-restored-both-panels.png); -- [ошибка повреждённого EXE](../artifacts/mame-p4/exec/broken-exe-error.png); -- [отказ для обычного TXT](../artifacts/mame-p4/exec/unsupported-file.png); -- [диалог выхода после EXEC](../artifacts/mame-p4/exec/quit-after-exec.png); -- [чистый возврат в DSS](../artifacts/mame-p4/exec/clean-dss-return.png). - -## Размер сборки - -Конфигурация: `--memory big --safe --max-allocs 3000`. - -| Область | Размер | -|---|---:| -| `_CODE` W2 | 8870 байт | -| данные W2 | 3240 байт | -| свободная куча W2 | 2738 байт | -| стек | 1279 байт | -| BANK1: scan/sort/copy job | 4242 / 16384 байт | -| BANK2: draw/copy/EXEC UI | 5973 / 16384 байт | -| `SPRCMD.EXE` | 42447 байт | - -`check_bank_calls.py` — чисто. Таблица актуализирована по финальной сборке P5 -после исправления режима `O_RDONLY` в libc; P4.1 и P4.2 вместе закрывают -критерий выхода P4. diff --git a/applications/Volkov/docs/p5-stability-results.md b/applications/Volkov/docs/p5-stability-results.md deleted file mode 100644 index 471c5d3..0000000 --- a/applications/Volkov/docs/p5-stability-results.md +++ /dev/null @@ -1,215 +0,0 @@ -# Sprinter Commander: результаты стабилизации P5 - -Дата: 8 сентября 2026 года. - -Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`. -Release gate PoC 0.1 закрыт в эмуляторе: пройдены граничные размеры, серия из -20 копирований, 100 refresh, повторный lifecycle, расширенная матрица ошибок, -смена носителя и запуск реальной внешней программы. Проверка на настоящем -Sprinter остаётся отдельным аппаратным smoke-тестом. - -## 20 copy и граничные размеры - -`tests/p5_fixture.py` создаёт двадцать файлов `F00.BIN..F19.BIN`. В набор -обязательно входят размеры: - -```text -0, 1, 4095, 4096, 65535, 65536, 1048613 байт -``` - -Остальные тринадцать файлов проверяют повторное использование copy job, -временных имён и файловых дескрипторов. Lua-сценарий последовательно вызывает -F5 двадцать раз, не меняя активную панель. После остановки MAME -`tests/check_p5_hdd.sh` извлекает оба дерева и сравнивает каждую пару -побайтно: - -```text -PASS: 20/20 файлов совпали; все граничные размеры и >1 МБ пройдены. -``` - -Первый вариант расписания успел выполнить только 17 команд: кадр показывал -активный прогресс `F16.BIN`, когда Lua уже отпустил следующие клавиши. Это не -трактовалось как ошибка Commander. Финальный сценарий разносит команды с -учётом числа 4-КБ блоков и получает все двадцать результатов на CHD. - -## Refresh и EMM lifecycle - -После copy тот же процесс получает 100 отдельных `Ctrl+R` с интервалом 0,5 -секунды. Затем Commander штатно завершается и ещё дважды запускается и -завершается на том же HDD. - -`sc_app_cleanup()` сравнивает `mem_info()` до выделения и после возврата общего -четырёхстраничного EMM-блока. При несовпадении программа печатает -`SPRCMD cleanup did not restore EMM free pages.` и возвращает код 4. Во всех -трёх контрольных кадрах присутствует только чистый prompt `D:\>`; повторные -инициализации также состоялись. - -Артефакты: - -- [прогресс файла больше 1 МБ](../artifacts/mame-p5/stability/copy-large-progress.png); -- [20 файлов после ста refresh](../artifacts/mame-p5/stability/twenty-copy-hundred-refresh.png); -- [cleanup-цикл 1](../artifacts/mame-p5/stability/cleanup-cycle-1.png); -- [cleanup-цикл 2](../artifacts/mame-p5/stability/cleanup-cycle-2.png); -- [cleanup-цикл 3](../artifacts/mame-p5/stability/cleanup-cycle-3.png). - -## Реальный ENOSPC - -Отдельный образ содержит `SOURCE/FULL.BIN` размером 262181 байт и filler -размером 32850000 байт. До запуска копирования на FAT16 свободно 104448 байт: -temp успевает создаться и частично вырасти, но не может вместить исходник. - -Commander показал `Copy failed (errno 10); temporary file removed.` и остался -управляемым. После выхода валидатор подтвердил: - -```text -PASS: ENOSPC не изменил source/filler и не оставил target/temp. -``` - -То есть проверен именно путь ошибки записи после создания temp, а не ранний -отказ `open()`. - -Артефакты: - -- [начало копирования на почти полном диске](../artifacts/mame-p5/enospc/copy-progress.png); -- [управляемый errno 10 и пустая target-панель](../artifacts/mame-p5/enospc/enospc-cleanup.png); -- [диалог после ошибки](../artifacts/mame-p5/enospc/quit-after-error.png); -- [чистый возврат в DSS](../artifacts/mame-p5/enospc/clean-dss-return.png). - -## Точная граница видимой области 27/28 - -`N27` содержит 26 физических файлов плюс синтетический `..`, а `N28` — 27 -файлов плюс `..`. Первый кадр одновременно показывает `027 entries` и -`028 entries`. После `End` панель N28 имеет `cursor=27`, первая видимая запись -становится `B0000000.TXT`, то есть `top=1`. Для N27 `End` даёт `cursor=26`, -`A0000000.TXT` остаётся первой строкой, то есть `top=0`. - -- [обе точные границы](../artifacts/mame-p5/panel-edges/counts-27-28.png); -- [28-я запись включает прокрутку](../artifacts/mame-p5/panel-edges/count-28-scrolls.png); -- [27 записей помещаются без прокрутки](../artifacts/mame-p5/panel-edges/count-27-fits.png); -- [cleanup после проверки](../artifacts/mame-p5/panel-edges/clean-dss-return.png). - -## Детерминированная матрица отказов copy/EXEC - -`CPERR.EXE` вызывает то же банковое ядро `sc_copy_job.c`, но останавливает его -в точно известных состояниях. Это позволило проверить случаи, которые нельзя -надёжно поймать Lua-клавишей между двумя соседними 4-КБ блоками: - -- отсутствующий EXE возвращает `ENOENT` (`errno 3`); -- отсутствующий source не оставляет уже созданный temp; -- отмена до первого read не создаёт target; -- после записи последнего блока job останавливается в отдельной точке - pre-commit, и отмена удаляет temp; -- принудительная ошибка read удаляет temp; -- temp, повторно открытый с `O_RDONLY`, даёт реальный отказ write - `EROFS` (`errno 8`) и удаляется; -- исчезновение temp непосредственно перед rename даёт управляемую ошибку; -- target, появившийся между begin и commit, сохраняет содержимое `KEEP`; -- после всей матрицы число свободных EMM-страниц полностью восстановлено. - -Проверка выявила ошибку общего libc: исторические Sprinter-флаги имеют -значения `O_WRONLY=1`, `O_RDONLY=2`, `O_RDWR=3`, а `open()` трактовал младшие -биты как POSIX-нумерацию. Исправлены `libc/include/fcntl.h` и `libc/io/open.c`, -добавлен постоянный режимный тест в `tests/openenv`. После пересборки fast и -safe libc матрица прошла целиком, а основной F5-сценарий был повторён. - -Результат внутри CHD проверяется не только по экрану: `RESULT.TXT` обязан -содержать все строки `PASS`, в `TARGET` разрешён только неизменённый -`RACE.BIN`, временные `~SC*.TMP` запрещены. - -- [вся fault-matrix: ALL PASS](../artifacts/mame-p5/copy-faults/fault-matrix-all-pass.png); -- [возврат CPERR в DSS](../artifacts/mame-p5/copy-faults/return-to-dss.png). - -## Смена HDD во время копирования - -Lua физически выгружает только тестовый `hard2` во время первого F5 и через -несколько секунд подключает тот же CHD обратно. На DSS 1.71 активный системный -вызов ожидает возврата устройства; после подключения Commander получает -управляемый `errno 3`, удаляет temp и продолжает принимать команды. Повторный -F5 без перезапуска приложения успешно копирует `DATA.BIN` размером 262181 байт. - -Посмертная проверка CHD подтверждает точное совпадение source/target и -отсутствие temp: - -```text -PASS: HDD removal дал управляемый отказ; после возврата DATA совпал, temp нет. -``` - -- [управляемый отказ первого F5](../artifacts/mame-p5/media-change/managed-error.png); -- [прогресс повторного F5](../artifacts/mame-p5/media-change/retry-progress.png); -- [восстановленный target](../artifacts/mame-p5/media-change/recovered-copy.png); -- [чистый возврат в DSS](../artifacts/mame-p5/media-change/clean-dss-return.png). - -## Реальный viewer и восстановление окружения - -Внешним приложением служит не специальный test child, а полноэкранный -`examples/mdview2`. Commander перед EXEC устанавливает CWD активной панели, -поэтому viewer без передачи аргументов открывает лежащий рядом `README.MD`. -Проверены обычный вид, raw-режим, PageDown, выход и возврат в Commander с -восстановленными режимом, палитрой, CWD и обеими панелями. - -Тест обнаружил в самом `mdview2` утечку scratch-блока EMM. Viewer переведён -на явное владение блоком: результат `mem_alloc_pages()` проверяется, а блок -освобождается в `unload_file()`. После исправления Commander штатно завершает -собственную lifecycle-проверку EMM. - -- [главный экран mdview2](../artifacts/mame-p5/viewer/mdview-main.png); -- [mdview2 после PageDown](../artifacts/mame-p5/viewer/mdview-pagedown.png); -- [Commander после возврата](../artifacts/mame-p5/viewer/commander-restored.png); -- [чистый возврат в DSS](../artifacts/mame-p5/viewer/clean-dss-return.png). - -## Финальный размер PoC - -Конфигурация: `--memory big --safe --max-allocs 3000`. - -| Область | Размер | -|---|---:| -| `_CODE` W2 | 8870 байт | -| данные W2 | 3240 байт | -| свободная куча W2 | 2738 байт | -| стек | 1279 байт | -| BANK1: scan/sort/copy job | 4242 / 16384 байт | -| BANK2: draw/copy/EXEC UI | 5973 / 16384 байт | -| `SPRCMD.EXE` | 42447 байт | - -Проверка межбанковых вызовов чистая. Общий `make size-check` ожидаемо сообщает -рост `openenv` на 188 байт: это не скрытый рост libc, а добавленный постоянный -тест `write()` через `O_RDONLY` вместе с исправленной веткой mode mapping. -Эталон размеров автоматически не обновлялся, поскольку в общем дереве также -присутствует посторонний новый `hello3`. - -## Команды воспроизведения - -```sh -make hdd-p5 ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p5_stability.chd \ - tests/mame_p5_stability.lua artifacts/mame-p5/local 205 -tests/check_p5_hdd.sh build/hdd/p5_stability.chd - -make hdd-p5-enospc ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p5_enospc.chd \ - tests/mame_p5_enospc.lua artifacts/mame-p5/enospc-local 55 -tests/check_p5_enospc_hdd.sh build/hdd/p5_enospc.chd - -make hdd-p5-panels ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p5_panel_edges.chd \ - tests/mame_p5_panel_edges.lua artifacts/mame-p5/panels-local 50 - -make hdd-p5-copy-faults ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p5_copy_faults.chd \ - tests/mame_p5_copy_faults.lua artifacts/mame-p5/faults-local 180 -tests/check_p5_copy_faults_hdd.sh build/hdd/p5_copy_faults.chd - -make hdd-p5-viewer ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p5_viewer.chd \ - tests/mame_p5_viewer.lua artifacts/mame-p5/viewer-local 65 - -make hdd-p5-media ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p5_media.chd \ - tests/mame_p5_media_change.lua artifacts/mame-p5/media-local 50 -tests/check_p5_media_hdd.sh build/hdd/p5_media.chd -``` - -Перед каждым запуском должен отсутствовать другой процесс MAME Sprinter. -`run_mame_hdd.sh` проверяет это сам. Единственный незакрытый пункт за пределами -эмуляторного release gate — повторить короткий smoke-сценарий на реальном -Sprinter Sp2000. diff --git a/applications/Volkov/docs/p6-sort-results.md b/applications/Volkov/docs/p6-sort-results.md deleted file mode 100644 index 7e9ed49..0000000 --- a/applications/Volkov/docs/p6-sort-results.md +++ /dev/null @@ -1,98 +0,0 @@ -# Sprinter Commander: сортировки 0.2/P6 - -Дата: 8 сентября 2026 года. - -Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`. - -## Реализация - -Каждая `ScPanel` хранит собственные `sort_key` и `sort_descending`. Доступны -четыре ключа: - -| Комбинация | Ключ | -|---|---| -| `Ctrl+F3` | имя | -| `Ctrl+F4` | расширение | -| `Ctrl+F5` | дата и время last-write | -| `Ctrl+F6` | размер | - -Выбор другого ключа включает прямой порядок; повтор текущей комбинации меняет -направление. На нижней рамке каждой панели постоянно виден режим `N/E/D/S` и -знак `+`/`-`. Refresh и переходы в каталог используют сохранённый режим этой -же панели. - -Порядок групп не зависит от reverse: синтетический `..` всегда первый, -затем идут каталоги, затем обычные файлы, а красный маркер ограничения DSS -остаётся последним. Направление применяется только внутри групп. При равном -основном ключе детерминированным вторичным ключом служит имя без учёта регистра. -Дата сравнивается как FAT date, затем FAT time; отдельная creation date не -имитируется, потому что текущий `F_FIRST/F_NEXT` возвращает last-write. - -Shell sort по-прежнему работает непосредственно с 24-байтовыми записями EMM -через `sc_store_get/put` и не сохраняет указатели W3. Выбранная запись после -пересортировки находится по имени; cursor/top нормализуются без повторного -сканирования каталога. - -## Автоматическая проверка - -`SORTRT.EXE` заполняет две разные EMM-страницы намеренно перемешанными -записями и пишет `SORTRES.TXT`. Посмертный валидатор подтвердил: - -```text -PASS: 4 sort keys, reverse, fixed groups, selection, two panels. -``` - -Проверяются восемь точных порядков, неизменность `..` и `>>> MORE...`, -сохранение `BETA.TXT` под курсором и отсутствие влияния режима первой панели -на вторую. - -Затем тот же MAME-сценарий запускает Commander, открывает `SORTL` и `SORTR`, -подаёт реальные `Ctrl+F3..F6`, переключает активную панель и делает refresh. -Два полных UI-прогона дали побайтно одинаковые контрольные кадры. - -- [name ascending](../artifacts/mame-p6/sort/name-ascending.png); -- [extension ascending](../artifacts/mame-p6/sort/extension-ascending.png); -- [extension descending](../artifacts/mame-p6/sort/extension-descending.png); -- [size ascending](../artifacts/mame-p6/sort/size-ascending.png); -- [size descending](../artifacts/mame-p6/sort/size-descending.png); -- [date ascending](../artifacts/mame-p6/sort/date-ascending.png); -- [date descending](../artifacts/mame-p6/sort/date-descending.png); -- [left `S-`, right `E+`](../artifacts/mame-p6/sort/independent-panels.png); -- [refresh сохранил `E+`](../artifacts/mame-p6/sort/refresh-keeps-mode.png); -- [штатный cleanup](../artifacts/mame-p6/sort/clean-dss-return.png). - -В текущем общем HDD-builder файлы fixture получают одинаковую дату при -копировании дерева, поэтому точный порядок date проверяет target-проба; -UI-сценарий независимо подтверждает доставку `Ctrl+F5`, оба состояния `D+/-` -и обратный redraw. Этот предел fixture не выдаётся за проверку разных FAT-дат -по скриншоту. - -## Размер - -Конфигурация `--memory big --safe --max-allocs 3000`: - -| Область | P5 | После сортировок | Изменение | -|---|---:|---:|---:| -| `_CODE` W2 | 8870 | 9125 | +255 | -| данные W2 | 3240 | 3244 | +4 | -| свободная куча W2 | 2738 | 2479 | -259 | -| BANK1 | 4242 | 5137 | +895 | -| BANK2 | 5973 | 6053 | +80 | -| `SPRCMD.EXE` | 42447 | 42702 | +255 | - -Рост BANK1 содержит компараторы и сохранение выбора, W2 — команды и один -банковый вызов. Первоначальный вариант с восемью статусными строками давал -рост W2 на 736 байт и был сокращён до общего статуса плюс компактный индикатор -на рамке. Межбанковый аудит финальной сборки чистый. - -## Воспроизведение - -```sh -make hdd-p6-sort ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p6_sort.chd \ - tests/mame_p6_sort.lua artifacts/mame-p6/local 90 -tests/check_p6_sort_hdd.sh build/hdd/p6_sort.chd -``` - -Перед запуском требуется убедиться, что другого MAME Sprinter нет; -`run_mame_hdd.sh` выполняет эту проверку. diff --git a/applications/Volkov/docs/p7-selection-results.md b/applications/Volkov/docs/p7-selection-results.md deleted file mode 100644 index d77ef51..0000000 --- a/applications/Volkov/docs/p7-selection-results.md +++ /dev/null @@ -1,73 +0,0 @@ -# Sprinter Commander: выбор записей 0.2/P7 - -Дата: 8 сентября 2026 года. - -Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`. - -## Контракт - -- `Insert` меняет выбор текущей обычной записи и передвигает cursor вниз; -- `*` инвертирует выбор только файлов текущей панели; -- файлы и каталоги можно выбирать, синтетический `..` и виртуальный маркер - DSS нельзя; -- групповые `+`, `-` и `*` игнорируют каталоги и не снимают выбор, - ранее поставленный с каталога через `Insert`; -- выбранная запись отмечается `*` слева от имени; -- footer активной панели показывает ` bytes in selected`, а - общая строка информации пока сохраняет компактное `sel размер/количество`; -- размер каталога в этой статистике равен нулю: рекурсивный подсчёт появится - вместе с групповой job; -- сортировка переносит `SC_ENTRY_SELECTED` вместе с записью и сохраняет - текущий файл под cursor; -- refresh текущего среза явно очищает выбор; режим сортировки сохраняется; -- панели имеют независимые EMM-страницы и независимую статистику. - -Функции выбора находятся в BANK1 и работают только через -`sc_store_get/put`; указатель на W3 не сохраняется. Для toggle и invert -используется один банковый entry point, чтобы не плодить resident thunk’и. - -## Проверка - -Target-проба `SELRT.EXE` проверяет Insert двух файлов, точную сумму 30, -отказы для `..`/DSS-маркера, две инверсии, выбор каталога, сброс статистики -через `sc_store_clear()` и возврат EMM. Результат извлекается из CHD: - -```text -PASS: Insert/invert/masks, virtual guards, totals, refresh и EMM. -``` - -UI-сценарий выбирает два каталога и `A.BIN`, меняет порядок на `N-`, -инвертирует выбор и перечитывает каталог. Контрольные состояния: - -- [виртуальная запись не выбирается](../artifacts/mame-p7/select/virtual-entry-guard.png); -- [три записи, `sel 10/003`](../artifacts/mame-p7/select/insert-three.png); -- [сортировка сохранила flags и cursor](../artifacts/mame-p7/select/sort-keeps-selection.png); -- [инверсия файлов не изменила ранее выбранные каталоги, `sel 50/004`](../artifacts/mame-p7/select/inverted.png); -- [refresh очистил выбор и сохранил `N-`](../artifacts/mame-p7/select/refresh-clears.png); -- [штатный возврат в DSS](../artifacts/mame-p7/select/clean-dss-return.png). - -## Размер - -Конфигурация `--memory big --safe --max-allocs 3000`: - -| Область | После P6 | После P7 | Изменение | -|---|---:|---:|---:| -| `_CODE` W2 | 9125 | 9434 | +309 | -| данные W2 | 3244 | 3256 | +12 | -| свободная куча W2 | 2479 | 2158 | -321 | -| BANK1 | 5137 | 6033 | +896 | -| BANK2 | 6053 | 6213 | +160 | -| `SPRCMD.EXE` | 42702 | 43011 | +309 | - -Первый вариант имел два банковых entry point и оставлял 2069 байт heap. -После объединения и сокращения резидентных сообщений финальная сборка -возвращает 2158 байт. Межбанковый аудит чистый. - -## Воспроизведение - -```sh -make hdd-p7-select ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p7_select.chd \ - tests/mame_p7_select.lua artifacts/mame-p7/local 65 -tests/check_p7_select_hdd.sh build/hdd/p7_select.chd -``` diff --git a/applications/Volkov/docs/p8-mask-results.md b/applications/Volkov/docs/p8-mask-results.md deleted file mode 100644 index b9f020d..0000000 --- a/applications/Volkov/docs/p8-mask-results.md +++ /dev/null @@ -1,69 +0,0 @@ -# Sprinter Commander: выбор по маске 0.2/P8 - -Дата: 8 сентября 2026 года. - -Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`. - -## Реализация - -Обычный `+` открывает `Select by mask`, обычный `-` — `Unselect by mask`. -Начальное значение `*.*`; Enter применяет действие, Esc отменяет его без -изменения панели. Доступны wildcard `*` и `?`, сравнение ASCII-букв не зависит -от регистра. Как в DOS, `*.*` означает все имена файлов, включая имена без точки. -Все групповые операции `+`, `-`, `*` игнорируют каталоги; синтетический `..` и виртуальный -маркер DSS также не участвуют. Выбор, поставленный на каталог отдельным `Insert`, не меняется. - -Модальный редактор не использует консольные окна или прямой VRAM. Диалог и -поле находятся в общей EMM-модели Commander и выводятся координатными -функциями. Реализованы insertion в позиции cursor, Backspace, Delete, -Left/Right, Home/End, Enter и Esc. Для текущих DSS 8.3-масок ввод ограничен -12 символами; тот же editor будет переиспользован командами F7/F6 и -командной строкой. - -Существующий `sc_dialog.c` вместе с новым editor перенесён из HOME в BANK2. -Mask matcher находится в BANK1 рядом с выбором; приложение передаёт ему -резидентный scratch-буфер copy job, пока файловая job не активна. - -## Проверка - -Target-часть проверяет: - -- `*.bin` в нижнем регистре против имён верхнего регистра; -- `?.b?n`; -- `*.*` для файла без точки и неизменность каталога; -- последовательное set/clear с точными count/size; -- неизменность `..` и DSS-маркера. - -UI-сценарий редактирует `*.*` в `*.bin`, получает три выбранных файла и -`sel 60/003`, затем снимает `b.bin`, получая `sel 40/002`. Следующий диалог -закрывается Esc, состояние не меняется. - -- [диалог с `*.*`](../artifacts/mame-p8/masks/default-dialog.png); -- [поле после редактирования в `*.bin`](../artifacts/mame-p8/masks/edited-bin-mask.png); -- [три BIN выбраны](../artifacts/mame-p8/masks/bin-selected.png); -- [`b.bin` снят](../artifacts/mame-p8/masks/b-unselected.png); -- [Esc сохранил выбор](../artifacts/mame-p8/masks/cancel-preserves.png); -- [штатный cleanup](../artifacts/mame-p8/masks/clean-dss-return.png). - -## Размер - -| Область | После P7 | После P8 | Изменение | -|---|---:|---:|---:| -| `_CODE` W2 | 9434 | 9416 | -18 | -| данные W2 | 3256 | 3256 | 0 | -| свободная куча W2 | 2158 | 2176 | +18 | -| BANK1 | 6033 | 6451 | +418 | -| BANK2 | 6213 | 7268 | +1055 | -| `SPRCMD.EXE` | 43011 | 42993 | -18 | - -Несмотря на новую функцию, HOME стал меньше благодаря переносу диалогов. -Оба банка имеют более 9 КБ резерва; межбанковый аудит чистый. - -## Воспроизведение - -```sh -make hdd-p8-masks ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p7_select.chd \ - tests/mame_p7_select.lua artifacts/mame-p8/local 80 -tests/check_p7_select_hdd.sh build/hdd/p7_select.chd -``` diff --git a/applications/Volkov/docs/p9-mkdir-results.md b/applications/Volkov/docs/p9-mkdir-results.md deleted file mode 100644 index 65f4248..0000000 --- a/applications/Volkov/docs/p9-mkdir-results.md +++ /dev/null @@ -1,71 +0,0 @@ -# Sprinter Commander: создание каталога 0.2/P9 - -Дата: 8 сентября 2026 года. - -Статус: `PASS` в MAME 0.288 / BIOS 3.06 / DSS 1.71.57 на HDD `D:`. - -## Контракт - -- `F7` открывает общий координатный редактор и принимает один компонент - DOS 8.3 длиной до 12 символов; -- разрешены база до 8 и расширение до 3 символов, одна необязательная точка, - ASCII-буквы/цифры, допустимая DOS-пунктуация и CP866-байты `80h..FFh`; -- пустое имя, `.`/`..`, разделители пути, пробелы, wildcard, лишние точки и - превышение 8.3 отвергаются до вызова DSS; -- полный путь строится в уже принадлежащем `ScApp` scratch-буфере; -- `mkdir()` получает полный путь и не меняет глобальный CWD DSS; -- после успеха перечитывается только активная панель; -- существующий каталог и невалидное имя дают управляемое сообщение, `Esc` - закрывает редактор без файловой операции. - -Валидатор вынесен в отдельный банковый модуль `sc_name.c`. Сам Commander -размещает редактор, оркестрацию F7 и валидатор в BANK2; HOME/W2 содержит -только dispatch и банковые трамплины. - -## Проверка - -Target-проба `MKRT.EXE` проверяет допустимые и запрещённые имена, построение -полного пути, реальный цикл `mkdir`/duplicate/`rmdir` и неизменность CWD. -После MAME дерево извлекается из CHD: внешний checker подтверждает созданный -`MKTEST/NEWDIR`, неизменный `KEEP.TXT` и отсутствие пробных каталогов. - -```text -PASS: validator, mkdir/error/rmdir/CWD и F7 NEWDIR. -``` - -UI-сценарий отдельно подтверждает: - -- [пустой редактор F7](../artifacts/mame-p9/mkdir/dialog-empty.png); -- [создание и refresh активной панели](../artifacts/mame-p9/mkdir/created.png); -- [управляемую ошибку повторного имени](../artifacts/mame-p9/mkdir/duplicate-error.png); -- [отказ для имени длиннее 8.3](../artifacts/mame-p9/mkdir/invalid-name.png); -- [вход в созданный каталог](../artifacts/mame-p9/mkdir/entered-newdir.png); -- [штатный возврат в DSS](../artifacts/mame-p9/mkdir/clean-dss-return.png). - -## Размер - -Конфигурация `--memory big --safe --max-allocs 3000`: - -| Область | После P8 | После P9 и уточнения group select | Изменение | -|---|---:|---:|---:| -| `_CODE` W2 | 9416 | 9449 | +33 | -| данные W2 | 3256 | 3256 | 0 | -| свободная куча W2 | 2176 | 2143 | -33 | -| BANK1 | 6451 | 6477 | +26 | -| BANK2 | 7268 | 8156 | +888 | -| `SPRCMD.EXE` | 42993 | 43026 | +33 | - -Оба банка сохраняют более 8 КБ резерва. Межбанковый аудит чист. - -Общий `size-check` по всему C-Compiler пока ожидаемо отмечает посторонний -новый `hello3` и намеренный рост `openenv` после постоянной проверки -`O_RDONLY`; эталон размеров из Commander не обновлялся. - -## Воспроизведение - -```sh -make hdd-p9-mkdir ALLOCS=3000 -tests/run_mame_hdd.sh build/hdd/p9_mkdir.chd \ - tests/mame_p9_mkdir.lua artifacts/mame-p9/local 70 -tests/check_p9_mkdir_hdd.sh build/hdd/p9_mkdir.chd -``` diff --git a/applications/Volkov/docs/poc-0.1-release.md b/applications/Volkov/docs/poc-0.1-release.md deleted file mode 100644 index d49c9f3..0000000 --- a/applications/Volkov/docs/poc-0.1-release.md +++ /dev/null @@ -1,85 +0,0 @@ -# Sprinter Commander PoC 0.1 - -Дата: 8 сентября 2026 года. - -Статус: техническая архитектура подтверждена в MAME 0.288 / BIOS 3.06 / -DSS 1.71.57. `SPRCMD.EXE` является proof of concept, а не готовым файловым -менеджером для повседневной работы. Аппаратный smoke-тест на Sprinter Sp2000 -ещё требуется. - -## Что уже работает - -- полноэкранный двухпанельный интерфейс 80x32 через безоконные координатные - функции BIOS/ESTEX, без прямой записи в VRAM; -- независимые пути и навигация обеих панелей, синтетический `..`, Home/End, - PgUp/PgDn, Tab и refresh; -- одна EMM-страница на панель, максимум 640 записей без заполнения страницы - целиком, красный `>>> MORE...` при известном ограничении DSS; -- каталоги перед файлами и сортировка по имени; -- безопасное копирование одного файла по F5 через отдельную EMM-страницу W3, - 4-КБ блоками и temp/rename commit; -- отмена по Esc между блоками и в отдельной точке перед commit; -- запрет перезаписи уже существующего target; -- запуск `.EXE` по Enter с временным CWD активной панели и восстановлением - экрана, палитры, CWD и панелей; -- штатное освобождение EMM, закрытие fd и восстановление системного экрана - при выходе. - -## Сборка и запуск - -Из каталога приложения: - -```sh -make ALLOCS=3000 -make hdd -``` - -Результат — `build/sprcmd.exe`; образ для быстрого интеграционного запуска — -`build/hdd/sprcmd.chd`. Для MAME штатно используются системный HDD как `C:` и -отдельный тестовый `hard2` как `D:`. Одновременно разрешён только один процесс -MAME Sprinter. - -Полный перечень автоматизированных образов и команд находится в -[test-media.md](test-media.md), доказательства P5 — в -[p5-stability-results.md](p5-stability-results.md). - -Финальная конфигурация PoC: `--memory big --safe --max-allocs 3000`; -`SPRCMD.EXE` — 42447 байт, `_CODE` W2 — 8870 байт, свободная куча W2 — -2738 байт, BANK1 — 4242 байта, BANK2 — 5973 байта. - -## Клавиши PoC - -| Клавиша | Действие | -|---|---| -| стрелки, Home/End, PgUp/PgDn | перемещение по активной панели | -| `Tab` | сменить активную панель | -| `Enter` | войти в каталог/`..` или запустить `.EXE` | -| `Ctrl+R` | перечитать активную панель | -| `F5` | скопировать текущий обычный файл в пассивную панель | -| `Esc` | отменить активное копирование | -| `F10` | подтверждение выхода | - -Надписи F6/F7/F8 и меню пока являются зарезервированными командами следующих -этапов, а не обещанием выполненной операции. - -## Известные ограничения - -- в панели показывается не более 640 записей; многостраничный режим относится - только к 2.0+; -- стабильный DSS 1.71 может остановить физическое перечисление около 512-й - FAT-записи с ошибкой 35 — показывается информирующий красный маркер; -- сортировка PoC только по имени; ext/size/date и обратный порядок входят в - следующую функциональную фазу; -- нет выбора нескольких файлов, масок, очереди и рекурсивных операций; -- F6 move/rename, F7 mkdir, F8 delete и overwrite policy ещё не реализованы; -- нет командной строки, `Ctrl+O`, истории, associations и передачи аргументов - внешней программе; -- нет конфигурационного файла цветов, viewer/editor вызываются только как - обычные внешние `.EXE`; -- нет длинных имён: DSS-контракт текущего слоя — 8.3; -- floppy поддерживается лишь как медленный smoke-носитель; основная матрица - выполнялась на HDD `D:`; -- проверка на реальном Sprinter пока не выполнена. - -Эти ограничения намеренно отделяют доказанную платформенную основу PoC от -безопасного файлового ядра 0.2 и полного набора функций версии 1.0. diff --git a/applications/Volkov/docs/test-media.md b/applications/Volkov/docs/test-media.md deleted file mode 100644 index 869e575..0000000 --- a/applications/Volkov/docs/test-media.md +++ /dev/null @@ -1,90 +0,0 @@ -# Носители для тестов Commander - -Дата: 8 сентября 2026 года. - -## Решение - -Стандартный носитель интеграционных и нагрузочных тестов Commander — -отдельный FAT16 HDD-образ, подключаемый к MAME как `hard2`. DSS видит его как -диск `D:`; системный `hard1` остаётся диском `C:`. - -MAME качественно эмулирует механические задержки FDD. Это полезно для проверки -совместимости, но делает повторяющиеся scan/copy-прогоны на порядок медленнее и -не даёт пользы большинству функциональных тестов. Поэтому FDD `A:` сохраняется -только для отдельных smoke-тестов чтения/запуска с дискеты. - -## Образы - -Все рабочие образы Commander лежат локально в `build/hdd/` и не подменяют -общий `mame/v306/IMG/test_hdd.chd`: - -```sh -make hdd # только SPRCMD.EXE -> build/hdd/sprcmd.chd -make hdd-stress # P3: EMPTY/MANY/BIGDIR -> build/hdd/p3_stress.chd -make hdd-copy # P4: COPYFROM/COPYTO -> build/hdd/p4_copy.chd -make hdd-exec # P4: EXECLEFT/EXECRGHT -> build/hdd/p4_exec.chd -make hdd-p5 # P5: 20 copy + resource cycles -> p5_stability.chd -make hdd-p5-enospc # P5: почти полный HDD -> p5_enospc.chd -make hdd-p5-panels # P5: 27/28 записей -> p5_panel_edges.chd -make hdd-p5-copy-faults # P5: copy/EXEC fault matrix -> p5_copy_faults.chd -make hdd-p5-viewer # P5: SPRCMD + реальный mdview2 -> p5_viewer.chd -make hdd-p5-media # P5: hot-unplug hard2 во время F5 -> p5_media.chd -make hdd-p6-sort # 0.2: target/UI сортировки -> p6_sort.chd -make hdd-p7-select # 0.2: target/UI выбора -> p7_select.chd -make hdd-p8-masks # 0.2: расширение того же образа масками +/− -make hdd-p9-mkdir # 0.2: validator и UI F7 mkdir -> p9_mkdir.chd -make hdd-p10-rename # 0.2: file/dir rename и UI F6 -> p10_rename.chd -make hdd-p11-delete # 0.2: confirm/cancel и UI F8 -> p11_delete.chd -make hdd-p12-group-copy # 0.2: выбранные файлы -> p12_group_copy.chd -make hdd-p13-group-delete # 0.2: выбранные файлы -> p13_group_delete.chd -make hdd-p14-default-colors # UI: dir/EXE/file -> p14_default_colors.chd -make hdd-p15-tree-copy # 0.2: recursive current dir -> p15_tree_copy.chd -make hdd-p15-tree-cancel # 0.2: cancel/cleanup -> p15_tree_cancel.chd -make hdd-p15-tree-self # 0.2: self-target guard -> p15_tree_self.chd -make hdd-p16-group-tree-copy # 0.2: multi-root F5 -> p16_group_tree_copy.chd -make hdd-p17-tree-delete # 0.2: recursive group F8 -> p17_tree_delete.chd -make hdd-p17-tree-delete-cancel # runtime Esc -> p17_tree_delete_cancel.chd -make hdd-p18-metadata # exact FAT metadata F5 -> p18_metadata.chd -make hdd-p19-overwrite # backup/rollback probe -> p19_overwrite.chd -make hdd-p19-policy # UI conflict policy -> p19_policy.chd -``` - -Сборщик `toolchain/make_hdd.sh` принимает как отдельные файлы, так и готовые -деревья каталогов. Рекурсивное копирование дерева одной командой особенно -важно для `BIGDIR`: 680 файлов не требуют 680 отдельных запусков `mcopy`. - -## Запуск и проверка - -Lua-сценарий сначала вводит `D:`, а затем запускает `SPRCMD.EXE`. Благодаря -этому текущий диск DSS и оба относительных контекста панелей находятся на -тестовом HDD, а не на системном `C:` или медленном `A:`. - -Пример полного теста F5: - -```sh -make hdd-copy ALLOCS=3000 -tests/run_mame_hdd.sh \ - build/hdd/p4_copy.chd tests/mame_p4_copy_hdd.lua \ - artifacts/mame-p4/copy-hdd-local 75 -tests/check_p4_copy_hdd.sh build/hdd/p4_copy.chd -``` - -После пересборки CHD MAME требуется полностью перезапустить. Нельзя извлекать -или проверять образ во время работающего MAME: окончательное состояние -валидируется только после чистого выхода эмулятора. - -Скриншоты подтверждают состояние интерфейса, но не заменяют файловую -проверку. Для copy-теста после MAME CHD преобразуется обратно в RAW, файлы -извлекаются через mtools и сравниваются с детерминированными шаблонами. - -Для EXEC-теста аналогичная тройка команд использует -`tests/mame_p4_exec_hdd.lua` и `tests/check_p4_exec_hdd.sh`. Все имена, -которые должен открыть DSS, задаются в формате 8.3: VFAT long name на образе -не является доступным DSS-именем и может отображаться как alias с `~1`. - -Сценарий смены носителя управляет только тестовым `hard2`. Перед запуском -любой автоматизации проверяется, что другого экземпляра MAME Sprinter нет: -одновременный доступ двух процессов к CHD запрещён. После физического unload -DSS может ожидать возврата устройства внутри системного вызова; Lua возвращает -тот же образ, после чего Commander получает управляемую ошибку и продолжает -работу. diff --git a/applications/Volkov/docs/ui-reference.md b/applications/Volkov/docs/ui-reference.md deleted file mode 100644 index 82c49d4..0000000 --- a/applications/Volkov/docs/ui-reference.md +++ /dev/null @@ -1,53 +0,0 @@ -# Sprinter Commander: направление развития UI - -Дата: 8 сентября 2026 года. - -## Референс - -Volkov Commander используется как поведенческий и композиционный образец, -но интерфейс не переносится побайтно. Цель — узнаваемая двухпанельная модель -VC, адаптированная к текстовому экрану Sprinter 80x32 и к координатному -BIOS/ESTEX backend без консольных окон и прямого доступа к VRAM. - -По предоставленным контрольным экранам фиксируются следующие ориентиры: - -- независимый режим панели `Brief` с несколькими колонками коротких имён; -- режим `Details` с колонками `Name`, `Size`, `Date`, `Time`; -- строка итога выбора внизу соответствующей панели вида - `N bytes in M selected files`; -- верхняя полоса меню, открываемая `F9`, с разделами панелей, файлов, - команд и настроек; -- однострочный prompt под панелями и полоса функциональных клавиш внизу; -- путь активной панели должен читаться как единая строка и визуально - отличаться от содержимого панели; -- активность панели определяется cursor/title, а не лишними служебными - словами внутри рамки. - -## Поэтапное сближение - -1. В 0.2 сохранить устойчивую текущую геометрию, но заменить диагностическую - статистику выбора пользовательской строкой итога и привести подписи - F-клавиш к реально работающим командам. Строка итога в footer панели уже - реализована и подтверждена - [кадром MAME](../artifacts/mame-ui/selected-footer-clean.png); - окончательная геометрия появится вместе с режимами панелей. -2. После безопасных F6/F7/F8 добавить модель режима панели и `Details` с - заголовками колонок; затем `Brief` с расчётом числа колонок из ширины. -3. В 0.4 добавить верхнее меню `F9`, настоящую командную строку и - полноэкранную псевдооболочку `Ctrl+O` на общем редакторе строки. -4. Стандартный fallback цветов уже добавлен: белые каталоги, жёлтые `.EXE`, - светло-серые остальные файлы. После стабилизации режимов добавить загрузку - конфигурируемых правил расширений и сохранение режима отдельно для левой и - правой панели. - -До появления отдельного счётчика файлов строка итога не должна называть -каталоги файлами: `Insert` может выбирать каталог, тогда как групповые -`+`, `-`, `*` его обязаны игнорировать. Это различие остаётся частью модели, -а не только правилом renderer. - -## Проверка - -Каждый визуальный шаг принимается не по одному кадру, а по сценарию с -известным содержимым панели, соседним контрольным кадрам и, где применимо, -извлечённому состоянию HDD. Зона имитации индикаторов дисководов MAME не -участвует в сравнении интерфейса Commander. diff --git a/applications/Volkov/include/sc_app.h b/applications/Volkov/include/sc_app.h deleted file mode 100644 index 32ab5df..0000000 --- a/applications/Volkov/include/sc_app.h +++ /dev/null @@ -1,49 +0,0 @@ -/* sc_app.h — состояние и главный цикл Sprinter Commander. */ - -#ifndef SC_APP_H -#define SC_APP_H - -#include -#include "sc_command.h" -#include "sc_copy.h" -#include "sc_panel.h" - -#define SC_PANEL_COUNT 2u -#define SC_PANEL_VISIBLE 27u -#define SC_EMM_PAGE_COUNT 4u -typedef struct ScApp { - ScPanel panels[SC_PANEL_COUNT]; - uint8_t active_panel; - uint8_t running; - uint8_t emm_block; - uint8_t screen_page; - uint8_t copy_page; - uint16_t emm_total_before; - uint16_t emm_free_before; - ScCopyJob copy_job; - const char *status; -} ScApp; - -int sc_app_init(ScApp *app); -void sc_app_run(ScApp *app); -int sc_app_cleanup(ScApp *app); - -void sc_draw_all(const ScApp *app) __banked; -void sc_draw_panel_rows(const ScApp *app, uint8_t panel_index) __banked; -void sc_draw_panel_row(const ScApp *app, uint8_t panel_index, - uint8_t row) __banked; -void sc_draw_info(const ScApp *app) __banked; -void sc_draw_status(const ScApp *app) __banked; - -uint8_t sc_dialog_confirm_quit(void) __banked; -uint8_t sc_dialog_confirm(const char *title, const char *message) __banked; -void sc_dialog_message(const char *title, const char *message) __banked; -uint8_t sc_dialog_choice(const char *title, const char *message, - const char *keys, const char *buttons) __banked; -uint8_t sc_dialog_input(const char *title, const char *prompt, - char *buffer, uint8_t capacity) __banked; -/* Возвращает выбранную ASCII-клавишу из keys в верхнем регистре; Esc = 0. */ -uint8_t sc_dialog_choice(const char *title, const char *message, - const char *keys, const char *buttons) __banked; - -#endif diff --git a/applications/Volkov/include/sc_command.h b/applications/Volkov/include/sc_command.h deleted file mode 100644 index b8b765f..0000000 --- a/applications/Volkov/include/sc_command.h +++ /dev/null @@ -1,43 +0,0 @@ -/* sc_command.h — логические команды Commander. */ - -#ifndef SC_COMMAND_H -#define SC_COMMAND_H - -#include "sc_input.h" - -typedef enum { - SC_CMD_NONE = 0, - SC_CMD_CURSOR_UP, - SC_CMD_CURSOR_DOWN, - SC_CMD_PAGE_UP, - SC_CMD_PAGE_DOWN, - SC_CMD_HOME, - SC_CMD_END, - SC_CMD_PANEL_SWITCH, - SC_CMD_OPEN, - SC_CMD_PARENT, - SC_CMD_REFRESH, - SC_CMD_SORT_NAME, - SC_CMD_SORT_EXTENSION, - SC_CMD_SORT_DATE, - SC_CMD_SORT_SIZE, - SC_CMD_SELECT, - SC_CMD_INVERT_SELECTION, - SC_CMD_SELECT_MASK, - SC_CMD_UNSELECT_MASK, - SC_CMD_HELP, - SC_CMD_USER_MENU, - SC_CMD_VIEW, - SC_CMD_EDIT, - SC_CMD_COPY, - SC_CMD_MOVE_RENAME, - SC_CMD_MKDIR, - SC_CMD_DELETE, - SC_CMD_MAIN_MENU, - SC_CMD_QUIT, - SC_CMD_CANCEL -} ScCommand; - -ScCommand sc_command_from_event(const ScKeyEvent *event); - -#endif diff --git a/applications/Volkov/include/sc_copy.h b/applications/Volkov/include/sc_copy.h deleted file mode 100644 index a4b5aef..0000000 --- a/applications/Volkov/include/sc_copy.h +++ /dev/null @@ -1,59 +0,0 @@ -/* sc_copy.h — состояние безопасного однофайлового copy-job PoC. */ - -#ifndef SC_COPY_H -#define SC_COPY_H - -#include - -#define SC_COPY_PATH_CAPACITY 256u -#define SC_COPY_CHUNK_SIZE 4096u - -#define SC_COPY_IDLE 0u -#define SC_COPY_RUNNING 1u -#define SC_COPY_DONE 2u -#define SC_COPY_CANCELLED 3u -#define SC_COPY_ERROR 4u - -#define SC_COPY_STEP_ERROR (-1) -#define SC_COPY_STEP_DONE 0 -#define SC_COPY_STEP_MORE 1 /* нужен ещё шаг; done==total означает pre-commit */ - -typedef struct { - char source_path[SC_COPY_PATH_CAPACITY]; - char target_path[SC_COPY_PATH_CAPACITY]; - char temp_path[SC_COPY_PATH_CAPACITY]; - uint32_t total; - uint32_t done; - uint16_t source_date; - uint16_t source_time; - int source_fd; - int temp_fd; - int error; - uint8_t page; - uint8_t state; - uint8_t temp_exists; - uint8_t source_attr; - uint8_t existing_attr; - uint8_t preserve_metadata; - uint8_t replace_existing; - uint8_t committed; -} ScCopyJob; - -int sc_copy_begin(ScCopyJob *job, const char *source_dir, - const char *target_dir, const char *name, - uint32_t size, uint8_t page) __banked; -/* source_name и target_name могут различаться; replace включает backup/rollback. */ -int sc_copy_begin_policy(ScCopyJob *job, const char *source_dir, - const char *target_dir, const char *source_name, - const char *target_name, uint32_t size, - uint8_t page, uint8_t replace) __banked; -/* Для tree-job: CWD уже равен source-dir, а target_path подготовлен целиком. */ -int sc_copy_begin_prepared(ScCopyJob *job, const char *name, - uint32_t size, uint8_t page) __banked; -int sc_copy_begin_prepared_policy(ScCopyJob *job, const char *name, - uint32_t size, uint8_t page, - uint8_t replace) __banked; -int sc_copy_step(ScCopyJob *job) __banked; -void sc_copy_cancel(ScCopyJob *job) __banked; - -#endif diff --git a/applications/Volkov/include/sc_copy_ui.h b/applications/Volkov/include/sc_copy_ui.h deleted file mode 100644 index 250b00b..0000000 --- a/applications/Volkov/include/sc_copy_ui.h +++ /dev/null @@ -1,10 +0,0 @@ -/* sc_copy_ui.h — банковый обработчик команды F5 и её прогресса. */ - -#ifndef SC_COPY_UI_H -#define SC_COPY_UI_H - -struct ScApp; - -void sc_copy_selected(struct ScApp *app) __banked; - -#endif diff --git a/applications/Volkov/include/sc_disk.h b/applications/Volkov/include/sc_disk.h deleted file mode 100644 index 06d5958..0000000 --- a/applications/Volkov/include/sc_disk.h +++ /dev/null @@ -1,11 +0,0 @@ -/* sc_disk.h — проверка свободных кластеров целевого диска через DSKINFO. */ - -#ifndef SC_DISK_H -#define SC_DISK_H - -#include - -/* 1 — места достаточно; 0 — недостаточно; -1 — ошибка DSS. */ -int sc_disk_has_space(const char *path, uint32_t bytes) __banked; - -#endif diff --git a/applications/Volkov/include/sc_entry.h b/applications/Volkov/include/sc_entry.h deleted file mode 100644 index b7a839c..0000000 --- a/applications/Volkov/include/sc_entry.h +++ /dev/null @@ -1,27 +0,0 @@ -/* sc_entry.h — компактная 24-байтовая запись каталога в EMM. */ - -#ifndef SC_ENTRY_H -#define SC_ENTRY_H - -#include - -#define SC_ENTRY_PARENT 0x01u -#define SC_ENTRY_SELECTED 0x02u -#define SC_ENTRY_DSS_LIMIT 0x04u - -#define SC_DIR_CAPACITY 640u - -typedef struct { - char name[13]; - uint32_t size; - uint16_t date; - uint16_t time; - uint8_t attr; - uint8_t flags; - uint8_t reserved; -} ScDirEntry; - -/* SDCC обязан сохранить зафиксированный дисковый формат без padding. */ -typedef char ScDirEntry_must_be_24_bytes[(sizeof(ScDirEntry) == 24u) ? 1 : -1]; - -#endif diff --git a/applications/Volkov/include/sc_exec.h b/applications/Volkov/include/sc_exec.h deleted file mode 100644 index fc64788..0000000 --- a/applications/Volkov/include/sc_exec.h +++ /dev/null @@ -1,8 +0,0 @@ -/* sc_exec.h — низкоуровневый запуск EXE через ESTEX. */ - -#ifndef SC_EXEC_H -#define SC_EXEC_H - -int sc_platform_exec(const char *path); - -#endif diff --git a/applications/Volkov/include/sc_exec_ui.h b/applications/Volkov/include/sc_exec_ui.h deleted file mode 100644 index 4ac11ba..0000000 --- a/applications/Volkov/include/sc_exec_ui.h +++ /dev/null @@ -1,10 +0,0 @@ -/* sc_exec_ui.h — банковый обработчик Enter на EXE. */ - -#ifndef SC_EXEC_UI_H -#define SC_EXEC_UI_H - -struct ScApp; - -void sc_exec_selected(struct ScApp *app) __banked; - -#endif diff --git a/applications/Volkov/include/sc_file_ops.h b/applications/Volkov/include/sc_file_ops.h deleted file mode 100644 index 3f45e76..0000000 --- a/applications/Volkov/include/sc_file_ops.h +++ /dev/null @@ -1,17 +0,0 @@ -/* sc_file_ops.h — низкоуровневые безопасные файловые операции Commander. */ - -#ifndef SC_FILE_OPS_H -#define SC_FILE_OPS_H - -#include - -/* Переименовать old_name в new_name внутри directory и восстановить CWD. - * 0 — успех; -1 — операция отклонена, CWD восстановлен; - * -2 — восстановить CWD после попытки не удалось. */ -int sc_rename_local(const char *directory, - const char *old_name, - const char *new_name, - char *cwd_buffer, - uint16_t cwd_capacity) __banked; - -#endif diff --git a/applications/Volkov/include/sc_file_ui.h b/applications/Volkov/include/sc_file_ui.h deleted file mode 100644 index e90339f..0000000 --- a/applications/Volkov/include/sc_file_ui.h +++ /dev/null @@ -1,12 +0,0 @@ -/* sc_file_ui.h — банковые UI-команды безопасных файловых операций. */ - -#ifndef SC_FILE_UI_H -#define SC_FILE_UI_H - -#include "sc_app.h" - -void sc_mkdir_command(ScApp *app) __banked; -void sc_rename_command(ScApp *app) __banked; -void sc_delete_command(ScApp *app) __banked; - -#endif diff --git a/applications/Volkov/include/sc_glyphs.h b/applications/Volkov/include/sc_glyphs.h deleted file mode 100644 index 10ebfa5..0000000 --- a/applications/Volkov/include/sc_glyphs.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * sc_glyphs.h — raw-коды CP866/CP437 для интерфейса Commander. - * - * Управляющие байты 0x18..0x1B здесь являются экранными глифами. Их нельзя - * передавать функциям, интерпретирующим CR/LF/BS/TAB. - */ - -#ifndef SC_GLYPHS_H -#define SC_GLYPHS_H - -#define SC_G_H 0xC4u -#define SC_G_V 0xB3u -#define SC_G_TL 0xDAu -#define SC_G_TR 0xBFu -#define SC_G_BL 0xC0u -#define SC_G_BR 0xD9u -#define SC_G_TM 0xC2u -#define SC_G_BM 0xC1u -#define SC_G_ML 0xC3u -#define SC_G_MR 0xB4u -#define SC_G_X 0xC5u - -#define SC_G_ARROW_UP 0x18u -#define SC_G_ARROW_DOWN 0x19u -#define SC_G_ARROW_RIGHT 0x1Au -#define SC_G_ARROW_LEFT 0x1Bu - -#define SC_G_SHADE_LIGHT 0xB0u -#define SC_G_SHADE_MEDIUM 0xB1u -#define SC_G_SHADE_DARK 0xB2u -#define SC_G_BLOCK 0xDBu -#define SC_G_HALF_UP 0xDFu -#define SC_G_HALF_DOWN 0xDCu -#define SC_G_HALF_LEFT 0xDDu -#define SC_G_HALF_RIGHT 0xDEu -#define SC_G_BULLET 0xF9u -#define SC_G_CHECK 0xFBu -#define SC_G_SQUARE 0xFEu - -#endif diff --git a/applications/Volkov/include/sc_input.h b/applications/Volkov/include/sc_input.h deleted file mode 100644 index 76d2ea4..0000000 --- a/applications/Volkov/include/sc_input.h +++ /dev/null @@ -1,21 +0,0 @@ -/* sc_input.h — нормализованное событие клавиатуры. */ - -#ifndef SC_INPUT_H -#define SC_INPUT_H - -#include - -#define SC_MOD_SHIFT 0x01u -#define SC_MOD_CTRL 0x02u -#define SC_MOD_ALT 0x04u - -typedef struct { - uint8_t ascii; - uint8_t scan; - uint8_t modifiers; -} ScKeyEvent; - -void sc_input_read(ScKeyEvent *event); -uint8_t sc_input_poll(ScKeyEvent *event); - -#endif diff --git a/applications/Volkov/include/sc_metadata.h b/applications/Volkov/include/sc_metadata.h deleted file mode 100644 index a13fddd..0000000 --- a/applications/Volkov/include/sc_metadata.h +++ /dev/null @@ -1,13 +0,0 @@ -/* sc_metadata.h — узкие wrappers записи файловых метаданных DSS. */ - -#ifndef SC_METADATA_H -#define SC_METADATA_H - -#include - -int sc_metadata_set_datetime(uint8_t fd, uint16_t date, - uint16_t time) __banked; -int sc_metadata_get_attributes(const char *path, uint8_t *attr) __banked; -int sc_metadata_set_attributes(const char *path, uint8_t attr) __banked; - -#endif diff --git a/applications/Volkov/include/sc_panel.h b/applications/Volkov/include/sc_panel.h deleted file mode 100644 index 0f4f0e2..0000000 --- a/applications/Volkov/include/sc_panel.h +++ /dev/null @@ -1,41 +0,0 @@ -/* sc_panel.h — состояние одной независимой файловой панели. */ - -#ifndef SC_PANEL_H -#define SC_PANEL_H - -#include -#include "sc_entry.h" - -#define SC_SOURCE_REAL_FS 0u - -typedef enum { - SC_SORT_NAME = 0, - SC_SORT_EXTENSION, - SC_SORT_SIZE, - SC_SORT_DATE -} ScSortKey; - -typedef struct { - char path[256]; - uint16_t count; - uint16_t cursor; - uint16_t top; - uint32_t total_size; - uint16_t selected_count; - uint32_t selected_size; - uint8_t source_kind; - uint8_t phys_page; - uint8_t truncated; - uint8_t dirty; - uint8_t sort_key; - uint8_t sort_descending; -} ScPanel; - -int sc_panel_init(ScPanel *panel, uint8_t phys_page); -int sc_panel_refresh(ScPanel *panel); -int sc_panel_selected(const ScPanel *panel, ScDirEntry *entry); -int sc_panel_open_selected(ScPanel *panel); -int sc_panel_parent(ScPanel *panel); -void sc_panel_normalize_view(ScPanel *panel); - -#endif diff --git a/applications/Volkov/include/sc_path.h b/applications/Volkov/include/sc_path.h deleted file mode 100644 index 84d108c..0000000 --- a/applications/Volkov/include/sc_path.h +++ /dev/null @@ -1,12 +0,0 @@ -/* sc_path.h — безопасные операции над путями DOS 8.3 Commander. */ - -#ifndef SC_PATH_H -#define SC_PATH_H - -#include - -int sc_path_join(char *out, uint16_t capacity, - const char *directory, const char *name); -int sc_path_validate_component(const char *name) __banked; - -#endif diff --git a/applications/Volkov/include/sc_screen.h b/applications/Volkov/include/sc_screen.h deleted file mode 100644 index 13db63f..0000000 --- a/applications/Volkov/include/sc_screen.h +++ /dev/null @@ -1,38 +0,0 @@ -/* sc_screen.h — экранная EMM-модель Commander 80x32. */ - -#ifndef SC_SCREEN_API_H -#define SC_SCREEN_API_H - -#include - -#define SC_SCREEN_W 80u -#define SC_SCREEN_H 32u -#define SC_ROW_BYTES 160u -#define SC_SCREEN_BYTES 5120u - -#define SC_SCROLL_UP 1u -#define SC_SCROLL_DOWN 2u - -typedef struct { - uint8_t ch; - uint8_t attr; -} ScCell; - -typedef char ScCell_must_be_2_bytes[(sizeof(ScCell) == 2) ? 1 : -1]; - -void sc_screen_attach(uint8_t page); -void sc_screen_detach(void); -void sc_screen_clear(uint8_t attr); -void sc_screen_cell(uint8_t x, uint8_t y, uint8_t ch, uint8_t attr); -void sc_screen_fill(uint8_t x, uint8_t y, uint8_t width, - uint8_t ch, uint8_t attr); -void sc_screen_text(uint8_t x, uint8_t y, const char *text, - uint8_t attr, uint8_t max_width); -int sc_screen_present_full(void); -int sc_screen_present_row(uint8_t x, uint8_t y, uint8_t width); -int sc_screen_present_rows(uint8_t first, uint8_t count); -int sc_screen_scroll_rows(uint8_t x, uint8_t y, - uint8_t width, uint8_t height, - uint8_t direction, uint8_t fill_attr); - -#endif diff --git a/applications/Volkov/include/sc_select.h b/applications/Volkov/include/sc_select.h deleted file mode 100644 index b7f6a66..0000000 --- a/applications/Volkov/include/sc_select.h +++ /dev/null @@ -1,16 +0,0 @@ -/* sc_select.h — выбор записей текущей панели для групповых операций. */ - -#ifndef SC_SELECT_H -#define SC_SELECT_H - -#include "sc_panel.h" - -#define SC_SELECT_TOGGLE 0u -#define SC_SELECT_INVERT 1u -#define SC_SELECT_MASK_SET 2u -#define SC_SELECT_MASK_CLEAR 3u - -int sc_select_apply(ScPanel *panel, uint8_t action, - const char *mask) __banked; - -#endif diff --git a/applications/Volkov/include/sc_sort.h b/applications/Volkov/include/sc_sort.h deleted file mode 100644 index 0a40019..0000000 --- a/applications/Volkov/include/sc_sort.h +++ /dev/null @@ -1,11 +0,0 @@ -/* sc_sort.h — сортировка EMM-записей панели. */ - -#ifndef SC_SORT_H -#define SC_SORT_H - -#include "sc_panel.h" - -int sc_sort_panel(ScPanel *panel) __banked; -int sc_sort_set(ScPanel *panel, uint8_t key) __banked; - -#endif diff --git a/applications/Volkov/include/sc_source.h b/applications/Volkov/include/sc_source.h deleted file mode 100644 index 33e545a..0000000 --- a/applications/Volkov/include/sc_source.h +++ /dev/null @@ -1,11 +0,0 @@ -/* sc_source.h — источник настоящего каталога DSS для P3. */ - -#ifndef SC_SOURCE_H -#define SC_SOURCE_H - -#include "sc_panel.h" - -int sc_source_fs_scan(ScPanel *panel) __banked; -int sc_source_fs_change_dir(ScPanel *panel, const char *name) __banked; - -#endif diff --git a/applications/Volkov/include/sc_store.h b/applications/Volkov/include/sc_store.h deleted file mode 100644 index 8674144..0000000 --- a/applications/Volkov/include/sc_store.h +++ /dev/null @@ -1,14 +0,0 @@ -/* sc_store.h — доступ к записям панели без публикации W3-указателей. */ - -#ifndef SC_STORE_H -#define SC_STORE_H - -#include -#include "sc_panel.h" - -int sc_store_get(const ScPanel *panel, uint16_t index, ScDirEntry *out); -int sc_store_put(const ScPanel *panel, uint16_t index, - const ScDirEntry *entry); -void sc_store_clear(ScPanel *panel); - -#endif diff --git a/applications/Volkov/include/sc_theme.h b/applications/Volkov/include/sc_theme.h deleted file mode 100644 index baaa8d2..0000000 --- a/applications/Volkov/include/sc_theme.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * sc_theme.h — смысловые атрибуты интерфейса. - * - * P2.2 подтвердил системный save/set/restore четырёх текстовых палитровых - * планов. Renderer знает только эти роли, а точные RGB принадлежат модулю - * sc_theme.c. - */ - -#ifndef SC_THEME_H -#define SC_THEME_H - -#include - -#define SC_ATTR_FRAME COLOR(COLOR_WHITE, COLOR_BLUE) -#define SC_ATTR_TITLE COLOR(COLOR_YELLOW, COLOR_BLUE) -#define SC_ATTR_NORMAL COLOR(COLOR_LIGHTCYAN, COLOR_BLUE) -#define SC_ATTR_FILE COLOR(COLOR_LIGHTGRAY, COLOR_BLUE) -#define SC_ATTR_DIR SC_ATTR_FRAME -#define SC_ATTR_EXE SC_ATTR_TITLE -#define SC_ATTR_ACTIVE COLOR(COLOR_BLACK, COLOR_CYAN) -#define SC_ATTR_STATUS COLOR(COLOR_BLACK, COLOR_LIGHTGRAY) -#define SC_ATTR_DIALOG COLOR(COLOR_WHITE, COLOR_RED) -#define SC_ATTR_LIMIT COLOR(COLOR_LIGHTRED, COLOR_BLUE) -#define SC_ATTR_LIMIT_ACTIVE COLOR(COLOR_LIGHTRED, COLOR_CYAN) -#define SC_ATTR_KEYS SC_ATTR_ACTIVE - -int sc_theme_init(void); -void sc_theme_reapply(void); -void sc_theme_shutdown(void); - -#endif diff --git a/applications/Volkov/include/sc_tree.h b/applications/Volkov/include/sc_tree.h deleted file mode 100644 index 4adcc6e..0000000 --- a/applications/Volkov/include/sc_tree.h +++ /dev/null @@ -1,43 +0,0 @@ -/* sc_tree.h — пошаговый BFS-обход дерева для рекурсивных файловых jobs. */ - -#ifndef SC_TREE_H -#define SC_TREE_H - -#include -#include "sc_copy.h" -#include "sc_entry.h" - -#define SC_TREE_STEP_ERROR (-1) -#define SC_TREE_STEP_DONE 0 -#define SC_TREE_STEP_FILE 1 -#define SC_TREE_STEP_DIRECTORY 2 -#define SC_TREE_STEP_SCAN_DIR 3 - -typedef struct { - uint8_t block; - uint8_t page; - uint8_t active; - uint8_t scan_state; - uint16_t head; - uint16_t tail; - uint16_t current; - uint16_t reverse; - uint16_t created_directories; - uint16_t files_seen; - int error; -} ScTreeJob; - -int sc_tree_begin(ScTreeJob *job, ScCopyJob *copy) __banked; -int sc_tree_add_root(ScTreeJob *job, ScCopyJob *copy, - const char *source_base, const char *target_base, - const char *name) __banked; -int sc_tree_add_delete_root(ScTreeJob *job, const char *name) __banked; -int sc_tree_next(ScTreeJob *job, ScCopyJob *copy, - const char *source_base, const char *target_base, - ScDirEntry *entry) __banked; -int sc_tree_delete_next(ScTreeJob *job, ScCopyJob *copy, - const char *source_base, - ScDirEntry *entry) __banked; -int sc_tree_finish(ScTreeJob *job, ScCopyJob *copy) __banked; - -#endif diff --git a/applications/Volkov/include/sc_video.h b/applications/Volkov/include/sc_video.h deleted file mode 100644 index e9de2e8..0000000 --- a/applications/Volkov/include/sc_video.h +++ /dev/null @@ -1,22 +0,0 @@ -/* sc_video.h — системный координатный вывод без BIOS window descriptors. */ - -#ifndef SC_VIDEO_H -#define SC_VIDEO_H - -#include - -int sc_video_init(void); -int sc_video_restore(void); -void sc_video_shutdown(void); -int sc_video_present_rect(uint8_t page, uint16_t offset, - uint8_t row, uint8_t col, - uint8_t height, uint8_t width); -int sc_video_scroll_rect(uint8_t row, uint8_t col, - uint8_t height, uint8_t width, - uint8_t direction); - -void sc_platform_winrest(uint8_t row, uint8_t col, - uint8_t height, uint8_t width, - uint8_t page, uint16_t offset); - -#endif diff --git a/applications/Volkov/sdk.mk b/applications/Volkov/sdk.mk deleted file mode 100644 index b0d0882..0000000 --- a/applications/Volkov/sdk.mk +++ /dev/null @@ -1,11 +0,0 @@ -# Общая привязка Commander и его target-проб к Sprinter-CC. -VOLKOV_ROOT := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) --include $(VOLKOV_ROOT)/local.mk -SPRINTER_ROOT ?= $(if $(wildcard $(VOLKOV_ROOT)/../../bin/sprinter-cc),$(abspath $(VOLKOV_ROOT)/../..),) -ifeq ($(strip $(SPRINTER_ROOT)),) -$(error Задайте SPRINTER_ROOT или создайте Volkov/local.mk) -endif -PROJ_ROOT := $(SPRINTER_ROOT) -export SPRINTER_ROOT MAME_HOME MAME_BIN MAME_ROMPATH MAME_DSS_IMAGE -export MAME_SYSTEM_HDD_IMAGE MAME_BIOS CHDMAN_BIN -include $(PROJ_ROOT)/app.mk diff --git a/applications/Volkov/src/sc_app.c b/applications/Volkov/src/sc_app.c deleted file mode 100644 index e48e520..0000000 --- a/applications/Volkov/src/sc_app.c +++ /dev/null @@ -1,405 +0,0 @@ -/* sc_app.c — инициализация, команды и главный цикл Commander. */ - -#include -#include -#include -#include "sc_app.h" -#include "sc_copy_ui.h" -#include "sc_exec_ui.h" -#include "sc_file_ui.h" -#include "sc_input.h" -#include "sc_panel.h" -#include "sc_screen.h" -#include "sc_select.h" -#include "sc_sort.h" -#include "sc_theme.h" -#include "sc_video.h" - -static void sc_app_redraw(ScApp *app) -{ - sc_draw_all(app); - (void)sc_screen_present_full(); -} - -static void sc_app_redraw_info(ScApp *app) -{ - sc_draw_info(app); - sc_draw_status(app); - (void)sc_screen_present_rows(29u, 2u); -} - -static void sc_app_move_cursor(ScApp *app, ScCommand command) -{ - ScPanel *panel = &app->panels[app->active_panel]; - uint16_t old_cursor = panel->cursor; - uint16_t old_top = panel->top; - uint16_t last; - uint8_t left = app->active_panel ? 41u : 1u; - uint8_t old_row; - uint8_t new_row; - - if (panel->count == 0u) return; - last = (uint16_t)(panel->count - 1u); - - switch (command) { - case SC_CMD_CURSOR_UP: - if (panel->cursor != 0u) panel->cursor--; - break; - case SC_CMD_CURSOR_DOWN: - if (panel->cursor < last) panel->cursor++; - break; - case SC_CMD_PAGE_UP: - panel->cursor = (panel->cursor > SC_PANEL_VISIBLE) ? - (uint16_t)(panel->cursor - SC_PANEL_VISIBLE) : 0u; - break; - case SC_CMD_PAGE_DOWN: - panel->cursor = (uint16_t)(panel->cursor + SC_PANEL_VISIBLE); - if (panel->cursor > last) panel->cursor = last; - break; - case SC_CMD_HOME: - panel->cursor = 0u; - break; - case SC_CMD_END: - panel->cursor = last; - break; - default: - return; - } - - if (panel->cursor < panel->top) panel->top = panel->cursor; - if (panel->cursor >= (uint16_t)(panel->top + SC_PANEL_VISIBLE)) { - panel->top = (uint16_t)(panel->cursor - SC_PANEL_VISIBLE + 1u); - } - if (panel->cursor == old_cursor && panel->top == old_top) return; - - app->status = "Navigation uses only panel state; no directory rescan."; - - if (panel->top == (uint16_t)(old_top + 1u)) { - (void)sc_screen_scroll_rows(left, 1u, 38u, SC_PANEL_VISIBLE, - SC_SCROLL_UP, SC_ATTR_NORMAL); - sc_draw_panel_row(app, app->active_panel, - (uint8_t)(SC_PANEL_VISIBLE - 1u)); - sc_draw_panel_row(app, app->active_panel, - (uint8_t)(old_cursor - panel->top)); - (void)sc_screen_present_row(left, 27u, 38u); - (void)sc_screen_present_row(left, - (uint8_t)(old_cursor - panel->top + 1u), - 38u); - } else if (old_top == (uint16_t)(panel->top + 1u)) { - (void)sc_screen_scroll_rows(left, 1u, 38u, SC_PANEL_VISIBLE, - SC_SCROLL_DOWN, SC_ATTR_NORMAL); - sc_draw_panel_row(app, app->active_panel, 0u); - sc_draw_panel_row(app, app->active_panel, - (uint8_t)(old_cursor - panel->top)); - (void)sc_screen_present_row(left, 1u, 38u); - (void)sc_screen_present_row(left, - (uint8_t)(old_cursor - panel->top + 1u), - 38u); - } else if (panel->top != old_top) { - sc_draw_panel_rows(app, app->active_panel); - sc_app_redraw(app); - return; - } else { - old_row = (uint8_t)(old_cursor - panel->top); - new_row = (uint8_t)(panel->cursor - panel->top); - sc_draw_panel_row(app, app->active_panel, old_row); - sc_draw_panel_row(app, app->active_panel, new_row); - (void)sc_screen_present_row(left, (uint8_t)(old_row + 1u), 38u); - (void)sc_screen_present_row(left, (uint8_t)(new_row + 1u), 38u); - } - sc_app_redraw_info(app); -} - -static void sc_app_stub_dialog(ScApp *app) -{ - sc_dialog_message(" Sprinter Commander ", - "Command is reserved for a later phase."); - app->status = "Reserved command closed; screen restored from EMM model."; - sc_app_redraw(app); -} - -static void sc_app_open(ScApp *app) -{ - ScPanel *panel = &app->panels[app->active_panel]; - int result = sc_panel_open_selected(panel); - - if (result == 0) { - app->status = panel->truncated ? - "Directory opened; not all entries are shown." : - "Directory opened; inactive panel kept its own path."; - sc_app_redraw(app); - } else if (result == 2) { - app->status = "More entries exist, but DSS 1.71 cannot enumerate them."; - sc_app_redraw_info(app); - } else if (result > 0) { - sc_exec_selected(app); - } else { - app->status = "Cannot open directory; DSS path/CWD was restored."; - sc_app_redraw_info(app); - } -} - -static void sc_app_parent(ScApp *app) -{ - ScPanel *panel = &app->panels[app->active_panel]; - if (sc_panel_parent(panel) == 0) { - app->status = panel->truncated ? - "Parent opened; not all entries are shown." : - "Parent directory opened; other panel is unchanged."; - sc_app_redraw(app); - } else { - app->status = "Cannot open parent; DSS path/CWD was restored."; - sc_app_redraw_info(app); - } -} - -static void sc_app_refresh(ScApp *app) -{ - ScPanel *panel = &app->panels[app->active_panel]; - if (sc_panel_refresh(panel) == 0) { - app->status = panel->truncated ? - "Panel refreshed; not all entries are shown." : - "Panel refreshed; cursor restored by entry name."; - sc_app_redraw(app); - } else { - app->status = "Refresh failed; DSS path/CWD was restored."; - sc_app_redraw_info(app); - } -} - -static void sc_app_sort(ScApp *app, uint8_t key) -{ - ScPanel *panel = &app->panels[app->active_panel]; - - if (sc_sort_set(panel, key) != 0) { - app->status = "Cannot sort panel; EMM access failed."; - sc_app_redraw_info(app); - return; - } - - app->status = "Panel sort changed: N/E/D/S and +/- are shown on its frame."; - sc_app_redraw(app); -} - -static void sc_app_select(ScApp *app, uint8_t action) -{ - ScPanel *panel = &app->panels[app->active_panel]; - int result = sc_select_apply(panel, action, 0); - - if (result < 0) { - app->status = "Selection failed: EMM access error."; - } else if (result > 0) { - app->status = "This virtual entry cannot be selected."; - } else { - app->status = "Selection changed; virtual entries are excluded."; - } - sc_app_redraw(app); -} - -static void sc_app_select_mask(ScApp *app, uint8_t action) -{ - ScPanel *panel = &app->panels[app->active_panel]; - char *mask = app->copy_job.source_path; - int result; - - mask[0] = '*'; - mask[1] = '.'; - mask[2] = '*'; - mask[3] = '\0'; - if (!sc_dialog_input(action == SC_SELECT_MASK_SET ? - " Select by mask " : " Unselect by mask ", - "DOS mask (* and ?):", mask, 13u)) { - app->status = "Mask selection cancelled."; - sc_app_redraw(app); - return; - } - - result = sc_select_apply(panel, action, mask); - app->status = result == 0 ? "Mask selection applied." : - "Mask selection failed."; - sc_app_redraw(app); -} - -static void sc_app_dispatch(ScApp *app, ScCommand command) -{ - switch (command) { - case SC_CMD_CURSOR_UP: - case SC_CMD_CURSOR_DOWN: - case SC_CMD_PAGE_UP: - case SC_CMD_PAGE_DOWN: - case SC_CMD_HOME: - case SC_CMD_END: - sc_app_move_cursor(app, command); - break; - - case SC_CMD_PANEL_SWITCH: - app->active_panel ^= 1u; - app->status = "Tab switched the active panel."; - sc_app_redraw(app); - break; - - case SC_CMD_OPEN: - sc_app_open(app); - break; - - case SC_CMD_PARENT: - sc_app_parent(app); - break; - - case SC_CMD_REFRESH: - sc_app_refresh(app); - break; - - case SC_CMD_SORT_NAME: - sc_app_sort(app, SC_SORT_NAME); - break; - - case SC_CMD_SORT_EXTENSION: - sc_app_sort(app, SC_SORT_EXTENSION); - break; - - case SC_CMD_SORT_DATE: - sc_app_sort(app, SC_SORT_DATE); - break; - - case SC_CMD_SORT_SIZE: - sc_app_sort(app, SC_SORT_SIZE); - break; - - case SC_CMD_SELECT: - sc_app_select(app, SC_SELECT_TOGGLE); - break; - - case SC_CMD_INVERT_SELECTION: - sc_app_select(app, SC_SELECT_INVERT); - break; - - case SC_CMD_SELECT_MASK: - sc_app_select_mask(app, SC_SELECT_MASK_SET); - break; - - case SC_CMD_UNSELECT_MASK: - sc_app_select_mask(app, SC_SELECT_MASK_CLEAR); - break; - - case SC_CMD_COPY: - sc_copy_selected(app); - break; - - case SC_CMD_MKDIR: - sc_mkdir_command(app); - break; - - case SC_CMD_MOVE_RENAME: - sc_rename_command(app); - break; - - case SC_CMD_DELETE: - sc_delete_command(app); - break; - - case SC_CMD_HELP: - case SC_CMD_USER_MENU: - case SC_CMD_VIEW: - case SC_CMD_EDIT: - case SC_CMD_MAIN_MENU: - sc_app_stub_dialog(app); - break; - - case SC_CMD_QUIT: - if (sc_dialog_confirm_quit()) { - app->running = 0u; - } else { - app->status = "Exit cancelled; screen restored from EMM model."; - sc_app_redraw(app); - } - break; - - case SC_CMD_CANCEL: - case SC_CMD_NONE: - default: - break; - } -} - -int sc_app_init(ScApp *app) -{ - uint8_t i; - - memset(app, 0, sizeof(*app)); - if (sc_video_init() != 0) return -1; - if (sc_theme_init() != 0) { - sc_video_shutdown(); - return -2; - } - - mem_info(&app->emm_total_before, &app->emm_free_before); - app->emm_block = mem_alloc_pages(SC_EMM_PAGE_COUNT); - if (app->emm_block == 0u) { - sc_theme_shutdown(); - sc_video_shutdown(); - return -3; - } - - for (i = 0u; i < SC_PANEL_COUNT; i++) { - app->panels[i].phys_page = mem_get_page(app->emm_block, i); - } - app->screen_page = mem_get_page(app->emm_block, 2u); - app->copy_page = mem_get_page(app->emm_block, 3u); - if (app->panels[0].phys_page == 0u || - app->panels[1].phys_page == 0u || app->screen_page == 0u || - app->copy_page == 0u) { - mem_free_block(app->emm_block); - app->emm_block = 0u; - sc_theme_shutdown(); - sc_video_shutdown(); - return -4; - } - - for (i = 0u; i < SC_PANEL_COUNT; i++) { - if (sc_panel_init(&app->panels[i], app->panels[i].phys_page) != 0) { - mem_free_block(app->emm_block); - app->emm_block = 0u; - sc_theme_shutdown(); - sc_video_shutdown(); - return -5; - } - } - - app->active_panel = 0u; - app->running = 1u; - app->status = "0.2: independent panel sorting; safe F5 copy and EXEC."; - sc_screen_attach(app->screen_page); - sc_app_redraw(app); - return 0; -} - -void sc_app_run(ScApp *app) -{ - ScKeyEvent event; - - while (app->running) { - sc_input_read(&event); - sc_app_dispatch(app, sc_command_from_event(&event)); - } -} - -int sc_app_cleanup(ScApp *app) -{ - uint16_t total_after = 0u; - uint16_t free_after = 0u; - - if (app->copy_job.state == SC_COPY_RUNNING) { - sc_copy_cancel(&app->copy_job); - } - sc_screen_detach(); - if (app->emm_block != 0u) { - mem_free_block(app->emm_block); - app->emm_block = 0u; - } - mem_info(&total_after, &free_after); - sc_theme_shutdown(); - sc_video_shutdown(); - return (total_after == app->emm_total_before && - free_after == app->emm_free_before) ? 0 : -1; -} diff --git a/applications/Volkov/src/sc_command.c b/applications/Volkov/src/sc_command.c deleted file mode 100644 index efe521f..0000000 --- a/applications/Volkov/src/sc_command.c +++ /dev/null @@ -1,49 +0,0 @@ -/* sc_command.c — перевод физических клавиш в команды приложения. */ - -#include -#include "sc_command.h" - -ScCommand sc_command_from_event(const ScKeyEvent *event) -{ - if (event->ascii == 0x1Bu) return SC_CMD_CANCEL; - if (event->ascii == 0x09u) return SC_CMD_PANEL_SWITCH; - if (event->ascii == 0x0Du) return SC_CMD_OPEN; - if (event->ascii == 0x08u) return SC_CMD_PARENT; - if (event->ascii == 0x12u) return SC_CMD_REFRESH; - if (event->ascii == '*') return SC_CMD_INVERT_SELECTION; - if (event->ascii == '+') return SC_CMD_SELECT_MASK; - if (event->ascii == '-') return SC_CMD_UNSELECT_MASK; - - if ((event->modifiers & SC_MOD_CTRL) != 0u) { - if (event->scan == KEY_PGUP) return SC_CMD_PARENT; - if (event->scan == 0x13u) return SC_CMD_REFRESH; - if (event->scan == KEY_F3) return SC_CMD_SORT_NAME; - if (event->scan == KEY_F4) return SC_CMD_SORT_EXTENSION; - if (event->scan == KEY_F5) return SC_CMD_SORT_DATE; - if (event->scan == KEY_F6) return SC_CMD_SORT_SIZE; - return SC_CMD_NONE; - } - - if (event->ascii != 0u) return SC_CMD_NONE; - - switch (event->scan) { - case KEY_UP: return SC_CMD_CURSOR_UP; - case KEY_DOWN: return SC_CMD_CURSOR_DOWN; - case KEY_PGUP: return SC_CMD_PAGE_UP; - case KEY_PGDN: return SC_CMD_PAGE_DOWN; - case KEY_HOME: return SC_CMD_HOME; - case KEY_END: return SC_CMD_END; - case KEY_INS: return SC_CMD_SELECT; - case KEY_F1: return SC_CMD_HELP; - case KEY_F2: return SC_CMD_USER_MENU; - case KEY_F3: return SC_CMD_VIEW; - case KEY_F4: return SC_CMD_EDIT; - case KEY_F5: return SC_CMD_COPY; - case KEY_F6: return SC_CMD_MOVE_RENAME; - case KEY_F7: return SC_CMD_MKDIR; - case KEY_F8: return SC_CMD_DELETE; - case KEY_F9: return SC_CMD_MAIN_MENU; - case KEY_F10: return SC_CMD_QUIT; - default: return SC_CMD_NONE; - } -} diff --git a/applications/Volkov/src/sc_copy_job.c b/applications/Volkov/src/sc_copy_job.c deleted file mode 100644 index 4d4d527..0000000 --- a/applications/Volkov/src/sc_copy_job.c +++ /dev/null @@ -1,533 +0,0 @@ -/* - * sc_copy_job.c — безопасное копирование одного файла для PoC. - * - * Код живёт в W1-банке, данные job — в W2, единственный блок данных — в - * выделенной EMM-странице W3. Target никогда не открывается с усечением: - * сначала создаётся уникальный temp через O_EXCL, а после закрытия обоих fd - * target проверяется повторно и temp атомарно переименовывается. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "sc_copy.h" -#include "sc_disk.h" -#include "sc_metadata.h" -#include "sc_path.h" - -#define SC_COPY_TEMP_ATTEMPTS 256u -#define SC_COPY_BACKUP_ATTEMPTS 256u - -static uint16_t sc_copy_temp_sequence; -static uint16_t sc_copy_backup_sequence; -static char sc_copy_temp_name[13]; -static char sc_copy_backup_name[13]; - -static char sc_hex_digit(uint8_t value) -{ - value &= 0x0Fu; - return (char)(value < 10u ? '0' + value : 'A' + value - 10u); -} - -static void sc_make_temp_name(uint16_t value) -{ - sc_copy_temp_name[0] = '~'; - sc_copy_temp_name[1] = 'S'; - sc_copy_temp_name[2] = 'C'; - sc_copy_temp_name[3] = sc_hex_digit((uint8_t)(value >> 12)); - sc_copy_temp_name[4] = sc_hex_digit((uint8_t)(value >> 8)); - sc_copy_temp_name[5] = sc_hex_digit((uint8_t)(value >> 4)); - sc_copy_temp_name[6] = sc_hex_digit((uint8_t)value); - sc_copy_temp_name[7] = '.'; - sc_copy_temp_name[8] = 'T'; - sc_copy_temp_name[9] = 'M'; - sc_copy_temp_name[10] = 'P'; - sc_copy_temp_name[11] = '\0'; -} - -static void sc_make_backup_name(uint16_t value) -{ - sc_copy_backup_name[0] = '~'; - sc_copy_backup_name[1] = 'S'; - sc_copy_backup_name[2] = 'B'; - sc_copy_backup_name[3] = sc_hex_digit((uint8_t)(value >> 12)); - sc_copy_backup_name[4] = sc_hex_digit((uint8_t)(value >> 8)); - sc_copy_backup_name[5] = sc_hex_digit((uint8_t)(value >> 4)); - sc_copy_backup_name[6] = sc_hex_digit((uint8_t)value); - sc_copy_backup_name[7] = '.'; - sc_copy_backup_name[8] = 'B'; - sc_copy_backup_name[9] = 'A'; - sc_copy_backup_name[10] = 'K'; - sc_copy_backup_name[11] = '\0'; -} - -/* 1 — существует; 0 — достоверно отсутствует; -1 — проверить не удалось. */ -static int sc_path_exists(const char *path) -{ - int fd; - int saved_errno; - - errno = 0; - fd = open(path, O_RDONLY); - if (fd >= 0) { - if (close(fd) != 0) return -1; - return 1; - } - saved_errno = errno; - if (saved_errno == ENOENT || saved_errno == ENOPATH) return 0; - errno = saved_errno; - return -1; -} - -static void sc_copy_close_fd(int *fd) -{ - int owned = *fd; - *fd = -1; - if (owned >= 0) (void)close(owned); -} - -static void sc_copy_remove_temp(ScCopyJob *job) -{ - if (job->temp_exists) { - (void)unlink(job->temp_path); - job->temp_exists = 0u; - } -} - -static int sc_copy_fail(ScCopyJob *job, int error) -{ - if (error == 0) error = EUNKERR; - sc_copy_close_fd(&job->source_fd); - sc_copy_close_fd(&job->temp_fd); - sc_copy_remove_temp(job); - job->error = error; - job->state = SC_COPY_ERROR; - errno = error; - return SC_COPY_STEP_ERROR; -} - -static int sc_copy_create_temp(ScCopyJob *job, const char *target_dir) -{ - uint16_t attempt; - - for (attempt = 0u; attempt < SC_COPY_TEMP_ATTEMPTS; attempt++) { - sc_make_temp_name(sc_copy_temp_sequence++); - if (sc_path_join(job->temp_path, SC_COPY_PATH_CAPACITY, - target_dir, sc_copy_temp_name) != 0) return -1; - errno = 0; - job->temp_fd = open(job->temp_path, - O_WRONLY | O_CREAT | O_EXCL); - if (job->temp_fd >= 0) { - job->temp_exists = 1u; - return 0; - } - if (errno != EEXIST) return -1; - } - errno = EEXIST; - return -1; -} - -static char *sc_copy_path_name(char *path) -{ - char *name = path; - char *scan = path; - - while (*scan != '\0') { - if (*scan == '\\' || *scan == '/') name = scan + 1; - scan++; - } - return name; -} - -/* - * Возвращает байт, который можно временно заменить нулём, чтобы получить - * каталог temp. Для корня режется первый байт имени (`\NAME` -> `\`), - * для обычного пути — предшествующий разделитель (`\DIR\NAME` -> `\DIR`). - */ -static char *sc_copy_directory_cut(char *path, char *name) -{ - char *separator; - - if (name == path) return 0; - separator = name - 1; - if (separator == path || - (separator == path + 2 && path[1] == ':')) return name; - return separator; -} - -/* Выбирает отсутствующее backup-имя, не создавая файла. Полный путь временно - * строится в source_path: source fd к этому моменту уже закрыт. */ -static int sc_copy_choose_backup(ScCopyJob *job, char *target_name) -{ - char *directory_cut = sc_copy_directory_cut(job->target_path, - target_name); - char saved_cut = '\0'; - uint16_t attempt; - int exists; - - if (directory_cut) { - saved_cut = *directory_cut; - *directory_cut = '\0'; - } - for (attempt = 0u; attempt < SC_COPY_BACKUP_ATTEMPTS; attempt++) { - sc_make_backup_name(sc_copy_backup_sequence++); - if (directory_cut) { - if (sc_path_join(job->source_path, SC_COPY_PATH_CAPACITY, - job->target_path, sc_copy_backup_name) != 0) { - *directory_cut = saved_cut; - return -1; - } - } else { - strncpy(job->source_path, sc_copy_backup_name, - SC_COPY_PATH_CAPACITY - 1u); - job->source_path[SC_COPY_PATH_CAPACITY - 1u] = '\0'; - } - exists = sc_path_exists(job->source_path); - if (exists == 0) { - if (directory_cut) *directory_cut = saved_cut; - return 0; - } - if (exists < 0) { - if (directory_cut) *directory_cut = saved_cut; - return -1; - } - } - if (directory_cut) *directory_cut = saved_cut; - errno = EEXIST; - return -1; -} - -static int sc_copy_finish(ScCopyJob *job) -{ - int exists; - int saved_errno; - int rename_errno = 0; - int final_errno = 0; - char *temp_name; - char *target_name; - char *directory_cut; - char saved_cut = '\0'; - uint8_t changed_directory = 0u; - uint8_t replace_target = 0u; - uint8_t backup_exists = 0u; - uint8_t readonly_cleared = 0u; - - if (job->source_fd >= 0) { - int fd = job->source_fd; - job->source_fd = -1; - if (close(fd) != 0) return sc_copy_fail(job, errno); - } - if (job->temp_fd >= 0) { - int fd = job->temp_fd; - if (job->preserve_metadata && - sc_metadata_set_datetime((uint8_t)fd, job->source_date, - job->source_time) != 0) { - return sc_copy_fail(job, errno); - } - job->temp_fd = -1; - if (close(fd) != 0) return sc_copy_fail(job, errno); - } - - exists = sc_path_exists(job->target_path); - if (exists < 0) return sc_copy_fail(job, errno); - if (exists > 0) { - if (!job->replace_existing) return sc_copy_fail(job, EEXIST); - if (sc_metadata_get_attributes(job->target_path, - &job->existing_attr) != 0) { - return sc_copy_fail(job, errno); - } - replace_target = 1u; - } - - /* DSS 1.71 RENAME принимает два имени из текущего каталога. Полный - * путь старого файла он частично разбирает, но полный новый путь затем - * отвергает как EINAME. Поэтому commit выполняется локально в target-dir - * с обязательным восстановлением глобального CWD приложения. */ - temp_name = sc_copy_path_name(job->temp_path); - target_name = sc_copy_path_name(job->target_path); - if (*temp_name == '\0' || *target_name == '\0') { - return sc_copy_fail(job, EINAME); - } - directory_cut = sc_copy_directory_cut(job->temp_path, temp_name); - if (replace_target && sc_copy_choose_backup(job, target_name) != 0) { - return sc_copy_fail(job, errno); - } - if (directory_cut) { - if (!getcwd(job->source_path, SC_COPY_PATH_CAPACITY)) { - return sc_copy_fail(job, errno); - } - saved_cut = *directory_cut; - *directory_cut = '\0'; - if (chdir(job->temp_path) != 0) { - saved_errno = errno; - *directory_cut = saved_cut; - return sc_copy_fail(job, saved_errno); - } - *directory_cut = saved_cut; - changed_directory = 1u; - } - - if (replace_target && (job->existing_attr & FA_RDONLY)) { - if (sc_metadata_set_attributes(target_name, - (uint8_t)(job->existing_attr & (uint8_t)~FA_RDONLY)) != 0) { - rename_errno = errno; - } else { - readonly_cleared = 1u; - } - } - if (replace_target && rename_errno == 0) { - if (rename(target_name, sc_copy_backup_name) != 0) { - rename_errno = errno; - } else { - backup_exists = 1u; - } - } - if (rename_errno == 0 && rename(temp_name, target_name) != 0) { - rename_errno = errno; - } else if (rename_errno == 0) { - job->temp_exists = 0u; - job->committed = 1u; - } - - /* До удаления backup новая запись должна получить все метаданные. При - * отказе ATTRIB возвращаем старый target на место. */ - if (job->committed && job->preserve_metadata && - sc_metadata_set_attributes(target_name, job->source_attr) != 0) { - rename_errno = errno; - } - if (rename_errno != 0 && backup_exists) { - if (job->committed && unlink(target_name) != 0) { - final_errno = errno; - } else if (rename(sc_copy_backup_name, target_name) != 0) { - final_errno = errno; - } else { - backup_exists = 0u; - job->committed = 0u; - if (readonly_cleared && - sc_metadata_set_attributes(target_name, - job->existing_attr) != 0) { - final_errno = errno; - } - } - } - if (rename_errno == 0 && backup_exists) { - if (unlink(sc_copy_backup_name) != 0) final_errno = errno; - else backup_exists = 0u; - } - if (rename_errno != 0 && readonly_cleared && !backup_exists && - !job->committed) { - if (sc_metadata_set_attributes(target_name, - job->existing_attr) != 0 && - final_errno == 0) final_errno = errno; - } - if (changed_directory && chdir(job->source_path) != 0) { - saved_errno = errno; - if (rename_errno != 0) return sc_copy_fail(job, - final_errno != 0 ? - final_errno : rename_errno); - job->error = saved_errno; - job->state = SC_COPY_ERROR; - errno = saved_errno; - return SC_COPY_STEP_ERROR; - } - if (rename_errno != 0) { - return sc_copy_fail(job, final_errno != 0 ? final_errno : rename_errno); - } - if (final_errno != 0) { - job->error = final_errno; - job->state = SC_COPY_ERROR; - errno = final_errno; - return SC_COPY_STEP_ERROR; - } - - job->state = SC_COPY_DONE; - job->error = 0; - return SC_COPY_STEP_DONE; -} - -static int sc_copy_begin_policy_impl(ScCopyJob *job, const char *source_dir, - const char *target_dir, - const char *source_name, - const char *target_name, uint32_t size, - uint8_t page, uint8_t replace) -{ - int exists; - - if (!job || !source_dir || !target_dir || !source_name || !target_name || - source_name[0] == '\0' || target_name[0] == '\0' || page == 0u) { - errno = EINVAL; - return -1; - } - memset(job, 0, sizeof(*job)); - job->source_fd = -1; - job->temp_fd = -1; - job->page = page; - job->total = size; - job->replace_existing = replace ? 1u : 0u; - - if (sc_path_join(job->source_path, SC_COPY_PATH_CAPACITY, - source_dir, source_name) != 0 || - sc_path_join(job->target_path, SC_COPY_PATH_CAPACITY, - target_dir, target_name) != 0) { - return sc_copy_fail(job, errno); - } - - exists = sc_path_exists(job->target_path); - if (exists < 0) return sc_copy_fail(job, errno); - if (exists > 0) { - if (sc_metadata_get_attributes(job->target_path, - &job->existing_attr) != 0) { - return sc_copy_fail(job, errno); - } - if (!job->replace_existing) return sc_copy_fail(job, EEXIST); - } - exists = sc_disk_has_space(job->target_path, size); - if (exists <= 0) return sc_copy_fail(job, exists == 0 ? ENOSPC : errno); - if (sc_copy_create_temp(job, target_dir) != 0) { - return sc_copy_fail(job, errno); - } - job->source_fd = open(job->source_path, O_RDONLY); - if (job->source_fd < 0) return sc_copy_fail(job, errno); - - job->state = SC_COPY_RUNNING; - return 0; -} - -int sc_copy_begin(ScCopyJob *job, const char *source_dir, - const char *target_dir, const char *name, - uint32_t size, uint8_t page) __banked -{ - return sc_copy_begin_policy_impl(job, source_dir, target_dir, name, name, - size, page, 0u); -} - -int sc_copy_begin_policy(ScCopyJob *job, const char *source_dir, - const char *target_dir, const char *source_name, - const char *target_name, uint32_t size, - uint8_t page, uint8_t replace) __banked -{ - return sc_copy_begin_policy_impl(job, source_dir, target_dir, - source_name, target_name, size, page, - replace); -} - -static int sc_copy_begin_prepared_impl(ScCopyJob *job, const char *name, - uint32_t size, uint8_t page, - uint8_t replace) -{ - char *target_name; - char *directory_cut; - char saved_cut; - int exists; - int saved_errno; - - if (!job || !name || name[0] == '\0' || page == 0u || - job->target_path[0] == '\0') { - errno = EINVAL; - return -1; - } - job->source_fd = -1; - job->temp_fd = -1; - job->total = size; - job->done = 0ul; - job->error = 0; - job->page = page; - job->state = SC_COPY_IDLE; - job->temp_exists = 0u; - job->preserve_metadata = 0u; - job->replace_existing = replace ? 1u : 0u; - job->committed = 0u; - job->existing_attr = 0u; - strncpy(job->source_path, name, SC_COPY_PATH_CAPACITY - 1u); - job->source_path[SC_COPY_PATH_CAPACITY - 1u] = '\0'; - - exists = sc_path_exists(job->target_path); - if (exists < 0) return sc_copy_fail(job, errno); - if (exists > 0) { - if (sc_metadata_get_attributes(job->target_path, - &job->existing_attr) != 0) { - return sc_copy_fail(job, errno); - } - if (!job->replace_existing) return sc_copy_fail(job, EEXIST); - } - - exists = sc_disk_has_space(job->target_path, size); - if (exists <= 0) return sc_copy_fail(job, exists == 0 ? ENOSPC : errno); - - target_name = sc_copy_path_name(job->target_path); - directory_cut = sc_copy_directory_cut(job->target_path, target_name); - if (!directory_cut || *target_name == '\0') { - return sc_copy_fail(job, EINAME); - } - saved_cut = *directory_cut; - *directory_cut = '\0'; - if (sc_copy_create_temp(job, job->target_path) != 0) { - saved_errno = errno; - *directory_cut = saved_cut; - return sc_copy_fail(job, saved_errno); - } - *directory_cut = saved_cut; - - job->source_fd = open(job->source_path, O_RDONLY); - if (job->source_fd < 0) return sc_copy_fail(job, errno); - job->state = SC_COPY_RUNNING; - return 0; -} - -int sc_copy_begin_prepared(ScCopyJob *job, const char *name, - uint32_t size, uint8_t page) __banked -{ - return sc_copy_begin_prepared_impl(job, name, size, page, 0u); -} - -int sc_copy_begin_prepared_policy(ScCopyJob *job, const char *name, - uint32_t size, uint8_t page, - uint8_t replace) __banked -{ - return sc_copy_begin_prepared_impl(job, name, size, page, replace); -} - -int sc_copy_step(ScCopyJob *job) __banked -{ - uint32_t remaining; - uint16_t chunk; - int count; - - if (!job || job->state != SC_COPY_RUNNING) { - errno = EINVAL; - return SC_COPY_STEP_ERROR; - } - remaining = job->total - job->done; - if (remaining == 0ul) return sc_copy_finish(job); - chunk = remaining > SC_COPY_CHUNK_SIZE ? - SC_COPY_CHUNK_SIZE : (uint16_t)remaining; - - count = bank_read_page(job->source_fd, job->page, 0u, chunk); - if (count != (int)chunk) { - return sc_copy_fail(job, count < 0 ? errno : EREAD); - } - count = bank_write_page(job->temp_fd, job->page, 0u, chunk); - if (count != (int)chunk) { - return sc_copy_fail(job, count < 0 ? errno : EWRITE); - } - job->done += (uint32_t)chunk; - /* Даже после последнего блока сначала возвращаем управление UI. Так - * между полной записью temp и commit/rename остаётся точка отмены. - * Следующий вызов попадёт в remaining == 0 и выполнит commit. */ - return SC_COPY_STEP_MORE; -} - -void sc_copy_cancel(ScCopyJob *job) __banked -{ - if (!job || job->state != SC_COPY_RUNNING) return; - sc_copy_close_fd(&job->source_fd); - sc_copy_close_fd(&job->temp_fd); - sc_copy_remove_temp(job); - job->error = 0; - job->state = SC_COPY_CANCELLED; -} diff --git a/applications/Volkov/src/sc_copy_ui.c b/applications/Volkov/src/sc_copy_ui.c deleted file mode 100644 index cac913c..0000000 --- a/applications/Volkov/src/sc_copy_ui.c +++ /dev/null @@ -1,556 +0,0 @@ -/* - * sc_copy_ui.c — банковая оркестрация F5 и отображение прогресса. - * - * Обработчик остаётся синхронным, но после каждого блока возвращает W3, - * обновляет строку состояния и проверяет Esc. Сам блок данных здесь не - * хранится: copy job переносит его через выделенную EMM-страницу ScApp. - */ - -#include -#include -#include -#include "sc_app.h" -#include "sc_copy_ui.h" -#include "sc_input.h" -#include "sc_panel.h" -#include "sc_path.h" -#include "sc_screen.h" -#include "sc_store.h" -#include "sc_tree.h" - -#define SC_COPY_RUN_DONE 0 -#define SC_COPY_RUN_ERROR -1 -#define SC_COPY_RUN_CANCELLED 1 -#define SC_COPY_RUN_SKIPPED 2 - -#define SC_COPY_POLICY_ASK 0u -#define SC_COPY_POLICY_OVERWRITE_ALL 1u -#define SC_COPY_POLICY_SKIP_ALL 2u - -static char sc_copy_status_text[79]; -static char sc_copy_rename_name[13]; - -static char *sc_copy_status_append(char *out, const char *text) -{ - char *limit = sc_copy_status_text + sizeof(sc_copy_status_text) - 1u; - - while (*text != '\0' && out < limit) *out++ = *text++; - *out = '\0'; - return out; -} - -static char *sc_copy_status_u32(char *out, uint32_t value) -{ - char digits[10]; - uint8_t count = 0u; - - do { - digits[count++] = (char)('0' + value % 10ul); - value /= 10ul; - } while (value != 0ul); - while (count != 0u) { - char one[2]; - one[0] = digits[--count]; - one[1] = '\0'; - out = sc_copy_status_append(out, one); - } - return out; -} - -static void sc_copy_redraw_info(ScApp *app) -{ - sc_draw_info(app); - sc_draw_status(app); - (void)sc_screen_present_rows(29u, 2u); -} - -static void sc_copy_redraw_all(ScApp *app) -{ - sc_draw_all(app); - (void)sc_screen_present_full(); -} - -static void sc_copy_progress(ScApp *app, const char *name, - uint16_t number, uint16_t total) -{ - char *out = sc_copy_status_text; - - out = sc_copy_status_append(out, "Copying "); - if (total > 1u) { - out = sc_copy_status_u32(out, number); - out = sc_copy_status_append(out, "/"); - out = sc_copy_status_u32(out, total); - out = sc_copy_status_append(out, " "); - } - out = sc_copy_status_append(out, name); - out = sc_copy_status_append(out, ": "); - out = sc_copy_status_u32(out, app->copy_job.done); - out = sc_copy_status_append(out, "/"); - out = sc_copy_status_u32(out, app->copy_job.total); - (void)sc_copy_status_append(out, " bytes; Esc cancels."); - app->status = sc_copy_status_text; -} - -static void sc_copy_group_status(ScApp *app, const char *prefix, - uint16_t copied, uint16_t total, - uint16_t directories, int error) -{ - char *out = sc_copy_status_text; - - out = sc_copy_status_append(out, prefix); - out = sc_copy_status_u32(out, copied); - out = sc_copy_status_append(out, "/"); - out = sc_copy_status_u32(out, total); - out = sc_copy_status_append(out, " files"); - if (directories != 0u) { - out = sc_copy_status_append(out, "; unprocessed "); - out = sc_copy_status_u32(out, directories); - out = sc_copy_status_append(out, " dirs"); - } - if (error != 0) { - out = sc_copy_status_append(out, "; errno "); - out = sc_copy_status_u32(out, (uint16_t)error); - } - (void)sc_copy_status_append(out, "."); - app->status = sc_copy_status_text; -} - -static void sc_copy_error(ScApp *app, int error) -{ - char *out; - - if (app->copy_job.committed) { - out = sc_copy_status_text; - out = sc_copy_status_append(out, "File committed; finalization failed (errno "); - out = sc_copy_status_u32(out, (uint16_t)error); - (void)sc_copy_status_append(out, ")."); - app->status = sc_copy_status_text; - return; - } - if (error == EEXIST) { - app->status = "Copy refused: target file already exists."; - return; - } - if (error == ENOSPC) { - app->status = "Copy refused: not enough free space; no temp created."; - return; - } - out = sc_copy_status_text; - out = sc_copy_status_append(out, "Copy failed (errno "); - out = sc_copy_status_u32(out, (uint16_t)error); - (void)sc_copy_status_append(out, "); temporary file removed."); - app->status = sc_copy_status_text; -} - -static int sc_copy_run(ScApp *app, const ScDirEntry *entry, - uint16_t number, uint16_t total) -{ - ScKeyEvent event; - int result; - - sc_copy_progress(app, entry->name, number, total); - sc_copy_redraw_info(app); - for (;;) { - if (sc_input_poll(&event) && event.ascii == 0x1Bu) { - sc_copy_cancel(&app->copy_job); - return SC_COPY_RUN_CANCELLED; - } - result = sc_copy_step(&app->copy_job); - if (result == SC_COPY_STEP_MORE) { - sc_copy_progress(app, entry->name, number, total); - sc_copy_redraw_info(app); - continue; - } - if (result == SC_COPY_STEP_ERROR) return SC_COPY_RUN_ERROR; - return SC_COPY_RUN_DONE; - } -} - -static void sc_copy_set_metadata(ScApp *app, const ScDirEntry *entry) -{ - app->copy_job.source_date = entry->date; - app->copy_job.source_time = entry->time; - app->copy_job.source_attr = entry->attr; - app->copy_job.preserve_metadata = 1u; -} - -static uint8_t sc_copy_conflict_action(ScApp *app, uint8_t *policy) -{ - uint8_t action; - - if (*policy == SC_COPY_POLICY_SKIP_ALL) return 'S'; - if (*policy == SC_COPY_POLICY_OVERWRITE_ALL && - !(app->copy_job.existing_attr & FA_RDONLY)) return 'O'; - - action = sc_dialog_choice( - (app->copy_job.existing_attr & FA_RDONLY) ? - " Read-only target " : " Target exists ", - "Choose action for the existing file.", - "OSRAN", "O/S/R A=all N=skip all Esc"); - if (action == 'A') { - *policy = SC_COPY_POLICY_OVERWRITE_ALL; - return 'O'; - } - if (action == 'N') { - *policy = SC_COPY_POLICY_SKIP_ALL; - return 'S'; - } - return action; -} - -static uint8_t sc_copy_input_rename(void) -{ - for (;;) { - sc_copy_rename_name[0] = '\0'; - if (!sc_dialog_input(" Copy under new name ", - "New DOS 8.3 name:", - sc_copy_rename_name, - sizeof(sc_copy_rename_name))) return 0u; - if (sc_path_validate_component(sc_copy_rename_name) == 0) return 1u; - sc_dialog_message(" Invalid target name ", - "Use one DOS 8.3 component."); - } -} - -static int sc_copy_target_set_name(ScCopyJob *job, const char *name) -{ - char *target = job->target_path; - char *write = target; - uint16_t prefix; - uint8_t length = 0u; - - while (*target != '\0') { - if (*target == '\\' || *target == '/') write = target + 1; - target++; - } - prefix = (uint16_t)(write - job->target_path); - while (name[length] != '\0') length++; - if (prefix + length + 1u > SC_COPY_PATH_CAPACITY) { - errno = EINAME; - return -1; - } - while ((*write++ = *name++) != '\0') {} - return 0; -} - -static int sc_copy_one(ScApp *app, const ScPanel *source, - const ScPanel *target, const ScDirEntry *entry, - uint16_t number, uint16_t total, uint8_t *policy) -{ - const char *target_name = entry->name; - uint8_t replace = 0u; - uint8_t action; - - for (;;) { - if (sc_copy_begin_policy(&app->copy_job, source->path, target->path, - entry->name, target_name, entry->size, - app->copy_page, replace) == 0) break; - if (errno != EEXIST || replace) return SC_COPY_RUN_ERROR; - action = sc_copy_conflict_action(app, policy); - if (action == 0u) return SC_COPY_RUN_CANCELLED; - if (action == 'S') return SC_COPY_RUN_SKIPPED; - if (action == 'R') { - if (!sc_copy_input_rename()) return SC_COPY_RUN_CANCELLED; - target_name = sc_copy_rename_name; - replace = 0u; - } else { - replace = 1u; - } - } - sc_copy_set_metadata(app, entry); - return sc_copy_run(app, entry, number, total); -} - -static int sc_copy_tree_file(ScApp *app, const ScDirEntry *entry, - uint16_t number, uint8_t *policy) -{ - uint8_t replace = 0u; - uint8_t action; - - for (;;) { - if (sc_copy_begin_prepared_policy(&app->copy_job, entry->name, - entry->size, app->copy_page, - replace) == 0) break; - if (errno != EEXIST || replace) return SC_COPY_RUN_ERROR; - action = sc_copy_conflict_action(app, policy); - if (action == 0u) return SC_COPY_RUN_CANCELLED; - if (action == 'S') return SC_COPY_RUN_SKIPPED; - if (action == 'R') { - if (!sc_copy_input_rename()) return SC_COPY_RUN_CANCELLED; - if (sc_copy_target_set_name(&app->copy_job, - sc_copy_rename_name) != 0) { - return SC_COPY_RUN_ERROR; - } - replace = 0u; - } else { - replace = 1u; - } - } - sc_copy_set_metadata(app, entry); - return sc_copy_run(app, entry, number, 0u); -} - -static void sc_copy_tree_status(ScApp *app, const char *prefix, - uint16_t files, uint16_t directories, - int error) -{ - char *out = sc_copy_status_text; - - out = sc_copy_status_append(out, prefix); - out = sc_copy_status_u32(out, files); - out = sc_copy_status_append(out, " files; "); - out = sc_copy_status_u32(out, directories); - out = sc_copy_status_append(out, " dirs"); - if (error != 0) { - out = sc_copy_status_append(out, "; errno "); - out = sc_copy_status_u32(out, (uint16_t)error); - } - (void)sc_copy_status_append(out, "."); - app->status = sc_copy_status_text; -} - -static void sc_copy_directory(ScApp *app, ScPanel *source, - ScPanel *target, const ScDirEntry *root, - uint16_t copied_before, uint8_t *policy) -{ - ScTreeJob tree; - ScDirEntry entry; - ScKeyEvent event; - uint16_t copied = copied_before; - uint16_t skipped = 0u; - uint16_t created = 0u; - uint16_t i; - int step = SC_TREE_STEP_DONE; - int result = SC_COPY_RUN_DONE; - int operation_error = 0; - - if (sc_tree_begin(&tree, &app->copy_job) != 0) { - if (copied_before != 0u) { - app->status = sc_panel_refresh(target) != 0 ? - "Files copied, but traversal alloc and refresh failed." : - "Files copied; tree traversal page allocation failed."; - } else { - app->status = "Tree copy failed: cannot allocate traversal page."; - } - sc_copy_redraw_all(app); - return; - } - if (root) { - if (sc_tree_add_root(&tree, &app->copy_job, source->path, - target->path, root->name) != 0) { - operation_error = errno; - step = SC_TREE_STEP_ERROR; - } - } else { - for (i = 0u; i < source->count; i++) { - if (sc_store_get(source, i, &entry) != 0) { - operation_error = ENOMEM; - step = SC_TREE_STEP_ERROR; - break; - } - if (!(entry.flags & SC_ENTRY_SELECTED) || - !(entry.attr & FA_DIREC) || - (entry.flags & (SC_ENTRY_PARENT | SC_ENTRY_DSS_LIMIT))) { - continue; - } - if (sc_tree_add_root(&tree, &app->copy_job, source->path, - target->path, entry.name) != 0) { - operation_error = errno; - step = SC_TREE_STEP_ERROR; - break; - } - } - } - - while (step != SC_TREE_STEP_ERROR) { - if (sc_input_poll(&event) && event.ascii == 0x1Bu) { - result = SC_COPY_RUN_CANCELLED; - break; - } - step = sc_tree_next(&tree, &app->copy_job, source->path, - target->path, &entry); - if (step == SC_TREE_STEP_DONE) break; - if (step == SC_TREE_STEP_ERROR) { - operation_error = tree.error; - break; - } - created = tree.created_directories; - if (step == SC_TREE_STEP_DIRECTORY) { - sc_copy_tree_status(app, "Copying tree: ", copied, created, 0); - sc_copy_redraw_info(app); - continue; - } - result = sc_copy_tree_file(app, &entry, - (uint16_t)(copied + skipped + 1u), policy); - if (result == SC_COPY_RUN_SKIPPED) { - skipped++; - result = SC_COPY_RUN_DONE; - continue; - } - if (result != SC_COPY_RUN_DONE) { - operation_error = app->copy_job.error; - break; - } - copied++; - } - - created = tree.created_directories; - if (sc_tree_finish(&tree, &app->copy_job) != 0 && - operation_error == 0) { - operation_error = errno; - step = SC_TREE_STEP_ERROR; - } - - if ((created != 0u || copied != 0u) && sc_panel_refresh(target) != 0) { - app->status = "Tree copied partly, but passive panel refresh failed."; - } else if (result == SC_COPY_RUN_CANCELLED) { - sc_copy_tree_status(app, "Tree cancelled after ", copied, created, 0); - } else if (step == SC_TREE_STEP_ERROR || result == SC_COPY_RUN_ERROR) { - sc_copy_tree_status(app, "Tree stopped after ", copied, created, - operation_error); - } else { - sc_copy_tree_status(app, "Tree copied: ", copied, created, 0); - } - sc_copy_redraw_all(app); -} - -static int sc_copy_count_group(const ScPanel *source, - uint16_t *files, uint16_t *directories) -{ - ScDirEntry entry; - uint16_t i; - - *files = 0u; - *directories = 0u; - for (i = 0u; i < source->count; i++) { - if (sc_store_get(source, i, &entry) != 0) return -1; - if (!(entry.flags & SC_ENTRY_SELECTED)) continue; - if (entry.attr & FA_DIREC) (*directories)++; - else if (!(entry.flags & SC_ENTRY_DSS_LIMIT)) (*files)++; - } - return 0; -} - -static void sc_copy_group(ScApp *app, ScPanel *source, ScPanel *target, - uint8_t *policy) -{ - ScDirEntry entry; - uint16_t files; - uint16_t directories; - uint16_t copied = 0u; - uint16_t skipped = 0u; - uint16_t i; - int result = SC_COPY_RUN_DONE; - int operation_error = 0; - - if (sc_copy_count_group(source, &files, &directories) != 0) { - app->status = "Group copy failed: EMM access error."; - sc_copy_redraw_info(app); - return; - } - if (files == 0u && directories == 0u) { - app->status = "Copy refused: selected group has no real entries."; - sc_copy_redraw_info(app); - return; - } - - for (i = 0u; i < source->count; i++) { - if (sc_store_get(source, i, &entry) != 0) { - result = SC_COPY_RUN_ERROR; - operation_error = ENOMEM; - break; - } - if (!(entry.flags & SC_ENTRY_SELECTED) || - (entry.attr & FA_DIREC) || - (entry.flags & SC_ENTRY_DSS_LIMIT)) continue; - result = sc_copy_one(app, source, target, &entry, - (uint16_t)(copied + skipped + 1u), files, - policy); - if (result == SC_COPY_RUN_SKIPPED) { - skipped++; - result = SC_COPY_RUN_DONE; - continue; - } - if (result != SC_COPY_RUN_DONE) { - operation_error = app->copy_job.error; - break; - } - copied++; - } - - if (result == SC_COPY_RUN_DONE && directories != 0u) { - sc_copy_directory(app, source, target, 0, copied, policy); - return; - } - - if (copied != 0u && sc_panel_refresh(target) != 0) { - app->status = "Files copied, but passive panel refresh failed."; - } else if (result == SC_COPY_RUN_CANCELLED) { - sc_copy_group_status(app, "Group cancelled after ", copied, files, - directories, 0); - } else if (result == SC_COPY_RUN_ERROR) { - sc_copy_group_status(app, "Group stopped after ", copied, files, - directories, operation_error); - } else { - sc_copy_group_status(app, "Copied ", copied, files, - directories, 0); - } - sc_copy_redraw_all(app); -} - -void sc_copy_selected(ScApp *app) __banked -{ - ScPanel *source = &app->panels[app->active_panel]; - ScPanel *target = &app->panels[app->active_panel ^ 1u]; - ScDirEntry entry; - int result; - uint8_t policy = SC_COPY_POLICY_ASK; - - if (source->selected_count != 0u) { - sc_copy_group(app, source, target, &policy); - return; - } - - if (sc_panel_selected(source, &entry) != 0) { - app->status = "Copy refused: active panel has no selected entry."; - sc_copy_redraw_info(app); - return; - } - if ((entry.flags & SC_ENTRY_DSS_LIMIT) != 0u) { - app->status = "Copy refused: the DSS limit marker is not a file."; - sc_copy_redraw_info(app); - return; - } - if ((entry.attr & FA_DIREC) != 0u) { - sc_copy_directory(app, source, target, &entry, 0u, &policy); - return; - } - - result = sc_copy_one(app, source, target, &entry, 1u, 1u, &policy); - if (result == SC_COPY_RUN_ERROR) { - sc_copy_error(app, app->copy_job.error); - sc_copy_redraw_all(app); - return; - } - if (result == SC_COPY_RUN_CANCELLED) { - app->status = "Copy cancelled; temporary file removed."; - sc_copy_redraw_all(app); - return; - } - if (result == SC_COPY_RUN_SKIPPED) { - app->status = "Copy skipped; existing target was not changed."; - sc_copy_redraw_all(app); - return; - } - - if (sc_panel_refresh(target) != 0) { - app->status = "File copied, but passive panel refresh failed."; - } else { - char *out = sc_copy_status_text; - out = sc_copy_status_append(out, "Copied "); - out = sc_copy_status_append(out, entry.name); - (void)sc_copy_status_append(out, " to the passive panel."); - app->status = sc_copy_status_text; - } - sc_copy_redraw_all(app); -} diff --git a/applications/Volkov/src/sc_dialog.c b/applications/Volkov/src/sc_dialog.c deleted file mode 100644 index 6441689..0000000 --- a/applications/Volkov/src/sc_dialog.c +++ /dev/null @@ -1,169 +0,0 @@ -/* sc_dialog.c — единая модальная поверхность сообщений Commander. */ - -#include -#include -#include "sc_app.h" -#include "sc_glyphs.h" -#include "sc_input.h" -#include "sc_screen.h" -#include "sc_theme.h" - -#define DIALOG_X 18u -#define DIALOG_Y 11u -#define DIALOG_W 44u -#define DIALOG_H 8u - -static void sc_dialog_box(const char *title, const char *message, - const char *buttons) -{ - uint8_t x; - uint8_t y; - uint8_t right = (uint8_t)(DIALOG_X + DIALOG_W - 1u); - uint8_t bottom = (uint8_t)(DIALOG_Y + DIALOG_H - 1u); - - for (y = DIALOG_Y; y <= bottom; y++) { - sc_screen_fill(DIALOG_X, y, DIALOG_W, ' ', SC_ATTR_DIALOG); - } - sc_screen_cell(DIALOG_X, DIALOG_Y, SC_G_TL, SC_ATTR_DIALOG); - sc_screen_cell(right, DIALOG_Y, SC_G_TR, SC_ATTR_DIALOG); - sc_screen_cell(DIALOG_X, bottom, SC_G_BL, SC_ATTR_DIALOG); - sc_screen_cell(right, bottom, SC_G_BR, SC_ATTR_DIALOG); - for (x = (uint8_t)(DIALOG_X + 1u); x < right; x++) { - sc_screen_cell(x, DIALOG_Y, SC_G_H, SC_ATTR_DIALOG); - sc_screen_cell(x, bottom, SC_G_H, SC_ATTR_DIALOG); - } - for (y = (uint8_t)(DIALOG_Y + 1u); y < bottom; y++) { - sc_screen_cell(DIALOG_X, y, SC_G_V, SC_ATTR_DIALOG); - sc_screen_cell(right, y, SC_G_V, SC_ATTR_DIALOG); - } - sc_screen_text((uint8_t)(DIALOG_X + 3u), DIALOG_Y, title, - SC_ATTR_DIALOG, 34u); - sc_screen_text((uint8_t)(DIALOG_X + 3u), (uint8_t)(DIALOG_Y + 3u), - message, SC_ATTR_DIALOG, 38u); - sc_screen_text((uint8_t)(DIALOG_X + 3u), (uint8_t)(DIALOG_Y + 5u), - buttons, SC_ATTR_DIALOG, 38u); - (void)sc_screen_present_full(); -} - -uint8_t sc_dialog_confirm(const char *title, const char *message) __banked -{ - ScKeyEvent event; - - sc_dialog_box(title, message, "Y/Enter = yes N/Esc = no"); - for (;;) { - sc_input_read(&event); - if (event.ascii == 'y' || event.ascii == 'Y' || - event.ascii == 0x0Du) return 1u; - if (event.ascii == 'n' || event.ascii == 'N' || - event.ascii == 0x1Bu) return 0u; - } -} - -uint8_t sc_dialog_confirm_quit(void) __banked -{ - return sc_dialog_confirm(" Exit Commander ", - "Leave Sprinter Commander?"); -} - -void sc_dialog_message(const char *title, const char *message) __banked -{ - ScKeyEvent event; - - sc_dialog_box(title, message, "Enter/Esc = close"); - for (;;) { - sc_input_read(&event); - if (event.ascii == 0x0Du || event.ascii == 0x1Bu) return; - } -} - -uint8_t sc_dialog_choice(const char *title, const char *message, - const char *keys, const char *buttons) __banked -{ - ScKeyEvent event; - const char *scan; - uint8_t key; - - sc_dialog_box(title, message, buttons); - for (;;) { - sc_input_read(&event); - if (event.ascii == 0x1Bu) return 0u; - key = event.ascii; - if (key >= 'a' && key <= 'z') key -= (uint8_t)('a' - 'A'); - scan = keys; - while (*scan != '\0') { - if ((uint8_t)*scan++ == key) return key; - } - } -} - -static void sc_dialog_input_row(const char *buffer, uint8_t cursor) -{ - uint8_t length = 0u; - - while (buffer[length] != '\0' && length < 38u) length++; - sc_screen_fill((uint8_t)(DIALOG_X + 3u), - (uint8_t)(DIALOG_Y + 4u), 38u, ' ', SC_ATTR_ACTIVE); - sc_screen_text((uint8_t)(DIALOG_X + 3u), - (uint8_t)(DIALOG_Y + 4u), buffer, - SC_ATTR_ACTIVE, 38u); - sc_screen_cell((uint8_t)(DIALOG_X + 3u + cursor), - (uint8_t)(DIALOG_Y + 4u), - cursor < length ? (uint8_t)buffer[cursor] : ' ', - SC_ATTR_DIALOG); - (void)sc_screen_present_row((uint8_t)(DIALOG_X + 3u), - (uint8_t)(DIALOG_Y + 4u), 38u); -} - -uint8_t sc_dialog_input(const char *title, const char *prompt, - char *buffer, uint8_t capacity) __banked -{ - ScKeyEvent event; - uint8_t length = 0u; - uint8_t cursor; - uint8_t limit; - uint8_t i; - - if (!buffer || capacity < 2u) return 0u; - limit = (uint8_t)(capacity - 1u); - if (limit > 37u) limit = 37u; - while (buffer[length] != '\0' && length < limit) length++; - buffer[length] = '\0'; - cursor = length; - - sc_dialog_box(title, prompt, "Enter = apply Esc = cancel"); - sc_dialog_input_row(buffer, cursor); - for (;;) { - sc_input_read(&event); - if (event.ascii == 0x0Du) return 1u; - if (event.ascii == 0x1Bu) return 0u; - - if (event.ascii == 0x08u) { - if (cursor != 0u) { - cursor--; - for (i = cursor; i < length; i++) buffer[i] = buffer[i + 1u]; - length--; - } - } else if (event.ascii >= 0x20u && event.ascii != 0x7Fu) { - if (length < limit) { - i = length; - while (i > cursor) { - buffer[i] = buffer[i - 1u]; - i--; - } - buffer[cursor++] = (char)event.ascii; - length++; - buffer[length] = '\0'; - } - } else if (event.ascii == 0u) { - if (event.scan == KEY_LEFT && cursor != 0u) cursor--; - else if (event.scan == KEY_RIGHT && cursor < length) cursor++; - else if (event.scan == KEY_HOME) cursor = 0u; - else if (event.scan == KEY_END) cursor = length; - else if (event.scan == KEY_DEL && cursor < length) { - for (i = cursor; i < length; i++) buffer[i] = buffer[i + 1u]; - length--; - } - } - sc_dialog_input_row(buffer, cursor); - } -} diff --git a/applications/Volkov/src/sc_disk.c b/applications/Volkov/src/sc_disk.c deleted file mode 100644 index c9b9eb0..0000000 --- a/applications/Volkov/src/sc_disk.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * sc_disk.c — preflight свободного места через ESTEX DSKINFO ($03). - * - * DSS 1.71 возвращает free clusters как DE':DE, bytes/sector в BC и - * sectors/cluster в A. Raw-leaf сохраняет обе половины shadow-регистров; - * C-обвязка сравнивает кластеры без округления размера вниз. - */ - -#include -#include -#include -#include "sc_disk.h" - -typedef struct { - uint32_t free_clusters; - uint16_t bytes_per_sector; - uint8_t sectors_per_cluster; -} ScDiskInfo; - -static ScDiskInfo sc_disk_raw_info; - -static int sc_disk_info_raw(uint8_t disk) __naked -{ - (void)disk; - __asm - ;; __sdcccall(1): disk в A. - push ix - ld b, #1 ; DSS 1.71: пересчитать free clusters - ld c, #0x03 ; ESTEX DSKINFO - rst #0x10 - jr c, _sc_disk_info_error - ld (_sc_disk_raw_info + 0), de - exx - ld (_sc_disk_raw_info + 2), de - exx - ld (_sc_disk_raw_info + 4), bc - ld (_sc_disk_raw_info + 6), a - pop ix - ld de, #0 - ret - _sc_disk_info_error: - pop ix - call __errno_set - ld de, #-1 - ret - __endasm; -} - -int sc_disk_has_space(const char *path, uint32_t bytes) __banked -{ - uint32_t cluster_bytes; - uint32_t required_clusters; - uint8_t disk; - uint8_t first; - - if (!path) { - errno = EINVAL; - return -1; - } - if (path[0] != '\0' && path[1] == ':') { - first = (uint8_t)path[0]; - if (first >= 'a' && first <= 'z') first -= (uint8_t)('a' - 'A'); - if (first < 'A' || first > 'Z') { - errno = ENODRV; - return -1; - } - disk = (uint8_t)(first - 'A'); - } else { - disk = getdisk(); - } - if (sc_disk_info_raw(disk) != 0) return -1; - if (bytes == 0ul) return 1; - cluster_bytes = (uint32_t)sc_disk_raw_info.bytes_per_sector * - (uint32_t)sc_disk_raw_info.sectors_per_cluster; - if (cluster_bytes == 0ul) { - errno = EMEDIA; - return -1; - } - required_clusters = bytes / cluster_bytes; - if (bytes % cluster_bytes != 0ul) required_clusters++; - return sc_disk_raw_info.free_clusters >= required_clusters ? 1 : 0; -} diff --git a/applications/Volkov/src/sc_draw.c b/applications/Volkov/src/sc_draw.c deleted file mode 100644 index 5db628c..0000000 --- a/applications/Volkov/src/sc_draw.c +++ /dev/null @@ -1,358 +0,0 @@ -/* sc_draw.c — renderer двух независимых файловых панелей, не знающий ESTEX. */ - -#include -#include -#include "sc_app.h" -#include "sc_glyphs.h" -#include "sc_screen.h" -#include "sc_store.h" -#include "sc_theme.h" - -#define PANEL_W 40u -#define PANEL_INNER_W 38u -#define PANEL_TOP 0u -#define PANEL_FIRST 1u -#define PANEL_BOTTOM 28u - -/* Стандартный профиль 1.0: отдельный цвет только для точного `.EXE`. */ -static uint8_t sc_draw_is_exe(const char *name) -{ - const char *extension = 0; - - while (*name != '\0') { - if (*name == '.') extension = name + 1; - name++; - } - if (extension == 0) return 0u; - return (uint8_t)((extension[0] == 'E' || extension[0] == 'e') && - (extension[1] == 'X' || extension[1] == 'x') && - (extension[2] == 'E' || extension[2] == 'e') && - extension[3] == '\0'); -} - -static void sc_draw_dec3(uint8_t x, uint8_t y, uint16_t value, - uint8_t attr) -{ - sc_screen_cell(x, y, (uint8_t)('0' + (value / 100u) % 10u), attr); - sc_screen_cell((uint8_t)(x + 1u), y, - (uint8_t)('0' + (value / 10u) % 10u), attr); - sc_screen_cell((uint8_t)(x + 2u), y, - (uint8_t)('0' + value % 10u), attr); -} - -static void sc_draw_dec5(uint8_t x, uint8_t y, uint16_t value, - uint8_t attr) -{ - uint16_t divisor = 10000u; - uint8_t i; - for (i = 0u; i < 5u; i++) { - sc_screen_cell((uint8_t)(x + i), y, - (uint8_t)('0' + (value / divisor) % 10u), attr); - divisor /= 10u; - } -} - -static void sc_draw_dec2(uint8_t x, uint8_t y, uint8_t value, - uint8_t attr) -{ - sc_screen_cell(x, y, (uint8_t)('0' + (value / 10u) % 10u), attr); - sc_screen_cell((uint8_t)(x + 1u), y, - (uint8_t)('0' + value % 10u), attr); -} - -static void sc_draw_dec4(uint8_t x, uint8_t y, uint16_t value, - uint8_t attr) -{ - uint16_t divisor = 1000u; - uint8_t i; - for (i = 0u; i < 4u; i++) { - sc_screen_cell((uint8_t)(x + i), y, - (uint8_t)('0' + (value / divisor) % 10u), attr); - divisor /= 10u; - } -} - -static void sc_draw_u32_right(uint8_t x, uint8_t y, uint32_t value, - uint8_t width, uint8_t attr) -{ - uint8_t i; - for (i = 0u; i < width; i++) { - uint8_t pos = (uint8_t)(width - i - 1u); - uint8_t ch = (uint8_t)('0' + (uint8_t)(value % 10ul)); - sc_screen_cell((uint8_t)(x + pos), y, ch, attr); - value /= 10ul; - if (value == 0ul) { - while (pos != 0u) { - pos--; - sc_screen_cell((uint8_t)(x + pos), y, ' ', attr); - } - break; - } - } -} - -static void sc_draw_path_tail(uint8_t x, uint8_t y, const char *path, - uint8_t width, uint8_t attr) -{ - uint16_t length = 0u; - const char *shown = path; - - while (path[length] != '\0') length++; - if (length > width) { - shown = &path[length - width + 1u]; - sc_screen_cell(x, y, '<', attr); - sc_screen_text((uint8_t)(x + 1u), y, shown, attr, - (uint8_t)(width - 1u)); - } else { - sc_screen_text(x, y, shown, attr, width); - } -} - -static void sc_draw_date(uint8_t x, uint8_t y, uint16_t date, uint8_t attr) -{ - uint16_t year = (uint16_t)(1980u + (date >> 9)); - uint8_t month = (uint8_t)((date >> 5) & 0x0Fu); - uint8_t day = (uint8_t)(date & 0x1Fu); - - if (date == 0u) { - sc_screen_text(x, y, "----------", attr, 10u); - return; - } - sc_draw_dec4(x, y, year, attr); - sc_screen_cell((uint8_t)(x + 4u), y, '-', attr); - sc_draw_dec2((uint8_t)(x + 5u), y, month, attr); - sc_screen_cell((uint8_t)(x + 7u), y, '-', attr); - sc_draw_dec2((uint8_t)(x + 8u), y, day, attr); -} - -static void sc_draw_time(uint8_t x, uint8_t y, uint16_t time, uint8_t attr) -{ - sc_draw_dec2(x, y, (uint8_t)(time >> 11), attr); - sc_screen_cell((uint8_t)(x + 2u), y, ':', attr); - sc_draw_dec2((uint8_t)(x + 3u), y, - (uint8_t)((time >> 5) & 0x3Fu), attr); -} - -static void sc_draw_frame(uint8_t left) -{ - uint8_t y; - uint8_t right = (uint8_t)(left + PANEL_W - 1u); - - sc_screen_cell(left, PANEL_TOP, SC_G_TL, SC_ATTR_FRAME); - sc_screen_fill((uint8_t)(left + 1u), PANEL_TOP, PANEL_INNER_W, - SC_G_H, SC_ATTR_FRAME); - sc_screen_cell(right, PANEL_TOP, SC_G_TR, SC_ATTR_FRAME); - for (y = PANEL_FIRST; y < PANEL_BOTTOM; y++) { - sc_screen_cell(left, y, SC_G_V, SC_ATTR_FRAME); - sc_screen_cell(right, y, SC_G_V, SC_ATTR_FRAME); - } - sc_screen_cell(left, PANEL_BOTTOM, SC_G_BL, SC_ATTR_FRAME); - sc_screen_fill((uint8_t)(left + 1u), PANEL_BOTTOM, PANEL_INNER_W, - SC_G_H, SC_ATTR_FRAME); - sc_screen_cell(right, PANEL_BOTTOM, SC_G_BR, SC_ATTR_FRAME); -} - -static void sc_draw_title(const ScApp *app, uint8_t panel_index) -{ - uint8_t left = panel_index ? 40u : 0u; - const ScPanel *panel = &app->panels[panel_index]; - - sc_screen_cell((uint8_t)(left + 2u), 0u, - panel_index ? 'R' : 'L', SC_ATTR_TITLE); - sc_draw_path_tail((uint8_t)(left + 4u), 0u, panel->path, - 23u, SC_ATTR_TITLE); - if (app->active_panel == panel_index) { - sc_screen_text((uint8_t)(left + 29u), 0u, "[ACTIVE]", - SC_ATTR_ACTIVE, 8u); - } else { - sc_screen_text((uint8_t)(left + 29u), 0u, "passive ", - SC_ATTR_NORMAL, 9u); - } -} - -static void sc_draw_sort_mode(uint8_t left, const ScPanel *panel) -{ - uint8_t key; - - switch (panel->sort_key) { - case SC_SORT_EXTENSION: key = 'E'; break; - case SC_SORT_SIZE: key = 'S'; break; - case SC_SORT_DATE: key = 'D'; break; - default: key = 'N'; break; - } - sc_screen_cell((uint8_t)(left + 3u), PANEL_BOTTOM, key, SC_ATTR_TITLE); - sc_screen_cell((uint8_t)(left + 4u), PANEL_BOTTOM, - panel->sort_descending ? '-' : '+', SC_ATTR_TITLE); -} - -static void sc_draw_panel_footer(uint8_t left, const ScPanel *panel) -{ - if (panel->selected_count != 0u) { - /* Footer выбора — цельная строка, а не подписи поверх линии рамки. */ - sc_screen_fill((uint8_t)(left + 2u), PANEL_BOTTOM, - 36u, ' ', SC_ATTR_TITLE); - } - sc_draw_sort_mode(left, panel); - if (panel->selected_count != 0u) { - sc_draw_u32_right((uint8_t)(left + 6u), PANEL_BOTTOM, - panel->selected_size, 10u, SC_ATTR_TITLE); - sc_screen_text((uint8_t)(left + 17u), PANEL_BOTTOM, - "bytes in", SC_ATTR_TITLE, 8u); - sc_draw_dec3((uint8_t)(left + 26u), PANEL_BOTTOM, - panel->selected_count, SC_ATTR_TITLE); - sc_screen_text((uint8_t)(left + 30u), PANEL_BOTTOM, - "selected", SC_ATTR_TITLE, 8u); - } else { - sc_draw_dec3((uint8_t)(left + 14u), PANEL_BOTTOM, - panel->count, SC_ATTR_TITLE); - sc_screen_text((uint8_t)(left + 18u), PANEL_BOTTOM, - panel->truncated ? "entries+" : "entries", - SC_ATTR_TITLE, 8u); - } -} - -void sc_draw_panel_row(const ScApp *app, uint8_t panel_index, - uint8_t row) __banked -{ - const ScPanel *panel = &app->panels[panel_index]; - uint8_t left = panel_index ? 41u : 1u; - uint16_t item = (uint16_t)(panel->top + row); - uint8_t y = (uint8_t)(PANEL_FIRST + row); - uint8_t attr; - ScDirEntry entry; - - if (row >= SC_PANEL_VISIBLE) return; - sc_screen_fill(left, y, PANEL_INNER_W, ' ', SC_ATTR_NORMAL); - if (item >= panel->count) return; - - if (sc_store_get(panel, item, &entry) != 0) { - sc_screen_text((uint8_t)(left + 2u), y, "", - SC_ATTR_DIALOG, 13u); - return; - } - - if (entry.flags & SC_ENTRY_DSS_LIMIT) { - attr = (app->active_panel == panel_index && item == panel->cursor) ? - SC_ATTR_LIMIT_ACTIVE : SC_ATTR_LIMIT; - } else if (app->active_panel == panel_index && item == panel->cursor) { - attr = SC_ATTR_ACTIVE; - } else if (entry.attr & FA_DIREC) { - attr = SC_ATTR_DIR; - } else if (sc_draw_is_exe(entry.name)) { - attr = SC_ATTR_EXE; - } else { - attr = SC_ATTR_FILE; - } - sc_screen_fill(left, y, PANEL_INNER_W, ' ', attr); - - sc_screen_cell(left, y, - (app->active_panel == panel_index && - item == panel->cursor) ? '>' : ' ', - attr); - sc_screen_cell((uint8_t)(left + 1u), y, - (entry.flags & SC_ENTRY_SELECTED) ? '*' : ' ', attr); - sc_screen_text((uint8_t)(left + 2u), y, entry.name, attr, 13u); - if (entry.flags & SC_ENTRY_DSS_LIMIT) { - sc_screen_text((uint8_t)(left + 17u), y, "", attr, 11u); - return; - } - if (entry.attr & FA_DIREC) { - sc_screen_text((uint8_t)(left + 17u), y, "", attr, 5u); - } else { - sc_draw_u32_right((uint8_t)(left + 17u), y, entry.size, 10u, attr); - } - sc_screen_cell((uint8_t)(left + 29u), y, - (entry.attr & FA_RDONLY) ? 'R' : '-', attr); - sc_screen_cell((uint8_t)(left + 30u), y, - (entry.attr & FA_HIDDEN) ? 'H' : '-', attr); - sc_screen_cell((uint8_t)(left + 31u), y, - (entry.attr & FA_SYSTEM) ? 'S' : '-', attr); - sc_screen_cell((uint8_t)(left + 32u), y, - (entry.attr & FA_ARCH) ? 'A' : '-', attr); -} - -void sc_draw_panel_rows(const ScApp *app, uint8_t panel_index) __banked -{ - uint8_t row; - for (row = 0u; row < SC_PANEL_VISIBLE; row++) { - sc_draw_panel_row(app, panel_index, row); - } -} - -void sc_draw_info(const ScApp *app) __banked -{ - const ScPanel *panel = &app->panels[app->active_panel]; - ScDirEntry entry; - - sc_screen_fill(0u, 29u, SC_SCREEN_W, ' ', SC_ATTR_STATUS); - sc_screen_cell(1u, 29u, app->active_panel ? 'R' : 'L', SC_ATTR_STATUS); - sc_draw_dec5(3u, 29u, panel->cursor, SC_ATTR_STATUS); - sc_screen_cell(8u, 29u, '/', SC_ATTR_STATUS); - sc_draw_dec5(9u, 29u, panel->count, SC_ATTR_STATUS); - if (sc_panel_selected(panel, &entry) == 0) { - sc_screen_text(15u, 29u, entry.name, SC_ATTR_STATUS, 13u); - if (entry.flags & SC_ENTRY_DSS_LIMIT) { - sc_screen_text(29u, 29u, "", SC_ATTR_STATUS, 11u); - } else if (entry.attr & FA_DIREC) { - sc_screen_text(29u, 29u, " ", SC_ATTR_STATUS, 10u); - } else { - sc_draw_u32_right(29u, 29u, entry.size, 10u, SC_ATTR_STATUS); - } - if (!(entry.flags & SC_ENTRY_DSS_LIMIT)) { - sc_draw_date(40u, 29u, entry.date, SC_ATTR_STATUS); - sc_draw_time(51u, 29u, entry.time, SC_ATTR_STATUS); - sc_screen_cell(57u, 29u, - (entry.attr & FA_RDONLY) ? 'R' : '-', SC_ATTR_STATUS); - sc_screen_cell(58u, 29u, - (entry.attr & FA_HIDDEN) ? 'H' : '-', SC_ATTR_STATUS); - sc_screen_cell(59u, 29u, - (entry.attr & FA_SYSTEM) ? 'S' : '-', SC_ATTR_STATUS); - sc_screen_cell(60u, 29u, - (entry.attr & FA_ARCH) ? 'A' : '-', SC_ATTR_STATUS); - } - } - sc_screen_text(61u, 29u, - panel->selected_count ? "sel" : "sum", - SC_ATTR_STATUS, 3u); - sc_draw_u32_right(65u, 29u, - panel->selected_count ? panel->selected_size : - panel->total_size, - 10u, SC_ATTR_STATUS); - if (panel->selected_count) { - sc_screen_cell(75u, 29u, '/', SC_ATTR_STATUS); - sc_draw_dec3(76u, 29u, panel->selected_count, SC_ATTR_STATUS); - } - if (panel->truncated) sc_screen_cell(79u, 29u, '+', SC_ATTR_DIALOG); -} - -void sc_draw_status(const ScApp *app) __banked -{ - sc_screen_fill(0u, 30u, SC_SCREEN_W, ' ', SC_ATTR_STATUS); - sc_screen_text(1u, 30u, app->status, SC_ATTR_STATUS, 78u); -} - -static void sc_draw_keys(void) -{ - sc_screen_fill(0u, 31u, SC_SCREEN_W, ' ', SC_ATTR_KEYS); - sc_screen_text(0u, 31u, - "1 Help 2 Menu 3 View 4 Edit 5 Copy 6 Ren 7 Mkdir 8 Delete9 Menu 10 Quit", - SC_ATTR_KEYS, SC_SCREEN_W); -} - -void sc_draw_all(const ScApp *app) __banked -{ - uint8_t p; - - sc_screen_clear(SC_ATTR_NORMAL); - for (p = 0u; p < SC_PANEL_COUNT; p++) { - const ScPanel *panel = &app->panels[p]; - uint8_t left = p ? 40u : 0u; - sc_draw_frame(p ? 40u : 0u); - sc_draw_title(app, p); - sc_draw_panel_rows(app, p); - sc_draw_panel_footer(left, panel); - } - sc_draw_info(app); - sc_draw_status(app); - sc_draw_keys(); -} diff --git a/applications/Volkov/src/sc_exec_ui.c b/applications/Volkov/src/sc_exec_ui.c deleted file mode 100644 index 82c39ca..0000000 --- a/applications/Volkov/src/sc_exec_ui.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * sc_exec_ui.c — запуск EXE из панели и полное восстановление UI. - * - * EXEC синхронен. Пока copy-job не активен, его строковые поля служат - * scratch-буферами пути запуска и сохранённого CWD; это экономит 512 байт - * W2 и безопасно, потому что диспетчер Commander не выполняет jobs параллельно. - */ - -#include -#include -#include -#include -#include -#include "sc_app.h" -#include "sc_exec.h" -#include "sc_exec_ui.h" -#include "sc_path.h" -#include "sc_screen.h" -#include "sc_theme.h" -#include "sc_video.h" - -static char sc_exec_status_text[79]; - -static void sc_exec_status_plain(ScApp *app, const char *text) -{ - char *out = sc_exec_status_text; - char *limit = sc_exec_status_text + sizeof(sc_exec_status_text) - 1u; - - while (*text != '\0' && out < limit) *out++ = *text++; - *out = '\0'; - app->status = sc_exec_status_text; -} - -static uint8_t sc_exec_upper(uint8_t ch) -{ - if (ch >= 'a' && ch <= 'z') return (uint8_t)(ch - ('a' - 'A')); - return ch; -} - -static uint8_t sc_exec_is_exe(const char *name) -{ - uint8_t length = 0u; - - while (name[length] != '\0' && length < 12u) length++; - if (length < 4u || name[length - 4u] != '.') return 0u; - return sc_exec_upper((uint8_t)name[length - 3u]) == 'E' && - sc_exec_upper((uint8_t)name[length - 2u]) == 'X' && - sc_exec_upper((uint8_t)name[length - 1u]) == 'E'; -} - -static char *sc_exec_status_append(char *out, const char *text) -{ - char *limit = sc_exec_status_text + sizeof(sc_exec_status_text) - 1u; - - while (*text != '\0' && out < limit) *out++ = *text++; - *out = '\0'; - return out; -} - -static char *sc_exec_status_uint(char *out, uint16_t value) -{ - char digits[5]; - uint8_t count = 0u; - - do { - digits[count++] = (char)('0' + value % 10u); - value /= 10u; - } while (value != 0u); - while (count != 0u) { - char one[2]; - one[0] = digits[--count]; - one[1] = '\0'; - out = sc_exec_status_append(out, one); - } - return out; -} - -static void sc_exec_status_error(ScApp *app, const char *name, int error) -{ - char *out = sc_exec_status_text; - - out = sc_exec_status_append(out, "Cannot execute "); - out = sc_exec_status_append(out, name); - out = sc_exec_status_append(out, " (errno "); - out = sc_exec_status_uint(out, (uint16_t)error); - (void)sc_exec_status_append(out, ")."); - app->status = sc_exec_status_text; -} - -static void sc_exec_status_unsupported(ScApp *app, const char *name) -{ - char *out = sc_exec_status_text; - - out = sc_exec_status_append(out, "Cannot execute "); - out = sc_exec_status_append(out, name); - (void)sc_exec_status_append(out, ": only .EXE files are supported."); - app->status = sc_exec_status_text; -} - -static void sc_exec_status_success(ScApp *app, int exit_code) -{ - static const char hex[] = "0123456789ABCDEF"; - char *out = sc_exec_status_text; - char code[3]; - - code[0] = hex[((uint8_t)exit_code >> 4) & 0x0Fu]; - code[1] = hex[(uint8_t)exit_code & 0x0Fu]; - code[2] = '\0'; - out = sc_exec_status_append(out, "Program returned "); - out = sc_exec_status_append(out, code); - (void)sc_exec_status_append(out, "; screen, CWD and panels restored."); - app->status = sc_exec_status_text; -} - -static void sc_exec_redraw(ScApp *app) -{ - sc_draw_all(app); - (void)sc_screen_present_full(); -} - -void sc_exec_selected(ScApp *app) __banked -{ - ScPanel *panel = &app->panels[app->active_panel]; - ScDirEntry entry; - ScCopyJob *scratch = &app->copy_job; - uint8_t saved_drive; - int exit_code; - int exec_errno = 0; - int restore_errno = 0; - int video_errno = 0; - int refresh_errno = 0; - uint8_t i; - - if (sc_panel_selected(panel, &entry) != 0 || - (entry.attr & FA_DIREC) != 0u || - (entry.flags & SC_ENTRY_DSS_LIMIT) != 0u) { - sc_exec_status_plain(app, - "Cannot execute: the selected entry is not a file."); - sc_exec_redraw(app); - return; - } - if (!sc_exec_is_exe(entry.name)) { - sc_exec_status_unsupported(app, entry.name); - sc_exec_redraw(app); - return; - } - if (scratch->state == SC_COPY_RUNNING) { - sc_exec_status_plain(app, - "Cannot execute while a copy job is active."); - sc_exec_redraw(app); - return; - } - - saved_drive = getdisk(); - if (!getcwd(scratch->target_path, SC_COPY_PATH_CAPACITY) || - sc_path_join(scratch->source_path, SC_COPY_PATH_CAPACITY, - panel->path, entry.name) != 0) { - sc_exec_status_error(app, entry.name, errno); - sc_exec_redraw(app); - return; - } - - /* Как в классическом Commander, child получает каталог активной панели - * текущим. Это позволяет утилитам без argv открывать соседние файлы. - * Полный путь EXE уже построен; исходный CWD восстановится после EXEC. */ - if (chdir(panel->path) != 0) { - sc_exec_status_error(app, entry.name, errno); - sc_exec_redraw(app); - return; - } - - sc_exec_status_plain(app, - "Starting selected EXE; Commander state is preserved."); - sc_draw_status(app); - (void)sc_screen_present_rows(30u, 1u); - - errno = 0; - exit_code = sc_platform_exec(scratch->source_path); - if (exit_code < 0) exec_errno = errno; - - if (setdisk(saved_drive) < 0) { - restore_errno = errno; - } else if (chdir(scratch->target_path) != 0) { - restore_errno = errno; - } - if (sc_video_restore() != 0) video_errno = errno; - sc_theme_reapply(); - - for (i = 0u; i < SC_PANEL_COUNT; i++) { - if (sc_panel_refresh(&app->panels[i]) != 0 && refresh_errno == 0) { - refresh_errno = errno; - } - } - - if (restore_errno != 0) { - sc_exec_status_error(app, "CWD restore", restore_errno); - } else if (video_errno != 0) { - sc_exec_status_error(app, "video restore", video_errno); - } else if (exit_code < 0) { - sc_exec_status_error(app, entry.name, exec_errno); - } else if (refresh_errno != 0) { - sc_exec_status_error(app, "panel refresh", refresh_errno); - } else { - sc_exec_status_success(app, exit_code); - } - sc_exec_redraw(app); -} diff --git a/applications/Volkov/src/sc_file_ui.c b/applications/Volkov/src/sc_file_ui.c deleted file mode 100644 index ec2139f..0000000 --- a/applications/Volkov/src/sc_file_ui.c +++ /dev/null @@ -1,407 +0,0 @@ -/* - * sc_file_ui.c — оркестрация безопасных файловых команд версии 0.2. - * - * F7 принимает только один компонент DOS 8.3, строит полный путь в - * резидентном scratch copy job и не меняет глобальный CWD DSS. - */ - -#include -#include -#include -#include -#include "sc_file_ui.h" -#include "sc_file_ops.h" -#include "sc_input.h" -#include "sc_path.h" -#include "sc_screen.h" -#include "sc_store.h" -#include "sc_tree.h" - -#define SC_DELETE_TREE_ERROR (-1) -#define SC_DELETE_TREE_DONE 0 -#define SC_DELETE_TREE_CANCELLED 1 -#define SC_DELETE_DSS_LIMIT 35 - -static void sc_file_redraw(ScApp *app) -{ - sc_draw_all(app); - (void)sc_screen_present_full(); -} - -static uint8_t sc_file_upper(uint8_t ch) -{ - if (ch >= 'a' && ch <= 'z') return (uint8_t)(ch - ('a' - 'A')); - return ch; -} - -static uint8_t sc_file_same_name(const char *left, const char *right) -{ - while (*left != '\0' && *right != '\0') { - if (sc_file_upper((uint8_t)*left) != - sc_file_upper((uint8_t)*right)) return 0u; - left++; - right++; - } - return *left == *right; -} - -static void sc_file_copy_name(char *target, const char *source) -{ - uint8_t i = 0u; - while (i < 12u && source[i] != '\0') { - target[i] = source[i]; - i++; - } - target[i] = '\0'; -} - -static void sc_file_focus_name(ScPanel *panel, const char *name) -{ - ScDirEntry entry; - uint16_t i; - - for (i = 0u; i < panel->count; i++) { - if (sc_store_get(panel, i, &entry) != 0) break; - if (sc_file_same_name(entry.name, name)) { - panel->cursor = i; - sc_panel_normalize_view(panel); - return; - } - } -} - -void sc_mkdir_command(ScApp *app) __banked -{ - ScPanel *panel = &app->panels[app->active_panel]; - char *name = app->copy_job.source_path; - char *path = app->copy_job.target_path; - - name[0] = '\0'; - if (!sc_dialog_input(" Make directory ", "New DOS 8.3 name:", - name, 13u)) { - app->status = "Create directory cancelled."; - sc_file_redraw(app); - return; - } - if (sc_path_validate_component(name) != 0 || - sc_path_join(path, SC_COPY_PATH_CAPACITY, panel->path, name) != 0) { - app->status = "Invalid directory name; one DOS 8.3 component required."; - sc_file_redraw(app); - return; - } - - errno = 0; - if (mkdir(path) != 0) { - app->status = (errno == EEXIST || errno == EISDIR) ? - "Cannot create directory: it already exists." : - "Cannot create directory; DSS rejected the request."; - sc_file_redraw(app); - return; - } - - if (sc_panel_refresh(panel) != 0) { - app->status = "Directory created, but panel refresh failed."; - } else { - app->status = "Directory created; active panel refreshed."; - } - sc_file_redraw(app); -} - -void sc_rename_command(ScApp *app) __banked -{ - ScPanel *panel = &app->panels[app->active_panel]; - ScDirEntry entry; - char *new_name = app->copy_job.source_path; - int result; - int saved_errno; - - if (sc_panel_selected(panel, &entry) != 0 || - (entry.flags & (SC_ENTRY_PARENT | SC_ENTRY_DSS_LIMIT))) { - app->status = "This virtual entry cannot be renamed."; - sc_file_redraw(app); - return; - } - if (panel->selected_count != 0u && - !(panel->selected_count == 1u && - (entry.flags & SC_ENTRY_SELECTED))) { - app->status = "Group move/rename is not implemented yet."; - sc_file_redraw(app); - return; - } - - sc_file_copy_name(new_name, entry.name); - if (!sc_dialog_input(" Rename ", "New DOS 8.3 name:", - new_name, 13u)) { - app->status = "Rename cancelled."; - sc_file_redraw(app); - return; - } - if (sc_path_validate_component(new_name) != 0) { - app->status = "Invalid name; one DOS 8.3 component required."; - sc_file_redraw(app); - return; - } - if (sc_file_same_name(entry.name, new_name)) { - app->status = "Rename skipped: the name is unchanged."; - sc_file_redraw(app); - return; - } - - errno = 0; - result = sc_rename_local(panel->path, entry.name, new_name, - app->copy_job.temp_path, - SC_COPY_PATH_CAPACITY); - saved_errno = errno; - if (result == -2) { - app->status = "Rename attempted, but DSS CWD restore failed."; - sc_file_redraw(app); - return; - } - if (result != 0) { - app->status = (saved_errno == EEXIST || saved_errno == EISDIR) ? - "Cannot rename: target already exists." : - "Cannot rename; DSS rejected the request."; - sc_file_redraw(app); - return; - } - - if (sc_panel_refresh(panel) != 0) { - app->status = "Renamed, but panel refresh failed."; - } else { - sc_file_focus_name(panel, new_name); - app->status = "Renamed in active directory."; - } - sc_file_redraw(app); -} - -static int sc_file_count_selected(const ScPanel *panel, - uint16_t *files, - uint16_t *directories) -{ - ScDirEntry entry; - uint16_t i; - - *files = 0u; - *directories = 0u; - for (i = 0u; i < panel->count; i++) { - if (sc_store_get(panel, i, &entry) != 0) return -1; - if (!(entry.flags & SC_ENTRY_SELECTED)) continue; - if (entry.attr & FA_DIREC) (*directories)++; - else if (!(entry.flags & SC_ENTRY_DSS_LIMIT)) (*files)++; - } - return 0; -} - -static void sc_file_redraw_info(ScApp *app) -{ - sc_draw_info(app); - sc_draw_status(app); - (void)sc_screen_present_rows(29u, 2u); -} - -static int sc_delete_tree(ScApp *app, ScPanel *panel, - uint16_t *deleted_files, - uint16_t *deleted_directories, - int *operation_error) -{ - ScTreeJob tree; - ScDirEntry entry; - ScKeyEvent event; - uint16_t i; - int step = SC_TREE_STEP_DONE; - int result = SC_DELETE_TREE_DONE; - - if (sc_tree_begin(&tree, &app->copy_job) != 0) { - *operation_error = errno; - return SC_DELETE_TREE_ERROR; - } - for (i = 0u; i < panel->count; i++) { - if (sc_store_get(panel, i, &entry) != 0) { - *operation_error = ENOMEM; - step = SC_TREE_STEP_ERROR; - result = SC_DELETE_TREE_ERROR; - break; - } - if (!(entry.flags & SC_ENTRY_SELECTED) || - !(entry.attr & FA_DIREC) || - (entry.flags & (SC_ENTRY_PARENT | SC_ENTRY_DSS_LIMIT))) continue; - if (sc_tree_add_delete_root(&tree, entry.name) != 0) { - *operation_error = errno; - step = SC_TREE_STEP_ERROR; - result = SC_DELETE_TREE_ERROR; - break; - } - } - - while (step != SC_TREE_STEP_ERROR) { - if (sc_input_poll(&event) && event.ascii == 0x1Bu) { - result = SC_DELETE_TREE_CANCELLED; - break; - } - step = sc_tree_delete_next(&tree, &app->copy_job, - panel->path, &entry); - if (step == SC_TREE_STEP_DONE) break; - if (step == SC_TREE_STEP_ERROR) { - *operation_error = tree.error; - result = SC_DELETE_TREE_ERROR; - break; - } - if (step == SC_TREE_STEP_SCAN_DIR) { - app->status = "Scanning directory tree for delete; Esc cancels."; - sc_file_redraw_info(app); - } else if (step == SC_TREE_STEP_FILE) { - if (unlink(app->copy_job.target_path) != 0) { - *operation_error = errno; - result = SC_DELETE_TREE_ERROR; - break; - } - (*deleted_files)++; - } else { - if (rmdir(app->copy_job.target_path) != 0) { - *operation_error = errno; - result = SC_DELETE_TREE_ERROR; - break; - } - (*deleted_directories)++; - } - } - - if (sc_tree_finish(&tree, &app->copy_job) != 0 && - *operation_error == 0) { - *operation_error = errno; - result = SC_DELETE_TREE_ERROR; - } - return result; -} - -static void sc_delete_group(ScApp *app, ScPanel *panel) -{ - ScDirEntry entry; - char *path = app->copy_job.target_path; - uint16_t files; - uint16_t directories; - uint16_t deleted = 0u; - uint16_t deleted_directories = 0u; - uint16_t i; - uint8_t failed = 0u; - uint8_t cancelled = 0u; - int operation_error = 0; - - if (sc_file_count_selected(panel, &files, &directories) != 0) { - app->status = "Group delete failed: EMM access error."; - sc_file_redraw(app); - return; - } - if (files == 0u && directories == 0u) { - app->status = "No real selected entries to delete."; - sc_file_redraw(app); - return; - } - if (!sc_dialog_confirm(" Delete group ", - directories ? - "Delete selected files and trees?" : - "Delete all selected files?")) { - app->status = "Group delete cancelled; nothing changed."; - sc_file_redraw(app); - return; - } - - for (i = 0u; i < panel->count; i++) { - if (sc_store_get(panel, i, &entry) != 0) { - failed = 1u; - operation_error = ENOMEM; - break; - } - if (!(entry.flags & SC_ENTRY_SELECTED) || - (entry.attr & FA_DIREC) || - (entry.flags & SC_ENTRY_DSS_LIMIT)) continue; - if (sc_path_join(path, SC_COPY_PATH_CAPACITY, - panel->path, entry.name) != 0) { - failed = 1u; - operation_error = errno; - break; - } - if (unlink(path) != 0) { - failed = 1u; - operation_error = errno; - break; - } - deleted++; - } - - if (!failed && directories != 0u) { - int tree_result = sc_delete_tree(app, panel, &deleted, - &deleted_directories, - &operation_error); - if (tree_result == SC_DELETE_TREE_CANCELLED) cancelled = 1u; - else if (tree_result == SC_DELETE_TREE_ERROR) failed = 1u; - } - - if ((deleted != 0u || deleted_directories != 0u) && - sc_panel_refresh(panel) != 0) { - app->status = "Objects deleted, but panel refresh failed."; - } else if (cancelled) { - app->status = "Recursive delete cancelled; completed changes remain."; - } else if (failed) { - app->status = operation_error == SC_DELETE_DSS_LIMIT ? - "Recursive delete stopped at DSS directory limit; changes remain." : - "Group delete stopped on first error; completed changes remain."; - } else if (directories != 0u) { - app->status = "Selected files and directory trees deleted."; - } else { - app->status = "Selected files deleted."; - } - sc_file_redraw(app); -} - -void sc_delete_command(ScApp *app) __banked -{ - ScPanel *panel = &app->panels[app->active_panel]; - ScDirEntry entry; - char *path = app->copy_job.target_path; - int result; - int saved_errno; - - if (panel->selected_count != 0u) { - sc_delete_group(app, panel); - return; - } - if (sc_panel_selected(panel, &entry) != 0 || - (entry.flags & (SC_ENTRY_PARENT | SC_ENTRY_DSS_LIMIT))) { - app->status = "This virtual entry cannot be deleted."; - sc_file_redraw(app); - return; - } - if (sc_path_join(path, SC_COPY_PATH_CAPACITY, - panel->path, entry.name) != 0) { - app->status = "Cannot build the path to delete."; - sc_file_redraw(app); - return; - } - if (!sc_dialog_confirm(" Delete ", - (entry.attr & FA_DIREC) ? - "Delete this empty directory?" : - "Delete this file?")) { - app->status = "Delete cancelled; nothing changed."; - sc_file_redraw(app); - return; - } - - errno = 0; - result = (entry.attr & FA_DIREC) ? rmdir(path) : unlink(path); - saved_errno = errno; - if (result != 0) { - app->status = (saved_errno == ENOTEMPTY) ? - "Cannot delete: directory is not empty." : - "Cannot delete; DSS rejected the request."; - sc_file_redraw(app); - return; - } - if (sc_panel_refresh(panel) != 0) { - app->status = "Deleted, but panel refresh failed."; - } else { - app->status = (entry.attr & FA_DIREC) ? - "Empty directory deleted." : "File deleted."; - } - sc_file_redraw(app); -} diff --git a/applications/Volkov/src/sc_input.c b/applications/Volkov/src/sc_input.c deleted file mode 100644 index 7d8849d..0000000 --- a/applications/Volkov/src/sc_input.c +++ /dev/null @@ -1,35 +0,0 @@ -/* sc_input.c — нормализация getkey() и live-модификаторов DSS. */ - -#include -#include -#include "sc_input.h" - -static uint8_t sc_compact_modifiers(uint16_t state) -{ - uint8_t result = 0u; - if (state & (KBD_MOD_LSHIFT | KBD_MOD_RSHIFT)) result |= SC_MOD_SHIFT; - if (state & (KBD_MOD_CTRL | KBD_MOD_LCTRL | KBD_MOD_RCTRL)) { - result |= SC_MOD_CTRL; - } - if (state & (KBD_MOD_ALT | KBD_MOD_LALT | KBD_MOD_RALT)) { - result |= SC_MOD_ALT; - } - return result; -} - -void sc_input_read(ScKeyEvent *event) -{ - uint16_t key = getkey(); - uint16_t state = kbd_mod_state(); - - event->ascii = (uint8_t)key; - event->scan = (uint8_t)(key >> 8) & 0x7Fu; - event->modifiers = sc_compact_modifiers(state); -} - -uint8_t sc_input_poll(ScKeyEvent *event) -{ - if (!kbhit()) return 0u; - sc_input_read(event); - return 1u; -} diff --git a/applications/Volkov/src/sc_metadata.c b/applications/Volkov/src/sc_metadata.c deleted file mode 100644 index fbdaf2d..0000000 --- a/applications/Volkov/src/sc_metadata.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * sc_metadata.c — запись FAT date/time и атрибутов через DSS. - * - * PUT_D_T ($18) принимает раскрытые поля даты/времени по открытому handle; - * изменение фиксируется при CLOSE. ATTRIB ($16) вызывается после commit и - * получает полный путь. Оба wrapper сохраняют IX и переводят CF/A в errno. - */ - -#include -#include -#include "sc_metadata.h" - -typedef struct { - uint8_t day; - uint8_t month; - uint16_t year; - uint8_t hour; - uint8_t minute; - uint8_t second; -} ScMetadataDateTime; - -static int sc_metadata_put_raw(uint8_t fd, - const ScMetadataDateTime *value) __naked -{ - (void)fd; - (void)value; - __asm - ;; __sdcccall(1): fd в A, value в DE. - push ix ; IX caller - push af ; handle - push de - pop ix ; IX = value - - ld c, 2 (ix) ; год сначала сохраняем на стеке - ld b, 3 (ix) - push bc - ld d, 0 (ix) ; день - ld e, 1 (ix) ; месяц - ld h, 4 (ix) ; час - ld l, 5 (ix) ; минуты - ld b, 6 (ix) ; секунды - pop ix ; IX = год - pop af ; A = handle - ld c, #0x18 ; ESTEX PUT_D_T - rst #0x10 - jr c, _sc_metadata_put_error - pop ix - ld de, #0 - ret - _sc_metadata_put_error: - pop ix - call __errno_set - ld de, #-1 - ret - __endasm; -} - -int sc_metadata_set_datetime(uint8_t fd, uint16_t date, - uint16_t time) __banked -{ - ScMetadataDateTime value; - - value.day = (uint8_t)(date & 0x1Fu); - value.month = (uint8_t)((date >> 5) & 0x0Fu); - value.year = (uint16_t)(1980u + (date >> 9)); - value.hour = (uint8_t)(time >> 11); - value.minute = (uint8_t)((time >> 5) & 0x3Fu); - value.second = (uint8_t)((time & 0x1Fu) << 1); - - if (value.day == 0u || value.month == 0u || value.month > 12u || - value.hour > 23u || value.minute > 59u || value.second > 59u) { - errno = EINVAL; - return -1; - } - return sc_metadata_put_raw(fd, &value); -} - -static int sc_metadata_attr_raw(const char *path, uint8_t attr) __naked -{ - (void)path; - (void)attr; - __asm - ;; __sdcccall(1): path в HL, 8-битный attr в [SP+2]. - ld iy, #2 - add iy, sp - ld a, 0 (iy) - push ix - and #0x27 ; readonly/hidden/system/archive - ld b, #1 ; SET - ld c, #0x16 ; ESTEX ATTRIB - rst #0x10 - pop ix - pop hl ; адрес возврата - inc sp ; снять однобайтовый attr - jr c, _sc_metadata_attr_raw_error - ld de, #0 - jp (hl) - _sc_metadata_attr_raw_error: - call __errno_set - ld de, #-1 - jp (hl) - __endasm; -} - -static int sc_metadata_attr_get_raw(const char *path, - uint8_t *attr) __naked -{ - (void)path; - (void)attr; - __asm - ;; __sdcccall(1): path в HL, attr в DE. DSS клобберит регистры, - ;; поэтому near-указатель переживает RST на стеке. - push ix - push de - ld b, #0 ; GET - ld c, #0x16 ; ESTEX ATTRIB - rst #0x10 - pop de - pop ix - jr c, _sc_metadata_attr_get_error - ld (de), a - ld de, #0 - ret - _sc_metadata_attr_get_error: - call __errno_set - ld de, #-1 - ret - __endasm; -} - -int sc_metadata_get_attributes(const char *path, uint8_t *attr) __banked -{ - if (!path || !attr) { - errno = EINVAL; - return -1; - } - return sc_metadata_attr_get_raw(path, attr); -} - -int sc_metadata_set_attributes(const char *path, uint8_t attr) __banked -{ - return sc_metadata_attr_raw(path, (uint8_t)(attr & 0x27u)); -} diff --git a/applications/Volkov/src/sc_name.c b/applications/Volkov/src/sc_name.c deleted file mode 100644 index e6730cd..0000000 --- a/applications/Volkov/src/sc_name.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * sc_name.c — строгая проверка одного пользовательского компонента DOS 8.3. - * - * Разделители, wildcard и неоднозначные `.`/`..` запрещены до вызова DSS; - * CP866-байты 80h..FFh разрешены как буквы имени. - */ - -#include -#include -#include "sc_path.h" - -static uint8_t sc_name_char(uint8_t ch) -{ - if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || - (ch >= '0' && ch <= '9') || ch >= 0x80u) return 1u; - switch (ch) { - case '_': case '-': case '$': case '~': case '!': case '#': - case '%': case '&': case '\'': case '(': case ')': case '@': - case '^': case '`': case '{': case '}': - return 1u; - default: - return 0u; - } -} - -int sc_path_validate_component(const char *name) __banked -{ - uint8_t base = 0u; - uint8_t extension = 0u; - uint8_t have_dot = 0u; - uint8_t ch; - - if (!name || name[0] == '\0') goto invalid; - while ((ch = (uint8_t)*name++) != 0u) { - if (ch == '.') { - if (have_dot || base == 0u) goto invalid; - have_dot = 1u; - } else { - if (!sc_name_char(ch)) goto invalid; - if (have_dot) { - if (++extension > 3u) goto invalid; - } else if (++base > 8u) { - goto invalid; - } - } - } - if (base == 0u || (have_dot && extension == 0u)) goto invalid; - return 0; - -invalid: - errno = EINAME; - return -1; -} diff --git a/applications/Volkov/src/sc_panel.c b/applications/Volkov/src/sc_panel.c deleted file mode 100644 index 49135d7..0000000 --- a/applications/Volkov/src/sc_panel.c +++ /dev/null @@ -1,63 +0,0 @@ -/* sc_panel.c — инварианты панели и переходы по настоящим каталогам. */ - -#include -#include -#include -#include "sc_panel.h" -#include "sc_source.h" -#include "sc_store.h" - -void sc_panel_normalize_view(ScPanel *panel) -{ - if (panel->count == 0u) { - panel->cursor = 0u; - panel->top = 0u; - return; - } - if (panel->cursor >= panel->count) panel->cursor = panel->count - 1u; - if (panel->top > panel->cursor) panel->top = panel->cursor; - if (panel->cursor >= (uint16_t)(panel->top + 27u)) { - panel->top = (uint16_t)(panel->cursor - 26u); - } -} - -int sc_panel_init(ScPanel *panel, uint8_t phys_page) -{ - panel->phys_page = phys_page; - panel->source_kind = SC_SOURCE_REAL_FS; - panel->count = 0u; - panel->cursor = 0u; - panel->top = 0u; - panel->total_size = 0ul; - panel->truncated = 0u; - panel->dirty = 1u; - panel->sort_key = SC_SORT_NAME; - panel->sort_descending = 0u; - if (!getcwd(panel->path, (uint16_t)sizeof(panel->path))) return -1; - return sc_source_fs_scan(panel); -} - -int sc_panel_refresh(ScPanel *panel) -{ - return sc_source_fs_scan(panel); -} - -int sc_panel_selected(const ScPanel *panel, ScDirEntry *entry) -{ - if (panel->count == 0u) return -1; - return sc_store_get(panel, panel->cursor, entry); -} - -int sc_panel_open_selected(ScPanel *panel) -{ - ScDirEntry entry; - if (sc_panel_selected(panel, &entry) != 0) return -1; - if (entry.flags & SC_ENTRY_DSS_LIMIT) return 2; - if (!(entry.attr & FA_DIREC)) return 1; - return sc_source_fs_change_dir(panel, entry.name); -} - -int sc_panel_parent(ScPanel *panel) -{ - return sc_source_fs_change_dir(panel, ".."); -} diff --git a/applications/Volkov/src/sc_path.c b/applications/Volkov/src/sc_path.c deleted file mode 100644 index 16fa04c..0000000 --- a/applications/Volkov/src/sc_path.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * sc_path.c — построение завершённого абсолютного/корневого пути. - * - * Функция не предполагает наличие или отсутствие завершающего `\` у - * каталога и до записи имени проверяет вместимость всего результата. - */ - -#include -#include -#include "sc_path.h" - -int sc_path_join(char *out, uint16_t capacity, - const char *directory, const char *name) -{ - uint16_t dir_len = 0u; - uint16_t name_len = 0u; - uint16_t pos; - uint8_t need_separator; - - if (!out || !directory || !name || capacity == 0u || name[0] == '\0') { - errno = EINVAL; - return -1; - } - while (directory[dir_len] != '\0') dir_len++; - while (name[name_len] != '\0') name_len++; - need_separator = dir_len != 0u && directory[dir_len - 1u] != '\\' && - directory[dir_len - 1u] != '/'; - if (dir_len + (uint16_t)need_separator + name_len + 1u > capacity) { - errno = EINAME; - return -1; - } - - for (pos = 0u; pos < dir_len; pos++) out[pos] = directory[pos]; - if (need_separator) out[pos++] = '\\'; - for (name_len = 0u; name[name_len] != '\0'; name_len++) { - out[pos++] = name[name_len]; - } - out[pos] = '\0'; - return 0; -} diff --git a/applications/Volkov/src/sc_platform_exec.c b/applications/Volkov/src/sc_platform_exec.c deleted file mode 100644 index 8dc9c9f..0000000 --- a/applications/Volkov/src/sc_platform_exec.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * sc_platform_exec.c — минимальная обёртка ESTEX EXEC $40. - * - * Вход: HL = ASCIIZ-путь в W1..W3. B=1 сообщает DSS, что передан явный - * путь. На успехе функция возвращает код завершения 0..255 как int в DE; - * на ошибке — -1 и errno. IX сохраняется согласно ABI SDCC. - */ - -#include -#include "sc_exec.h" - -int sc_platform_exec(const char *path) __naked -{ - (void)path; - __asm - push ix - ld b, #1 - ld c, #0x40 - rst #0x10 - pop ix - jr c, _sc_platform_exec_error - ld e, a - ld d, #0 - ret - _sc_platform_exec_error: - call __errno_set - ld de, #-1 - ret - __endasm; -} diff --git a/applications/Volkov/src/sc_platform_screen.c b/applications/Volkov/src/sc_platform_screen.c deleted file mode 100644 index afc0845..0000000 --- a/applications/Volkov/src/sc_platform_screen.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * sc_platform_screen.c — app-local wrapper ESTEX WINREST 5Ah. - * - * ABI SDCC __sdcccall(1): row в A, col в L, остальные аргументы на стеке. - * IX callee-saved. Источник — физическая EMM-страница B и 0xC000+offset. - */ - -#include -#include "sc_video.h" - -void sc_platform_winrest(uint8_t row, uint8_t col, - uint8_t height, uint8_t width, - uint8_t page, uint16_t offset) __naked -{ - (void)row; - (void)col; - (void)height; - (void)width; - (void)page; - (void)offset; - __asm - ld iy, #2 - add iy, sp - ld d, a - ld e, l - - push ix - ld l, 3 (iy) - ld h, 4 (iy) - ld bc, #0xC000 - add hl, bc - push hl - pop ix - - ld h, 0 (iy) - ld a, 1 (iy) - ld l, a - ld a, 2 (iy) - ld b, a - ld c, #0x5A - di - rst #0x10 - ei - pop ix - - pop hl - inc sp - inc sp - inc sp - inc sp - inc sp - jp (hl) - __endasm; -} diff --git a/applications/Volkov/src/sc_rename.c b/applications/Volkov/src/sc_rename.c deleted file mode 100644 index a6292b7..0000000 --- a/applications/Volkov/src/sc_rename.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * sc_rename.c — локальный RENAME DSS с обязательным возвратом CWD. - * - * DSS 1.71 принимает два basename только относительно текущего каталога. - * Все строки и scratch принадлежат вызывающему W2-коду; банковый модуль не - * публикует указателей на свои данные и не хранит глобального состояния. - */ - -#include -#include -#include -#include -#include -#include "sc_file_ops.h" - -#define SC_RENAME_FIND_ATTRS \ - (FA_RDONLY | FA_HIDDEN | FA_SYSTEM | FA_DIREC | FA_ARCH) - -int sc_rename_local(const char *directory, - const char *old_name, - const char *new_name, - char *cwd_buffer, - uint16_t cwd_capacity) __banked -{ - ffblk_t found; - int operation_result; - int operation_errno; - - if (!directory || !old_name || !new_name || !cwd_buffer || - old_name[0] == '\0' || new_name[0] == '\0' || cwd_capacity < 2u) { - errno = EINVAL; - return -1; - } - if (!getcwd(cwd_buffer, cwd_capacity)) return -1; - if (chdir(directory) != 0) return -1; - - errno = 0; - if (ffirst(new_name, &found, SC_RENAME_FIND_ATTRS) == 0) { - operation_result = -1; - operation_errno = EEXIST; - } else if (errno != ENOENT) { - operation_result = -1; - operation_errno = errno; - } else { - errno = 0; - operation_result = rename(old_name, new_name); - operation_errno = errno; - } - if (chdir(cwd_buffer) != 0) return -2; - if (operation_result != 0) { - errno = operation_errno ? operation_errno : EUNKERR; - return -1; - } - return 0; -} diff --git a/applications/Volkov/src/sc_screen.c b/applications/Volkov/src/sc_screen.c deleted file mode 100644 index c921f6e..0000000 --- a/applications/Volkov/src/sc_screen.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * sc_screen.c — единственный владелец экранной EMM-модели. - * - * Указатель 0xC000 существует только внутри короткой операции; исходное - * отображение W3 восстанавливается до возврата вызывающему коду. - */ - -#include -#include -#include -#include "sc_screen.h" -#include "sc_video.h" - -static uint8_t sc_screen_page; -static uint8_t sc_screen_attached; - -static uint16_t sc_cell_offset(uint8_t x, uint8_t y) -{ - return (uint16_t)y * SC_ROW_BYTES + (uint16_t)x * 2u; -} -void sc_screen_attach(uint8_t page) -{ - sc_screen_page = page; - sc_screen_attached = 1u; -} - -void sc_screen_detach(void) -{ - sc_screen_attached = 0u; -} - -void sc_screen_clear(uint8_t attr) -{ - uint8_t saved; - ScCell *cells; - uint16_t i; - - if (!sc_screen_attached) return; - saved = _io_page_w3; - sprinter_page_w3(sc_screen_page); - cells = (ScCell *)0xC000u; - for (i = 0u; i < (uint16_t)SC_SCREEN_W * SC_SCREEN_H; i++) { - cells[i].ch = ' '; - cells[i].attr = attr; - } - sprinter_page_w3(saved); -} - -void sc_screen_cell(uint8_t x, uint8_t y, uint8_t ch, uint8_t attr) -{ - uint8_t saved; - ScCell *cell; - - if (!sc_screen_attached || x >= SC_SCREEN_W || y >= SC_SCREEN_H) return; - saved = _io_page_w3; - sprinter_page_w3(sc_screen_page); - cell = (ScCell *)(0xC000u + sc_cell_offset(x, y)); - cell->ch = ch; - cell->attr = attr; - sprinter_page_w3(saved); -} - -void sc_screen_fill(uint8_t x, uint8_t y, uint8_t width, - uint8_t ch, uint8_t attr) -{ - uint8_t saved; - ScCell *cell; - uint8_t i; - - if (!sc_screen_attached || y >= SC_SCREEN_H || x >= SC_SCREEN_W) return; - if ((uint16_t)x + width > SC_SCREEN_W) width = (uint8_t)(SC_SCREEN_W - x); - - saved = _io_page_w3; - sprinter_page_w3(sc_screen_page); - cell = (ScCell *)(0xC000u + sc_cell_offset(x, y)); - for (i = 0u; i < width; i++) { - cell[i].ch = ch; - cell[i].attr = attr; - } - sprinter_page_w3(saved); -} - -void sc_screen_text(uint8_t x, uint8_t y, const char *text, - uint8_t attr, uint8_t max_width) -{ - uint8_t saved; - ScCell *cell; - uint8_t i = 0u; - - if (!sc_screen_attached || !text || y >= SC_SCREEN_H || - x >= SC_SCREEN_W) return; - if ((uint16_t)x + max_width > SC_SCREEN_W) { - max_width = (uint8_t)(SC_SCREEN_W - x); - } - - saved = _io_page_w3; - sprinter_page_w3(sc_screen_page); - cell = (ScCell *)(0xC000u + sc_cell_offset(x, y)); - while (text[i] && i < max_width) { - cell[i].ch = (uint8_t)text[i]; - cell[i].attr = attr; - i++; - } - sprinter_page_w3(saved); -} - -int sc_screen_present_full(void) -{ - if (!sc_screen_attached) return -1; - return sc_video_present_rect(sc_screen_page, 0u, 0u, 0u, - SC_SCREEN_H, SC_SCREEN_W); -} - -int sc_screen_present_row(uint8_t x, uint8_t y, uint8_t width) -{ - if (!sc_screen_attached || y >= SC_SCREEN_H || x >= SC_SCREEN_W || - width == 0u || (uint16_t)x + width > SC_SCREEN_W) return -1; - return sc_video_present_rect(sc_screen_page, sc_cell_offset(x, y), - y, x, 1u, width); -} - -int sc_screen_present_rows(uint8_t first, uint8_t count) -{ - if (!sc_screen_attached || first >= SC_SCREEN_H || count == 0u || - (uint16_t)first + count > SC_SCREEN_H) return -1; - return sc_video_present_rect(sc_screen_page, - (uint16_t)first * SC_ROW_BYTES, - first, 0u, count, SC_SCREEN_W); -} - -int sc_screen_scroll_rows(uint8_t x, uint8_t y, - uint8_t width, uint8_t height, - uint8_t direction, uint8_t fill_attr) -{ - uint8_t saved; - uint8_t i; - uint8_t j; - ScCell *model; - ScCell *open_row; - - if (!sc_screen_attached || width == 0u || height < 2u || - x >= SC_SCREEN_W || y >= SC_SCREEN_H || - (uint16_t)x + width > SC_SCREEN_W || - (uint16_t)y + height > SC_SCREEN_H || - (direction != SC_SCROLL_UP && direction != SC_SCROLL_DOWN)) return -1; - - saved = _io_page_w3; - sprinter_page_w3(sc_screen_page); - model = (ScCell *)0xC000u; - - if (direction == SC_SCROLL_UP) { - for (i = 0u; i + 1u < height; i++) { - memmove(model + (uint16_t)(y + i) * SC_SCREEN_W + x, - model + (uint16_t)(y + i + 1u) * SC_SCREEN_W + x, - (uint16_t)width * sizeof(ScCell)); - } - open_row = model + (uint16_t)(y + height - 1u) * SC_SCREEN_W + x; - } else { - i = (uint8_t)(height - 1u); - while (i != 0u) { - memmove(model + (uint16_t)(y + i) * SC_SCREEN_W + x, - model + (uint16_t)(y + i - 1u) * SC_SCREEN_W + x, - (uint16_t)width * sizeof(ScCell)); - i--; - } - open_row = model + (uint16_t)y * SC_SCREEN_W + x; - } - - for (j = 0u; j < width; j++) { - open_row[j].ch = ' '; - open_row[j].attr = fill_attr; - } - sprinter_page_w3(saved); - - return sc_video_scroll_rect(y, x, height, width, direction); -} diff --git a/applications/Volkov/src/sc_select.c b/applications/Volkov/src/sc_select.c deleted file mode 100644 index 15b6c7a..0000000 --- a/applications/Volkov/src/sc_select.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * sc_select.c — флаги выбора непосредственно в EMM-store панели. - * - * `..` и виртуальный маркер DSS никогда не выбираются. Insert может - * выбрать каталог, но групповые `+`, `-` и `*` работают только с файлами. - * Размер выбранного каталога пока нулевой; его рекурсивный размер будет считать job. - * Долгоживущих указателей W3 нет. - */ - -#include -#include -#include "sc_select.h" -#include "sc_store.h" - -static uint8_t sc_select_allowed(const ScDirEntry *entry) -{ - return (entry->flags & (SC_ENTRY_PARENT | SC_ENTRY_DSS_LIMIT)) == 0u; -} - -static uint8_t sc_select_group_allowed(const ScDirEntry *entry) -{ - return sc_select_allowed(entry) && !(entry->attr & FA_DIREC); -} - -static uint8_t sc_select_upper(uint8_t ch) -{ - if (ch >= 'a' && ch <= 'z') return (uint8_t)(ch - ('a' - 'A')); - return ch; -} - -static uint8_t sc_select_mask_match(const char *pattern, const char *name) -{ - const char *star = 0; - const char *retry = 0; - - /* DOS `*.*` означает все имена файлов, включая имена без точки. */ - if (pattern[0] == '*' && pattern[1] == '.' && pattern[2] == '*' && - pattern[3] == '\0') pattern = "*"; - - while (*name != '\0') { - if (*pattern == '?' || - sc_select_upper((uint8_t)*pattern) == - sc_select_upper((uint8_t)*name)) { - pattern++; - name++; - } else if (*pattern == '*') { - star = pattern++; - retry = name; - } else if (star) { - pattern = star + 1; - name = ++retry; - } else { - return 0u; - } - } - while (*pattern == '*') pattern++; - return *pattern == '\0'; -} - -static int sc_select_toggle(ScPanel *panel) -{ - ScDirEntry entry; - uint8_t was_selected; - - if (panel->count == 0u || - sc_store_get(panel, panel->cursor, &entry) != 0) return -1; - if (!sc_select_allowed(&entry)) return 1; - - was_selected = (entry.flags & SC_ENTRY_SELECTED) != 0u; - entry.flags ^= SC_ENTRY_SELECTED; - if (sc_store_put(panel, panel->cursor, &entry) != 0) return -1; - - if (was_selected) { - if (panel->selected_count != 0u) panel->selected_count--; - if (!(entry.attr & FA_DIREC)) panel->selected_size -= entry.size; - } else { - panel->selected_count++; - if (!(entry.attr & FA_DIREC)) panel->selected_size += entry.size; - } - - if (panel->cursor + 1u < panel->count) panel->cursor++; - sc_panel_normalize_view(panel); - panel->dirty = 1u; - return 0; -} - -static int sc_select_many(ScPanel *panel, uint8_t action, - const char *mask) -{ - ScDirEntry entry; - uint16_t i; - uint16_t selected_count = 0u; - uint32_t selected_size = 0ul; - - for (i = 0u; i < panel->count; i++) { - if (sc_store_get(panel, i, &entry) != 0) return -1; - if (sc_select_group_allowed(&entry)) { - uint8_t old_flags = entry.flags; - if (action == SC_SELECT_INVERT) { - entry.flags ^= SC_ENTRY_SELECTED; - } else if (sc_select_mask_match(mask, entry.name)) { - if (action == SC_SELECT_MASK_SET) { - entry.flags |= SC_ENTRY_SELECTED; - } else { - entry.flags &= (uint8_t)~SC_ENTRY_SELECTED; - } - } - if (entry.flags != old_flags && - sc_store_put(panel, i, &entry) != 0) return -1; - } - if (sc_select_allowed(&entry) && - (entry.flags & SC_ENTRY_SELECTED)) { - selected_count++; - if (!(entry.attr & FA_DIREC)) selected_size += entry.size; - } - } - panel->selected_count = selected_count; - panel->selected_size = selected_size; - panel->dirty = 1u; - return 0; -} - -int sc_select_apply(ScPanel *panel, uint8_t action, - const char *mask) __banked -{ - if (action == SC_SELECT_TOGGLE) return sc_select_toggle(panel); - if (action > SC_SELECT_MASK_CLEAR || - (action != SC_SELECT_INVERT && !mask)) return -1; - return sc_select_many(panel, action, mask); -} diff --git a/applications/Volkov/src/sc_sort.c b/applications/Volkov/src/sc_sort.c deleted file mode 100644 index 87cb77c..0000000 --- a/applications/Volkov/src/sc_sort.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * sc_sort.c — нерекурсивная Shell sort записей непосредственно в EMM. - * Порядок: синтетический `..`, каталоги, файлы, предупреждение о пределе - * DSS. Внутри каталогов и файлов действует независимый режим панели: - * name/ext/size/date и прямое/обратное направление. Callback qsort и - * долгоживущие W3-указатели не нужны. - */ - -#include -#include -#include -#include "sc_sort.h" -#include "sc_store.h" - -static uint8_t sc_ascii_upper(uint8_t ch) -{ - if (ch >= 'a' && ch <= 'z') return (uint8_t)(ch - ('a' - 'A')); - return ch; -} - -static uint8_t sc_entry_group(const ScDirEntry *entry) -{ - if (entry->flags & SC_ENTRY_PARENT) return 0u; - if (entry->flags & SC_ENTRY_DSS_LIMIT) return 3u; - if (entry->attr & FA_DIREC) return 1u; - return 2u; -} - -static int sc_compare_text(const char *left, const char *right) -{ - uint8_t i; - - for (i = 0u; i < 13u; i++) { - uint8_t a = sc_ascii_upper((uint8_t)left[i]); - uint8_t b = sc_ascii_upper((uint8_t)right[i]); - if (a != b) return a < b ? -1 : 1; - if (a == 0u) break; - } - return 0; -} - -static const char *sc_extension(const char *name) -{ - const char *result = &name[12]; - uint8_t i; - - for (i = 0u; i < 13u && name[i] != '\0'; i++) { - if (name[i] == '.') result = &name[i + 1u]; - } - return result; -} - -static int sc_compare_u16(uint16_t left, uint16_t right) -{ - if (left == right) return 0; - return left < right ? -1 : 1; -} - -static int sc_compare_u32(uint32_t left, uint32_t right) -{ - if (left == right) return 0; - return left < right ? -1 : 1; -} - -static int sc_entry_compare(const ScPanel *panel, - const ScDirEntry *left, - const ScDirEntry *right) -{ - uint8_t left_group = sc_entry_group(left); - uint8_t right_group = sc_entry_group(right); - int result = 0; - - if (left_group != right_group) { - return left_group < right_group ? -1 : 1; - } - - switch (panel->sort_key) { - case SC_SORT_EXTENSION: - result = sc_compare_text(sc_extension(left->name), - sc_extension(right->name)); - break; - case SC_SORT_SIZE: - result = sc_compare_u32(left->size, right->size); - break; - case SC_SORT_DATE: - result = sc_compare_u16(left->date, right->date); - if (result == 0) { - result = sc_compare_u16(left->time, right->time); - } - break; - case SC_SORT_NAME: - default: - break; - } - if (result == 0) result = sc_compare_text(left->name, right->name); - return panel->sort_descending ? -result : result; -} - -int sc_sort_panel(ScPanel *panel) __banked -{ - uint16_t gap; - uint16_t i; - uint16_t j; - ScDirEntry moving; - ScDirEntry previous; - - for (gap = (uint16_t)(panel->count / 2u); gap != 0u; gap /= 2u) { - for (i = gap; i < panel->count; i++) { - if (sc_store_get(panel, i, &moving) != 0) return -1; - j = i; - while (j >= gap) { - if (sc_store_get(panel, (uint16_t)(j - gap), - &previous) != 0) return -1; - if (sc_entry_compare(panel, &previous, &moving) <= 0) break; - if (sc_store_put(panel, j, &previous) != 0) return -1; - j = (uint16_t)(j - gap); - } - if (sc_store_put(panel, j, &moving) != 0) return -1; - } - } - return 0; -} - -int sc_sort_set(ScPanel *panel, uint8_t key) __banked -{ - ScDirEntry selected; - ScDirEntry current; - uint8_t have_selection = 0u; - uint8_t old_key = panel->sort_key; - uint8_t old_descending = panel->sort_descending; - uint16_t i; - - if (key > SC_SORT_DATE) return -1; - if (panel->count != 0u && - sc_store_get(panel, panel->cursor, &selected) == 0) { - have_selection = 1u; - } - - if (panel->sort_key == key) { - panel->sort_descending ^= 1u; - } else { - panel->sort_key = key; - panel->sort_descending = 0u; - } - - if (sc_sort_panel(panel) != 0) { - panel->sort_key = old_key; - panel->sort_descending = old_descending; - return -1; - } - - if (have_selection) { - for (i = 0u; i < panel->count; i++) { - if (sc_store_get(panel, i, ¤t) != 0) return -1; - if (strcmp(current.name, selected.name) == 0) { - panel->cursor = i; - break; - } - } - } - sc_panel_normalize_view(panel); - panel->dirty = 1u; - return 0; -} diff --git a/applications/Volkov/src/sc_source_fs.c b/applications/Volkov/src/sc_source_fs.c deleted file mode 100644 index 1646051..0000000 --- a/applications/Volkov/src/sc_source_fs.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * sc_source_fs.c — чтение каталога DSS в одну EMM-страницу панели. - * - * F_FIRST/F_NEXT используют один общий near-буфер. DSS имеет глобальный CWD, - * поэтому каждый scan/change_dir сохраняет его и восстанавливает до возврата. - */ - -#include -#include -#include -#include -#include -#include "sc_source.h" -#include "sc_sort.h" -#include "sc_store.h" - -#define SC_FIND_ATTRS (FA_RDONLY | FA_HIDDEN | FA_SYSTEM | FA_DIREC | FA_ARCH) - -/* DSS 1.71: каталог больше одной 16-КБ страницы завершается этим - * недокументированным в libc кодом. Это не потеря уже прочитанных данных: - * панель принимает частичный список и показывает признак усечения. */ -#define SC_DSS_TOO_MANY_FILES 35 - -/* Один near-буфер обязателен для всей пары F_FIRST/F_NEXT. Его адрес не - * влияет на системный предел DSS и не должен отнимать хвост банка W1. */ -static ffblk_t sc_source_find; -static char sc_source_process_cwd[256]; -static char sc_source_old_path[256]; -static char sc_source_new_path[256]; - -static void sc_copy_string(char *dst, const char *src, uint16_t capacity) -{ - uint16_t i = 0u; - if (capacity == 0u) return; - while (i + 1u < capacity && src[i] != '\0') { - dst[i] = src[i]; - i++; - } - dst[i] = '\0'; -} - -static uint8_t sc_path_is_root(const char *path) -{ - uint16_t i = 0u; - uint8_t separators = 0u; - - if (path[0] == '\\' && path[1] == '\0') return 1u; - if (path[0] != '\0' && path[1] == ':') i = 2u; - for (; path[i] != '\0'; i++) { - if (path[i] == '\\' || path[i] == '/') separators++; - else return 0u; - } - return separators <= 1u; -} - -static uint8_t sc_is_dot_name(const char *name) -{ - return (name[0] == '.' && name[1] == '\0') || - (name[0] == '.' && name[1] == '.' && name[2] == '\0'); -} - -static int sc_source_add_parent(ScPanel *panel) -{ - ScDirEntry entry; - memset(&entry, 0, sizeof(entry)); - entry.name[0] = '.'; - entry.name[1] = '.'; - entry.attr = FA_DIREC; - entry.flags = SC_ENTRY_PARENT; - if (sc_store_put(panel, panel->count, &entry) != 0) return -1; - panel->count++; - return 0; -} - -static int sc_source_add_found(ScPanel *panel) -{ - ScDirEntry entry; - - if (panel->count >= SC_DIR_CAPACITY) { - panel->truncated = 1u; - return 1; - } - memset(&entry, 0, sizeof(entry)); - sc_copy_string(entry.name, sc_source_find.found_name, - (uint16_t)sizeof(entry.name)); - entry.size = sc_source_find.size; - entry.date = sc_source_find.date; - entry.time = sc_source_find.time; - entry.attr = sc_source_find.found_attr; - if (sc_store_put(panel, panel->count, &entry) != 0) return -1; - panel->count++; - if (!(entry.attr & FA_DIREC)) panel->total_size += entry.size; - return 0; -} - -static int sc_source_add_dss_limit(ScPanel *panel) -{ - ScDirEntry entry; - - /* Предупреждение обязано быть видимым. Если будущий DSS когда-нибудь - * вернёт код 35 уже после заполнения нашей страницы, уступаем маркеру - * последнюю строку и корректируем сумму удалённого файла. */ - if (panel->count >= SC_DIR_CAPACITY) { - if (sc_store_get(panel, (uint16_t)(SC_DIR_CAPACITY - 1u), - &entry) != 0) return -1; - if (!(entry.attr & FA_DIREC)) panel->total_size -= entry.size; - panel->count = (uint16_t)(SC_DIR_CAPACITY - 1u); - } - - memset(&entry, 0, sizeof(entry)); - sc_copy_string(entry.name, ">>> MORE...", (uint16_t)sizeof(entry.name)); - entry.flags = SC_ENTRY_DSS_LIMIT; - if (sc_store_put(panel, panel->count, &entry) != 0) return -1; - panel->count++; - panel->truncated = 1u; - return 0; -} - -static void sc_source_restore_selection(ScPanel *panel, - const char *previous_name) -{ - ScDirEntry entry; - uint16_t i; - - if (previous_name[0] == '\0') { - sc_panel_normalize_view(panel); - return; - } - for (i = 0u; i < panel->count; i++) { - if (sc_store_get(panel, i, &entry) != 0) break; - if (strcmp(entry.name, previous_name) == 0) { - panel->cursor = i; - break; - } - } - sc_panel_normalize_view(panel); -} - -int sc_source_fs_scan(ScPanel *panel) __banked -{ - ScDirEntry selected; - char previous_name[13]; - int rc; - int scan_errno = 0; - uint8_t done = 0u; - uint8_t dss_limited = 0u; - - previous_name[0] = '\0'; - if (panel->count != 0u && - sc_store_get(panel, panel->cursor, &selected) == 0) { - sc_copy_string(previous_name, selected.name, - (uint16_t)sizeof(previous_name)); - } - if (!getcwd(sc_source_process_cwd, - (uint16_t)sizeof(sc_source_process_cwd))) return -1; - if (chdir(panel->path) != 0) return -1; - - sc_store_clear(panel); - if (!sc_path_is_root(panel->path) && sc_source_add_parent(panel) != 0) { - scan_errno = ENOMEM; - done = 1u; - } - - if (!done) { - errno = 0; - /* DSS 1.71/BIOS 3.06 трактует `*` как имя без расширения. Только - * подтверждённый P1-шаблон `*.*` перечисляет полный каталог. */ - rc = ffirst("*.*", &sc_source_find, SC_FIND_ATTRS); - if (rc != 0) { - if (errno == SC_DSS_TOO_MANY_FILES) dss_limited = 1u; - else if (errno != ENOENT) scan_errno = errno; - } else { - for (;;) { - if (!sc_is_dot_name(sc_source_find.found_name) && - !(sc_source_find.found_attr & FA_LABEL)) { - rc = sc_source_add_found(panel); - if (rc < 0) { - scan_errno = ENOMEM; - break; - } - /* 641-я подходящая запись уже доказала усечение. DSS - * хранит итератор глобально, но следующий F_FIRST его - * переинициализирует; дочитывать огромный каталог нельзя. */ - if (rc > 0) break; - } - errno = 0; - if (fnext(&sc_source_find) != 0) { - if (errno == SC_DSS_TOO_MANY_FILES) { - dss_limited = 1u; - } else if (errno != ENOENT) { - scan_errno = errno; - } - break; - } - } - } - } - - if (dss_limited && scan_errno == 0 && - sc_source_add_dss_limit(panel) != 0) scan_errno = ENOMEM; - - if (chdir(sc_source_process_cwd) != 0) return -2; - if (scan_errno != 0) { - errno = scan_errno; - return -1; - } - if (sc_sort_panel(panel) != 0) { - errno = ENOMEM; - return -1; - } - sc_source_restore_selection(panel, previous_name); - panel->dirty = 1u; - return 0; -} - -int sc_source_fs_change_dir(ScPanel *panel, const char *name) __banked -{ - int result; - - sc_copy_string(sc_source_old_path, panel->path, - (uint16_t)sizeof(sc_source_old_path)); - if (!getcwd(sc_source_process_cwd, - (uint16_t)sizeof(sc_source_process_cwd))) return -1; - if (chdir(panel->path) != 0) return -1; - if (chdir(name) != 0) { - (void)chdir(sc_source_process_cwd); - return -1; - } - if (!getcwd(sc_source_new_path, - (uint16_t)sizeof(sc_source_new_path))) { - (void)chdir(sc_source_process_cwd); - return -1; - } - if (chdir(sc_source_process_cwd) != 0) return -2; - - sc_copy_string(panel->path, sc_source_new_path, - (uint16_t)sizeof(panel->path)); - panel->count = 0u; - result = sc_source_fs_scan(panel); - if (result != 0) { - sc_copy_string(panel->path, sc_source_old_path, - (uint16_t)sizeof(panel->path)); - (void)sc_source_fs_scan(panel); - } - return result; -} diff --git a/applications/Volkov/src/sc_store.c b/applications/Volkov/src/sc_store.c deleted file mode 100644 index 35ef5ba..0000000 --- a/applications/Volkov/src/sc_store.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * sc_store.c — поэлементный доступ к странице каталога через W3. - * - * bank_read/bank_write сами сохраняют и восстанавливают страницу W3. - * Ни одна функция не возвращает указатель внутрь отображённой страницы. - */ - -#include -#include -#include "sc_store.h" - -static uint16_t sc_store_offset(uint16_t index) -{ - return (uint16_t)(index * (uint16_t)sizeof(ScDirEntry)); -} - -int sc_store_get(const ScPanel *panel, uint16_t index, ScDirEntry *out) -{ - if (!panel || !out || index >= panel->count || - index >= SC_DIR_CAPACITY || panel->phys_page == 0u) return -1; - bank_read(panel->phys_page, sc_store_offset(index), out, - (uint16_t)sizeof(*out)); - return 0; -} - -int sc_store_put(const ScPanel *panel, uint16_t index, - const ScDirEntry *entry) -{ - if (!panel || !entry || index >= SC_DIR_CAPACITY || - panel->phys_page == 0u) return -1; - bank_write(panel->phys_page, sc_store_offset(index), entry, - (uint16_t)sizeof(*entry)); - return 0; -} - -void sc_store_clear(ScPanel *panel) -{ - panel->count = 0u; - panel->cursor = 0u; - panel->top = 0u; - panel->total_size = 0ul; - panel->selected_count = 0u; - panel->selected_size = 0ul; - panel->truncated = 0u; - panel->dirty = 1u; -} diff --git a/applications/Volkov/src/sc_theme.c b/applications/Volkov/src/sc_theme.c deleted file mode 100644 index ae0d80a..0000000 --- a/applications/Volkov/src/sc_theme.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * sc_theme.c — владение многоцветной текстовой палитрой Commander. - * - * BIOS-функции работают с четырьмя планами: paper, ink, flash-paper и - * flash-ink. Для каждого используемого атрибута исходные RGB сохраняются, - * обе пары задаются одинаково, а при shutdown полностью восстанавливаются. - */ - -#include -#include -#include "sc_theme.h" - -#define SC_THEME_COLOR_COUNT 9u -#define SC_THEME_PLANE_COUNT 4u - -typedef struct { - uint8_t attr; - uint8_t paper_r; - uint8_t paper_g; - uint8_t paper_b; - uint8_t ink_r; - uint8_t ink_g; - uint8_t ink_b; -} ScThemeColor; - -static const ScThemeColor sc_theme_colors[SC_THEME_COLOR_COUNT] = { - { SC_ATTR_FRAME, 0x00, 0x00, 0xA8, 0xFF, 0xFF, 0xFF }, - { SC_ATTR_TITLE, 0x00, 0x00, 0xA8, 0xFF, 0xFF, 0x00 }, - { SC_ATTR_NORMAL, 0x00, 0x00, 0xA8, 0x55, 0xFF, 0xFF }, - { SC_ATTR_FILE, 0x00, 0x00, 0xA8, 0xC0, 0xC0, 0xC0 }, - { SC_ATTR_ACTIVE, 0x00, 0xA8, 0xA8, 0x00, 0x00, 0x00 }, - { SC_ATTR_STATUS, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00 }, - { SC_ATTR_DIALOG, 0xA8, 0x00, 0x00, 0xFF, 0xFF, 0xFF }, - { SC_ATTR_LIMIT, 0x00, 0x00, 0xA8, 0xFF, 0x55, 0x55 }, - { SC_ATTR_LIMIT_ACTIVE, - 0x00, 0xA8, 0xA8, 0xFF, 0x55, 0x55 } -}; - -/* Static-буфер гарантированно находится в W2, как требует BIOS $A4. */ -static uint8_t sc_theme_saved[SC_THEME_COLOR_COUNT] - [SC_THEME_PLANE_COUNT][3u]; -static uint8_t sc_theme_started; - -static void sc_theme_set_plane(uint8_t plane, const ScThemeColor *color) -{ - if (plane == TEXT_PAL_PAPER || plane == TEXT_PAL_BLINK_PAPER) { - text_pal_set_color(plane, color->attr, - color->paper_r, color->paper_g, color->paper_b); - } else { - text_pal_set_color(plane, color->attr, - color->ink_r, color->ink_g, color->ink_b); - } -} - -int sc_theme_init(void) -{ - uint8_t color_index; - uint8_t plane; - - if (sc_theme_started) return 0; - for (color_index = 0u; color_index < SC_THEME_COLOR_COUNT; - color_index++) { - const ScThemeColor *color = &sc_theme_colors[color_index]; - for (plane = 0u; plane < SC_THEME_PLANE_COUNT; plane++) { - text_pal_get_color(plane, color->attr, - &sc_theme_saved[color_index][plane][0], - &sc_theme_saved[color_index][plane][1], - &sc_theme_saved[color_index][plane][2]); - sc_theme_set_plane(plane, color); - } - } - sc_theme_started = 1u; - return 0; -} - -void sc_theme_reapply(void) -{ - uint8_t color_index; - uint8_t plane; - - if (!sc_theme_started) return; - for (color_index = 0u; color_index < SC_THEME_COLOR_COUNT; - color_index++) { - const ScThemeColor *color = &sc_theme_colors[color_index]; - for (plane = 0u; plane < SC_THEME_PLANE_COUNT; plane++) { - sc_theme_set_plane(plane, color); - } - } -} - -void sc_theme_shutdown(void) -{ - uint8_t color_index; - uint8_t plane; - - if (!sc_theme_started) return; - for (color_index = 0u; color_index < SC_THEME_COLOR_COUNT; - color_index++) { - for (plane = 0u; plane < SC_THEME_PLANE_COUNT; plane++) { - text_pal_set_color( - plane, sc_theme_colors[color_index].attr, - sc_theme_saved[color_index][plane][0], - sc_theme_saved[color_index][plane][1], - sc_theme_saved[color_index][plane][2]); - } - } - sc_theme_started = 0u; -} diff --git a/applications/Volkov/src/sc_tree.c b/applications/Volkov/src/sc_tree.c deleted file mode 100644 index 0d53fda..0000000 --- a/applications/Volkov/src/sc_tree.c +++ /dev/null @@ -1,432 +0,0 @@ -/* - * sc_tree.c — пошаговый BFS-обход каталога для рекурсивных F5/F8. - * - * Очередь компонентов хранится в отдельной динамической EMM-странице W3. - * Каждый узел содержит parent-index и DOS 8.3 name; абсолютные пути - * восстанавливаются перед операцией. Первый 256-байтовый диапазон страницы - * хранит исходный CWD процесса, поэтому job всегда может его восстановить. - */ - -#include -#include -#include -#include -#include -#include -#include "sc_path.h" -#include "sc_tree.h" - -#define SC_TREE_SAVED_CWD_SIZE 256u -#define SC_TREE_NODE_OFFSET 256u -#define SC_TREE_NODE_NONE 0xFFFFu -#define SC_TREE_MAX_DEPTH 32u -#define SC_TREE_FIND_ATTRS \ - (FA_RDONLY | FA_HIDDEN | FA_SYSTEM | FA_DIREC | FA_ARCH) -#define SC_TREE_DSS_LIMIT 35 - -typedef struct { - uint16_t parent; - char name[13]; - uint8_t depth; -} ScTreeNode; - -typedef char ScTreeNode_must_be_16_bytes[ - (sizeof(ScTreeNode) == 16u) ? 1 : -1]; - -#define SC_TREE_NODE_CAPACITY \ - ((16384u - SC_TREE_NODE_OFFSET) / (uint16_t)sizeof(ScTreeNode)) - -static ffblk_t sc_tree_find; - -static uint8_t sc_tree_upper(uint8_t ch) -{ - if (ch >= 'a' && ch <= 'z') return (uint8_t)(ch - ('a' - 'A')); - if (ch == '/') return '\\'; - return ch; -} - -/* Истина, если path совпадает с root либо лежит внутри него. */ -static uint8_t sc_tree_path_contains(const char *root, const char *path) -{ - while (*root != '\0' && *path != '\0') { - if (sc_tree_upper((uint8_t)*root) != - sc_tree_upper((uint8_t)*path)) return 0u; - root++; - path++; - } - if (*root != '\0') return 0u; - if (*path == '\0') return 1u; - return (uint8_t)(*path == '\\' || *path == '/'); -} - -static uint16_t sc_tree_node_offset(uint16_t index) -{ - return (uint16_t)(SC_TREE_NODE_OFFSET + - index * (uint16_t)sizeof(ScTreeNode)); -} - -static int sc_tree_get(const ScTreeJob *job, uint16_t index, - ScTreeNode *node) -{ - if (!job || !node || index >= job->tail || - index >= SC_TREE_NODE_CAPACITY || job->page == 0u) { - errno = EINVAL; - return -1; - } - bank_read(job->page, sc_tree_node_offset(index), node, - (uint16_t)sizeof(*node)); - return 0; -} - -static int sc_tree_put(ScTreeJob *job, uint16_t parent, - const char *name, uint8_t depth) -{ - ScTreeNode node; - uint8_t i = 0u; - - if (job->tail >= SC_TREE_NODE_CAPACITY || depth > SC_TREE_MAX_DEPTH) { - errno = depth > SC_TREE_MAX_DEPTH ? EINAME : ENOMEM; - return -1; - } - memset(&node, 0, sizeof(node)); - node.parent = parent; - node.depth = depth; - while (i < 12u && name[i] != '\0') { - node.name[i] = name[i]; - i++; - } - if (name[i] != '\0') { - errno = EINAME; - return -1; - } - bank_write(job->page, sc_tree_node_offset(job->tail), &node, - (uint16_t)sizeof(node)); - job->tail++; - return 0; -} - -static int sc_tree_copy_base(char *out, const char *base) -{ - uint16_t i = 0u; - - while (base[i] != '\0') { - if (i + 1u >= SC_COPY_PATH_CAPACITY) { - errno = EINAME; - return -1; - } - out[i] = base[i]; - i++; - } - out[i] = '\0'; - return 0; -} - -static int sc_tree_append(char *out, const char *name) -{ - uint16_t pos = 0u; - uint16_t i = 0u; - - while (out[pos] != '\0') pos++; - if (pos != 0u && out[pos - 1u] != '\\' && out[pos - 1u] != '/') { - if (pos + 1u >= SC_COPY_PATH_CAPACITY) { - errno = EINAME; - return -1; - } - out[pos++] = '\\'; - } - while (name[i] != '\0') { - if (pos + 1u >= SC_COPY_PATH_CAPACITY) { - errno = EINAME; - return -1; - } - out[pos++] = name[i++]; - } - out[pos] = '\0'; - return 0; -} - -static int sc_tree_build_path(const ScTreeJob *job, uint16_t index, - const char *base, char *out) -{ - uint16_t chain[SC_TREE_MAX_DEPTH]; - uint8_t count = 0u; - ScTreeNode node; - - while (index != SC_TREE_NODE_NONE) { - if (count >= SC_TREE_MAX_DEPTH || sc_tree_get(job, index, &node) != 0) { - errno = EINAME; - return -1; - } - chain[count++] = index; - index = node.parent; - } - if (sc_tree_copy_base(out, base) != 0) return -1; - while (count != 0u) { - if (sc_tree_get(job, chain[--count], &node) != 0 || - sc_tree_append(out, node.name) != 0) return -1; - } - return 0; -} - -static uint8_t sc_tree_is_dot(const char *name) -{ - return (uint8_t)((name[0] == '.' && name[1] == '\0') || - (name[0] == '.' && name[1] == '.' && - name[2] == '\0')); -} - -static void sc_tree_found_entry(ScDirEntry *entry) -{ - memset(entry, 0, sizeof(*entry)); - strncpy(entry->name, sc_tree_find.found_name, - (uint16_t)(sizeof(entry->name) - 1u)); - entry->name[sizeof(entry->name) - 1u] = '\0'; - entry->size = sc_tree_find.size; - entry->date = sc_tree_find.date; - entry->time = sc_tree_find.time; - entry->attr = sc_tree_find.found_attr; -} - -static int sc_tree_fail(ScTreeJob *job, int error) -{ - if (error == 0) error = EUNKERR; - job->error = error; - errno = error; - return SC_TREE_STEP_ERROR; -} - -int sc_tree_begin(ScTreeJob *job, ScCopyJob *copy) __banked -{ - if (!job || !copy) { - errno = EINVAL; - return -1; - } - memset(job, 0, sizeof(*job)); - job->block = mem_alloc_pages(1u); - if (job->block == 0u) return -1; - job->page = mem_get_page(job->block, 0u); - if (job->page == 0u) { - mem_free_block(job->block); - job->block = 0u; - return -1; - } - if (!getcwd(copy->source_path, SC_COPY_PATH_CAPACITY)) { - mem_free_block(job->block); - job->block = 0u; - job->page = 0u; - return -1; - } - bank_write(job->page, 0u, copy->source_path, SC_TREE_SAVED_CWD_SIZE); - job->active = 1u; - return 0; -} - -int sc_tree_add_root(ScTreeJob *job, ScCopyJob *copy, - const char *source_base, const char *target_base, - const char *name) __banked -{ - if (!job || !copy || !job->active || !source_base || !target_base || - !name || name[0] == '\0') { - errno = EINVAL; - return -1; - } - if (sc_path_join(copy->source_path, SC_COPY_PATH_CAPACITY, - source_base, name) != 0) return -1; - if (sc_tree_path_contains(copy->source_path, target_base)) { - errno = EINVAL; - return -1; - } - return sc_tree_put(job, SC_TREE_NODE_NONE, name, 1u); -} - -int sc_tree_add_delete_root(ScTreeJob *job, const char *name) __banked -{ - if (!job || !job->active || !name || name[0] == '\0') { - errno = EINVAL; - return -1; - } - return sc_tree_put(job, SC_TREE_NODE_NONE, name, 1u); -} - -int sc_tree_next(ScTreeJob *job, ScCopyJob *copy, - const char *source_base, const char *target_base, - ScDirEntry *entry) __banked -{ - int rc; - int scan_errno; - ScTreeNode node; - - if (!job || !copy || !entry || !job->active) { - errno = EINVAL; - return SC_TREE_STEP_ERROR; - } - - while (job->head < job->tail) { - job->current = job->head; - if (job->scan_state == 0u) { - if (sc_tree_build_path(job, job->current, source_base, - copy->source_path) != 0) { - return sc_tree_fail(job, errno); - } - if (chdir(copy->source_path) != 0) { - return sc_tree_fail(job, errno); - } - if (sc_tree_build_path(job, job->current, target_base, - copy->target_path) != 0) { - return sc_tree_fail(job, errno); - } - if (mkdir(copy->target_path) != 0) { - return sc_tree_fail(job, errno); - } - job->created_directories++; - job->scan_state = 1u; - return SC_TREE_STEP_DIRECTORY; - } - - errno = 0; - if (job->scan_state == 1u) { - rc = ffirst("*.*", &sc_tree_find, SC_TREE_FIND_ATTRS); - if (rc == 0) job->scan_state = 2u; - } else { - rc = fnext(&sc_tree_find); - } - if (rc != 0) { - scan_errno = errno; - if (scan_errno == ENOENT) { - job->head++; - job->scan_state = 0u; - continue; - } - if (scan_errno == SC_TREE_DSS_LIMIT) { - return sc_tree_fail(job, scan_errno); - } - return sc_tree_fail(job, scan_errno); - } - - if (sc_tree_is_dot(sc_tree_find.found_name) || - (sc_tree_find.found_attr & FA_LABEL)) continue; - if (sc_tree_find.found_attr & FA_DIREC) { - if (sc_tree_get(job, job->current, &node) != 0 || - sc_tree_put(job, job->current, sc_tree_find.found_name, - (uint8_t)(node.depth + 1u)) != 0) { - return sc_tree_fail(job, errno); - } - continue; - } - - sc_tree_found_entry(entry); - if (sc_tree_build_path(job, job->current, target_base, - copy->target_path) != 0 || - sc_tree_append(copy->target_path, entry->name) != 0) { - return sc_tree_fail(job, errno); - } - job->files_seen++; - return SC_TREE_STEP_FILE; - } - return SC_TREE_STEP_DONE; -} - -int sc_tree_delete_next(ScTreeJob *job, ScCopyJob *copy, - const char *source_base, - ScDirEntry *entry) __banked -{ - int rc; - int scan_errno; - ScTreeNode node; - - if (!job || !copy || !source_base || !entry || !job->active) { - errno = EINVAL; - return SC_TREE_STEP_ERROR; - } - - for (;;) { - if (job->scan_state == 3u) { - if (job->reverse == 0u) return SC_TREE_STEP_DONE; - job->reverse--; - if (sc_tree_build_path(job, job->reverse, source_base, - copy->target_path) != 0) { - return sc_tree_fail(job, errno); - } - return SC_TREE_STEP_DIRECTORY; - } - - if (job->head >= job->tail) { - if (chdir(source_base) != 0) return sc_tree_fail(job, errno); - job->reverse = job->tail; - job->scan_state = 3u; - continue; - } - - job->current = job->head; - if (job->scan_state == 0u) { - if (sc_tree_build_path(job, job->current, source_base, - copy->source_path) != 0 || - chdir(copy->source_path) != 0) { - return sc_tree_fail(job, errno); - } - job->scan_state = 1u; - return SC_TREE_STEP_SCAN_DIR; - } - - errno = 0; - if (job->scan_state == 1u) { - rc = ffirst("*.*", &sc_tree_find, SC_TREE_FIND_ATTRS); - if (rc == 0) job->scan_state = 2u; - } else { - rc = fnext(&sc_tree_find); - } - if (rc != 0) { - scan_errno = errno; - if (scan_errno == ENOENT) { - job->head++; - job->scan_state = 0u; - continue; - } - return sc_tree_fail(job, scan_errno); - } - - if (sc_tree_is_dot(sc_tree_find.found_name) || - (sc_tree_find.found_attr & FA_LABEL)) continue; - if (sc_tree_find.found_attr & FA_DIREC) { - if (sc_tree_get(job, job->current, &node) != 0 || - sc_tree_put(job, job->current, sc_tree_find.found_name, - (uint8_t)(node.depth + 1u)) != 0) { - return sc_tree_fail(job, errno); - } - /* Возвращаем управление после каждого enqueue: иначе каталог с - * сотнями подкаталогов надолго задержит проверку Esc. */ - return SC_TREE_STEP_SCAN_DIR; - } - - sc_tree_found_entry(entry); - if (sc_tree_build_path(job, job->current, source_base, - copy->target_path) != 0 || - sc_tree_append(copy->target_path, entry->name) != 0) { - return sc_tree_fail(job, errno); - } - job->files_seen++; - return SC_TREE_STEP_FILE; - } -} - -int sc_tree_finish(ScTreeJob *job, ScCopyJob *copy) __banked -{ - int restore_errno = 0; - - if (!job || !copy) { - errno = EINVAL; - return -1; - } - if (!job->active) return 0; - bank_read(job->page, 0u, copy->source_path, SC_TREE_SAVED_CWD_SIZE); - if (chdir(copy->source_path) != 0) restore_errno = errno; - mem_free_block(job->block); - job->block = 0u; - job->page = 0u; - job->active = 0u; - if (restore_errno != 0) { - errno = restore_errno; - return -1; - } - return 0; -} diff --git a/applications/Volkov/src/sc_video_system.c b/applications/Volkov/src/sc_video_system.c deleted file mode 100644 index dd31f69..0000000 --- a/applications/Volkov/src/sc_video_system.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * sc_video_system.c — полноэкранный системный backend Commander. - * - * Использует только абсолютные ESTEX WINREST/SCROLL. Окна BIOS и прямая - * адресация VRAM не применяются. Палитрой владеет отдельный sc_theme. - */ - -#include -#include -#include "sc_video.h" - -static uint8_t sc_previous_mode; -static uint8_t sc_video_started; - -int sc_video_init(void) -{ - sc_previous_mode = gettextmode(); - if (settextmode(TEXT_MODE_80x32) != 0) return -1; - sc_video_started = 1u; - return 0; -} - -int sc_video_restore(void) -{ - if (!sc_video_started) return -1; - return settextmode(TEXT_MODE_80x32); -} - -void sc_video_shutdown(void) -{ - if (!sc_video_started) return; - - textattr(COLOR(COLOR_WHITE, COLOR_BLACK)); - clrscr(); - if (sc_previous_mode == TEXT_MODE_40x32 || - sc_previous_mode == TEXT_MODE_80x32) { - (void)settextmode(sc_previous_mode); - } - sc_video_started = 0u; -} - -int sc_video_present_rect(uint8_t page, uint16_t offset, - uint8_t row, uint8_t col, - uint8_t height, uint8_t width) -{ - if (height == 0u || width == 0u) return 0; - sc_platform_winrest(row, col, height, width, page, offset); - return 0; -} - -int sc_video_scroll_rect(uint8_t row, uint8_t col, - uint8_t height, uint8_t width, - uint8_t direction) -{ - if (height == 0u || width == 0u) return 0; - scroll(col, row, width, height, direction, 0u); - return 0; -} diff --git a/applications/Volkov/src/sprcmd.c b/applications/Volkov/src/sprcmd.c deleted file mode 100644 index 5d4fdfe..0000000 --- a/applications/Volkov/src/sprcmd.c +++ /dev/null @@ -1,23 +0,0 @@ -/* sprcmd.c — точка входа Sprinter Commander. */ - -#include -#include -#include "sc_app.h" - -static ScApp sc_app; - -int main(void) -{ - int result = sc_app_init(&sc_app); - - if (result != 0) { - cputs("SPRCMD initialization failed.\r\n"); - return (uint8_t)(-result); - } - sc_app_run(&sc_app); - if (sc_app_cleanup(&sc_app) != 0) { - cputs("SPRCMD cleanup did not restore EMM free pages.\r\n"); - return 4; - } - return 0; -} diff --git a/applications/Volkov/tests/check_p10_rename_hdd.sh b/applications/Volkov/tests/check_p10_rename_hdd.sh deleted file mode 100755 index 08b08af..0000000 --- a/applications/Volkov/tests/check_p10_rename_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Извлекает результат target-пробы и дерево после UI F6. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p10_rename.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-rename-check.XXXXXX") -RAW="$TEMP_DIR/p10_rename.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/RENTEST z:/RENRES.TXT "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p10_rename_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p11_delete_hdd.sh b/applications/Volkov/tests/check_p11_delete_hdd.sh deleted file mode 100755 index 8c4a8b5..0000000 --- a/applications/Volkov/tests/check_p11_delete_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Извлекает результат target-пробы и дерево после UI F8. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p11_delete.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-delete-check.XXXXXX") -RAW="$TEMP_DIR/p11_delete.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/DELTEST z:/DELRES.TXT "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p11_delete_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p12_group_copy_hdd.sh b/applications/Volkov/tests/check_p12_group_copy_hdd.sh deleted file mode 100755 index d6f8c09..0000000 --- a/applications/Volkov/tests/check_p12_group_copy_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Извлекает обе стороны группового F5 и сравнивает их побайтно. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p12_group_copy.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-group-copy-check.XXXXXX") -RAW="$TEMP_DIR/p12_group_copy.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/GRSRC z:/GRDST "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p12_group_copy_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p13_group_delete_hdd.sh b/applications/Volkov/tests/check_p13_group_delete_hdd.sh deleted file mode 100755 index 5dacd2e..0000000 --- a/applications/Volkov/tests/check_p13_group_delete_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Извлекает дерево после группового F8 и проверяет его. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p13_group_delete.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-group-delete-check.XXXXXX") -RAW="$TEMP_DIR/p13_group_delete.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/GDEL "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p13_group_delete_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p14_default_colors_hdd.sh b/applications/Volkov/tests/check_p14_default_colors_hdd.sh deleted file mode 100755 index f6e5fc8..0000000 --- a/applications/Volkov/tests/check_p14_default_colors_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Проверяет неизменность HDD-fixture после визуального теста цветов. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p14_default_colors.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-default-colors-check.XXXXXX") -RAW="$TEMP_DIR/p14_default_colors.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/COLORS "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p14_default_colors_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p15_tree_cancel_hdd.sh b/applications/Volkov/tests/check_p15_tree_cancel_hdd.sh deleted file mode 100755 index b63961f..0000000 --- a/applications/Volkov/tests/check_p15_tree_cancel_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Проверяет носитель после отмены рекурсивного F5. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p15_tree_cancel.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-cancel-check.XXXXXX") -RAW="$TEMP_DIR/p15_tree_cancel.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/CSRC z:/CDST "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p15_tree_cancel_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p15_tree_copy_hdd.sh b/applications/Volkov/tests/check_p15_tree_copy_hdd.sh deleted file mode 100755 index 9257426..0000000 --- a/applications/Volkov/tests/check_p15_tree_copy_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Извлекает дерево после рекурсивного F5 и сравнивает его побайтно. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p15_tree_copy.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-copy-check.XXXXXX") -RAW="$TEMP_DIR/p15_tree_copy.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/SRC z:/DST "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p15_tree_copy_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p15_tree_self_hdd.sh b/applications/Volkov/tests/check_p15_tree_self_hdd.sh deleted file mode 100755 index 2fffc24..0000000 --- a/applications/Volkov/tests/check_p15_tree_self_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Проверяет неизменность source после self-target guard. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p15_tree_self.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-self-check.XXXXXX") -RAW="$TEMP_DIR/p15_tree_self.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/SRC "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p15_tree_self_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p16_group_tree_copy_hdd.sh b/applications/Volkov/tests/check_p16_group_tree_copy_hdd.sh deleted file mode 100755 index 2602ae1..0000000 --- a/applications/Volkov/tests/check_p16_group_tree_copy_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Извлекает и проверяет результат group F5 с directory roots. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p16_group_tree_copy.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-group-tree-check.XXXXXX") -RAW="$TEMP_DIR/p16_group_tree_copy.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/GSRC z:/GDST "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p16_group_tree_copy_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p17_tree_delete_cancel_hdd.sh b/applications/Volkov/tests/check_p17_tree_delete_cancel_hdd.sh deleted file mode 100755 index 989a772..0000000 --- a/applications/Volkov/tests/check_p17_tree_delete_cancel_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Извлекает и проверяет состояние после runtime-cancel рекурсивного F8. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p17_tree_delete_cancel.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-delete-cancel-check.XXXXXX") -RAW="$TEMP_DIR/p17_tree_delete_cancel.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/CDEL "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p17_tree_delete_cancel_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p17_tree_delete_hdd.sh b/applications/Volkov/tests/check_p17_tree_delete_hdd.sh deleted file mode 100755 index 04bcc2c..0000000 --- a/applications/Volkov/tests/check_p17_tree_delete_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Извлекает и проверяет результат рекурсивного группового F8. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p17_tree_delete.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-delete-check.XXXXXX") -RAW="$TEMP_DIR/p17_tree_delete.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/RDEL "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p17_tree_delete_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p18_metadata_hdd.sh b/applications/Volkov/tests/check_p18_metadata_hdd.sh deleted file mode 100755 index 8b4ad31..0000000 --- a/applications/Volkov/tests/check_p18_metadata_hdd.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh -# Извлекает raw FAT16 и сверяет payload, date/time и атрибуты source/target. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p18_metadata.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-metadata-check.XXXXXX") -RAW="$TEMP_DIR/p18_metadata.raw" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -python3 "$PROJECT_DIR/tests/p18_metadata_fixture.py" verify "$RAW" diff --git a/applications/Volkov/tests/check_p19_overwrite_hdd.sh b/applications/Volkov/tests/check_p19_overwrite_hdd.sh deleted file mode 100755 index 773da35..0000000 --- a/applications/Volkov/tests/check_p19_overwrite_hdd.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -# Извлекает результат target-пробы P19 и сверяет все файлы побайтно. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p19_overwrite.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-p19-check.XXXXXX") -RAW="$TEMP_DIR/p19.raw" -MTC="$TEMP_DIR/mtools.conf" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir -p "$TEMP_DIR/out" -mcopy -s z:/* "$TEMP_DIR/out" -python3 "$PROJECT_DIR/tests/p19_overwrite_fixture.py" verify "$TEMP_DIR/out" diff --git a/applications/Volkov/tests/check_p19_policy_hdd.sh b/applications/Volkov/tests/check_p19_policy_hdd.sh deleted file mode 100755 index 46e2b87..0000000 --- a/applications/Volkov/tests/check_p19_policy_hdd.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -# Извлекает UI-результат P19 и сверяет policy-последствия побайтно. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p19_policy.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-p19-ui-check.XXXXXX") -RAW="$TEMP_DIR/p19.raw" -MTC="$TEMP_DIR/mtools.conf" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir -p "$TEMP_DIR/out" -mcopy -s z:/* "$TEMP_DIR/out" -python3 "$PROJECT_DIR/tests/p19_policy_fixture.py" verify "$TEMP_DIR/out" diff --git a/applications/Volkov/tests/check_p4_copy_hdd.sh b/applications/Volkov/tests/check_p4_copy_hdd.sh deleted file mode 100755 index 433571d..0000000 --- a/applications/Volkov/tests/check_p4_copy_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Проверяет изменённый MAME-образ после tests/mame_p4_copy_hdd.lua. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p4_copy.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-copy-check.XXXXXX") -RAW="$TEMP_DIR/p4_copy.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/COPYFROM z:/COPYTO "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p4_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p4_exec_hdd.sh b/applications/Volkov/tests/check_p4_exec_hdd.sh deleted file mode 100755 index 0651d97..0000000 --- a/applications/Volkov/tests/check_p4_exec_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Извлекает изменённый HDD и проверяет результаты P4CHILD. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p4_exec.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-exec-check.XXXXXX") -RAW="$TEMP_DIR/p4_exec.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/EXECLEFT z:/EXECRGHT "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p4_exec_verify.py" "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p5_copy_faults_hdd.sh b/applications/Volkov/tests/check_p5_copy_faults_hdd.sh deleted file mode 100755 index 8f3a39d..0000000 --- a/applications/Volkov/tests/check_p5_copy_faults_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Извлекает результат низкоуровневой матрицы copy-job из CHD. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p5_copy_faults.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-copy-faults-check.XXXXXX") -RAW="$TEMP_DIR/p5_copy_faults.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/SOURCE z:/TARGET z:/RESULT.TXT "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p5_copy_faults_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p5_enospc_hdd.sh b/applications/Volkov/tests/check_p5_enospc_hdd.sh deleted file mode 100755 index bc38a5e..0000000 --- a/applications/Volkov/tests/check_p5_enospc_hdd.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh -# Проверяет cleanup почти полного HDD после ошибки копирования. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p5_enospc.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-enospc-check.XXXXXX") -RAW="$TEMP_DIR/enospc.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/SOURCE z:/TARGET "$EXTRACTED/" -mcopy z:/FILLER.BIN "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p5_enospc_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p5_hdd.sh b/applications/Volkov/tests/check_p5_hdd.sh deleted file mode 100755 index 2a94601..0000000 --- a/applications/Volkov/tests/check_p5_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Извлекает HDD после P5 и сравнивает все двадцать копий. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p5_stability.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-p5-check.XXXXXX") -RAW="$TEMP_DIR/p5.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/SOURCE z:/TARGET "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p5_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p5_media_hdd.sh b/applications/Volkov/tests/check_p5_media_hdd.sh deleted file mode 100755 index 6182d19..0000000 --- a/applications/Volkov/tests/check_p5_media_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Проверяет HDD после unload/load и повторного F5. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p5_media.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-media-check.XXXXXX") -RAW="$TEMP_DIR/p5_media.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/MEDIA z:/DATA.BIN "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p5_media_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p6_sort_hdd.sh b/applications/Volkov/tests/check_p6_sort_hdd.sh deleted file mode 100755 index 4236ba8..0000000 --- a/applications/Volkov/tests/check_p6_sort_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Извлекает SORTRES.TXT и проверяет target-регрессию сортировки. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p6_sort.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-sort-check.XXXXXX") -RAW="$TEMP_DIR/p6_sort.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy z:/SORTRES.TXT "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p6_sort_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p7_select_hdd.sh b/applications/Volkov/tests/check_p7_select_hdd.sh deleted file mode 100755 index 57b92a1..0000000 --- a/applications/Volkov/tests/check_p7_select_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Извлекает SELRES.TXT и проверяет target-регрессию выбора. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p7_select.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-select-check.XXXXXX") -RAW="$TEMP_DIR/p7_select.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy z:/SELRES.TXT "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p7_select_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/check_p9_mkdir_hdd.sh b/applications/Volkov/tests/check_p9_mkdir_hdd.sh deleted file mode 100755 index 739b79d..0000000 --- a/applications/Volkov/tests/check_p9_mkdir_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Извлекает результат target-пробы и дерево после F7. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p9_mkdir.chd} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-mkdir-check.XXXXXX") -RAW="$TEMP_DIR/p9_mkdir.raw" -MTC="$TEMP_DIR/mtools.conf" -EXTRACTED="$TEMP_DIR/extracted" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -chdman extractraw -i "$IMAGE" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mkdir "$EXTRACTED" -mcopy -s z:/MKTEST z:/MKRES.TXT "$EXTRACTED/" -python3 "$PROJECT_DIR/tests/p9_mkdir_fixture.py" verify "$EXTRACTED" diff --git a/applications/Volkov/tests/fixtures/mdview2/LICENSE b/applications/Volkov/tests/fixtures/mdview2/LICENSE deleted file mode 100644 index 217ff75..0000000 --- a/applications/Volkov/tests/fixtures/mdview2/LICENSE +++ /dev/null @@ -1,41 +0,0 @@ -MIT License - -Copyright (c) 2026 Sprinter C Compiler contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -================================================================================ - -Vendored third-party components are under their own licenses: - - third_party/sdcc/ — SDCC 4.5 under GPL v2 with linking exception. - See third_party/sdcc/COPYING.txt. - third_party/solid-c/ — Original Sprinter Solid C, used only as a reference - for compatibility. Licence not specified upstream; - treat as reference material, do not redistribute - original binaries. - mame/v306/ — MAME (https://www.mamedev.org), GPL v2+. - mame/v306/IMG/*.img *.chd *.iso — Sprinter ROM / DSS / sample disk images - from Peters Plus. Redistribution policy: see - Peters Plus documentation. - -Documentation in docs/converted/, docs/reference/, docs/samples/, and -docs/memory management/ contains material originally published by Peters Plus -team (Иван Мак, Дмитрий Паринов and others). Used here for cross-compilation -reference; original copyrights apply. diff --git a/applications/Volkov/tests/fixtures/mdview2/ORIGIN.md b/applications/Volkov/tests/fixtures/mdview2/ORIGIN.md deleted file mode 100644 index e16bcf3..0000000 --- a/applications/Volkov/tests/fixtures/mdview2/ORIGIN.md +++ /dev/null @@ -1,8 +0,0 @@ -# MDVIEW2 fixture для теста запуска дочернего приложения - -Снимок исходников `examples/mdview2` из Sprinter-SDCC commit `5323b16` -(2026-09-15). Лицензия MIT находится в `LICENSE`. Fixture специально -зафиксирован: тест Commander должен проверять запуск полноэкранного viewer, -PageDown/raw-режим и восстановление окружения после возврата, независимо от -последующих изменений проекта Examples. Обновлять снимок только вместе с -повторной проверкой сценария `mame_p5_viewer.lua` и эталонных кадров. diff --git a/applications/Volkov/tests/fixtures/mdview2/README.md b/applications/Volkov/tests/fixtures/mdview2/README.md deleted file mode 100644 index 0cc3c6a..0000000 --- a/applications/Volkov/tests/fixtures/mdview2/README.md +++ /dev/null @@ -1,153 +0,0 @@ -# MDView v1.0 (b3) — просмотрщик Markdown для Sprinter - -**MDView** — программа просмотра документов Markdown для компьютера -**Sprinter-2000** (Z80, ОС ESTEX). Файл загружается в расширенную память (EMM) -и один раз «прогоняется» через парсер: готовые к показу строки (пары -символ+атрибут) складываются в **рендер-кэш** в EMM, после чего прокрутка в -любую сторону — это просто копирование готовых строк на экран, без повторного -парсинга. Даже на файлах в сотни килобайт листание остаётся мгновенным. - -Текстовый режим 80×32, цветное оформление элементов разметки, три режима -просмотра (MD / RAW / HEX) и четыре кодировки с автоопределением. - ---- - -## Возможности - -* **Markdown-рендеринг** с цветовым оформлением: - * заголовки `#`…`######` (H5/H6 отображаются как H4); - * **жирный** (`**текст**`), *курсив* (`*текст*`), подчёркнутый (`_текст_`), - ~~зачёркнутый~~ (`~~текст~~`), `встроенный код` (`` `текст` ``); - * экранирование `\*`, `\_`, `` \` `` и любой ASCII-пунктуации; - * чекбоксы `[x]` / `[ ]` в списках; - * ненумерованные (`-`, `*`, `+`) и нумерованные (`1.`, `1)`) списки - с базовой вложенностью по отступам; - * цитаты `>` (склейка многострочных, маркер │ на переносах); - * fenced-блоки кода ` ``` ` (без переносов, горизонтальный скролл); - * таблицы `| … | … |` — рисуются псевдографической рамкой, ширины колонок - вычисляются по содержимому (до 16 колонок); - * горизонтальные разделители `---` / `***` / `___`; - * жёсткие переносы (два пробела или `\` в конце строки); - * мягкая склейка абзацев с переносом по словам под ширину экрана. -* **Кодировки: CP866, CP1251, KOI8-R, UTF-8.** - * автоопределение при открытии (BOM → UTF-8; валидность multibyte-структуры; - частотный анализ ходовых русских букв для 8-битных); - * переключение по кругу клавишей **F8** в любой момент; - * 8-битные кодировки отличаются только перекодировкой глифов на отрисовке — - переключение мгновенно; - * UTF-8 декодируется в CP866 в **отдельный набор** (файл + индекс + кэш); - второй набор готовится **в фоне**, пока вы читаете документ, — обычно - к первому нажатию F8 он уже построен и переключение мгновенно, - с сохранением позиции. Если фон не успел, F8 докручивает начатую - сборку (со спиннером), а не начинает её заново. -* **Три режима просмотра:** - * **MD** — форматированный Markdown (по умолчанию); - * **RAW** (**F2**) — исходный текст без разметки: перенос строк кратно 80 - (**F3** — режим панорамы с горизонтальным скроллом); - * **HEX** (**F4**) — дамп *оригинального* файла: - `0x012340 │ 16 байт hex │ 16 печатных символов`. Печатная колонка - интерпретируется текущей кодировкой; для UTF-8 глиф ставится на позиции - лид-байта, continuation-байты показываются точкой. -* **Единая позиция** при любых переключениях: MD ↔ RAW ↔ HEX и смена - кодировки сохраняют текущее место в документе (между наборами разного - размера — пропорционально, с точностью до строки). -* **Прогрессивная загрузка**: первый экран показывается сразу, индексация - продолжается в фоне; по готовой части документа уже можно листать, - **Esc**/**F10** прерывают загрузку. -* **Фоновая работа незаметна**: второй набор кодировки строится только в - паузах между клавишами (после нажатия выдерживается пауза), поэтому - скролл — в том числе с автоповтором — не теряет плавности. - ---- - -## Запуск - -``` -MDVIEW2.EXE <файл.md> -``` - -Без аргумента открывается `README.MD` из текущего каталога. - -## Клавиши - -| Клавиша | Действие | -|--------------|-------------------------------------------------------------| -| ↑ / ↓ | прокрутка на одну строку | -| PgUp / PgDn | прокрутка на экран (30 строк) | -| Home / End | в начало / в конец документа | -| ← / → | горизонтальный сдвиг: код/таблицы в MD, панорама в RAW | -| F1 | справка | -| F2 | RAW-режим ↔ MD | -| F3 | в RAW: перенос строк ↔ панорама | -| F4 | HEX-режим ↔ прежний вид | -| F8 | кодировка: CP866 → CP1251 → KOI8-R → UTF-8 → … | -| Esc / F10 | выход (во время загрузки — прервать её) | - -Статус-бар (верхняя строка): имя файла, кодировка, диапазон видимых строк -и процент прокрутки. Нижняя строка — меню доступных F-клавиш. - ---- - -## Ограничения - -| Параметр | Значение | -|---------------------------------|-------------------------------------------| -| Размер файла | до 256 КБ (больший — обрезается с предупреждением) | -| Логических строк (после переносов) | до 18 432 | -| Длина строки в рендер-кэше | 255 ячеек (RAW/HEX ограничения не имеют) | -| Колонок в таблице | до 16 | -| Шаг табуляции | 4 | - -При исчерпании любого лимита документ завершается строкой-сообщением -с указанием причины обрыва; всё, что вошло, доступно для просмотра. - -## Требования - -* Sprinter-2000 с ОС ESTEX; -* расширенная память (EMM): в худшем случае (файл 256 КБ + оба набора - кодировок) — до ~150 страниц по 16 КБ (~2,4 МБ). Для типичных файлов - в десятки килобайт достаточно нескольких десятков страниц. - -Код, данные, стек и куча программы занимают окна W1+W2 (32 КБ, режим -памяти `small`); окно W3 используется только для доступа к EMM-страницам. - ---- - -## Сборка - -Требуется тулчейн этого репозитория (обёртка `sprinter-cc` над SDCC 4.5). -Из каталога `examples/mdview2`: - -``` -make # собрать mdview2.exe -make floppy # собрать и упаковать дискету для MAME (mc.img) -make run # floppy + автоматический прогон в MAME (скриншоты) -``` - -На дискету кладутся: `MDVIEW2.EXE` и три документа, каждый в своей кодировке -(заодно покрывают все пути автодетекта): `README.MD` — этот файл, как есть -(UTF-8); `DEMO.MD` — демонстрация всех элементов разметки (CP1251); -`CHANGES.MD` — история версий (CP866). - -## Структура исходников - -| Файл | Назначение | -|------------------|--------------------------------------------------------------| -| `mdview2.c` | ядро: EMM-аллокации, рендер-кэш, наборы кодировок, загрузка файла, главный цикл | -| `mdview2_index.c`| парсер/индексатор Markdown — единственный проход по файлу | -| `mdview2_md.c` | MD-вид: отрисовка из кэша, прокрутка | -| `mdview2_raw.c` | RAW-вид (F2/F3) | -| `mdview2_hex.c` | HEX-вид (F4) | -| `mdview2_enc.c` | кодировки: детект, ремап-таблицы, конвертер UTF-8 → CP866 | -| `mdview2_table.c`| отрисовка таблиц | -| `mdview2_status.c`| статус-бар, меню, спиннер | -| `mdview2_help.c` | справка (F1) | -| `mdview2_conf.h` | конфигурация: `WITH_RAW` / `WITH_HEX` (модули отключаемы) | -| `mdview2.h` | общие константы, атрибуты, межмодульный API | - -Подробности архитектуры — в `docs/mdview2-plan.md`. - -## Лицензия и авторы - -© 2026 Петров А.Г. Часть проекта Sprinter C Compiler -(см. LICENSE в корне репозитория). diff --git a/applications/Volkov/tests/fixtures/mdview2/mdview2.c b/applications/Volkov/tests/fixtures/mdview2/mdview2.c deleted file mode 100644 index c4f2898..0000000 --- a/applications/Volkov/tests/fixtures/mdview2/mdview2.c +++ /dev/null @@ -1,1338 +0,0 @@ -/* - * mdview2 — просмотрщик Markdown для Sprinter (render-cache версия). - * - * Этот файл — ЯДРО (инфраструктура + точка входа). Прикладные подсистемы - * вынесены в отдельные модули (общий интерфейс — mdview2.h): - * mdview2_index.c — парсер/индексатор markdown (построение рендер-кэша) - * mdview2_md.c — представление MD-документа (отрисовка из кэша + скролл) - * mdview2_raw.c — RAW-просмотр исходного текста (F2/F3) - * mdview2_enc.c — кодировки CP866/CP1251/KOI8R + UTF-8 конвертер - * mdview2_table.c — выровненная отрисовка таблиц - * mdview2_help.c — диалог справки (F1) - * - * В ядре остаётся: пул рендер-кэша, доступ к EMM/окну W3 (fb/win_rest), - * примитивы экрана, загрузка файла, наборы документа (doc-slots), кооператив- - * ная loading-loop и диспетчер клавиш, сборка наборов и main(). - * - * Разметка экрана (80x32): - * строка 0 — статус-бар (имя файла / диапазон строк / всего / %) - * строки 1..30 — область документа (30 видимых строк текста) - * строка 31 — меню (F1 Help / F2 RAW / F3 Wrap / F8 CodePg / F10 Exit) - * - * Память: - * CODE → W1 (режим sprinter-cc --memory small) - * STACK/HEAP/DATA → W2 - * Буфер файла → страницы EMM (до 8 × 16 КБ = 128 КБ), - * отображаемые в W3 по требованию через fb(). - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mdview2.h" /* геометрия, атрибуты, общий интерфейс модулей */ - -/* ---- Палитра атрибутов ------------------------------------------- */ - -static const uint8_t md_pallete[16 * 4] = { - 0x00, 0x00, 0x00, 0x00, - 0x60, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x00, 0x00, - 0x60, 0x60, 0x00, 0x00, - 0x00, 0x00, 0x60, 0x00, - 0x60, 0x00, 0x60, 0x00, - 0x00, 0x60, 0x60, 0x00, - 0xC8, 0xC8, 0xC8, 0x00, - 0xA0, 0xA0, 0x80, 0x00, - 0xFF, 0xAA, 0xAA, 0x00, - 0xAA, 0xFF, 0xAA, 0x00, - 0xFF, 0xFF, 0xAA, 0x00, - 0xAA, 0xAA, 0xFF, 0x00, - 0xFF, 0xAA, 0xFF, 0x00, - 0xAA, 0xFF, 0xFF, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF -}; - -/* Атрибуты экрана/справки и геометрия — в mdview2.h (общие со всеми модулями). - * Типы записей (idx_rec_t — в mdview2_index.c, cache_rec_t — в mdview2.h), - * словари флагов/стилей (IF_, CK_, INIT_STYLE_) и константы индекса - * (INDEX_RECS_PER_PAGE, MAX_CACHE_LINE_LEN) — в mdview2.h. - * - * В SDCC/z80 критично явно инициализировать static-переменные (`= 0`): - * неинициализированные static-объекты могут конфликтовать по размещению - * и перезаписывать друг друга (см. memory/sdcc_static_storage_gotcha). */ - -#define MAX_INDEX_PAGES 9u /* максимум 9 * 2048 = 18432 сегментов индекса (256 КБ) */ - -/* ================================================================== - * Рендер-кэш: директория и пул EMM-страниц - * ================================================================== - * Каждая логическая строка экрана рендерится РОВНО ОДИН РАЗ (Фаза 2) - * в готовый буфер и больше никогда не парсится повторно при скролле. - * Содержимое живёт в ОТДЕЛЬНОМ пуле EMM-страниц (cache_content_*), - * не путать с idx_rec_t/index_phys[] выше — те остаются неизменными, - * ими пользуется рендер-воркер ПРИ построении кэша (Фаза 2), и их - * нельзя перезатирать, т.к. воркер для одной строки может заглядывать - * в idx-записи СОСЕДНИХ строк (откат cont-сегментов). - * - * ЕДИНЫЙ формат для ВСЕХ строк (решено 2026-06-23 — изначальный план - * различал тип 1/тип 2 по экономии памяти, но строки таблиц — IF_NOWRAP, - * но НЕ IF_CODE — всё равно проходят inline-парсер и могут содержать - * несколько атрибутов, так что "один стиль" для них неверно; бюджет EMM - * (см. sprinter_emm_budget) с большим запасом покрывает (char,attr) - * для всех строк без исключения): контент — `len` пар (char,attr), - * `len` — реальная длина в ЯЧЕЙКАХ без паддинга до SCREEN_W, капается на - * 255 (патологически длинные nowrap-строки урезаются; индикатор обрезки - * добавляется на этапе вывода из кэша, не при рендере в кэш). - */ -/* cache_rec_t (РОВНО 8 байт, см. sprinter_winrest_format) — в mdview2.h. */ - -#define CACHE_DIR_REC_SIZE ((uint16_t)sizeof(cache_rec_t)) -#define CACHE_DIR_RECS_PER_PAGE (16384 / CACHE_DIR_REC_SIZE) /* 2048 */ -#define MAX_CACHE_DIR_PAGES 9u /* симметрично индексу: 9 * 2048 = 18432 записей */ -/* MAX_CACHE_LINE_LEN — в mdview2.h (буфер ячеек g_cells живёт в mdview2_index.c). */ -#define MAX_CACHE_CONTENT_PAGES 40u /* кап контента на набор: 40 * 16 КБ = 640 КБ (80 на оба набора) */ - -static uint8_t cache_dir_blk; -static uint8_t cache_dir_pages; -static uint8_t cache_dir_phys[MAX_CACHE_DIR_PAGES]; - -static uint8_t cache_content_blk[MAX_CACHE_CONTENT_PAGES]; /* id блока на каждую выделенную страницу */ -static uint8_t cache_content_phys[MAX_CACHE_CONTENT_PAGES]; -static uint8_t cache_content_n_pages; /* сколько страниц уже выделено */ -static uint8_t cache_cur_page_idx; /* индекс текущей страницы записи в cache_content_phys */ -static uint16_t cache_cur_off; /* текущее смещение записи в этой странице */ - -/* Доступ к директории рендер-кэша — тот же приём, что у idx_get/idx_put. */ -void cache_dir_get(uint16_t idx, cache_rec_t *r) -{ - uint8_t page = (uint8_t)(idx >> 11); - uint16_t off = (uint16_t)((idx & 0x07FF) << 3); /* 2047 */ - bank_read(cache_dir_phys[page], off, r, CACHE_DIR_REC_SIZE); -} - -void cache_dir_put(uint16_t idx, const cache_rec_t *r) -{ - uint8_t page = (uint8_t)(idx >> 11); - uint16_t off = (uint16_t)((idx & 0x07FF) << 3); /* 2047 */ - bank_write(cache_dir_phys[page], off, r, CACHE_DIR_REC_SIZE); -} - -/* Резервирует nbytes подряд в пуле контента рендер-кэша. - * Запись никогда не разбивается через границу страницы: если остаток - * текущей страницы недостаточен — переходим на следующую целиком, - * жертвуя остатком (проще и безопаснее, чем page-crossing в fb()). - * При нехватке текущего пула — лениво доращиваем его на 1 страницу. - * Возврат: 1 — успех (out_page и out_off заполнены), 0 — EMM кончилась. */ -uint8_t cache_reserve(uint16_t nbytes, uint8_t *out_page, uint16_t *out_off) -{ - if (nbytes == 0 || nbytes > PAGE_SIZE) return 0; - - if (cache_content_n_pages == 0 || - (uint16_t)(cache_cur_off + nbytes) > PAGE_SIZE) { - /* Нужна новая страница: либо пул ещё пуст, либо текущая страница - * не вмещает запись целиком. */ - uint8_t next_idx = (cache_content_n_pages == 0) ? 0 : (uint8_t)(cache_cur_page_idx + 1); - if (next_idx >= cache_content_n_pages) { - if (cache_content_n_pages >= MAX_CACHE_CONTENT_PAGES) - return 0; - uint8_t blk = mem_alloc_pages(1); - if (blk == 0) - return 0; - cache_content_blk[cache_content_n_pages] = blk; - cache_content_phys[cache_content_n_pages] = mem_get_page(blk, 0); - cache_content_n_pages++; - } - cache_cur_page_idx = next_idx; - cache_cur_off = 0; - } - - *out_page = cache_content_phys[cache_cur_page_idx]; - *out_off = cache_cur_off; - cache_cur_off = (uint16_t)(cache_cur_off + nbytes); - return 1; -} - -/* Копирует уже готовый буфер (рендер одной строки, собранный в W1/W2 — - * НЕ в W3, чтобы не конфликтовать с fb()/чтением исходника во время - * рендера) в зарезервированное место в пуле контента. Один bank_write - * на строку, а не по байту. len — РОВНО как у cache_reserve()/bank_write - * — uint16_t, не uint8_t: строка до MAX_CACHE_LINE_LEN=255 ячеек даёт - * до 510 байт (char,attr), это уже не лезет в uint8_t. */ -void cache_commit(uint8_t page, uint16_t off, const void *buf, uint16_t len) -{ - bank_write(page, off, buf, len); -} - -/* emph_to_attr определена ниже (рядом с inline-логикой), нужна в inline_scan. */ -/* emph_to_attr — в mdview2_index.c. */ - -uint16_t n_lines; /* extern: всего строк в индексе текущего набора */ -uint16_t max_lines; /* extern: ёмкость индекса: index_pages * 2048 */ -uint16_t top_line; /* extern: верхняя видимая строка */ -uint32_t file_size; /* extern: размер активного буфера документа */ -static uint8_t file_blk; -static uint8_t file_pages; -static uint8_t file_phys[MAX_PAGES]; -uint8_t index_blk; /* extern: дескриптор EMM-блока индекса */ -uint8_t index_pages; /* */ -uint8_t index_phys[MAX_INDEX_PAGES]; /* extern: физ. страницы индекса */ -uint8_t index_truncated; /* выставляется при исчерпании ёмкости индекса */ -uint8_t g_file_clamped; /* extern: файл >256 КБ обрезан до MAX_FILE при загрузке */ - -/* cur_seg_off/cur_rec (near-состояние индексатора) — в mdview2_index.c. */ -uint8_t cur_page = 0xFF; /* extern: 0xFF = в W3 ещё не показана ни одна страница (extern: кодек инвалидирует) */ -uint8_t viewport_x; /* extern: горизонтальный сдвиг для nowrap-строк */ -char filename[64]; /* extern: статус-бар */ - -/* Прогрессивная загрузка: рисуем первый экран и пускаем скролл, пока - * index_lines() ещё досканирует файл (кооперативно, без прерываний). */ -uint8_t g_view = VIEW_MD; /* extern: активный режим просмотра (существует даже когда доступен только MD режим) */ -#if WITH_RAW -uint8_t g_ready; /* extern: 1 когда первичный документ построен — F2 (RAW) рабочий */ -static uint8_t g_raw_last = VIEW_RAW_WRAP; /* последний под-режим RAW (возврат по F2) */ -#if WITH_HEX -static uint8_t g_hex_last = VIEW_MD; /* вид, из которого вошли в HEX (возврат по F4) */ -#endif -#endif -uint8_t g_loading; /* extern: 1 во время видимой индексации (build_doc) */ -static uint8_t g_first_drawn; /* первый экран уже нарисован */ -uint8_t g_f8_enabled; /* extern: можно ли сейчас переключать кодировку */ -static uint8_t g_utf_failed; /* UTF-8 набор не удалось построить (нет EMM) — пропускаем UTF-8 в цикле F8 */ -uint8_t g_bg_building; /* extern: 1 во время фонового index_step — гасит спиннер индексатора */ -static uint8_t g_bg_doc = 0xFF; /* слот, строящийся в фоне (bg_step); 0xFF = фоновой сборки нет */ -uint8_t g_abort; /* extern: F10/Esc во время загрузки → прервать индексацию */ -static uint16_t g_last_prog; /* n_lines на последнем обновлении статуса */ - -/* Кодировки (ENC_*) и g_encoding — в mdview2.h / mdview2_enc.c. */ -const uint8_t *g_remap; /* extern: активная таблица ремапа (CP1251/KOI8), 0 = без ремапа (CP866)*/ -uint8_t g_scratch_phys; /* extern: EMM-страница под ремап-буфер строки для win_rest */ -static uint8_t g_scratch_blk; /* handle scratch-блока: нужен для освобождения при exit */ - -/* UTF-8 набор (utf_blk/utf_pages/utf_phys/utf_size/utf_avail, конвертер и его - * состояние) — в mdview2_enc.c; разделяемые символы и API — в mdview2.h. - * build_doc() ниже перенаправляет живой file_* на utf_phys и оркеструет - * инкрементальную конвертацию (utf_conv_reset/utf_convert_more). */ - -/* Снимок исходного (8-битного) файлового буфера — набор DOC_8BIT читает его - * как есть, набор DOC_UTF8 читает utf_phys. */ -static uint8_t orig_file_blk; -uint8_t orig_file_pages; /* extern: читает кодек (utf_alloc) */ -uint32_t orig_file_size; /* extern: читает кодек (utf_convert_more) */ -uint8_t orig_file_phys[MAX_PAGES]; /* extern: читает кодек (cv_read) */ - -/* ----------------- Два сосуществующих набора индекс/кэш ----------------- - * DOC_8BIT — исходные байты (CP866/CP1251/KOI8R, различаются ремапом на - * отрисовке); DOC_UTF8 — UTF-8, декодированный в CP866. Оба строятся eager - * при старте (кооперативно), поэтому F8 переключает активный набор мгновенно. - * fb()/idx/cache-аксессоры работают по «живым» глобалам; doc_switch() свапает - * их с сохранённой копией нужного набора. */ -typedef struct { - uint8_t built; - uint8_t enc; /* кодировка отображения этого набора */ - uint8_t file_blk, file_pages; - uint32_t file_size; - uint8_t file_phys[MAX_PAGES]; - uint8_t index_blk, index_pages, index_truncated; - uint16_t n_lines, max_lines; - uint8_t index_phys[MAX_INDEX_PAGES]; - uint8_t cache_dir_blk, cache_dir_pages; - uint8_t cache_dir_phys[MAX_CACHE_DIR_PAGES]; - uint8_t cache_content_n_pages, cache_cur_page_idx; - uint16_t cache_cur_off; - uint8_t cache_content_blk[MAX_CACHE_CONTENT_PAGES]; - uint8_t cache_content_phys[MAX_CACHE_CONTENT_PAGES]; - uint16_t top_line; - uint8_t viewport_x; -} docset_t; - -#define DOC_8BIT 0 -#define DOC_UTF8 1 -static docset_t g_doc[2]; /* built=0 для обоих: _DATA зануляется в crt0 gsinit - * (с 2026-07-04), явный {0}-инициализатор больше - * не нужен — он стоил ~260 байт _INITIALIZER */ -static uint8_t g_active_doc = DOC_8BIT; /* какой набор сейчас в живых глобалах */ -static uint8_t g_disp_doc = DOC_8BIT; /* какой набор показан пользователю */ - -static void doc_switch(uint8_t target); /* forward (нужна в progress_tick) */ - -/* Статус-бар, меню и спиннер — в mdview2_status.c. */ - -/* ================================================================== - * Примитивы экрана, доступ к EMM/окну W3 (fb/win_rest), палитра - * ================================================================== */ - -/* Печатает строку с указанным атрибутом, начиная с позиции (x,y), - * и останавливается на конце строки или правой границе экрана. - * `s` — литерал/статический буфер (CODE/DATA, окна 1/2), поэтому его - * можно передать в BIOS LP_PRINT_LN напрямую, без промежуточной копии. */ -void put_str_attr(uint8_t x, uint8_t y, const char *s, uint8_t attr) -{ - uint8_t len = (x < SCREEN_W) ? (uint8_t)(SCREEN_W - x) : 0; - len = min(len, strlen(s)); - if (len == 0) return; - bios_set_place(y, x); - bios_writeattr_until(s, len, attr, 0); -} - - -/* Полностью очищает указанную строку экрана символами пробела с attr. */ -void fill_row(uint8_t y, uint8_t attr) -{ - bios_clearwin(y, 0, 1, SCREEN_W, attr); -} - -/* Процент 0..100 = num*100/den на чистой 16-битной арифметике — чтобы НЕ - * тянуть 32-битное деление __divulong (~760 Б) ради показа процента в статусе. - * Оба вызова (MD/RAW) дают операнды, влезающие в 16 бит (≤18432 и ≤1024), - * переполняет только *100 — поэтому масштабируем оба вниз, пока num*100 не - * влезёт в uint16, затем считаем долю циклом-вычитанием (без div-хелпера). - * Инвариант вызовов: num ≤ den; результат всё равно ограничен 100. */ -uint8_t pct16(uint16_t num, uint16_t den) -{ - if (den == 0) return 0; - while (num > 655) { num >>= 1; den >>= 1; } /* num*100 ≤ 65500 < 65536 */ - if (den == 0) return 100; /* страховка от num>den */ - uint16_t n = (uint16_t)(num * 100u); - uint8_t p = 0; - while (n >= den && p < 100) { n = (uint16_t)(n - den); p++; } - return p; -} - - -/* Отображает указанную EMM-страницу файла в окно W3. - * Если эта страница уже активна, переключение не выполняется. */ -// static inline void map_page(uint8_t page) -// { -// if (page != cur_page) { -// /* file_phys[] позволяет не вызывать mem_get_page() на каждом свопе. */ -// sprinter_page_w3(file_phys[page]); -// cur_page = page; -// } -// } - - -/* Читает один байт логического файла по смещению p. - * При переходе между страницами автоматически переключает W3. - * Проверка границ выполняется вызывающей стороной. - * - * Эквивалент C-кода (оставлен для справки): - * uint8_t *pb = (uint8_t *)&p; - * uint8_t page = (pb[1] >> 6) | (pb[2] << 2); - * uint16_t off = ((pb[1] & 0x3F) << 8) | pb[0]; - * map_page(page); // if (page!=cur_page){cur_page=page; OUT W3,file_phys[page];} - * return FILE_BUF[off]; // *(0xC000 + off) - * - * Ручной asm — это горячая примитивная операция (вызывается сотни тысяч раз - * при индексации 50КБ-файла); C-версия тратила вызов функции + 32-битную - * арифметику на КАЖДЫЙ байт. Раскладка __sdcccall(1): аргумент uint32_t в - * HLDE (E=pb[0], D=pb[1], L=pb[2], H=pb[3] — проверено sdcc -S), возврат - * char в A. Координация маппинга с bank_read/bank_write держится на том, - * что _io_page_w3 — это ПОРТ (__sfr 0xE2): здесь пишем его через OUT, - * bank_read читает через IN — общий аппаратный регистр, без теневой RAM. - * NB: __naked обязателен (внутри собственный ret); IX/IY не трогаем - * (callee-saved), BC/DE/HL/AF — caller-saved, можно клобберить. */ -char fb(uint32_t p) __naked -{ - (void)p; /* p = HLDE; доступ только из asm */ - __asm - ; off -> BC = 0xC000 + ((pb[1]&0x3F)<<8 | pb[0]) ; D=pb[1], E=pb[0] - ld a, d - or a, #0xC0 ; (D|0xC0) == (D&0x3F)|0xC0 — старший байт окна W3 - ld b, a - ld c, e ; C = pb[0] - - ; page = (pb[1]>>6) | (pb[2]<<2) ; L=pb[2] - ld a, l - add a, a - add a, a ; A = pb[2]<<2 - ld e, a ; E свободен (pb[0] уже сохранён в C) - - ld a, d - rlca - rlca - and a, #0x03 ; A = pb[1]>>6 - or a, e ; A = page - ld e, a ; E = page - - ; map_page(page) - ld a, (_cur_page) - cp e - jr Z, fb_same - ld a, e - ld (_cur_page), a - ld hl, #_file_phys - ld d, #0x00 - add hl, de ; hl = &file_phys[page] - ld a, (hl) - out (#0xE2), a ; OUT PORT_PAGE_W3 (== _io_page_w3 sfr) - fb_same: - ld a, (bc) ; A = *(0xC000 + off) - ret - __endasm; -} - -/* ESTEX WINREST (5Ah, RST 0x10) — копирует готовый буфер (char,attr) - * из смещения `off` ВНУТРИ EMM-страницы `page` прямо в текстовое окно - * экрана, минуя любые BIOS print-вызовы. Формат буфера подтверждён - * эмпирически в MAME (см. memory/sprinter_winrest_format.md и - * tests/winrest/winrest.c): пары (char,attr) по ячейкам, по строкам, - * шаг строки = width*2, без паддинга. row/col — левый верхний угол - * вывода на экране, h/w — высота/ширина в символах. - * - * Регистровая раскладка SDCC __sdcccall(1) для (row,col,h,w,page,off) - * проверена через `sdcc -S` (пробный non-naked враппер): A=row, L=col, - * стек [SP+2]=h [SP+3]=w [SP+4]=page [SP+5..6]=off (little-endian). - * - * История: до 2026-06-23 `off` не было — IX всегда указывал на самое - * начало страницы (#0xC000), это работало только пока в кэше было - * РОВНО ОДНА запись на странице со смещением 0 (Фаза 1 self-test). - * Когда Фаза 2 начала пакетировать много строк на одной странице с - * РАЗНЫМИ off, всё стало читать с начала страницы (т.е. контент первой - * записи) независимо от реальной строки — добавлен off, IX = 0xC000+off. */ -void win_rest(uint8_t row, uint8_t col, uint8_t h, uint8_t w, uint8_t page, uint16_t off) __naked -{ - (void)row; (void)col; (void)h; (void)w; (void)page; (void)off; - __asm - ld iy, #2 - add iy, sp - ld d, a ; D = row - ld e, l ; E = col - push ix ; сохранить IX вызывающей функции (callee-saved) - ld l, 3 (iy) ; off (младший байт), SP+5 - ld h, 4 (iy) ; off (старший байт), SP+6 - ld bc, #0xC000 - add hl, bc - push hl - pop ix ; IX = 0xC000 + off - ld h, 0 (iy) ; H = height - ld a, 1 (iy) ; width -> L - ld l, a - ld a, 2 (iy) ; page -> B - ld b, a - ld c, #0x5A ; ESTEX WINREST - di - rst #0x10 - ei - pop ix ; восстановить IX вызывающей функции - pop hl ; адрес возврата - inc sp - inc sp - inc sp - inc sp - inc sp ; снять h,w,page,off (1+1+1+2 байт) со стека - jp (hl) - __endasm; -} - -/* Char-хелперы, idx_get/idx_put/seg_off, set_*_cur — в mdview2_index.c. */ - -/* Загружает 16 текстовых атрибутов в палитры ink/paper/blink. */ -static void set_pallete(void) { - uint8_t buff[64]; - for(int i = 0; i < 16; i++) { - for(int j = 0; j < 16; j++) { - buff[j * 4] = md_pallete[i * 4]; - buff[j * 4 + 1] = md_pallete[i * 4 + 1]; - buff[j * 4 + 2] = md_pallete[i * 4 + 2]; - buff[j * 4 + 3] = md_pallete[i * 4 + 3]; - } - - text_pal_load(TEXT_PAL_INK, i * 16, 16, md_pallete); - text_pal_load(TEXT_PAL_BLINK_INK, i * 16, 16, md_pallete); - text_pal_load(TEXT_PAL_PAPER, i * 16, 16, buff); - text_pal_load(TEXT_PAL_BLINK_PAPER, i * 16, 8, buff + 32); - text_pal_load(TEXT_PAL_BLINK_PAPER, i * 16 + 8, 8, buff + 32); - } -} - -/* ================================================================== - * Загрузка/выгрузка файла - * ================================================================== */ - -/* Загружает исходные байты файла в EMM-страницы (без индекса/кэша — те - * выделяются отдельно на КАЖДЫЙ набор через alloc_set_storage()). - * Возврат: 0 — успех, отрицательное значение — код ошибки. */ -static int load_file(const char *path) -{ - int fd = open(path, O_RDONLY); - if (fd < 0) - return -1; - - long sz = lseek(fd, 0, SEEK_END); - if (sz < 0) { - close(fd); - return -2; - } - /* Файл больше MAX_FILE (256 КБ) не отвергаем, а КЛАМПим: читаем первые - * 256 КБ, индексатор допишет строку-предупреждение (TRUNC_FILE). */ - g_file_clamped = 0; - if ((uint32_t)sz > MAX_FILE) { - sz = (long)MAX_FILE; - g_file_clamped = 1; - } - - file_size = (uint32_t)sz; - lseek(fd, 0, SEEK_SET); - - /* Выделяем ровно столько EMM-страниц, сколько нужно под файл. */ - file_pages = (uint8_t)((file_size + PAGE_SIZE - 1u) / PAGE_SIZE); - if (file_pages == 0) file_pages = 1; - file_blk = mem_alloc_pages(file_pages); - if (file_blk == 0) { - close(fd); - return -3; - } - - /* Читаем файл кусками по 16 КБ, последовательно маппя страницы в W3. - * На последней итерации читаем остаток меньше PAGE_SIZE. */ - uint32_t remaining = file_size; - for (uint8_t page = 0; page < file_pages; page++) { - spinner_tick(); - uint8_t phys = mem_get_page(file_blk, page); - file_phys[page] = phys; - sprinter_page_w3(phys); - cur_page = page; - uint16_t chunk = (remaining > PAGE_SIZE) - ? (uint16_t)PAGE_SIZE - : (uint16_t)remaining; - int n = read(fd, FILE_BUF, chunk); - if (n != (int)chunk) { - close(fd); - return -4; - } - remaining -= chunk; - } - close(fd); - return 0; -} - -/* Выделяет в живые глобалы блок индекса + директорию рендер-кэша под текущий - * набор. file_pages должно быть уже выставлено (размер набора). Сбрасывает - * пул контента в пустое состояние (страницы доращиваются лениво при рендере). - * Возврат: 0 — успех, -5/-6 — нехватка EMM. */ -static int alloc_set_storage(void) -{ - uint8_t want; - - want = (uint8_t)((file_pages >> 1) + 1u); - if (want > MAX_INDEX_PAGES) want = MAX_INDEX_PAGES; - index_blk = mem_alloc_pages(want); - if (index_blk == 0) return -5; - index_pages = want; - for (uint8_t i = 0; i < index_pages; i++) - index_phys[i] = mem_get_page(index_blk, i); - max_lines = (uint16_t)index_pages * INDEX_RECS_PER_PAGE; - - want = (uint8_t)((file_pages >> 1) + 1u); - if (want > MAX_CACHE_DIR_PAGES) want = MAX_CACHE_DIR_PAGES; - cache_dir_blk = mem_alloc_pages(want); - if (cache_dir_blk == 0) return -6; - cache_dir_pages = want; - for (uint8_t i = 0; i < cache_dir_pages; i++) - cache_dir_phys[i] = mem_get_page(cache_dir_blk, i); - - cache_content_n_pages = 0; - cache_cur_page_idx = 0; - cache_cur_off = 0; - return 0; -} - -static void doc_save_live(uint8_t i); /* forward */ - -/* Освобождает все EMM-блоки обоих наборов (индекс/директория/пул контента) - * плюс два файловых буфера (исходный 8-битный и UTF-8). */ -static void unload_file(void) -{ - doc_save_live(g_active_doc); /* зафиксировать живой набор в g_doc */ - for (uint8_t s = 0; s < 2; s++) { - docset_t *d = &g_doc[s]; - if (d->index_blk) { mem_free_block(d->index_blk); d->index_blk = 0; } - if (d->cache_dir_blk) { mem_free_block(d->cache_dir_blk); d->cache_dir_blk = 0; } - for (uint8_t i = 0; i < d->cache_content_n_pages; i++) - if (d->cache_content_blk[i]) mem_free_block(d->cache_content_blk[i]); - d->cache_content_n_pages = 0; - } - if (orig_file_blk) { mem_free_block(orig_file_blk); orig_file_blk = 0; } - if (utf_blk) { mem_free_block(utf_blk); utf_blk = 0; } - if (g_scratch_blk) { mem_free_block(g_scratch_blk); g_scratch_blk = 0; } - file_blk = 0; -} - -/* Парсер/индексатор (classify_line, scan_join_stream, inline_scan, index_lines, emph_to_attr) — в mdview2_index.c. */ - - -/* Таблицы ремапа, set_encoding, кодировки и UTF-8 конвертер — в mdview2_enc.c. */ - -/* ================================================================== - * Наборы документа (doc-slots): сохранение/свап - * ================================================================== - * Два сосуществующих набора (DOC_8BIT / DOC_UTF8) живут как копии всего - * состояния (file/index/cache/позиция) в g_doc[]; «живые» глобалы — это - * активный набор. doc_save_live: живые → g_doc[i]; doc_load_live: g_doc[i] - * → живые (+ инвалидация маппинга fb() и активной кодировки). */ -static void doc_save_live(uint8_t i) -{ - docset_t *d = &g_doc[i]; - d->enc = g_encoding; - d->file_blk = file_blk; d->file_pages = file_pages; d->file_size = file_size; - memcpy(d->file_phys, file_phys, MAX_PAGES); - d->index_blk = index_blk; d->index_pages = index_pages; d->index_truncated = index_truncated; - d->n_lines = n_lines; d->max_lines = max_lines; - memcpy(d->index_phys, index_phys, MAX_INDEX_PAGES); - d->cache_dir_blk = cache_dir_blk; d->cache_dir_pages = cache_dir_pages; - memcpy(d->cache_dir_phys, cache_dir_phys, MAX_CACHE_DIR_PAGES); - d->cache_content_n_pages = cache_content_n_pages; - d->cache_cur_page_idx = cache_cur_page_idx; d->cache_cur_off = cache_cur_off; - memcpy(d->cache_content_blk, cache_content_blk, MAX_CACHE_CONTENT_PAGES); - memcpy(d->cache_content_phys, cache_content_phys, MAX_CACHE_CONTENT_PAGES); - d->top_line = top_line; d->viewport_x = viewport_x; -} - -static void doc_load_live(uint8_t i) -{ - docset_t *d = &g_doc[i]; - file_blk = d->file_blk; file_pages = d->file_pages; file_size = d->file_size; - memcpy(file_phys, d->file_phys, MAX_PAGES); - index_blk = d->index_blk; index_pages = d->index_pages; index_truncated = d->index_truncated; - n_lines = d->n_lines; max_lines = d->max_lines; - memcpy(index_phys, d->index_phys, MAX_INDEX_PAGES); - cache_dir_blk = d->cache_dir_blk; cache_dir_pages = d->cache_dir_pages; - memcpy(cache_dir_phys, d->cache_dir_phys, MAX_CACHE_DIR_PAGES); - cache_content_n_pages = d->cache_content_n_pages; - cache_cur_page_idx = d->cache_cur_page_idx; cache_cur_off = d->cache_cur_off; - memcpy(cache_content_blk, d->cache_content_blk, MAX_CACHE_CONTENT_PAGES); - memcpy(cache_content_phys, d->cache_content_phys, MAX_CACHE_CONTENT_PAGES); - top_line = d->top_line; viewport_x = d->viewport_x; - cur_page = 0xFF; /* следующий fb() пере-смаппит страницу */ - set_encoding(d->enc); -} - -static void doc_switch(uint8_t target) -{ - if (target == g_active_doc) - return; - doc_save_live(g_active_doc); - doc_load_live(target); - g_active_doc = target; -} - -/* Автоопределение/имя кодировки, ремап-вывод и UTF-8→CP866 конвертер — в mdview2_enc.c. */ - -/* Отрисовка области/статус-бара/меню и прокрутка — в модуле mdview2_md.c. */ - - - -/* ================================================================== - * Прогрессивная загрузка и диспетчер клавиш (MD) - * ================================================================== - * index_lines() строит индекс кооперативно и периодически зовёт - * progress_tick(): тот рисует первый готовый экран, крутит спиннер и - * обрабатывает клавиши на лету (load_key). Диалог справки (F1) — в - * mdview2_help.c, RAW-навигация — в mdview2_raw.c. */ - -/* Обработка клавиши во время прогрессивной загрузки: скролл строго в - * пределах готовых строк (drawable_lines); End = последняя готовая страница; - * F1 = справка (show_help сам блокирует и перерисовывает). */ -static void load_key(uint16_t k) -{ - uint8_t ascii = (uint8_t)(k & 0xFF); - uint8_t scan = (uint8_t)((k >> 8) & 0x7F); - uint16_t dr = drawable_lines(); - uint16_t maxtop = (dr > VIEW_H) ? (uint16_t)(dr - VIEW_H) : 0; - uint16_t nt = top_line; - - if (ascii == 0x1B || scan == KEY_F10) { - g_abort = 1; - return; - } /* Esc/F10 → выход */ - if (ascii) - return; /* прочие ascii игнорируем */ - - switch (scan) { - case KEY_UP: - nt = (top_line >= 1) ? (uint16_t)(top_line - 1) : 0; - break; - case KEY_DOWN: - nt = top_line + 1; - break; - case KEY_PGUP: - nt = (top_line >= VIEW_H) ? (uint16_t)(top_line - VIEW_H) : 0; - break; - case KEY_PGDN: - nt = top_line + VIEW_H; - break; - case KEY_HOME: - top_line = 0; - viewport_x = 0; - draw_viewport_from_cache(); - render_md_status_numbers(); - return; - case KEY_END: - top_line = maxtop; - viewport_x = 0; - draw_viewport_from_cache(); - render_md_status_numbers(); - return; - case KEY_LEFT: - md_scroll_horizon(-(int8_t)HPAN_STEP); - render_md_status_numbers(); - return; - case KEY_RIGHT: - md_scroll_horizon(+(int8_t)HPAN_STEP); - render_md_status_numbers(); - return; - case KEY_F1: - show_help(); - return; - case KEY_F8: - /* во время сборки крутим ТОЛЬКО 8-бит и только если набор 8-битный - * (ремап на отрисовке; индексация не зависит от кодировки) */ - if (g_f8_enabled && g_active_doc == DOC_8BIT) { - uint8_t nx = (g_encoding == ENC_CP866) ? ENC_CP1251 : - (g_encoding == ENC_CP1251) ? ENC_KOI8R : ENC_CP866; - set_encoding(nx); - g_doc[DOC_8BIT].enc = nx; - draw_viewport_from_cache(); - render_full_status(); - } - return; - default: - return; - } - - if (nt > maxtop) - nt = maxtop; - if (nt != top_line) { - top_line = nt; - draw_viewport_from_cache(); - render_md_status_numbers(); - } -} - -/* Кооперативный «тик» прогрессивной загрузки — вызывается из index_lines(). */ -void progress_tick(void) -{ -#if WITH_RAW - /* В RAW-режиме буфер документа готов сразу после конвертации (до индекса), - * поэтому первый экран RAW можно показать немедленно — рисуем raw_draw, а - * не MD-вид (иначе мелькал бы MD). Индекс при этом строится дальше (нужен - * для MD-вида). */ - if (g_view != VIEW_MD) { -#if WITH_HEX - if (g_view == VIEW_HEX) { /* HEX показывает оригинал — экран уже актуален */ - spinner_tick(); - return; - } -#endif - if (!g_first_drawn) { - raw_draw(); - // render_raw_status(); - g_first_drawn = 1; - cur_page = 0xFF; /* raw_draw сбил W3-маппинг, на который опирается fb() */ - } - spinner_tick(); - return; - } -#endif - /* Прогрессивная отрисовка строящегося (всегда показываемого) набора. */ - uint8_t touched = 0; - if (!g_first_drawn && drawable_lines() >= VIEW_H) { - draw_viewport_from_cache(); - render_md_status_numbers(); - g_first_drawn = 1; - touched = 1; - } - if (n_lines >= (uint16_t)(g_last_prog + 10)) { - g_last_prog = n_lines; - spinner_tick(); - if (g_first_drawn) - render_md_status_numbers(); - touched = 1; - } - if (g_first_drawn && kbhit()) { - load_key(getkey()); - touched = 1; - } - /* Отрисовка/WINREST/BIOS могли сбить маппинг W3, на который опирается fb() - * при индексации → форсируем ре-маппинг страницы файла на следующем fb(). */ - if (touched) - cur_page = 0xFF; -} - -/* ================================================================== - * Точка входа - * ================================================================== */ - -/* Строит набор `slot` кооперативно (index_step + progress_tick). - * visible=1 — первичный (показываемый): прогрессивная отрисовка первого экрана - * и скролл по готовым строкам. visible=0 — вторичный: индексация идёт в живых - * глобалах, а progress_tick через свап показывает/листает уже готовый - * g_disp_doc. Возврат: 0 — успех, <0 — нехватка EMM (-5/-6/-7). - * - * Если slot прямо сейчас строится ФОНОМ (g_bg_doc, см. bg_step ниже) — - * не начинаем заново, а ДОКРУЧИВАЕМ: загружаем частично построенный набор - * и продолжаем с сохранённой позиции индексатора (его межшаговое состояние - * живёт в статиках модуля index и переживает свапы наборов). - * - * UTF-8 набор конвертируется ИНКРЕМЕНТАЛЬНО: выделяем страницы, переключаем - * живой буфер на utf и запускаем конвертацию первого чанка; дальше index_step - * докручивает её впереди позиции чтения (g_utf_building). Конвертер читает - * оригинал из orig_file_phys[] через cv_read (не через fb, который теперь - * указывает на utf). */ -/* Настраивает живые глобалы под сборку набора slot: файловый буфер (utf/orig), - * индекс + директория кэша, сброс индексатора. Общая часть build_doc и - * bg_build_start. Возврат: 0 — успех, <0 — нехватка EMM (-5/-6/-7). */ -static int doc_setup(uint8_t slot) -{ - if (slot == DOC_UTF8) { - if (!utf_avail) { /* первый вход: выделить страницы */ - if (!utf_alloc()) - return -7; /* выделяет страницы + сбрасывает конвертер */ - utf_avail = 1; - } - file_blk = utf_blk; - file_pages = utf_pages; - memcpy(file_phys, utf_phys, MAX_PAGES); - if (g_utf_building) { - file_size = 0; - cur_page = 0xFF; - utf_convert_more(CONV_MARGIN); /* первый чанк — для первого экрана */ - } else { - file_size = utf_size; - } - } else { - file_blk = orig_file_blk; - file_pages = orig_file_pages; - file_size = orig_file_size; - memcpy(file_phys, orig_file_phys, MAX_PAGES); - } - cur_page = 0xFF; - - int rc = alloc_set_storage(); - if (rc < 0) - return rc; - index_begin(); - return 0; -} - -static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible) -{ - if (g_bg_doc == slot) { /* фон уже строит его — продолжить */ - doc_load_live(slot); - g_bg_doc = 0xFF; - } else { - int rc = doc_setup(slot); - if (rc < 0) - return rc; - } - - set_encoding(enc); - top_line = 0; - viewport_x = 0; - g_active_doc = slot; - if (visible) { - g_disp_doc = slot; - g_first_drawn = 0; - status_encoding(); /* показать кодировку сразу (детект/F8), не дожидаясь индексации */ - cur_page = 0xFF; /* BIOS-вывод мог сбить W3-маппинг, на который опирается fb() */ - } - - g_loading = 1; - g_last_prog = 0; - while (!index_step(255)) {} - g_loading = 0; - - /* Зафиксировать конвертацию (на случай обрыва индекса по max_lines до конца - * исходника) — иначе g_utf_building остался бы 1 и сломал бы следующую сборку. */ - if (g_utf_building) { - utf_size = file_size; - g_utf_building = 0; - } - - g_doc[slot].built = 1; - doc_save_live(slot); - return 0; -} - -/* ================================================================== - * Фоновая сборка второго набора (idle-time, между клавишами) - * ================================================================== - * Пока пользователь читает документ, главный цикл в паузах между клавишами - * докручивает индексацию второго набора (UTF-8 при 8-битном первичном и - * наоборот) порциями index_step: свап на bg-набор → несколько итераций → - * свап обратно. Экран при этом не трогается (g_bg_building гасит спиннер), - * задержка реакции на клавишу — не больше одного шага. По готовности F8 - * переключает наборы мгновенно; F8 ДО готовности докручивает сборку видимо - * (ветка resume в build_doc), не начиная её заново. */ - -#define BG_STEP_BUDGET 2u /* итераций индексатора на idle-шаг: латентность клавиши - * = длительность шага (~единицы мс на обычном абзаце) */ -#define BG_KEY_HOLDOFF 300u /* пустых опросов kbhit после клавиши до возобновления - * фона — при автоповторе (скролл) фон не вклинивается */ - -/* Готовит фоновую сборку второго набора: настраивает живые глобалы (буфер, - * индекс, кэш, index_begin) так же, как build_doc, но БЕЗ индексации, и - * возвращает живым прежний набор. При нехватке EMM фон просто не стартует — - * остаётся сегодняшний ленивый путь по F8. */ -static void bg_build_start(void) -{ - uint8_t slot = (uint8_t)((g_active_doc == DOC_8BIT) ? DOC_UTF8 : DOC_8BIT); - uint8_t vis = g_active_doc; - if (g_doc[slot].built || (slot == DOC_UTF8 && g_utf_failed)) - return; /* строить нечего */ - - doc_save_live(vis); - if (doc_setup(slot) < 0) { /* нет EMM — как и в ленивом пути */ - if (slot == DOC_UTF8) g_utf_failed = 1; - doc_load_live(vis); - return; - } - top_line = 0; - viewport_x = 0; - - g_active_doc = slot; /* живые глобалы = bg-набор... */ - doc_switch(vis); /* ...сохранить его и вернуть видимый */ - g_bg_doc = slot; -} - -/* Один idle-шаг фона: свап на строящийся набор, порция индексации, свап - * обратно. Межшаговое состояние индексатора/UTF-конвертера — статики их - * модулей, обработчики клавиш видимого набора их не трогают. По завершении - * финализирует набор (дальше F8 переключается мгновенно). */ -static void bg_step(void) -{ - uint8_t vis = g_active_doc; - doc_switch(g_bg_doc); - g_bg_building = 1; - uint8_t done = index_step(BG_STEP_BUDGET); - g_bg_building = 0; - if (done) { - if (g_utf_building) { /* как build_doc: обрыв индекса до конца исходника */ - utf_size = file_size; - g_utf_building = 0; - } - g_doc[g_bg_doc].built = 1; /* doc_save_live поле built не трогает */ - g_bg_doc = 0xFF; - } - doc_switch(vis); -} - -/* Пропорциональный перенос байт-позиции между буферами разного размера - * (8bit ↔ UTF-8 при F8): off * to / from БЕЗ __mullong/__divulong. Один - * цикл restoring-деления: бит k дроби off/from (сравнение с from/2^k) - * сразу добавляет to/2^k в результат — сумма to/2^k по установленным - * битам и есть off*to/from. Точность from/65536 (≤4 байта на максимальном - * файле); ляжет на границу строки через line_at_off / raw_reanchor. */ -static uint32_t map_off(uint32_t off, uint32_t from, uint32_t to) -{ - if (from == to || from == 0) return off; - uint32_t res = 0; - for (uint8_t i = 0; i < 16; i++) { - from >>= 1; - to >>= 1; - if (off >= from) { off -= from; res += to; } - } - return res; -} - -/* Байт-позиция текущего вида (верх экрана) — единая «валюта» для переноса - * позиции между видами/наборами. Валюта = offset АКТИВНОГО буфера; HEX живёт - * в байтах оригинала, для UTF-набора конвертируем через map_off. */ -static uint32_t view_pos(void) -{ -#if WITH_HEX - if (g_view == VIEW_HEX) - return (g_active_doc == DOC_UTF8) - ? map_off(hex_pos(), orig_file_size, file_size) - : hex_pos(); -#endif -#if WITH_RAW - if (g_view != VIEW_MD) return raw_pos(); -#endif - return seg_off(top_line); -} - -/* Поставить текущий вид на байт-позицию off (активного буфера). */ -static void view_reanchor(uint32_t off) -{ -#if WITH_HEX - if (g_view == VIEW_HEX) { - hex_reanchor((g_active_doc == DOC_UTF8) - ? map_off(off, file_size, orig_file_size) - : off); - return; - } -#endif -#if WITH_RAW - if (g_view != VIEW_MD) { raw_reanchor(off); return; } -#endif - top_line = line_at_off(off); -} - -/* Переключает кодировку на `next` (вызывается из главного цикла по F8). - * 8-бит↔8-бит внутри одного набора — мгновенный ремап; переход в другой набор - * — мгновенный doc_switch, если он построен, иначе ЛЕНИВАЯ сборка по месту - * (со спиннером), результат кэшируется. */ -/* Меняет кодировку: тот же набор — ремап; другой готовый — doc_switch; - * непостроенный — ЛЕНИВАЯ ТИХАЯ сборка (спиннер, без отрисовки документа). - * НЕ перерисовывает документ/статус — это делает вызывающий в своём режиме - * (MD: draw_viewport_from_cache; RAW: raw_draw), чтобы не мелькал чужой вид. */ -static void switch_encoding(uint8_t next) -{ - uint8_t target = (next == ENC_UTF8) ? DOC_UTF8 : DOC_8BIT; - - if (target == g_active_doc) { /* тот же набор — только ремап */ - set_encoding(next); - g_doc[target].enc = next; - } else if (g_doc[target].built) { /* набор готов — мгновенный свап */ - /* Позицию переносим пропорцией байтов (буферы наборов разного - * размера), а не из сохранённой в g_doc копии — иначе каждый набор - * скачет по своей старой позиции. */ - uint32_t old_off = view_pos(); - uint32_t old_size = file_size; - doc_switch(target); - g_disp_doc = target; - set_encoding(next); - g_doc[target].enc = next; - view_reanchor(map_off(old_off, old_size, file_size)); - } else { /* строим набор лениво */ - doc_save_live(g_active_doc); /* сохранить текущий (позиция и т.п.) */ -#if WITH_RAW - uint32_t raw_save = raw_pos(); - if (VIEW_IS_RAW(g_view)) /* HEX показывает оригинал — его позиция инвариантна */ - raw_reanchor(0); /* прогрессивный показ нового буфера — с начала */ - g_ready = 0; - render_menu(); /* идёт индексация — прячем F2 */ -#endif - spinner_show(1); - int rc = build_doc(target, next, 1); /* visible: прогрессивный показ первого экрана - * (progress_tick рисует по текущему виду — MD/RAW) */ - spinner_show(0); -#if WITH_RAW - g_ready = 1; - render_menu(); -#endif - if (rc < 0) { /* нет EMM — откат на текущий набор */ - if (target == DOC_UTF8) - g_utf_failed = 1; - doc_load_live(g_active_doc); -#if WITH_RAW - if (VIEW_IS_RAW(g_view)) - raw_reanchor(raw_save); /* буфер прежний — вернуть позицию RAW */ -#endif - return; - } - g_disp_doc = target; /* build_doc уже выставил active/enc */ - } - clamp_top(); /* привести top_line к новому набору (для MD) */ -} - -/* ================================================================== - * Вывод ошибки - * ================================================================== */ - -static void on_exit(void) { - unload_file(); - pal_reset(PAL_CGA); -} - -int main(int argc, char **argv) -{ - const char *path; - if (argc >= 2) { - path = argv[1]; - } else { - path = "README.MD"; - /* Дефолтный файл отсутствует — выводим подсказку без смены режима экрана. */ - { - int chk = open(path, O_RDONLY); - if (chk < 0) { - // settextmode(TEXT_MODE_80x32); - clrscr_attr(ATTR_TEXT); - cputs("MDView v0.3 -- Markdown Viewer for Sprinter\r\n" \ - "(c) 2026 Petrov A.G.\r\n\r\n" \ - "Usage: mdview2 \r\n" \ - " Displays a Markdown document (CP866/CP1251/KOI8R/UTF8, up to 128 KB).\r\n"); - exit(-1); - } - close(chk); - } - } - - atexit(on_exit); - - /* Копируем путь в static filename[] для отображения в статус-баре. */ - { - uint8_t i = 0; - while (i < (uint8_t)(sizeof(filename) - 1) && path[i]) { - filename[i] = path[i]; - i++; - } - filename[i] = 0; - } - - /* Сначала поднимаем UI, чтобы пользователь видел шапку/меню/спиннер - * уже во время загрузки файла и построения индекса. */ - settextmode(TEXT_MODE_80x32); - clrscr_attr(ATTR_TEXT); - set_pallete(); - prerender_status(); - render_menu(); - render_full_status(); - render_percent_progress(0); - spinner_show(1); - - int rc = load_file(path); - if (rc < 0) { - spinner_show(0); - clrscr_attr(ATTR_RESET); - textattr(ATTR_RESET); - cputs("MDView: cannot load file '"); - cputs(path); - cputs("'\n\rError: "); - switch (rc) { - case -1: cputs("open failed"); break; - case -2: cputs("seek failed"); break; - case -3: cputs("mem_alloc_pages failed"); break; - case -4: cputs("short read"); break; - case -5: cputs("index alloc failed"); break; - case -6: cputs("render-cache dir alloc failed"); break; - } - cputs("\n\r\n\r"); - exit(-1); - } - /* Scratch-страница под ремап-буфер строки (win_rest_remap). Handle - * сохраняем отдельно: физический номер страницы нельзя передавать - * mem_free_block(), а on_exit() обязан вернуть EMM системе. */ - g_scratch_blk = mem_alloc_pages(1); - if (g_scratch_blk == 0) { - spinner_show(0); - clrscr_attr(ATTR_RESET); - textattr(ATTR_RESET); - cputs("MDView: scratch page allocation failed\n\r"); - exit(-1); - } - g_scratch_phys = mem_get_page(g_scratch_blk, 0); - - /* Оба набора пока не построены (страховка к инициализатору g_doc). */ - g_doc[DOC_8BIT].built = 0; - g_doc[DOC_UTF8].built = 0; - - /* Снимок исходного 8-битного буфера (его читает набор DOC_8BIT). */ - orig_file_blk = file_blk; orig_file_pages = file_pages; orig_file_size = file_size; - memcpy(orig_file_phys, file_phys, MAX_PAGES); - - /* Детекция кодировки по оригиналу. UTF-8 конвертацию НЕ делаем здесь — - * она дорогая (полный проход) и нужна только для UTF-8 набора, который - * строится через build_doc лениво. Так первый экран рисуется по индексации - * первичного набора без лишнего прохода. */ - uint8_t det = detect_encoding(); - - /* Первичный (показываемый) набор — по детекции; второй строится ЛЕНИВО по - * первому F8 (switch_encoding). Во время сборки 8-битного первичного F8 - * разрешён (ремап 8-бит, см. load_key) → показываем метку F8; для UTF-8 - * первичного переключаться пока некуда → метку прячем. */ - uint8_t prim_enc = det; - uint8_t prim_doc = (prim_enc == ENC_UTF8) ? DOC_UTF8 : DOC_8BIT; - g_f8_enabled = (prim_doc == DOC_8BIT); - render_menu(); - - rc = build_doc(prim_doc, prim_enc, 1); /* первичный — видимый */ - if (rc < 0 && prim_doc == DOC_UTF8) { /* UTF-8 не вышел (нет EMM) → 8-бит */ - prim_doc = DOC_8BIT; prim_enc = ENC_CP866; - rc = build_doc(DOC_8BIT, ENC_CP866, 1); - } - if (rc < 0) { - spinner_show(0); - clrscr_attr(ATTR_RESET); - textattr(ATTR_RESET); - cputs("MDView: index/cache alloc failed\n\r\n\r"); - exit(-1); - } - if (g_abort) - exit(0); /* F10/Esc во время загрузки — корректный выход */ - if (n_lines == 0) { - clrscr_attr(ATTR_RESET); - textattr(ATTR_RESET); - cputs("mdview: empty file"); - exit(-1); - } - - /* Первичный готов — гасим спиннер, включаем F8 (второй набор дособерётся - * лениво по первому переключению) и рисуем финальный статус с ПОЛНЫМ - * счётчиком строк. */ - spinner_show(0); - g_f8_enabled = 1; -#if WITH_RAW - g_ready = 1; /* документ построен — теперь доступен F2 (RAW) */ -#endif - render_menu(); - clamp_top(); - render_full_status(); - draw_viewport_from_cache(); - - /* Второй набор (UTF-8 при 8-битном первичном и наоборот) готовим фоном - * в паузах между клавишами — к первому F8 он обычно уже построен. */ - bg_build_start(); - - for (;;) { - /* Idle-фон: пока второй набор не достроен, вместо блокирующего getkey - * опрашиваем клавиатуру и тратим паузы на bg_step. Холдаун после - * каждой клавиши: при активном скролле (автоповтор) фон молчит и - * плавность не страдает. После достройки — снова блокирующий getkey. */ - if (g_bg_doc != 0xFF) { - uint16_t idle = 0; - while (!kbhit()) { - if (idle < BG_KEY_HOLDOFF) { - idle++; - continue; - } - bg_step(); - if (g_bg_doc == 0xFF) - break; - } - } - uint16_t k = getkey(); - uint8_t ascii = (uint8_t)(k & 0xFF); - uint8_t scan = (uint8_t)((k >> 8) & 0x7F); - - if (ascii) { - if (ascii == 0x1B) - break; /* клавиша Esc */ - continue; - } - if (scan == KEY_F10) { - break; - } - if (scan == KEY_F1) { - show_help(); - continue; - } /* show_help сам перерисует */ - /* F2 — тумблер RAW ↔ MD (из HEX тоже уводит в MD). - * Рабочий только после постройки документа. */ - if (scan == KEY_F2) { - if (!g_ready) continue; - if (g_view == VIEW_MD) { - g_view = g_raw_last; /* → RAW (последний под-режим) */ - raw_seed_from(top_line); /* позиция из MD */ - raw_screen_init(); - status_encoding(); - render_menu(); - render_raw_status(); - raw_draw(); - } else { - uint32_t off = view_pos(); /* позиция из RAW/HEX */ - if (VIEW_IS_RAW(g_view)) - g_raw_last = g_view; /* запомнить под-режим RAW */ - g_view = VIEW_MD; - top_line = line_at_off(off); - clamp_top(); - render_full_status(); render_menu(); - draw_viewport_from_cache(); - } - continue; - } -#if WITH_HEX - /* F4 — тумблер HEX ↔ прежний вид (MD/RAW). */ - if (scan == KEY_F4) { - if (!g_ready) continue; - uint32_t off = view_pos(); /* позиция текущего вида */ - if (g_view == VIEW_HEX) { /* HEX → прежний вид */ - g_view = g_hex_last; - view_reanchor(off); - if (g_view == VIEW_MD) { - clamp_top(); - render_full_status(); render_menu(); - draw_viewport_from_cache(); - } else { - raw_screen_init(); - status_encoding(); render_menu(); - render_raw_status(); raw_draw(); - } - } else { /* MD/RAW → HEX */ - g_hex_last = g_view; - g_view = VIEW_HEX; - view_reanchor(off); /* active → байты оригинала */ - hex_screen_init(); - status_encoding(); render_menu(); - render_hex_status(); hex_draw(); - } - continue; - } -#endif - /* цикл CP866→CP1251→KOI8R→UTF8→CP866; набор дособерётся лениво */ - if (scan == KEY_F8) { - uint8_t next = (g_encoding == ENC_CP866) ? ENC_CP1251 : - (g_encoding == ENC_CP1251) ? ENC_KOI8R : - (g_encoding == ENC_KOI8R) ? ENC_UTF8 : ENC_CP866; - if (next == ENC_UTF8 && g_utf_failed) - next = ENC_CP866; /* UTF-8 недоступен */ - switch_encoding(next); - if (g_abort) - exit(0); /* Esc/F10 во время ленивой досборки */ - /* Перерисовка — по текущему виду (switch_encoding сам не рисует). */ -#if WITH_HEX - if (g_view == VIEW_HEX) { - status_encoding(); - render_hex_status(); - hex_draw(); /* printable-колонка в новой кодировке */ - continue; - } -#endif -#if WITH_RAW - if (g_view != VIEW_MD) { - status_encoding(); - render_raw_status(); - raw_draw(); - continue; - } -#endif - render_full_status(); - draw_viewport_from_cache(); - continue; - } - switch(g_view) { - case VIEW_MD: - /* MD-режим: F1/F8/F10 здесь, навигация — в md_key() (peer к raw_key). */ - md_key(scan); /* навигация MD (сама рисует+статус); незнакомые игнорит */ - continue; -#if WITH_HEX - case VIEW_HEX: - hex_key(scan); /* навигация HEX (сама рисует+процент) */ - continue; -#endif -#if WITH_RAW - case VIEW_RAW_WRAP: - case VIEW_RAW_HSCROLL: - /* RAW-режим: F3 Wrap/Unwrap, своя навигация; F8 — кодировка; F1/F10. */ - /* Wrap ↔ Unwrap */ - if (scan == KEY_F3) { - g_view = (g_view == VIEW_RAW_WRAP) ? VIEW_RAW_HSCROLL : VIEW_RAW_WRAP; - g_raw_last = g_view; - raw_renorm(); - raw_screen_init(); - render_menu(); - render_raw_status(); - raw_draw(); - continue; - } - raw_key(scan); - continue; -#endif /* WITH_RAW */ - default: - break; - } - } - clrscr_attr(ATTR_TEXT); - exit(0); - return 0; -} diff --git a/applications/Volkov/tests/fixtures/mdview2/mdview2.h b/applications/Volkov/tests/fixtures/mdview2/mdview2.h deleted file mode 100644 index 1c53d6f..0000000 --- a/applications/Volkov/tests/fixtures/mdview2/mdview2.h +++ /dev/null @@ -1,304 +0,0 @@ -/* - * mdview2.h — общие определения и интерфейс между модулями mdview2. - * - * Монолит mdview2.c расщеплён на ядро + модули: index (парсер), md/raw (виды), - * enc (кодировки), table, help. Здесь — разделяемые константы экрана/атрибутов, - * словари флагов/стилей, тип cache_rec_t, extern-объявления разделяемого - * состояния ядра и API каждого модуля (сгруппированы по секциям ниже). - */ -#ifndef MDVIEW2_H -#define MDVIEW2_H - -#include -#include /* COLOR(), COLOR_* */ -#include "mdview2_conf.h" /* WITH_RAW / WITH_HEX — выбор опциональных модулей */ - -/* ---- Геометрия экрана -------------------------------------------- */ -#define SCREEN_W 80 -#define SCREEN_H 32 -#define VIEW_TOP_ROW 1 -#define VIEW_H 30 /* видимая область: строки 1..30 включительно */ -#define MENU_ROW 31 -#define TAB_STOP 4 -#define HPAN_STEP 8u /* шаг горизонтального сдвига (←/→) */ - -/* ---- Геометрия статус-бара (строка 0): фиксированные поля и разделители ---- */ -#define SPINNER_COL 8 /* слот спиннера загрузки */ -#define DIV1_X 45 /* разделитель │ перед числами диапазона строк */ -#define DIV2_X 71 /* разделитель │ перед процентом */ -/* Числа/метка пишутся с DIVn_X+2; область между разделителями — [DIV1_X+1 .. DIV2_X-1]. */ - -/* ---- Параметры файла и памяти ------------------------------------ */ -#define PAGE_BITS 14u -#define PAGE_SIZE (1u << PAGE_BITS) /* размер EMM-страницы: 16 КБ */ -#define PAGE_MASK ((uint16_t)(PAGE_SIZE - 1u)) -#define MAX_PAGES 16 /* 16 страниц × 16 КБ = 256 КБ */ -#define MAX_FILE ((uint32_t)MAX_PAGES * PAGE_SIZE) -#define FILE_BUF ((char *)0xC000) /* окно W3, куда мапится текущая EMM-страница */ - -/* ---- Палитра атрибутов ------------------------------------------- */ -#define ATTR_RESET COLOR(COLOR_LIGHTGRAY, COLOR_BLACK) -#define ATTR_TEXT COLOR(COLOR_LIGHTGRAY, COLOR_BLUE) -#define ATTR_TEXT_TITLE1 COLOR(COLOR_YELLOW, COLOR_BLUE) -#define ATTR_TEXT_TITLE2 COLOR(COLOR_LIGHTBLUE, COLOR_BLUE) -#define ATTR_TEXT_TITLE3 COLOR(COLOR_LIGHTGREEN, COLOR_BLUE) -#define ATTR_TEXT_TITLE4 COLOR(COLOR_LIGHTGREEN, COLOR_BLUE) -#define ATTR_TEXT_BOLD COLOR(COLOR_LIGHTRED, COLOR_BLUE) -#define ATTR_TEXT_ITALIC COLOR(COLOR_LIGHTGREEN, COLOR_BLUE) -#define ATTR_TEXT_UNDERSORE COLOR(COLOR_LIGHTMAGENTA, COLOR_BLUE) -#define ATTR_TEXT_CODE COLOR(COLOR_WHITE, COLOR_BLUE) -#define ATTR_TEXT_STRIKE COLOR(COLOR_DARKGRAY, COLOR_BLUE) -#define ATTR_LIST_MARKER COLOR(COLOR_LIGHTCYAN, COLOR_BLUE) -#define ATTR_QUOTE_MARKER COLOR(COLOR_CYAN, COLOR_BLUE) -#define ATTR_HR COLOR(COLOR_CYAN, COLOR_BLUE) -#define ATTR_BOX COLOR(COLOR_CYAN, COLOR_BLUE) -#define ATTR_TRUNC COLOR(COLOR_YELLOW, COLOR_BLUE) -#define ATTR_BAR COLOR(COLOR_BLACK, COLOR_LIGHTCYAN) -#define ATTR_BAR_SPINNER COLOR(COLOR_WHITE, COLOR_LIGHTCYAN) -#define ATTR_MENU_T COLOR(COLOR_BLACK, COLOR_LIGHTCYAN) -#define ATTR_MENU_K COLOR(COLOR_YELLOW, COLOR_BLACK) -#define ATTR_WARN COLOR(COLOR_YELLOW, COLOR_RED) /* строка-обрыв (исчерпан кэш/лимит) */ - -/* ---- Атрибуты диалога справки ------------------------------------ */ -#define ATTR_HELP_BG COLOR(COLOR_LIGHTGRAY, COLOR_BLACK) -#define ATTR_HELP_BDR COLOR(COLOR_WHITE, COLOR_BLACK) -#define ATTR_HELP_TIT COLOR(COLOR_YELLOW, COLOR_BLACK) -#define ATTR_HELP_HDR COLOR(COLOR_WHITE, COLOR_BLACK) - -/* ---- Режимы просмотра -------------------------------------------- */ -#define VIEW_MD 0 /* markdown с форматированием (по умолчанию) */ -#define VIEW_RAW_WRAP 1 /* RAW: длинные строки переносятся кратно 80 */ -#define VIEW_RAW_HSCROLL 2 /* RAW: длинные строки в одну, гориз. скролл */ -#define VIEW_HEX 3 /* HEX-дамп оригинального файла */ -#define VIEW_IS_RAW(v) ((v) == VIEW_RAW_WRAP || (v) == VIEW_RAW_HSCROLL) - -/* ---- Кодировки --------------------------------------------------- */ -/* 8-битные (CP866/CP1251/KOI8R) различаются только ремапом глифов [128-255] - * на отрисовке; UTF-8 — отдельный декодированный набор. */ -#define ENC_CP866 0 -#define ENC_CP1251 1 -#define ENC_KOI8R 2 -#define ENC_UTF8 3 -#define ENC_UNSUPPORTED -1 /* UTF16 / UTF32 */ -#define CONV_MARGIN 4096u /* на сколько байт держать UTF-конвертацию впереди индексатора */ - -/* ---- Флаги сегмента индекса (IF_*) — общие для индексатора/кэша/вью ---- */ -#define IF_CONT 0x01u /* сегмент является продолжением перенесённой строки */ -#define IF_NOWRAP 0x02u /* строка не переносится (кодовый блок / HR / таблица) */ -#define IF_BLANK 0x04u /* визуально пустая строка */ -#define IF_CODE 0x08u /* тело fenced code-блока (verbatim-режим) */ -#define IF_HSCROLL 0x10u /* блок горизонтально скроллируется ЦЕЛИКОМ (код/таблица), - * включая строки короче 80 — двигаются как единый блок. - * НЕ ставится на HR и границы fence (им нечего прятать). */ -#define IF_TRUNC_MSG 0x20u /* строка-обрыв: рисуется ВЖИВУЮ фикс. текстом (без контент-кэша); - * cache_rec.reserved несёт код причины (TRUNC_*). */ - -/* ---- Причина обрыва индексации (cache_rec.reserved при IF_TRUNC_MSG) ---- */ -#define TRUNC_CONTENT 1 /* исчерпан контент-кэш (MAX_CACHE_CONTENT_PAGES) */ -#define TRUNC_LINES 2 /* исчерпана ёмкость индекса (max_lines) */ -#define TRUNC_FILE 3 /* файл больше MAX_FILE — прочитаны первые 256 КБ */ - -/* ---- Словарь сегментов/стилей (общий для индексатора и таблиц) ---- */ -/* Тип сегмента-продолжения, передаваемый в emit_seg()/inline_scan(). */ -#define CK_PLAIN 0 -#define CK_QUOTE 1 -#define CK_LIST 2 -#define CK_OTHER 3 - -/* Начальный inline-стиль сегмента (INIT_STYLE_*). */ -#define INIT_STYLE_PLAIN 0x0 -#define INIT_STYLE_BOLD 0x1 -#define INIT_STYLE_ITALIC 0x2 -#define INIT_STYLE_UNDER 0x3 -#define INIT_STYLE_CODE 0x4 -#define INIT_STYLE_STRIKE 0x5 - -/* ---- Таблицы: CP866 light box-drawing (общий для индексатора и модуля) ---- */ -#define TBL_MAX_COLS 16 -#define TBL_ATTR ATTR_BOX -#define TBL_H 0xC4 /* ─ */ -#define TBL_V 0xB3 /* │ */ -#define TBL_TL 0xDA /* ┌ */ -#define TBL_TM 0xC2 /* ┬ */ -#define TBL_TR 0xBF /* ┐ */ -#define TBL_ML 0xC3 /* ├ */ -#define TBL_MM 0xC5 /* ┼ */ -#define TBL_MR 0xB4 /* ┤ */ -#define TBL_BL 0xC0 /* └ */ -#define TBL_BM 0xC1 /* ┴ */ -#define TBL_BR 0xD9 /* ┘ */ - -/* ================================================================== * - * Разделяемые символы mdview2.c, используемые модулем RAW. - * ================================================================== */ -extern uint8_t g_view; /* активный режим просмотра (VIEW_*) */ -extern uint32_t file_size; /* размер активного буфера документа */ -extern const uint8_t *g_remap; /* таблица ремапа [128-255] (0 = нет) */ -extern uint8_t g_scratch_phys; /* scratch EMM-страница для win_rest */ - -char fb(uint32_t p); /* байт активного буфера (W3-маппинг) */ -void win_rest(uint8_t row, uint8_t col, uint8_t h, uint8_t w, uint8_t page, uint16_t off); -void fill_row(uint8_t y, uint8_t attr); -uint8_t pct16(uint16_t num, uint16_t den); /* num*100/den (0..100), 16-бит, без __divulong */ -uint32_t seg_off(uint16_t idx); /* offset исходника для логической строки idx */ -uint16_t line_at_off(uint32_t off); /* обратно: последняя строка с seg_off <= off */ - -/* ================================================================== * - * API модуля RAW (mdview2_raw.c). - * ================================================================== */ -void raw_seed_from(uint16_t md_top_line); /* вход в RAW: позиция по top_line */ -uint32_t raw_pos(void); /* текущий байт-offset верха экрана RAW */ -void raw_reanchor(uint32_t off); /* поставить RAW на строку с байтом off */ -void raw_draw(void); /* перерисовать область документа в RAW */ -uint8_t raw_pct(void); /* % прокрутки по байтам (для render_raw_status_numbers) */ -uint8_t raw_key(uint8_t scan); /* навигация RAW; 1 = обработано */ -void raw_screen_init(void); /* очистка экрана с нужным аттрибутом */ -void raw_renorm(void); /* выровнять позицию при смене под-режима (F3) */ - -/* ================================================================== * - * API модуля HEX (mdview2_hex.c) — дамп ОРИГИНАЛЬНОГО файла (F4). - * Позиция hex_pos/hex_reanchor — в байтах ОРИГИНАЛА (orig_file_*); - * конвертацию в/из активного буфера делает ядро (map_off). - * ================================================================== */ -uint32_t hex_pos(void); /* offset верхнего ряда (кратен 16) */ -void hex_reanchor(uint32_t orig_off); /* поставить на ряд с байтом orig_off */ -void hex_draw(void); /* перерисовать область документа */ -uint8_t hex_key(uint8_t scan); /* навигация HEX; 1 = обработано */ -void hex_screen_init(void); /* очистка экрана */ - -/* ================================================================== * - * Запись директории рендер-кэша (одна на видимую строку, РОВНО 8 байт — - * cache_dir_get/put адресуют сдвигом idx<<3). Контент — len пар (char,attr). - * ================================================================== */ -typedef struct cache_rec_s { - uint8_t page; /* EMM-страница рендер-кэша (физический номер) */ - uint16_t off; /* смещение в странице, 0..16383 (байты, не ячейки) */ - uint8_t len; /* длина контента в ЯЧЕЙКАХ (char,attr пар), 0..255; 0 = пустая/HR */ - uint8_t flags; /* копия IF_NOWRAP/IF_CODE/IF_BLANK на момент рендера */ - uint8_t reserved; /* не используется */ - uint8_t pad[2]; /* явный резерв, добивка до 8 байт */ -} cache_rec_t; - -void cache_dir_get(uint16_t idx, cache_rec_t *r); /* читатель директории кэша (ядро) */ -void put_str_attr(uint8_t x, uint8_t y, const char *s, uint8_t attr); /* печать строки с атрибутом */ - -/* ================================================================== * - * Состояние навигации/представления (определено в mdview2.c). - * ================================================================== */ -extern uint16_t n_lines; /* всего строк в индексе текущего набора */ -extern uint16_t top_line; /* верхняя видимая строка */ -extern uint8_t viewport_x; /* горизонтальный сдвиг (nowrap-строки) */ -extern char filename[]; /* имя файла для статус-бара */ -extern uint8_t g_loading; /* 1 во время index_lines() */ -extern uint8_t g_ready; /* 1 когда первичный документ построен (F2 RAW) */ -extern uint8_t g_f8_enabled; /* можно ли сейчас переключать кодировку */ - -/* ================================================================== * - * Статус-бар (строка 0), меню (строка 31), спиннер — mdview2_status.c. - * Атомарные части: prerender (фикс. хром, один раз) / encoding (поле кодировки) - * / numbers (диапазон строк + %) / menu / spinner — обновляются по отдельности. - * ================================================================== */ -void prerender_status(void); /* фикс. часть: фон, разделители, MDVIEW, имя файла (1 раз) */ -void status_encoding(void); /* только поле кодировки (col 37) */ -void render_md_status_numbers(void); /* числа MD: диапазон строк + % */ -void render_raw_status(void); /* RAW mode: метка режима */ -void render_hex_status(void); /* HEX mode: метка режима */ -void render_full_status(void); /* encoding + numbers (MD) */ -void render_menu(void); /* строка меню (по смене режима/готовности) */ -uint16_t drawable_lines(void); /* число строк, готовых к показу */ -void spinner_tick(void); /* кадр спиннера загрузки */ -void spinner_show(uint8_t on); /* вкл/выкл спиннер */ -void render_percent_progress(uint8_t pct); /* показать процентный прогресс просмотра */ - -/* ================================================================== * - * Отрисовка области MD-документа и прокрутка (mdview2_md.c). - * ================================================================== */ -void draw_viewport_from_cache(void); /* перерисовать область документа из кэша */ -void clamp_top(void); /* привести top_line к диапазону */ -void md_scroll_up(uint16_t n); -void md_scroll_down(uint16_t n); -void md_scroll_horizon(int8_t delta); /* горизонтальный сдвиг nowrap-блоков */ -uint8_t md_key(uint8_t scan); /* навигация MD (после загрузки); 1 = обработано */ - -/* ================================================================== * - * API модуля справки (mdview2_help.c). - * ================================================================== */ -void show_help(void); /* модальный диалог F1 */ - -/* ================================================================== * - * Парсер/индексатор (mdview2_index.c) и разделяемое с ним состояние. - * Хранилище индекса (index_blk/pages/phys, n_lines/max_lines) — в ядре - * (аллокация/doc-slot), индексатор пишет в него; cache_*-писатели и - * progress_tick ядро экспортирует индексатору (spinner_tick — из status). - * ================================================================== */ -#define INDEX_RECS_PER_PAGE 2048u /* 16384 / 8; запись всегда в одной странице */ -#define MAX_CACHE_LINE_LEN 255u /* кап длины контента строки в ячейках (буфер g_cells) */ - -extern uint16_t max_lines; /* ёмкость индекса: index_pages * 2048 */ -extern uint8_t index_blk, index_pages; /* дескриптор EMM-блока индекса */ -extern uint8_t index_phys[]; /* физ. страницы индекса */ -extern uint8_t index_truncated; /* индекс упёрся в ёмкость */ -extern uint8_t g_abort; /* F10/Esc во время загрузки → прервать индексацию */ -extern uint8_t g_file_clamped; /* файл был обрезан до MAX_FILE при загрузке (>256 КБ) */ - -uint8_t cache_reserve(uint16_t nbytes, uint8_t *out_page, uint16_t *out_off); -void cache_commit(uint8_t page, uint16_t off, const void *buf, uint16_t len); -void cache_dir_put(uint16_t idx, const cache_rec_t *r); -void progress_tick(void); /* кооперативный шаг loading-loop (рисует/листает) */ - -/* Резюмируемая индексация (для фоновой сборки второго набора): begin сбрасывает - * проход, step выполняет до budget итераций (блоков исходника) и возвращает 1, - * когда индекс финализирован. Межшаговое состояние — статики модуля index; - * оно не входит в docset_t (см. bg_step ядра). */ -void index_begin(void); -uint8_t index_step(uint8_t budget); -extern uint8_t g_bg_building; /* 1 = фоновый шаг индексации: не трогать экран (спиннер) */ - -/* ================================================================== * - * Буфер ячеек рендера (mdview2.c) — общий с модулем таблиц. - * Таблицы эмитят свои строки теми же примитивами, что и индексатор. - * ================================================================== */ -extern uint8_t g_ncells; /* ячеек в текущем сегменте */ -void gc_put(char ch, uint8_t attr); /* добавить ячейку в g_cells */ -void gc_fill(char ch, uint8_t attr, uint8_t n); /* n одинаковых ячеек */ -uint8_t inline_scan(uint32_t q, uint32_t q_end, uint8_t col, - uint8_t ckind, uint8_t line_style, uint8_t base_attr, - uint8_t nowrap); - -/* ================================================================== * - * API модуля таблиц (mdview2_table.c) — вызывается из index_lines(). - * ================================================================== */ -uint32_t row_end(uint32_t p); /* offset завершающего '\n' (или file_size) */ -uint32_t table_first_cell(uint32_t row_start); /* контент первой ячейки строки */ -uint8_t table_next_cell(uint32_t *pp, uint32_t lineend, uint32_t *cs, uint32_t *ce); -uint8_t table_is_sep_row(uint32_t row_start, uint32_t lineend); -void table_border(const uint8_t *widths, uint8_t ncols, char left, char mid, char right); -void table_data_row(uint32_t row_start, uint32_t lineend, const uint8_t *widths, uint8_t ncols); - -/* ================================================================== * - * Кодировки и UTF-8 конвертация (mdview2_enc.c). - * Состояние кодека владеется модулем; build_doc()/index_lines() ядра - * оркестрируют его (привязка UTF-буфера к живому file_*, сборка наборов). - * ================================================================== */ -extern uint8_t g_encoding; /* активная кодировка (ENC_*) */ -extern uint8_t g_utf_building; /* 1 пока UTF-конвертация не дошла до конца исходника */ -extern uint8_t utf_avail; /* 1 = страницы UTF-8 выделены */ -extern uint8_t utf_blk, utf_pages; /* дескриптор EMM-буфера UTF-набора */ -extern uint8_t utf_phys[]; /* физ. страницы UTF-буфера */ -extern uint32_t utf_size; /* размер сконвертированного UTF-потока */ - -/* Ядро-owned состояние, читаемое кодеком (определено в mdview2.c). */ -extern uint8_t cur_page; /* текущая страница файла в W3 (инвалидация маппинга) */ -extern uint8_t orig_file_phys[]; /* физ. страницы снимка исходного файла */ -extern uint8_t orig_file_pages; -extern uint32_t orig_file_size; - -void set_encoding(uint8_t enc); /* активная кодировка + таблица ремапа */ -const char *enc_name(uint8_t enc); /* короткое имя для статус-бара */ -uint8_t detect_encoding(void); /* автоопределение по сэмплу байт */ -void win_rest_remap(uint8_t row, uint8_t w, uint8_t page, uint16_t off); /* вывод среза с ремапом */ -void utf_convert_more(uint32_t target); /* инкрементальная конвертация до target байт */ -uint8_t utf_alloc(void); /* выделить EMM-страницы под UTF-набор; 0 = нет EMM */ -uint8_t utf_cp_glyph(uint32_t cp); /* кодпойнт → один CP866-глиф ('?' без аналога) */ - -#endif /* MDVIEW2_H */ diff --git a/applications/Volkov/tests/fixtures/mdview2/mdview2_conf.h b/applications/Volkov/tests/fixtures/mdview2/mdview2_conf.h deleted file mode 100644 index 330505e..0000000 --- a/applications/Volkov/tests/fixtures/mdview2/mdview2_conf.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * mdview2_conf.h — конфигурация сборки mdview2: какие опциональные модули - * просмотра включать. 1 = включить (код входит в бинарь), 0 = исключить - * полностью (нулевой расход кода/памяти). - * - * Модули обёрнуты в #if WITH_xxx целиком, поэтому файлы можно держать в - * сборке всегда (при 0 они компилируются в пустой объектник) — менять - * нужно только эти define. - */ -#ifndef MDVIEW2_CONF_H -#define MDVIEW2_CONF_H - -#define WITH_RAW 1 /* RAW-просмотр исходника (F2): mdview2_raw.c */ -#define WITH_HEX 1 /* HEX-дамп оригинального файла (F4): mdview2_hex.c */ - -#endif /* MDVIEW2_CONF_H */ diff --git a/applications/Volkov/tests/fixtures/mdview2/mdview2_enc.c b/applications/Volkov/tests/fixtures/mdview2/mdview2_enc.c deleted file mode 100644 index 2bf83dc..0000000 --- a/applications/Volkov/tests/fixtures/mdview2/mdview2_enc.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * mdview2_enc.c — кодировки и конвертация UTF-8 → CP866. - * - * Две связанные подсистемы: - * 1. 8-битные кодировки (CP866/CP1251/KOI8-R) — различаются только ремапом - * старших байтов [128-255] на ОТРИСОВКЕ (g_remap, win_rest_remap). - * Автоопределение — detect_encoding по частоте ходовых букв. - * 2. UTF-8 — отдельный набор: исходник инкрементально декодируется в - * utf_phys[] как чистый CP866 (utf_convert_more), после чего весь - * конвейер индексации/кэша работает по нему без ремапа. - * - * Владеет состоянием кодека (conv-буфер, позиция конвертации, UTF-страницы); - * описание разделяемых символов и API — в mdview2.h. Оркестрацией (привязка - * UTF-буфера к живому file_*, сборка наборов) занимается build_doc() ядра. - */ - -#include -#include /* COLOR()/COLOR_* для ATTR_* в attr_is_content */ -#include /* sprinter_page_w3 */ -#include /* bank_read/bank_write/mem_alloc_pages/mem_get_page */ -#include "mdview2.h" - -/* ---- Состояние кодировки/UTF (владелец — этот модуль) ------------- */ -uint8_t g_encoding = ENC_CP866; /* активная кодировка */ - -uint8_t utf_blk; -uint8_t utf_pages; -uint8_t utf_phys[MAX_PAGES]; -uint32_t utf_size; -uint8_t utf_avail; /* 1 = страницы UTF-8 выделены */ -uint8_t g_utf_building; /* 1 пока конвертация не дошла до конца исходника */ - -/* Приватное состояние конвертера (наружу не торчит — сброс через - * utf_conv_reset(), чтение оригинала через cv_read). */ -static uint32_t utf_src; /* позиция чтения ОРИГИНАЛА (utf-8) */ -static uint8_t cv_page = 0xFF; /* orig-страница в W3 для cv_read (своя от cur_page) */ - -/* Таблицы ремапа СТАРШИХ байтов [128-255] в CP866. Индексируются (ch-0x80): - * младшие 128 (ASCII) — identity, в ремапе не участвуют (win_rest_remap - * трогает только ch>=0x80), поэтому в таблицах их нет. Кириллица + ходовая - * пунктуация; неизвестное → '?' (0x3F). Сгенерированы Python codecs. */ -static const uint8_t cp1251_to_866[128] = { - /* 80 */ 0x3F, 0x3F, 0x27, 0x3F, 0x22, 0x2E, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3C, 0x3F, 0x3F, 0x3F, 0x3F, - /* 90 */ 0x3F, 0x27, 0x27, 0x22, 0x22, 0x07, 0x2D, 0x2D, 0x3F, 0x3F, 0x3F, 0x3E, 0x3F, 0x3F, 0x3F, 0x3F, - /* A0 */ 0xFF, 0xF6, 0xF7, 0x3F, 0xFD, 0x3F, 0x3F, 0x3F, 0xF0, 0x63, 0xF2, 0x3C, 0x3F, 0x3F, 0x72, 0xF4, - /* B0 */ 0xF8, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xFA, 0xF1, 0xFC, 0xF3, 0x3E, 0x3F, 0x3F, 0x3F, 0xF5, - /* C0 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, - /* D0 */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, - /* E0 */ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, - /* F0 */ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, -}; -static const uint8_t koi8r_to_866[128] = { - /* 80 */ 0xC4, 0xB3, 0xDA, 0xBF, 0xC0, 0xD9, 0xC3, 0xB4, 0xC2, 0xC1, 0xC5, 0xDF, 0xDC, 0xDB, 0xDD, 0xDE, - /* 90 */ 0xB0, 0xB1, 0xB2, 0x3F, 0xFE, 0xF9, 0xFB, 0x3F, 0x3F, 0x3F, 0xFF, 0x3F, 0xF8, 0x3F, 0xFA, 0x3F, - /* A0 */ 0xCD, 0xBA, 0xD5, 0xF1, 0xD6, 0xC9, 0xB8, 0xB7, 0xBB, 0xD4, 0xD3, 0xC8, 0xBE, 0xBD, 0xBC, 0xC6, - /* B0 */ 0xC7, 0xCC, 0xB5, 0xF0, 0xB6, 0xB9, 0xD1, 0xD2, 0xCB, 0xCF, 0xD0, 0xCA, 0xD8, 0xD7, 0xCE, 0x63, - /* C0 */ 0xEE, 0xA0, 0xA1, 0xE6, 0xA4, 0xA5, 0xE4, 0xA3, 0xE5, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, - /* D0 */ 0xAF, 0xEF, 0xE0, 0xE1, 0xE2, 0xE3, 0xA6, 0xA2, 0xEC, 0xEB, 0xA7, 0xE8, 0xED, 0xE9, 0xE7, 0xEA, - /* E0 */ 0x9E, 0x80, 0x81, 0x96, 0x84, 0x85, 0x94, 0x83, 0x95, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, - /* F0 */ 0x8F, 0x9F, 0x90, 0x91, 0x92, 0x93, 0x86, 0x82, 0x9C, 0x9B, 0x87, 0x98, 0x9D, 0x99, 0x97, 0x9A, -}; - -/* Устанавливает активную кодировку и таблицу ремапа (для 8-битных). */ -void set_encoding(uint8_t enc) -{ - g_encoding = enc; - g_remap = (enc == ENC_CP1251) ? cp1251_to_866 : - (enc == ENC_KOI8R) ? koi8r_to_866 : (const uint8_t *)0; -} - -/* Самые ходовые строчные русские буквы (о е а и н т с р в л) — их байты - * различают 8-битные кодировки по частоте. */ -// static const uint8_t common866 [10] = {0xAE,0xA5,0xA0,0xA8,0xAD,0xE2,0xE1,0xE0,0xA2,0xAB}; -// static const uint8_t common1251[10] = {0xEE,0xE5,0xE0,0xE8,0xED,0xF2,0xF1,0xF0,0xE2,0xEB}; -// static const uint8_t commonkoi [10] = {0xCF,0xC5,0xC1,0xC9,0xCE,0xD4,0xD3,0xD2,0xD7,0xCC}; -// static uint8_t in_set10(const uint8_t *s, uint8_t b) -// { -// for (uint8_t i = 0; i < 10; i++) if (s[i] == b) return 1; -// return 0; -// } - -/* Проверяем только пять самых популярных символов (о е а и н) */ -static const uint8_t common866 [5] = {0xAE,0xA5,0xA0,0xA8,0xAD}; -static const uint8_t common1251[5] = {0xEE,0xE5,0xE0,0xE8,0xED}; -static const uint8_t commonkoi [5] = {0xCF,0xC5,0xC1,0xC9,0xCE}; -static uint8_t in_set10(const uint8_t *s, uint8_t b) -{ - for (uint8_t i = 0; i < 5; i++) if (s[i] == b) return 1; - return 0; -} - -/* Автоопределение кодировки дешёвым сканом байтов (до построения индекса). - * BOM → UTF8; иначе валидность UTF-8 (структура multibyte); иначе 8-бит по - * частоте ходовых букв; фолбэк CP866. */ -uint8_t detect_encoding(void) -{ - if (file_size >= 3 && (uint8_t)fb(0) == 0xEF && (uint8_t)fb(1) == 0xBB && (uint8_t)fb(2) == 0xBF) - return ENC_UTF8; - - uint32_t n = file_size; - if (n > 1024u) n = 1024u; /* сэмпл: первый 1 КБ — детекции хватает */ - uint8_t utf_ok = 1, has_mb = 0, has_high = 0, cont = 0; - uint16_t s866 = 0, s1251 = 0, skoi = 0; - - for (uint32_t p = 0; p < n; p++) { - uint8_t b = (uint8_t)fb(p); - if (b < 0x80) { if (cont) { utf_ok = 0; cont = 0; } continue; } - has_high = 1; - if (in_set10(common866, b)) s866++; - if (in_set10(common1251, b)) s1251++; - if (in_set10(commonkoi, b)) skoi++; - if (cont) { - if ((b & 0xC0) == 0x80) cont--; - else { utf_ok = 0; cont = 0; } - } - else if (b >= 0xC2 && b <= 0xDF) { cont = 1; has_mb = 1; } - else if (b >= 0xE0 && b <= 0xEF) { cont = 2; has_mb = 1; } - else if (b >= 0xF0 && b <= 0xF4) { cont = 3; has_mb = 1; } - else utf_ok = 0; /* битый лид/одиночный континюэйшн */ - } - /* Незавершённая multibyte-последовательность на КОНЦЕ — нарушение только - * если это настоящий EOF; на границе сэмпла (n= s866 && s1251 >= skoi) return ENC_CP1251; - if (skoi >= s866) return ENC_KOI8R; - return ENC_CP866; -} - -const char *enc_name(uint8_t enc) -{ - return (enc == ENC_CP1251) ? "CP1251" : - (enc == ENC_KOI8R) ? "KOI8-R" : - (enc == ENC_UTF8) ? "UTF-8 " : "CP866 "; -} - -/* Контентный глиф (ремапим при смене кодировки) vs структурный (рамка/HR/ - * маркеры — уже CP866, не трогаем). Различаем по attr. */ -static uint8_t attr_is_content(uint8_t a) -{ - return (uint8_t)(a != ATTR_BOX && a != ATTR_HR && - a != ATTR_LIST_MARKER && a != ATTR_QUOTE_MARKER); -} - -/* Вывод среза строки кэша с ремапом контентных глифов [128-255] через - * g_remap (CP1251/KOI8). Без активной таблицы — прямой win_rest. */ -void win_rest_remap(uint8_t row, uint8_t w, uint8_t page, uint16_t off) -{ - if (!g_remap || w == 0) { - win_rest(row, 0, 1, w, page, off); - return; - } - uint8_t buf[SCREEN_W * 2]; - bank_read(page, off, buf, (uint16_t)w * 2u); - for (uint8_t i = 0; i < w; i++) { - uint8_t ch = buf[(uint16_t)i * 2u]; - if (ch >= 0x80 && attr_is_content(buf[(uint16_t)i * 2u + 1u])) - buf[(uint16_t)i * 2u] = g_remap[ch - 0x80]; /* таблицы хранят только старшие 128 */ - } - bank_write(g_scratch_phys, 0, buf, (uint16_t)w * 2u); - win_rest(row, 0, 1, w, g_scratch_phys, 0); -} - -/* ========================= UTF-8 → CP866 (Фаза 2) ======================== */ - -/* Кириллица U+0400..U+045F → CP866. Русский набор + Ё/ё и часть украинских - * (Є є Ї ї Ў ў), которые есть в CP866; пропуски → '?' (0x3F). */ -static const uint8_t utf_cyr_to_866[96] = { - /* 0400 */ 0x3F, 0xF0, 0x3F, 0x3F, 0xF2, 0x3F, 0x3F, 0xF4, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xF6, 0x3F, - /* 0410 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, - /* 0420 */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, - /* 0430 */ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, - /* 0440 */ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - /* 0450 */ 0x3F, 0xF1, 0x3F, 0x3F, 0xF3, 0x3F, 0x3F, 0xF5, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xF7, 0x3F, -}; - -/* Поток вывода конвертера: пишем в utf_phys чанками по странице, буфер в - * near-памяти, флаш через один bank_write (не побайтовый swap W3). */ -static uint8_t conv_buf[256]; -static uint16_t conv_n; /* байт в буфере, ещё не сброшено */ -static uint8_t conv_page; /* индекс текущей страницы в utf_phys */ -static uint16_t conv_off; /* уже сброшенное смещение в этой странице */ - -static void conv_flush(void) -{ - if (conv_n) { - bank_write(utf_phys[conv_page], conv_off, conv_buf, conv_n); - conv_off = (uint16_t)(conv_off + conv_n); - conv_n = 0; - } -} - -/* Добавляет один CP866-байт в выходной поток. Запись никогда не пересекает - * границу страницы (256 делит 16384 нацело). */ -static void conv_put(uint8_t b) -{ - if ((uint16_t)(conv_off + conv_n) == PAGE_SIZE) { /* страница заполнена */ - conv_flush(); - conv_page++; - conv_off = 0; - } - conv_buf[conv_n++] = b; - if (conv_n == (uint16_t)sizeof(conv_buf)) conv_flush(); -} - -/* Подстановки одиночных не-кириллических символов UTF-8 → один CP866-байт. - * Таблица вместо switch (экономия кода); линейный поиск дёшев — вызывается - * лишь при конвертации на редких символьных кодпойнтах. Чтобы добавить новый - * символ — достаточно дописать одну строку {кодпойнт, байт-CP866}. - * (Символы с НЕ-1:1 заменой, напр. … -> "...", обрабатываются отдельно ниже.) */ -typedef struct { uint16_t utf8; uint8_t cp866; } utf_sym_t; -static const utf_sym_t utf_sym[] = { - {0x00A0, 0x20}, {0x00AB, 0x3C}, {0x00BB, 0x3E}, /* nbsp « » → space < > */ - {0x00B0, 0xF8}, {0x00B7, 0xFA}, /* ° · */ - {0x2013, 0x2D}, {0x2014, 0x2D}, /* – — → '-' */ - {0x2018, 0x27}, {0x2019, 0x27}, /* ‘ ’ → '\'' */ - {0x201C, 0x22}, {0x201D, 0x22}, /* “ ” → '"' */ - {0x2022, 0xF9}, {0x2116, 0xFC}, /* • → ∙ № */ - {0x2190, 0x1B}, {0x2191, 0x18}, {0x2192, 0x1A}, {0x2193, 0x19}, /* ← ↑ → ↓ */ - {0x2500, 0xC4}, {0x2502, 0xB3}, {0x250C, 0xDA}, {0x2510, 0xBF}, /* ─ │ ┌ ┐ */ - {0x2514, 0xC0}, {0x2518, 0xD9}, {0x251C, 0xC3}, {0x2524, 0xB4}, /* └ ┘ ├ ┤ */ - {0x252C, 0xC2}, {0x2534, 0xC1}, {0x253C, 0xC5}, /* ┬ ┴ ┼ */ - {0x2580, 0xDF}, {0x2584, 0xDC}, {0x2588, 0xDB}, /* ▀ ▄ █ */ - {0x2591, 0xB0}, {0x2592, 0xB1}, {0x2593, 0xB2}, {0x25A0, 0xFE}, /* ░ ▒ ▓ ■ */ - {0x2713, 0xFB}, {0x2714, 0xFB}, /* ✓ ✔ → галка */ - {0x2715, 0x78}, {0x2717, 0x78}, {0x2718, 0x78}, /* ✕ ✗ ✘ → 'x' */ -}; -#define UTF_SYM_N (uint8_t)(sizeof(utf_sym) / sizeof(utf_sym[0])) - -/* Кодпойнт → ОДИН CP866-глиф: кириллица по таблице, ходовые символы по - * utf_sym[], прочее → '?'. Общая для конвертера и HEX-printable (F4). */ -uint8_t utf_cp_glyph(uint32_t cp) -{ - if (cp < 0x80) return (uint8_t)cp; - if (cp >= 0x0400 && cp <= 0x045F) return utf_cyr_to_866[cp - 0x0400]; - for (uint8_t i = 0; i < UTF_SYM_N; i++) - if (utf_sym[i].utf8 == (uint16_t)cp) return utf_sym[i].cp866; - return '?'; -} - -/* Кодпойнт → CP866-глиф(ы) в поток конвертера. Спецслучаи с НЕ-1:1 заменой - * (… → "...", BOM → ничего) здесь; остальное через utf_cp_glyph(). */ -static void conv_emit_cp(uint32_t cp) -{ - if (cp == 0x2026) { conv_put('.'); conv_put('.'); conv_put('.'); return; } /* … → "..." */ - if (cp == 0xFEFF) return; /* BOM/ZWNBSP — выкинуть */ - conv_put(utf_cp_glyph(cp)); -} - -/* Последовательное чтение байта ОРИГИНАЛА (utf-8) конвертером. Оригинал лежит - * в orig_file_phys[]; мапим его страницу в W3 напрямую, со своей кэш-переменной - * cv_page (отдельной от cur_page индексатора, т.к. оба используют W3 и - * чередуются). На границе с fb()-чтением utf-буфера кэши взаимно сбрасываются. */ -static uint8_t cv_read(uint32_t s) -{ - uint8_t pg = (uint8_t)(s >> PAGE_BITS); - if (pg != cv_page) { sprinter_page_w3(orig_file_phys[pg]); cv_page = pg; } - return *((volatile uint8_t *)(0xC000u + (uint16_t)(s & PAGE_MASK))); -} - -/* Возобновляемая конвертация: дописывает utf_phys из оригинала, пока - * сконвертированных (flushed) байт меньше target и не достигнут конец - * исходника. По выходу file_size = доступная (сконвертированная) часть; при - * достижении конца фиксирует utf_size и снимает g_utf_building. */ -void utf_convert_more(uint32_t target) -{ - cv_page = 0xFF; /* W3 был на utf (fb индексатора) — пере-смаппим orig */ - uint32_t n = orig_file_size; - while (utf_src < n && - (uint32_t)((uint32_t)conv_page * PAGE_SIZE + conv_off) < target) { - uint8_t b = (uint8_t)cv_read(utf_src++); - if (b < 0x80) { - conv_emit_cp(b); - continue; - } - - uint32_t cp; - uint8_t need; - if ((b & 0xE0) == 0xC0) { cp = (uint32_t)(b & 0x1F); need = 1; } - else if ((b & 0xF0) == 0xE0) { cp = (uint32_t)(b & 0x0F); need = 2; } - else if ((b & 0xF8) == 0xF0) { cp = (uint32_t)(b & 0x07); need = 3; } - else { conv_put('?'); continue; } - - uint8_t ok = 1; - for (uint8_t k = 0; k < need; k++) { - if (utf_src >= n) { ok = 0; break; } - uint8_t cb = (uint8_t)cv_read(utf_src); - if ((cb & 0xC0) != 0x80) { ok = 0; break; } - cp = (cp << 6) | (uint32_t)(cb & 0x3F); - utf_src++; - } - if (!ok || need == 3) { conv_put('?'); continue; } - conv_emit_cp(cp); - } - conv_flush(); - file_size = (uint32_t)conv_page * PAGE_SIZE + conv_off; /* доступно индексатору */ - if (utf_src >= n) { utf_size = file_size; g_utf_building = 0; } - cur_page = 0xFF; /* W3 был на orig — fb индексатора пере-смаппит utf */ -} - -/* Выделяет страницы под UTF-8 набор (конвертированный ≤ оригинала по размеру) - * и сбрасывает приватное состояние конвертера в начало (conv-буфер, utf_src, - * g_utf_building). Сама конвертация — инкрементальная, через utf_convert_more(). - * Возврат: 1 — успех, 0 — нет EMM. */ -uint8_t utf_alloc(void) -{ - utf_pages = orig_file_pages; - utf_blk = mem_alloc_pages(utf_pages); - if (utf_blk == 0) return 0; - for (uint8_t i = 0; i < utf_pages; i++) - utf_phys[i] = mem_get_page(utf_blk, i); - conv_n = 0; conv_page = 0; conv_off = 0; /* converter reset (вызывается один раз) */ - utf_src = 0; - g_utf_building = 1; - return 1; -} diff --git a/applications/Volkov/tests/fixtures/mdview2/mdview2_help.c b/applications/Volkov/tests/fixtures/mdview2/mdview2_help.c deleted file mode 100644 index 6999f0d..0000000 --- a/applications/Volkov/tests/fixtures/mdview2/mdview2_help.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * mdview2_help.c — диалог справки (F1). - * - * Модальное окно поверх документа: рамка с заголовком, список горячих - * клавиш и поддерживаемых markdown-элементов. Блокирует до нажатия любой - * клавиши, после чего восстанавливает статус-бар, область документа и меню. - * - * Зависимости от ядра (mdview2.c): render_full_status / draw_viewport_from_cache - * / render_menu для восстановления экрана. Текст справки — в CP866. - */ - -#include -#include -#include -#include -#include "mdview2.h" - -/* Геометрия диалога справки (в символьных координатах 80×32). */ -#define HELP_X 8u /* левая граница рамки */ -#define HELP_Y 3u /* верхняя граница рамки */ -#define HELP_W 64u /* ширина рамки (включая │) */ -#define HELP_H 26u /* высота рамки (включая ─) */ - -/* Заполняет одну внутреннюю строку диалога (r=0 — первая строка за рамкой). - * Строка s в кодировке CP866; остаток до края дополняется пробелами. */ -static void help_line(uint8_t r, const char *s, uint8_t attr) -{ - uint8_t x = HELP_X + 1u; - uint8_t y = HELP_Y + 1u + r; - uint8_t len = (uint8_t)strlen(s); - - if (len > HELP_W - 2u) - len = HELP_W - 2u; - - bios_set_place(y, x); - if (len > 0) - bios_writeattr(s, len, attr); - /* place уже продвинут bios_writeattr на len колонок (verified). */ - if (len < HELP_W - 2u) { - bios_fillcharattr(' ', ATTR_HELP_BG, (uint8_t)(HELP_W - 2u - len)); - } -} - -void show_help(void) -{ - /* " Помощь " в CP866 (8 байт) */ - static const char title[] = " \x8F\xAE\xAC\xAE\xE9\xEC "; - uint8_t tlen = 8u; - uint8_t lft = (uint8_t)((HELP_W - 2u - tlen) / 2u); /* = 27 */ - uint8_t rgt = (uint8_t)(HELP_W - 2u - tlen - lft); - /* Верхняя граница рамки с заголовком по центру. - * place продвигается каждым BIOS-вызовом (verified), поэтому - * достаточно одной установки места на всю строку рамки. */ - wrchar(HELP_X, HELP_Y, 0xDA, ATTR_HELP_BDR); /* ┌ */ - bios_set_place(HELP_Y, (uint8_t)(HELP_X + 1u)); - if (lft > 0) bios_fillcharattr(0xC4, ATTR_HELP_BDR, lft); - if (tlen > 0) bios_writeattr(title, tlen, ATTR_HELP_TIT); - if (rgt > 0) bios_fillcharattr(0xC4, ATTR_HELP_BDR, rgt); - wrchar(HELP_X + HELP_W - 1u, HELP_Y, 0xBF, ATTR_HELP_BDR); /* ┐ */ - - /* Боковые границы (левый и правый │ для каждой строки тела) */ - for (uint8_t r = 1u; r < HELP_H - 1u; r++) { - wrchar(HELP_X, HELP_Y + r, 0xB3, ATTR_HELP_BDR); /* │ */ - wrchar(HELP_X + HELP_W - 1u, HELP_Y + r, 0xB3, ATTR_HELP_BDR); /* │ */ - } - - /* Нижняя граница рамки */ - wrchar(HELP_X, HELP_Y + HELP_H - 1u, 0xC0, ATTR_HELP_BDR); /* └ */ - bios_set_place((uint8_t)(HELP_Y + HELP_H - 1u), (uint8_t)(HELP_X + 1u)); - bios_fillcharattr(0xC4, ATTR_HELP_BDR, (uint8_t)(HELP_W - 2u)); - wrchar(HELP_X + HELP_W - 1u, HELP_Y + HELP_H - 1u, 0xD9, ATTR_HELP_BDR); /* ┘ */ - - /* Содержимое (20 внутренних строк) */ - uint8_t r = 0; - help_line(r++, "", ATTR_HELP_BG); - help_line(r++, " MDView v1.0 (b3) -- Markdown Viewer for Sprinter", ATTR_HELP_HDR); - help_line(r++, " (c) 2026 \x8F\xA5\xE2\xE0\xAE\xA2 \x80\x2E\x83\x2E", - ATTR_HELP_BG); - help_line(r++, "", ATTR_HELP_BG); - help_line(r++, " Navigation:", ATTR_HELP_HDR); - help_line(r++, " \x18 \x19 Scroll one line up / down", ATTR_HELP_BG); - help_line(r++, " PgUp PgDn Scroll one page up / down", ATTR_HELP_BG); - help_line(r++, " Home End Jump to beginning / end of document", ATTR_HELP_BG); - help_line(r++, " \x1B \x1A Horizontal pan (code blocks/tables/unwrap)", ATTR_HELP_BG); - help_line(r++, " Esc F10 Exit", ATTR_HELP_BG); - help_line(r++, "", ATTR_HELP_BG); - help_line(r++, " Markdown elements:", ATTR_HELP_HDR); - help_line(r++, " # ## ### Headings H1-H6, **bold**, *italic*,", ATTR_HELP_BG); - help_line(r++, " `code`, ``` code ```, ~~strike~~, > quote", ATTR_HELP_BG); - help_line(r++, " - * + 1. 2. Ordered list, |----|----| Tables", ATTR_HELP_BG); - help_line(r++, "", ATTR_HELP_BG); - help_line(r++, " Encoding and View modes:", ATTR_HELP_HDR); - help_line(r++, " F2 RAW Mode", ATTR_HELP_BG); - help_line(r++, " F3 Wrap/Unwrap Mode for RAW View", ATTR_HELP_BG); - help_line(r++, " F4 HEX Mode (dump of original file)", ATTR_HELP_BG); - help_line(r++, " F8 Cycle CP866 / CP1251 / KOI8-R / UTF-8", ATTR_HELP_BG); - help_line(r++, " Auto-detected on open; F8 to override", ATTR_HELP_BG); - help_line(r++, "", ATTR_HELP_BG); - help_line(r++, " File size: up to 256 KB (EMM). Lines: up to 18432.", ATTR_HELP_BG); - - (void)getkey(); - - render_full_status(); - draw_viewport_from_cache(); - render_menu(); -} diff --git a/applications/Volkov/tests/fixtures/mdview2/mdview2_hex.c b/applications/Volkov/tests/fixtures/mdview2/mdview2_hex.c deleted file mode 100644 index c1eb72e..0000000 --- a/applications/Volkov/tests/fixtures/mdview2/mdview2_hex.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * mdview2_hex.c — HEX-дамп ОРИГИНАЛЬНОГО файла (F4). - * - * Показывает исходные байты файла (orig_file_phys), а не активный буфер: - * hex-дамп декодированного UTF-набора был бы искажением. Printable-колонка - * интерпретируется ТЕКУЩЕЙ кодировкой: CP866 как есть, CP1251/KOI8 через - * g_remap, UTF-8 — глиф на позиции лид-байта (continuation-байты → '.'). - * - * Формат ряда (79 колонок, один атрибут ATTR_TEXT — как RAW): - * 0x012340 │ XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX │ 16 символов - * - * Ряд выровнен на 16 байт и никогда не пересекает границу EMM-страницы - * (16 делит 16384 нацело) — один bank_read на ряд, fb()/W3-маппинг не нужны. - * Позиция (hex_top) живёт в байтах ОРИГИНАЛА; конвертацию в/из байт-позиции - * активного буфера делает ядро (view_pos/view_reanchor через map_off). - */ - -#include -#include -#include -#include -#include /* bank_read (чтение оригинала по страницам EMM) */ -#include "mdview2.h" - -#if WITH_HEX /* весь модуль опционален (mdview2_conf.h); при 0 пустой объектник */ - -#define HEX_BPR 16u /* байт на ряд */ -#define HEX_SPAN ((uint32_t)(VIEW_H - 1) * HEX_BPR) - -/* Геометрия ряда (колонки). */ -#define HX_OFF 1u /* "0x012340" */ -#define HX_SEP1 10u /* │ */ -#define HX_DUMP 12u /* пары hex-цифр */ -#define HX_SEP2 61u /* │ */ -#define HX_PRINT 63u /* printable */ - -static uint32_t hex_top; /* offset верхнего ряда (кратен 16) */ - -uint32_t hex_pos(void) { return hex_top; } - -/* Максимальный hex_top: последний ряд файла на нижней строке экрана. */ -static uint32_t hex_max_top(void) -{ - uint32_t last = (orig_file_size == 0) ? 0 - : ((orig_file_size - 1u) & ~(uint32_t)(HEX_BPR - 1u)); - return (last > HEX_SPAN) ? (last - HEX_SPAN) : 0; -} - -void hex_reanchor(uint32_t orig_off) -{ - uint32_t mt = hex_max_top(); - orig_off &= ~(uint32_t)(HEX_BPR - 1u); - hex_top = (orig_off > mt) ? mt : orig_off; -} - -void hex_screen_init(void) -{ - bios_clearwin(VIEW_TOP_ROW, 0, VIEW_H, SCREEN_W, ATTR_TEXT); -} - -/* ---- отрисовка ----------------------------------------------------- */ - -static uint8_t hexdig(uint8_t v) -{ - v &= 15u; - return (uint8_t)((v < 10u) ? ('0' + v) : ('A' - 10 + v)); -} - -/* Один байт оригинала (для UTF-lookahead за границей ряда — редкий путь). */ -static uint8_t fbo(uint32_t off) -{ - uint8_t b; - bank_read(orig_file_phys[off >> PAGE_BITS], (uint16_t)(off & PAGE_MASK), &b, 1); - return b; -} - -/* Printable-глиф для n байт ряда b[] (off — offset b[0] в файле) → out[]. */ -static void hex_print_cells(const uint8_t *b, uint32_t off, uint8_t n, uint8_t *out) -{ - for (uint8_t i = 0; i < n; i++) { - uint8_t c = b[i]; - uint8_t g; - if (c < 0x20u) { - g = '.'; - } else if (c < 0x80u || g_encoding != ENC_UTF8) { - g = (uint8_t)((c >= 0x80u && g_remap) ? g_remap[c - 0x80u] : c); - } else if ((c & 0xC0u) == 0x80u) { - g = '.'; /* continuation-байт */ - } else { - /* Лид-байт: собрать кодпойнт (хвост может выйти за ряд → fbo). - * 4-байтовые (за пределами CP866) и битые последовательности → '.'. */ - uint32_t cp; - uint8_t need, ok = 1; - if ((c & 0xE0u) == 0xC0u) { cp = (uint32_t)(c & 0x1Fu); need = 1; } - else if ((c & 0xF0u) == 0xE0u) { cp = (uint32_t)(c & 0x0Fu); need = 2; } - else { out[i] = '.'; continue; } - for (uint8_t k = 1; k <= need; k++) { - uint32_t p = off + i + k; - if (p >= orig_file_size) { ok = 0; break; } - uint8_t cb = (uint8_t)((i + k < n) ? b[i + k] : fbo(p)); - if ((cb & 0xC0u) != 0x80u) { ok = 0; break; } - cp = (cp << 6) | (uint32_t)(cb & 0x3Fu); - } - g = ok ? utf_cp_glyph(cp) : '.'; - if (g < 0x20u) g = '.'; /* overlong мог дать управляющий */ - } - out[i] = g; - } -} - -static uint8_t hex_line[SCREEN_W]; /* собранный ряд (только символы, единый attr) */ - -static void hex_draw_row(uint32_t off, uint8_t row) -{ - uint8_t buf[HEX_BPR]; - uint8_t n = 0; - - memset(hex_line, ' ', SCREEN_W); - if (off < orig_file_size) { - uint32_t rem = orig_file_size - off; - n = (rem >= HEX_BPR) ? (uint8_t)HEX_BPR : (uint8_t)rem; - bank_read(orig_file_phys[off >> PAGE_BITS], (uint16_t)(off & PAGE_MASK), buf, n); - } - if (n) { /* ряды за EOF — пустые */ - hex_line[HX_OFF] = '0'; - hex_line[HX_OFF + 1] = 'x'; - hex_line[HX_OFF + 2] = hexdig((uint8_t)(off >> 20)); - hex_line[HX_OFF + 3] = hexdig((uint8_t)(off >> 16)); - hex_line[HX_OFF + 4] = hexdig((uint8_t)(off >> 12)); - hex_line[HX_OFF + 5] = hexdig((uint8_t)(off >> 8)); - hex_line[HX_OFF + 6] = hexdig((uint8_t)(off >> 4)); - hex_line[HX_OFF + 7] = hexdig((uint8_t)off); - hex_line[HX_SEP1] = TBL_V; - for (uint8_t i = 0; i < n; i++) { - uint8_t x = (uint8_t)(HX_DUMP + i * 3u + ((i >= 8u) ? 1u : 0u)); - hex_line[x] = hexdig((uint8_t)(buf[i] >> 4)); - hex_line[x + 1] = hexdig(buf[i]); - } - hex_line[HX_SEP2] = TBL_V; - hex_print_cells(buf, off, n, hex_line + HX_PRINT); - } - bios_set_place(row, 0); - bios_write_until((const char *)hex_line, SCREEN_W, 0); -} - -void hex_draw(void) -{ - uint32_t off = hex_top; - for (uint8_t r = 0; r < VIEW_H; r++) { - hex_draw_row(off, (uint8_t)(VIEW_TOP_ROW + r)); - off += HEX_BPR; - } -} - -/* ---- статус-бар: % по байтам (как calc_raw_pct) --------------------- */ - -static uint8_t calc_hex_pct(void) -{ - return pct16((uint16_t)(hex_top >> 8), (uint16_t)(orig_file_size >> 8)); -} - -/* ---- навигация (вызывается из главного цикла при g_view == VIEW_HEX) */ - -uint8_t hex_key(uint8_t scan) -{ - uint32_t mt = hex_max_top(); - switch (scan) { - /* Одна строка — аппаратный скролл окна + подрисовка одного ряда - * (как в MD/RAW), без перерисовки всего экрана. */ - case KEY_DOWN: - if (hex_top < mt) { - hex_top += HEX_BPR; - scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 1, 0); /* содержимое вверх */ - hex_draw_row(hex_top + HEX_SPAN, (uint8_t)(VIEW_TOP_ROW + VIEW_H - 1)); - } - break; - case KEY_UP: - if (hex_top) { - hex_top -= HEX_BPR; - scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 2, 0); /* содержимое вниз */ - hex_draw_row(hex_top, VIEW_TOP_ROW); - } - break; - case KEY_PGDN: { - uint32_t nt = hex_top + (uint32_t)VIEW_H * HEX_BPR; - if (nt > mt) nt = mt; - if (nt != hex_top) { hex_top = nt; hex_draw(); } - break; - } - case KEY_PGUP: { - uint32_t d = (uint32_t)VIEW_H * HEX_BPR; - uint32_t nt = (hex_top >= d) ? hex_top - d : 0; - if (nt != hex_top) { hex_top = nt; hex_draw(); } - break; - } - case KEY_HOME: - if (hex_top) { hex_top = 0; hex_draw(); } - break; - case KEY_END: - if (hex_top != mt) { hex_top = mt; hex_draw(); } - break; - default: - return 0; /* не наша клавиша */ - } - render_percent_progress(calc_hex_pct()); - return 1; -} - -#endif /* WITH_HEX */ diff --git a/applications/Volkov/tests/fixtures/mdview2/mdview2_index.c b/applications/Volkov/tests/fixtures/mdview2/mdview2_index.c deleted file mode 100644 index 2740fbb..0000000 --- a/applications/Volkov/tests/fixtures/mdview2/mdview2_index.c +++ /dev/null @@ -1,1031 +0,0 @@ -/* - * mdview2_index.c — парсер/индексатор markdown (сердце приложения). - * - * Единственный проход по исходному файлу (index_begin/index_step): классификация строк - * (classify_line, is_*_raw), сборка абзацев с переносом (scan_join_stream), - * inline-разметка (inline_marker/inline_scan) и эмиссия готовых (char,attr)- - * ячеек в рендер-кэш (gc_put → seg_commit_cells → cache_*). Ведёт индекс - * сегментов в EMM (idx_get/idx_put) и докручивает UTF-конвертацию впереди - * позиции чтения. Разделяемые символы и API — в mdview2.h. - * - * Приватно (не торчит в .h): idx_rec_t, cur_rec/cur_seg_off, буфер ячеек - * g_cells, idx_get/idx_put и все классификаторы/сканеры. - */ - -#include -#include -#include /* COLOR()/COLOR_* для ATTR_* в сканерах */ -#include -#include /* bank_read/bank_write (idx_get/idx_put) */ -#include "mdview2.h" - -static uint8_t emph_to_attr(uint8_t emph, uint8_t base_attr); /* forward (нужна в inline-сканере) */ -static uint8_t is_hr_raw(uint32_t p); /* forward (нужна в classify_line) */ - -/* Начало следующей физической строки (байт после '\n', либо file_size). */ -static uint32_t next_line(uint32_t p) -{ - p = row_end(p); - return (p < file_size) ? p + 1 : p; -} - -typedef struct idx_rec_s { - uint32_t off; /* смещение первого байта исходного текста для сегмента */ - uint8_t flags; /* битовые флаги IF_* ниже */ - uint8_t style; /* начальный стиль INIT_STYLE_* в начале сегмента */ - uint8_t pad0; /* добивка до 8 байт (2048 записей на страницу 16 КБ) */ - uint8_t pad1; -} idx_rec_t; - -#define INDEX_REC_SIZE 8u - -static uint32_t cur_seg_off; /* смещение последнего emit_seg (кэш в near-памяти) */ -static idx_rec_t cur_rec; /* near-копия последней записанной idx-записи */ - -/* Для проверки границ выделения считаем пробел/таб/конец строки/0 пробельными. */ -static uint8_t ws_or_eol(char c) -{ - return (uint8_t)(c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == 0); -} - -static uint8_t ws_or_eol_or_delim(char c) -{ - return (uint8_t)(c == ' ' || c == ',' || c == '.' || c == '!' || c == '?' || c == ':' || c == ';' || c == '/' - || c == '\t' || c == '\n' || c == '\r' || c == 0); -} - -/* Маркер inline-выделения (*, **, _) распознаётся только если - * пробельная граница есть ровно с одной стороны (XOR-правило). - * Это защищает выражения вроде "2 * 3", "2 ** 3" и внутрисловные - * случаи (например COLOR_YELLOW) от ложного форматирования. */ -static uint8_t is_emph_flanked(char prev_ch, char next_ch) -{ - return (uint8_t)(ws_or_eol(prev_ch) != ws_or_eol_or_delim(next_ch)); -} - -/* Символ можно экранировать обратным слешем (\X): любая печатная ASCII- - * пунктуация (как в CommonMark). \* \_ \` \[ и т.п. → литерал X. */ -static uint8_t is_escapable(char c) -{ - return (uint8_t)(c >= '!' && c <= '~' && - !((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))); -} - -/* Типы сегмента CK_* и стили INIT_STYLE_* — в mdview2.h (общие с таблицами). */ - -/* Доступ к индексу в EMM. - * Размер записи 8 байт, поэтому запись не пересекает границу страницы - * и bank_read/bank_write работают с цельным блоком. */ -static void idx_get(uint16_t idx, idx_rec_t *r) -{ - uint8_t page = (uint8_t)(idx >> 11); /* номер страницы: idx / 2048 */ - uint16_t off = (uint16_t)((idx & 2047u) << 3); /* смещение записи: (idx % 2048) * 8 */ - bank_read(index_phys[page], off, r, INDEX_REC_SIZE); -} - -static void idx_put(uint16_t idx, const idx_rec_t *r) -{ - uint8_t page = (uint8_t)(idx >> 11); - uint16_t off = (uint16_t)((idx & 2047u) << 3); - bank_write(index_phys[page], off, r, INDEX_REC_SIZE); -} - -uint32_t seg_off(uint16_t idx) -{ - idx_rec_t r; - if (idx >= n_lines) return file_size; - idx_get(idx, &r); - return r.off; -} - -/* Последняя строка индекса с исходным offset <= off — обратный перевод - * байт-позиции в MD-строку (позиционирование при F2/F8). Оффсеты сегментов - * монотонны, поэтому бинарный поиск (~15 bank-чтений). */ -uint16_t line_at_off(uint32_t off) -{ - uint16_t lo = 0, hi = n_lines; - while (lo < hi) { - uint16_t mid = (uint16_t)((lo + hi) >> 1); - if (seg_off(mid) <= off) lo = (uint16_t)(mid + 1); - else hi = mid; - } - return (lo > 0) ? (uint16_t)(lo - 1) : 0; -} - -/* Установка флагов (маска IF_*) всегда идёт в последний emit_seg (n_lines-1). - * Используется near-копия cur_rec, чтобы не делать обратное чтение из банка. */ -static void set_cur_flags(uint8_t f) { cur_rec.flags |= f; idx_put((uint16_t)(n_lines - 1), &cur_rec); } - -/* ================================================================== - * Индексация строк - * ================================================================== */ - -/* INIT_STYLE_* — в mdview2.h (общие с модулем таблиц). */ - -/* Тип строки, который возвращает classify_line(). */ -#define LK_PLAIN 0 -#define LK_H1 1 -#define LK_H2 2 -#define LK_H3 3 -#define LK_H4 4 /* также используется для H5/H6 */ -#define LK_HR 5 -#define LK_ULIST 6 /* маркер ненумерованного списка */ -#define LK_OLIST 7 /* маркер нумерованного списка */ -#define LK_QUOTE 8 /* маркер цитаты */ - -/* Классифицирует логическую строку. - * - Заголовки: 1..4 символа '#' и затем пробел/таб (H5/H6 сводятся к H4). - * - Горизонтальная линия: только '-', '*' или '_' (одного вида, >=3), - * возможно с пробелами/табами между символами. - * - Ненумерованный список: "- ", "* " или "+ " перед содержимым. - * - Нумерованный список: цифры + '.'/ ')' + пробел. - * - Цитата: '>' с опциональным пробелом. - * - Иначе обычный текст. - * Для заголовков/списков/цитат out_content указывает на первый байт - * содержимого после маркера и завершающего пробела. */ -static uint8_t classify_line(uint32_t p_start, uint32_t *out_content) -{ - if (p_start >= file_size) return LK_PLAIN; - - char c0 = fb(p_start); - - /* Заголовок? */ - if (c0 == '#') { - uint8_t lvl = 0; - uint32_t p = p_start; - while (p < file_size && fb(p) == '#' && lvl < 6) { - lvl++; p++; - } - if (p < file_size) { - char ch = fb(p); - if (ch == ' ' || ch == '\t') { - p++; - *out_content = p; - if (lvl > 4) lvl = 4; - return lvl; /* LK_H1 .. LK_H4 */ - } - } - /* Символ '#' без пробела после него не считается заголовком. */ - } - - /* Горизонтальный разделитель. - * Проверяется до списков, чтобы `- - -` не стало маркером списка. */ - if (is_hr_raw(p_start)) return LK_HR; - - /* Для списков/цитат дополнительно допускаем ведущие пробелы - * (базовая поддержка вложенности). */ - uint32_t lp = p_start; - while (lp < file_size && fb(lp) == ' ') lp++; - char cl = (lp < file_size) ? fb(lp) : 0; - - /* Ненумерованный список? */ - if ((cl == '-' || cl == '*' || cl == '+') && - (lp + 1) < file_size && - fb(lp + 1) == ' ') { - *out_content = lp + 2; - return LK_ULIST; - } - - /* Нумерованный список? */ - if (cl >= '0' && cl <= '9') { - uint32_t p = lp; - while (p < file_size) { - char ch = fb(p); - if (ch < '0' || ch > '9') break; - p++; - } - if (p < file_size) { - char ch = fb(p); - if ((ch == '.' || ch == ')') && - (p + 1) < file_size && fb(p + 1) == ' ') { - *out_content = p + 2; - return LK_OLIST; - } - } - } - - /* Цитата? */ - if (cl == '>') { - uint32_t p = lp + 1; - if (p < file_size && fb(p) == ' ') p++; - *out_content = p; - return LK_QUOTE; - } - - return LK_PLAIN; -} - -/* Возвращает видимую ширину префикса (отступ + маркер) в колонках. */ -static uint8_t marker_visible_col(uint8_t kind, uint32_t p_start, uint32_t content_off) -{ - switch (kind) { - case LK_ULIST: - case LK_QUOTE: - case LK_OLIST: return (uint8_t)(content_off - p_start); - default: return 0; /* plain/headers спец-префикса не имеют */ - } -} - -/* ================== Merge: сборка ячеек на этапе индекс-скана ============== - * Форвард-сканер (scan_join_stream/inline_scan + прямые emit'ы в index_lines) - * собирает готовые (char,attr)-ячейки в g_cells ПО ХОДУ единственного прохода - * по файлу — без отдельного второго скана. На каждом emit_seg ячейки - * ПРЕДЫДУЩего сегмента флашатся в кэш (lag-1: к этому моменту сегмент уже - * полностью просканирован); последний сегмент флашится после цикла index_lines. - * g_ncells_at_space — снимок длины на последнем пробеле для усечения буфера - * при переносе строки (отбрасываем уехавшее за границу слово). */ -static uint8_t g_cells[MAX_CACHE_LINE_LEN * 2]; -uint8_t g_ncells; /* ячеек в текущем сегменте (общий с таблицами) */ -static uint8_t g_ncells_at_space; /* снимок g_ncells на последнем пробеле (для усечения при переносе) */ -static uint8_t g_skip_flush; /* следующий emit_seg пропустит lag-флаш (pending уже зафлашен вручную, напр. перед таблицей) */ - -/* Обрыв индексации: причина (0 / TRUNC_CONTENT / TRUNC_LINES) и строка, на - * которой упёрлись. Выставляются ОДИН раз (первая причина побеждает); по ним - * index_lines() в конце пишет строку-сообщение (IF_TRUNC_MSG, рисуется вживую). */ -static uint8_t g_trunc_cause; -static uint16_t g_trunc_line; - -/* Пишет одну (char,attr)-ячейку в g_cells, молча игнорируя переполнение - * MAX_CACHE_LINE_LEN. Оставлена ФУНКЦИЕЙ намеренно: инлайн в сильно - * регистро-нагруженный скан-цикл на Z80/SDCC увеличивает спиллы и замедляет - * (проверено замером — макро-инлайн дал регрессию ~11→13с). */ -void gc_put(char ch, uint8_t attr) -{ - if (g_ncells < MAX_CACHE_LINE_LEN) { - uint16_t i = (uint16_t)g_ncells * 2u; - g_cells[i] = (uint8_t)ch; - g_cells[i + 1] = attr; - g_ncells++; - } -} -void gc_fill(char ch, uint8_t attr, uint8_t n) { while (n-- > 0) gc_put(ch, attr); } - -/* Пишет g_cells как контент кэша для line_idx: flags берём из idx-записи, - * пустой буфер/blank → len 0; иначе резервируем место в пуле и коммитим. */ -static void seg_commit_cells(uint16_t line_idx) -{ - cache_rec_t out_rec; - out_rec.flags = 0; out_rec.reserved = 0; out_rec.pad[0] = 0; out_rec.pad[1] = 0; - idx_rec_t r; - uint8_t have = (uint8_t)(line_idx < n_lines); - if (have) { idx_get(line_idx, &r); out_rec.flags = r.flags; } - if (!have || (r.flags & IF_BLANK) || g_ncells == 0) { - out_rec.page = 0; out_rec.off = 0; out_rec.len = 0; - cache_dir_put(line_idx, &out_rec); - return; - } - out_rec.len = g_ncells; - if (cache_reserve((uint16_t)(g_ncells * 2u), &out_rec.page, &out_rec.off)) { - cache_commit(out_rec.page, out_rec.off, g_cells, (uint16_t)(g_ncells * 2u)); - } else { - /* Контент-кэш исчерпан: фиксируем причину и строку обрыва (один раз). - * index_lines() оборвёт цикл и перезапишет эту строку сообщением. */ - if (!g_trunc_cause) { g_trunc_cause = TRUNC_CONTENT; g_trunc_line = line_idx; } - out_rec.page = 0; out_rec.off = 0; out_rec.len = 0; - } - cache_dir_put(line_idx, &out_rec); -} - - -static void emit_seg(uint32_t off, uint8_t style, uint8_t ckind, uint8_t cont) -{ - (void)ckind; /* тип сегмента восстанавливается флагами idx-записи */ - if (n_lines >= max_lines) { - index_truncated = 1; - if (!g_trunc_cause) { g_trunc_cause = TRUNC_LINES; g_trunc_line = (uint16_t)(max_lines - 1); } - return; - } - cur_rec.off = off; - cur_rec.flags = (uint8_t)(cont ? IF_CONT : 0); - cur_rec.style = (uint8_t)(style & 7u); - cur_rec.pad0 = 0; - cur_rec.pad1 = 0; - idx_put(n_lines, &cur_rec); - cur_seg_off = off; - n_lines++; - /* Флаш ячеек ПРЕДЫДУЩего сегмента (lag-1): он полностью собран форвард-сканером. */ - if (n_lines >= 2) { - if (g_skip_flush) g_skip_flush = 0; /* pending уже зафлашен вручную */ - else seg_commit_cells((uint16_t)(n_lines - 2)); - } - g_ncells = 0; /* буфер ячеек под новый сегмент */ - g_ncells_at_space = 0; -} - -/* Проверяет, что физическая строка пуста (только пробелы/таб + перевод строки). */ -static uint8_t is_line_blank(uint32_t p) -{ - while (p < file_size) { - char c = fb(p); - if (c == '\n') return 1; - if (c != ' ' && c != '\t') return 0; - p++; - } - return 1; -} - -/* Число ведущих пробелов строки (отступ). Используется для отделения - * dedent-пунктов списка (#3) и для отступа обычного абзаца (#4). */ -static uint8_t leading_spaces(uint32_t p) -{ - uint8_t n = 0; - while (p < file_size && fb(p) == ' ' && n < 250) { n++; p++; } - return n; -} - -/* Быстрая проверка границы fenced-блока по маркеру ``` . - * Ведущие пробелы/табы пропускаются — fence может быть с отступом - * (например, ``` ```c ``` внутри элемента списка с отступом 2+ пробела). - * Язык после бэктиков (```c, ```sh) игнорируется: достаточно трёх ` подряд. */ -static uint8_t is_fence_raw(uint32_t p) -{ - while (p < file_size && (fb(p) == ' ' || fb(p) == '\t')) p++; - if (p + 2 >= file_size) return 0; - return (fb(p) == '`' && fb(p + 1) == '`' && fb(p + 2) == '`'); -} - -/* Быстрая проверка горизонтального разделителя (`---`, `***`, `___`). */ -static uint8_t is_hr_raw(uint32_t p) -{ - char c0 = fb(p); - if (c0 != '-' && c0 != '*' && c0 != '_') return 0; - char marker = 0; - uint8_t count = 0, ok = 1; - uint32_t hr_p = p; - while (hr_p < file_size) { - char ch = fb(hr_p++); - if (ch == '\n' || ch == '\r') break; - if (ch == ' ' || ch == '\t') continue; - if (ch != '-' && ch != '*' && ch != '_') { ok = 0; break; } - if (marker == 0) marker = ch; - else if (ch != marker) { ok = 0; break; } - count++; - } - return ok && count >= 3; -} - -/* Строка таблицы: первый непустой символ — '|'. - * Ведущие пробелы разрешены; такие строки не склеиваются и не переносятся. */ -static uint8_t is_table_raw(uint32_t p) -{ - while (p < file_size) { - char c = fb(p); - if (c == ' ' || c == '\t') { p++; continue; } - return (uint8_t)(c == '|'); - } - return 0; -} - -/* Разбор одного inline-МАРКЕРА в позиции *pq (< bound): \X, [x], `, **, ~~, - * *, _, \t. Общий код для inline_scan() и scan_join_stream(). Возврат: 1 — - * маркер обработан (вызывающий делает continue), 0 — символ обычный (пробел/ - * текст), его кладёт сам вызывающий (и проверяет перенос). Меняет - * *pq/*pls/*pseg/*pprev. attr берётся как emph_to_attr(line_style, base) — - * для join base=ATTR_TEXT (атрибут «обычного» текста). */ -static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls, - uint8_t *pseg, char *pprev, uint8_t base) -{ - uint32_t q = *pq; - uint8_t ls = *pls; - char ch = fb(q); - uint8_t at = emph_to_attr(ls, base); /* атрибут литералов текущего стиля */ - - if (ls != INIT_STYLE_CODE && ch == '\\' && (q + 1) < bound && is_escapable(fb(q + 1))) { - char nb = fb(q + 1); - gc_put(nb, at); - *pseg += 1; *pq = q + 2; *pprev = nb; return 1; - } - if (ls != INIT_STYLE_CODE && ch == '[' && (q + 2) < bound && fb(q + 2) == ']') { - gc_put('[', ATTR_TEXT_BOLD); gc_put(fb(q + 1), ATTR_TEXT_BOLD); gc_put(']', ATTR_TEXT_BOLD); - *pseg += 3; *pq = q + 3; *pprev = ']'; return 1; - } - if (ch == '`') { - if (ls == INIT_STYLE_PLAIN) { *pls = INIT_STYLE_CODE; *pq = q + 1; return 1; } - if (ls == INIT_STYLE_CODE) { *pls = INIT_STYLE_PLAIN; *pq = q + 1; return 1; } - gc_put('`', at); *pseg += 1; *pq = q + 1; *pprev = '`'; return 1; - } - if (ch == '*' && (q + 1) < bound && fb(q + 1) == '*') { - char nx = ((q + 2) < bound) ? fb(q + 2) : '\n'; - if (is_emph_flanked(*pprev, nx)) { - if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = INIT_STYLE_BOLD; *pq = q + 2; return 1; } - if (ls == INIT_STYLE_BOLD && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 2; return 1; } - } - gc_put('*', at); gc_put('*', at); - *pseg += 2; *pq = q + 2; *pprev = '*'; return 1; - } - if (ch == '~' && (q + 1) < bound && fb(q + 1) == '~') { - char nx = ((q + 2) < bound) ? fb(q + 2) : '\n'; - if (is_emph_flanked(*pprev, nx)) { - if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = INIT_STYLE_STRIKE; *pq = q + 2; return 1; } - if (ls == INIT_STYLE_STRIKE && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 2; return 1; } - } - gc_put('~', at); gc_put('~', at); - *pseg += 2; *pq = q + 2; *pprev = '~'; return 1; - } - if (ch == '*' || ch == '_') { - char nx = ((q + 1) < bound) ? fb(q + 1) : '\n'; - if (is_emph_flanked(*pprev, nx)) { - uint8_t ns = (ch == '*') ? INIT_STYLE_ITALIC : INIT_STYLE_UNDER; - if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = ns; *pq = q + 1; return 1; } - if (ls != INIT_STYLE_PLAIN && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 1; return 1; } - } - gc_put(ch, at); *pseg += 1; *pq = q + 1; *pprev = ch; return 1; - } - if (ch == '\t') { - uint8_t old_sc = *pseg; - uint8_t tgt_full = (uint8_t)((old_sc & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP); - uint8_t tgt = tgt_full; if (tgt > SCREEN_W) tgt = SCREEN_W; - gc_fill(' ', at, (uint8_t)(tgt_full - old_sc)); - *pseg = tgt; *pq = q + 1; *pprev = ' '; return 1; - } - return 0; /* пробел или обычный символ — обрабатывает вызывающий */ -} - -#define JOIN_MODE_NONE 0u -#define JOIN_MODE_LIST 1u -#define JOIN_MODE_QUOTE 2u -#define JOIN_MODE_PLAIN 3u - -/* ================== Единый сканер сегмента ================================ - * Общий цикл inline-форматирования, переносов и (в join-режимах) склейки - * физических строк — бывшие inline_scan + scan_join_stream. mode: - * JOIN_MODE_NONE — сканировать РОВНО [q, bound) без склейки; '\n' в - * диапазоне не ожидается (вызывающие дают строку/ячейку до EOL); - * активны base_attr (title-attr заголовка) и nowrap (замер таблиц); - * ckind всегда CK_OTHER (continuation-префикс не эмитится). - * JOIN_MODE_LIST/QUOTE/PLAIN — правила '\n'-склейки; bound == file_size, - * base_attr == ATTR_TEXT (emph_to_attr(ls, ATTR_TEXT) тождественно - * бывшему styles_map[ls]), nowrap == 0. - * Возвращает позицию останова; итоговый стиль — через *io_line_style. */ -static uint32_t scan_stream(uint32_t q, uint32_t bound, uint32_t para_start, - uint8_t col, uint8_t ckind, uint8_t *io_line_style, - uint8_t base_attr, uint8_t nowrap, uint8_t mode) -{ - uint32_t last_space = 0xFFFFFFFFu; - uint8_t last_space_style = *io_line_style; /* снимок line_style на момент last_space */ - char prev_ch = ' '; - uint8_t seg_col = col; - uint8_t line_style = *io_line_style; - /* base — атрибут «обычного» текста сегмента. Для заголовка первый сегмент - * имеет title-attr, а continuation-сегменты render рисует как PLAIN — - * переключаем base после 1-го переноса (в join-режимах base == ATTR_TEXT - * и присваивание ничего не меняет). */ - uint8_t base = base_attr; - - while (q < bound && n_lines < max_lines) { - char ch = fb(q); - uint8_t soft_break = 0; - - if (ch == '\n') { - uint32_t next = q + 1; - if (mode == JOIN_MODE_NONE) break; /* NONE: '\n' в [q,bound) не ожидается */ - if (next >= bound) break; - - if (mode == JOIN_MODE_LIST) { - if (is_line_blank(next)) break; - if (is_fence_raw(next) || is_hr_raw(next) || is_table_raw(next)) break; - - { - uint32_t next_content = next; - uint8_t next_kind = classify_line(next, &next_content); - if (next_kind == LK_ULIST || next_kind == LK_OLIST || - next_kind == LK_QUOTE || - (next_kind >= LK_H1 && next_kind <= LK_H4) || - next_kind == LK_HR) { - break; - } - } - while (next < bound) { - char ws = fb(next); - if (ws == ' ' || ws == '\t') next++; - else break; - } - q = next; - soft_break = 1; - ch = ' '; - } else if (mode == JOIN_MODE_QUOTE) { - if (is_line_blank(next)) break; - if (is_fence_raw(next) || is_hr_raw(next) || is_table_raw(next)) break; - - { - uint32_t next_content = next; - uint8_t next_kind = classify_line(next, &next_content); - if (next_kind != LK_QUOTE) break; - - { - uint32_t next_line_end = row_end(next_content); - - { - uint8_t empty_quote = 1; - for (uint32_t z = next_content; z < next_line_end; z++) { - char c = fb(z); - if (c != ' ' && c != '\t') { empty_quote = 0; break; } - } - if (empty_quote) break; - } - - { - uint32_t t = next_content; - while (t < next_line_end && (fb(t) == ' ' || fb(t) == '\t')) t++; - if (t < next_line_end && fb(t) == '>') break; - } - - while (next_content < next_line_end) { - char ws = fb(next_content); - if (ws == ' ' || ws == '\t') next_content++; - else break; - } - if (next_content < next_line_end && fb(next_content) == '>') { - next_content++; - if (next_content < next_line_end && fb(next_content) == ' ') next_content++; - } - q = next_content; - soft_break = 1; - ch = ' '; - } - } - } else { /* JOIN_MODE_PLAIN */ - if (fb(next) == '\n') break; - if (is_fence_raw(next) || is_hr_raw(next) || is_table_raw(next)) break; - { - uint32_t dummy; - if (classify_line(next, &dummy) != LK_PLAIN) break; - } - if (is_line_blank(next)) break; - - { - uint8_t is_hard = 0; - if (q >= para_start + 1) { - char b1 = fb(q - 1); - if (b1 == '\\') { - is_hard = 1; - } else if (q >= para_start + 2) { - char b2 = fb(q - 2); - if ((b1 == ' ' || b1 == '\t') && (b2 == ' ' || b2 == '\t')) - is_hard = 1; - } - } - if (is_hard) { - q++; - emit_seg(q, line_style, ckind, 0); - last_space = 0xFFFFFFFFu; - prev_ch = ' '; - seg_col = col; - continue; - } - } - - q++; - /* как render: пропускаем ведущие пробелы/табы строки-продолжения, - * склейка даёт РОВНО один пробел (иначе merge эмитит отступ как лишние ячейки) */ - while (q < bound && (fb(q) == ' ' || fb(q) == '\t')) q++; - soft_break = 1; - ch = ' '; - } - } - - /* Inline-маркеры (\X [x] ` ** ~~ * _ \t) — общий разбор; при - * soft_break ch==' ' (синтетический пробел склейки) — маркеры - * пропускаем, его кладёт ветка пробела ниже. */ - if (!soft_break && inline_marker(&q, bound, &line_style, &seg_col, &prev_ch, base)) - continue; - - if (ch == ' ') { - gc_put(' ', emph_to_attr(line_style, base)); - if (soft_break) { - last_space = q; /* синтетический пробел склейки */ - } else { - last_space = q + 1; - q++; - } - last_space_style = line_style; - g_ncells_at_space = g_ncells; /* снимок для усечения */ - seg_col++; - prev_ch = ' '; - } else { - gc_put(ch, emph_to_attr(line_style, base)); - seg_col++; - q++; /* soft_break здесь всегда 0 (ch != ' ') */ - prev_ch = ch; - } - if (!nowrap && seg_col >= SCREEN_W) { - uint8_t use_ls = (uint8_t)(last_space != 0xFFFFFFFFu && last_space > cur_seg_off); - uint32_t wrap_at = use_ls ? last_space : q; - if (wrap_at >= bound || wrap_at == cur_seg_off) break; - uint8_t wrap_style = use_ls ? last_space_style : line_style; - if (use_ls) g_ncells = g_ncells_at_space; /* усечь до пробела */ - emit_seg(wrap_at, wrap_style, ckind, 1); - base = ATTR_TEXT; /* continuation заголовка → PLAIN (в join base и так ATTR_TEXT) */ - /* Префикс continuation-сегмента: список и отступленный абзац (#4) — - * col пробелов выравнивания; цитата — отступ + маркер 0xB3 + пробел. - * Для обычного абзаца без отступа col==0 → префикса нет; CK_OTHER - * (заголовки/таблицы, NONE-режим) — префикса нет. */ - if (ckind == CK_LIST || ckind == CK_PLAIN) { - gc_fill(' ', ATTR_TEXT, col); - } else if (ckind == CK_QUOTE) { - uint32_t content_off2 = para_start + col; - uint8_t qsp = 0; - while (qsp + 2 < content_off2 && qsp < SCREEN_W && fb(para_start + qsp) == ' ') qsp++; - gc_fill(' ', ATTR_TEXT, qsp); - gc_put(0xB3, ATTR_QUOTE_MARKER); - gc_put(' ', ATTR_TEXT); - } - seg_col = col; - last_space = 0xFFFFFFFFu; - prev_ch = ' '; - q = wrap_at; - /* Откатываем line_style к снимку на момент wrap_at: диапазон - * [wrap_at, q_old) будет пересканирован заново, и переключающие - * символы (обратная кавычка, звёздочка, подчёркивание, тильда) - * внутри него иначе применились бы дважды — именно так возникал - * баг: code-маркер на границе переноса ломает цвет следующего слова (mdview.c, отчёт пользователя - * 2026-06-23). */ - line_style = wrap_style; - } - } - - *io_line_style = line_style; - return q; -} - -/* Тонкая обёртка прежнего API: скан ровно [q, q_end) без склейки строк. - * Вызывается для заголовков и ячеек таблиц (всегда CK_OTHER, col=0). */ -uint8_t inline_scan(uint32_t q, uint32_t q_end, uint8_t col, - uint8_t ckind, uint8_t line_style, uint8_t base_attr, - uint8_t nowrap) -{ - (void)scan_stream(q, q_end, q, col, ckind, &line_style, base_attr, nowrap, - JOIN_MODE_NONE); - return line_style; -} - -/* Потоковый сканер для index_lines(): mode задаёт правила '\n'-склейки. */ -static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col, - uint8_t ckind, uint8_t *io_line_style, uint8_t mode) -{ - return scan_stream(q, file_size, para_start, col, ckind, io_line_style, - ATTR_TEXT, 0, mode); -} - -/* Отрисовка таблиц (row_end/table_*) — в модуле mdview2_table.c. */ - -/* ================== Резюмируемый проход индексации ======================= - * Бывший монолит index_lines() порезан на begin/step/finish, чтобы фоновая - * сборка второго набора (bg_step ядра) могла выполнять индексацию порциями - * в паузах между клавишами. Всё межшаговое состояние — в статиках модуля - * (idx_p/idx_in_block ниже + g_cells/cur_rec/g_trunc_* выше); оно НЕ входит - * в docset_t и переживает свапы наборов, потому что обработчики клавиш - * видимого набора индексатор не трогают. */ -static uint32_t idx_p; /* позиция чтения исходника между шагами */ -static uint8_t idx_in_block; /* внутри fenced code-блока */ -static uint8_t idx_active; /* 1 = проход начат и ещё не финализирован */ - -/* Начинает новый проход (сброс позиции/буферов/причин обрыва). */ -void index_begin(void) -{ - idx_p = 0; - idx_in_block = 0; - n_lines = 0; - index_truncated = 0; - g_trunc_cause = 0; - g_trunc_line = 0; - cur_seg_off = 0xFFFFFFFFu; /* пока не эмитили ни одного сегмента */ - g_ncells = 0; - g_ncells_at_space = 0; - g_skip_flush = 0; - idx_active = 1; -} - -/* Хвост прохода: финальный флаш ячеек + строка-обрыв (бывший финал index_lines). */ -static void index_finish(void) -{ - idx_active = 0; - /* emit_seg() флашит ячейки с отставанием на одну строку (см. её - * комментарий) — последняя эмитированная строка файла так и остаётся - * незафлашенной, её ячейки ещё в g_cells; коммитим здесь. */ - if (n_lines > 0) seg_commit_cells((uint16_t)(n_lines - 1)); - - /* Лимит строк ловим ЗДЕСЬ, а не в emit_seg: главный цикл выходит по - * `n_lines < max_lines` ДО того, как emit_seg будет вызван в переполненном - * состоянии (для code-block — один emit_seg на строку, эта ветка не - * достигается). Признак — остановились, а файл не кончился; g_abort и - * исчерпание контента (TRUNC_CONTENT) уже обработаны/исключены. */ - if (!g_trunc_cause && !g_abort && idx_p < file_size) { - g_trunc_cause = TRUNC_LINES; - g_trunc_line = (uint16_t)(max_lines - 1); - } - - /* Файл был обрезан до 256 КБ при загрузке, а индексация дошла до конца - * клампнутых данных без исчерпания контента/строк → ДОПИСЫВАЕМ строку - * (место есть: n_lines < max_lines). Приоритет ниже content/lines — - * те означают, что обрезали ещё раньше, их сообщение точнее. */ - if (!g_trunc_cause && !g_abort && g_file_clamped) { - g_trunc_cause = TRUNC_FILE; - g_trunc_line = n_lines; - } - - /* Обрыв по контенту/лимиту строк (не путать с пользовательским g_abort): - * документ заканчиваем строкой-сообщением на g_trunc_line. Сообщение - * рисуется вживую (IF_TRUNC_MSG) — место в контент-кэше ему не нужно, что - * и снимает тупик «кэш полон, а сообщение тоже надо закэшировать». */ - if (g_trunc_cause && !g_abort) { - cache_rec_t r; - r.page = 0; r.off = 0; r.len = 0; - r.flags = IF_TRUNC_MSG; r.reserved = g_trunc_cause; - r.pad[0] = 0; r.pad[1] = 0; - cache_dir_put(g_trunc_line, &r); - n_lines = (uint16_t)(g_trunc_line + 1); /* документ кончается на строке-обрыве */ - } -} - -/* Одна итерация бывшего главного цикла index_lines(): классифицирует и - * эмитит очередной блок исходника (параграф/строку кода/таблицу целиком). - * do{}while(0) сохраняет исходные `continue` тела как выход из итерации - * с записью локальной p обратно в idx_p. */ -static void index_iter(void) -{ - if (g_loading) - progress_tick(); /* первый экран + статус + скролл по готовым данным */ - else if (!g_bg_building && (n_lines & 15) == 0) - spinner_tick(); - - uint32_t p = idx_p; - do { - /* Пустая строка — разделитель параграфов. - * В индекс добавляем не более одной пустой экранной строки подряд. */ - if (is_line_blank(p)) { - p = next_line(p); - /* Серия пустых строк схлопывается в одну визуальную строку. */ - if (n_lines == 0 || !(cur_rec.flags & IF_BLANK)) { - emit_seg(p, INIT_STYLE_PLAIN, CK_PLAIN, 0); - set_cur_flags(IF_BLANK); - } - continue; - } - - /* ---- fenced code-блок (граница или тело) ---- */ - if (is_fence_raw(p)) { - idx_in_block = !idx_in_block; - emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_cur_flags(IF_NOWRAP); - p = next_line(p); - continue; - } - if (idx_in_block) { - emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_cur_flags(IF_NOWRAP | IF_CODE | IF_HSCROLL); /* код скроллится целым блоком */ - { - uint8_t ccx = 0; - uint32_t pp = p; - while (pp < file_size && fb(pp) != '\n') { - char ch = fb(pp); - if (ch == '\t') { - uint8_t tgt = (uint8_t)((ccx & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP); - gc_fill(' ', ATTR_TEXT_CODE, (uint8_t)(tgt - ccx)); - ccx = tgt; - } else if (ch == '\r') { - gc_put(' ', ATTR_TEXT_CODE); ccx++; /* как render: \r → пробел */ - } else { - gc_put(ch, ATTR_TEXT_CODE); ccx++; - } - pp++; - } - } - p = next_line(p); - continue; - } - - /* ---- горизонтальный разделитель ---- */ - if (is_hr_raw(p)) { - emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_cur_flags(IF_NOWRAP); - gc_fill(0xC4, ATTR_HR, SCREEN_W); - p = next_line(p); - continue; - } - - /* ---- строка таблицы (| ... |): один nowrap-сегмент на исходную строку ---- */ - if (is_table_raw(p)) { - /* Освобождаем g_cells под измерение ширин: коммитим pending-сегмент - * сейчас; первый emit_seg таблицы пропустит свой lag-флаш. */ - if (n_lines >= 1) { seg_commit_cells((uint16_t)(n_lines - 1)); g_skip_flush = 1; } - g_ncells = 0; - - /* Проход 1: границы блока + ОТРЕНДЕРЕННЫЕ ширины колонок (мерим тем же - * inline_scan, что и при отрисовке — невидимые маркеры стиля не считаются). */ - uint8_t widths[TBL_MAX_COLS]; - for (uint8_t i = 0; i < TBL_MAX_COLS; i++) widths[i] = 0; - uint8_t ncols = 0; - uint32_t r = p; - while (r < file_size && is_table_raw(r)) { - uint32_t le = row_end(r); - uint8_t sep = table_is_sep_row(r, le); - uint32_t cp = table_first_cell(r); - uint32_t cs, ce; - uint8_t c = 0; - while (c < TBL_MAX_COLS && table_next_cell(&cp, le, &cs, &ce)) { - if (!sep && ce > cs) { - g_ncells = 0; - (void)inline_scan(cs, ce, 0, CK_OTHER, INIT_STYLE_PLAIN, ATTR_TEXT, 1); - if (g_ncells > widths[c]) widths[c] = g_ncells; - } - c++; - } - if (c > ncols) ncols = c; - r = (le < file_size) ? le + 1 : le; - } - g_ncells = 0; /* чисто для верхней рамки */ - uint32_t tbl_end = r; - if (ncols == 0) ncols = 1; - - /* Проход 2: верхняя рамка → строки/разделитель → нижняя рамка. */ - emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_cur_flags(IF_NOWRAP | IF_HSCROLL); - table_border(widths, ncols, TBL_TL, TBL_TM, TBL_TR); - - r = p; - while (r < tbl_end) { - uint32_t le = row_end(r); - emit_seg(r, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_cur_flags(IF_NOWRAP | IF_HSCROLL); - if (table_is_sep_row(r, le)) table_border(widths, ncols, TBL_ML, TBL_MM, TBL_MR); - else table_data_row(r, le, widths, ncols); - r = (le < file_size) ? le + 1 : le; - } - - emit_seg(tbl_end, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_cur_flags(IF_NOWRAP | IF_HSCROLL); - table_border(widths, ncols, TBL_BL, TBL_BM, TBL_BR); - - p = tbl_end; - continue; - } - - /* ---- классификация текущей строки ---- */ - uint32_t content_off = p; - uint8_t kind = classify_line(p, &content_off); - - /* ---- Заголовок: отдельная строка, не склеивается с параграфом ---- */ - if (kind >= LK_H1 && kind <= LK_H4) { - uint8_t hbase; /* title-attr, как в render */ - switch (kind) { - case LK_H1: hbase = ATTR_TEXT_TITLE1; break; - case LK_H2: hbase = ATTR_TEXT_TITLE2; break; - case LK_H3: hbase = ATTR_TEXT_TITLE3; break; - default: hbase = ATTR_TEXT_TITLE4; break; - } - emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - uint32_t line_end = row_end(content_off); - (void)inline_scan(content_off, line_end, 0, CK_OTHER, INIT_STYLE_PLAIN, hbase, 0); - p = next_line(p); - continue; - } - - /* ---- Пункт списка: может занимать несколько физических строк ---- */ - if (kind == LK_ULIST || kind == LK_OLIST) { - uint8_t col = marker_visible_col(kind, p, content_off); - uint8_t line_style = INIT_STYLE_PLAIN; - uint32_t q = content_off; - - emit_seg(p, line_style, CK_LIST, 0); - /* Префикс ПЕРВОГО сегмента: отступ + маркер + пробел (как render). */ - { - uint32_t qq = p, qmax = p + SCREEN_W; - if (kind == LK_ULIST) { - while (qq + 2 < content_off && qq < qmax && fb(qq) == ' ') qq++; - gc_fill(' ', ATTR_TEXT, (uint8_t)(qq - p)); - gc_put(0x07, ATTR_LIST_MARKER); - gc_put(' ', ATTR_TEXT); - } else { - while (qq < content_off && qq < qmax && fb(qq) == ' ') qq++; - gc_fill(' ', ATTR_TEXT, (uint8_t)(qq - p)); - while (qq < content_off - 1) { gc_put(fb(qq), ATTR_LIST_MARKER); qq++; } - gc_put(' ', ATTR_TEXT); - } - } - q = scan_join_stream(q, p, col, CK_LIST, &line_style, JOIN_MODE_LIST); - - /* Переходим к началу следующей физической строки после пункта. */ - q = next_line(q); - - /* Одна пустая строка между соседними маркерами списка подавляется. - * Две и более пустых строки оставляют видимый разделитель. */ - if (q < file_size && is_line_blank(q)) { - uint32_t t = q; - uint8_t blanks = 0; - while (t < file_size && is_line_blank(t)) { - t = next_line(t); - blanks++; - // if (blanks >= 2) break; - } - if (blanks == 1 && t < file_size) { - uint32_t d = t; - uint8_t nk = classify_line(t, &d); - if (nk == LK_ULIST || nk == LK_OLIST) { - /* #3: подавляем blank только если следующий пункт того же - * уровня или глубже; при МЕНЬШЕМ отступе (dedent) пустую - * строку оставляем — пункты должны визуально разделиться. */ - if (leading_spaces(t) >= leading_spaces(p)) { - p = t; /* подавляем единственную пустую строку */ - continue; - } - } - } - } - - p = q; - continue; - } - - /* ---- Параграф цитаты: может покрывать несколько физических строк ---- */ - if (kind == LK_QUOTE) { - uint8_t col = marker_visible_col(kind, p, content_off); - uint8_t line_style = INIT_STYLE_PLAIN; - uint32_t q = content_off; - - emit_seg(p, line_style, CK_QUOTE, 0); - /* Префикс ПЕРВОГО сегмента: отступ + маркер 0xB3 + пробел (как render LK_QUOTE). */ - { - uint32_t qq = p, qmax = p + SCREEN_W; - while (qq + 1 < content_off && qq < qmax && fb(qq) == ' ') qq++; - gc_fill(' ', ATTR_TEXT, (uint8_t)(qq - p)); - gc_put(0xB3, ATTR_QUOTE_MARKER); - gc_put(' ', ATTR_TEXT); - } - q = scan_join_stream(q, p, col, CK_QUOTE, &line_style, JOIN_MODE_QUOTE); - - p = next_line(q); - continue; - } - - /* ---- Обычный текстовый параграф: склейка через soft-break ---- */ - { - uint8_t line_style = INIT_STYLE_PLAIN; - /* #4: если абзац начинается с отступа — все его перенесённые строки - * получают такой же отступ (как continuation-префикс списка, но без - * маркера). Контент сканируем после отступа. Патологически большой - * отступ игнорируем. */ - uint8_t indent = leading_spaces(p); - if (indent >= SCREEN_W / 2) indent = 0; - - emit_seg(p, line_style, CK_PLAIN, 0); - if (indent) gc_fill(' ', ATTR_TEXT, indent); /* префикс первого сегмента */ - - uint32_t q = p + indent; - q = scan_join_stream(q, p, indent, CK_PLAIN, &line_style, JOIN_MODE_PLAIN); - - if (q >= file_size) { - p = file_size; - } else { - /* q стоит на завершающем newline параграфа. - * Переходим на следующую строку, чтобы внешняя петля её - * корректно переклассифицировала. */ - p = next_line(q); - } - } - } while (0); - idx_p = p; -} - -/* Выполняет до budget итераций индексации. Возврат: 1 — проход завершён - * (индекс финализирован index_finish), 0 — есть ещё работа. - * Конвертация UTF-8 держится впереди позиции чтения ЗДЕСЬ, до проверки - * условия останова: пока исходник не сконвертирован до конца, idx_p не - * может «догнать» file_size и оборвать проход преждевременно. */ -uint8_t index_step(uint8_t budget) -{ - if (!idx_active) return 1; - while (budget-- > 0) { - /* UTF-8: держим конвертацию впереди позиции чтения (включая look-ahead - * склейки/классификации ~ один абзац). file_size при этом = доступная - * сконвертированная часть и растёт по ходу. */ - if (g_utf_building && (uint32_t)(idx_p + CONV_MARGIN) > file_size) - utf_convert_more((uint32_t)(idx_p + CONV_MARGIN)); - - if (!(idx_p < file_size && n_lines < max_lines && !g_abort && !g_trunc_cause)) { - index_finish(); - return 1; - } - index_iter(); - } - return 0; -} -#define EM_NONE 0 -#define EM_BOLD 1 -#define EM_ITALIC 2 -#define EM_UNDER 3 -#define EM_CODE 4 -#define EM_STRIKE 5 - -/* Преобразует внутреннее состояние emphasis в экранный атрибут. */ -static uint8_t emph_to_attr(uint8_t emph, uint8_t base_attr) -{ - switch (emph) { - case EM_BOLD: return ATTR_TEXT_BOLD; - case EM_ITALIC: return ATTR_TEXT_ITALIC; - case EM_UNDER: return ATTR_TEXT_UNDERSORE; - case EM_CODE: return ATTR_TEXT_CODE; - case EM_STRIKE: return ATTR_TEXT_STRIKE; - default: return base_attr; - } -} diff --git a/applications/Volkov/tests/fixtures/mdview2/mdview2_md.c b/applications/Volkov/tests/fixtures/mdview2/mdview2_md.c deleted file mode 100644 index 712d949..0000000 --- a/applications/Volkov/tests/fixtures/mdview2/mdview2_md.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * mdview2_md.c — представление MD-документа (peer к mdview2_raw.c): отрисовка - * области из рендер-кэша + прокрутка (верт./гориз.) + навигация md_key(). - * Статус-бар, меню и спиннер — в mdview2_status.c (общие для MD/RAW). - * - * Работает поверх готового рендер-кэша (cache_dir_get → cache_rec_t) и - * состояния навигации (top_line/viewport_x/n_lines, см. mdview2.h). Никакого - * парсинга/обращения к исходному файлу: всё рисуется из (char,attr)-ячеек кэша - * через win_rest_remap (ремап 8-битных кодировок — в mdview2_enc.c). - * - * Пара видов: mdview2_md.c (MD, из кэша) ↔ mdview2_raw.c (RAW, из fb()). - */ - -#include -#include /* wrchar/scroll/COLOR */ -#include /* bios_set_place/bios_fillcharattr */ -#include "mdview2.h" - -/* Фаза 4-5 — cache-only draw path: рисует одну строку ИЗ КЭША, без - * единого обращения к fb()/исходному файлу. Горизонтальный скролл для - * nowrap-строк (Фаза 5) — это просто смещение начала среза на vx*2 - * байт ВНУТРИ ТОГО ЖЕ кэш-буфера (cache_reserve гарантирует, что вся - * строка лежит в одной странице, так что off+vx*2 не пересекает - * границу страницы); не nowrap-строки игнорируют viewport_x, как и - * раньше в живом render_line() (там это называлось effective_vx). */ -static void draw_line_from_cache(uint16_t line_idx, uint8_t row) -{ - cache_rec_t rec; - cache_dir_get(line_idx, &rec); - - /* Строка-обрыв (исчерпан контент-кэш / лимит строк): фикс. текст вживую, - * без обращения к контент-кэшу (см. index_lines финализацию). */ - if (rec.flags & IF_TRUNC_MSG) { - const char *msg; - switch (rec.reserved) { - case TRUNC_LINES: msg = "*** Line limit reached - document truncated ***"; break; - case TRUNC_FILE: msg = "*** File too large - truncated at 256 KB ***"; break; - default: msg = "*** Content cache exhausted - document truncated ***"; break; - } - bios_set_place(row, 0); - bios_fillcharattr(' ', ATTR_WARN, SCREEN_W); - put_str_attr(2, row, msg, ATTR_WARN); - return; - } - - bios_set_place(row, 0); - bios_fillcharattr(' ', ATTR_TEXT, SCREEN_W); - - /* Горизонтальный сдвиг применяется ко ВСЕМ строкам hscroll-блока (код, - * таблицы) — по ТИПУ, а не по длине: блок двигается целиком, включая - * строки короче 80 (короткая строка, ушедшая за левый край, просто - * показывает маркер '<' на пустом месте). HR и границы fence не имеют - * IF_HSCROLL и не сдвигаются. */ - uint8_t vx = (rec.flags & IF_HSCROLL) ? viewport_x : 0; - - if (rec.len > vx) { - uint16_t remaining = (uint16_t)(rec.len - vx); - uint8_t w = (remaining > SCREEN_W) ? SCREEN_W : (uint8_t)remaining; - uint16_t off = (uint16_t)(rec.off + (uint16_t)vx * 2u); - win_rest_remap(row, w, rec.page, off); - if (remaining > SCREEN_W) { - wrchar(SCREEN_W - 1, row, '>', ATTR_TRUNC); - } - } - if (vx > 0) { - wrchar(0, row, '<', ATTR_TRUNC); - } -} - -/* Перерисовывает все VIEW_H строк окна из кэша — основной путь - * перерисовки видимой области после загрузки файла (Фаза 3-4). */ -void draw_viewport_from_cache(void) -{ - for (uint8_t i = 0; i < VIEW_H; i++) { - draw_line_from_cache((uint16_t)(top_line + i), (uint8_t)(VIEW_TOP_ROW + i)); - } -} - - -/* ================================================================== - * Прокрутка - * ================================================================== */ - -/* Ограничивает top_line допустимым диапазоном с учётом высоты окна. */ -void clamp_top(void) -{ - if (n_lines <= VIEW_H) { - top_line = 0; - } else if (top_line > n_lines - VIEW_H) { - top_line = (uint16_t)(n_lines - VIEW_H); - } -} - -/* Прокрутка вверх на n строк с частичной перерисовкой при n == 1. */ -void md_scroll_up(uint16_t n) -{ - uint16_t new_top_line = (top_line >= n) ? (uint16_t)(top_line - n) : 0; - if( new_top_line != top_line) { - top_line = new_top_line; - if (n == 1) { - scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 2, 0); - draw_line_from_cache((uint16_t)(top_line), (uint8_t)(VIEW_TOP_ROW)); - } else { - draw_viewport_from_cache(); - } - } -} - -/* Прокрутка вниз на n строк с частичной перерисовкой при n == 1. */ -void md_scroll_down(uint16_t n) -{ - uint16_t new_top_line = (top_line + n < n_lines - VIEW_H) ? - (uint16_t)(top_line + n) : n_lines - VIEW_H; - if( new_top_line != top_line) { - top_line = new_top_line; - if (n == 1) { - scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 1, 0); - draw_line_from_cache((uint16_t)(top_line + VIEW_H - 1), (uint8_t)(VIEW_TOP_ROW + VIEW_H - 1)); - } else { - clamp_top(); - draw_viewport_from_cache(); - } - } -} - -/* Горизонтальный сдвиг (только если в окне есть nowrap-строки). - * Максимум сдвига ограничен самой широкой nowrap-строкой на экране. - * Фаза 5: ширина берётся прямо из директории кэша (rec.len — уже - * посчитанная реальная длина в ячейках после рендера), без единого - * обращения к fb()/seg_off()/исходному файлу. */ -void md_scroll_horizon(int8_t delta) -{ - uint16_t maxw = 0; - for (uint8_t i = 0; i < VIEW_H; i++) { - uint16_t li = (uint16_t)(top_line + i); - if (li >= n_lines) break; - cache_rec_t rec; - cache_dir_get(li, &rec); - if (!(rec.flags & IF_HSCROLL)) continue; /* только код/таблицы */ - if (rec.len > maxw) maxw = rec.len; /* самая широкая строка блока */ - } - if (maxw == 0) return; /* нет строк шире экрана */ - - /* Максимальный сдвиг = ширина за пределами экрана, в границах uint8. */ - uint16_t over = (maxw > SCREEN_W) ? (uint16_t)(maxw - SCREEN_W) : 0; - if (over > 248u) over = 248u; - uint8_t max_vx = (uint8_t)over; - - int16_t nx = (int16_t)viewport_x + delta; - if (nx < 0) nx = 0; - if (nx > (int16_t)max_vx) nx = max_vx; - - /* ПОРЯДОК ВАЖЕН: записываем viewport_x ДО сравнения, а сравниваем уже - * сохранённую копию old_vx с new_vx. Иначе SDCC генерирует для - * `if (new_vx != viewport_x) viewport_x = new_vx;` такой код: - * ld a, new_vx ; sub a,(viewport_x) ; jr Z,.. ; ld (viewport_x),a - * — т.е. для записи переиспользует регистр A, испорченный вычитанием в - * сравнении, и кладёт в viewport_x не new_vx, а (new_vx - old_vx). - * (asm-дамп 2026-06-24 подтвердил; даёт 10-8=2, 10-2=8 — ровно - * наблюдавшийся "прыжок" скролла.) Записав viewport_x первой, мы убираем - * портящий sub с пути записи; сравнение ниже только решает, перерисовывать - * ли экран. */ - uint8_t new_vx = (uint8_t)nx; - uint8_t old_vx = viewport_x; - viewport_x = new_vx; - if (new_vx != old_vx) - draw_viewport_from_cache(); -} - -/* Процент прокрутки (0..100) относительно доступного диапазона. */ -static uint8_t calc_md_pct(void) -{ - uint16_t total = drawable_lines(); - if (total <= VIEW_H) return 100; - return pct16(top_line, (uint16_t)(total - VIEW_H)); -} - -/* ---- навигация MD после загрузки (peer к raw_key) ---------------- */ - -/* Обрабатывает навигационную клавишу в MD-режиме (вызывается из главного - * цикла). Сама перерисовывает область/статус. Возврат: 1 — обработано, - * 0 — не наша клавиша (F1/F8/F10 разбирает main). */ -uint8_t md_key(uint8_t scan) -{ - switch (scan) { - case KEY_UP: - md_scroll_up(1); - break; - case KEY_DOWN: - md_scroll_down(1); - break; - case KEY_LEFT: - md_scroll_horizon(-(int8_t)HPAN_STEP); - break; - case KEY_RIGHT: - md_scroll_horizon(+(int8_t)HPAN_STEP); - break; - case KEY_PGUP: - md_scroll_up(VIEW_H); - break; - case KEY_PGDN: - md_scroll_down(VIEW_H); - break; - case KEY_HOME: - if(top_line != 0 || viewport_x != 0 ) { - top_line = 0; - viewport_x = 0; - draw_viewport_from_cache(); - } - break; - case KEY_END: - uint16_t new_top_line = (n_lines > VIEW_H) ? (uint16_t)(n_lines - VIEW_H) : 0; - if(top_line != new_top_line || viewport_x != 0 ) { - top_line = new_top_line; - viewport_x = 0; - draw_viewport_from_cache(); - } - break; - - default: - return 0; /* не наша клавиша */ - } - - render_percent_progress(calc_md_pct()); - render_md_status_numbers(); - return 1; -} diff --git a/applications/Volkov/tests/fixtures/mdview2/mdview2_raw.c b/applications/Volkov/tests/fixtures/mdview2/mdview2_raw.c deleted file mode 100644 index 051214b..0000000 --- a/applications/Volkov/tests/fixtures/mdview2/mdview2_raw.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * mdview2_raw.c — RAW-просмотр исходного текста (без markdown-форматирования). - * - * Работает по АКТИВНОМУ буферу документа через fb() (8-бит = исходные байты, - * UTF-8 = декодированный в CP866 буфер), ремап ≥0x80 (CP1251/KOI8) — на - * отрисовке. Индекс/кэш markdown не используются: позиция — байт-offset, - * 1 байт = 1 ячейка, \t показываем пробелом. Два режима: - * VIEW_RAW_WRAP — длинные строки переносятся кратно 80 (рвём слова); - * VIEW_RAW_HSCROLL — строка в один ряд, горизонтальный скролл. - * - * Память: 0 доп. EMM; near-буфер строки + переиспользование g_scratch_phys. - */ - -#include -#include -#include -#include -#include "mdview2.h" - -#if WITH_RAW /* весь модуль — опционален (mdview_conf.h); при 0 пустой объектник */ - -static uint32_t raw_top; /* байт-offset верхней строки экрана */ -static uint16_t raw_hpan; /* гориз. сдвиг (только HSCROLL) */ - -/* ---- примитивы навигации по физическим строкам / переносам -------- */ - -static uint32_t raw_line_start(uint32_t off) -{ - while (off && fb(off - 1) != '\n') off--; - return off; -} - -/* WRAP: начало следующего ряда от off (\n завершает строку; иначе +80). */ -static uint32_t raw_next_wrap(uint32_t off) -{ - uint32_t e = off, lim = off + SCREEN_W; - while (e < file_size) { - if (fb(e) == '\n') return e + 1; - e++; - if (e >= lim) { /* прошли 80 без \n */ - if (e < file_size && fb(e) == '\n') return e + 1; /* ровно 80 + \n */ - return e; /* длинная строка → продолжение */ - } - } - return e; /* EOF */ -} - -static uint32_t raw_prev_wrap(uint32_t off) -{ - if (off == 0) return 0; - uint32_t pls = raw_line_start(off - 1); /* off-1 — это \n пред. строки */ - while (pls + SCREEN_W < (off - 1)) pls += SCREEN_W; - return pls; -} - -/* HSCROLL: начало следующей/предыдущей физической строки. */ -static uint32_t raw_next_line(uint32_t off) -{ - while (off < file_size && fb(off) != '\n') off++; - return (off < file_size) ? off + 1 : off; -} - -static uint32_t raw_prev_line(uint32_t off) -{ - if (off == 0) return 0; - return raw_line_start(off - 1); -} - -/* ---- отрисовка ряда (общий near-буфер → g_scratch_phys → win_rest) - */ - -static uint8_t raw_buf[SCREEN_W + 1]; /* только символы (атрибут единый, задаётся при выводе) */ - -/* В RAW атрибут единый (ATTR_TEXT): буфер — только 80 символов, без пар - * (char,attr). Кладём один байт-глиф (\t→пробел, ремап ≥0x80). */ -static void raw_cell(uint8_t i, uint8_t ch) -{ - if (ch >= 0x80 && g_remap) ch = g_remap[ch - 0x80]; - raw_buf[i] = ch; -} -/* Вывод готового 80-символьного буфера в строку экрана одним BIOS-вызовом - * (char-буфер + единый attr — без WINREST/scratch-страницы). */ -static void raw_flush(uint8_t row) -{ - bios_set_place(row, 0); - // bios_writeattr((const char *)raw_buf, SCREEN_W, ATTR_TEXT); - // bios_writeattr_until((const char *)raw_buf, SCREEN_W, ATTR_TEXT, 0); - bios_write_until((const char *)raw_buf, SCREEN_W, 0); -} - -static uint32_t raw_draw_wrap_row(uint32_t off, uint8_t row) -{ - uint8_t i = 0; - uint32_t e = off, lim = off + SCREEN_W; - while (i < SCREEN_W && e < file_size) { - char c = fb(e); - if (c == '\n') { - raw_cell(i, 0); - break; - } - raw_cell(i, (uint8_t)(c == '\t' ? ' ' : c)); - i++; e++; - } - raw_flush(row); - return raw_next_wrap(off); -} - -static uint32_t raw_draw_hscroll_row(uint32_t off, uint8_t row) -{ - uint32_t e = off; - while (e < file_size && fb(e) != '\n') e++; /* e = конец строки */ - uint16_t len = e - off; - - uint8_t i = 0; - uint32_t p = off + raw_hpan; - while (i < SCREEN_W && p < e) { - char c = fb(p); - raw_cell(i, (uint8_t)(c == '\t' ? ' ' : c)); - i++; p++; - } - // raw_buf[i] = 0; - - if (raw_hpan > 0) - raw_buf[0] = '<'; - if (len > (uint16_t)raw_hpan + SCREEN_W) - raw_buf[SCREEN_W - 1] = '>'; - if (i < SCREEN_W) - raw_buf[i] = 0; - raw_flush(row); - return (e < file_size) ? e + 1 : e; -} - -/* Рисует один ряд от off (по режиму) и возвращает offset следующего ряда. */ -static uint32_t raw_draw_one(uint32_t off, uint8_t row) -{ - if (off >= file_size) { fill_row(row, ATTR_TEXT); return off; } - return (g_view == VIEW_RAW_WRAP) ? raw_draw_wrap_row(off, row) - : raw_draw_hscroll_row(off, row); -} - -void raw_draw(void) -{ - uint32_t off = raw_top; - for (uint8_t r = 0; r < VIEW_H; r++) - off = raw_draw_one(off, (uint8_t)(VIEW_TOP_ROW + r)); -} - -/* Прокрутка на одну строку: аппаратный scroll окна документа + отрисовка - * ОДНОЙ новой строки (как в MD-режиме). Смещение нижнего ряда вычисляется - * проходом VIEW_H рядов от raw_top — без отдельного инкрементального счётчика, - * который рассинхронизировался, когда контент кончается в середине экрана - * (короче окна после wrap→unwrap у конца файла). */ -static uint32_t raw_next(uint32_t off) -{ - return (g_view == VIEW_RAW_WRAP) ? raw_next_wrap(off) : raw_next_line(off); -} -static uint32_t raw_prev(uint32_t off) -{ - return (g_view == VIEW_RAW_WRAP) ? raw_prev_wrap(off) : raw_prev_line(off); -} - -/* ---- статус-бар: % по байтам (отрисовка — render_raw_status_numbers - * в mdview2_status.c, читает позицию через этот raw_pct) ---- */ - -/* Процент по байтам без 32-битного деления: масштабируем offset'ы (>>8 → - * ≤1024), дальше 16-битный pct16 (без __divulong). raw_top приватен модулю, - * поэтому % считаем здесь и отдаём готовым числом в статус-модуль. */ -uint8_t calc_raw_pct(void) -{ - uint16_t a = (uint16_t)(raw_top >> 8); - uint16_t b = (uint16_t)(file_size >> 8); - return pct16(a, b); /* den==0 (файл < 1 КБ) → pct16 вернёт 0 */ -} - -/* ---- посев позиции / пересев при смене активного буфера ----------- */ - -uint32_t raw_pos(void) -{ - return raw_top; -} - -/* Поставить RAW на начало строки, содержащей байт off (позиционирование - * при F2 MD→RAW и F8-переносе позиции между наборами). */ -void raw_reanchor(uint32_t off) -{ - if (off > file_size) off = file_size; - raw_top = raw_line_start(off); - raw_hpan = 0; -} - -void raw_seed_from(uint16_t md_top_line) -{ - raw_reanchor(seg_off(md_top_line)); -} - -/* Сброс в начало (клавиша Home). */ -static void raw_home(void) -{ - raw_top = 0; - raw_hpan = 0; -} - -void raw_end(void) -{ - raw_top = file_size; /* отмотать VIEW_H рядов назад */ - for (uint8_t k = 0; k < VIEW_H; k++) - raw_top = raw_prev(raw_top); - raw_hpan = 0; -} - -void raw_screen_init(void) { - bios_clearwin(1, 0, 30, 80, ATTR_TEXT); -} - -/* Нормализация позиции при смене под-режима RAW (F3 Wrap/Unwrap): выровнять - * raw_top на начало физической строки и снять горизонтальный сдвиг. */ -void raw_renorm(void) -{ - raw_top = raw_line_start(raw_top); - raw_hpan = 0; -} - -void raw_scroll_down(uint8_t delta) { - uint32_t off = raw_top; - for (uint8_t r = 0; r < VIEW_H; r++) { - if (off >= file_size) - return; /* экран не заполнен контентом */ - off = raw_next(off); - } - if (off >= file_size) - return; /* контент ровно по экран, ниже пусто */ - - if(delta ==1) { - scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 1, 0); /* содержимое вверх */ - off = raw_draw_one(off, (uint8_t)(VIEW_TOP_ROW + VIEW_H - 1)); /* новый нижний ряд */ - raw_top = raw_next(raw_top); - } else { - for (uint8_t k = 0; k < delta; k++) { - uint32_t n2 = raw_next(raw_top); - if (n2 >= file_size) - break; - raw_top = n2; - } - raw_draw(); /* PgUp/PgDn/Home/End — полный экран */ - } -} - -void raw_scroll_up(uint8_t delta) { - if (raw_top == 0) - return; - - if (delta == 1) { - raw_top = raw_prev(raw_top); - scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 2, 0); /* содержимое вниз */ - (void)raw_draw_one(raw_top, VIEW_TOP_ROW); - } else { - for (uint8_t k = 0; k < delta; k++) - raw_top = raw_prev(raw_top); - raw_draw(); /* PgUp/PgDn/Home/End — полный экран */ - } -} - -/* ---- навигация (вызывается из главного цикла при g_view != VIEW_MD) - */ - -uint8_t raw_key(uint8_t scan) -{ - switch (scan) { - case KEY_DOWN: - raw_scroll_down(1); - break; - case KEY_UP: - raw_scroll_up(1); - break; - case KEY_PGDN: - raw_scroll_down(VIEW_H); - break; - case KEY_PGUP: - raw_scroll_up(VIEW_H); - break; - case KEY_HOME: - raw_home(); - raw_draw(); - break; - case KEY_END: - raw_end(); - raw_draw(); - break; - case KEY_LEFT: - if (g_view == VIEW_RAW_HSCROLL && raw_hpan) { - raw_hpan = (raw_hpan >= HPAN_STEP) ? (uint16_t)(raw_hpan - HPAN_STEP) : 0; - raw_draw(); - } - break; - case KEY_RIGHT: - if (g_view == VIEW_RAW_HSCROLL && raw_hpan < 248u) { - raw_hpan = (uint16_t)(raw_hpan + HPAN_STEP); - raw_draw(); - } - break; - default: - return 0; /* не наша клавиша */ - } - - // render_raw_status_numbers(); - render_percent_progress(calc_raw_pct()); - return 1; -} - -#endif /* WITH_RAW */ diff --git a/applications/Volkov/tests/fixtures/mdview2/mdview2_status.c b/applications/Volkov/tests/fixtures/mdview2/mdview2_status.c deleted file mode 100644 index c767fbc..0000000 --- a/applications/Volkov/tests/fixtures/mdview2/mdview2_status.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * mdview2_status.c — статус-бар (строка 0), нижнее меню (строка 31) и спиннер. - * - * Хром бара АТОМИЗИРОВАН — обновляется по частям, без перерисовки всего бара: - * prerender_status() — фон + разделители │ + "MDVIEW" + имя файла (1 раз); - * status_encoding() — только поле кодировки (col 37), по F8; - * render_md_status_numbers() — числа MD: диапазон строк + % (при скролле); - * render_raw_status_numbers()— числа RAW: метка режима + % по байтам; - * render_full_status() — encoding + numbers (MD), при смене набора/режима; - * render_menu() — строка меню, по смене режима/готовности; - * spinner_tick/show — индикатор занятости во время загрузки. - * Оба *_status_numbers() самоочищаются по геометрии разделителей (DIV1_X/DIV2_X - * в mdview2.h) — фикс. хром не перерисовывается. RAW-позицию даёт raw_pct() - * из mdview2_raw.c (raw_top там приватен). - * - * Раскладка строки 0: - * 0 1..6 8 10.. 37..43 45 46..70 71 72..79 - * . MDVIEW spinner filename encod. │ L a-b / total │ pct% - */ - -#include -#include /* dec8/dec16, cputs */ -#include /* textattr/gotoxy/wrchar/COLOR */ -#include -#include /* bios_fillcharattr/bios_write_until */ -#include "mdview2.h" - -/* ---- Спиннер (слот SPINNER_COL в баре) --------------------------- */ -static const char spinner_chars[4] = { '|', '/', '-', '\\' }; -static uint8_t spinner_phase = 0; -static uint8_t spinner_active = 0; - -/* Продвигает спиннер на один кадр (если включён). */ -void spinner_tick(void) -{ - if (!spinner_active) - return; - wrchar(SPINNER_COL, 0, spinner_chars[spinner_phase & 3], ATTR_BAR_SPINNER); - spinner_phase++; -} - -/* Вкл/выкл спиннер; при выключении гасит его позицию. */ -void spinner_show(uint8_t on) -{ - spinner_active = on; - if (!on) wrchar(SPINNER_COL, 0, ' ', ATTR_BAR); -} - -/* ---- Вычисления для чисел статус-бара ----------------------------- */ - -/* Число строк, готовых к показу. Во время загрузки последняя эмитированная - * строка ещё в g_cells (не зафлашена), поэтому доступны [0..n_lines-2]. */ -uint16_t drawable_lines(void) -{ - if (g_loading) - return (n_lines >= 1) ? (uint16_t)(n_lines - 1) : 0; - return n_lines; -} - - -/* ---- Атомарные части статус-бара ---------------------------------- */ - -/* Фиксированный хром бара — рисуется ОДИН раз при старте: фон, два - * разделителя │, метка "MDVIEW" и имя файла (они не меняются за сессию). */ -void prerender_status(void) -{ - fill_row(0, ATTR_BAR); - wrchar(DIV1_X, 0, 0xB3, ATTR_BAR); /* │ */ - wrchar(DIV2_X, 0, 0xB3, ATTR_BAR); /* │ */ - put_str_attr(1, 0, "MDVIEW", ATTR_BAR); - put_str_attr(10, 0, filename, ATTR_BAR); -} - -/* Поле текущей кодировки (col 37, 7 ячеек). Меняется только по F8. */ -void status_encoding(void) -{ - textattr(ATTR_BAR); - gotoxy(DIV1_X - 10, 0); - bios_write_until(enc_name(g_encoding), 8, 0); -} - -static uint16_t local_total = UINT_MAX; -static uint16_t local_last = UINT_MAX; -static uint8_t local_loading = UCHAR_MAX; -static uint8_t local_pct = UCHAR_MAX; - -/* Числовая часть (MD): "L a-b / total" между разделителями + "pct%" справа. - * Перезаписывает ТОЛЬКО области между/после разделителей, сами │ и фикс. - * хром не трогает. */ -void render_md_status_numbers(void) -{ - uint16_t total = drawable_lines(); - uint16_t last = top_line + VIEW_H; - if (last > total) - last = total; - - if(local_total != total || local_last != last || local_loading != g_loading) { - local_total = total; local_last = last; local_loading = g_loading; - textattr(ATTR_BAR); - gotoxy(DIV1_X + 2, 0); - bios_fillcharattr(' ', ATTR_BAR, DIV2_X - DIV1_X - 2); /* очистить [DIV1_X+1 .. DIV2_X-1] */ - gotoxy(DIV1_X + 2, 0); - cputs("L "); - dec16(top_line + 1); - cputs("-"); - dec16(last); - cputs(" / "); - dec16(total); - if (g_loading) cputs("..."); /* ещё грузится */ - } - -} - -/* Полное обновление переменной части (MD): кодировка + числа. */ -void render_full_status(void) -{ - local_loading = UCHAR_MAX; - status_encoding(); - render_md_status_numbers(); -} - -void render_percent_progress(uint8_t pct) { - if (local_pct != pct) { - local_pct = pct; - gotoxy(DIV2_X + 2, 0); - bios_fillcharattr(' ', ATTR_BAR, SCREEN_W - DIV2_X - 2); /* очистить [DIV2_X+1 .. конец] */ - gotoxy(DIV2_X + 2, 0); - dec8(pct); - cputs("%"); - } -} - -#if WITH_RAW -/* RAW-вариант числовой части бара (peer к render_md_status_numbers): метка - * режима между разделителями + процент справа. Сам очищает свои области (та же - * геометрия DIVn_X), атомарен — не требует общего fill всего бара. Позиция - * берётся готовым числом из raw_pct() (raw_top приватен mdview2_raw.c). */ -void render_raw_status(void) -{ - // uint8_t pct = raw_pct(); - textattr(ATTR_BAR); - gotoxy(DIV1_X + 1, 0); - bios_writeattr_until(g_view == VIEW_RAW_WRAP ? " RAW wrap" : " RAW pan", - DIV2_X - DIV1_X - 1, ATTR_BAR, 0); /* метка + пад до DIV2_X-1 */ -} -#endif /* WITH_RAW */ - -#if WITH_HEX -/* HEX-вариант числовой части бара (peer к render_raw_status). */ -void render_hex_status(void) -{ - textattr(ATTR_BAR); - gotoxy(DIV1_X + 1, 0); - bios_writeattr_until(" HEX", DIV2_X - DIV1_X - 1, ATTR_BAR, 0); -} -#endif /* WITH_HEX */ - -/* ---- Нижнее меню (строка 31) -------------------------------------- */ - -void render_menu(void) -{ - /* Блок i = колонка i*8 (8 шириной): 2 поз. НОМЕР клавиши без 'F' (стиль - * ATTR_MENU_K, ведущий «чёрный» пробел кроме F10) + 6 поз. ТЕКСТ-функция - * (ATTR_MENU_T) сразу за номером. Номера рисуем для ВСЕХ 10 клавиш; текст — - * только у задействованных и доступных сейчас. */ - char num[3]; - - fill_row(MENU_ROW, ATTR_MENU_T); - - num[2] = 0; - for (uint8_t i = 0; i < 9; i++) { /* F1..F9: ' 1'..' 9' */ - num[0] = ' '; num[1] = (char)('1' + i); - put_str_attr((uint8_t)(i * 8), MENU_ROW, num, ATTR_MENU_K); - } - put_str_attr(9 * 8, MENU_ROW, "10", ATTR_MENU_K); /* F10 */ - - put_str_attr(0 * 8 + 2, MENU_ROW, "Help", ATTR_MENU_T); -#if WITH_RAW - if (g_ready) /* RAW доступен после постройки документа */ - put_str_attr(1 * 8 + 2, MENU_ROW, (g_view == VIEW_MD) ? "RAW" : "MD", ATTR_MENU_T); - if (VIEW_IS_RAW(g_view)) /* Wrap/Unwrap — только в RAW */ - put_str_attr(2 * 8 + 2, MENU_ROW, - (g_view == VIEW_RAW_WRAP) ? "UnWrap" : "Wrap", ATTR_MENU_T); -#endif -#if WITH_HEX - if (g_ready) /* HEX доступен после постройки документа */ - put_str_attr(3 * 8 + 2, MENU_ROW, (g_view == VIEW_HEX) ? "Back" : "Hex", ATTR_MENU_T); -#endif - if (g_f8_enabled) /* смена кодировки сейчас возможна */ - put_str_attr(7 * 8 + 2, MENU_ROW, "CodePg", ATTR_MENU_T); - put_str_attr(9 * 8 + 2, MENU_ROW, "Exit", ATTR_MENU_T); -} diff --git a/applications/Volkov/tests/fixtures/mdview2/mdview2_table.c b/applications/Volkov/tests/fixtures/mdview2/mdview2_table.c deleted file mode 100644 index d0d3aa5..0000000 --- a/applications/Volkov/tests/fixtures/mdview2/mdview2_table.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * mdview2_table.c — выровненная отрисовка markdown-таблиц. - * - * Разбор |-разделённых ячеек и эмиссия рамок/строк в общий буфер ячеек - * рендера (gc_put/gc_fill, см. mdview2.h). Вызывается из index_lines() - * ядра при обнаружении таблицы. Геометрия рамок (TBL_n), словарь стилей - * (CK_n, INIT_STYLE_n) и cell-buffer API объявлены в mdview2.h. - */ - -#include -#include /* COLOR() / COLOR_* для атрибутов TBL_ATTR/ATTR_TEXT */ -#include "mdview2.h" - -uint32_t row_end(uint32_t p) -{ - while (p < file_size && fb(p) != '\n') p++; - return p; -} - -/* Начало контента первой ячейки строки (после ведущих пробелов и '|'). */ -uint32_t table_first_cell(uint32_t row_start) -{ - uint32_t p = row_start; - while (p < file_size && fb(p) == ' ') p++; - if (p < file_size && fb(p) == '|') p++; - return p; -} - -/* Следующая ячейка: [*cs,*ce) — обрезанный диапазон; *pp продвигается за '|'. - * 0 — ячеек больше нет (включая хвостовую пустоту после последнего '|'). */ -uint8_t table_next_cell(uint32_t *pp, uint32_t lineend, uint32_t *cs, uint32_t *ce) -{ - uint32_t p = *pp; - if (p >= lineend) return 0; - uint32_t a = p; - while (p < lineend && fb(p) != '|') p++; - uint32_t b = p; - uint8_t had_pipe = (uint8_t)(p < lineend && fb(p) == '|'); - if (had_pipe) p++; - *pp = p; - while (a < b && fb(a) == ' ') a++; - while (b > a && fb(b - 1) == ' ') b--; - if (!had_pipe && a == b) return 0; - *cs = a; *ce = b; - return 1; -} - -/* Строка-разделитель (|---|:-:|): каждая ячейка непустая и только '-'/':'. */ -uint8_t table_is_sep_row(uint32_t row_start, uint32_t lineend) -{ - uint32_t p = table_first_cell(row_start); - uint32_t cs, ce; - uint8_t n = 0; - while (table_next_cell(&p, lineend, &cs, &ce)) { - if (cs >= ce) return 0; - for (uint32_t q = cs; q < ce; q++) { char c = fb(q); if (c != '-' && c != ':') return 0; } - n++; - } - return (uint8_t)(n > 0); -} - -/* Горизонтальная рамка в g_cells: left + (H×(w+2) + mid|right) по колонкам. */ -void table_border(const uint8_t *widths, uint8_t ncols, char left, char mid, char right) -{ - gc_put(left, TBL_ATTR); - for (uint8_t c = 0; c < ncols; c++) { - gc_fill(TBL_H, TBL_ATTR, (uint8_t)(widths[c] + 2)); - gc_put((c == (uint8_t)(ncols - 1)) ? right : mid, TBL_ATTR); - } -} - -/* Строка данных в g_cells: │ <ячейка, добитая до widths[c]> │ … */ -void table_data_row(uint32_t row_start, uint32_t lineend, const uint8_t *widths, uint8_t ncols) -{ - uint32_t p = table_first_cell(row_start); - uint32_t cs, ce; - gc_put(TBL_V, TBL_ATTR); - for (uint8_t c = 0; c < ncols; c++) { - uint8_t got = table_next_cell(&p, lineend, &cs, &ce); - gc_put(' ', TBL_ATTR); - uint8_t rendered = 0; - if (got && ce > cs) { - uint8_t before = g_ncells; - (void)inline_scan(cs, ce, 0, CK_OTHER, INIT_STYLE_PLAIN, ATTR_TEXT, 1); - rendered = (uint8_t)(g_ncells - before); - } - if (rendered < widths[c]) gc_fill(' ', ATTR_TEXT, (uint8_t)(widths[c] - rendered)); - gc_put(' ', TBL_ATTR); - gc_put(TBL_V, TBL_ATTR); - } -} diff --git a/applications/Volkov/tests/make_p10_rename_hdd.sh b/applications/Volkov/tests/make_p10_rename_hdd.sh deleted file mode 100755 index e0a1a75..0000000 --- a/applications/Volkov/tests/make_p10_rename_hdd.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh -# Создаёт HDD с target-пробой и UI-fixture F6 local rename. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p10_rename.chd} -COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe} -PROBE=${3:-$PROJECT_DIR/build/tests/renrt.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-rename.XXXXXX") - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -python3 "$PROJECT_DIR/tests/p10_rename_fixture.py" create "$TEMP_DIR" -sh "$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$COMMANDER" "$PROBE" "$TEMP_DIR/RENTEST" -printf 'P10 rename HDD ready: %s\n' "$IMAGE" diff --git a/applications/Volkov/tests/make_p11_delete_hdd.sh b/applications/Volkov/tests/make_p11_delete_hdd.sh deleted file mode 100755 index bdded54..0000000 --- a/applications/Volkov/tests/make_p11_delete_hdd.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh -# Создаёт HDD с target-пробой и UI-fixture F8 delete. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p11_delete.chd} -COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe} -PROBE=${3:-$PROJECT_DIR/build/tests/delrt.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-delete.XXXXXX") - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -python3 "$PROJECT_DIR/tests/p11_delete_fixture.py" create "$TEMP_DIR" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$COMMANDER" "$PROBE" "$TEMP_DIR/DELTEST" -printf 'P11 delete HDD ready: %s\n' "$IMAGE" diff --git a/applications/Volkov/tests/make_p12_group_copy_hdd.sh b/applications/Volkov/tests/make_p12_group_copy_hdd.sh deleted file mode 100755 index 6ef3c9b..0000000 --- a/applications/Volkov/tests/make_p12_group_copy_hdd.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -# Создаёт HDD с двумя панелями для группового F5. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p12_group_copy.chd} -COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-group-copy.XXXXXX") - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -python3 "$PROJECT_DIR/tests/p12_group_copy_fixture.py" create "$TEMP_DIR" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$COMMANDER" "$TEMP_DIR/GRSRC" "$TEMP_DIR/GRDST" -printf 'P12 group-copy HDD ready: %s\n' "$IMAGE" diff --git a/applications/Volkov/tests/make_p13_group_delete_hdd.sh b/applications/Volkov/tests/make_p13_group_delete_hdd.sh deleted file mode 100755 index 9794464..0000000 --- a/applications/Volkov/tests/make_p13_group_delete_hdd.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -# Создаёт HDD-fixture группового F8. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p13_group_delete.chd} -COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-group-delete.XXXXXX") - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -python3 "$PROJECT_DIR/tests/p13_group_delete_fixture.py" create "$TEMP_DIR" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$COMMANDER" "$TEMP_DIR/GDEL" -printf 'P13 group-delete HDD ready: %s\n' "$IMAGE" diff --git a/applications/Volkov/tests/make_p14_default_colors_hdd.sh b/applications/Volkov/tests/make_p14_default_colors_hdd.sh deleted file mode 100755 index c1dd8c5..0000000 --- a/applications/Volkov/tests/make_p14_default_colors_hdd.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -# Создаёт HDD-fixture стандартных цветов имён. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p14_default_colors.chd} -COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-default-colors.XXXXXX") - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -python3 "$PROJECT_DIR/tests/p14_default_colors_fixture.py" create "$TEMP_DIR" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$COMMANDER" "$TEMP_DIR/COLORS" -printf 'P14 default-colours HDD ready: %s\n' "$IMAGE" diff --git a/applications/Volkov/tests/make_p15_tree_cancel_hdd.sh b/applications/Volkov/tests/make_p15_tree_cancel_hdd.sh deleted file mode 100755 index badd603..0000000 --- a/applications/Volkov/tests/make_p15_tree_cancel_hdd.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -# Создаёт HDD-fixture отмены рекурсивного F5. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p15_tree_cancel.chd} -COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-cancel.XXXXXX") - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -python3 "$PROJECT_DIR/tests/p15_tree_cancel_fixture.py" create "$TEMP_DIR" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$COMMANDER" "$TEMP_DIR/CSRC" "$TEMP_DIR/CDST" -printf 'P15 tree-cancel HDD ready: %s\n' "$IMAGE" diff --git a/applications/Volkov/tests/make_p15_tree_copy_hdd.sh b/applications/Volkov/tests/make_p15_tree_copy_hdd.sh deleted file mode 100755 index 60578e4..0000000 --- a/applications/Volkov/tests/make_p15_tree_copy_hdd.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -# Создаёт HDD-fixture рекурсивного F5 одного каталога. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p15_tree_copy.chd} -COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-copy.XXXXXX") - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -python3 "$PROJECT_DIR/tests/p15_tree_copy_fixture.py" create "$TEMP_DIR" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$COMMANDER" "$TEMP_DIR/SRC" "$TEMP_DIR/DST" -printf 'P15 tree-copy HDD ready: %s\n' "$IMAGE" diff --git a/applications/Volkov/tests/make_p15_tree_self_hdd.sh b/applications/Volkov/tests/make_p15_tree_self_hdd.sh deleted file mode 100755 index 0e017db..0000000 --- a/applications/Volkov/tests/make_p15_tree_self_hdd.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -# Создаёт HDD-fixture target-inside-source guard. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p15_tree_self.chd} -COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-self.XXXXXX") - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -python3 "$PROJECT_DIR/tests/p15_tree_self_fixture.py" create "$TEMP_DIR" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$COMMANDER" "$TEMP_DIR/SRC" -printf 'P15 tree-self HDD ready: %s\n' "$IMAGE" diff --git a/applications/Volkov/tests/make_p16_group_tree_copy_hdd.sh b/applications/Volkov/tests/make_p16_group_tree_copy_hdd.sh deleted file mode 100755 index df21ae0..0000000 --- a/applications/Volkov/tests/make_p16_group_tree_copy_hdd.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -# Создаёт HDD-fixture group F5 с двумя directory roots. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p16_group_tree_copy.chd} -COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-group-tree.XXXXXX") - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -python3 "$PROJECT_DIR/tests/p16_group_tree_copy_fixture.py" create "$TEMP_DIR" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$COMMANDER" "$TEMP_DIR/GSRC" "$TEMP_DIR/GDST" -printf 'P16 group-tree HDD ready: %s\n' "$IMAGE" diff --git a/applications/Volkov/tests/make_p17_tree_delete_cancel_hdd.sh b/applications/Volkov/tests/make_p17_tree_delete_cancel_hdd.sh deleted file mode 100755 index cec1bbf..0000000 --- a/applications/Volkov/tests/make_p17_tree_delete_cancel_hdd.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -# Создаёт HDD-fixture runtime-cancel рекурсивного группового F8. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p17_tree_delete_cancel.chd} -COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-delete-cancel.XXXXXX") - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -python3 "$PROJECT_DIR/tests/p17_tree_delete_cancel_fixture.py" create "$TEMP_DIR" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$COMMANDER" "$TEMP_DIR/CDEL" -printf 'P17 tree-delete cancel HDD ready: %s\n' "$IMAGE" diff --git a/applications/Volkov/tests/make_p17_tree_delete_hdd.sh b/applications/Volkov/tests/make_p17_tree_delete_hdd.sh deleted file mode 100755 index 7e2670b..0000000 --- a/applications/Volkov/tests/make_p17_tree_delete_hdd.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -# Создаёт HDD-fixture рекурсивного группового F8. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p17_tree_delete.chd} -COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-tree-delete.XXXXXX") - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -python3 "$PROJECT_DIR/tests/p17_tree_delete_fixture.py" create "$TEMP_DIR" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$COMMANDER" "$TEMP_DIR/RDEL" -printf 'P17 tree-delete HDD ready: %s\n' "$IMAGE" diff --git a/applications/Volkov/tests/make_p18_metadata_hdd.sh b/applications/Volkov/tests/make_p18_metadata_hdd.sh deleted file mode 100755 index 8036cc5..0000000 --- a/applications/Volkov/tests/make_p18_metadata_hdd.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/sh -# Создаёт HDD с точными FAT date/time и R/H/S/A у source-файла. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p18_metadata.chd} -COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-metadata.XXXXXX") -BASE_CHD="$TEMP_DIR/base.chd" -RAW="$TEMP_DIR/p18_metadata.raw" -MTC="$TEMP_DIR/mtools.conf" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -python3 "$PROJECT_DIR/tests/p18_metadata_fixture.py" create "$TEMP_DIR" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$BASE_CHD" \ - "$COMMANDER" "$TEMP_DIR/MSRC" "$TEMP_DIR/MDST" - -chdman extractraw -i "$BASE_CHD" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -# Базовый make_hdd копирует дерево без host mtime. Повторная запись с -m -# задаёт контролируемые FAT date/time до включения readonly. -mcopy -m -o "$TEMP_DIR/MSRC/META.BIN" z:/MSRC/META.BIN -mattrib +r +h +s +a z:/MSRC/META.BIN -python3 "$PROJECT_DIR/tests/p18_metadata_fixture.py" verify-source "$RAW" - -mkdir -p "$(dirname -- "$IMAGE")" -chdman createhd -i "$RAW" -o "$IMAGE" -c none \ - -chs 128,16,32 -ss 512 -f -printf 'P18 metadata HDD ready: %s\n' "$IMAGE" diff --git a/applications/Volkov/tests/make_p19_overwrite_hdd.sh b/applications/Volkov/tests/make_p19_overwrite_hdd.sh deleted file mode 100755 index 29578f7..0000000 --- a/applications/Volkov/tests/make_p19_overwrite_hdd.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh -# Создаёт HDD для target-пробы транзакционного overwrite. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p19_overwrite.chd} -PROBE=${2:-$PROJECT_DIR/build/tests/p19/delrt.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-p19.XXXXXX") -BASE_CHD="$TEMP_DIR/base.chd" -RAW="$TEMP_DIR/p19.raw" -MTC="$TEMP_DIR/mtools.conf" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -python3 "$PROJECT_DIR/tests/p19_overwrite_fixture.py" create "$TEMP_DIR" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$BASE_CHD" \ - "$PROBE" "$TEMP_DIR/SOURCE" "$TEMP_DIR/TARGET" -chdman extractraw -i "$BASE_CHD" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mattrib +r z:/TARGET/READONLY.BIN -mkdir -p "$(dirname -- "$IMAGE")" -chdman createhd -i "$RAW" -o "$IMAGE" -c none \ - -chs 128,16,32 -ss 512 -f -printf 'P19 overwrite HDD ready: %s\n' "$IMAGE" diff --git a/applications/Volkov/tests/make_p19_policy_hdd.sh b/applications/Volkov/tests/make_p19_policy_hdd.sh deleted file mode 100755 index 6fc1897..0000000 --- a/applications/Volkov/tests/make_p19_policy_hdd.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh -# Создаёт HDD полного UI-сценария conflict-policy P19. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p19_policy.chd} -COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-p19-ui.XXXXXX") -BASE_CHD="$TEMP_DIR/base.chd" -RAW="$TEMP_DIR/p19.raw" -MTC="$TEMP_DIR/mtools.conf" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -python3 "$PROJECT_DIR/tests/p19_policy_fixture.py" create "$TEMP_DIR" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$BASE_CHD" \ - "$COMMANDER" "$TEMP_DIR/P19SRC" "$TEMP_DIR/P19DST" -chdman extractraw -i "$BASE_CHD" -o "$RAW" -f >/dev/null -printf 'drive z: file="%s" partition=1\n' "$RAW" > "$MTC" -export MTOOLSRC="$MTC" MTOOLS_SKIP_CHECK=1 -mattrib +r z:/P19DST/RONLY.BIN -mkdir -p "$(dirname -- "$IMAGE")" -chdman createhd -i "$RAW" -o "$IMAGE" -c none \ - -chs 128,16,32 -ss 512 -f -printf 'P19 policy HDD ready: %s\n' "$IMAGE" diff --git a/applications/Volkov/tests/make_p3_stress_floppy.sh b/applications/Volkov/tests/make_p3_stress_floppy.sh deleted file mode 100644 index ba509b3..0000000 --- a/applications/Volkov/tests/make_p3_stress_floppy.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/sh -# Создаёт стандартную FAT12-дискету для P3 stress-теста. -# Аргументы: [образ [исполняемый файл]]; по умолчанию — локальный образ. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -DEFAULT_IMAGE="$PROJECT_DIR/build/media/p3_stress.img" -IMAGE=${1:-$DEFAULT_IMAGE} -EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-p3.XXXXXX") -mkdir -p "$(dirname -- "$IMAGE")" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -mkdir "$TEMP_DIR/MANY" "$TEMP_DIR/BIGDIR" - -i=0 -while [ "$i" -lt 40 ]; do - name=$(printf 'M%07d.TXT' "$i") - cp "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" "$TEMP_DIR/MANY/$name" - i=$((i + 1)) -done - -i=0 -while [ "$i" -lt 680 ]; do - name=$(printf 'B%07d.DAT' "$i") - cp "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" "$TEMP_DIR/BIGDIR/$name" - i=$((i + 1)) -done - -mformat -C -i "$IMAGE" -f 1440 -v SPRCMD :: -mcopy -i "$IMAGE" "$EXECUTABLE" :: -mmd -i "$IMAGE" ::EMPTY -mmd -i "$IMAGE" ::TESTDIR -mcopy -i "$IMAGE" "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" :: -mcopy -i "$IMAGE" "$PROJECT_DIR/tests/p3_data/ZETA.BIN" :: -mcopy -i "$IMAGE" "$PROJECT_DIR/tests/p3_data/INNER.TXT" ::TESTDIR/ -mcopy -i "$IMAGE" -s "$TEMP_DIR/MANY" :: -mcopy -i "$IMAGE" -s "$TEMP_DIR/BIGDIR" :: - -mdir -i "$IMAGE" :: -echo "P3 stress floppy ready: $IMAGE (executable: $EXECUTABLE)" diff --git a/applications/Volkov/tests/make_p3_stress_hdd.sh b/applications/Volkov/tests/make_p3_stress_hdd.sh deleted file mode 100755 index 3516eed..0000000 --- a/applications/Volkov/tests/make_p3_stress_hdd.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/sh -# Создаёт отдельный FAT16 HDD для интеграционных тестов Commander. -# Аргументы: [образ [исполняемый файл]]; по умолчанию — локальный build/HDD. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p3_stress.chd" -IMAGE=${1:-$DEFAULT_IMAGE} -EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-hdd.XXXXXX") -FIXTURE_ROOT="$TEMP_DIR/P3DATA" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -mkdir -p "$(dirname -- "$IMAGE")" -mkdir "$FIXTURE_ROOT" -mkdir "$FIXTURE_ROOT/EMPTY" "$FIXTURE_ROOT/TESTDIR" \ - "$FIXTURE_ROOT/MANY" "$FIXTURE_ROOT/BIGDIR" - -cp "$PROJECT_DIR/tests/p3_data/INNER.TXT" \ - "$FIXTURE_ROOT/TESTDIR/INNER.TXT" - -i=0 -while [ "$i" -lt 40 ]; do - name=$(printf 'M%07d.TXT' "$i") - cp "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" \ - "$FIXTURE_ROOT/MANY/$name" - i=$((i + 1)) -done - -i=0 -while [ "$i" -lt 680 ]; do - name=$(printf 'B%07d.DAT' "$i") - cp "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" \ - "$FIXTURE_ROOT/BIGDIR/$name" - i=$((i + 1)) -done - -cp "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" "$FIXTURE_ROOT/P3ROOT.TXT" -cp "$PROJECT_DIR/tests/p3_data/ZETA.BIN" "$FIXTURE_ROOT/ZETA.BIN" - -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$EXECUTABLE" \ - "$FIXTURE_ROOT/EMPTY" "$FIXTURE_ROOT/TESTDIR" \ - "$FIXTURE_ROOT/MANY" "$FIXTURE_ROOT/BIGDIR" \ - "$FIXTURE_ROOT/P3ROOT.TXT" "$FIXTURE_ROOT/ZETA.BIN" - -echo "P3 stress HDD ready: $IMAGE (D:, executable: SPRCMD.EXE)" diff --git a/applications/Volkov/tests/make_p4_copy_hdd.sh b/applications/Volkov/tests/make_p4_copy_hdd.sh deleted file mode 100755 index bc5d76a..0000000 --- a/applications/Volkov/tests/make_p4_copy_hdd.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -# Создаёт отдельный FAT16 HDD для интеграционного теста F5. -# Аргументы: [образ [исполняемый файл]]. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p4_copy.chd" -IMAGE=${1:-$DEFAULT_IMAGE} -EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-copy.XXXXXX") -FIXTURE_ROOT="$TEMP_DIR/P4DATA" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -mkdir -p "$(dirname -- "$IMAGE")" -python3 "$PROJECT_DIR/tests/p4_fixture.py" create "$FIXTURE_ROOT" - -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$EXECUTABLE" "$FIXTURE_ROOT/COPYFROM" "$FIXTURE_ROOT/COPYTO" - -echo "P4 copy HDD ready: $IMAGE (D:, executable: SPRCMD.EXE)" diff --git a/applications/Volkov/tests/make_p4_exec_hdd.sh b/applications/Volkov/tests/make_p4_exec_hdd.sh deleted file mode 100755 index 2a76f40..0000000 --- a/applications/Volkov/tests/make_p4_exec_hdd.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh -# Создаёт отдельный HDD для EXEC/restore-теста P4.2. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p4_exec.chd" -IMAGE=${1:-$DEFAULT_IMAGE} -COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe} -CHILD=${3:-$PROJECT_DIR/tests/p4_exec_child/p4child.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-exec.XXXXXX") -FIXTURE_ROOT="$TEMP_DIR/P4EXEC" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -mkdir -p "$(dirname -- "$IMAGE")" -mkdir -p "$FIXTURE_ROOT/EXECLEFT" "$FIXTURE_ROOT/EXECRGHT" -cp "$CHILD" "$FIXTURE_ROOT/EXECLEFT/P4CHILD.EXE" -cp "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" \ - "$FIXTURE_ROOT/EXECLEFT/BROKEN.EXE" -cp "$PROJECT_DIR/tests/p3_data/INNER.TXT" \ - "$FIXTURE_ROOT/EXECLEFT/NOTE.TXT" - -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$COMMANDER" "$FIXTURE_ROOT/EXECLEFT" "$FIXTURE_ROOT/EXECRGHT" - -echo "P4 EXEC HDD ready: $IMAGE (D:, child: EXECLEFT/P4CHILD.EXE)" diff --git a/applications/Volkov/tests/make_p5_copy_faults_hdd.sh b/applications/Volkov/tests/make_p5_copy_faults_hdd.sh deleted file mode 100755 index 3293a2e..0000000 --- a/applications/Volkov/tests/make_p5_copy_faults_hdd.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -# Создаёт HDD с отдельной матрицей отказов банкового copy-job. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p5_copy_faults.chd" -IMAGE=${1:-$DEFAULT_IMAGE} -EXECUTABLE=${2:-$PROJECT_DIR/build/tests/cperr.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-copy-faults.XXXXXX") -FIXTURE_ROOT="$TEMP_DIR/P5COPY" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -mkdir -p "$(dirname -- "$IMAGE")" -python3 "$PROJECT_DIR/tests/p5_copy_faults_fixture.py" create "$FIXTURE_ROOT" - -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$EXECUTABLE" "$FIXTURE_ROOT/SOURCE" "$FIXTURE_ROOT/TARGET" - -echo "P5 copy-fault HDD ready: $IMAGE" diff --git a/applications/Volkov/tests/make_p5_enospc_hdd.sh b/applications/Volkov/tests/make_p5_enospc_hdd.sh deleted file mode 100755 index aafc0b6..0000000 --- a/applications/Volkov/tests/make_p5_enospc_hdd.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh -# Создаёт почти полный HDD для реальной ошибки записи ENOSPC. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p5_enospc.chd" -IMAGE=${1:-$DEFAULT_IMAGE} -EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-enospc.XXXXXX") -FIXTURE_ROOT="$TEMP_DIR/ENOSPC" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -mkdir -p "$(dirname -- "$IMAGE")" -python3 "$PROJECT_DIR/tests/p5_enospc_fixture.py" create "$FIXTURE_ROOT" -python3 "$PROJECT_DIR/tests/p5_enospc_fixture.py" verify-initial "$FIXTURE_ROOT" - -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$EXECUTABLE" "$FIXTURE_ROOT/SOURCE" "$FIXTURE_ROOT/TARGET" \ - "$FIXTURE_ROOT/FILLER.BIN" - -echo "P5 ENOSPC HDD ready: $IMAGE" diff --git a/applications/Volkov/tests/make_p5_hdd.sh b/applications/Volkov/tests/make_p5_hdd.sh deleted file mode 100755 index 3091c01..0000000 --- a/applications/Volkov/tests/make_p5_hdd.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -# Создаёт HDD для P5: 20 копирований, 100 refresh и cleanup-циклы. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p5_stability.chd" -IMAGE=${1:-$DEFAULT_IMAGE} -EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-p5.XXXXXX") -FIXTURE_ROOT="$TEMP_DIR/P5DATA" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -mkdir -p "$(dirname -- "$IMAGE")" -python3 "$PROJECT_DIR/tests/p5_fixture.py" create "$FIXTURE_ROOT" -python3 "$PROJECT_DIR/tests/p5_fixture.py" verify-initial "$FIXTURE_ROOT" - -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$EXECUTABLE" "$FIXTURE_ROOT/SOURCE" "$FIXTURE_ROOT/TARGET" - -echo "P5 stability HDD ready: $IMAGE" diff --git a/applications/Volkov/tests/make_p5_media_hdd.sh b/applications/Volkov/tests/make_p5_media_hdd.sh deleted file mode 100755 index e5aa06d..0000000 --- a/applications/Volkov/tests/make_p5_media_hdd.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh -# Создаёт HDD для физического unload/load во время F5. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p5_media.chd" -IMAGE=${1:-$DEFAULT_IMAGE} -EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-media.XXXXXX") -FIXTURE_ROOT="$TEMP_DIR/P5MEDIA" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -mkdir -p "$(dirname -- "$IMAGE")" -python3 "$PROJECT_DIR/tests/p5_media_fixture.py" create "$FIXTURE_ROOT" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$EXECUTABLE" "$FIXTURE_ROOT/MEDIA" - -echo "P5 media-change HDD ready: $IMAGE" diff --git a/applications/Volkov/tests/make_p5_panel_edges_hdd.sh b/applications/Volkov/tests/make_p5_panel_edges_hdd.sh deleted file mode 100755 index 65414a2..0000000 --- a/applications/Volkov/tests/make_p5_panel_edges_hdd.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/sh -# Создаёт каталоги с итоговыми panel.count ровно 27 и 28 (включая `..`). - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p5_panel_edges.chd" -IMAGE=${1:-$DEFAULT_IMAGE} -EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-panel-edge.XXXXXX") -FIXTURE_ROOT="$TEMP_DIR/EDGES" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -mkdir -p "$(dirname -- "$IMAGE")" "$FIXTURE_ROOT/N27" "$FIXTURE_ROOT/N28" - -i=0 -while [ "$i" -lt 26 ]; do - name=$(printf 'A%07d.TXT' "$i") - cp "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" "$FIXTURE_ROOT/N27/$name" - i=$((i + 1)) -done - -i=0 -while [ "$i" -lt 27 ]; do - name=$(printf 'B%07d.TXT' "$i") - cp "$PROJECT_DIR/tests/p3_data/P3ROOT.TXT" "$FIXTURE_ROOT/N28/$name" - i=$((i + 1)) -done - -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$EXECUTABLE" "$FIXTURE_ROOT/N27" "$FIXTURE_ROOT/N28" - -echo "P5 panel-edge HDD ready: N27=26+parent, N28=27+parent" diff --git a/applications/Volkov/tests/make_p5_viewer_hdd.sh b/applications/Volkov/tests/make_p5_viewer_hdd.sh deleted file mode 100755 index 68f4b3a..0000000 --- a/applications/Volkov/tests/make_p5_viewer_hdd.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh -# Создаёт HDD для запуска реального полноэкранного mdview2 из Commander. - -set -eu - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -DEFAULT_IMAGE="$PROJECT_DIR/build/hdd/p5_viewer.chd" -IMAGE=${1:-$DEFAULT_IMAGE} -COMMANDER=${2:-$PROJECT_DIR/build/sprcmd.exe} -VIEWER=${3:-$PROJECT_DIR/build/external/mdview2.exe} -README=${4:-$PROJECT_DIR/tests/fixtures/mdview2/README.md} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-viewer.XXXXXX") -FIXTURE_ROOT="$TEMP_DIR/VIEWDATA" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -mkdir -p "$(dirname -- "$IMAGE")" "$FIXTURE_ROOT/VIEWER" -cp "$VIEWER" "$FIXTURE_ROOT/VIEWER/MDVIEW2.EXE" -cp "$README" "$FIXTURE_ROOT/VIEWER/README.MD" - -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$COMMANDER" "$FIXTURE_ROOT/VIEWER" - -echo "P5 viewer HDD ready: VIEWER/MDVIEW2.EXE + README.MD" diff --git a/applications/Volkov/tests/make_p6_sort_hdd.sh b/applications/Volkov/tests/make_p6_sort_hdd.sh deleted file mode 100755 index fbd8e09..0000000 --- a/applications/Volkov/tests/make_p6_sort_hdd.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -# Собирает HDD с target-регрессией и двумя каталогами UI-сортировки. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p6_sort.chd} -EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe} -PROBE=${3:-$PROJECT_DIR/build/tests/sortrt.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-sort.XXXXXX") -FIXTURE_ROOT="$TEMP_DIR/SORTDATA" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -mkdir -p "$(dirname -- "$IMAGE")" -python3 "$PROJECT_DIR/tests/p6_sort_fixture.py" create "$FIXTURE_ROOT" - -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$EXECUTABLE" "$PROBE" "$FIXTURE_ROOT/SORTL" "$FIXTURE_ROOT/SORTR" - -echo "P6 sort HDD ready: $IMAGE" diff --git a/applications/Volkov/tests/make_p7_select_hdd.sh b/applications/Volkov/tests/make_p7_select_hdd.sh deleted file mode 100755 index 6f03e50..0000000 --- a/applications/Volkov/tests/make_p7_select_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Собирает HDD target/UI-регрессии выбора записей. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p7_select.chd} -EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe} -PROBE=${3:-$PROJECT_DIR/build/tests/selrt.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-select.XXXXXX") -FIXTURE_ROOT="$TEMP_DIR/SELDATA" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -mkdir -p "$(dirname -- "$IMAGE")" -python3 "$PROJECT_DIR/tests/p7_select_fixture.py" create "$FIXTURE_ROOT" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$EXECUTABLE" "$PROBE" "$FIXTURE_ROOT/SELECT" -echo "P7 selection HDD ready: $IMAGE" diff --git a/applications/Volkov/tests/make_p9_mkdir_hdd.sh b/applications/Volkov/tests/make_p9_mkdir_hdd.sh deleted file mode 100755 index d5d73ac..0000000 --- a/applications/Volkov/tests/make_p9_mkdir_hdd.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Собирает HDD target/UI-регрессии F7 mkdir. - -set -eu -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для упаковки HDD}" - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -IMAGE=${1:-$PROJECT_DIR/build/hdd/p9_mkdir.chd} -EXECUTABLE=${2:-$PROJECT_DIR/build/sprcmd.exe} -PROBE=${3:-$PROJECT_DIR/build/tests/mkrt.exe} -TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/sprcmd-mkdir.XXXXXX") -FIXTURE_ROOT="$TEMP_DIR/MKDATA" - -cleanup() -{ - rm -rf -- "$TEMP_DIR" -} -trap cleanup EXIT HUP INT TERM - -mkdir -p "$(dirname -- "$IMAGE")" -python3 "$PROJECT_DIR/tests/p9_mkdir_fixture.py" create "$FIXTURE_ROOT" -"$SPRINTER_ROOT/toolchain/make_hdd.sh" "$IMAGE" \ - "$EXECUTABLE" "$PROBE" "$FIXTURE_ROOT/MKTEST" -echo "P9 mkdir HDD ready: $IMAGE" diff --git a/applications/Volkov/tests/mame_image_probe.lua b/applications/Volkov/tests/mame_image_probe.lua deleted file mode 100644 index c608fcc..0000000 --- a/applications/Volkov/tests/mame_image_probe.lua +++ /dev/null @@ -1,15 +0,0 @@ --- Диагностический список MAME image-устройств Sprinter и их свойств. - -local done = false - -emu.register_periodic(function() - if done then return end - done = true - for tag, image in pairs(manager.machine.images) do - print(string.format( - "[image-probe] tag=%s instance=%s exists=%s readonly=%s file=%s", - tag, image.instance_name, tostring(image.exists), - tostring(image.readonly), image.filename)) - end - manager.machine:exit() -end) diff --git a/applications/Volkov/tests/mame_p10_rename.lua b/applications/Volkov/tests/mame_p10_rename.lua deleted file mode 100644 index 8edaabe..0000000 --- a/applications/Volkov/tests/mame_p10_rename.lua +++ /dev/null @@ -1,148 +0,0 @@ --- 0.2/P10: F6 local rename, unchanged name, collision и group guard. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - a={"P1.6",0x04}, b={"P1.5",0x10}, c={"P1.4",0x08}, - d={"P1.4",0x04}, e={"P1.4",0x20}, f={"P1.5",0x02}, - i={"P1.4",0x01}, l={"P2.2",0x08}, m={"P2.0",0x10}, - n={"P2.0",0x08}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, t={"P1.5",0x20}, x={"P1.2",0x10}, - ["."]={"P2.2",0x10}, ["\n"]={"P2.1",0x10}, - [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -local function backspaces(t, count) - for _ = 1, count do - key(t, "P2.1", 0x20) - t = t + 0.18 - end - return t -end - -type_text(8.0, "d:\n") -type_text(9.0, "RENRT.EXE\n") -type_text(12.0, "SPRCMD.EXE\n") - -key(18.0, "P2.1", 0x10) -- enter RENTEST. -key(20.0, "P2.3", 0x10) -- Down x4 -> UIOLD.TXT. -key(20.5, "P2.3", 0x10) -key(21.0, "P2.3", 0x10) -key(21.5, "P2.3", 0x10) -key(23.0, "P2.1", 0x80) -- F6. -local t = backspaces(24.0, 9) -type_text(t, "final.bin") -key(t + 1.4, "P2.1", 0x10) - -key(32.0, "P2.1", 0x80) -- F6, имя без изменений. -key(33.0, "P2.1", 0x10) - -key(36.0, "P2.1", 0x80) -- F6, collision с EXIST.TXT. -t = backspaces(37.0, 9) -type_text(t, "exist.txt") -key(t + 1.4, "P2.1", 0x10) - -key(44.0, "P2.5", 0x01) -- Home -> `..`. -key(45.0, "P2.1", 0x80) -- F6 guard virtual. -key(48.0, "P2.3", 0x10) -- NEWDIR. -key(49.0, "P2.6", 0x01) -- Insert dir; cursor -> file. -key(50.0, "P2.1", 0x80) -- group ambiguity guard. - -key(54.0, "P2.3", 0x80) -- F10. -key(56.0, "P2.1", 0x10) - -table.sort(events, function(left, right) return left[1] < right[1] end) - -local snap_times = { - 19.0, -- target probe already renamed file and directory; - 23.5, -- dialog default UIOLD.TXT; - 30.5, -- FINAL.BIN created and focused; - 34.0, -- unchanged name skipped; - 42.0, -- existing target rejected; - 46.0, -- virtual `..` rejected; - 51.0, -- selected dir elsewhere makes group action explicit; - 58.0 -- clean DSS prompt. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local value = manager.machine.time - return value.seconds + value.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p10-rename] snapshot t=" .. elapsed) - end - end - - if elapsed >= 60.0 then - print("[p10-rename] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p11_delete.lua b/applications/Volkov/tests/mame_p11_delete.lua deleted file mode 100644 index 4b9d5dd..0000000 --- a/applications/Volkov/tests/mame_p11_delete.lua +++ /dev/null @@ -1,140 +0,0 @@ --- 0.2/P11: F8 cancel, file, empty/nonempty directory и guards. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - l={"P2.2",0x08}, m={"P2.0",0x10}, n={"P2.0",0x08}, - p={"P2.3",0x20}, r={"P1.5",0x01}, s={"P1.2",0x08}, - t={"P1.5",0x20}, x={"P1.2",0x10}, ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(9.0, "DELRT.EXE\n") -type_text(12.0, "SPRCMD.EXE\n") - -key(18.0, "P2.1", 0x10) -- enter DELTEST. -key(20.0, "P2.3", 0x10) -- Down x4 -> REMOVE.TXT. -key(20.5, "P2.3", 0x10) -key(21.0, "P2.3", 0x10) -key(21.5, "P2.3", 0x10) -key(23.0, "P2.2", 0x80) -- F8 file, then cancel. -key(25.0, "P2.0", 0x08) -- N. - -key(28.0, "P2.2", 0x80) -- F8 file, confirm. -key(30.0, "P2.1", 0x10) - -key(34.0, "P2.3", 0x10) -- EMPTYDIR. -key(35.0, "P2.2", 0x80) -key(37.0, "P2.1", 0x10) - -key(40.0, "P2.3", 0x10) -- NONEMPTY. -key(41.0, "P2.2", 0x80) -key(43.0, "P2.1", 0x10) - -key(46.0, "P2.5", 0x01) -- Home -> `..`. -key(47.0, "P2.2", 0x80) -- virtual guard. -key(50.0, "P2.3", 0x10) -- NONEMPTY. -key(51.0, "P2.6", 0x01) -- select dir, cursor -> KEEP. -key(52.0, "P2.2", 0x80) -- group ambiguity guard. - -key(56.0, "P2.3", 0x80) -- F10. -key(58.0, "P2.1", 0x10) - -table.sort(events, function(left, right) return left[1] < right[1] end) - -local snap_times = { - 19.0, -- target probe left UI fixture intact; - 23.5, -- file confirmation; - 26.0, -- cancel preserved REMOVE.TXT; - 32.0, -- file deleted; - 39.0, -- empty directory deleted; - 45.0, -- nonempty directory rejected; - 48.0, -- virtual `..` rejected; - 53.0, -- ambiguous group rejected; - 60.0 -- clean DSS prompt. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local value = manager.machine.time - return value.seconds + value.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p11-delete] snapshot t=" .. elapsed) - end - end - - if elapsed >= 62.0 then - print("[p11-delete] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p12_group_copy.lua b/applications/Volkov/tests/mame_p12_group_copy.lua deleted file mode 100644 index 80b993e..0000000 --- a/applications/Volkov/tests/mame_p12_group_copy.lua +++ /dev/null @@ -1,125 +0,0 @@ --- Регрессия P12/P16: выбранные файлы и каталог копируются одним F5. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(10.0, "SPRCMD.EXE\n") - -key(16.0, "P1.6", 0x20) -- Tab -> right, cursor GRDST. -key(17.0, "P2.1", 0x10) -- right enters GRDST. -key(18.0, "P1.6", 0x20) -- Tab -> left. -key(19.0, "P2.3", 0x10) -- left GRDST -> GRSRC. -key(20.0, "P2.1", 0x10) -- enter GRSRC. - -key(23.0, "P2.3", 0x10) -- ADIR. -key(24.0, "P2.6", 0x01) -- select ADIR, cursor A.BIN. -key(25.0, "P2.6", 0x01) -- A.BIN. -key(26.0, "P2.6", 0x01) -- B.BIN. -key(27.0, "P2.6", 0x01) -- C.BIN. -key(30.0, "P2.1", 0x40) -- F5 group success. -key(36.0, "P2.1", 0x40) -- second F5 stops at existing A.BIN. -key(41.0, "P1.6", 0x20) -- show refreshed passive panel. -key(46.0, "P2.3", 0x80) -- F10. -key(48.0, "P2.1", 0x10) - -table.sort(events, function(left, right) return left[1] < right[1] end) - -local snap_times = { - 21.5, -- source/target panels opened; - 28.5, -- ADIR + three files selected; - 34.0, -- top-level files and selected directory copied; - 39.0, -- repeat stopped at existing target; - 43.0, -- passive panel shows three files; - 50.0 -- clean DSS prompt. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local value = manager.machine.time - return value.seconds + value.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p12-group-copy] snapshot t=" .. elapsed) - end - end - - if elapsed >= 52.0 then - print("[p12-group-copy] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p13_group_delete.lua b/applications/Volkov/tests/mame_p13_group_delete.lua deleted file mode 100644 index ae4312e..0000000 --- a/applications/Volkov/tests/mame_p13_group_delete.lua +++ /dev/null @@ -1,121 +0,0 @@ --- 0.2/P13 regression: file-only group F8 leaves unselected directories. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(10.0, "SPRCMD.EXE\n") - -key(17.0, "P2.1", 0x10) -- enter GDEL. -key(20.0, "P2.3", 0x10) -- ADIR. -key(21.0, "P2.3", 0x10) -- EMPTYDIR. -key(22.0, "P2.3", 0x10) -- A.TXT. -key(23.0, "P2.6", 0x01) -- select A.TXT -> B.BIN. -key(24.0, "P2.6", 0x01) -- select B.BIN -> KEEP.TXT. -key(27.0, "P2.2", 0x80) -- F8 group, cancel. -key(29.0, "P2.0", 0x08) -- N. -key(32.0, "P2.2", 0x80) -- F8 group, confirm. -key(34.0, "P2.1", 0x10) - -key(42.0, "P2.3", 0x80) -- F10. -key(44.0, "P2.1", 0x10) - -table.sort(events, function(left, right) return left[1] < right[1] end) - -local snap_times = { - 19.0, -- исходное дерево; - 25.0, -- два каталога и два файла выбраны; - 27.5, -- group confirmation; - 30.0, -- cancel сохранил всё; - 36.0, -- файлы удалены, невыбранные каталоги сохранены; - 46.0 -- clean DSS prompt. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local value = manager.machine.time - return value.seconds + value.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p13-group-delete] snapshot t=" .. elapsed) - end - end - - if elapsed >= 48.0 then - print("[p13-group-delete] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p14_default_colors.lua b/applications/Volkov/tests/mame_p14_default_colors.lua deleted file mode 100644 index b879534..0000000 --- a/applications/Volkov/tests/mame_p14_default_colors.lua +++ /dev/null @@ -1,106 +0,0 @@ --- 1.0/P14: стандартные цвета каталогов, EXE и остальных файлов. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(10.0, "SPRCMD.EXE\n") -key(17.0, "P2.1", 0x10) -- enter COLORS. -key(24.0, "P2.3", 0x80) -- F10. -key(26.0, "P2.1", 0x10) -- confirm exit. - -table.sort(events, function(left, right) return left[1] < right[1] end) - -local snap_times = { - 19.0, -- известный набор: dir, EXE, ZIP, no-ext, TXT; - 29.0 -- clean DSS prompt. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local value = manager.machine.time - return value.seconds + value.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p14-default-colors] snapshot t=" .. elapsed) - end - end - - if elapsed >= 31.0 then - print("[p14-default-colors] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p15_tree_cancel.lua b/applications/Volkov/tests/mame_p15_tree_cancel.lua deleted file mode 100644 index 0ee4424..0000000 --- a/applications/Volkov/tests/mame_p15_tree_cancel.lua +++ /dev/null @@ -1,105 +0,0 @@ --- 0.2/P15 hardening: Esc посреди 4-МБ файла tree-job. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(10.0, "SPRCMD.EXE\n") -key(16.0, "P1.6", 0x20) -- right. -key(17.0, "P2.1", 0x10) -- enter CDST. -key(18.0, "P1.6", 0x20) -- left. -key(19.0, "P2.3", 0x10) -- CDST -> CSRC. -key(20.0, "P2.1", 0x10) -- enter CSRC. -key(21.0, "P2.3", 0x10) -- TREE. -key(23.0, "P2.1", 0x40) -- F5. -key(24.5, "P1.6", 0x40) -- Esc during BIG.BIN. -key(31.0, "P2.3", 0x80) -- F10. -key(33.0, "P2.1", 0x10) - -table.sort(events, function(left, right) return left[1] < right[1] end) - -local snap_times = {22.0, 27.0, 36.0} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local value = manager.machine.time - return value.seconds + value.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() start_time = now() end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p15-tree-cancel] snapshot t=" .. elapsed) - end - end - if elapsed >= 38.0 then - print("[p15-tree-cancel] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p15_tree_copy.lua b/applications/Volkov/tests/mame_p15_tree_copy.lua deleted file mode 100644 index 74d6b55..0000000 --- a/applications/Volkov/tests/mame_p15_tree_copy.lua +++ /dev/null @@ -1,119 +0,0 @@ --- 0.2/P15: рекурсивный F5 текущего каталога, depth=16 и EEXIST repeat. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(10.0, "SPRCMD.EXE\n") - -key(16.0, "P1.6", 0x20) -- right panel. -key(17.0, "P2.1", 0x10) -- enter DST. -key(18.0, "P1.6", 0x20) -- left panel. -key(19.0, "P2.3", 0x10) -- DST -> SRC. -key(20.0, "P2.1", 0x10) -- enter SRC. -key(21.0, "P2.3", 0x10) -- `..` -> TREE. - -key(23.0, "P2.1", 0x40) -- F5 recursive success. -key(34.0, "P2.1", 0x40) -- repeat: target TREE exists. -key(39.0, "P1.6", 0x20) -- show refreshed passive panel. -key(44.0, "P2.3", 0x80) -- F10. -key(46.0, "P2.1", 0x10) - -table.sort(events, function(left, right) return left[1] < right[1] end) - -local snap_times = { - 22.0, -- source TREE and empty destination; - 31.0, -- recursive success; - 37.0, -- repeat stopped on EEXIST; - 41.0, -- passive panel contains TREE; - 49.0 -- clean DSS prompt, including dynamic EMM cleanup. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local value = manager.machine.time - return value.seconds + value.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p15-tree-copy] snapshot t=" .. elapsed) - end - end - - if elapsed >= 51.0 then - print("[p15-tree-copy] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p15_tree_self.lua b/applications/Volkov/tests/mame_p15_tree_self.lua deleted file mode 100644 index a178c3f..0000000 --- a/applications/Volkov/tests/mame_p15_tree_self.lua +++ /dev/null @@ -1,109 +0,0 @@ --- 0.2/P15 hardening: passive target лежит внутри копируемого source root. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(10.0, "SPRCMD.EXE\n") - -key(16.0, "P1.6", 0x20) -- right. -key(17.0, "P2.1", 0x10) -- enter SRC. -key(18.0, "P2.3", 0x10) -- TREE. -key(19.0, "P2.1", 0x10) -- enter TREE. -key(20.0, "P2.3", 0x10) -- INNER. -key(21.0, "P2.1", 0x10) -- enter INNER: passive target. -key(22.0, "P1.6", 0x20) -- left root. -key(23.0, "P2.1", 0x10) -- enter SRC. -key(24.0, "P2.3", 0x10) -- TREE: source root. -key(26.0, "P2.1", 0x40) -- F5 must refuse before mkdir. -key(32.0, "P2.3", 0x80) -- F10. -key(34.0, "P2.1", 0x10) - -table.sort(events, function(left, right) return left[1] < right[1] end) - -local snap_times = {25.0, 29.0, 37.0} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local value = manager.machine.time - return value.seconds + value.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() start_time = now() end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p15-tree-self] snapshot t=" .. elapsed) - end - end - if elapsed >= 39.0 then - print("[p15-tree-self] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p16_group_tree_copy.lua b/applications/Volkov/tests/mame_p16_group_tree_copy.lua deleted file mode 100644 index 49a320a..0000000 --- a/applications/Volkov/tests/mame_p16_group_tree_copy.lua +++ /dev/null @@ -1,109 +0,0 @@ --- 0.2/P16: выбранный файл и два выбранных каталога копируются одним F5. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(10.0, "SPRCMD.EXE\n") -key(16.0, "P1.6", 0x20) -- right. -key(17.0, "P2.1", 0x10) -- enter GDST. -key(18.0, "P1.6", 0x20) -- left. -key(19.0, "P2.3", 0x10) -- GDST -> GSRC. -key(20.0, "P2.1", 0x10) -- enter GSRC. -key(21.0, "P2.3", 0x10) -- ADIR. -key(22.0, "P2.6", 0x01) -- select ADIR -> BDIR. -key(23.0, "P2.6", 0x01) -- select BDIR -> KEEP.TXT. -key(24.0, "P2.3", 0x10) -- KEEP.TXT -> ROOT.BIN. -key(25.0, "P2.6", 0x01) -- select ROOT.BIN. -key(28.0, "P2.1", 0x40) -- group F5. -key(39.0, "P1.6", 0x20) -- passive panel. -key(44.0, "P2.3", 0x80) -- F10. -key(46.0, "P2.1", 0x10) - -table.sort(events, function(left, right) return left[1] < right[1] end) - -local snap_times = {26.0, 36.0, 41.0, 49.0} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local value = manager.machine.time - return value.seconds + value.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() start_time = now() end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p16-group-tree] snapshot t=" .. elapsed) - end - end - if elapsed >= 51.0 then - print("[p16-group-tree] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p17_tree_delete.lua b/applications/Volkov/tests/mame_p17_tree_delete.lua deleted file mode 100644 index 68cdea0..0000000 --- a/applications/Volkov/tests/mame_p17_tree_delete.lua +++ /dev/null @@ -1,121 +0,0 @@ --- 0.2/P17: group F8 удаляет выбранные файлы и каталоги рекурсивно. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(10.0, "SPRCMD.EXE\n") - -key(17.0, "P2.1", 0x10) -- enter RDEL. -key(20.0, "P2.3", 0x10) -- `..` -> ADIR. -key(21.0, "P2.6", 0x01) -- select ADIR -> BDIR. -key(22.0, "P2.6", 0x01) -- select BDIR -> KEEPDIR. -key(23.0, "P2.3", 0x10) -- KEEPDIR -> KEEP.TXT. -key(24.0, "P2.3", 0x10) -- KEEP.TXT -> TOP.BIN. -key(25.0, "P2.6", 0x01) -- select TOP.BIN. - -key(27.0, "P2.2", 0x80) -- first group F8: cancel confirmation. -key(29.0, "P2.0", 0x08) -- N. -key(32.0, "P2.2", 0x80) -- second group F8. -key(34.0, "P2.1", 0x10) -- confirm. - -key(42.0, "P2.3", 0x80) -- F10. -key(44.0, "P2.1", 0x10) -- confirm exit. - -table.sort(events, function(left, right) return left[1] < right[1] end) - -local snap_times = { - 19.0, -- исходное дерево; - 25.5, -- два каталога и файл выбраны; - 27.5, -- подтверждение группы; - 30.0, -- cancel до mutation сохранил всё; - 39.0, -- выбранные деревья удалены; - 46.0 -- clean DSS prompt. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local value = manager.machine.time - return value.seconds + value.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() start_time = now() end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p17-tree-delete] snapshot t=" .. elapsed) - end - end - - if elapsed >= 48.0 then - print("[p17-tree-delete] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p17_tree_delete_cancel.lua b/applications/Volkov/tests/mame_p17_tree_delete_cancel.lua deleted file mode 100644 index e2fc3dc..0000000 --- a/applications/Volkov/tests/mame_p17_tree_delete_cancel.lua +++ /dev/null @@ -1,110 +0,0 @@ --- 0.2/P17: Esc во время рекурсивного group F8. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(10.0, "SPRCMD.EXE\n") - -key(17.0, "P2.1", 0x10) -- enter CDEL. -key(20.0, "P2.3", 0x10) -- `..` -> TREE. -key(21.0, "P2.6", 0x01) -- select TREE -> KEEP.TXT. -key(22.0, "P2.3", 0x10) -- KEEP.TXT -> TOP.BIN. -key(23.0, "P2.6", 0x01) -- select TOP.BIN. -key(25.0, "P2.2", 0x80) -- group F8. -key(27.0, "P2.1", 0x10) -- confirm; TOP.BIN удаляется первым. -key(27.5, "P1.6", 0x40) -- Esc во время enqueue большого дерева. - -key(35.0, "P2.3", 0x80) -- F10 после cleanup job. -key(37.0, "P2.1", 0x10) - -table.sort(events, function(left, right) return left[1] < right[1] end) - -local snap_times = {24.0, 25.5, 31.0, 39.0} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local value = manager.machine.time - return value.seconds + value.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() start_time = now() end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p17-tree-delete-cancel] snapshot t=" .. elapsed) - end - end - - if elapsed >= 41.0 then - print("[p17-tree-delete-cancel] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p18_metadata.lua b/applications/Volkov/tests/mame_p18_metadata.lua deleted file mode 100644 index 50b405f..0000000 --- a/applications/Volkov/tests/mame_p18_metadata.lua +++ /dev/null @@ -1,109 +0,0 @@ --- 0.2/P18: F5 сохраняет точные FAT date/time и R/H/S/A. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, x={"P1.2",0x10}, ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(10.0, "SPRCMD.EXE\n") - -key(16.0, "P1.6", 0x20) -- right panel. -key(17.0, "P2.1", 0x10) -- enter MDST. -key(18.0, "P1.6", 0x20) -- left panel. -key(19.0, "P2.3", 0x10) -- MDST -> MSRC. -key(20.0, "P2.1", 0x10) -- enter MSRC. -key(22.0, "P2.3", 0x10) -- `..` -> META.BIN. -key(25.0, "P2.1", 0x40) -- F5. -key(31.0, "P1.6", 0x20) -- inspect refreshed MDST. -key(36.0, "P2.3", 0x80) -- F10. -key(38.0, "P2.1", 0x10) - -table.sort(events, function(left, right) return left[1] < right[1] end) - -local snap_times = {23.0, 29.0, 33.0, 40.0} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local value = manager.machine.time - return value.seconds + value.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() start_time = now() end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p18-metadata] snapshot t=" .. elapsed) - end - end - - if elapsed >= 42.0 then - print("[p18-metadata] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p19_overwrite.lua b/applications/Volkov/tests/mame_p19_overwrite.lua deleted file mode 100644 index c7cd432..0000000 --- a/applications/Volkov/tests/mame_p19_overwrite.lua +++ /dev/null @@ -1,88 +0,0 @@ --- P19: запускает target-пробу overwrite и фиксирует итоговую таблицу. --- Запас на I/O MAME 0.287: ранний выход обрывал пробу до ALL PASS. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - d={"P1.4",0x04}, e={"P1.4",0x20}, l={"P2.2",0x08}, - r={"P1.5",0x01}, t={"P1.5",0x20}, x={"P1.2",0x10}, - ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then chord(t, "P1.7", 0x02, def[1], def[2]) - else key(t, def[1], def[2]) end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(10.0, "DELRT.EXE\n") -key(90.0, "P2.1", 0x10) - -table.sort(events, function(a, b) return a[1] < b[1] end) -local snap_times = {80.0, 95.0} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local value = manager.machine.time - return value.seconds + value.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() start_time = now() end) -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - end - end - if elapsed >= 100.0 then manager.machine:exit() end -end) diff --git a/applications/Volkov/tests/mame_p19_policy.lua b/applications/Volkov/tests/mame_p19_policy.lua deleted file mode 100644 index 1e683e4..0000000 --- a/applications/Volkov/tests/mame_p19_policy.lua +++ /dev/null @@ -1,139 +0,0 @@ --- P19 UI: overwrite-all, readonly re-prompt, skip-all, rename и cancel. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - a={"P1.6",0x04}, b={"P1.5",0x10}, c={"P1.4",0x08}, - d={"P1.4",0x04}, e={"P1.4",0x20}, i={"P1.4",0x01}, - m={"P2.0",0x10}, n={"P2.0",0x08}, o={"P2.2",0x02}, - p={"P2.3",0x20}, r={"P1.5",0x01}, s={"P1.2",0x08}, - t={"P1.5",0x20}, x={"P1.2",0x10}, ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then chord(t, "P1.7", 0x02, def[1], def[2]) - else key(t, def[1], def[2]) end - t = t + 0.14 - end - return t -end - -type_text(8.0, "d:\n") -type_text(10.0, "SPRCMD.EXE\n") -key(16.0, "P1.6", 0x20) -- right root. -key(17.0, "P2.1", 0x10) -- enter P19DST. -key(18.0, "P1.6", 0x20) -- left root. -key(19.0, "P2.3", 0x10) -- P19DST -> P19SRC. -key(20.0, "P2.1", 0x10) -- enter P19SRC. - -key(22.0, "P2.3", 0x10) -- AONE. -key(23.0, "P2.6", 0x01) -- select AONE -> ATWO. -key(24.0, "P2.6", 0x01) -- select ATWO -> CANCEL. -key(25.0, "P2.3", 0x10) -- REN. -key(26.0, "P2.3", 0x10) -- RONLY. -key(27.0, "P2.6", 0x01) -- select RONLY -> SONE. -key(30.0, "P2.1", 0x40) -- group F5. -key(33.0, "P1.6", 0x04) -- A = overwrite all. -key(40.0, "P2.2", 0x02) -- O = explicit readonly overwrite. - -chord(47.0, "P1.1", 0x02, "P1.5", 0x01) -- refresh/reset selection. -key(49.0, "P2.6", 0x01) -- select SONE -> STWO. -key(50.0, "P2.6", 0x01) -- select STWO. -key(52.0, "P2.1", 0x40) -- group F5. -key(56.0, "P2.0", 0x08) -- N = skip all. - -chord(60.0, "P1.1", 0x02, "P1.5", 0x01) -key(62.0, "P2.5", 0x01) -- Home -> `..`. -key(63.0, "P2.3", 0x10) -- AONE. -key(64.0, "P2.3", 0x10) -- ATWO. -key(65.0, "P2.3", 0x10) -- CANCEL. -key(66.0, "P2.3", 0x10) -- REN. -key(68.0, "P2.1", 0x40) -- single F5. -key(72.0, "P1.5", 0x01) -- R = rename. -type_text(74.0, "NEXT.BIN\n") - -key(80.0, "P2.5", 0x01) -- Home -> `..`. -key(80.5, "P2.3", 0x10) -- AONE. -key(81.0, "P2.3", 0x10) -- ATWO. -key(81.5, "P2.3", 0x10) -- CANCEL. -key(83.0, "P2.1", 0x40) -key(87.0, "P1.6", 0x40) -- Esc conflict. -key(90.0, "P1.6", 0x20) -- inspect target. -key(95.0, "P2.3", 0x80) -- F10. -key(97.0, "P2.1", 0x10) - --- Запас на I/O MAME 0.287: ответ readonly должен идти после двух копий. -for _, event in ipairs(events) do - if event[1] >= 40.0 then event[1] = event[1] + 20.0 end -end - -table.sort(events, function(left, right) return left[1] < right[1] end) -local snap_times = {31.5, 38.0, 44.0, 54.0, 58.0, 70.0, 76.0, 85.0, 89.0, 92.0, 99.0} -for i, t in ipairs(snap_times) do - if t >= 40.0 then snap_times[i] = t + 20.0 end -end -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local value = manager.machine.time - return value.seconds + value.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() start_time = now() end) -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p19-policy] snapshot t=" .. elapsed) - end - end - if elapsed >= 121.0 then manager.machine:exit() end -end) diff --git a/applications/Volkov/tests/mame_p2_skeleton.lua b/applications/Volkov/tests/mame_p2_skeleton.lua deleted file mode 100644 index bb4d97e..0000000 --- a/applications/Volkov/tests/mame_p2_skeleton.lua +++ /dev/null @@ -1,130 +0,0 @@ --- Автоматический MAME-сценарий для двухпанельного skeleton P2. --- Использует реальные поля AT-клавиатуры, включая PgDn и F1/F10, которых --- нет в текстовом алфавите mame_interactive.py. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local chars = { - a={"P1.6",0x04}, c={"P1.4",0x08}, d={"P1.4",0x04}, - e={"P1.4",0x20}, m={"P2.0",0x10}, n={"P2.0",0x08}, - p={"P2.3",0x20}, r={"P1.5",0x01}, s={"P1.2",0x08}, - x={"P1.2",0x10}, y={"P2.0",0x01}, - [" "]={"P2.4",0x80}, ["."]={"P2.2",0x10}, - ["\\"]={"P2.1",0x04}, ["\n"]={"P2.1",0x10}, - [":"]={"P2.3",0x02,true} -} - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] - local shifted = false - if not def then - local lower = ch:lower() - def = chars[lower] - shifted = ch ~= lower - else - shifted = def[3] or false - end - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "a:\\SPRCMD .EXE\n") - -key(17.0, "P1.6", 0x20) -- Tab: активировать правую панель. -key(21.0, "P1.2", 0x20) -- PgDn: cursor 27, top 1, системный scroll. -key(22.0, "P2.3", 0x10) -- Down: cursor 28, top 2, ещё один scroll. -key(25.0, "P2.3", 0x80) -- F10: диалог выхода. -key(29.0, "P2.0", 0x08) -- N: отменить выход и восстановить экран. -key(33.0, "P1.2", 0x40) -- F1: единый диалог заглушки. -key(37.0, "P1.6", 0x40) -- Esc: закрыть сообщение. -key(40.0, "P2.3", 0x80) -- F10 повторно. -key(44.0, "P2.1", 0x10) -- Enter: подтвердить штатный выход. - --- Немедленный второй запуск ловит утечку EMM между процессами. Сам EXE --- дополнительно сравнивает free pages до alloc и после cleanup. -type_text(50.0, "a:\\SPRCMD .EXE\n") -key(59.0, "P2.3", 0x80) -- F10 во втором процессе. -key(63.0, "P2.1", 0x10) -- Подтвердить второй cleanup. - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local snap_times = { - 15.0, 19.0, 23.0, 27.0, 31.0, 35.0, 39.0, 42.0, 47.0, - 57.0, 61.0, 66.0 -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p2-ui] snapshot t=" .. elapsed) - end - end - - if elapsed >= 68.0 then - print("[p2-ui] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p3_dir_probe.lua b/applications/Volkov/tests/mame_p3_dir_probe.lua deleted file mode 100644 index f6c5ab2..0000000 --- a/applications/Volkov/tests/mame_p3_dir_probe.lua +++ /dev/null @@ -1,104 +0,0 @@ --- Изолированная MAME-проба предельного каталога DSS. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - a={"P1.6",0x04}, d={"P1.4",0x04}, e={"P1.4",0x20}, - i={"P1.4",0x01}, p={"P2.3",0x20}, r={"P1.5",0x01}, - x={"P1.2",0x10}, ["3"]={"P1.4",0x40}, - [" "]={"P2.4",0x80}, ["."]={"P2.2",0x10}, - ["\\"]={"P2.1",0x04}, ["\n"]={"P2.1",0x10}, - [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] - local shifted = false - if not def then - local lower = ch:lower() - def = chars[lower] - shifted = ch ~= lower - else - shifted = def[3] or false - end - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "a:\\P3DIR .EXE\n") - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local start_time = nil -local ports = nil -local index = 1 -local snapped = false -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - if not snapped and elapsed >= 18.0 then - snapped = true - manager.machine.video:snapshot() - print("[p3-dir-probe] snapshot t=" .. elapsed) - end - if elapsed >= 20.0 then - print("[p3-dir-probe] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p3_enum_matrix.lua b/applications/Volkov/tests/mame_p3_enum_matrix.lua deleted file mode 100644 index a0e84f4..0000000 --- a/applications/Volkov/tests/mame_p3_enum_matrix.lua +++ /dev/null @@ -1,98 +0,0 @@ --- Запуск P3MAP.EXE и снимок матрицы влияния W3 на F_NEXT. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - a={"P1.6",0x04}, e={"P1.4",0x20}, m={"P2.0",0x10}, - p={"P2.3",0x20}, x={"P1.2",0x10}, ["3"]={"P1.4",0x40}, - [" "]={"P2.4",0x80}, ["."]={"P2.2",0x10}, - ["\\"]={"P2.1",0x04}, ["\n"]={"P2.1",0x10}, - [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] - local shifted = false - if not def then - local lower = ch:lower() - def = chars[lower] - shifted = ch ~= lower - else - shifted = def[3] or false - end - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "a:\\P3MAP .EXE\n") -table.sort(events, function(a, b) return a[1] < b[1] end) - -local start_time = nil -local ports = nil -local index = 1 -local snapped = false -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() start_time = now() end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - if not snapped and elapsed >= 30.0 then - snapped = true - manager.machine.video:snapshot() - print("[p3-enum-matrix] snapshot t=" .. elapsed) - end - if elapsed >= 32.0 then - print("[p3-enum-matrix] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p3_panels.lua b/applications/Volkov/tests/mame_p3_panels.lua deleted file mode 100644 index 566c93b..0000000 --- a/applications/Volkov/tests/mame_p3_panels.lua +++ /dev/null @@ -1,137 +0,0 @@ --- Автоматический MAME-сценарий P3: настоящие каталоги и две панели. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - a={"P1.6",0x04}, c={"P1.4",0x08}, d={"P1.4",0x04}, - e={"P1.4",0x20}, m={"P2.0",0x10}, p={"P2.3",0x20}, - r={"P1.5",0x01}, s={"P1.2",0x08}, x={"P1.2",0x10}, - [" "]={"P2.4",0x80}, ["."]={"P2.2",0x10}, - ["\\"]={"P2.1",0x04}, ["\n"]={"P2.1",0x10}, - [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] - local shifted = false - if not def then - local lower = ch:lower() - def = chars[lower] - shifted = ch ~= lower - else - shifted = def[3] or false - end - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "a:\\SPRCMD .EXE\n") - -key(17.0, "P2.1", 0x10) -- Enter: левая -> TESTDIR. -key(22.0, "P2.1", 0x10) -- Enter на `..`: обратно в корень. -key(27.0, "P2.1", 0x10) -- Снова открыть TESTDIR. -key(29.0, "P2.3", 0x10) -- Выбрать INNER.TXT. -chord(31.0, "P1.1", 0x02, "P1.5", 0x01) -- Ctrl+R: сохранить выбор имени. -key(36.0, "P1.6", 0x20) -- Tab: правая панель. -key(38.0, "P2.3", 0x10) -- Выбрать P3ROOT.TXT справа. -key(43.0, "P1.6", 0x20) -- Tab: вернуться в левую. -chord(45.0, "P1.1", 0x02, "P1.2", 0x01) -- Ctrl+PgUp: левая -> корень. -key(50.0, "P2.3", 0x80) -- F10. -key(55.0, "P2.1", 0x10) -- Enter: cleanup. - --- Второй процесс проверяет освобождение EMM и повторную инициализацию темы. -type_text(61.0, "a:\\SPRCMD .EXE\n") -key(70.0, "P2.3", 0x80) -key(74.0, "P2.1", 0x10) - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local snap_times = { - 15.0, -- root/root: TESTDIR должен быть первым; - 20.0, -- left TESTDIR: `..`, INNER.TXT; right root; - 25.0, -- Enter на `..` вернул left в root; - 34.0, -- refresh сохранил INNER.TXT и cursor=1; - 41.0, -- right root cursor=1, left TESTDIR cursor=1; - 48.0, -- Ctrl+PgUp изменил только left; - 53.0, -- quit dialog; - 58.0, -- DSS после первого cleanup; - 68.0, -- второй root/root; - 72.0, -- второй quit dialog; - 77.0 -- DSS после второго cleanup. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p3-panels] snapshot t=" .. elapsed) - end - end - - if elapsed >= 79.0 then - print("[p3-panels] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p3_scan_probe.lua b/applications/Volkov/tests/mame_p3_scan_probe.lua deleted file mode 100644 index f28015b..0000000 --- a/applications/Volkov/tests/mame_p3_scan_probe.lua +++ /dev/null @@ -1,107 +0,0 @@ --- Полная MAME-проба scan/store/sort большого каталога P3. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - a={"P1.6",0x04}, c={"P1.4",0x08}, d={"P1.4",0x04}, - e={"P1.4",0x20}, n={"P2.0",0x08}, p={"P2.3",0x20}, - s={"P1.2",0x08}, x={"P1.2",0x10}, - ["3"]={"P1.4",0x40}, [" "]={"P2.4",0x80}, - ["."]={"P2.2",0x10}, ["\\"]={"P2.1",0x04}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] - local shifted = false - if not def then - local lower = ch:lower() - def = chars[lower] - shifted = ch ~= lower - else - shifted = def[3] or false - end - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "a:\\P3SCAN .EXE\n") - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local snap_times = {20.0, 45.0, 75.0} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p3-scan-probe] snapshot t=" .. elapsed) - end - end - if elapsed >= 78.0 then - print("[p3-scan-probe] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p3_stress.lua b/applications/Volkov/tests/mame_p3_stress.lua deleted file mode 100644 index 6997333..0000000 --- a/applications/Volkov/tests/mame_p3_stress.lua +++ /dev/null @@ -1,137 +0,0 @@ --- P3 stress: пустой каталог, 40 строк и явный маркер предела DSS. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, - e={"P1.4",0x20}, m={"P2.0",0x10}, p={"P2.3",0x20}, - r={"P1.5",0x01}, s={"P1.2",0x08}, x={"P1.2",0x10}, - [" "]={"P2.4",0x80}, ["."]={"P2.2",0x10}, - ["\\"]={"P2.1",0x04}, ["\n"]={"P2.1",0x10}, - [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] - local shifted = false - if not def then - local lower = ch:lower() - def = chars[lower] - shifted = ch ~= lower - else - shifted = def[3] or false - end - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(9.0, "SPRCMD .EXE\n") - --- Root order: BIGDIR, EMPTY, MANY, TESTDIR, P3ROOT, SPRCMD, ZETA. -key(17.0, "P2.3", 0x10) -- EMPTY. -key(18.0, "P2.1", 0x10) -- Enter EMPTY. -key(24.0, "P2.1", 0x10) -- Enter `..`. -key(27.0, "P2.3", 0x10) -- EMPTY. -key(28.0, "P2.3", 0x10) -- MANY. -key(29.0, "P2.1", 0x10) -- Enter MANY. -key(36.0, "P1.2", 0x20) -- PgDn -> cursor 27/top 1. -key(38.0, "P2.3", 0x10) -- Down -> cursor 28/top 2. -chord(43.0, "P1.1", 0x02, "P1.2", 0x01) -- Ctrl+PgUp -> root. -key(46.0, "P2.1", 0x10) -- Enter BIGDIR (index 0). - --- BIGDIR scan может быть долгим; последующие события намеренно отнесены. -key(76.0, "P2.5", 0x20) -- End: красный `>>> MORE...`. -key(81.0, "P2.1", 0x10) -- Enter: пояснение, без перехода. -key(86.0, "P2.3", 0x80) -- F10. -key(90.0, "P2.1", 0x10) -- cleanup. - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local snap_times = { - 15.0, -- root count=7, dirs before files; - 21.0, -- EMPTY: only synthetic `..`, count=1; - 32.0, -- MANY: count=41; - 40.0, -- real list cursor=28/top=2; - 73.0, -- BIGDIR: частичный список и признак усечения DSS; - 79.0, -- End: выбран красный виртуальный `>>> MORE...`; - 84.0, -- Enter: статус о недоступном остатке, каталог не меняется; - 88.0, -- quit dialog over intact screen; - 93.0 -- DSS after cleanup. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p3-stress] snapshot t=" .. elapsed) - end - end - - if elapsed >= 95.0 then - print("[p3-stress] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p4_copy_hdd.lua b/applications/Volkov/tests/mame_p4_copy_hdd.lua deleted file mode 100644 index 30c8773..0000000 --- a/applications/Volkov/tests/mame_p4_copy_hdd.lua +++ /dev/null @@ -1,142 +0,0 @@ --- P4/F5 на HDD D: успех >64 КБ, отказ overwrite и отмена с cleanup. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, m={"P2.0",0x10}, - p={"P2.3",0x20}, r={"P1.5",0x01}, s={"P1.2",0x08}, - x={"P1.2",0x10}, [" "]={"P2.4",0x80}, ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] - local shifted = false - if not def then - local lower = ch:lower() - def = chars[lower] - shifted = ch ~= lower - else - shifted = def[3] or false - end - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(9.0, "SPRCMD .EXE\n") - -key(15.0, "P2.1", 0x10) -- Enter: левая -> COPYFROM. -key(17.0, "P1.6", 0x20) -- Tab: правая панель. -key(18.0, "P2.3", 0x10) -- Down: COPYTO. -key(19.0, "P2.1", 0x10) -- Enter: правая -> COPYTO. -key(21.0, "P1.6", 0x20) -- Tab: левая панель. - --- `..`, CANCEL, EMPTY, EXIST, F65535, F65536, K4095, K4096, LARGE. -for i = 1, 8 do key(22.0 + (i - 1) * 0.20, "P2.3", 0x10) end -key(25.0, "P2.1", 0x40) -- F5: LARGE.BIN (131109 байт). - -key(31.0, "P2.5", 0x01) -- Home. -for i = 1, 3 do key(32.0 + (i - 1) * 0.20, "P2.3", 0x10) end -key(33.5, "P2.1", 0x40) -- F5: EXIST.TXT, overwrite запрещён. - -key(36.0, "P2.5", 0x01) -- Home. -key(37.0, "P2.3", 0x10) -- CANCEL.BIN. -key(38.0, "P2.1", 0x40) -- F5: начать 4-МБ копирование. -key(41.0, "P1.6", 0x40) -- Esc: отменить между блоками. - -key(44.0, "P2.3", 0x10) -- Down: EMPTY.ZRO. -key(44.5, "P2.1", 0x40) -- F5: нулевой файл. - -key(48.0, "P2.3", 0x80) -- F10. -key(50.0, "P2.1", 0x10) -- Enter: cleanup и возврат в DSS. - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local snap_times = { - 14.0, -- обе панели в корне D:; - 20.5, -- COPYFROM/COPYTO разведены по панелям; - 25.8, 28.5, 30.0, -- начало, поздний прогресс и результат LARGE.BIN; - 35.0, -- отказ перезаписи EXIST.TXT; - 39.5, -- активный прогресс CANCEL.BIN; - 43.0, -- отмена и cleanup temp; - 46.0, -- нулевой EMPTY.ZRO скопирован; - 49.0, -- диалог выхода; - 52.0 -- возврат в D:\>. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p4-copy-hdd] snapshot t=" .. elapsed) - end - end - - if elapsed >= 54.0 then - print("[p4-copy-hdd] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p4_exec_hdd.lua b/applications/Volkov/tests/mame_p4_exec_hdd.lua deleted file mode 100644 index 7d98d2e..0000000 --- a/applications/Volkov/tests/mame_p4_exec_hdd.lua +++ /dev/null @@ -1,141 +0,0 @@ --- P4.2 на HDD D: child меняет mode/palette/CWD и обе панели. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, x={"P1.2",0x10}, [" "]={"P2.4",0x80}, - ["."]={"P2.2",0x10}, ["\n"]={"P2.1",0x10}, - [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] - local shifted = false - if not def then - local lower = ch:lower() - def = chars[lower] - shifted = ch ~= lower - else - shifted = def[3] or false - end - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(9.0, "SPRCMD .EXE\n") - -key(15.0, "P2.1", 0x10) -- левая -> EXECLEFT. -key(17.0, "P1.6", 0x20) -- Tab. -key(18.0, "P2.3", 0x10) -- EXECRGHT. -key(19.0, "P2.1", 0x10) -- правая -> EXECRGHT. -key(21.0, "P1.6", 0x20) -- Tab влево. - --- `..`, BROKEN.EXE, NOTE.TXT, P4CHILD.EXE. -for i = 1, 3 do key(22.0 + (i - 1) * 0.20, "P2.3", 0x10) end -key(23.5, "P2.1", 0x10) -- Enter: P4CHILD.EXE. -key(28.0, "P2.1", 0x10) -- Enter внутри child: return 5A. - --- После refresh: `..`, BROKEN, CREATED, NOTE, P4CHILD. -key(34.0, "P2.5", 0x01) -- Home. -key(35.0, "P2.3", 0x10) -- BROKEN.EXE. -key(36.0, "P2.1", 0x10) -- ошибка загрузчика EXE. - -key(40.0, "P2.5", 0x01) -- Home. -for i = 1, 3 do key(41.0 + (i - 1) * 0.20, "P2.3", 0x10) end -key(42.5, "P2.1", 0x10) -- NOTE.TXT: тип не поддерживается. - -key(47.0, "P2.3", 0x80) -- F10. -key(49.0, "P2.1", 0x10) -- cleanup. - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local snap_times = { - 14.0, -- root D:; - 20.5, -- панели EXECLEFT/EXECRIGHT; - 26.0, -- экран дочерней программы 40x32; - 32.0, -- Commander восстановлен, обе панели refreshed; - 38.0, -- ошибка BROKEN.EXE без завершения Commander; - 45.0, -- NOTE.TXT не запускается; - 48.0, -- quit dialog поверх восстановленного экрана; - 51.0 -- чистый возврат в D:\>. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p4-exec-hdd] snapshot t=" .. elapsed) - end - end - - if elapsed >= 53.0 then - print("[p4-exec-hdd] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p5_copy_faults.lua b/applications/Volkov/tests/mame_p5_copy_faults.lua deleted file mode 100644 index 3385d18..0000000 --- a/applications/Volkov/tests/mame_p5_copy_faults.lua +++ /dev/null @@ -1,108 +0,0 @@ --- Запас на завершение target-пробы в MAME 0.287. --- P5: отдельная детерминированная матрица состояний copy-job на HDD D:. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - p={"P2.3",0x20}, r={"P1.5",0x01}, x={"P1.2",0x10}, - ["."]={"P2.2",0x10}, ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] - local shifted = false - if not def then - local lower = ch:lower() - def = chars[lower] - shifted = ch ~= lower - else - shifted = def[3] or false - end - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(9.0, "CPERR.EXE\n") -key(90.0, "P2.1", 0x10) -- Enter после итоговой таблицы. - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local snap_times = {80.0, 95.0} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p5-copy-faults] snapshot t=" .. elapsed) - end - end - - if elapsed >= 100.0 then - print("[p5-copy-faults] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p5_enospc.lua b/applications/Volkov/tests/mame_p5_enospc.lua deleted file mode 100644 index 905116a..0000000 --- a/applications/Volkov/tests/mame_p5_enospc.lua +++ /dev/null @@ -1,125 +0,0 @@ --- P5 на HDD D: реальный disk-full при F5 и обязательный cleanup temp. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, x={"P1.2",0x10}, [" "]={"P2.4",0x80}, - ["."]={"P2.2",0x10}, ["\n"]={"P2.1",0x10}, - [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] - local shifted = false - if not def then - local lower = ch:lower() - def = chars[lower] - shifted = ch ~= lower - else - shifted = def[3] or false - end - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(9.0, "SPRCMD .EXE\n") - -key(15.0, "P2.1", 0x10) -- левая -> SOURCE. -key(17.0, "P1.6", 0x20) -- Tab. -key(18.0, "P2.3", 0x10) -- TARGET. -key(19.0, "P2.1", 0x10) -- правая -> TARGET. -key(21.0, "P1.6", 0x20) -- Tab влево. -key(22.0, "P2.3", 0x10) -- FULL.BIN после `..`. -key(23.0, "P2.1", 0x40) -- F5, ожидается ENOSPC. - -key(35.0, "P2.3", 0x80) -- F10 после управляемой ошибки. -key(37.0, "P2.1", 0x10) - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local snap_times = { - 20.5, -- SOURCE/TARGET на почти полном диске; - 25.0, -- промежуточный progress либо уже ошибка; - 32.0, -- управляемый ENOSPC, TARGET пуст; - 36.0, -- диалог выхода после ошибки; - 40.0 -- чистый prompt DSS. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p5-enospc] snapshot t=" .. elapsed) - end - end - - if elapsed >= 42.0 then - print("[p5-enospc] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p5_media_change.lua b/applications/Volkov/tests/mame_p5_media_change.lua deleted file mode 100644 index 60d190d..0000000 --- a/applications/Volkov/tests/mame_p5_media_change.lua +++ /dev/null @@ -1,133 +0,0 @@ --- P5: извлечение и возврат тестового HDD D: во время работы Commander. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local HDD_TAG = ":ata2:1:hdd:image" -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, x={"P1.2",0x10}, [" "]={"P2.4",0x80}, - ["."]={"P2.2",0x10}, ["\n"]={"P2.1",0x10}, - [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] - local shifted = false - if not def then - local lower = ch:lower() - def = chars[lower] - shifted = ch ~= lower - else - shifted = def[3] or false - end - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(9.0, "SPRCMD .EXE\n") - -key(15.0, "P2.1", 0x10) -- левая панель -> MEDIA. -key(17.0, "P2.3", 0x10) -- DEEP. -key(17.3, "P2.3", 0x10) -- DATA.BIN. -key(19.0, "P2.1", 0x40) -- F5 при отсутствующем HDD. -key(26.0, "P2.1", 0x40) -- тот же F5 после возврата HDD. -key(34.0, "P2.3", 0x80) -- F10. -key(36.0, "P2.1", 0x10) -- подтвердить выход. - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local snap_times = {14.0, 17.7, 21.0, 24.5, 31.0, 35.0, 38.0} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} -local image_path = nil -local media_phase = 0 - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - if media_phase == 0 and elapsed >= 18.0 then - local hdd = assert(manager.machine.images[HDD_TAG], "hard2 image not found") - image_path = hdd.filename - hdd:unload() - media_phase = 1 - print("[p5-media] hard2 unloaded, exists=" .. tostring(hdd.exists)) - elseif media_phase == 1 and elapsed >= 23.0 then - local hdd = assert(manager.machine.images[HDD_TAG], "hard2 image not found") - local err = hdd:load(image_path) - media_phase = 2 - print("[p5-media] hard2 loaded, exists=" .. tostring(hdd.exists) .. - " err=" .. tostring(err)) - end - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p5-media] snapshot t=" .. elapsed) - end - end - - if elapsed >= 40.0 then - print("[p5-media] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p5_panel_edges.lua b/applications/Volkov/tests/mame_p5_panel_edges.lua deleted file mode 100644 index 951d2a8..0000000 --- a/applications/Volkov/tests/mame_p5_panel_edges.lua +++ /dev/null @@ -1,124 +0,0 @@ --- P5: ровно 27/28 записей и начало прокрутки на 28-й. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, x={"P1.2",0x10}, [" "]={"P2.4",0x80}, - ["."]={"P2.2",0x10}, ["\n"]={"P2.1",0x10}, - [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] - local shifted = false - if not def then - local lower = ch:lower() - def = chars[lower] - shifted = ch ~= lower - else - shifted = def[3] or false - end - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(9.0, "SPRCMD .EXE\n") - -key(15.0, "P2.1", 0x10) -- левая -> N27. -key(17.0, "P1.6", 0x20) -- Tab. -key(18.0, "P2.3", 0x10) -- N28. -key(19.0, "P2.1", 0x10) -- правая -> N28. -key(23.0, "P2.5", 0x20) -- End: cursor=27, top=1. -key(27.0, "P1.6", 0x20) -- Tab: N27. -key(28.0, "P2.5", 0x20) -- End: cursor=26, top=0. -key(32.0, "P2.3", 0x80) -- F10. -key(34.0, "P2.1", 0x10) - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local snap_times = { - 20.5, -- count 27/28, обе панели top=0; - 25.0, -- N28 cursor=27/top=1; - 30.0, -- N27 cursor=26/top=0; - 36.0 -- чистый prompt DSS. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p5-panel-edges] snapshot t=" .. elapsed) - end - end - - if elapsed >= 38.0 then - print("[p5-panel-edges] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p5_stability.lua b/applications/Volkov/tests/mame_p5_stability.lua deleted file mode 100644 index 71bd4f5..0000000 --- a/applications/Volkov/tests/mame_p5_stability.lua +++ /dev/null @@ -1,166 +0,0 @@ --- На медленном DSS можно растянуть расписание, сохранив всю матрицу. -local time_scale = tonumber(os.getenv("SPRCMD_TEST_TIME_SCALE") or "1") -assert(time_scale and time_scale >= 1, "SPRCMD_TEST_TIME_SCALE должен быть >= 1") -if os.getenv("SPRCMD_TEST_UNTHROTTLED") == "1" then - manager.machine.video.throttled = false -end - --- P5 на HDD D: 20 copy, 100 refresh и три полных cleanup-цикла. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local function refresh_chord(t) - add(t, "P1.1", 0x02, 1) - add(t + 0.03, "P1.5", 0x01, 1) - add(t + 0.20, "P1.5", 0x01, 0) - add(t + 0.23, "P1.1", 0x02, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, x={"P1.2",0x10}, [" "]={"P2.4",0x80}, - ["."]={"P2.2",0x10}, ["\n"]={"P2.1",0x10}, - [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] - local shifted = false - if not def then - local lower = ch:lower() - def = chars[lower] - shifted = ch ~= lower - else - shifted = def[3] or false - end - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(9.0, "SPRCMD .EXE\n") - -key(15.0, "P2.1", 0x10) -- левая -> SOURCE. -key(17.0, "P1.6", 0x20) -- Tab. -key(18.0, "P2.3", 0x10) -- TARGET. -key(19.0, "P2.1", 0x10) -- правая -> TARGET. -key(21.0, "P1.6", 0x20) -- Tab влево. - --- В SOURCE: `..`, затем F00.BIN..F19.BIN. Copy не меняет active selection. --- Интервалы растут вместе с числом 4-КБ блоков: ввод во время синхронного --- copy не буферизуется и не должен считаться выполненной командой. -local copy_times = { - 22.0, 24.0, 26.0, 28.0, 30.0, 34.0, 38.0, 40.0, 42.0, 44.0, - 46.0, 48.0, 50.0, 52.0, 54.0, 57.0, 60.0, 64.0, 68.0, 75.0 -} -for i = 0, 19 do - local t = copy_times[i + 1] - key(t, "P2.3", 0x10) -- Down. - key(t + 0.25, "P2.1", 0x40) -- F5. -end - --- Последний файл больше 1 МБ; оставляем запас до refresh. -for i = 0, 99 do - refresh_chord(105.0 + i * 0.50) -end - -key(160.0, "P2.3", 0x80) -- F10 первого запуска. -key(161.5, "P2.1", 0x10) - -type_text(167.0, "SPRCMD .EXE\n") -key(173.0, "P2.3", 0x80) -key(174.5, "P2.1", 0x10) - -type_text(180.0, "SPRCMD .EXE\n") -key(186.0, "P2.3", 0x80) -key(187.5, "P2.1", 0x10) - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local snap_times = { - 14.0, -- root D:; - 20.5, -- SOURCE/TARGET; - 80.0, -- прогресс последнего файла больше 1 МБ; - 100.0, -- 20 файлов показаны в TARGET; - 157.0, -- состояние после 100 refresh; - 164.0, -- первый cleanup, чистый prompt; - 177.0, -- второй cleanup; - 190.0 -- третий cleanup. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = (now() - start_time) / time_scale - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p5-stability] snapshot t=" .. elapsed) - end - end - - if elapsed >= 192.0 then - print("[p5-stability] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p5_viewer.lua b/applications/Volkov/tests/mame_p5_viewer.lua deleted file mode 100644 index e64019c..0000000 --- a/applications/Volkov/tests/mame_p5_viewer.lua +++ /dev/null @@ -1,127 +0,0 @@ --- P5: запуск реального mdview2, переключение вида и возврат в Commander. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, x={"P1.2",0x10}, [" "]={"P2.4",0x80}, - ["."]={"P2.2",0x10}, ["\n"]={"P2.1",0x10}, - [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] - local shifted = false - if not def then - local lower = ch:lower() - def = chars[lower] - shifted = ch ~= lower - else - shifted = def[3] or false - end - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(9.0, "SPRCMD .EXE\n") - -key(15.0, "P2.1", 0x10) -- Commander -> VIEWER. -key(17.0, "P2.3", 0x10) -- MDVIEW2.EXE после `..`. -key(18.0, "P2.1", 0x10) -- Enter/EXEC; README.MD берётся из child CWD. - -key(28.0, "P1.2", 0x20) -- PgDn внутри mdview2. -key(31.0, "P1.2", 0x80) -- F2: RAW. -key(36.0, "P2.3", 0x80) -- F10: выход из mdview2. - -key(44.0, "P2.3", 0x80) -- F10 Commander. -key(46.0, "P2.1", 0x10) - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local snap_times = { - 16.0, -- Commander в VIEWER; - 25.0, -- реальный MD-экран mdview2; - 29.5, -- mdview2 после PgDn; - 33.5, -- RAW-режим mdview2; - 40.0, -- Commander восстановлен после viewer; - 45.0, -- диалог выхода Commander; - 49.0 -- чистый prompt DSS. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p5-viewer] snapshot t=" .. elapsed) - end - end - - if elapsed >= 51.0 then - print("[p5-viewer] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p6_sort.lua b/applications/Volkov/tests/mame_p6_sort.lua deleted file mode 100644 index 5f20b80..0000000 --- a/applications/Volkov/tests/mame_p6_sort.lua +++ /dev/null @@ -1,143 +0,0 @@ --- 0.2/P6: четыре сортировки, reverse, независимость панелей и refresh. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - m={"P2.0",0x10}, o={"P2.2",0x02}, p={"P2.3",0x20}, - r={"P1.5",0x01}, s={"P1.2",0x08}, t={"P1.5",0x20}, - x={"P1.2",0x10}, [" "]={"P2.4",0x80}, ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -local function ctrl_f(t, tag, mask) - chord(t, "P1.1", 0x02, tag, mask) -end - -type_text(8.0, "d:\n") -type_text(9.0, "SORTRT.EXE\n") -type_text(12.0, "SPRCMD.EXE\n") - -key(18.0, "P2.1", 0x10) -- left -> SORTL. -key(20.0, "P1.6", 0x20) -- Tab -> right. -key(21.0, "P2.3", 0x10) -- Down -> SORTR. -key(22.0, "P2.1", 0x10) -- right -> SORTR. -key(24.0, "P1.6", 0x20) -- left active again. - -ctrl_f(27.0, "P2.6", 0x80) -- Ctrl+F4: extension ascending. -ctrl_f(31.0, "P2.6", 0x80) -- extension descending. -ctrl_f(35.0, "P2.1", 0x80) -- Ctrl+F6: size ascending. -ctrl_f(39.0, "P2.1", 0x80) -- size descending. -ctrl_f(43.0, "P2.1", 0x40) -- Ctrl+F5: date ascending. -ctrl_f(47.0, "P2.1", 0x40) -- date descending. -ctrl_f(51.0, "P2.6", 0x40) -- Ctrl+F3: name ascending. -ctrl_f(55.0, "P2.6", 0x40) -- name descending. - -ctrl_f(59.0, "P2.1", 0x80) -- left size ascending. -ctrl_f(61.0, "P2.1", 0x80) -- left size descending. -key(63.0, "P1.6", 0x20) -- right active, still N+. -ctrl_f(65.0, "P2.6", 0x80) -- right extension ascending. -chord(68.0, "P1.1", 0x02, "P1.5", 0x01) -- refresh keeps E+. - -key(72.0, "P2.3", 0x80) -- F10. -key(74.0, "P2.1", 0x10) -- Enter: cleanup. - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local snap_times = { - 25.5, -- обе панели N+; - 29.0, -- left E+; - 33.0, -- left E-; - 37.0, -- left S+; - 41.0, -- left S-; - 45.0, -- left D+; - 49.0, -- left D-; - 53.0, -- left N+; - 57.0, -- left N-; - 66.5, -- left S-, right E+; - 70.0, -- refresh сохранил right E+; - 76.0 -- чистый DSS prompt. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p6-sort] snapshot t=" .. elapsed) - end - end - - if elapsed >= 78.0 then - print("[p6-sort] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p7_select.lua b/applications/Volkov/tests/mame_p7_select.lua deleted file mode 100644 index ffe86af..0000000 --- a/applications/Volkov/tests/mame_p7_select.lua +++ /dev/null @@ -1,148 +0,0 @@ --- 0.2/P7: Insert, invert, перенос flags через sort и сброс на refresh. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - b={"P1.5",0x10}, c={"P1.4",0x08}, d={"P1.4",0x04}, - e={"P1.4",0x20}, i={"P1.4",0x01}, l={"P2.2",0x08}, - m={"P2.0",0x10}, n={"P2.0",0x08}, p={"P2.3",0x20}, - r={"P1.5",0x01}, s={"P1.2",0x08}, t={"P1.5",0x20}, - x={"P1.2",0x10}, [" "]={"P2.4",0x80}, ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(9.0, "SELRT.EXE\n") -type_text(12.0, "SPRCMD.EXE\n") - -key(18.0, "P2.1", 0x10) -- left -> SELECT. -key(21.0, "P2.6", 0x01) -- Insert на `..`: запрещён. -key(23.0, "P2.3", 0x10) -- Down -> ADIR. -key(24.0, "P2.6", 0x01) -- выбрать ADIR, cursor -> ZDIR. -key(26.0, "P2.6", 0x01) -- выбрать ZDIR, cursor -> A.BIN. -key(28.0, "P2.6", 0x01) -- выбрать A.BIN, cursor -> B.BIN. -chord(32.0, "P1.1", 0x02, "P2.6", 0x40) -- Ctrl+F3: N-. -chord(36.0, "P1.7", 0x02, "P1.4", 0x80) -- Shift+8 = `*`. -chord(40.0, "P1.1", 0x02, "P1.5", 0x01) -- Ctrl+R: выбор сброшен. - -chord(43.0, "P1.7", 0x02, "P2.1", 0x01) -- Shift+=: `+`. -key(44.0, "P2.1", 0x20) -- стереть default `*.*`. -key(44.3, "P2.1", 0x20) -key(44.6, "P2.1", 0x20) -chord(45.0, "P1.7", 0x02, "P1.4", 0x80) -- `*`. -key(45.3, "P2.2", 0x10) -- `.`. -type_text(45.6, "bin") -key(46.4, "P2.1", 0x10) -- apply `*.bin`. - -key(50.0, "P2.2", 0x20) -- `-`. -key(51.0, "P2.1", 0x20) -- стереть default `*.*`. -key(51.3, "P2.1", 0x20) -key(51.6, "P2.1", 0x20) -type_text(52.0, "b.bin") -key(53.0, "P2.1", 0x10) -- unselect B.BIN. - -chord(56.0, "P1.7", 0x02, "P2.1", 0x01) -- `+`, затем cancel. -key(57.0, "P1.6", 0x40) -- Esc. -key(60.0, "P2.3", 0x80) -- F10. -key(62.0, "P2.1", 0x10) -- Enter: cleanup. - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local snap_times = { - 20.0, -- исходный список; - 22.0, -- `..` не выбран; - 29.5, -- два каталога и A.BIN выбраны, sel=10/003; - 33.5, -- sort N- сохранил flags и текущий B.BIN; - 37.5, -- invert оставил каталоги и B.BIN/C.BIN, sel=50/004; - 41.5, -- refresh сбросил выбор, N- сохранён; - 43.5, -- mask dialog с default `*.*`; - 46.1, -- отредактировано `*.bin`; - 48.0, -- маска выбрала три BIN, sel=60/003; - 54.5, -- `- b.bin` оставил A/C, sel=40/002; - 58.0, -- cancel следующего `+` сохранил выбор; - 64.0 -- чистый DSS prompt. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p7-select] snapshot t=" .. elapsed) - end - end - - if elapsed >= 66.0 then - print("[p7-select] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/mame_p9_mkdir.lua b/applications/Volkov/tests/mame_p9_mkdir.lua deleted file mode 100644 index 3852e11..0000000 --- a/applications/Volkov/tests/mame_p9_mkdir.lua +++ /dev/null @@ -1,136 +0,0 @@ --- 0.2/P9: F7 success, duplicate, invalid 8.3, cancel и навигация. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - c={"P1.4",0x08}, d={"P1.4",0x04}, e={"P1.4",0x20}, - g={"P1.5",0x04}, i={"P1.4",0x01}, k={"P1.4",0x02}, - l={"P2.2",0x08}, m={"P2.0",0x10}, n={"P2.0",0x08}, - o={"P2.2",0x02}, p={"P2.3",0x20}, r={"P1.5",0x01}, - s={"P1.2",0x08}, t={"P1.5",0x20}, w={"P1.2",0x04}, - x={"P1.2",0x10}, ["9"]={"P2.2",0x01}, - [" "]={"P2.4",0x80}, ["."]={"P2.2",0x10}, - ["\n"]={"P2.1",0x10}, [":"]={"P2.3",0x02,true} -} - -local function type_text(t, value) - for i = 1, #value do - local ch = value:sub(i, i) - local def = chars[ch] or chars[ch:lower()] - local shifted = def[3] or ch ~= ch:lower() - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - -type_text(8.0, "d:\n") -type_text(9.0, "MKRT.EXE\n") -type_text(12.0, "SPRCMD.EXE\n") - -key(18.0, "P2.1", 0x10) -- left -> MKTEST. -key(20.0, "P2.2", 0x40) -- F7. -type_text(21.0, "newdir") -key(22.2, "P2.1", 0x10) -- create. - -key(26.0, "P2.2", 0x40) -- F7 duplicate. -type_text(27.0, "newdir") -key(28.2, "P2.1", 0x10) - -key(32.0, "P2.2", 0x40) -- F7 invalid basename > 8. -type_text(33.0, "toolong99") -key(34.5, "P2.1", 0x10) - -key(38.0, "P2.2", 0x40) -- F7 then cancel. -key(39.0, "P1.6", 0x40) -key(42.0, "P2.3", 0x10) -- Down -> NEWDIR. -key(43.0, "P2.1", 0x10) -- enter NEWDIR. -key(47.0, "P2.1", 0x20) -- Backspace -> MKTEST. -key(51.0, "P2.3", 0x80) -- F10. -key(53.0, "P2.1", 0x10) -- cleanup. - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local snap_times = { - 19.0, -- исходный MKTEST; - 20.5, -- пустой F7 dialog; - 23.5, -- NEWDIR создан; - 29.5, -- duplicate managed error; - 35.5, -- invalid 8.3 rejected; - 40.0, -- cancel не изменил каталог; - 44.5, -- вошли в NEWDIR; - 48.5, -- вернулись в MKTEST; - 55.0 -- чистый DSS prompt. -} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p9-mkdir] snapshot t=" .. elapsed) - end - end - - if elapsed >= 57.0 then - print("[p9-mkdir] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/p10_rename_fixture.py b/applications/Volkov/tests/p10_rename_fixture.py deleted file mode 100755 index c25516b..0000000 --- a/applications/Volkov/tests/p10_rename_fixture.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python3 -"""Создаёт и проверяет HDD-fixture команды F6 local rename.""" - -from __future__ import annotations - -import pathlib -import sys - - -def create(root: pathlib.Path) -> None: - panel = root / "RENTEST" - panel.mkdir(parents=True) - (panel / "OLD.TXT").write_bytes(b"old payload\r\n") - (panel / "EXIST.TXT").write_bytes(b"existing payload\r\n") - (panel / "UIOLD.TXT").write_bytes(b"ui payload\r\n") - old_dir = panel / "OLDDIR" - old_dir.mkdir() - (old_dir / "KEEP.BIN").write_bytes(b"dir payload\r\n") - - -def verify(root: pathlib.Path) -> None: - expected = [ - "PASS CWD captured before rename matrix", - "PASS file renamed without data change", - "PASS existing target blocks rename and stays intact", - "PASS directory renamed with contents intact", - "PASS failed directory change preserves CWD", - "PASS successful and rejected renames restore CWD", - "ALL PASS", - ] - failures: list[str] = [] - result = root / "RENRES.TXT" - panel = root / "RENTEST" - - if not result.is_file(): - failures.append("нет RENRES.TXT") - else: - text = result.read_bytes().decode("cp866", errors="replace") - for line in expected: - if line not in text: - failures.append(f"нет строки результата: {line}") - - files = { - "NEW.TXT": b"old payload\r\n", - "EXIST.TXT": b"existing payload\r\n", - "FINAL.BIN": b"ui payload\r\n", - "NEWDIR/KEEP.BIN": b"dir payload\r\n", - } - for relative, content in files.items(): - path = panel / relative - if not path.is_file() or path.read_bytes() != content: - failures.append(f"неверное содержимое {relative}") - for unwanted in ("OLD.TXT", "UIOLD.TXT", "OLDDIR"): - if (panel / unwanted).exists(): - failures.append(f"старое имя осталось: {unwanted}") - - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: file/dir rename, no-overwrite, CWD restore и UI F6.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p10_rename_probe.c b/applications/Volkov/tests/p10_rename_probe.c deleted file mode 100644 index 9c25636..0000000 --- a/applications/Volkov/tests/p10_rename_probe.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * p10_rename_probe.c — target-регрессия локального F6/RENAME. - * - * Проверяет файл, каталог, запрет overwrite и восстановление CWD через тот - * же низкоуровневый helper, который вызывает Commander. - */ - -#include -#include -#include -#include -#include -#include "sc_file_ops.h" - -static int result_fd = -1; -static uint8_t failures; -static char cwd_before[256]; -static char cwd_after[256]; -static char cwd_scratch[256]; - -static void log_text(const char *text) -{ - const char *end = text; - while (*end != '\0') end++; - if (result_fd >= 0) { - (void)write(result_fd, text, (uint16_t)(end - text)); - } -} - -static void check(uint8_t condition, const char *name) -{ - log_text(condition ? "PASS " : "FAIL "); - if (!condition) failures++; - log_text(name); - log_text("\r\n"); -} - -static uint8_t file_has(const char *path, const char *expected) -{ - char data[24]; - int fd = open(path, O_RDONLY); - int got; - uint16_t length = 0u; - - if (fd < 0) return 0u; - while (expected[length] != '\0') length++; - got = read(fd, data, sizeof(data)); - (void)close(fd); - return got == (int)length && memcmp(data, expected, length) == 0; -} - -static uint8_t file_absent(const char *path) -{ - int fd; - errno = 0; - fd = open(path, O_RDONLY); - if (fd >= 0) { - (void)close(fd); - return 0u; - } - return errno == ENOENT || errno == ENOPATH; -} - -int main(void) -{ - int rc; - int collision_errno; - int entered; - - result_fd = open("D:\\RENRES.TXT", O_WRONLY | O_CREAT | O_TRUNC); - check(getcwd(cwd_before, sizeof(cwd_before)) != 0, - "CWD captured before rename matrix"); - - rc = sc_rename_local("D:\\RENTEST", "OLD.TXT", "NEW.TXT", - cwd_scratch, sizeof(cwd_scratch)); - check(rc == 0 && file_absent("D:\\RENTEST\\OLD.TXT") && - file_has("D:\\RENTEST\\NEW.TXT", "old payload\r\n"), - "file renamed without data change"); - - errno = 0; - rc = sc_rename_local("D:\\RENTEST", "NEW.TXT", "EXIST.TXT", - cwd_scratch, sizeof(cwd_scratch)); - collision_errno = errno; - check(rc != 0 && collision_errno == EEXIST && - file_has("D:\\RENTEST\\NEW.TXT", "old payload\r\n") && - file_has("D:\\RENTEST\\EXIST.TXT", "existing payload\r\n"), - "existing target blocks rename and stays intact"); - - rc = sc_rename_local("D:\\RENTEST", "OLDDIR", "NEWDIR", - cwd_scratch, sizeof(cwd_scratch)); - entered = chdir("D:\\RENTEST\\NEWDIR"); - if (entered == 0) entered = chdir(cwd_before); - check(rc == 0 && entered == 0 && - file_has("D:\\RENTEST\\NEWDIR\\KEEP.BIN", "dir payload\r\n"), - "directory renamed with contents intact"); - - errno = 0; - rc = sc_rename_local("D:\\MISSING", "A.TXT", "B.TXT", - cwd_scratch, sizeof(cwd_scratch)); - check(rc != 0 && getcwd(cwd_after, sizeof(cwd_after)) != 0 && - strcmp(cwd_before, cwd_after) == 0, - "failed directory change preserves CWD"); - check(getcwd(cwd_after, sizeof(cwd_after)) != 0 && - strcmp(cwd_before, cwd_after) == 0, - "successful and rejected renames restore CWD"); - - if (failures == 0u) log_text("ALL PASS\r\n"); - if (result_fd >= 0) (void)close(result_fd); - return failures; -} diff --git a/applications/Volkov/tests/p11_delete_fixture.py b/applications/Volkov/tests/p11_delete_fixture.py deleted file mode 100755 index c09b7d1..0000000 --- a/applications/Volkov/tests/p11_delete_fixture.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python3 -"""Создаёт и проверяет HDD-fixture команды F8 delete.""" - -from __future__ import annotations - -import pathlib -import sys - - -def create(root: pathlib.Path) -> None: - panel = root / "DELTEST" - panel.mkdir(parents=True) - (panel / "TARGET.TXT").write_bytes(b"target payload\r\n") - (panel / "REMOVE.TXT").write_bytes(b"remove payload\r\n") - (panel / "KEEP.TXT").write_bytes(b"keep remains\r\n") - (panel / "TEMPDIR").mkdir() - (panel / "EMPTYDIR").mkdir() - nonempty = panel / "NONEMPTY" - nonempty.mkdir() - (nonempty / "CHILD.TXT").write_bytes(b"child remains\r\n") - - -def verify(root: pathlib.Path) -> None: - expected = [ - "PASS CWD captured before delete matrix", - "PASS unlink removes one regular file", - "PASS rmdir removes one empty directory", - "PASS nonempty directory is not removed", - "PASS unrelated file remains intact", - "PASS full-path delete operations preserve CWD", - "ALL PASS", - ] - failures: list[str] = [] - result = root / "DELRES.TXT" - panel = root / "DELTEST" - - if not result.is_file(): - failures.append("нет DELRES.TXT") - else: - text = result.read_bytes().decode("cp866", errors="replace") - for line in expected: - if line not in text: - failures.append(f"нет строки результата: {line}") - - absent = ("TARGET.TXT", "TEMPDIR", "REMOVE.TXT", "EMPTYDIR") - for name in absent: - if (panel / name).exists(): - failures.append(f"объект не удалён: {name}") - remaining = { - "KEEP.TXT": b"keep remains\r\n", - "NONEMPTY/CHILD.TXT": b"child remains\r\n", - } - for name, content in remaining.items(): - path = panel / name - if not path.is_file() or path.read_bytes() != content: - failures.append(f"объект изменён: {name}") - - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: file/empty-dir delete, cancel, guards, nonempty и CWD.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p11_delete_probe.c b/applications/Volkov/tests/p11_delete_probe.c deleted file mode 100644 index ce7d60e..0000000 --- a/applications/Volkov/tests/p11_delete_probe.c +++ /dev/null @@ -1,85 +0,0 @@ -/* p11_delete_probe.c — target-регрессия однообъектного F8 delete. */ - -#include -#include -#include -#include -#include - -static int result_fd = -1; -static uint8_t failures; -static char cwd_before[256]; -static char cwd_after[256]; - -static void log_text(const char *text) -{ - const char *end = text; - while (*end != '\0') end++; - if (result_fd >= 0) { - (void)write(result_fd, text, (uint16_t)(end - text)); - } -} - -static void check(uint8_t condition, const char *name) -{ - log_text(condition ? "PASS " : "FAIL "); - if (!condition) failures++; - log_text(name); - log_text("\r\n"); -} - -static uint8_t file_has(const char *path, const char *expected) -{ - char data[24]; - int fd = open(path, O_RDONLY); - int got; - uint16_t length = 0u; - - if (fd < 0) return 0u; - while (expected[length] != '\0') length++; - got = read(fd, data, sizeof(data)); - (void)close(fd); - return got == (int)length && memcmp(data, expected, length) == 0; -} - -static uint8_t file_absent(const char *path) -{ - int fd; - errno = 0; - fd = open(path, O_RDONLY); - if (fd >= 0) { - (void)close(fd); - return 0u; - } - return errno == ENOENT || errno == ENOPATH; -} - -int main(void) -{ - int rc; - - result_fd = open("D:\\DELRES.TXT", O_WRONLY | O_CREAT | O_TRUNC); - check(getcwd(cwd_before, sizeof(cwd_before)) != 0, - "CWD captured before delete matrix"); - check(unlink("D:\\DELTEST\\TARGET.TXT") == 0 && - file_absent("D:\\DELTEST\\TARGET.TXT"), - "unlink removes one regular file"); - check(rmdir("D:\\DELTEST\\TEMPDIR") == 0, - "rmdir removes one empty directory"); - - errno = 0; - rc = rmdir("D:\\DELTEST\\NONEMPTY"); - check(rc != 0 && - file_has("D:\\DELTEST\\NONEMPTY\\CHILD.TXT", - "child remains\r\n"), - "nonempty directory is not removed"); - check(file_has("D:\\DELTEST\\KEEP.TXT", "keep remains\r\n"), - "unrelated file remains intact"); - check(getcwd(cwd_after, sizeof(cwd_after)) != 0 && - strcmp(cwd_before, cwd_after) == 0, - "full-path delete operations preserve CWD"); - - if (failures == 0u) log_text("ALL PASS\r\n"); - if (result_fd >= 0) (void)close(result_fd); - return failures; -} diff --git a/applications/Volkov/tests/p12_group_copy_fixture.py b/applications/Volkov/tests/p12_group_copy_fixture.py deleted file mode 100755 index f7d59d4..0000000 --- a/applications/Volkov/tests/p12_group_copy_fixture.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python3 -"""Создаёт и проверяет HDD-fixture группового F5.""" - -from __future__ import annotations - -import pathlib -import sys - - -def payload(size: int, seed: int) -> bytes: - return bytes(((index * 37 + seed) & 0xFF) for index in range(size)) - - -def create(root: pathlib.Path) -> None: - source = root / "GRSRC" - target = root / "GRDST" - source.mkdir(parents=True) - target.mkdir() - (source / "A.BIN").write_bytes(payload(10, 3)) - (source / "B.BIN").write_bytes(payload(4097, 11)) - (source / "C.BIN").write_bytes(b"") - directory = source / "ADIR" - directory.mkdir() - (directory / "INSIDE.TXT").write_bytes(b"must not be copied\r\n") - - -def verify(root: pathlib.Path) -> None: - source = root / "GRSRC" - target = root / "GRDST" - failures: list[str] = [] - expected = { - "A.BIN": payload(10, 3), - "B.BIN": payload(4097, 11), - "C.BIN": b"", - } - - for name, content in expected.items(): - source_path = source / name - target_path = target / name - if not source_path.is_file() or source_path.read_bytes() != content: - failures.append(f"source изменён: {name}") - if not target_path.is_file() or target_path.read_bytes() != content: - failures.append(f"target не совпадает: {name}") - target_inside = target / "ADIR" / "INSIDE.TXT" - if (not target_inside.is_file() or - target_inside.read_bytes() != b"must not be copied\r\n"): - failures.append("выбранный каталог не скопирован рекурсивно") - temporary = [path.name for path in target.rglob("*") - if path.name.upper().endswith(".TMP")] - if temporary: - failures.append(f"остались temp: {temporary}") - inside = source / "ADIR" / "INSIDE.TXT" - if not inside.is_file() or inside.read_bytes() != b"must not be copied\r\n": - failures.append("исходный выбранный каталог изменён") - - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: 3 selected files and selected directory copied; no temp.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p13_group_delete_fixture.py b/applications/Volkov/tests/p13_group_delete_fixture.py deleted file mode 100755 index 0a2998c..0000000 --- a/applications/Volkov/tests/p13_group_delete_fixture.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python3 -"""Создаёт и проверяет HDD-fixture группового F8.""" - -from __future__ import annotations - -import pathlib -import sys - - -def create(root: pathlib.Path) -> None: - panel = root / "GDEL" - panel.mkdir(parents=True) - (panel / "A.TXT").write_bytes(b"delete A\r\n") - (panel / "B.BIN").write_bytes(b"delete B payload\r\n") - (panel / "KEEP.TXT").write_bytes(b"keep payload\r\n") - directory = panel / "ADIR" - directory.mkdir() - (directory / "INSIDE.TXT").write_bytes(b"directory payload\r\n") - (panel / "EMPTYDIR").mkdir() - - -def verify(root: pathlib.Path) -> None: - panel = root / "GDEL" - failures: list[str] = [] - - for name in ("A.TXT", "B.BIN"): - if (panel / name).exists(): - failures.append(f"выбранный файл не удалён: {name}") - expected = { - "KEEP.TXT": b"keep payload\r\n", - "ADIR/INSIDE.TXT": b"directory payload\r\n", - } - for name, content in expected.items(): - path = panel / name - if not path.is_file() or path.read_bytes() != content: - failures.append(f"сохранённый объект изменён: {name}") - if not (panel / "EMPTYDIR").is_dir(): - failures.append("выбранный пустой каталог ошибочно удалён") - - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: selected files deleted; selected directories and KEEP preserved.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p14_default_colors_fixture.py b/applications/Volkov/tests/p14_default_colors_fixture.py deleted file mode 100755 index ac31e55..0000000 --- a/applications/Volkov/tests/p14_default_colors_fixture.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python3 -"""Создаёт и проверяет HDD-fixture стандартных цветов имён.""" - -from __future__ import annotations - -import pathlib -import sys - - -CONTENTS = { - "APP.EXE": b"EXE colour sample\r\n", - "ARCHIVE.ZIP": b"ZIP colour sample\r\n", - "NOEXT": b"no extension sample\r\n", - "README.TXT": b"text colour sample\r\n", - "SUBDIR/INSIDE.DAT": b"directory sample\r\n", -} - - -def create(root: pathlib.Path) -> None: - panel = root / "COLORS" - for name, content in CONTENTS.items(): - path = panel / name - path.parent.mkdir(parents=True, exist_ok=True) - path.write_bytes(content) - - -def verify(root: pathlib.Path) -> None: - panel = root / "COLORS" - failures: list[str] = [] - for name, content in CONTENTS.items(): - path = panel / name - if not path.is_file() or path.read_bytes() != content: - failures.append(f"объект отсутствует или изменён: {name}") - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: colour fixture preserved.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p15_tree_cancel_fixture.py b/applications/Volkov/tests/p15_tree_cancel_fixture.py deleted file mode 100755 index 9064042..0000000 --- a/applications/Volkov/tests/p15_tree_cancel_fixture.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python3 -"""Создаёт и проверяет fixture отмены рекурсивного F5.""" - -from __future__ import annotations - -import pathlib -import sys - - -def payload(size: int) -> bytes: - return bytes(((index * 29 + 17) & 0xFF) for index in range(size)) - - -BIG = payload(4 * 1024 * 1024) -AFTER = b"may be committed before traversal reaches BIG\r\n" - - -def create(root: pathlib.Path) -> None: - tree = root / "CSRC" / "TREE" - tree.mkdir(parents=True) - (root / "CDST").mkdir() - (tree / "BIG.BIN").write_bytes(BIG) - (tree / "AFTER.TXT").write_bytes(AFTER) - - -def verify(root: pathlib.Path) -> None: - source = root / "CSRC" / "TREE" - target = root / "CDST" / "TREE" - failures: list[str] = [] - - if (source / "BIG.BIN").read_bytes() != BIG: - failures.append("source BIG.BIN изменён") - if (source / "AFTER.TXT").read_bytes() != AFTER: - failures.append("source AFTER.TXT изменён") - if not target.is_dir(): - failures.append("root target-каталог не был создан до cancel") - else: - big_target = target / "BIG.BIN" - if big_target.exists(): - failures.append("BIG.BIN committed несмотря на ранний cancel") - after_target = target / "AFTER.TXT" - if after_target.exists() and after_target.read_bytes() != AFTER: - failures.append("частично завершённый AFTER.TXT повреждён") - temporary = [path.name for path in target.iterdir() - if path.name.upper().endswith(".TMP")] - if temporary: - failures.append(f"после cancel остались temp: {temporary}") - - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: tree cancel removed active temp; source intact; no BIG commit.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p15_tree_copy_fixture.py b/applications/Volkov/tests/p15_tree_copy_fixture.py deleted file mode 100755 index fd6f0f2..0000000 --- a/applications/Volkov/tests/p15_tree_copy_fixture.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python3 -"""Создаёт и проверяет HDD-fixture рекурсивного F5 одного каталога.""" - -from __future__ import annotations - -import pathlib -import sys - - -def payload(size: int, seed: int) -> bytes: - return bytes(((index * 53 + seed) & 0xFF) for index in range(size)) - - -def create(root: pathlib.Path) -> None: - tree = root / "SRC" / "TREE" - (root / "DST").mkdir(parents=True) - (tree / "EMPTY").mkdir(parents=True) - (tree / "ROOT.TXT").write_bytes(b"recursive root\r\n") - sub = tree / "SUB" - sub.mkdir() - (sub / "A.BIN").write_bytes(payload(4097, 7)) - (sub / "ZERO.DAT").write_bytes(b"") - - deep = sub - for level in range(1, 17): - deep = deep / f"L{level:02d}" - deep.mkdir() - (deep / "BOTTOM.TXT").write_bytes(b"depth sixteen\r\n") - - -def tree_snapshot(root: pathlib.Path) -> dict[str, bytes | None]: - result: dict[str, bytes | None] = {} - for path in sorted(root.rglob("*")): - relative = path.relative_to(root).as_posix().upper() - result[relative] = None if path.is_dir() else path.read_bytes() - return result - - -def verify(root: pathlib.Path) -> None: - source = root / "SRC" / "TREE" - target = root / "DST" / "TREE" - failures: list[str] = [] - - if not source.is_dir() or not target.is_dir(): - failures.append("source или target TREE отсутствует") - elif tree_snapshot(source) != tree_snapshot(target): - failures.append("рекурсивное дерево target не совпадает с source") - - temporary = [path.as_posix() for path in (root / "DST").rglob("*") - if path.name.upper().endswith(".TMP")] - if temporary: - failures.append(f"остались temp: {temporary}") - - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: recursive tree copied exactly; depth=16; no temp.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p15_tree_self_fixture.py b/applications/Volkov/tests/p15_tree_self_fixture.py deleted file mode 100755 index 0a5578d..0000000 --- a/applications/Volkov/tests/p15_tree_self_fixture.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python3 -"""Создаёт и проверяет fixture запрета target внутри source.""" - -from __future__ import annotations - -import pathlib -import sys - - -CONTENT = b"self-target guard\r\n" - - -def create(root: pathlib.Path) -> None: - tree = root / "SRC" / "TREE" - (tree / "INNER").mkdir(parents=True) - (tree / "FILE.TXT").write_bytes(CONTENT) - - -def verify(root: pathlib.Path) -> None: - tree = root / "SRC" / "TREE" - failures: list[str] = [] - if (tree / "FILE.TXT").read_bytes() != CONTENT: - failures.append("source FILE.TXT изменён") - inner = tree / "INNER" - if not inner.is_dir() or any(inner.iterdir()): - failures.append("внутри source появился рекурсивный target") - temporary = [path.as_posix() for path in tree.rglob("*") - if path.name.upper().endswith(".TMP")] - if temporary: - failures.append(f"остались temp: {temporary}") - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: target-inside-source refused before mutation.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p16_group_tree_copy_fixture.py b/applications/Volkov/tests/p16_group_tree_copy_fixture.py deleted file mode 100755 index d0ddf56..0000000 --- a/applications/Volkov/tests/p16_group_tree_copy_fixture.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python3 -"""Создаёт и проверяет group F5 с несколькими directory roots.""" - -from __future__ import annotations - -import pathlib -import sys - - -def payload(size: int, seed: int) -> bytes: - return bytes(((index * 41 + seed) & 0xFF) for index in range(size)) - - -def create(root: pathlib.Path) -> None: - source = root / "GSRC" - target = root / "GDST" - target.mkdir(parents=True) - adir = source / "ADIR" - bdir = source / "BDIR" - (adir / "EMPTY").mkdir(parents=True) - bdir.mkdir(parents=True) - (adir / "A1.BIN").write_bytes(payload(10, 3)) - (bdir / "B1.TXT").write_bytes(b"second root\r\n") - (source / "ROOT.BIN").write_bytes(payload(4097, 9)) - (source / "KEEP.TXT").write_bytes(b"not selected\r\n") - - -def snapshot(root: pathlib.Path) -> dict[str, bytes | None]: - result: dict[str, bytes | None] = {} - for path in sorted(root.rglob("*")): - name = path.relative_to(root).as_posix().upper() - result[name] = None if path.is_dir() else path.read_bytes() - return result - - -def verify(root: pathlib.Path) -> None: - source = root / "GSRC" - target = root / "GDST" - failures: list[str] = [] - expected = { - "ADIR": None, - "ADIR/A1.BIN": payload(10, 3), - "ADIR/EMPTY": None, - "BDIR": None, - "BDIR/B1.TXT": b"second root\r\n", - "ROOT.BIN": payload(4097, 9), - } - if snapshot(target) != expected: - failures.append("target не равен двум выбранным деревьям и ROOT.BIN") - if not (source / "KEEP.TXT").is_file(): - failures.append("source KEEP.TXT отсутствует") - if (target / "KEEP.TXT").exists(): - failures.append("невыбранный KEEP.TXT ошибочно скопирован") - temporary = [path.as_posix() for path in target.rglob("*") - if path.name.upper().endswith(".TMP")] - if temporary: - failures.append(f"остались temp: {temporary}") - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: selected file and two directory roots copied exactly.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p17_tree_delete_cancel_fixture.py b/applications/Volkov/tests/p17_tree_delete_cancel_fixture.py deleted file mode 100644 index 6be2c6f..0000000 --- a/applications/Volkov/tests/p17_tree_delete_cancel_fixture.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python3 -"""Создаёт и проверяет runtime-cancel рекурсивного группового F8.""" - -from __future__ import annotations - -import pathlib -import sys - - -DIRECTORY_COUNT = 180 - - -def payload(index: int) -> bytes: - return bytes(((offset * 17 + index) & 0xFF) for offset in range(97)) - - -def create(root: pathlib.Path) -> None: - panel = root / "CDEL" - tree = panel / "TREE" - tree.mkdir(parents=True) - - for index in range(DIRECTORY_COUNT): - child = tree / f"D{index:03d}" - child.mkdir() - (child / "ITEM.BIN").write_bytes(payload(index)) - - (panel / "TOP.BIN").write_bytes(b"top-level selected file\r\n") - (panel / "KEEP.TXT").write_bytes(b"unselected cancel sentinel\r\n") - - -def verify(root: pathlib.Path) -> None: - panel = root / "CDEL" - failures: list[str] = [] - - if (panel / "TOP.BIN").exists(): - failures.append("завершённое удаление TOP.BIN ошибочно откатилось") - - keep = panel / "KEEP.TXT" - if not keep.is_file() or keep.read_bytes() != b"unselected cancel sentinel\r\n": - failures.append("невыбранный KEEP.TXT отсутствует или изменён") - - tree = panel / "TREE" - if not tree.is_dir(): - failures.append("TREE успел удалиться целиком: runtime-cancel не подтверждён") - else: - remaining = list(tree.glob("D*/ITEM.BIN")) - if not remaining: - failures.append("после cancel не осталось ни одного файла дерева") - for path in remaining: - index = int(path.parent.name[1:]) - if path.read_bytes() != payload(index): - failures.append(f"повреждён оставшийся файл: {path.name}") - break - - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: runtime cancel kept remaining tree; completed TOP delete remains.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p17_tree_delete_fixture.py b/applications/Volkov/tests/p17_tree_delete_fixture.py deleted file mode 100644 index df90169..0000000 --- a/applications/Volkov/tests/p17_tree_delete_fixture.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python3 -"""Создаёт и проверяет HDD-fixture рекурсивного группового F8.""" - -from __future__ import annotations - -import pathlib -import sys - - -def payload(size: int, seed: int) -> bytes: - return bytes(((index * 29 + seed) & 0xFF) for index in range(size)) - - -def create(root: pathlib.Path) -> None: - panel = root / "RDEL" - adir = panel / "ADIR" - bdir = panel / "BDIR" - keepdir = panel / "KEEPDIR" - - (adir / "EMPTY").mkdir(parents=True) - bdir.mkdir(parents=True) - keepdir.mkdir(parents=True) - - # Несколько файлов в одном каталоге проверяют, что F_NEXT продолжает - # перечисление после удаления только что возвращённой записи. - for index, size in enumerate((0, 1, 16, 4097)): - (adir / f"A{index}.BIN").write_bytes(payload(size, index + 3)) - - deep = adir - for depth in range(1, 13): - deep = deep / f"D{depth:02d}" - deep.mkdir() - (deep / f"F{depth:02d}.TXT").write_bytes( - payload(depth * 7, depth + 19) - ) - - (bdir / "B0.TXT").write_bytes(b"delete second root\r\n") - (bdir / "B1.BIN").write_bytes(payload(513, 71)) - (panel / "TOP.BIN").write_bytes(payload(33, 91)) - - (panel / "KEEP.TXT").write_bytes(b"keep top-level payload\r\n") - (keepdir / "SAFE.BIN").write_bytes(payload(257, 111)) - - -def verify(root: pathlib.Path) -> None: - panel = root / "RDEL" - failures: list[str] = [] - - for name in ("ADIR", "BDIR", "TOP.BIN"): - if (panel / name).exists(): - failures.append(f"выбранный объект не удалён: {name}") - - keep_file = panel / "KEEP.TXT" - if not keep_file.is_file() or keep_file.read_bytes() != b"keep top-level payload\r\n": - failures.append("невыбранный KEEP.TXT отсутствует или изменён") - - safe = panel / "KEEPDIR" / "SAFE.BIN" - if not safe.is_file() or safe.read_bytes() != payload(257, 111): - failures.append("невыбранное дерево KEEPDIR изменено") - - remaining = sorted( - path.relative_to(panel).as_posix().upper() - for path in panel.rglob("*") - ) - if remaining != ["KEEP.TXT", "KEEPDIR", "KEEPDIR/SAFE.BIN"]: - failures.append(f"неожиданный остаток RDEL: {remaining}") - - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: selected file and two directory trees deleted; KEEP preserved.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p18_metadata_fixture.py b/applications/Volkov/tests/p18_metadata_fixture.py deleted file mode 100644 index 5bdb120..0000000 --- a/applications/Volkov/tests/p18_metadata_fixture.py +++ /dev/null @@ -1,175 +0,0 @@ -#!/usr/bin/env python3 -"""Создаёт payload и проверяет точные FAT16-метаданные после F5.""" - -from __future__ import annotations - -import datetime -import os -import pathlib -import struct -import sys - - -EXPECTED_ATTR = 0x27 # readonly + hidden + system + archive -EXPECTED_DATE = ((2001 - 1980) << 9) | (2 << 5) | 3 -EXPECTED_TIME = (4 << 11) | (5 << 5) | (6 // 2) -PAYLOAD = bytes(((index * 43 + 17) & 0xFF) for index in range(4097)) - - -def create(root: pathlib.Path) -> None: - source = root / "MSRC" - target = root / "MDST" - source.mkdir(parents=True) - target.mkdir(parents=True) - path = source / "META.BIN" - path.write_bytes(PAYLOAD) - timestamp = datetime.datetime(2001, 2, 3, 4, 5, 6).timestamp() - os.utime(path, (timestamp, timestamp)) - - -class Fat16: - def __init__(self, raw: bytes) -> None: - self.raw = raw - part = struct.unpack_from(" int: - return self.data + (cluster - 2) * self.cluster_size - - def next_cluster(self, cluster: int) -> int: - return struct.unpack_from(" bytes: - if cluster is None: - return self.raw[self.root:self.root + self.root_entries * 32] - result = bytearray() - while 2 <= cluster < 0xFFF8: - offset = self.cluster_offset(cluster) - result += self.raw[offset:offset + self.cluster_size] - cluster = self.next_cluster(cluster) - return bytes(result) - - @staticmethod - def short_name(name: str) -> bytes: - stem, dot, ext = name.upper().partition(".") - if len(stem) > 8 or len(ext) > 3: - raise ValueError(f"not an 8.3 name: {name}") - return stem.ljust(8).encode("ascii") + ext.ljust(3).encode("ascii") - - def find(self, directory: int | None, name: str) -> bytes | None: - wanted = self.short_name(name) - data = self.directory_bytes(directory) - for offset in range(0, len(data), 32): - entry = data[offset:offset + 32] - if entry[0] == 0x00: - break - if entry[0] == 0xE5 or entry[11] == 0x0F: - continue - if entry[:11] == wanted: - return entry - return None - - @staticmethod - def cluster(entry: bytes) -> int: - return struct.unpack_from(" bytes: - remaining = struct.unpack_from(" tuple[int, int, int]: - return ( - entry[11], - struct.unpack_from(" tuple[Fat16, bytes, bytes | None]: - fat = Fat16(raw_path.read_bytes()) - source_dir = fat.find(None, "MSRC") - target_dir = fat.find(None, "MDST") - if source_dir is None or target_dir is None: - raise RuntimeError("MSRC/MDST отсутствуют в root FAT") - source = fat.find(fat.cluster(source_dir), "META.BIN") - target = fat.find(fat.cluster(target_dir), "META.BIN") - if source is None: - raise RuntimeError("MSRC/META.BIN отсутствует") - return fat, source, target - - -def verify_source(raw_path: pathlib.Path) -> None: - fat, source, target = records(raw_path) - failures: list[str] = [] - if target is not None: - failures.append("MDST не пуст до MAME") - if metadata(source) != (EXPECTED_ATTR, EXPECTED_DATE, EXPECTED_TIME): - failures.append(f"неверные source metadata: {metadata(source)!r}") - if fat.file_bytes(source) != PAYLOAD: - failures.append("source payload повреждён") - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: source FAT metadata prepared exactly.") - - -def verify(raw_path: pathlib.Path) -> None: - fat, source, target = records(raw_path) - failures: list[str] = [] - if target is None: - failures.append("MDST/META.BIN не создан") - else: - if metadata(target) != metadata(source): - failures.append( - f"metadata differ: src={metadata(source)!r} dst={metadata(target)!r}" - ) - if fat.file_bytes(target) != PAYLOAD: - failures.append("target payload не совпадает") - if metadata(source) != (EXPECTED_ATTR, EXPECTED_DATE, EXPECTED_TIME): - failures.append("source metadata изменены") - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: F5 preserved exact FAT date/time and R/H/S/A attributes.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in { - "create", "verify-source", "verify" - }: - raise SystemExit( - f"usage: {sys.argv[0]} create|verify-source|verify PATH" - ) - path = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(path) - elif sys.argv[1] == "verify-source": - verify_source(path) - else: - verify(path) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p19_overwrite_fixture.py b/applications/Volkov/tests/p19_overwrite_fixture.py deleted file mode 100644 index 157d8c1..0000000 --- a/applications/Volkov/tests/p19_overwrite_fixture.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python3 -"""Fixture и побайтная проверка транзакционного overwrite P19.""" - -from __future__ import annotations - -import pathlib -import sys - - -def payload(seed: int, size: int) -> bytes: - return bytes(((index * 29 + seed) & 0xFF) for index in range(size)) - - -SOURCES = { - "NORMAL.BIN": payload(11, 4097), - "READONLY.BIN": payload(23, 8193), - "ABSENT.BIN": payload(37, 17), - "COLLIDE.BIN": payload(51, 4096), - "ROLLBACK.BIN": payload(67, 4096), -} -OLD = { - "NORMAL.BIN": b"old-normal-target", - "READONLY.BIN": b"old-readonly-target", - "COLLIDE.BIN": b"old-collision-target", - "ROLLBACK.BIN": b"old-rollback-must-survive", -} -COLLISION = b"pre-existing backup name must survive" - - -def create(root: pathlib.Path) -> None: - source = root / "SOURCE" - target = root / "TARGET" - source.mkdir(parents=True) - target.mkdir(parents=True) - for name, data in SOURCES.items(): - (source / name).write_bytes(data) - for name, data in OLD.items(): - (target / name).write_bytes(data) - (target / "~SB0000.BAK").write_bytes(COLLISION) - - -def verify(root: pathlib.Path) -> None: - target = root / "TARGET" - expected = { - "NORMAL.BIN": SOURCES["NORMAL.BIN"], - "READONLY.BIN": SOURCES["READONLY.BIN"], - "COLLIDE.BIN": SOURCES["COLLIDE.BIN"], - "ABSENT.BIN": SOURCES["ABSENT.BIN"], - "RENAMED.BIN": SOURCES["ABSENT.BIN"], - "ROLLBACK.BIN": OLD["ROLLBACK.BIN"], - "~SB0000.BAK": COLLISION, - } - failures: list[str] = [] - for name, data in expected.items(): - path = target / name - if not path.is_file() or path.read_bytes() != data: - failures.append(f"неверный target {name}") - unexpected = sorted( - path.name for path in target.glob("~SB*.BAK") - if path.name.upper() != "~SB0000.BAK" - ) - if unexpected: - failures.append(f"оставлены служебные backup: {unexpected!r}") - result = root / "RESULT.TXT" - text = result.read_text("ascii", errors="replace") if result.exists() else "" - if "ALL PASS" not in text: - failures.append("target-probe не записал ALL PASS") - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: transactional overwrite, readonly, collision and rollback.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify PATH") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p19_overwrite_probe.c b/applications/Volkov/tests/p19_overwrite_probe.c deleted file mode 100644 index e9a9565..0000000 --- a/applications/Volkov/tests/p19_overwrite_probe.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * p19_overwrite_probe.c — target-проверка backup/commit/rollback copy-job. - * - * UI-policy проверяется отдельно. Здесь ошибки подаются точно между - * последним write и commit, чтобы доказать сохранность старого target. - */ - -#include -#include -#include -#include -#include -#include -#include "sc_copy.h" - -static ScCopyJob job; -static int result_fd = -1; -static uint8_t failures; - -static void log_text(const char *text) -{ - const char *end = text; - uint16_t length; - - cputs(text); - while (*end != '\0') end++; - length = (uint16_t)(end - text); - if (result_fd >= 0) (void)write(result_fd, text, length); -} - -static void check(uint8_t condition, const char *name) -{ - log_text(condition ? "PASS " : "FAIL "); - if (!condition) failures++; - log_text(name); - log_text("\r\n"); -} - -static int copy_file(const char *source_name, const char *target_name, - uint32_t size, uint8_t page, uint8_t replace) -{ - int step; - - if (sc_copy_begin_policy(&job, "D:\\SOURCE", "D:\\TARGET", - source_name, target_name, size, page, - replace) != 0) return SC_COPY_STEP_ERROR; - do { - step = sc_copy_step(&job); - } while (step == SC_COPY_STEP_MORE); - return step; -} - -int main(void) -{ - uint16_t total_before = 0u; - uint16_t free_before = 0u; - uint16_t total_after = 0u; - uint16_t free_after = 0u; - uint8_t block; - uint8_t page; - int step; - - textattr(0x07u); - clrscr_attr(0x07u); - cputs("P19 transactional overwrite\r\n\r\n"); - result_fd = open("D:\\RESULT.TXT", O_WRONLY | O_CREAT | O_TRUNC); - mem_info(&total_before, &free_before); - block = mem_alloc_pages(1u); - if (block == 0u) return 1; - page = mem_get_page(block, 0u); - - errno = 0; - step = sc_copy_begin(&job, "D:\\SOURCE", "D:\\TARGET", - "NORMAL.BIN", 4097ul, page); - check(step != 0 && job.error == EEXIST, - "default still refuses existing target"); - - step = copy_file("NORMAL.BIN", "NORMAL.BIN", 4097ul, page, 1u); - check(step == SC_COPY_STEP_DONE && job.committed, - "overwrite commits through backup"); - - step = copy_file("READONLY.BIN", "READONLY.BIN", 8193ul, page, 1u); - check(step == SC_COPY_STEP_DONE && job.committed && - (job.existing_attr & 1u), "explicit readonly overwrite"); - - step = copy_file("COLLIDE.BIN", "COLLIDE.BIN", 4096ul, page, 1u); - check(step == SC_COPY_STEP_DONE && job.committed, - "pre-existing backup name skipped"); - - step = copy_file("ABSENT.BIN", "ABSENT.BIN", 17ul, page, 1u); - check(step == SC_COPY_STEP_DONE && job.committed, - "replace policy accepts vanished target"); - - step = copy_file("ABSENT.BIN", "RENAMED.BIN", 17ul, page, 0u); - check(step == SC_COPY_STEP_DONE && job.committed, - "distinct destination name"); - - step = sc_copy_begin_policy(&job, "D:\\SOURCE", "D:\\TARGET", - "ROLLBACK.BIN", "ROLLBACK.BIN", - 4096ul, page, 1u); - if (step == 0) step = sc_copy_step(&job); - if (step == SC_COPY_STEP_MORE) { - (void)close(job.temp_fd); - job.temp_fd = -1; - (void)unlink(job.temp_path); - step = sc_copy_step(&job); - } - check(step == SC_COPY_STEP_ERROR && !job.committed, - "rename failure rolls original target back"); - - mem_free_block(block); - mem_info(&total_after, &free_after); - check(total_before == total_after && free_before == free_after, - "EMM restored"); - if (failures == 0u) log_text("ALL PASS\r\n"); - if (result_fd >= 0) (void)close(result_fd); - cputs("\r\nPress Enter to return to DSS.\r\n"); - while (kbhit()) (void)getkey(); - (void)getkey(); - return failures; -} diff --git a/applications/Volkov/tests/p19_policy_fixture.py b/applications/Volkov/tests/p19_policy_fixture.py deleted file mode 100644 index 7554930..0000000 --- a/applications/Volkov/tests/p19_policy_fixture.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env python3 -"""Fixture и проверка UI-policy overwrite/skip/rename/cancel P19.""" - -from __future__ import annotations - -import pathlib -import sys - - -def payload(seed: int, size: int) -> bytes: - return bytes(((index * 31 + seed) & 0xFF) for index in range(size)) - - -SOURCES = { - "AONE.BIN": payload(1, 4097), - "ATWO.BIN": payload(2, 8193), - "CANCEL.BIN": payload(3, 33), - "REN.BIN": payload(4, 257), - "RONLY.BIN": payload(5, 5000), - "SONE.BIN": payload(6, 65), - "STWO.BIN": payload(7, 66), -} -OLD = {name: ("old-" + name).encode("ascii") for name in SOURCES} - - -def create(root: pathlib.Path) -> None: - source = root / "P19SRC" - target = root / "P19DST" - source.mkdir(parents=True) - target.mkdir(parents=True) - for name, data in SOURCES.items(): - (source / name).write_bytes(data) - for name, data in OLD.items(): - (target / name).write_bytes(data) - - -def verify(root: pathlib.Path) -> None: - target = root / "P19DST" - expected = { - "AONE.BIN": SOURCES["AONE.BIN"], - "ATWO.BIN": SOURCES["ATWO.BIN"], - "RONLY.BIN": SOURCES["RONLY.BIN"], - "SONE.BIN": OLD["SONE.BIN"], - "STWO.BIN": OLD["STWO.BIN"], - "REN.BIN": OLD["REN.BIN"], - "CANCEL.BIN": OLD["CANCEL.BIN"], - "NEXT.BIN": SOURCES["REN.BIN"], - } - failures: list[str] = [] - for name, data in expected.items(): - path = target / name - if not path.is_file() or path.read_bytes() != data: - failures.append(f"неверный target {name}") - garbage = sorted( - path.name for path in target.iterdir() - if path.name.upper().startswith(("~SC", "~SB")) - ) - if garbage: - failures.append(f"оставлены служебные файлы: {garbage!r}") - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: overwrite-all, readonly prompt, skip-all, rename and cancel.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify PATH") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p1_platform/Makefile b/applications/Volkov/tests/p1_platform/Makefile deleted file mode 100644 index b995b0c..0000000 --- a/applications/Volkov/tests/p1_platform/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -# P1 platform probe для Sprinter Commander. -# -# Собирает два файла: -# p1probe.exe — интерактивные пробы каталогов, EMM, экрана, клавиатуры, -# DSKINFO и EXEC; -# p1child.exe — дочерняя программа для проверки EXEC/возврата. -# -# Пробы намеренно собираются в small: это изолированный диагностический -# инструмент, а не topology будущего Commander. Сборка big проверяется в P2. - -EXAMPLE := p1probe -MEMORY := small -ALLOCS ?= 3000 -EXTRA_FLAGS := --safe --max-allocs $(ALLOCS) -EXTRA_SRCS := p1_sys.c p1_emm.c p1_dir.c p1_screen.c p1_keyboard.c p1_exec_test.c -EXTRA_DATA := p1child.exe - -include $(CURDIR)/../../sdk.mk - -# EXEC-тест ожидает дочерний EXE рядом с основной пробой. -$(EXE): Makefile p1child.exe - -p1child.exe: p1child.c - $(SPRINTER_CC) --memory tiny --safe --max-allocs $(ALLOCS) -o $@ $< - -clean-child: - rm -f p1child.exe - -clean: clean-child - -.PHONY: clean-child diff --git a/applications/Volkov/tests/p1_platform/mame_dump_keys.lua b/applications/Volkov/tests/p1_platform/mame_dump_keys.lua deleted file mode 100644 index 62b3238..0000000 --- a/applications/Volkov/tests/p1_platform/mame_dump_keys.lua +++ /dev/null @@ -1,25 +0,0 @@ --- Диагностическая проба полей AT-клавиатуры MAME. --- Нужна для воспроизводимого ввода навигационных и функциональных клавиш. - -local ready = false -local done = false - -emu.add_machine_reset_notifier(function() - ready = true -end) - -emu.register_periodic(function() - if not ready or done then return end - done = true - - for tag, port in pairs(manager.machine.ioport.ports) do - if tostring(tag):find(":kbd:ms_naturl:P", 1, true) then - for name, field in pairs(port.fields) do - print(string.format("[p1-key] %s mask=0x%02X name=%s", - tostring(tag), field.mask, tostring(name))) - end - end - end - - manager.machine:exit() -end) diff --git a/applications/Volkov/tests/p1_platform/mame_p1_keyboard.lua b/applications/Volkov/tests/p1_platform/mame_p1_keyboard.lua deleted file mode 100644 index 485e7cb..0000000 --- a/applications/Volkov/tests/p1_platform/mame_p1_keyboard.lua +++ /dev/null @@ -1,147 +0,0 @@ --- Автоматический MAME-сценарий для P1-пробы клавиатуры. --- Ввод идёт прямо в поля AT-клавиатуры Sprinter, как в --- toolchain/mame_interactive.py, но дополнен именованными клавишами. - -local KB = ":kbd:ms_naturl:" -local HOLD = 0.06 -local events = {} - -local function add(t, tag, mask, value) - events[#events + 1] = {t, KB .. tag, mask, value} -end - -local function key(t, tag, mask) - add(t, tag, mask, 1) - add(t + HOLD, tag, mask, 0) -end - -local function chord(t, mod_tag, mod_mask, key_tag, key_mask) - add(t, mod_tag, mod_mask, 1) - add(t + 0.02, key_tag, key_mask, 1) - add(t + 0.08, key_tag, key_mask, 0) - add(t + 0.10, mod_tag, mod_mask, 0) -end - -local chars = { - a={"P1.6",0x04}, b={"P1.5",0x10}, e={"P1.4",0x20}, - o={"P2.2",0x02}, p={"P2.3",0x20}, r={"P1.5",0x01}, - x={"P1.2",0x10}, ["1"]={"P1.6",0x01}, - [" "]={"P2.4",0x80}, ["."]={"P2.2",0x10}, - ["\\"]={"P2.1",0x04}, ["\n"]={"P2.1",0x10}, - [":"]={"P2.3",0x02,true} -} - -local function type_text(t, text) - for i = 1, #text do - local ch = text:sub(i, i) - local def = chars[ch] - local shifted = false - if not def then - local lower = ch:lower() - def = chars[lower] - shifted = ch ~= lower - else - shifted = def[3] or false - end - if shifted then - chord(t, "P1.7", 0x02, def[1], def[2]) - else - key(t, def[1], def[2]) - end - t = t + 0.14 - end -end - --- Запуск P1PROBE.EXE из приглашения DSS и выбор клавиатурной пробы. -type_text(8.0, "a:\\P1PROBE .EXE\n") -key(14.0, "P1.5", 0x40) -- 4 - --- 24 события: навигация, F1..F10, комбинации Commander и завершающий Esc. -local sequence = { - {"P2.4",0x01}, -- Up - {"P2.3",0x10}, -- Down - {"P2.1",0x08}, -- Left - {"P2.4",0x40}, -- Right - {"P2.5",0x01}, -- Home - {"P2.5",0x20}, -- End - {"P1.2",0x01}, -- Page Up - {"P1.2",0x20}, -- Page Down - {"P2.6",0x01}, -- Insert - {"P2.6",0x02}, -- Delete - {"P1.2",0x40}, -- F1 - {"P1.2",0x80}, -- F2 - {"P2.6",0x40}, -- F3 - {"P2.6",0x80}, -- F4 - {"P2.1",0x40}, -- F5 - {"P2.1",0x80}, -- F6 - {"P2.2",0x40}, -- F7 - {"P2.2",0x80}, -- F8 - {"P2.3",0x40}, -- F9 - {"P2.3",0x80} -- F10 -} - -for i, def in ipairs(sequence) do - key(16.0 + (i - 1) * 0.35, def[1], def[2]) -end -chord(23.00, "P1.1", 0x02, "P1.5", 0x01) -- Ctrl+R -chord(23.35, "P1.1", 0x02, "P1.2", 0x01) -- Ctrl+PgUp -chord(23.70, "P1.3", 0x04, "P1.2", 0x40) -- Alt+F1 -key(24.05, "P1.6", 0x40) -- Esc - --- Закрыть итоговый экран и подтвердить возврат к главному меню. -key(27.5, "P2.1", 0x10) - -table.sort(events, function(a, b) return a[1] < b[1] end) - -local snap_times = {17.8, 20.6, 23.4, 25.2, 29.0} -local snaps_done = {} -local start_time = nil -local ports = nil -local index = 1 -local field_cache = {} - -local function now() - local t = manager.machine.time - return t.seconds + t.attoseconds / 1e18 -end - -local function field(tag, mask) - local cache_key = tag .. "/" .. mask - if field_cache[cache_key] then return field_cache[cache_key] end - for _, candidate in pairs(ports[tag].fields) do - if candidate.mask == mask then - field_cache[cache_key] = candidate - return candidate - end - end - error("Не найдено поле " .. cache_key) -end - -emu.add_machine_reset_notifier(function() - start_time = now() -end) - -emu.register_periodic(function() - if not start_time then return end - if not ports then ports = manager.machine.ioport.ports end - local elapsed = now() - start_time - - while index <= #events and elapsed >= events[index][1] do - local event = events[index] - field(event[2], event[3]):set_value(event[4]) - index = index + 1 - end - - for _, snap_time in ipairs(snap_times) do - if not snaps_done[snap_time] and elapsed >= snap_time then - snaps_done[snap_time] = true - manager.machine.video:snapshot() - print("[p1-kbd] snapshot t=" .. elapsed) - end - end - - if elapsed >= 31.0 then - print("[p1-kbd] exit t=" .. elapsed) - manager.machine:exit() - end -end) diff --git a/applications/Volkov/tests/p1_platform/p1_dir.c b/applications/Volkov/tests/p1_platform/p1_dir.c deleted file mode 100644 index 25171ae..0000000 --- a/applications/Volkov/tests/p1_platform/p1_dir.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * p1_dir.c — наблюдаемая проверка CURDIR/CHDIR/F_FIRST/F_NEXT. - * - * Тест не меняет файлы. chdir(".") используется только для проверки - * сохранения текущего каталога. - */ - -#include -#include -#include -#include -#include -#include -#include "p1_platform.h" - -#define P1_FIND_ATTRS (FA_RDONLY | FA_HIDDEN | FA_SYSTEM | FA_DIREC | FA_ARCH) -#define P1_FIND_LIMIT 4096u - -static char cwd_before[256]; -static char cwd_after[256]; -static ffblk_t find_buf; - -static int scan_pattern(const char *pattern, uint16_t *out_count, - uint8_t *out_dot, uint8_t *out_dotdot) -{ - uint16_t count = 0; - uint8_t dot = 0; - uint8_t dotdot = 0; - int rc; - int end_errno; - - errno = 0; - rc = ffirst(pattern, &find_buf, P1_FIND_ATTRS); - if (rc != 0) { - if (errno == ENOENT) { - printf("Pattern %-4s: empty (ffirst errno=%d)\n", pattern, errno); - *out_count = 0; - *out_dot = 0; - *out_dotdot = 0; - return 0; - } - printf("ffirst(%s) failed: errno=%d\n", pattern, errno); - return -1; - } - - for (;;) { - if (strcmp(find_buf.found_name, ".") == 0) dot = 1; - if (strcmp(find_buf.found_name, "..") == 0) dotdot = 1; - - if (count < 20) { - printf(" %02X %10lu %04X/%04X %s\n", - (unsigned)find_buf.found_attr, - (unsigned long)find_buf.size, - (unsigned)find_buf.date, - (unsigned)find_buf.time, - find_buf.found_name); - } - - count++; - if (count >= P1_FIND_LIMIT) { - puts("Enumeration limit reached: possible F_NEXT loop."); - return -1; - } - - errno = 0; - if (fnext(&find_buf) != 0) break; - } - - end_errno = errno; - printf("Pattern %-4s: count=%u dot=%u dotdot=%u end_errno=%d\n", - pattern, (unsigned)count, (unsigned)dot, (unsigned)dotdot, - end_errno); - - /* MAME 0.287 / BIOS 3.06 подтвердил ENOENT (3) для конца F_NEXT. - * Код 0x0F из старого описания DSS здесь не является штатным. */ - if (end_errno != ENOENT) { - puts("Unexpected F_NEXT termination code."); - return -1; - } - - *out_count = count; - *out_dot = dot; - *out_dotdot = dotdot; - return 0; -} - -int p1_test_directory(void) -{ - uint16_t count_star_dot; - uint16_t count_star; - uint8_t dot1, dotdot1, dot2, dotdot2; - int failed = 0; - - p1_heading("Directory: CURDIR, CHDIR, F_FIRST/F_NEXT"); - - errno = 0; - if (getcwd(cwd_before, sizeof(cwd_before)) == 0) { - printf("getcwd failed: errno=%d\n", errno); - return -1; - } - printf("CWD before: %s\n", cwd_before); - - if (chdir(".") != 0) { - printf("chdir(.) failed: errno=%d\n", errno); - failed++; - } - - if (getcwd(cwd_after, sizeof(cwd_after)) == 0) { - printf("second getcwd failed: errno=%d\n", errno); - return -1; - } - printf("CWD after : %s\n", cwd_after); - if (strcmp(cwd_before, cwd_after) != 0) { - puts("CWD changed after chdir(.)."); - failed++; - } - - puts("\nFirst entries for *.*:"); - if (scan_pattern("*.*", &count_star_dot, &dot1, &dotdot1) != 0) { - failed++; - } - - puts("\nFirst entries for *:"); - if (scan_pattern("*", &count_star, &dot2, &dotdot2) != 0) { - failed++; - } - - if (count_star != count_star_dot) { - printf("NOTE: * count (%u) differs from *.* count (%u).\n", - (unsigned)count_star, (unsigned)count_star_dot); - } - if (dot1 != dot2 || dotdot1 != dotdot2) { - puts("NOTE: dot entries differ between patterns."); - } - - printf("Directory checks failed: %u\n", (unsigned)failed); - return failed ? -1 : 0; -} - -int p1_test_disk_info(void) -{ - P1DiskInfo info; - uint32_t cluster_bytes; - uint32_t total_bytes; - uint32_t free_bytes; - - p1_heading("DSKINFO: current disk"); - errno = 0; - if (p1_disk_info(0xFF, &info) != 0) { - printf("DSKINFO failed: errno=%d\n", errno); - return -1; - } - - cluster_bytes = (uint32_t)info.sectors_per_cluster * - (uint32_t)info.bytes_per_sector; - total_bytes = cluster_bytes * (uint32_t)info.total_clusters; - free_bytes = cluster_bytes * (uint32_t)info.free_clusters; - - printf("Sectors/cluster : %u\n", (unsigned)info.sectors_per_cluster); - printf("Bytes/sector : %u\n", (unsigned)info.bytes_per_sector); - printf("Total clusters : %u\n", (unsigned)info.total_clusters); - printf("Free clusters : %u\n", (unsigned)info.free_clusters); - printf("Cluster bytes : %lu\n", (unsigned long)cluster_bytes); - printf("Total bytes : %lu\n", (unsigned long)total_bytes); - printf("Free bytes : %lu\n", (unsigned long)free_bytes); - - if (info.sectors_per_cluster == 0 || info.bytes_per_sector == 0 || - info.total_clusters == 0 || info.free_clusters > info.total_clusters) { - puts("DSKINFO returned inconsistent values."); - return -1; - } - return 0; -} diff --git a/applications/Volkov/tests/p1_platform/p1_emm.c b/applications/Volkov/tests/p1_platform/p1_emm.c deleted file mode 100644 index 2844a1c..0000000 --- a/applications/Volkov/tests/p1_platform/p1_emm.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * p1_emm.c — проверка выделения трёх EMM-страниц и доступа через W3. - */ - -#include -#include -#include -#include -#include -#include "p1_platform.h" - -#define P1_PAGE_SIZE 0x4000u -#define P1_BLOCK 64u - -static uint8_t write_buf[P1_BLOCK]; -static uint8_t read_buf[P1_BLOCK]; - -static void make_pattern(uint8_t page_index, uint8_t salt) -{ - uint8_t i; - for (i = 0; i < P1_BLOCK; i++) { - write_buf[i] = (uint8_t)(salt ^ (uint8_t)(page_index * 0x41u + i)); - read_buf[i] = 0; - } -} - -static int check_region(uint8_t page, uint8_t page_index, - uint16_t offset, uint8_t salt) -{ - make_pattern(page_index, salt); - bank_write(page, offset, write_buf, P1_BLOCK); - bank_read(page, offset, read_buf, P1_BLOCK); - if (memcmp(write_buf, read_buf, P1_BLOCK) != 0) { - printf("Mismatch: logical=%u phys=%02X offset=%04X\n", - (unsigned)page_index, (unsigned)page, (unsigned)offset); - return -1; - } - return 0; -} - -int p1_test_emm(void) -{ - uint16_t total_before, free_before; - uint16_t total_after, free_after; - uint8_t block; - uint8_t page[3]; - uint8_t i; - int failed = 0; - - p1_heading("EMM: 3-page allocation and W3 access"); - errno = 0; - mem_info(&total_before, &free_before); - printf("Before: total=%u free=%u pages\n", - (unsigned)total_before, (unsigned)free_before); - - block = mem_alloc_pages(3); - if (block == 0) { - printf("mem_alloc_pages(3) failed: errno=%d\n", errno); - return -1; - } - - printf("Block id: %u\n", (unsigned)block); - for (i = 0; i < 3; i++) { - page[i] = mem_get_page(block, i); - printf(" logical %u -> physical %02X\n", - (unsigned)i, (unsigned)page[i]); - if (page[i] == 0) failed++; - } - - if (page[0] == page[1] || page[0] == page[2] || page[1] == page[2]) { - puts("Physical page numbers are not distinct."); - failed++; - } - - for (i = 0; i < 3; i++) { - if (page[i] == 0) continue; - if (check_region(page[i], i, 0, 0x5A) != 0) failed++; - if (check_region(page[i], i, - (uint16_t)(P1_PAGE_SIZE - P1_BLOCK), 0xA5) != 0) { - failed++; - } - } - - mem_free_block(block); - mem_info(&total_after, &free_after); - printf("After : total=%u free=%u pages\n", - (unsigned)total_after, (unsigned)free_after); - - if (total_before != total_after || free_before != free_after) { - puts("EMM counters were not restored after free."); - failed++; - } - - printf("EMM checks failed: %u\n", (unsigned)failed); - return failed ? -1 : 0; -} diff --git a/applications/Volkov/tests/p1_platform/p1_exec_test.c b/applications/Volkov/tests/p1_platform/p1_exec_test.c deleted file mode 100644 index 559eab2..0000000 --- a/applications/Volkov/tests/p1_platform/p1_exec_test.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * p1_exec_test.c — проверка EXEC, возврата к родителю и WAIT. - */ - -#include -#include -#include -#include -#include -#include -#include -#include "p1_platform.h" - -typedef struct { - int rc; - int error; - uint8_t exit_code; - uint8_t wait_code; - uint8_t page_w1; - uint8_t page_w2; - uint8_t page_w3; - char cwd[256]; -} P1ExecResult; - -static P1ExecResult short_result; -static P1ExecResult path_result; -static char cwd_before[256]; - -static void run_exec_case(const char *label, const char *path, - uint8_t path_mode, P1ExecResult *result) -{ - p1_heading(label); - printf("Calling EXEC: B=%u, path=%s\n", (unsigned)path_mode, path); - cputs("The child switches to 40x32 and waits for one key.\r\n"); - cputs("After that it returns code 0x5A.\r\n\r\n"); - cputs("Press any key to call EXEC..."); - (void)getkey(); - - errno = 0; - result->rc = p1_exec(path_mode, path); - result->error = errno; - result->exit_code = p1_exec_exit_code; - result->wait_code = p1_wait_code(); - - /* Сначала снять страницы, затем исправлять видеорежим и печатать. */ - result->page_w1 = _io_page_w1; - result->page_w2 = _io_page_w2; - result->page_w3 = _io_page_w3; - - if (getcwd(result->cwd, sizeof(result->cwd)) == 0) { - result->cwd[0] = 0; - } - (void)settextmode(TEXT_MODE_80x32); -} - -static void print_result(const char *name, const P1ExecResult *result) -{ - printf("%s: rc=%d errno=%d exit=%02X wait=%02X pages=%02X/%02X/%02X\n", - name, result->rc, result->error, - (unsigned)result->exit_code, (unsigned)result->wait_code, - (unsigned)result->page_w1, (unsigned)result->page_w2, - (unsigned)result->page_w3); - printf(" cwd=%s\n", result->cwd[0] ? result->cwd : ""); -} - -int p1_test_exec(void) -{ - uint8_t before_w1 = _io_page_w1; - uint8_t before_w2 = _io_page_w2; - uint8_t before_w3 = _io_page_w3; - int failed = 0; - - if (getcwd(cwd_before, sizeof(cwd_before)) == 0) { - p1_heading("EXEC: setup failed"); - printf("getcwd failed: errno=%d\n", errno); - return -1; - } - - /* B=0 характеризуем для будущей командной строки. Ошибка этого случая - * не блокирует PoC: Commander будет передавать явный путь с B=1. */ - run_exec_case("EXEC case 1: short name / PATH", - "P1CHILD.EXE", 0, &short_result); - - /* B=1 и явный относительный путь — обязательный случай PoC. */ - run_exec_case("EXEC case 2: explicit path", - ".\\P1CHILD.EXE", 1, &path_result); - - p1_heading("EXEC results"); - printf("Parent before: pages=%02X/%02X/%02X cwd=%s\n", - (unsigned)before_w1, (unsigned)before_w2, (unsigned)before_w3, - cwd_before); - print_result("B=0", &short_result); - print_result("B=1", &path_result); - - if (path_result.rc != 0 || path_result.exit_code != 0x5A || - path_result.wait_code != 0x5A) { - puts("Required B=1 EXEC result is incorrect."); - failed++; - } - if (path_result.page_w1 != before_w1 || path_result.page_w2 != before_w2 || - path_result.page_w3 != before_w3) { - puts("Parent window mapping was not restored."); - failed++; - } - if (strcmp(path_result.cwd, cwd_before) != 0) { - puts("Parent CWD changed after child."); - failed++; - } - - if (short_result.rc != 0) { - puts("NOTE: B=0 short-name lookup failed; explicit B=1 remains usable."); - } - - printf("EXEC checks failed: %u\n", (unsigned)failed); - return failed ? -1 : 0; -} diff --git a/applications/Volkov/tests/p1_platform/p1_keyboard.c b/applications/Volkov/tests/p1_platform/p1_keyboard.c deleted file mode 100644 index 34ff3dc..0000000 --- a/applications/Volkov/tests/p1_platform/p1_keyboard.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * p1_keyboard.c — печать сырых событий getkey()/kbd_mod_state(). - */ - -#include -#include -#include -#include "p1_platform.h" - -static uint8_t compact_modifiers(uint16_t state) -{ - uint8_t result = 0; - if (state & (KBD_MOD_LSHIFT | KBD_MOD_RSHIFT)) result |= 0x01; - if (state & (KBD_MOD_CTRL | KBD_MOD_LCTRL | KBD_MOD_RCTRL)) result |= 0x02; - if (state & (KBD_MOD_ALT | KBD_MOD_LALT | KBD_MOD_RALT)) result |= 0x04; - return result; -} - -int p1_test_keyboard(void) -{ - uint8_t n = 0; - - p1_heading("Keyboard: raw event capture"); - cputs("Press navigation keys, F1..F10 and Ctrl combinations.\r\n"); - cputs("Esc finishes after its event is printed. Maximum: 24 events.\r\n\r\n"); - cputs(" n key ascii scan live_mod compact\r\n"); - - while (n < 24) { - uint16_t key = getkey(); - uint16_t live = kbd_mod_state(); - uint8_t ascii = (uint8_t)key; - uint8_t scan = (uint8_t)(key >> 8); - uint8_t compact = compact_modifiers(live); - char printable = (ascii >= 0x20 && ascii < 0x7F) ? (char)ascii : '.'; - - printf("%2u %c %02X %02X %04X %02X\n", - (unsigned)n, printable, (unsigned)ascii, (unsigned)scan, - (unsigned)live, (unsigned)compact); - n++; - if (ascii == 0x1B) break; - } - - cputs("\r\nCompare the captured values with commander-keymap.md.\r\n"); - return 0; -} diff --git a/applications/Volkov/tests/p1_platform/p1_platform.h b/applications/Volkov/tests/p1_platform/p1_platform.h deleted file mode 100644 index 3a279cd..0000000 --- a/applications/Volkov/tests/p1_platform/p1_platform.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * p1_platform.h — общий контракт платформенных проб Commander P1. - * - * Это диагностический код приложения, не публичный заголовок libc. После - * подтверждения в MAME стабильные wrappers либо останутся в Commander, - * либо будут перенесены в libc отдельными модулями и тестами. - */ - -#ifndef P1_PLATFORM_H -#define P1_PLATFORM_H - -#include - -typedef struct { - uint8_t sectors_per_cluster; - uint16_t total_clusters; - uint16_t free_clusters; - uint16_t bytes_per_sector; -} P1DiskInfo; - -typedef char P1DiskInfo_must_be_7_bytes[(sizeof(P1DiskInfo) == 7) ? 1 : -1]; - -/* Последний код завершения, возвращённый EXEC при CF=0. */ -extern uint8_t p1_exec_exit_code; - -int p1_disk_info(uint8_t disk, P1DiskInfo *out); -int p1_exec(uint8_t path_mode, const char *path); -uint8_t p1_wait_code(void); - -void p1_winrest(uint8_t row, uint8_t col, uint8_t height, uint8_t width, - uint8_t page, uint16_t offset); - -int p1_test_emm(void); -int p1_test_directory(void); -int p1_test_screen(void); -int p1_test_keyboard(void); -int p1_test_exec(void); -int p1_test_disk_info(void); - -void p1_heading(const char *title); -void p1_pause(void); - -#endif diff --git a/applications/Volkov/tests/p1_platform/p1_screen.c b/applications/Volkov/tests/p1_platform/p1_screen.c deleted file mode 100644 index fc1e411..0000000 --- a/applications/Volkov/tests/p1_platform/p1_screen.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * p1_screen.c — визуальная проверка экранной EMM-страницы и WINREST. - */ - -#include -#include -#include -#include -#include -#include -#include "p1_platform.h" - -#define P1_SCREEN_W 80u -#define P1_SCREEN_H 32u -#define P1_ROW_BYTES 160u -#define P1_MARKER_OFF 0x2000u - -static uint8_t row_buf[P1_ROW_BYTES]; -static uint8_t marker_buf[2u * 20u * 2u]; - -static void row_clear(uint8_t attr) -{ - uint8_t x; - for (x = 0; x < P1_SCREEN_W; x++) { - row_buf[(uint16_t)x * 2u] = ' '; - row_buf[(uint16_t)x * 2u + 1u] = attr; - } -} - -static void row_text(uint8_t x, const char *text, uint8_t attr) -{ - while (*text && x < P1_SCREEN_W) { - row_buf[(uint16_t)x * 2u] = (uint8_t)*text++; - row_buf[(uint16_t)x * 2u + 1u] = attr; - x++; - } -} - -static void marker_build(void) -{ - static const char top[] = "PARTIAL WINREST OK "; - static const char bot[] = "offset = 0x2000 "; - uint8_t x; - - for (x = 0; x < 20; x++) { - marker_buf[(uint16_t)x * 2u] = (uint8_t)top[x]; - marker_buf[(uint16_t)x * 2u + 1u] = - COLOR(COLOR_WHITE, COLOR_RED); - marker_buf[40u + (uint16_t)x * 2u] = (uint8_t)bot[x]; - marker_buf[40u + (uint16_t)x * 2u + 1u] = - COLOR(COLOR_YELLOW, COLOR_RED); - } -} - -int p1_test_screen(void) -{ - uint8_t block; - uint8_t page; - uint8_t y; - - p1_heading("WINREST: preparing EMM screen"); - - errno = 0; - block = mem_alloc_pages(1); - if (block == 0) { - printf("mem_alloc_pages failed: errno=%d\n", errno); - return -1; - } - page = mem_get_page(block, 0); - if (page == 0) { - printf("mem_get_page failed: errno=%d\n", errno); - mem_free_block(block); - return -1; - } - - for (y = 0; y < P1_SCREEN_H; y++) { - /* Только яркие foreground 9..15 на синем: граница не должна - * исчезать из-за комбинации blue-on-blue вроде прежнего 0x11. */ - uint8_t attr = COLOR((uint8_t)(COLOR_LIGHTBLUE + (y % 7u)), - COLOR_BLUE); - row_clear(attr); - row_buf[0] = (y == 0 || y == 31) ? '+' : '|'; - row_buf[158] = (y == 0 || y == 31) ? '+' : '|'; - - if (y == 0 || y == 31) { - uint8_t x; - for (x = 1; x < 79; x++) row_buf[(uint16_t)x * 2u] = '-'; - } - if (y == 2) row_text(4, "FULL 80x32 WINREST FROM EMM PAGE", - COLOR(COLOR_WHITE, COLOR_BLUE)); - if (y == 4) row_text(4, "Rows have different foreground colours.", - COLOR(COLOR_YELLOW, COLOR_BLUE)); - if (y == 6) row_text(4, "Expected: complete border, no shifted cells.", - COLOR(COLOR_YELLOW, COLOR_BLUE)); - if (y == 28) row_text(4, "Press a key for partial-offset test.", - COLOR(COLOR_WHITE, COLOR_BLUE)); - - bank_write(page, (uint16_t)y * P1_ROW_BYTES, - row_buf, P1_ROW_BYTES); - } - - marker_build(); - bank_write(page, P1_MARKER_OFF, marker_buf, sizeof(marker_buf)); - - p1_winrest(0, 0, P1_SCREEN_H, P1_SCREEN_W, page, 0); - (void)getkey(); - - /* Две строки по 20 ячеек поверх середины полного экрана. */ - p1_winrest(14, 30, 2, 20, page, P1_MARKER_OFF); - (void)getkey(); - - mem_free_block(block); - p1_heading("WINREST test finished"); - cputs("Visual checks:\r\n"); - cputs(" 1. Full 80x32 frame had no shifts or holes.\r\n"); - cputs(" 2. Red/yellow 20x2 marker appeared at row 14, col 30.\r\n"); - cputs(" 3. Marker text started at offset 0x2000.\r\n"); - cputs("\r\nReturn PASS only records that both calls completed.\r\n"); - return 0; -} diff --git a/applications/Volkov/tests/p1_platform/p1_sys.c b/applications/Volkov/tests/p1_platform/p1_sys.c deleted file mode 100644 index 6c040af..0000000 --- a/applications/Volkov/tests/p1_platform/p1_sys.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * p1_sys.c — отсутствующие в libc сырые ESTEX wrappers для проб P1. - * - * ABI SDCC __sdcccall(1): int возвращается в DE, uint8_t — в A. Каждый RST - * сохраняет IX, потому что IX callee-saved. Ошибка ESTEX (CF=1, код в A) - * проходит через общий libc-хелпер __errno_set. - */ - -#include -#include "p1_platform.h" - -uint8_t p1_exec_exit_code; - -/* ESTEX DSKINFO $03: - * A=disk (0xFF=current) - * out: A=sectors/cluster, HL=total clusters, - * DE=free clusters, BC=bytes/sector. - */ -int p1_disk_info(uint8_t disk, P1DiskInfo *out) __naked -{ - (void)disk; - (void)out; - __asm - ;; A = disk, DE = out. - push ix - push de - ld c, #0x03 - rst #0x10 - jr c, _p1_di_error - - ;; Забрать out в IX, пока все выходные регистры DSS ещё целы. - pop ix - ld 0 (ix), a - ld 1 (ix), l - ld 2 (ix), h - ld 3 (ix), e - ld 4 (ix), d - ld 5 (ix), c - ld 6 (ix), b - pop ix - ld de, #0 - ret - - _p1_di_error: - pop de ; снять сохранённый out - pop ix - call __errno_set - ld de, #-1 - ret - __endasm; -} - -/* ESTEX EXEC $40. path_mode: 0 — короткое имя/PATH, 1 — передан путь. - * На успехе код дочерней программы сохраняется в p1_exec_exit_code. - */ -int p1_exec(uint8_t path_mode, const char *path) __naked -{ - (void)path_mode; - (void)path; - __asm - ;; A = path_mode, DE = path. Такой порядок не оставляет 8-битный - ;; второй аргумент на стеке в ABI SDCC. - push ix - ld b, a - ex de, hl - ld c, #0x40 - rst #0x10 - pop ix - jr c, _p1_exec_error - ld (_p1_exec_exit_code), a - ld de, #0 - ret - - _p1_exec_error: - call __errno_set - ld de, #-1 - ret - __endasm; -} - -/* ESTEX WAIT $42 возвращает код последней завершившейся программы в A. */ -uint8_t p1_wait_code(void) __naked -{ - __asm - push ix - ld c, #0x42 - rst #0x10 - pop ix - ret - __endasm; -} - -/* ESTEX WINREST $5A: - * D=row, E=col, H=height, L=width, B=physical page, - * IX=0xC000+offset. - * - * Раскладка аргументов подтверждена существующим tests/winrest и mdview2: - * A=row, L=col, стек +2 height, +3 width, +4 page, +5..6 offset. - */ -void p1_winrest(uint8_t row, uint8_t col, uint8_t height, uint8_t width, - uint8_t page, uint16_t offset) __naked -{ - (void)row; - (void)col; - (void)height; - (void)width; - (void)page; - (void)offset; - __asm - ld iy, #2 - add iy, sp - ld d, a - ld e, l - - push ix - ld l, 3 (iy) - ld h, 4 (iy) - ld bc, #0xC000 - add hl, bc - push hl - pop ix - - ld h, 0 (iy) - ld a, 1 (iy) - ld l, a - ld a, 2 (iy) - ld b, a - ld c, #0x5A - di - rst #0x10 - ei - pop ix - - ;; Снять 5 стековых байтов height,width,page,offset. - pop hl - inc sp - inc sp - inc sp - inc sp - inc sp - jp (hl) - __endasm; -} diff --git a/applications/Volkov/tests/p1_platform/p1child.c b/applications/Volkov/tests/p1_platform/p1child.c deleted file mode 100644 index 056f133..0000000 --- a/applications/Volkov/tests/p1_platform/p1child.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * p1child.c — дочерняя программа для проверки ESTEX EXEC. - * - * Намеренно меняет видеорежим, ничего не пишет на диск и возвращает 0x5A. - */ - -#include -#include -#include -#include -#include - -static char child_cwd[256]; - -int main(void) -{ - (void)settextmode(TEXT_MODE_40x32); - clrscr_attr(0x27); - gotoxy(0, 0); - textattr(0x2F); - cputs("P1CHILD.EXE\r\n\r\n"); - textattr(0x27); - cputs("EXEC reached child successfully.\r\n"); - cprintf("W1/W2/W3: %02X/%02X/%02X\r\n", - (unsigned)_io_page_w1, (unsigned)_io_page_w2, - (unsigned)_io_page_w3); - if (getcwd(child_cwd, sizeof(child_cwd)) != 0) { - cprintf("CWD: %s\r\n", child_cwd); - } else { - cputs("CWD: \r\n"); - } - cputs("\r\nPress any key to return 0x5A..."); - (void)getkey(); - return 0x5A; -} diff --git a/applications/Volkov/tests/p1_platform/p1probe.c b/applications/Volkov/tests/p1_platform/p1probe.c deleted file mode 100644 index d53e2d3..0000000 --- a/applications/Volkov/tests/p1_platform/p1probe.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * p1probe.c — интерактивный набор платформенных проб Sprinter Commander. - * - * Программа ничего не удаляет и не изменяет в текущем каталоге. EMM-тест - * пишет исключительно в выделенный блок памяти, дочерняя программа также - * не выполняет дисковых записей. - */ - -#include -#include -#include -#include "p1_platform.h" - -void p1_heading(const char *title) -{ - clrscr_attr(0x17); - gotoxy(0, 0); - textattr(0x1F); - cprintf("Sprinter Commander - P1 platform probe\r\n"); - textattr(0x17); - cprintf("%s\r\n\r\n", title); -} - -void p1_pause(void) -{ - textattr(0x1E); - cputs("\r\nPress any key to return to menu..."); - (void)getkey(); -} - -static void show_menu(void) -{ - p1_heading("Main menu"); - cputs("1 Directory and F_FIRST/F_NEXT\r\n"); - cputs("2 EMM allocation and page access\r\n"); - cputs("3 WINREST full/partial screen\r\n"); - cputs("4 Keyboard event codes\r\n"); - cputs("5 EXEC child and return\r\n"); - cputs("6 DSKINFO current disk\r\n"); - cputs("A Automatic non-visual probes (1, 2, 6)\r\n"); - cputs("0 Exit\r\n\r\n"); - textattr(0x1E); - cputs("Select: "); -} - -static void report_result(int rc) -{ - if (rc == 0) { - textattr(0x2F); - cputs("\r\nRESULT: PASS\r\n"); - } else { - textattr(0x4F); - cputs("\r\nRESULT: FAIL\r\n"); - } -} - -static void run_automatic(void) -{ - int failed = 0; - - p1_heading("Automatic non-visual probes"); - cputs("[1/3] Directory\r\n"); - if (p1_test_directory() != 0) failed++; - - cputs("\r\n[2/3] EMM\r\n"); - if (p1_test_emm() != 0) failed++; - - cputs("\r\n[3/3] DSKINFO\r\n"); - if (p1_test_disk_info() != 0) failed++; - - textattr(failed ? 0x4F : 0x2F); - cprintf("\r\nAutomatic result: %u failed\r\n", (unsigned)failed); - p1_pause(); -} - -int main(void) -{ - uint16_t key; - uint8_t ch; - int rc; - - if (settextmode(TEXT_MODE_80x32) != 0) { - puts("Cannot set 80x32 text mode."); - return 1; - } - - for (;;) { - show_menu(); - key = getkey(); - ch = (uint8_t)key; - if (ch >= 'a' && ch <= 'z') ch = (uint8_t)(ch - 'a' + 'A'); - - if (ch == '0' || ch == 0x1B) break; - if (ch == 'A') { - run_automatic(); - continue; - } - - rc = -1; - switch (ch) { - case '1': rc = p1_test_directory(); break; - case '2': rc = p1_test_emm(); break; - case '3': rc = p1_test_screen(); break; - case '4': rc = p1_test_keyboard(); break; - case '5': rc = p1_test_exec(); break; - case '6': rc = p1_test_disk_info(); break; - default: continue; - } - - report_result(rc); - p1_pause(); - } - - textattr(0x0F); - clrscr(); - return 0; -} diff --git a/applications/Volkov/tests/p2_palette/Makefile b/applications/Volkov/tests/p2_palette/Makefile deleted file mode 100644 index 6883e0b..0000000 --- a/applications/Volkov/tests/p2_palette/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# P2.2 — отдельная проба системных BIOS/ESTEX-вызовов текстовой палитры. - -EXAMPLE := p2palette -MEMORY := big -ALLOCS ?= 3000 -EXTRA_FLAGS := --safe --max-allocs $(ALLOCS) - -include $(CURDIR)/../../sdk.mk - -$(EXE): Makefile diff --git a/applications/Volkov/tests/p2_palette/p2palette.c b/applications/Volkov/tests/p2_palette/p2palette.c deleted file mode 100644 index da63b01..0000000 --- a/applications/Volkov/tests/p2_palette/p2palette.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * p2palette — минимальная проба многоцветной текстовой темы P2.2. - * - * Использует публичные системные text_pal_get/set_color(). Для каждого - * применяемого атрибута обычный и FLASH-планы задаются попарно одинаково. - * Исходные записи сохраняются и восстанавливаются перед выходом. - */ - -#include -#include - -#define SAMPLE_COUNT 6u -#define ATTR_UI COLOR(COLOR_BLACK, COLOR_CYAN) - -typedef struct { - uint8_t attr; - uint8_t paper_r; - uint8_t paper_g; - uint8_t paper_b; - uint8_t ink_r; - uint8_t ink_g; - uint8_t ink_b; - const char *name; -} ThemeSample; - -static const ThemeSample samples[SAMPLE_COUNT] = { - { COLOR(COLOR_WHITE, COLOR_BLUE), 0x00, 0x00, 0xA8, - 0xFF, 0xFF, 0xFF, "WHITE / BLUE" }, - { COLOR(COLOR_YELLOW, COLOR_GREEN), 0x00, 0xA8, 0x00, - 0xFF, 0xFF, 0x00, "YELLOW / GREEN" }, - { COLOR(COLOR_WHITE, COLOR_RED), 0xA8, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, "WHITE / RED" }, - { COLOR(COLOR_WHITE, COLOR_MAGENTA), 0xA8, 0x00, 0xA8, - 0xFF, 0xFF, 0xFF, "WHITE / MAGENTA" }, - { COLOR(COLOR_BLACK, COLOR_LIGHTGRAY),0xC0, 0xC0, 0xC0, - 0x00, 0x00, 0x00, "BLACK / LIGHTGRAY" }, - { ATTR_UI, 0x00, 0xA8, 0xA8, - 0x00, 0x00, 0x00, "BLACK / CYAN" } -}; - -/* [sample][plane][RGB]. Static: буферы BIOS остаются в W2, не на стеке. */ -static uint8_t saved[SAMPLE_COUNT][4u][3u]; -static uint8_t readback[SAMPLE_COUNT][4u][3u]; - -static void save_palette(void) -{ - uint8_t i; - uint8_t p; - for (i = 0u; i < SAMPLE_COUNT; i++) { - for (p = 0u; p < 4u; p++) { - text_pal_get_color(p, samples[i].attr, - &saved[i][p][0], &saved[i][p][1], - &saved[i][p][2]); - } - } -} -static void set_sample(uint8_t i) -{ - const ThemeSample *s = &samples[i]; - text_pal_set_color(TEXT_PAL_PAPER, s->attr, - s->paper_r, s->paper_g, s->paper_b); - text_pal_set_color(TEXT_PAL_BLINK_PAPER, s->attr, - s->paper_r, s->paper_g, s->paper_b); - text_pal_set_color(TEXT_PAL_INK, s->attr, - s->ink_r, s->ink_g, s->ink_b); - text_pal_set_color(TEXT_PAL_BLINK_INK, s->attr, - s->ink_r, s->ink_g, s->ink_b); -} - -static uint8_t verify_palette(void) -{ - uint8_t i; - uint8_t p; - uint8_t failed = 0u; - - for (i = 0u; i < SAMPLE_COUNT; i++) { - for (p = 0u; p < 4u; p++) { - const ThemeSample *s = &samples[i]; - uint8_t want_r = (p == TEXT_PAL_PAPER || - p == TEXT_PAL_BLINK_PAPER) ? - s->paper_r : s->ink_r; - uint8_t want_g = (p == TEXT_PAL_PAPER || - p == TEXT_PAL_BLINK_PAPER) ? - s->paper_g : s->ink_g; - uint8_t want_b = (p == TEXT_PAL_PAPER || - p == TEXT_PAL_BLINK_PAPER) ? - s->paper_b : s->ink_b; - - text_pal_get_color(p, s->attr, - &readback[i][p][0], &readback[i][p][1], - &readback[i][p][2]); - if (readback[i][p][0] != want_r || - readback[i][p][1] != want_g || - readback[i][p][2] != want_b) failed++; - } - } - return failed; -} - -static void restore_palette(void) -{ - uint8_t i; - uint8_t p; - for (i = 0u; i < SAMPLE_COUNT; i++) { - for (p = 0u; p < 4u; p++) { - text_pal_set_color(p, samples[i].attr, - saved[i][p][0], saved[i][p][1], - saved[i][p][2]); - } - } -} - -static void fill_row(uint8_t y, uint8_t attr, const char *name) -{ - uint8_t x; - for (x = 0u; x < 80u; x++) wrchar(x, y, ' ', attr); - for (x = 0u; name[x] && x < 30u; x++) { - wrchar((uint8_t)(4u + x), y, name[x], attr); - } - for (x = 42u; x < 76u; x++) { - wrchar(x, y, (x & 1u) ? '#' : '@', attr); - } -} - -static void draw_screen(uint8_t failed) -{ - uint8_t i; - - clrscr_attr(ATTR_UI); - textattr(ATTR_UI); - gotoxy(1u, 0u); - cputs("P2.2 SYSTEM TEXT PALETTE PROBE - BIOS CALLS ARE ALLOWED"); - gotoxy(1u, 2u); - cprintf("Readback of 6 attrs x 4 planes: %s (%u mismatches)", - failed ? "FAIL" : "PASS", (unsigned)failed); - gotoxy(1u, 3u); - cputs("Each PAPER pair and INK pair is identical; scene must not flash."); - - for (i = 0u; i < SAMPLE_COUNT; i++) { - fill_row((uint8_t)(6u + i * 3u), samples[i].attr, samples[i].name); - fill_row((uint8_t)(7u + i * 3u), samples[i].attr, - "same attribute, second row"); - } - - gotoxy(1u, 26u); - cputs("If any coloured band disappears, compare MAME phase with readback."); - gotoxy(1u, 28u); - cputs("Press a key to restore every saved palette entry and exit."); -} - -int main(void) -{ - uint8_t previous_mode = gettextmode(); - uint8_t i; - uint8_t failed; - - if (settextmode(TEXT_MODE_80x32) != 0) return 1; - save_palette(); - for (i = 0u; i < SAMPLE_COUNT; i++) set_sample(i); - failed = verify_palette(); - draw_screen(failed); - (void)getkey(); - restore_palette(); - textattr(COLOR(COLOR_WHITE, COLOR_BLACK)); - clrscr(); - if (previous_mode == TEXT_MODE_40x32 || - previous_mode == TEXT_MODE_80x32) { - (void)settextmode(previous_mode); - } - return failed ? 2 : 0; -} diff --git a/applications/Volkov/tests/p2_screen/Makefile b/applications/Volkov/tests/p2_screen/Makefile deleted file mode 100644 index 9602872..0000000 --- a/applications/Volkov/tests/p2_screen/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# P2.1 — координатный экранный backend Sprinter Commander. -# -# Тест использует системные ESTEX-функции WINREST/RDCHAR/SCROLL, не -# открывает BIOS-окна и не обращается к текстовой VRAM напрямую. - -EXAMPLE := p2screen -MEMORY := big -ALLOCS ?= 3000 -EXTRA_FLAGS := --safe --max-allocs $(ALLOCS) -EXTRA_SRCS := p2_platform.c - -include $(CURDIR)/../../sdk.mk - -$(EXE): Makefile p2_screen.h sc_glyphs.h diff --git a/applications/Volkov/tests/p2_screen/p2_platform.c b/applications/Volkov/tests/p2_screen/p2_platform.c deleted file mode 100644 index 849e2d3..0000000 --- a/applications/Volkov/tests/p2_screen/p2_platform.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * p2_platform.c — пакетный координатный вывод screen model через ESTEX. - * - * WINREST 5Ah не принимает идентификатор BIOS-окна: D/E/H/L задают - * абсолютный прямоугольник активной текстовой страницы, B:IX — источник. - * ABI SDCC __sdcccall(1) подтверждён P1-пробой. - */ - -#include -#include "p2_screen.h" - -void p2_winrest(uint8_t row, uint8_t col, uint8_t height, uint8_t width, - uint8_t page, uint16_t offset) __naked -{ - (void)row; - (void)col; - (void)height; - (void)width; - (void)page; - (void)offset; - __asm - ld iy, #2 - add iy, sp - ld d, a - ld e, l - - push ix - ld l, 3 (iy) - ld h, 4 (iy) - ld bc, #0xC000 - add hl, bc - push hl - pop ix - - ld h, 0 (iy) - ld a, 1 (iy) - ld l, a - ld a, 2 (iy) - ld b, a - ld c, #0x5A - di - rst #0x10 - ei - pop ix - - pop hl - inc sp - inc sp - inc sp - inc sp - inc sp - jp (hl) - __endasm; -} diff --git a/applications/Volkov/tests/p2_screen/p2_screen.h b/applications/Volkov/tests/p2_screen/p2_screen.h deleted file mode 100644 index 85379d6..0000000 --- a/applications/Volkov/tests/p2_screen/p2_screen.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * p2_screen.h — общий контракт координатной экранной пробы P2.1. - */ - -#ifndef P2_SCREEN_API_H -#define P2_SCREEN_API_H - -#include - -#define P2_SCREEN_W 80u -#define P2_SCREEN_H 32u -#define P2_ROW_BYTES 160u -#define P2_MODEL_BYTES 5120u -#define P2_MARKER_OFF 0x2000u - -typedef struct { - uint8_t ch; - uint8_t attr; -} P2Cell; - -typedef char P2Cell_must_be_2_bytes[(sizeof(P2Cell) == 2) ? 1 : -1]; - -void p2_winrest(uint8_t row, uint8_t col, uint8_t height, uint8_t width, - uint8_t page, uint16_t offset); - -#endif diff --git a/applications/Volkov/tests/p2_screen/p2screen.c b/applications/Volkov/tests/p2_screen/p2screen.c deleted file mode 100644 index f2c47c8..0000000 --- a/applications/Volkov/tests/p2_screen/p2screen.c +++ /dev/null @@ -1,432 +0,0 @@ -/* - * p2screen — проверка системного координатного backend Commander P2.1. - * - * Экранная модель лежит в одной EMM-странице как 80x32 пар (char, attr). - * Вывод: WINREST; проверка: RDCHAR; сдвиг: прямоугольный SCROLL 55h. - * BIOS window descriptors и прямой доступ к VRAM не используются. - */ - -#include -#include -#include -#include -#include -#include "p2_screen.h" -#include "sc_glyphs.h" - -#define LEFT_X 1u -#define RIGHT_X 41u -#define PANEL_Y 1u -#define PANEL_W 38u -#define PANEL_H 27u - -#define ATTR_UI COLOR(COLOR_BLACK, COLOR_CYAN) -#define ATTR_FRAME ATTR_UI -#define ATTR_TITLE ATTR_UI -#define ATTR_TEXT ATTR_UI -#define ATTR_DIM ATTR_UI -#define ATTR_ACTIVE ATTR_UI -#define ATTR_MARK1 ATTR_UI -#define ATTR_MARK2 ATTR_UI -#define ATTR_PASS ATTR_UI -#define ATTR_FAIL ATTR_UI -#define ATTR_KEYS ATTR_UI - -static P2Cell row_buf[P2_SCREEN_W]; -static P2Cell marker_buf[40u]; -static uint8_t fail_x; -static uint8_t fail_y; -static uint16_t fail_want; -static uint16_t fail_got; -static uint8_t w3_bad; - -static uint16_t cell_offset(uint8_t x, uint8_t y) -{ - return (uint16_t)y * P2_ROW_BYTES + (uint16_t)x * 2u; -} - -static void row_fill(uint8_t ch, uint8_t attr) -{ - uint8_t x; - for (x = 0; x < P2_SCREEN_W; x++) { - row_buf[x].ch = ch; - row_buf[x].attr = attr; - } -} - -static void cells_fill(P2Cell *cells, uint8_t count, - uint8_t ch, uint8_t attr) -{ - uint8_t i; - for (i = 0; i < count; i++) { - cells[i].ch = ch; - cells[i].attr = attr; - } -} - -static void cells_text(P2Cell *cells, uint8_t width, uint8_t x, - const char *text, uint8_t attr) -{ - while (*text && x < width) { - cells[x].ch = (uint8_t)*text++; - cells[x].attr = attr; - x++; - } -} - -static void cells_bytes(P2Cell *cells, uint8_t width, uint8_t x, - const uint8_t *bytes, uint8_t count, uint8_t attr) -{ - while (count-- && x < width) { - cells[x].ch = *bytes++; - cells[x].attr = attr; - x++; - } -} - -static void cells_dec2(P2Cell *cells, uint8_t width, uint8_t x, - uint8_t value, uint8_t attr) -{ - if (x + 1u >= width) return; - cells[x].ch = (uint8_t)('0' + (value / 10u) % 10u); - cells[x].attr = attr; - cells[x + 1u].ch = (uint8_t)('0' + value % 10u); - cells[x + 1u].attr = attr; -} - -static void cells_dec4(P2Cell *cells, uint8_t width, uint8_t x, - uint16_t value, uint8_t attr) -{ - uint16_t div = 1000u; - uint8_t i; - for (i = 0; i < 4u && x + i < width; i++) { - cells[x + i].ch = (uint8_t)('0' + (value / div) % 10u); - cells[x + i].attr = attr; - div /= 10u; - } -} - -static void row_panel_frame(uint8_t left, uint8_t y) -{ - uint8_t x; - uint8_t right = (uint8_t)(left + 39u); - - if (y == 0u || y == 28u) { - row_buf[left].ch = (y == 0u) ? SC_G_TL : SC_G_BL; - row_buf[right].ch = (y == 0u) ? SC_G_TR : SC_G_BR; - row_buf[left].attr = ATTR_FRAME; - row_buf[right].attr = ATTR_FRAME; - for (x = (uint8_t)(left + 1u); x < right; x++) { - row_buf[x].ch = SC_G_H; - row_buf[x].attr = ATTR_FRAME; - } - } else { - row_buf[left].ch = SC_G_V; - row_buf[right].ch = SC_G_V; - row_buf[left].attr = ATTR_FRAME; - row_buf[right].attr = ATTR_FRAME; - } -} - -static void build_model(uint8_t page) -{ - static const uint8_t boxes[] = { - SC_G_H, SC_G_V, SC_G_TL, SC_G_TR, SC_G_BL, SC_G_BR, - SC_G_TM, SC_G_BM, SC_G_ML, SC_G_MR, SC_G_X - }; - static const uint8_t arrows[] = { - SC_G_ARROW_UP, SC_G_ARROW_DOWN, SC_G_ARROW_LEFT, SC_G_ARROW_RIGHT - }; - static const uint8_t blocks[] = { - SC_G_SHADE_LIGHT, SC_G_SHADE_MEDIUM, SC_G_SHADE_DARK, - SC_G_BLOCK, SC_G_HALF_UP, SC_G_HALF_DOWN, - SC_G_HALF_LEFT, SC_G_HALF_RIGHT, SC_G_BULLET, - SC_G_CHECK, SC_G_SQUARE - }; - uint8_t y; - - for (y = 0; y < P2_SCREEN_H; y++) { - row_fill(' ', (y < 29u) ? ATTR_TEXT : ATTR_KEYS); - - if (y <= 28u) { - row_panel_frame(0u, y); - row_panel_frame(40u, y); - } - - if (y == 0u) { - cells_text(row_buf, P2_SCREEN_W, 11u, - " LEFT PANEL ", ATTR_TITLE); - cells_text(row_buf, P2_SCREEN_W, 50u, - " RIGHT PANEL ", ATTR_TITLE); - } else if (y >= 1u && y <= 27u) { - cells_text(row_buf, P2_SCREEN_W, 2u, "L ROW", ATTR_TEXT); - cells_dec2(row_buf, P2_SCREEN_W, 8u, y, ATTR_ACTIVE); - cells_text(row_buf, P2_SCREEN_W, 42u, "R ROW", ATTR_DIM); - cells_dec2(row_buf, P2_SCREEN_W, 48u, y, ATTR_DIM); - } - - if (y == 20u) { - cells_text(row_buf, P2_SCREEN_W, 2u, "BOX", ATTR_TITLE); - cells_bytes(row_buf, P2_SCREEN_W, 6u, boxes, - (uint8_t)sizeof(boxes), ATTR_FRAME); - } else if (y == 21u) { - cells_text(row_buf, P2_SCREEN_W, 2u, "ARROWS", ATTR_TITLE); - cells_bytes(row_buf, P2_SCREEN_W, 10u, arrows, - (uint8_t)sizeof(arrows), ATTR_FRAME); - } else if (y == 22u) { - cells_text(row_buf, P2_SCREEN_W, 2u, "BLOCKS", ATTR_TITLE); - cells_bytes(row_buf, P2_SCREEN_W, 10u, blocks, - (uint8_t)sizeof(blocks), ATTR_FRAME); - } else if (y == 28u) { - cells_text(row_buf, P2_SCREEN_W, 13u, " 27 rows ", ATTR_TITLE); - cells_text(row_buf, P2_SCREEN_W, 53u, " 27 rows ", ATTR_TITLE); - } else if (y == 29u) { - cells_text(row_buf, P2_SCREEN_W, 1u, - "P2.1 SYSTEM COORDINATE BACKEND - NO BIOS WINDOWS, NO VRAM ACCESS", - ATTR_KEYS); - } else if (y == 30u) { - cells_text(row_buf, P2_SCREEN_W, 1u, - "STAGE 1: FULL WINREST + RDCHAR VERIFY", - ATTR_KEYS); - } else if (y == 31u) { - cells_text(row_buf, P2_SCREEN_W, 1u, - "SYSTEM ATTR 0x30: NO PALETTE WRITES KEY: next stage", - ATTR_KEYS); - } - - bank_write(page, (uint16_t)y * P2_ROW_BYTES, - row_buf, P2_ROW_BYTES); - } -} - -static void build_marker(uint8_t page) -{ - cells_fill(marker_buf, 20u, ' ', ATTR_MARK1); - cells_fill(marker_buf + 20u, 20u, ' ', ATTR_MARK2); - cells_text(marker_buf, 20u, 0u, "PARTIAL RECT PASS", ATTR_MARK1); - cells_text(marker_buf + 20u, 20u, 0u, "OFFSET 0x2000 OK", ATTR_MARK2); - bank_write(page, P2_MARKER_OFF, marker_buf, sizeof(marker_buf)); -} - -static void model_apply_marker(uint8_t page, uint8_t x, uint8_t y) -{ - uint8_t saved = _io_page_w3; - P2Cell *model; - - sprinter_page_w3(page); - model = (P2Cell *)0xC000u; - memcpy(model + (uint16_t)y * P2_SCREEN_W + x, - marker_buf, 20u * sizeof(P2Cell)); - memcpy(model + (uint16_t)(y + 1u) * P2_SCREEN_W + x, - marker_buf + 20u, 20u * sizeof(P2Cell)); - sprinter_page_w3(saved); -} - -static void model_status(uint8_t page, const char *text, uint8_t ok) -{ - bank_read(page, 30u * P2_ROW_BYTES, row_buf, P2_ROW_BYTES); - cells_fill(row_buf, P2_SCREEN_W, ' ', ATTR_KEYS); - cells_text(row_buf, P2_SCREEN_W, 1u, text, ATTR_KEYS); - cells_text(row_buf, P2_SCREEN_W, 62u, - ok ? "PASS" : "FAIL", ok ? ATTR_PASS : ATTR_FAIL); - if (!ok) { - cells_text(row_buf, P2_SCREEN_W, 68u, "AT", ATTR_FAIL); - cells_dec2(row_buf, P2_SCREEN_W, 71u, fail_x, ATTR_FAIL); - row_buf[73u].ch = ','; - row_buf[73u].attr = ATTR_FAIL; - cells_dec2(row_buf, P2_SCREEN_W, 74u, fail_y, ATTR_FAIL); - } - bank_write(page, 30u * P2_ROW_BYTES, row_buf, P2_ROW_BYTES); -} - -static void present_rect(uint8_t page, uint8_t x, uint8_t y, - uint8_t width, uint8_t height, uint16_t offset) -{ - uint8_t saved = _io_page_w3; - p2_winrest(y, x, height, width, page, offset); - if (_io_page_w3 != saved) w3_bad = 1u; -} - -static void present_model_row(uint8_t page, uint8_t x, - uint8_t y, uint8_t width) -{ - present_rect(page, x, y, width, 1u, cell_offset(x, y)); -} - -static uint8_t verify_model(uint8_t page) -{ - uint8_t saved = _io_page_w3; - uint8_t x; - uint8_t y; - - for (y = 0; y < P2_SCREEN_H; y++) { - bank_read(page, (uint16_t)y * P2_ROW_BYTES, - row_buf, P2_ROW_BYTES); - for (x = 0; x < P2_SCREEN_W; x++) { - uint16_t got = rdchar(x, y); - uint16_t want = ((uint16_t)row_buf[x].attr << 8) | - row_buf[x].ch; - if (got != want) { - fail_x = x; - fail_y = y; - fail_want = want; - fail_got = got; - return 0u; - } - } - } - - if (_io_page_w3 != saved) w3_bad = 1u; - return w3_bad ? 0u : 1u; -} - -static uint8_t report_verify(uint8_t page, const char *stage) -{ - uint8_t ok = verify_model(page); - model_status(page, stage, ok); - present_model_row(page, 0u, 30u, P2_SCREEN_W); - return ok; -} - -static void mapped_new_row(P2Cell *line, uint8_t width, - uint8_t direction, uint16_t sequence) -{ - cells_fill(line, width, ' ', ATTR_ACTIVE); - cells_text(line, width, 1u, - (direction == 1u) ? "NEW BOTTOM ROW" : "NEW TOP ROW", - ATTR_ACTIVE); - cells_dec4(line, width, 17u, sequence, ATTR_ACTIVE); -} - -static void model_scroll(uint8_t page, uint8_t x, uint8_t y, - uint8_t width, uint8_t height, - uint8_t direction, uint16_t sequence) -{ - uint8_t saved = _io_page_w3; - P2Cell *model; - uint8_t i; - - sprinter_page_w3(page); - model = (P2Cell *)0xC000u; - - if (direction == 1u) { - for (i = 0; i + 1u < height; i++) { - memmove(model + (uint16_t)(y + i) * P2_SCREEN_W + x, - model + (uint16_t)(y + i + 1u) * P2_SCREEN_W + x, - (uint16_t)width * sizeof(P2Cell)); - } - mapped_new_row(model + (uint16_t)(y + height - 1u) * P2_SCREEN_W + x, - width, direction, sequence); - } else { - i = (uint8_t)(height - 1u); - while (i != 0u) { - memmove(model + (uint16_t)(y + i) * P2_SCREEN_W + x, - model + (uint16_t)(y + i - 1u) * P2_SCREEN_W + x, - (uint16_t)width * sizeof(P2Cell)); - i--; - } - mapped_new_row(model + (uint16_t)y * P2_SCREEN_W + x, - width, direction, sequence); - } - - sprinter_page_w3(saved); -} - -static void system_scroll(uint8_t page, uint8_t x, uint8_t y, - uint8_t width, uint8_t height, - uint8_t direction, uint16_t sequence) -{ - uint8_t saved; - uint8_t open_y; - - model_scroll(page, x, y, width, height, direction, sequence); - saved = _io_page_w3; - scroll(x, y, width, height, direction, 0u); - if (_io_page_w3 != saved) w3_bad = 1u; - - open_y = (direction == 1u) ? (uint8_t)(y + height - 1u) : y; - present_model_row(page, x, open_y, width); -} - -static uint8_t wait_stage(void) -{ - uint16_t key = getkey(); - return ((uint8_t)key == 0x1Bu) ? 0u : 1u; -} - -int main(void) -{ - uint8_t previous_mode = gettextmode(); - uint8_t block; - uint8_t page; - uint8_t ok = 1u; - uint16_t i; - - (void)fail_want; - (void)fail_got; - - if (settextmode(TEXT_MODE_80x32) != 0) return 1; - - block = mem_alloc_pages(1u); - if (block == 0u) { - if (previous_mode == TEXT_MODE_40x32 || - previous_mode == TEXT_MODE_80x32) { - (void)settextmode(previous_mode); - } - return 2; - } - page = mem_get_page(block, 0u); - if (page == 0u) { - mem_free_block(block); - if (previous_mode == TEXT_MODE_40x32 || - previous_mode == TEXT_MODE_80x32) { - (void)settextmode(previous_mode); - } - return 3; - } - build_model(page); - build_marker(page); - - present_rect(page, 0u, 0u, P2_SCREEN_W, P2_SCREEN_H, 0u); - if (!report_verify(page, "STAGE 1: FULL WINREST + RDCHAR")) ok = 0u; - if (!wait_stage()) goto done; - - present_rect(page, 30u, 14u, 20u, 2u, P2_MARKER_OFF); - model_apply_marker(page, 30u, 14u); - if (!report_verify(page, "STAGE 2: PARTIAL OFFSET 0x2000")) ok = 0u; - if (!wait_stage()) goto done; - - build_model(page); - present_rect(page, 0u, 0u, P2_SCREEN_W, P2_SCREEN_H, 0u); - system_scroll(page, LEFT_X, PANEL_Y, PANEL_W, PANEL_H, 1u, 1u); - if (!report_verify(page, "STAGE 3: LEFT RECT SCROLL UP")) ok = 0u; - if (!wait_stage()) goto done; - - system_scroll(page, RIGHT_X, PANEL_Y, PANEL_W, PANEL_H, 2u, 2u); - if (!report_verify(page, "STAGE 4: RIGHT RECT SCROLL DOWN")) ok = 0u; - if (!wait_stage()) goto done; - - model_status(page, "STAGE 5: 1000 RECT SCROLL OPERATIONS", 1u); - present_model_row(page, 0u, 30u, P2_SCREEN_W); - for (i = 0u; i < 1000u; i++) { - uint8_t direction = (i & 1u) ? 2u : 1u; - system_scroll(page, LEFT_X, PANEL_Y, PANEL_W, PANEL_H, - direction, (uint16_t)(i + 1u)); - } - if (!report_verify(page, "FINAL: MODEL/RDCHAR/W3 AFTER 1000")) ok = 0u; - if (!ok) { - model_status(page, "FINAL P2.1 RESULT", 0u); - present_model_row(page, 0u, 30u, P2_SCREEN_W); - } - (void)wait_stage(); - -done: - mem_free_block(block); - textattr(COLOR(COLOR_WHITE, COLOR_BLACK)); - clrscr(); - if (previous_mode == TEXT_MODE_40x32 || - previous_mode == TEXT_MODE_80x32) { - (void)settextmode(previous_mode); - } - return ok ? 0 : 1; -} diff --git a/applications/Volkov/tests/p2_screen/sc_glyphs.h b/applications/Volkov/tests/p2_screen/sc_glyphs.h deleted file mode 100644 index 0d293ea..0000000 --- a/applications/Volkov/tests/p2_screen/sc_glyphs.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * sc_glyphs.h — raw-коды CP866/CP437 для интерфейса Commander. - * - * Байты ниже являются экранными глифами, включая управляющий диапазон - * 0x18..0x1B. Их следует класть в ScCell или передавать WRCHAR/WINREST, - * но не печатать функцией, интерпретирующей CR/LF/BS/TAB. - */ - -#ifndef SC_GLYPHS_H -#define SC_GLYPHS_H - -#define SC_G_H 0xC4u -#define SC_G_V 0xB3u -#define SC_G_TL 0xDAu -#define SC_G_TR 0xBFu -#define SC_G_BL 0xC0u -#define SC_G_BR 0xD9u -#define SC_G_TM 0xC2u -#define SC_G_BM 0xC1u -#define SC_G_ML 0xC3u -#define SC_G_MR 0xB4u -#define SC_G_X 0xC5u - -#define SC_G_ARROW_UP 0x18u -#define SC_G_ARROW_DOWN 0x19u -#define SC_G_ARROW_RIGHT 0x1Au -#define SC_G_ARROW_LEFT 0x1Bu - -#define SC_G_SHADE_LIGHT 0xB0u -#define SC_G_SHADE_MEDIUM 0xB1u -#define SC_G_SHADE_DARK 0xB2u -#define SC_G_BLOCK 0xDBu -#define SC_G_HALF_UP 0xDFu -#define SC_G_HALF_DOWN 0xDCu -#define SC_G_HALF_LEFT 0xDDu -#define SC_G_HALF_RIGHT 0xDEu -#define SC_G_BULLET 0xF9u -#define SC_G_CHECK 0xFBu -#define SC_G_SQUARE 0xFEu - -#endif diff --git a/applications/Volkov/tests/p3_data/INNER.TXT b/applications/Volkov/tests/p3_data/INNER.TXT deleted file mode 100644 index b897cf7..0000000 --- a/applications/Volkov/tests/p3_data/INNER.TXT +++ /dev/null @@ -1 +0,0 @@ -File inside TESTDIR used to verify independent panel paths and synthetic parent. diff --git a/applications/Volkov/tests/p3_data/P3ROOT.TXT b/applications/Volkov/tests/p3_data/P3ROOT.TXT deleted file mode 100644 index 98d0bb5..0000000 --- a/applications/Volkov/tests/p3_data/P3ROOT.TXT +++ /dev/null @@ -1 +0,0 @@ -Sprinter Commander P3 root fixture. diff --git a/applications/Volkov/tests/p3_data/README.md b/applications/Volkov/tests/p3_data/README.md deleted file mode 100644 index 9a5776c..0000000 --- a/applications/Volkov/tests/p3_data/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Данные MAME-сценария P3 - -После `make floppy` сценарий создаёт на тестовой FAT12-дискете каталог -`TESTDIR`, кладёт в него `INNER.TXT`, а в корень — `P3ROOT.TXT` и `ZETA.BIN`. - -Ожидаемая сортировка корня: каталог `TESTDIR`, затем файлы по имени. Внутри -`TESTDIR` Commander сам добавляет синтетическую запись `..` перед -`INNER.TXT`; DSS-записи `.`/`..` в EMM не копируются. - -Fixture также является регрессией wildcard: первый P3-прогон с `ffirst("*")` -увидел только `TESTDIR`. Принятый источник обязан использовать `*.*`, иначе -DSS не возвращает имена с расширением. diff --git a/applications/Volkov/tests/p3_data/ZETA.BIN b/applications/Volkov/tests/p3_data/ZETA.BIN deleted file mode 100644 index 94ea3ed..0000000 --- a/applications/Volkov/tests/p3_data/ZETA.BIN +++ /dev/null @@ -1 +0,0 @@ -Second root file used to verify directory-first and alphabetical sorting. diff --git a/applications/Volkov/tests/p3_dir_probe/Makefile b/applications/Volkov/tests/p3_dir_probe/Makefile deleted file mode 100644 index 12f2dfd..0000000 --- a/applications/Volkov/tests/p3_dir_probe/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# Диагностическая проба предельного каталога P3. - -EXAMPLE := p3dir -MEMORY := small -ALLOCS ?= 3000 -EXTRA_FLAGS := --safe --max-allocs $(ALLOCS) - -include $(CURDIR)/../../sdk.mk - -$(EXE): Makefile diff --git a/applications/Volkov/tests/p3_dir_probe/p3dir.c b/applications/Volkov/tests/p3_dir_probe/p3dir.c deleted file mode 100644 index 1a0898f..0000000 --- a/applications/Volkov/tests/p3_dir_probe/p3dir.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * p3dir.c — изолированная диагностика большого FAT-каталога DSS. - * - * Проба ничего не изменяет: входит в BIGDIR, полностью исчерпывает - * F_FIRST/F_NEXT, печатает фактический предел и проверяет возврат в корень. - */ - -#include -#include -#include -#include -#include -#include -#include - -#define FIND_ATTRS (FA_RDONLY | FA_HIDDEN | FA_SYSTEM | FA_DIREC | FA_ARCH) - -static ffblk_t find_buf; -static char cwd[256]; -static char last_name[13]; -static char name_511[13]; -static char name_512[13]; -static char name_513[13]; -static uint16_t enum_count; -static int enum_errno; - -static void save_name(char *dst) -{ - strncpy(dst, find_buf.found_name, 12u); - dst[12] = '\0'; -} - -int main(void) -{ - int rc; - - (void)settextmode(TEXT_MODE_80x32); - clrscr_attr(0x17); - gotoxy(0, 0); - textattr(0x1Fu); - cputs("P3 BIGDIR diagnostic\r\n\r\n"); - textattr(0x17u); - - errno = 0; - rc = chdir("\\BIGDIR"); - printf("1 chdir BIGDIR: rc=%d errno=%d\n", rc, errno); - if (rc != 0) goto done; - - errno = 0; - if (getcwd(cwd, (uint16_t)sizeof(cwd))) - printf("2 cwd: %s\n", cwd); - else - printf("2 getcwd failed: errno=%d\n", errno); - - errno = 0; - rc = ffirst("*.*", &find_buf, FIND_ATTRS); - printf("3 ffirst: rc=%d errno=%d\n", rc, errno); - enum_count = 0u; - if (rc == 0) { - for (;;) { - enum_count++; - save_name(last_name); - if (enum_count == 511u) save_name(name_511); - if (enum_count == 512u) save_name(name_512); - if (enum_count == 513u) save_name(name_513); - errno = 0; - if (fnext(&find_buf) != 0) break; - if (enum_count == 2000u) { - puts("Loop guard reached."); - break; - } - } - } - enum_errno = errno; - printf("4 enum: count=%u errno=%d last=%s\n", - (unsigned)enum_count, enum_errno, last_name); - printf("5 boundary: 511=%s 512=%s 513=%s\n", - name_511, name_512, name_513); - - errno = 0; - rc = chdir("\\"); - printf("6 chdir root: rc=%d errno=%d\n", rc, errno); - errno = 0; - if (getcwd(cwd, (uint16_t)sizeof(cwd))) - printf("7 cwd: %s\n", cwd); - else - printf("7 getcwd failed: errno=%d\n", errno); - -done: - textattr(0x1Eu); - cputs("\r\nProbe stopped; MAME test will take a snapshot."); - for (;;) (void)getkey(); -} diff --git a/applications/Volkov/tests/p3_enum_matrix/Makefile b/applications/Volkov/tests/p3_enum_matrix/Makefile deleted file mode 100644 index 6302b04..0000000 --- a/applications/Volkov/tests/p3_enum_matrix/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# Матрица влияния W3/EMM на длинный F_FIRST/F_NEXT. - -EXAMPLE := p3map -MEMORY := big -ALLOCS ?= 3000 -EXTRA_FLAGS := --safe --max-allocs $(ALLOCS) --bank 1=bank1_enum.c - -include $(CURDIR)/../../sdk.mk - -$(EXE): Makefile bank1_enum.c diff --git a/applications/Volkov/tests/p3_enum_matrix/bank1_enum.c b/applications/Volkov/tests/p3_enum_matrix/bank1_enum.c deleted file mode 100644 index df14333..0000000 --- a/applications/Volkov/tests/p3_enum_matrix/bank1_enum.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * bank1_enum.c — отделяет влияние адреса ffblk от переключений W3. - * - * Весь перебор исполняется при подключённом BANK1, а диагностический DTA - * занимает последние 256 байт W1. Это историческая матрица, доказавшая, - * что ни адрес DTA, ни чтение/запись W3 не снимают предел DSS. mode=0 не - * трогает W3, mode=1 читает байт EMM, mode=2 пишет 24 байта EMM. - */ - -#include -#include -#include -#include - -#define FIND_ATTRS (FA_RDONLY | FA_HIDDEN | FA_SYSTEM | FA_DIREC | FA_ARCH) - -__at (0x7F00) static ffblk_t find_buf; - -/* Копии wrapper находятся в том же W1, что и цикл. Так проверяется не - * только адрес ffblk, но и адрес самой инструкции RST/возврата из DSS. */ -static int p3_ffirst_local(const char *pattern, ffblk_t *buf, - uint8_t attrib) __naked -{ - (void)pattern; (void)buf; (void)attrib; - __asm - ld iy, #2 - add iy, sp - ld a, 0 (iy) - push ix - ld bc, #0x0119 - rst #0x10 - pop ix - pop hl - inc sp - jr c, p3_ffirst_fail$ - ld de, #0 - jp (hl) - p3_ffirst_fail$: - call __errno_set - ld de, #-1 - jp (hl) - __endasm; -} - -static int p3_fnext_local(ffblk_t *buf) __naked -{ - (void)buf; - __asm - push ix - ex de, hl - ld c, #0x1A - rst #0x10 - pop ix - jr c, p3_fnext_fail$ - ld de, #0 - ret - p3_fnext_fail$: - call __errno_set - ld de, #-1 - ret - __endasm; -} - -uint16_t p3_enum_mode(uint8_t mode, uint8_t page, int *end_errno) __banked -{ - uint16_t count = 0u; - uint8_t payload[24]; - uint16_t offset; - - errno = 0; - if ((mode >= 3u ? - p3_ffirst_local("*.*", &find_buf, FIND_ATTRS) : - ffirst("*.*", &find_buf, FIND_ATTRS)) == 0) { - for (;;) { - count++; - if (mode == 1u) { - payload[0] = bank_load_byte(page, 0u); - } else if (mode == 2u || mode == 4u) { - offset = (uint16_t)(((count - 1u) % 640u) * 24u); - bank_write(page, offset, payload, 24u); - } - errno = 0; - if ((mode >= 3u ? p3_fnext_local(&find_buf) : - fnext(&find_buf)) != 0) break; - if (count == 2000u) break; - } - } - *end_errno = errno; - return count; -} diff --git a/applications/Volkov/tests/p3_enum_matrix/p3map.c b/applications/Volkov/tests/p3_enum_matrix/p3map.c deleted file mode 100644 index dd18eb7..0000000 --- a/applications/Volkov/tests/p3_enum_matrix/p3map.c +++ /dev/null @@ -1,56 +0,0 @@ -/* p3map.c — экранный отчёт матрицы длинного перебора каталога. */ - -#include -#include -#include -#include -#include -#include - -uint16_t p3_enum_mode(uint8_t mode, uint8_t page, int *end_errno) __banked; - -int main(void) -{ - uint8_t block; - uint8_t page; - uint8_t mode; - uint16_t count; - int end_errno; - - (void)settextmode(TEXT_MODE_80x32); - clrscr_attr(0x17u); - gotoxy(0u, 0u); - textattr(0x1Fu); - cputs("P3 F_NEXT / W3 matrix\r\n\r\n"); - textattr(0x17u); - - block = mem_alloc_pages(1u); - page = block ? mem_get_page(block, 0u) : 0u; - printf("EMM: block=%u page=%02X errno=%d\n", - (unsigned)block, (unsigned)page, errno); - if (!block || !page) goto done; - - for (mode = 0u; mode != 5u; mode++) { - errno = 0; - if (chdir("\\BIGDIR") != 0) { - printf("mode %u: chdir error=%d\n", (unsigned)mode, errno); - break; - } - count = p3_enum_mode(mode, page, &end_errno); - printf("mode %u: count=%u end_errno=%d\n", - (unsigned)mode, (unsigned)count, end_errno); - errno = 0; - if (chdir("\\") != 0) { - printf("root after mode %u: error=%d\n", - (unsigned)mode, errno); - break; - } - } - - mem_free_block(block); - -done: - textattr(0x1Eu); - cputs("\r\nProbe stopped; MAME test will take a snapshot."); - for (;;) (void)getkey(); -} diff --git a/applications/Volkov/tests/p3_scan_probe/Makefile b/applications/Volkov/tests/p3_scan_probe/Makefile deleted file mode 100644 index 2c52f01..0000000 --- a/applications/Volkov/tests/p3_scan_probe/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# Полный scan/store/sort большого каталога теми же модулями, что Commander. - -EXAMPLE := p3scan -MEMORY := big -ALLOCS ?= 3000 -EXTRA_FLAGS := --safe --max-allocs $(ALLOCS) -I ../../include \ - --bank 1=../../src/sc_source_fs.c \ - --bank 1=../../src/sc_sort.c -EXTRA_SRCS := ../../src/sc_panel.c ../../src/sc_store.c - -include $(CURDIR)/../../sdk.mk - -$(EXE): Makefile ../../src/sc_source_fs.c ../../src/sc_sort.c diff --git a/applications/Volkov/tests/p3_scan_probe/p3scan.c b/applications/Volkov/tests/p3_scan_probe/p3scan.c deleted file mode 100644 index 64b579e..0000000 --- a/applications/Volkov/tests/p3_scan_probe/p3scan.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * p3scan.c — диагностика полного пути scan/store/sort большого каталога. - * - * Использует те же банковые модули P3, но не делает panel change/rollback, - * поэтому сохраняет точные rc, errno и состояние панели в точке отказа. - */ - -#include -#include -#include -#include -#include -#include -#include "sc_panel.h" -#include "sc_source.h" -#include "sc_store.h" - -static ScPanel panel; -static ScDirEntry entry; - -int main(void) -{ - uint8_t block; - uint8_t page; - int rc; - int saved_errno; - - (void)settextmode(TEXT_MODE_80x32); - clrscr_attr(0x17); - gotoxy(0, 0); - textattr(0x1Fu); - cputs("P3 scan/store/sort diagnostic\r\n\r\n"); - textattr(0x17u); - - block = mem_alloc_pages(1u); - page = block ? mem_get_page(block, 0u) : 0u; - printf("EMM: block=%u page=%02X errno=%d\n", - (unsigned)block, (unsigned)page, errno); - if (!block || !page) goto done; - - memset(&panel, 0, sizeof(panel)); - strcpy(panel.path, "\\BIGDIR"); - panel.phys_page = page; - - puts("Calling sc_source_fs_scan..."); - errno = 0; - rc = sc_source_fs_scan(&panel); - saved_errno = errno; - printf("scan: rc=%d errno=%d count=%u trunc=%u\n", - rc, saved_errno, (unsigned)panel.count, - (unsigned)panel.truncated); - printf("view: cursor=%u top=%u total=%lu\n", - (unsigned)panel.cursor, (unsigned)panel.top, - (unsigned long)panel.total_size); - - if (panel.count != 0u && sc_store_get(&panel, 0u, &entry) == 0) - printf("first: %s attr=%02X flags=%02X\n", entry.name, - (unsigned)entry.attr, (unsigned)entry.flags); - if (panel.count != 0u && - sc_store_get(&panel, (uint16_t)(panel.count - 1u), &entry) == 0) - printf("last : %s attr=%02X flags=%02X\n", entry.name, - (unsigned)entry.attr, (unsigned)entry.flags); - - mem_free_block(block); - -done: - textattr(0x1Eu); - cputs("\r\nProbe stopped; MAME test will take snapshots."); - for (;;) (void)getkey(); -} diff --git a/applications/Volkov/tests/p3_store_probe/Makefile b/applications/Volkov/tests/p3_store_probe/Makefile deleted file mode 100644 index 830850a..0000000 --- a/applications/Volkov/tests/p3_store_probe/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Синтетическая проверка границы одной EMM-страницы панели: 640/641. - -EXAMPLE := p3store -MEMORY := big -ALLOCS ?= 3000 -EXTRA_FLAGS := --safe --max-allocs $(ALLOCS) -I ../../include -EXTRA_SRCS := ../../src/sc_store.c - -include $(CURDIR)/../../sdk.mk - -$(EXE): Makefile ../../src/sc_store.c ../../include/sc_entry.h \ - ../../include/sc_panel.h ../../include/sc_store.h diff --git a/applications/Volkov/tests/p3_store_probe/p3store.c b/applications/Volkov/tests/p3_store_probe/p3store.c deleted file mode 100644 index 906b526..0000000 --- a/applications/Volkov/tests/p3_store_probe/p3store.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * p3store.c — синтетическая проверка предела EMM-store панели. - * - * DSS не участвует: проба обязана дойти до продуктовой границы 640/641, - * которая недостижима через каталог стабильного DSS 1.71 с пределом 512. - */ - -#include -#include -#include -#include -#include -#include "sc_panel.h" -#include "sc_store.h" - -#define STORE_BYTES ((uint16_t)(SC_DIR_CAPACITY * sizeof(ScDirEntry))) - -static ScPanel panel; -static ScDirEntry entry; -static ScDirEntry check; - -static uint32_t expected_size(uint16_t index) -{ - return (uint32_t)index * 257ul + 3ul; -} - -static uint8_t verify_entry(uint16_t index) -{ - if (sc_store_get(&panel, index, &check) != 0) return 0u; - return check.name[0] == 'R' && check.name[1] == (char)(index & 0x7Fu) && - check.size == expected_size(index) && check.date == index && - check.time == (uint16_t)(index ^ 0x55AAu) && - check.reserved == 0xA5u; -} - -int main(void) -{ - uint16_t total_before; - uint16_t free_before; - uint16_t total_after; - uint16_t free_after; - uint16_t written = 0u; - uint16_t i; - uint8_t block; - uint8_t page; - uint8_t first_ok; - uint8_t middle_ok; - uint8_t last_ok; - uint8_t put_641_blocked; - uint8_t get_641_blocked; - uint8_t tail_ok; - uint8_t emm_restored; - - (void)settextmode(TEXT_MODE_80x32); - clrscr_attr(0x17u); - gotoxy(0u, 0u); - textattr(0x1Fu); - cputs("P3 EMM store 640/641 diagnostic\r\n\r\n"); - textattr(0x17u); - - printf("entry=%u capacity=%u bytes=%u reserve=%u\n", - (unsigned)sizeof(ScDirEntry), (unsigned)SC_DIR_CAPACITY, - (unsigned)STORE_BYTES, (unsigned)(0x4000u - STORE_BYTES)); - - mem_info(&total_before, &free_before); - block = mem_alloc_pages(1u); - page = block ? mem_get_page(block, 0u) : 0u; - printf("EMM: block=%u page=%02X free_before=%u\n", - (unsigned)block, (unsigned)page, (unsigned)free_before); - if (!block || !page) goto failed_alloc; - - memset(&panel, 0, sizeof(panel)); - panel.phys_page = page; - bank_store_byte(page, STORE_BYTES, 0x5Au); - bank_store_byte(page, 0x3FFFu, 0xC3u); - - for (i = 0u; i < SC_DIR_CAPACITY; i++) { - memset(&entry, 0, sizeof(entry)); - entry.name[0] = 'R'; - entry.name[1] = (char)(i & 0x7Fu); - entry.size = expected_size(i); - entry.date = i; - entry.time = (uint16_t)(i ^ 0x55AAu); - entry.reserved = 0xA5u; - if (sc_store_put(&panel, i, &entry) != 0) break; - written++; - } - panel.count = written; - - first_ok = verify_entry(0u); - middle_ok = verify_entry(319u); - last_ok = verify_entry(639u); - put_641_blocked = (sc_store_put(&panel, 640u, &entry) != 0); - get_641_blocked = (sc_store_get(&panel, 640u, &check) != 0); - tail_ok = bank_load_byte(page, STORE_BYTES) == 0x5Au && - bank_load_byte(page, 0x3FFFu) == 0xC3u; - - printf("write: count=%u first=%u middle=%u last=%u\n", - (unsigned)written, (unsigned)first_ok, - (unsigned)middle_ok, (unsigned)last_ok); - printf("guard: put640=%u get640=%u tail=%u\n", - (unsigned)put_641_blocked, (unsigned)get_641_blocked, - (unsigned)tail_ok); - - mem_free_block(block); - mem_info(&total_after, &free_after); - emm_restored = total_before == total_after && free_before == free_after; - printf("cleanup: free_after=%u restored=%u\n", - (unsigned)free_after, (unsigned)emm_restored); - - textattr((written == 640u && first_ok && middle_ok && last_ok && - put_641_blocked && get_641_blocked && tail_ok && emm_restored) ? - 0x1Eu : 0x4Fu); - cputs((written == 640u && first_ok && middle_ok && last_ok && - put_641_blocked && get_641_blocked && tail_ok && emm_restored) ? - "\r\nPASS: store boundary is intact." : - "\r\nFAIL: store boundary violation."); - goto stopped; - -failed_alloc: - textattr(0x4Fu); - cputs("\r\nFAIL: cannot allocate EMM page."); - -stopped: - textattr(0x1Eu); - cputs("\r\nProbe stopped; MAME test will take a snapshot."); - for (;;) (void)getkey(); -} diff --git a/applications/Volkov/tests/p4_bank_io_probe/Makefile b/applications/Volkov/tests/p4_bank_io_probe/Makefile deleted file mode 100644 index a2f2549..0000000 --- a/applications/Volkov/tests/p4_bank_io_probe/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# P4 — прямой файловый обмен через EMM-страницу в W3. - -EXAMPLE := p4bio -MEMORY := big -ALLOCS ?= 3000 -EXTRA_FLAGS := --safe --max-allocs $(ALLOCS) - -include $(CURDIR)/../../sdk.mk - -$(EXE): Makefile diff --git a/applications/Volkov/tests/p4_bank_io_probe/p4bio.c b/applications/Volkov/tests/p4_bank_io_probe/p4bio.c deleted file mode 100644 index 1938591..0000000 --- a/applications/Volkov/tests/p4_bank_io_probe/p4bio.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * p4bio.c — целевая проба bank_read_page()/bank_write_page(). - * - * Проверяет перенос 4096 байт через одну EMM-страницу без полноразмерного - * near-буфера, восстановление W3, контроль границ и отсутствие утечки EMM. - * Временный файл создаётся только на тестовой дискете A: и удаляется. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define TEST_PATH "A:\\P4BIO.TMP" -#define TEST_SIZE 4096u -#define READ_OFFSET 4096u - -static uint8_t chunk[256]; - -static uint8_t pattern(uint16_t pos) -{ - return (uint8_t)((uint8_t)(pos * 37u) + (uint8_t)(pos >> 8) + 0x5Au); -} - -static void prepare_page(uint8_t page) -{ - uint16_t base; - uint16_t i; - - for (base = 0u; base < TEST_SIZE; base += (uint16_t)sizeof(chunk)) { - for (i = 0u; i < (uint16_t)sizeof(chunk); i++) { - chunk[i] = pattern((uint16_t)(base + i)); - } - bank_write(page, base, chunk, (uint16_t)sizeof(chunk)); - } -} - -static uint8_t verify_page(uint8_t page) -{ - uint16_t i; - - for (i = 0u; i < TEST_SIZE; i++) { - if (bank_load_byte(page, (uint16_t)(READ_OFFSET + i)) != pattern(i)) { - return 0u; - } - } - return 1u; -} - -int main(void) -{ - uint16_t total_before; - uint16_t free_before; - uint16_t total_after; - uint16_t free_after; - uint8_t block = 0u; - uint8_t page = 0u; - uint8_t w3_before; - uint8_t write_w3_ok = 0u; - uint8_t read_w3_ok = 0u; - uint8_t data_ok = 0u; - uint8_t range_ok = 0u; - uint8_t cleanup_ok; - int fd = -1; - int written = -1; - int loaded = -1; - - (void)settextmode(TEXT_MODE_80x32); - clrscr_attr(0x17u); - gotoxy(0u, 0u); - textattr(0x1Fu); - cputs("P4 bank page file I/O diagnostic\r\n\r\n"); - textattr(0x17u); - - (void)unlink(TEST_PATH); - mem_info(&total_before, &free_before); - block = mem_alloc_pages(1u); - page = block ? mem_get_page(block, 0u) : 0u; - if (!block || !page) goto cleanup; - - prepare_page(page); - fd = open(TEST_PATH, O_WRONLY | O_CREAT | O_EXCL); - if (fd < 0) goto cleanup; - w3_before = _io_page_w3; - written = bank_write_page(fd, page, 0u, TEST_SIZE); - write_w3_ok = (_io_page_w3 == w3_before); - if (close(fd) != 0) goto cleanup; - fd = -1; - if (written != TEST_SIZE) goto cleanup; - - fd = open(TEST_PATH, O_RDONLY); - if (fd < 0) goto cleanup; - w3_before = _io_page_w3; - loaded = bank_read_page(fd, page, READ_OFFSET, TEST_SIZE); - read_w3_ok = (_io_page_w3 == w3_before); - if (close(fd) != 0) goto cleanup; - fd = -1; - if (loaded != TEST_SIZE) goto cleanup; - - data_ok = verify_page(page); - errno = 0; - w3_before = _io_page_w3; - range_ok = bank_write_page(-1, page, 0x3FFFu, 2u) == -1 && - errno == EINVAL && _io_page_w3 == w3_before; - -cleanup: - if (fd >= 0) (void)close(fd); - (void)unlink(TEST_PATH); - if (block != 0u) mem_free_block(block); - mem_info(&total_after, &free_after); - cleanup_ok = total_before == total_after && free_before == free_after; - - printf("page=%02X write=%d read=%d data=%u\n", - (unsigned)page, written, loaded, (unsigned)data_ok); - printf("W3: write=%u read=%u range=%u errno=%d\n", - (unsigned)write_w3_ok, (unsigned)read_w3_ok, - (unsigned)range_ok, errno); - printf("EMM: before=%u after=%u restored=%u\n", - (unsigned)free_before, (unsigned)free_after, - (unsigned)cleanup_ok); - - textattr((written == TEST_SIZE && loaded == TEST_SIZE && data_ok && - write_w3_ok && read_w3_ok && range_ok && cleanup_ok) ? - 0x1Eu : 0x4Fu); - cputs((written == TEST_SIZE && loaded == TEST_SIZE && data_ok && - write_w3_ok && read_w3_ok && range_ok && cleanup_ok) ? - "\r\nPASS: bank page file I/O is intact." : - "\r\nFAIL: bank page file I/O violation."); - textattr(0x1Eu); - cputs("\r\nProbe stopped; MAME test will take a snapshot."); - for (;;) (void)getkey(); -} diff --git a/applications/Volkov/tests/p4_exec_child/Makefile b/applications/Volkov/tests/p4_exec_child/Makefile deleted file mode 100644 index 507718a..0000000 --- a/applications/Volkov/tests/p4_exec_child/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# Дочерняя программа для интеграционного EXEC-теста Commander. - -EXAMPLE := p4child -MEMORY := tiny -ALLOCS ?= 3000 -EXTRA_FLAGS := --safe --max-allocs $(ALLOCS) - -include $(CURDIR)/../../sdk.mk diff --git a/applications/Volkov/tests/p4_exec_child/p4child.c b/applications/Volkov/tests/p4_exec_child/p4child.c deleted file mode 100644 index 89294d3..0000000 --- a/applications/Volkov/tests/p4_exec_child/p4child.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * p4child.c — дочерняя программа, намеренно меняющая состояние системы. - * - * Создаёт по одному файлу в каталогах обеих панелей, оставляет CWD в - * EXECLEFT, переключает экран в 40x32 и портит используемые Commander - * записи палитры. Родитель обязан восстановить всё это после EXEC. - */ - -#include -#include -#include -#include - -static char child_cwd[256]; - -static int write_marker(const char *path, const char *text, uint16_t size) -{ - int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC); - int result = 0; - - if (fd < 0) return -1; - if (write(fd, text, size) != (int)size) result = -1; - if (close(fd) != 0) result = -1; - return result; -} - -static void damage_commander_palette(void) -{ - static const uint8_t attrs[] = { - 0x1Fu, 0x1Eu, 0x1Bu, 0x30u, 0x70u, 0x4Fu, 0x1Cu, 0x3Cu - }; - uint8_t i; - uint8_t plane; - - for (i = 0u; i < (uint8_t)sizeof(attrs); i++) { - for (plane = 0u; plane < 4u; plane++) { - text_pal_set_color(plane, attrs[i], 0xFFu, 0x00u, 0xFFu); - } - } -} - -int main(void) -{ - static const char left_text[] = "created by P4CHILD\n"; - static const char right_text[] = "passive refresh by P4CHILD\n"; - int left_result; - int right_result; - - left_result = chdir("\\EXECLEFT"); - if (left_result == 0) { - left_result = write_marker("CREATED.TXT", left_text, - (uint16_t)(sizeof(left_text) - 1u)); - } - /* Идём через корень, оставляя проверяемые каталоги в формате 8.3. */ - right_result = chdir(".."); - if (right_result == 0) right_result = chdir("EXECRGHT"); - if (right_result == 0) { - right_result = write_marker("PASSIVE.TXT", right_text, - (uint16_t)(sizeof(right_text) - 1u)); - } - /* Родитель должен вернуть собственный CWD даже из этого каталога. */ - (void)chdir(".."); - (void)chdir("EXECLEFT"); - - (void)settextmode(TEXT_MODE_40x32); - damage_commander_palette(); - clrscr_attr(0x27u); - textattr(0x2Fu); - gotoxy(0u, 0u); - cputs("P4CHILD.EXE\r\n\r\n"); - textattr(0x27u); - cputs("Child changed mode, palette and CWD.\r\n"); - cprintf("markers: active=%d passive=%d\r\n", left_result, right_result); - if (getcwd(child_cwd, (uint16_t)sizeof(child_cwd))) { - cprintf("child CWD: %s\r\n", child_cwd); - } - cputs("\r\nPress Enter to return code 5A..."); - (void)getkey(); - return 0x5A; -} diff --git a/applications/Volkov/tests/p4_exec_verify.py b/applications/Volkov/tests/p4_exec_verify.py deleted file mode 100755 index 6bcf28f..0000000 --- a/applications/Volkov/tests/p4_exec_verify.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 -"""Проверка файлов, созданных дочерней программой P4CHILD.""" - -from __future__ import annotations - -import pathlib -import sys - - -def main() -> None: - if len(sys.argv) != 2: - raise SystemExit(f"usage: {sys.argv[0]} EXTRACTED_ROOT") - root = pathlib.Path(sys.argv[1]) - expected = { - root / "EXECLEFT" / "CREATED.TXT": b"created by P4CHILD\n", - root / "EXECRGHT" / "PASSIVE.TXT": b"passive refresh by P4CHILD\n", - } - failures = [] - for path, content in expected.items(): - if not path.is_file(): - failures.append(f"нет {path.relative_to(root)}") - elif path.read_bytes() != content: - failures.append(f"неверное содержимое {path.relative_to(root)}") - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: child создал active/passive markers с ожидаемым содержимым.") - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p4_fixture.py b/applications/Volkov/tests/p4_fixture.py deleted file mode 100755 index 3ac749b..0000000 --- a/applications/Volkov/tests/p4_fixture.py +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env python3 -"""Создание и проверка детерминированного HDD-набора для P4 copy.""" - -from __future__ import annotations - -import pathlib -import sys - - -SOURCE_FILES = { - "CANCEL.BIN": (4 * 1024 * 1024 + 37, 0x31), - "EMPTY.ZRO": (0, 0x42), - "EXIST.TXT": (513, 0x53), - "F65535.BIN": (65535, 0x64), - "F65536.BIN": (65536, 0x75), - "K4095.BIN": (4095, 0x86), - "K4096.BIN": (4096, 0x97), - "LARGE.BIN": (131109, 0xA8), - "ONE.BIN": (1, 0xB9), -} -TARGET_EXISTING = b"TARGET MUST SURVIVE AN F5 REFUSAL\n" - - -def pattern(size: int, seed: int) -> bytes: - return bytes(((index * 37 + (index >> 8) * 11 + seed) & 0xFF) - for index in range(size)) - - -def create(root: pathlib.Path) -> None: - source = root / "COPYFROM" - target = root / "COPYTO" - source.mkdir(parents=True) - target.mkdir(parents=True) - for name, (size, seed) in SOURCE_FILES.items(): - (source / name).write_bytes(pattern(size, seed)) - (target / "EXIST.TXT").write_bytes(TARGET_EXISTING) - - -def verify_common(root: pathlib.Path) -> list[str]: - source = root / "COPYFROM" - target = root / "COPYTO" - failures: list[str] = [] - - for name, (size, seed) in SOURCE_FILES.items(): - path = source / name - expected = pattern(size, seed) - if not path.is_file() or path.read_bytes() != expected: - failures.append(f"повреждён источник {name}") - - if not (target / "EXIST.TXT").is_file(): - failures.append("нет исходного COPYTO/EXIST.TXT") - elif (target / "EXIST.TXT").read_bytes() != TARGET_EXISTING: - failures.append("существующий COPYTO/EXIST.TXT был изменён") - return failures - - -def verify_initial(root: pathlib.Path) -> None: - target = root / "COPYTO" - failures = verify_common(root) - - if (target / "LARGE.BIN").exists(): - failures.append("COPYTO/LARGE.BIN существует до теста") - if (target / "CANCEL.BIN").exists(): - failures.append("COPYTO/CANCEL.BIN существует до теста") - if list(target.glob("~SC*.TMP")): - failures.append("в исходном образе есть временный ~SC*.TMP") - - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: исходный HDD-набор цел и готов к P4 copy-тесту.") - - -def verify(root: pathlib.Path) -> None: - target = root / "COPYTO" - failures = verify_common(root) - - large_expected = pattern(*SOURCE_FILES["LARGE.BIN"]) - if not (target / "LARGE.BIN").is_file(): - failures.append("нет скопированного COPYTO/LARGE.BIN") - elif (target / "LARGE.BIN").read_bytes() != large_expected: - failures.append("COPYTO/LARGE.BIN отличается от источника") - - if (target / "CANCEL.BIN").exists(): - failures.append("отменённый COPYTO/CANCEL.BIN остался на диске") - if not (target / "EMPTY.ZRO").is_file(): - failures.append("нет скопированного нулевого COPYTO/EMPTY.ZRO") - elif (target / "EMPTY.ZRO").stat().st_size != 0: - failures.append("COPYTO/EMPTY.ZRO имеет ненулевой размер") - if list(target.glob("~SC*.TMP")): - failures.append("после операции остался временный ~SC*.TMP") - - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: LARGE/EMPTY скопированы; EXIST сохранён; cancel/temp отсутствуют.") - - -def main() -> None: - commands = {"create", "verify-initial", "verify"} - if len(sys.argv) != 3 or sys.argv[1] not in commands: - raise SystemExit( - f"usage: {sys.argv[0]} create|verify-initial|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - elif sys.argv[1] == "verify-initial": - verify_initial(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p5_copy_faults.c b/applications/Volkov/tests/p5_copy_faults.c deleted file mode 100644 index 553c1c5..0000000 --- a/applications/Volkov/tests/p5_copy_faults.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * p5_copy_faults.c — детерминированные отрицательные сценарии copy-job. - * - * Это не подмена интерактивного теста F5: программа вызывает то же банковое - * ядро копирования и останавливает его в состояниях, которые невозможно - * надёжно поймать клавишей с точностью до одного sc_copy_step(). - */ - -#include -#include -#include -#include -#include -#include -#include "sc_copy.h" -#include "sc_exec.h" - -static ScCopyJob job; -static int result_fd = -1; -static uint8_t failures; - -static void log_text(const char *text) -{ - const char *end = text; - uint16_t length; - - cputs(text); - while (*end != '\0') end++; - length = (uint16_t)(end - text); - if (result_fd >= 0) (void)write(result_fd, text, length); -} - -static void check(uint8_t condition, const char *name) -{ - if (condition) { - log_text("PASS "); - } else { - log_text("FAIL "); - failures++; - } - log_text(name); - log_text("\r\n"); -} - -static uint8_t path_absent(const char *path) -{ - int fd = open(path, O_RDONLY); - - if (fd < 0) return 1u; - (void)close(fd); - return 0u; -} - -static int write_small(const char *path, const char *data, uint16_t size) -{ - int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC); - int result = 0; - - if (fd < 0) return -1; - if (write(fd, data, size) != (int)size) result = -1; - if (close(fd) != 0) result = -1; - return result; -} - -int main(void) -{ - uint16_t total_before = 0u; - uint16_t free_before = 0u; - uint16_t total_after = 0u; - uint16_t free_after = 0u; - uint8_t block; - uint8_t page; - int step; - - textattr(0x07u); - clrscr_attr(0x07u); - cputs("P5 copy-job fault matrix\r\n\r\n"); - result_fd = open("D:\\RESULT.TXT", O_WRONLY | O_CREAT | O_TRUNC); - - mem_info(&total_before, &free_before); - block = mem_alloc_pages(1u); - if (block == 0u) { - log_text("FAIL EMM allocation\r\n"); - if (result_fd >= 0) (void)close(result_fd); - return 1; - } - page = mem_get_page(block, 0u); - - errno = 0; - step = sc_platform_exec("D:\\MISSING.EXE"); - check(step < 0 && errno == ENOENT, "missing EXE errno 3"); - - /* Temp создаётся раньше открытия source: ENOENT обязан убрать его. */ - check(sc_copy_begin(&job, "D:\\SOURCE", "D:\\TARGET", - "MISSING.BIN", 4096ul, page) != 0 && - job.state == SC_COPY_ERROR && - path_absent("D:\\TARGET\\MISSING.BIN"), - "missing source cleanup"); - - /* Отмена до первого чтения. */ - step = sc_copy_begin(&job, "D:\\SOURCE", "D:\\TARGET", - "BEGIN.BIN", 8192ul, page); - if (step == 0) sc_copy_cancel(&job); - check(step == 0 && job.state == SC_COPY_CANCELLED && job.done == 0ul && - path_absent("D:\\TARGET\\BEGIN.BIN"), - "cancel before first read"); - - /* Последний блок записан, но rename ещё не выполнялся: точка pre-commit. */ - step = sc_copy_begin(&job, "D:\\SOURCE", "D:\\TARGET", - "LATE.BIN", 4096ul, page); - if (step == 0) step = sc_copy_step(&job); - check(step == SC_COPY_STEP_MORE && job.state == SC_COPY_RUNNING && - job.done == job.total && path_absent("D:\\TARGET\\LATE.BIN"), - "last block stops before commit"); - sc_copy_cancel(&job); - check(job.state == SC_COPY_CANCELLED && - path_absent("D:\\TARGET\\LATE.BIN"), - "cancel at pre-commit"); - - /* Закрытый source fd даёт реальную ошибку bank_read_page(). */ - step = sc_copy_begin(&job, "D:\\SOURCE", "D:\\TARGET", - "READERR.BIN", 4096ul, page); - if (step == 0) { - (void)close(job.source_fd); - job.source_fd = -1; - step = sc_copy_step(&job); - } - check(step == SC_COPY_STEP_ERROR && job.state == SC_COPY_ERROR && - path_absent("D:\\TARGET\\READERR.BIN"), - "read error cleanup"); - - /* Реальный DSS READONLY: temp повторно открыт только для чтения, поэтому - * WRITE обязан вернуть errno 8, а job — удалить файл после close. */ - step = sc_copy_begin(&job, "D:\\SOURCE", "D:\\TARGET", - "RONLY.BIN", 4096ul, page); - if (step == 0) { - (void)close(job.temp_fd); - job.temp_fd = open(job.temp_path, O_RDONLY); - if (job.temp_fd >= 0) { - step = sc_copy_step(&job); - } else { - sc_copy_cancel(&job); - step = -2; - } - } - check(step == SC_COPY_STEP_ERROR && job.state == SC_COPY_ERROR && - job.error == EROFS && path_absent("D:\\TARGET\\RONLY.BIN"), - "readonly write cleanup"); - - /* Удаляем уже записанный temp перед commit: RENAME обязан отказать. */ - step = sc_copy_begin(&job, "D:\\SOURCE", "D:\\TARGET", - "RENAME.BIN", 4096ul, page); - if (step == 0) step = sc_copy_step(&job); - if (step == SC_COPY_STEP_MORE) { - (void)close(job.temp_fd); - job.temp_fd = -1; - (void)unlink(job.temp_path); - step = sc_copy_step(&job); - } - check(step == SC_COPY_STEP_ERROR && job.state == SC_COPY_ERROR && - path_absent("D:\\TARGET\\RENAME.BIN"), - "rename error cleanup"); - - /* Имитируем гонку: target появился после begin, но до commit. */ - step = sc_copy_begin(&job, "D:\\SOURCE", "D:\\TARGET", - "RACE.BIN", 4096ul, page); - if (step == 0) step = sc_copy_step(&job); - if (step == SC_COPY_STEP_MORE && - write_small("D:\\TARGET\\RACE.BIN", "KEEP", 4u) == 0) { - step = sc_copy_step(&job); - } - check(step == SC_COPY_STEP_ERROR && job.error == EEXIST, - "target race refuses overwrite"); - - mem_free_block(block); - mem_info(&total_after, &free_after); - check(total_before == total_after && free_before == free_after, - "EMM restored"); - - if (failures == 0u) log_text("ALL PASS\r\n"); - if (result_fd >= 0) (void)close(result_fd); - cputs("\r\nPress Enter to return to DSS.\r\n"); - while (kbhit()) (void)getkey(); - (void)getkey(); - return failures; -} diff --git a/applications/Volkov/tests/p5_copy_faults_fixture.py b/applications/Volkov/tests/p5_copy_faults_fixture.py deleted file mode 100755 index cdfecf4..0000000 --- a/applications/Volkov/tests/p5_copy_faults_fixture.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python3 -"""Создание и проверка HDD-набора отрицательных сценариев copy-job.""" - -from __future__ import annotations - -import pathlib -import sys - - -SIZES = { - "BEGIN.BIN": 8192, - "LATE.BIN": 4096, - "READERR.BIN": 4096, - "RONLY.BIN": 4096, - "RENAME.BIN": 4096, - "RACE.BIN": 4096, -} - - -def content(name: str, size: int) -> bytes: - seed = sum(name.encode("ascii")) & 0xFF - return bytes((seed + index * 29 + (index >> 7)) & 0xFF - for index in range(size)) - - -def create(root: pathlib.Path) -> None: - source = root / "SOURCE" - (root / "TARGET").mkdir(parents=True) - source.mkdir() - for name, size in SIZES.items(): - (source / name).write_bytes(content(name, size)) - - -def verify(root: pathlib.Path) -> None: - failures: list[str] = [] - source = root / "SOURCE" - target = root / "TARGET" - result = root / "RESULT.TXT" - - for name, size in SIZES.items(): - path = source / name - if not path.is_file() or path.read_bytes() != content(name, size): - failures.append(f"повреждён SOURCE/{name}") - - expected_lines = [ - "PASS missing EXE errno 3", - "PASS missing source cleanup", - "PASS cancel before first read", - "PASS last block stops before commit", - "PASS cancel at pre-commit", - "PASS read error cleanup", - "PASS readonly write cleanup", - "PASS rename error cleanup", - "PASS target race refuses overwrite", - "PASS EMM restored", - "ALL PASS", - ] - if not result.is_file(): - failures.append("не создан RESULT.TXT") - else: - text = result.read_bytes().decode("cp866", errors="replace") - for line in expected_lines: - if line not in text: - failures.append(f"нет строки результата: {line}") - - if not target.is_dir(): - failures.append("исчез TARGET") - else: - unexpected = sorted(path.name for path in target.iterdir() - if path.name.upper() != "RACE.BIN") - if unexpected: - failures.append("лишние файлы TARGET: " + ", ".join(unexpected)) - race = target / "RACE.BIN" - if not race.is_file() or race.read_bytes() != b"KEEP": - failures.append("RACE.BIN перезаписан или отсутствует") - temporaries = sorted(path.name for path in target.glob("~SC*.TMP")) - if temporaries: - failures.append("остались temp: " + ", ".join(temporaries)) - - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: missing EXE, cancel, read/readonly/rename, race и EMM cleanup.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p5_enospc_fixture.py b/applications/Volkov/tests/p5_enospc_fixture.py deleted file mode 100755 index e2b10ec..0000000 --- a/applications/Volkov/tests/p5_enospc_fixture.py +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env python3 -"""Набор почти полного HDD для проверки ENOSPC и удаления copy-temp.""" - -from __future__ import annotations - -import pathlib -import sys - - -SOURCE_SIZE = 262_181 -FILLER_SIZE = 32_850_000 - - -def source_content() -> bytes: - return bytes(((index * 37 + (index >> 8) * 11 + 0xA7) & 0xFF) - for index in range(SOURCE_SIZE)) - - -def write_filler(path: pathlib.Path) -> None: - chunk = bytes(((index * 17 + 0x53) & 0xFF) for index in range(65536)) - remaining = FILLER_SIZE - with path.open("wb") as stream: - while remaining: - part = min(remaining, len(chunk)) - stream.write(chunk[:part]) - remaining -= part - - -def create(root: pathlib.Path) -> None: - source = root / "SOURCE" - target = root / "TARGET" - source.mkdir(parents=True) - target.mkdir(parents=True) - (source / "FULL.BIN").write_bytes(source_content()) - write_filler(root / "FILLER.BIN") - - -def verify_initial(root: pathlib.Path) -> None: - failures = [] - source = root / "SOURCE" / "FULL.BIN" - target = root / "TARGET" - filler = root / "FILLER.BIN" - if not source.is_file() or source.read_bytes() != source_content(): - failures.append("повреждён SOURCE/FULL.BIN") - if not filler.is_file() or filler.stat().st_size != FILLER_SIZE: - failures.append("неверный FILLER.BIN") - if not target.is_dir() or list(target.iterdir()): - failures.append("TARGET не пуст до теста") - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: ENOSPC fixture готов, SOURCE/FULL.BIN сохранён.") - - -def verify(root: pathlib.Path) -> None: - failures = [] - source = root / "SOURCE" / "FULL.BIN" - target = root / "TARGET" - filler = root / "FILLER.BIN" - if not source.is_file() or source.read_bytes() != source_content(): - failures.append("исходный FULL.BIN изменён") - if not filler.is_file() or filler.stat().st_size != FILLER_SIZE: - failures.append("FILLER.BIN изменён") - if (target / "FULL.BIN").exists(): - failures.append("после ENOSPC остался конечный TARGET/FULL.BIN") - temporaries = sorted(target.glob("~SC*.TMP")) - if temporaries: - failures.append("после ENOSPC остался временный файл") - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: ENOSPC не изменил source/filler и не оставил target/temp.") - - -def main() -> None: - commands = {"create", "verify-initial", "verify"} - if len(sys.argv) != 3 or sys.argv[1] not in commands: - raise SystemExit( - f"usage: {sys.argv[0]} create|verify-initial|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - elif sys.argv[1] == "verify-initial": - verify_initial(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p5_fixture.py b/applications/Volkov/tests/p5_fixture.py deleted file mode 100755 index 70b7570..0000000 --- a/applications/Volkov/tests/p5_fixture.py +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env python3 -"""Детерминированный набор для P5: 20 copy и граничные размеры.""" - -from __future__ import annotations - -import pathlib -import sys - - -SIZES = [ - 0, - 1, - 4095, - 4096, - 65535, - 65536, - 37, - 511, - 512, - 513, - 1023, - 1024, - 2047, - 2048, - 8191, - 8192, - 16383, - 16384, - 32769, - 1024 * 1024 + 37, -] - - -def content(index: int, size: int) -> bytes: - seed = (0x31 + index * 13) & 0xFF - return bytes(((position * 37 + (position >> 8) * 11 + seed) & 0xFF) - for position in range(size)) - - -def create(root: pathlib.Path) -> None: - source = root / "SOURCE" - target = root / "TARGET" - source.mkdir(parents=True) - target.mkdir(parents=True) - for index, size in enumerate(SIZES): - (source / f"F{index:02d}.BIN").write_bytes(content(index, size)) - - -def verify_initial(root: pathlib.Path) -> None: - source = root / "SOURCE" - target = root / "TARGET" - failures = [] - for index, size in enumerate(SIZES): - path = source / f"F{index:02d}.BIN" - if not path.is_file() or path.read_bytes() != content(index, size): - failures.append(f"повреждён SOURCE/{path.name}") - if not target.is_dir() or list(target.iterdir()): - failures.append("TARGET не пуст до теста") - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: P5 fixture содержит 20 источников, TARGET пуст.") - - -def verify(root: pathlib.Path) -> None: - source = root / "SOURCE" - target = root / "TARGET" - failures = [] - for index, size in enumerate(SIZES): - name = f"F{index:02d}.BIN" - source_path = source / name - target_path = target / name - if not target_path.is_file(): - failures.append(f"нет TARGET/{name}") - elif target_path.read_bytes() != source_path.read_bytes(): - failures.append(f"TARGET/{name} не совпадает с источником") - elif target_path.stat().st_size != size: - failures.append(f"неверный размер TARGET/{name}") - temporaries = sorted(target.glob("~SC*.TMP")) - if temporaries: - failures.append("остались временные файлы: " + - ", ".join(path.name for path in temporaries)) - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: 20/20 файлов совпали; все граничные размеры и >1 МБ пройдены.") - - -def main() -> None: - commands = {"create", "verify-initial", "verify"} - if len(sys.argv) != 3 or sys.argv[1] not in commands: - raise SystemExit( - f"usage: {sys.argv[0]} create|verify-initial|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - elif sys.argv[1] == "verify-initial": - verify_initial(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p5_media_fixture.py b/applications/Volkov/tests/p5_media_fixture.py deleted file mode 100755 index b0d69be..0000000 --- a/applications/Volkov/tests/p5_media_fixture.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python3 -"""Набор для отключения/возврата HDD во время F5.""" - -from __future__ import annotations - -import pathlib -import sys - - -SIZE = 262_181 - - -def data() -> bytes: - return bytes(((index * 41 + (index >> 9) * 7 + 0x35) & 0xFF) - for index in range(SIZE)) - - -def create(root: pathlib.Path) -> None: - media = root / "MEDIA" - (media / "DEEP").mkdir(parents=True) - (media / "DATA.BIN").write_bytes(data()) - (media / "DEEP" / "INNER.TXT").write_text( - "media-change fixture\n", encoding="ascii") - - -def verify(root: pathlib.Path) -> None: - failures: list[str] = [] - source = root / "MEDIA" / "DATA.BIN" - target = root / "DATA.BIN" - - if not source.is_file() or source.read_bytes() != data(): - failures.append("исходный MEDIA/DATA.BIN повреждён") - if not target.is_file() or target.read_bytes() != data(): - failures.append("DATA.BIN не скопирован после возврата HDD") - temporaries = sorted(root.glob("~SC*.TMP")) - if temporaries: - failures.append("после media-change остался временный файл") - inner = root / "MEDIA" / "DEEP" / "INNER.TXT" - if not inner.is_file() or inner.read_bytes() != b"media-change fixture\n": - failures.append("вложенный контрольный файл повреждён") - - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: HDD removal дал управляемый отказ; после возврата DATA совпал, temp нет.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p6_sort_fixture.py b/applications/Volkov/tests/p6_sort_fixture.py deleted file mode 100755 index ee5adca..0000000 --- a/applications/Volkov/tests/p6_sort_fixture.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env python3 -"""Создаёт каталоги UI-пробы сортировки и проверяет target-результат.""" - -from __future__ import annotations - -import os -import pathlib -import sys -from datetime import datetime - - -FILES = { - "ALPHA.BIN": (50, datetime(2025, 1, 2, 12, 0, 0)), - "BETA.TXT": (20, datetime(2023, 1, 2, 12, 0, 0)), - "GAMMA.TXT": (10, datetime(2024, 1, 2, 12, 0, 0)), - "DELTA.EXE": (30, datetime(2022, 1, 2, 12, 0, 0)), -} - - -def create(root: pathlib.Path) -> None: - for panel_name in ("SORTL", "SORTR"): - panel = root / panel_name - panel.mkdir(parents=True) - for directory in ("ADIR", "ZDIR"): - (panel / directory).mkdir() - for name, (size, stamp) in FILES.items(): - path = panel / name - path.write_bytes(bytes((index * 37 + size) & 0xFF - for index in range(size))) - timestamp = stamp.timestamp() - os.utime(path, (timestamp, timestamp)) - - -def verify(root: pathlib.Path) -> None: - result = root / "SORTRES.TXT" - expected = [ - "PASS name ascending and fixed groups", - "PASS selection fixture points to BETA", - "PASS name descending toggle", - "PASS selection preserved after sort", - "PASS extension ascending", - "PASS extension descending toggle", - "PASS size ascending", - "PASS size descending toggle", - "PASS date ascending", - "PASS date descending toggle", - "PASS panel sort modes are independent", - "PASS second EMM page sorted independently", - "ALL PASS", - ] - failures: list[str] = [] - if not result.is_file(): - failures.append("нет SORTRES.TXT") - else: - text = result.read_bytes().decode("cp866", errors="replace") - for line in expected: - if line not in text: - failures.append(f"нет строки результата: {line}") - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: 4 sort keys, reverse, fixed groups, selection, two panels.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p6_sort_probe.c b/applications/Volkov/tests/p6_sort_probe.c deleted file mode 100644 index b87fe91..0000000 --- a/applications/Volkov/tests/p6_sort_probe.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * p6_sort_probe.c — target-регрессия четырёх ключей сортировки панели. - * - * Проверяет реальный EMM-store и банковый sc_sort.c. Записи намеренно - * перемешаны; `..` и маркер DSS обязаны сохранять крайние позиции, а - * каталоги — оставаться перед файлами при любом направлении. - */ - -#include -#include -#include -#include -#include -#include -#include "sc_panel.h" -#include "sc_sort.h" -#include "sc_store.h" - -static int result_fd = -1; -static uint8_t failures; - -static void log_text(const char *text) -{ - const char *end = text; - uint16_t length; - - while (*end != '\0') end++; - length = (uint16_t)(end - text); - if (result_fd >= 0) (void)write(result_fd, text, length); -} - -static void check(uint8_t condition, const char *name) -{ - log_text(condition ? "PASS " : "FAIL "); - if (!condition) failures++; - log_text(name); - log_text("\r\n"); -} - -/* sc_sort_set() вызывает общий panel-инвариант. В probe достаточно той же - * нормализации cursor/top без подтягивания файлового source-модуля. */ -void sc_panel_normalize_view(ScPanel *panel) -{ - if (panel->count == 0u) { - panel->cursor = 0u; - panel->top = 0u; - return; - } - if (panel->cursor >= panel->count) panel->cursor = panel->count - 1u; - if (panel->top > panel->cursor) panel->top = panel->cursor; - if (panel->cursor >= (uint16_t)(panel->top + 27u)) { - panel->top = (uint16_t)(panel->cursor - 26u); - } -} - -static void put_entry(ScPanel *panel, uint16_t index, const char *name, - uint32_t size, uint16_t date, uint8_t attr, - uint8_t flags) -{ - ScDirEntry entry; - - memset(&entry, 0, sizeof(entry)); - strcpy(entry.name, name); - entry.size = size; - entry.date = date; - entry.time = (uint16_t)(index << 5); - entry.attr = attr; - entry.flags = flags; - (void)sc_store_put(panel, index, &entry); -} - -static void fill_panel(ScPanel *panel, uint8_t page) -{ - memset(panel, 0, sizeof(*panel)); - panel->phys_page = page; - panel->count = 8u; - panel->sort_key = SC_SORT_NAME; - put_entry(panel, 0u, "BETA.TXT", 20ul, 0x5621u, 0u, 0u); - put_entry(panel, 1u, ">>> MORE...", 0ul, 0u, 0u, - SC_ENTRY_DSS_LIMIT); - put_entry(panel, 2u, "ZDIR", 0ul, 0x5A21u, FA_DIREC, 0u); - put_entry(panel, 3u, "ALPHA.BIN", 50ul, 0x5A21u, 0u, 0u); - put_entry(panel, 4u, "..", 0ul, 0u, FA_DIREC, - SC_ENTRY_PARENT); - put_entry(panel, 5u, "GAMMA.TXT", 10ul, 0x5821u, 0u, 0u); - put_entry(panel, 6u, "ADIR", 0ul, 0x5421u, FA_DIREC, 0u); - put_entry(panel, 7u, "DELTA.EXE", 30ul, 0x5421u, 0u, 0u); -} - -static uint8_t order_is(const ScPanel *panel, const char *const *names) -{ - ScDirEntry entry; - uint16_t i; - - for (i = 0u; i < panel->count; i++) { - if (sc_store_get(panel, i, &entry) != 0 || - strcmp(entry.name, names[i]) != 0) return 0u; - } - return 1u; -} - -static uint8_t selected_is(const ScPanel *panel, const char *name) -{ - ScDirEntry entry; - return sc_store_get(panel, panel->cursor, &entry) == 0 && - strcmp(entry.name, name) == 0; -} - -int main(void) -{ - static const char *const name_up[] = { - "..", "ADIR", "ZDIR", "ALPHA.BIN", "BETA.TXT", "DELTA.EXE", - "GAMMA.TXT", ">>> MORE..." - }; - static const char *const name_down[] = { - "..", "ZDIR", "ADIR", "GAMMA.TXT", "DELTA.EXE", "BETA.TXT", - "ALPHA.BIN", ">>> MORE..." - }; - static const char *const ext_up[] = { - "..", "ADIR", "ZDIR", "ALPHA.BIN", "DELTA.EXE", "BETA.TXT", - "GAMMA.TXT", ">>> MORE..." - }; - static const char *const ext_down[] = { - "..", "ZDIR", "ADIR", "GAMMA.TXT", "BETA.TXT", "DELTA.EXE", - "ALPHA.BIN", ">>> MORE..." - }; - static const char *const size_up[] = { - "..", "ADIR", "ZDIR", "GAMMA.TXT", "BETA.TXT", "DELTA.EXE", - "ALPHA.BIN", ">>> MORE..." - }; - static const char *const size_down[] = { - "..", "ZDIR", "ADIR", "ALPHA.BIN", "DELTA.EXE", "BETA.TXT", - "GAMMA.TXT", ">>> MORE..." - }; - static const char *const date_up[] = { - "..", "ADIR", "ZDIR", "DELTA.EXE", "BETA.TXT", "GAMMA.TXT", - "ALPHA.BIN", ">>> MORE..." - }; - static const char *const date_down[] = { - "..", "ZDIR", "ADIR", "ALPHA.BIN", "GAMMA.TXT", "BETA.TXT", - "DELTA.EXE", ">>> MORE..." - }; - ScPanel left; - ScPanel right; - uint8_t block; - uint8_t left_page; - uint8_t right_page; - - result_fd = open("D:\\SORTRES.TXT", O_WRONLY | O_CREAT | O_TRUNC); - block = mem_alloc_pages(2u); - if (block == 0u) { - log_text("FAIL EMM allocation\r\n"); - if (result_fd >= 0) (void)close(result_fd); - return 1; - } - left_page = mem_get_page(block, 0u); - right_page = mem_get_page(block, 1u); - fill_panel(&left, left_page); - fill_panel(&right, right_page); - - check(sc_sort_panel(&left) == 0 && order_is(&left, name_up), - "name ascending and fixed groups"); - left.cursor = 4u; /* BETA.TXT в уже отсортированном списке. */ - check(selected_is(&left, "BETA.TXT"), - "selection fixture points to BETA"); - - check(sc_sort_set(&left, SC_SORT_NAME) == 0 && - left.sort_descending && order_is(&left, name_down), - "name descending toggle"); - check(selected_is(&left, "BETA.TXT"), - "selection preserved after sort"); - check(sc_sort_set(&left, SC_SORT_EXTENSION) == 0 && - !left.sort_descending && order_is(&left, ext_up), - "extension ascending"); - check(sc_sort_set(&left, SC_SORT_EXTENSION) == 0 && - left.sort_descending && order_is(&left, ext_down), - "extension descending toggle"); - check(sc_sort_set(&left, SC_SORT_SIZE) == 0 && - !left.sort_descending && order_is(&left, size_up), - "size ascending"); - check(sc_sort_set(&left, SC_SORT_SIZE) == 0 && - left.sort_descending && order_is(&left, size_down), - "size descending toggle"); - check(sc_sort_set(&left, SC_SORT_DATE) == 0 && - !left.sort_descending && order_is(&left, date_up), - "date ascending"); - check(sc_sort_set(&left, SC_SORT_DATE) == 0 && - left.sort_descending && order_is(&left, date_down), - "date descending toggle"); - - check(sc_sort_set(&right, SC_SORT_EXTENSION) == 0 && - right.sort_key == SC_SORT_EXTENSION && !right.sort_descending && - left.sort_key == SC_SORT_DATE && left.sort_descending, - "panel sort modes are independent"); - check(order_is(&right, ext_up), "second EMM page sorted independently"); - - mem_free_block(block); - if (failures == 0u) log_text("ALL PASS\r\n"); - if (result_fd >= 0) (void)close(result_fd); - return failures; -} diff --git a/applications/Volkov/tests/p7_select_fixture.py b/applications/Volkov/tests/p7_select_fixture.py deleted file mode 100755 index fa639e4..0000000 --- a/applications/Volkov/tests/p7_select_fixture.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env python3 -"""Создаёт UI-каталог выбора и проверяет SELRES.TXT target-пробы.""" - -from __future__ import annotations - -import pathlib -import sys - - -def create(root: pathlib.Path) -> None: - panel = root / "SELECT" - panel.mkdir(parents=True) - (panel / "ADIR").mkdir() - (panel / "ZDIR").mkdir() - for name, size in (("A.BIN", 10), ("B.BIN", 20), ("C.BIN", 30)): - (panel / name).write_bytes(bytes((index * 19 + size) & 0xFF - for index in range(size))) - - -def verify(root: pathlib.Path) -> None: - result = root / "SELRES.TXT" - expected = [ - "PASS parent cannot be selected", - "PASS Insert selects file and advances", - "PASS second Insert accumulates size", - "PASS DSS marker cannot be selected", - "PASS invert changes files only", - "PASS second invert restores file selection", - "PASS individual Insert may select directory", - "PASS clear group ignores selected directory", - "PASS mask is ASCII case insensitive", - "PASS question wildcard clears matching files", - "PASS DOS star-dot-star selects all files only", - "PASS group mask never changes directory flag", - "PASS refresh clear resets selection totals", - "PASS EMM restored", - "ALL PASS", - ] - failures: list[str] = [] - if not result.is_file(): - failures.append("нет SELRES.TXT") - else: - text = result.read_bytes().decode("cp866", errors="replace") - for line in expected: - if line not in text: - failures.append(f"нет строки результата: {line}") - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: Insert/invert/masks, virtual guards, totals, refresh и EMM.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p7_select_probe.c b/applications/Volkov/tests/p7_select_probe.c deleted file mode 100644 index 4e1902f..0000000 --- a/applications/Volkov/tests/p7_select_probe.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * p7_select_probe.c — target-регрессия выбора записей панели. - * - * Проверяет Insert/invert через настоящий EMM-store, статистику выбора и - * запрет синтетических записей без зависимости от UI-таймингов. - */ - -#include -#include -#include -#include -#include -#include -#include "sc_panel.h" -#include "sc_select.h" -#include "sc_store.h" - -static int result_fd = -1; -static uint8_t failures; - -static void log_text(const char *text) -{ - const char *end = text; - while (*end != '\0') end++; - if (result_fd >= 0) { - (void)write(result_fd, text, (uint16_t)(end - text)); - } -} - -static void check(uint8_t condition, const char *name) -{ - log_text(condition ? "PASS " : "FAIL "); - if (!condition) failures++; - log_text(name); - log_text("\r\n"); -} - -void sc_panel_normalize_view(ScPanel *panel) -{ - if (panel->count == 0u) { - panel->cursor = 0u; - panel->top = 0u; - return; - } - if (panel->cursor >= panel->count) panel->cursor = panel->count - 1u; - if (panel->top > panel->cursor) panel->top = panel->cursor; - if (panel->cursor >= (uint16_t)(panel->top + 27u)) { - panel->top = (uint16_t)(panel->cursor - 26u); - } -} - -static void put_entry(ScPanel *panel, uint16_t index, const char *name, - uint32_t size, uint8_t attr, uint8_t flags) -{ - ScDirEntry entry; - memset(&entry, 0, sizeof(entry)); - strcpy(entry.name, name); - entry.size = size; - entry.attr = attr; - entry.flags = flags; - (void)sc_store_put(panel, index, &entry); -} - -static uint8_t selected(const ScPanel *panel, uint16_t index) -{ - ScDirEntry entry; - return sc_store_get(panel, index, &entry) == 0 && - (entry.flags & SC_ENTRY_SELECTED) != 0u; -} - -int main(void) -{ - ScPanel panel; - uint16_t total_before = 0u; - uint16_t free_before = 0u; - uint16_t total_after = 0u; - uint16_t free_after = 0u; - uint8_t block; - uint8_t page; - - result_fd = open("D:\\SELRES.TXT", O_WRONLY | O_CREAT | O_TRUNC); - mem_info(&total_before, &free_before); - block = mem_alloc_pages(1u); - page = block ? mem_get_page(block, 0u) : 0u; - if (!block || !page) { - log_text("FAIL EMM allocation\r\n"); - if (result_fd >= 0) (void)close(result_fd); - return 1; - } - - memset(&panel, 0, sizeof(panel)); - panel.phys_page = page; - panel.count = 5u; - put_entry(&panel, 0u, "..", 0ul, FA_DIREC, SC_ENTRY_PARENT); - put_entry(&panel, 1u, "ADIR", 0ul, FA_DIREC, 0u); - put_entry(&panel, 2u, "A.BIN", 10ul, 0u, 0u); - put_entry(&panel, 3u, "B.BIN", 20ul, 0u, 0u); - put_entry(&panel, 4u, ">>> MORE...", 0ul, 0u, SC_ENTRY_DSS_LIMIT); - - check(sc_select_apply(&panel, SC_SELECT_TOGGLE, 0) == 1 && - panel.cursor == 0u && - panel.selected_count == 0u, "parent cannot be selected"); - - panel.cursor = 2u; - check(sc_select_apply(&panel, SC_SELECT_TOGGLE, 0) == 0 && - panel.cursor == 3u && - panel.selected_count == 1u && panel.selected_size == 10ul && - selected(&panel, 2u), "Insert selects file and advances"); - check(sc_select_apply(&panel, SC_SELECT_TOGGLE, 0) == 0 && - panel.cursor == 4u && - panel.selected_count == 2u && panel.selected_size == 30ul && - selected(&panel, 3u), "second Insert accumulates size"); - check(sc_select_apply(&panel, SC_SELECT_TOGGLE, 0) == 1 && - panel.cursor == 4u && - panel.selected_count == 2u && !selected(&panel, 4u), - "DSS marker cannot be selected"); - - check(sc_select_apply(&panel, SC_SELECT_INVERT, 0) == 0 && - panel.selected_count == 0u && panel.selected_size == 0ul && - !selected(&panel, 1u) && !selected(&panel, 2u) && - !selected(&panel, 3u), "invert changes files only"); - check(sc_select_apply(&panel, SC_SELECT_INVERT, 0) == 0 && - panel.selected_count == 2u && panel.selected_size == 30ul && - !selected(&panel, 1u) && selected(&panel, 2u) && - selected(&panel, 3u), "second invert restores file selection"); - - panel.cursor = 1u; - check(sc_select_apply(&panel, SC_SELECT_TOGGLE, 0) == 0 && - panel.selected_count == 3u && panel.selected_size == 30ul && - selected(&panel, 1u), "individual Insert may select directory"); - check(sc_select_apply(&panel, SC_SELECT_MASK_CLEAR, "*") == 0 && - panel.selected_count == 1u && panel.selected_size == 0ul && - selected(&panel, 1u), "clear group ignores selected directory"); - check(sc_select_apply(&panel, SC_SELECT_MASK_SET, "*.bin") == 0 && - panel.selected_count == 3u && panel.selected_size == 30ul && - selected(&panel, 1u) && - selected(&panel, 2u) && selected(&panel, 3u), - "mask is ASCII case insensitive"); - check(sc_select_apply(&panel, SC_SELECT_MASK_CLEAR, "?.b?n") == 0 && - panel.selected_count == 1u && panel.selected_size == 0ul && - selected(&panel, 1u), - "question wildcard clears matching files"); - check(sc_select_apply(&panel, SC_SELECT_MASK_SET, "*.*") == 0 && - panel.selected_count == 3u && panel.selected_size == 30ul && - selected(&panel, 1u), "DOS star-dot-star selects all files only"); - check(sc_select_apply(&panel, SC_SELECT_MASK_CLEAR, "A*") == 0 && - panel.selected_count == 2u && panel.selected_size == 20ul && - selected(&panel, 1u) && !selected(&panel, 2u) && - selected(&panel, 3u), "group mask never changes directory flag"); - - sc_store_clear(&panel); - check(panel.count == 0u && panel.selected_count == 0u && - panel.selected_size == 0ul, "refresh clear resets selection totals"); - - mem_free_block(block); - mem_info(&total_after, &free_after); - check(total_before == total_after && free_before == free_after, - "EMM restored"); - if (failures == 0u) log_text("ALL PASS\r\n"); - if (result_fd >= 0) (void)close(result_fd); - return failures; -} diff --git a/applications/Volkov/tests/p9_mkdir_fixture.py b/applications/Volkov/tests/p9_mkdir_fixture.py deleted file mode 100755 index 8459b43..0000000 --- a/applications/Volkov/tests/p9_mkdir_fixture.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python3 -"""Создаёт и проверяет HDD-fixture команды F7 mkdir.""" - -from __future__ import annotations - -import pathlib -import sys - - -def create(root: pathlib.Path) -> None: - panel = root / "MKTEST" - panel.mkdir(parents=True) - (panel / "KEEP.TXT").write_bytes(b"must remain\r\n") - - -def verify(root: pathlib.Path) -> None: - expected = [ - "PASS valid DOS 8.3 components accepted", - "PASS length and dot violations rejected", - "PASS path injection and wildcard rejected", - "PASS validated component joins full path", - "PASS CWD captured before mkdir", - "PASS mkdir creates directory", - "PASS duplicate mkdir is managed error", - "PASS rmdir removes probe directory", - "PASS mkdir/rmdir preserve process CWD", - "ALL PASS", - ] - failures: list[str] = [] - result = root / "MKRES.TXT" - panel = root / "MKTEST" - - if not result.is_file(): - failures.append("нет MKRES.TXT") - else: - text = result.read_bytes().decode("cp866", errors="replace") - for line in expected: - if line not in text: - failures.append(f"нет строки результата: {line}") - if not (panel / "NEWDIR").is_dir(): - failures.append("F7 не создал MKTEST/NEWDIR") - elif any((panel / "NEWDIR").iterdir()): - failures.append("NEWDIR неожиданно не пуст") - if not (panel / "KEEP.TXT").is_file() or \ - (panel / "KEEP.TXT").read_bytes() != b"must remain\r\n": - failures.append("KEEP.TXT изменён") - for unwanted in ("PROBE", "TOOLONG9", "TOOLONG99"): - if (panel / unwanted).exists(): - failures.append(f"остался лишний MKTEST/{unwanted}") - if failures: - for failure in failures: - print(f"FAIL: {failure}") - raise SystemExit(1) - print("PASS: validator, mkdir/error/rmdir/CWD и F7 NEWDIR.") - - -def main() -> None: - if len(sys.argv) != 3 or sys.argv[1] not in {"create", "verify"}: - raise SystemExit(f"usage: {sys.argv[0]} create|verify DIR") - root = pathlib.Path(sys.argv[2]) - if sys.argv[1] == "create": - create(root) - else: - verify(root) - - -if __name__ == "__main__": - main() diff --git a/applications/Volkov/tests/p9_mkdir_probe.c b/applications/Volkov/tests/p9_mkdir_probe.c deleted file mode 100644 index 784ffde..0000000 --- a/applications/Volkov/tests/p9_mkdir_probe.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * p9_mkdir_probe.c — target-регрессия validator и libc mkdir/rmdir. - */ - -#include -#include -#include -#include -#include -#include "sc_path.h" - -static int result_fd = -1; -static uint8_t failures; -static char cwd_before[256]; -static char cwd_after[256]; -static char joined[256]; - -static void log_text(const char *text) -{ - const char *end = text; - while (*end != '\0') end++; - if (result_fd >= 0) { - (void)write(result_fd, text, (uint16_t)(end - text)); - } -} - -static void check(uint8_t condition, const char *name) -{ - log_text(condition ? "PASS " : "FAIL "); - if (!condition) failures++; - log_text(name); - log_text("\r\n"); -} - -int main(void) -{ - int rc; - - result_fd = open("D:\\MKRES.TXT", O_WRONLY | O_CREAT | O_TRUNC); - check(sc_path_validate_component("A") == 0 && - sc_path_validate_component("ABCDEFGH") == 0 && - sc_path_validate_component("ABCDEFGH.XYZ") == 0 && - sc_path_validate_component("A_B-1.2") == 0, - "valid DOS 8.3 components accepted"); - check(sc_path_validate_component("") != 0 && - sc_path_validate_component(".") != 0 && - sc_path_validate_component("..") != 0 && - sc_path_validate_component("ABCDEFGHI") != 0 && - sc_path_validate_component("A.ABCD") != 0 && - sc_path_validate_component("A.B.C") != 0, - "length and dot violations rejected"); - check(sc_path_validate_component("BAD\\NAME") != 0 && - sc_path_validate_component("BAD/NAME") != 0 && - sc_path_validate_component("*.BIN") != 0 && - sc_path_validate_component("BAD NAME") != 0, - "path injection and wildcard rejected"); - check(sc_path_join(joined, sizeof(joined), - "D:\\MKTEST", "NEWDIR") == 0 && - strcmp(joined, "D:\\MKTEST\\NEWDIR") == 0, - "validated component joins full path"); - - check(getcwd(cwd_before, sizeof(cwd_before)) != 0, - "CWD captured before mkdir"); - (void)rmdir("D:\\MKTEST\\PROBE"); - errno = 0; - rc = mkdir("D:\\MKTEST\\PROBE"); - check(rc == 0, "mkdir creates directory"); - errno = 0; - rc = mkdir("D:\\MKTEST\\PROBE"); - check(rc != 0 && (errno == EEXIST || errno == EISDIR), - "duplicate mkdir is managed error"); - check(rmdir("D:\\MKTEST\\PROBE") == 0, - "rmdir removes probe directory"); - check(getcwd(cwd_after, sizeof(cwd_after)) != 0 && - strcmp(cwd_before, cwd_after) == 0, - "mkdir/rmdir preserve process CWD"); - - if (failures == 0u) log_text("ALL PASS\r\n"); - if (result_fd >= 0) (void)close(result_fd); - return failures; -} diff --git a/applications/Volkov/tests/run_mame_hdd.sh b/applications/Volkov/tests/run_mame_hdd.sh deleted file mode 100755 index e852498..0000000 --- a/applications/Volkov/tests/run_mame_hdd.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh -# Запускает MAME с локальным тестовым CHD как hard2 (D:) и заданным Lua. -# Состояние эмулятора изолировано; каталог кадров задаёт вызывающий. - -set -eu - -if [ "$#" -lt 3 ] || [ "$#" -gt 4 ]; then - echo "usage: $0 IMAGE.chd SCENARIO.lua SNAPSHOT_DIR [TIMEOUT]" >&2 - exit 1 -fi - -PROJECT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -if [ -z "${SPRINTER_ROOT:-}" ] && [ -f "$PROJECT_DIR/../../bin/sprinter-cc" ]; then - SPRINTER_ROOT="$PROJECT_DIR/../.." -fi -if [ -z "${MAME_HOME:-}" ] && [ -f "$PROJECT_DIR/../../mame/v306/mame.arm" ]; then - MAME_HOME="$PROJECT_DIR/../../mame/v306" -fi -: "${SPRINTER_ROOT:?задайте SPRINTER_ROOT для запуска теста}" -export MAME_HOME -IMAGE=$(CDPATH= cd -- "$(dirname -- "$1")" && pwd)/$(basename -- "$1") -LUA=$(CDPATH= cd -- "$(dirname -- "$2")" && pwd)/$(basename -- "$2") -SNAPSHOT_DIR=$(mkdir -p "$3" && CDPATH= cd -- "$3" && pwd) -TIMEOUT=${4:-75} - -exec python3 "$SPRINTER_ROOT/toolchain/run_sprinter_mame.py" \ - --hdd "$IMAGE" --script "$LUA" --snapshot-dir "$SNAPSHOT_DIR" \ - --timeout "$TIMEOUT" --video opengl diff --git a/docs/TODO.md b/docs/TODO.md index df68f5f..520829c 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -279,6 +279,12 @@ Quick wins: ## Прочий backlog +- [ ] **Несколько одновременных экземпляров MAME и MCP bridge (отдельная + задача, позже).** Проверить два изолированных процесса с разными + дискетами/CHD/state и двумя MCP-сессиями; в протоколе явно связывать + команду с PID/session ID, не допускать ответа от чужого процесса, + проверить сброс/exit и одновременные команды. До успешного end-to-end + теста параллельная работа MAME через MCP не гарантируется. - [ ] factoring parse_argv из crt0/crt0_banked в общий argv.s - [ ] `restore SP on EXIT` (паттерн z88dk +pps) — проверить нужность - [x] ~~CI: MAME с -aviwrite для screenshot-сравнения без человека~~ — diff --git a/docs/host-tests-plan.md b/docs/host-tests-plan.md index 0cc50fd..6c48b82 100644 --- a/docs/host-tests-plan.md +++ b/docs/host-tests-plan.md @@ -2,7 +2,7 @@ Статус: **не начато**, задача на будущее. Обвязка уже готова и обкатана — `testkit/` (см. `testkit/README.md`); первый потребитель — -`applications/PoP/roomtest/tests-host/`. Этот документ — про то, как накрыть +`../Applications/PoP-Archive/roomtest/tests-host/`. Этот документ — про то, как накрыть тем же способом основной продукт репозитория. ## Что это НЕ заменяет diff --git a/docs/im2_isr_design.md b/docs/im2_isr_design.md index c9f89c1..de4fd70 100644 --- a/docs/im2_isr_design.md +++ b/docs/im2_isr_design.md @@ -387,7 +387,7 @@ User's ISR НЕ должен: ## Phase 1 acceptance -- `examples/irq_test/` — счётчик тиков растёт с 50 Hz +- `../Examples/irq_test/` — будущий пример: счётчик тиков растёт с 50 Hz - Клавиатура продолжает работать через DSS chain (можно прервать тест клавишей) - Корректный exit — DSS shell получает управление обратно без crash - Работает во всех memory modes (tiny, small, big, huge) diff --git a/docs/kbd-games.md b/docs/kbd-games.md index b9be025..a13f279 100644 --- a/docs/kbd-games.md +++ b/docs/kbd-games.md @@ -2,7 +2,7 @@ Как правильно читать клавиатуру в play-цикле: что даёт платформа, чего она НЕ даёт, и какие паттерны использовать. Выжато из порта Prince of -Persia (applications/PoP/roomtest) и отладки багов «залипание клавиш» +Persia (`../Applications/PoP-Archive/roomtest`) и отладки багов «залипание клавиш» (2026-07-16) и «отвал Shift» (2026-07-22). ## Почему не ESTEX @@ -130,4 +130,4 @@ kbd_raw_close(); // вернуть клавиатуру DSS (ест сохраняются), см. libc/kbd/kbd_raw_sync.c. Связанные документы: docs/mame-autotest.md, docs/im2_isr_design.md, -docs/converted/IvanMak.txt §9.4, applications/PoP/docs/PORT_PLAN.md §2. +docs/converted/IvanMak.txt §9.4, `../Applications/PoP-Archive/docs/PORT_PLAN.md` §2. diff --git a/docs/libc-reference.md b/docs/libc-reference.md index a68313d..4ceceeb 100644 --- a/docs/libc-reference.md +++ b/docs/libc-reference.md @@ -269,7 +269,7 @@ tests/bgi_img. колоночный accel-проход: STOP между чтением и записью позволяет безопасно сменить Port_Y, промежуточный RAM-буфер не нужен). Копия = скролл + heal цели (банк 0x50). Открывшуюся полосу |d| не заполняют — возвращают в -*dirty (NULL = не нужно). Пример: examples/scroll. +*dirty (NULL = не нужно). Пример: `../Examples/scroll`. Для произвольных EMM-данных, которые затем маппятся в W0, `gfx_w0_page_prepare(page)` устанавливает IRQ/NMI-стабы в `0x38/0x66` и запоминает штатную DSS-страницу. `atlas_load()` вызывает её автоматически. @@ -306,7 +306,7 @@ banked-функции; только volatile-глобалы и быстрая р ## — эксклюзивный raw-канал клавиатуры (вектор 0xFF, свой путь) -Мотивация — applications/PoP/docs/PORT_PLAN.md §2: ESTEX +Мотивация — `../Applications/PoP-Archive/docs/PORT_PLAN.md` §2: ESTEX kbhit/getch/getkey/kbd_mod_state — событийные, без held-state для обычных (не модификаторных) клавиш. Decode make/break (PS/2 Scan Code Set 2: `0xF0` — префикс отпускания, `0xE0` — префикс расширенной @@ -332,7 +332,7 @@ UP/DOWN/RIGHT — из этого сделан ОШИБОЧНЫЙ вывод « нажать». На самом деле причина была в неудачном таймінге снимков относительно дуги прыжка, а не в отсутствии сигнала. Подтверждено брейкпоинтом в отладчике MAME (adress уровня приложения на входе в -код `jumping=1` в `applications/PoP/poc/poc.c`): `press_key('up')` +код `jumping=1` в `../Applications/PoP-Archive/poc/poc.c`): `press_key('up')` ЧЕСТНО доходит до raw-декодера и триггерит код прыжка — брейкпоинт сработал ровно один раз за одно удержание клавиши (это же заодно подтвердило фикс двойного триггера через `up_prev` edge-detect в @@ -373,7 +373,7 @@ FIFO SIO — 3 байта, и рассчитывать на «каждый ба опрос луча, а это ~2/3 периода кадра. Проверено: 35 нажатий стрелки с зажатым Shift → 35 дошедших make против 9 из 10 без хука. Остаточные редкие потери возможны (пачка целиком внутри DI-окна одного accel-прохода); -полный протокол и что делать дальше — `applications/PoP/roomtest/TASKS_CLOSED.md`, +полный протокол и что делать дальше — `../Applications/PoP-Archive/roomtest/TASKS_CLOSED.md`, задача KBD-1. **ГЛАВНОЕ СЛЕДСТВИЕ:** пока `kbd_raw_open()` активен, `kbhit/getch/getkey/ @@ -387,7 +387,7 @@ MAME дёргает ОБЕ клавиатуры (PC ms_naturl + ZX-матриц одновременно — наблюдался паразитный незатухающий бит от ZX-пути (обычный код без EXT-префикса, застревал в DOWN) — не воспроизводится на реальном сценарии (только PC/AT-клавиатура, без матрицы, см. -applications/PoP/docs/PORT_PLAN.md §2 — пользователь подтвердил, что +`../Applications/PoP-Archive/docs/PORT_PLAN.md` §2 — пользователь подтвердил, что матрица на Sprinter давно не используется). Для будущих MAME-тестов этой функции — бить только по `:kbd:ms_naturl:*`, не через удобный `press_key` (или перепроверить точную семантику полей моста). diff --git a/docs/mame-autotest.md b/docs/mame-autotest.md index 7f000c1..c10892c 100644 --- a/docs/mame-autotest.md +++ b/docs/mame-autotest.md @@ -17,21 +17,21 @@ make -C tests/bgitest # запустить в MAME, снять экран, выйти по таймауту python3 toolchain/mame_interactive.py tests/bgitest/bgitest.exe \ - --snap 12,14 --timeout 16 + --mame-home /путь/к/MAME/runtime --snap 12,14 --timeout 16 ``` Инструмент сам: -1. проверит, что нет висящих копий MAME; -2. положит `.exe` на дискету A: (`mame/v306/IMG/mc.img`); +1. соберёт отдельную временную FAT12-дискету с `.exe`; +2. создаст отдельные каталоги MAME для cfg/NVRAM/diff/snapshot; 3. запустит MAME с драйвером `sprinter`; -4. дождётся приглашения `C:\>` и **напечатает `a:\bgitest.exe` + Enter** - (эмулируя нажатия клавиш); +4. в момент `--launch-at` **напечатает `a:\bgitest.exe` + Enter** + (эмулируя нажатия клавиш); выбирайте время после загрузки DSS; 5. снимет скриншоты в указанные секунды эмулированного времени; 6. завершит сессию по таймауту; 7. выведет пути к PNG-скриншотам. -Скриншоты лежат в `mame/v306/snap_auto/sprinter/` (`0000.png`, `0001.png`, -…). Их читает Claude визуально (инструментом Read) — текст с экрана +Скриншоты лежат в `build/mame-autotest//sprinter/` (`0000.png`, +`0001.png`, …) относительно каталога запуска. Их можно читать визуально — текст с экрана программно НЕ распознаётся. --- @@ -40,7 +40,8 @@ python3 toolchain/mame_interactive.py tests/bgitest/bgitest.exe \ ``` python3 toolchain/mame_interactive.py [exe] [--data f ...] \ - [--launch-at T] [--step "T:TEXT" ...] [--snap t1,t2,...] [--timeout N] + [--mame-home DIR] [--mame-bin FILE] [--launch-at T] \ + [--step "T:TEXT" ...] [--snap t1,t2,...] [--timeout N] ``` | Аргумент | Назначение | @@ -51,6 +52,9 @@ python3 toolchain/mame_interactive.py [exe] [--data f ...] \ | `--step "T:TEXT"` | в момент `T` сек напечатать `TEXT`. Можно много раз — диалог с уже запущенной программой. В `TEXT`: `\n`=Enter, `\t`=Tab; заглавные и символы через Shift — автоматически. | | `--snap t1,t2,...` | секунды эмуляции для скриншотов. По умолчанию: `launch_at+4` и `+6` (для `exe`), либо сразу после последнего ввода. | | `--timeout N` | секунд эмуляции до принудительного выхода. По умолчанию — чуть позже последнего скриншота. | +| `--mame-home DIR` | установленная среда MAME; можно задать переменной `MAME_HOME`. | +| `--mame-bin`, `--mame-rompath`, `--mame-dss-image`, `--mame-system-hdd-image`, `--mame-bios` | выбор отдельных частей профиля; соответствуют `MAME_*` из окружения. | +| `--snapshot-dir DIR` | локальный каталог кадров вместо `build/mame-autotest`. | **Важно:** все времена — это **секунды эмулированного времени от старта машины** (не от нажатий, их «нет»). Загрузка DSS до `C:\>` занимает @@ -72,7 +76,7 @@ python3 toolchain/mame_interactive.py tests/menu/menu.exe \ --step "13:2\n" --snap 15 --timeout 17 # 4. Тест с файлом-данными на дискете -python3 toolchain/mame_interactive.py examples/mdview2/mdview2.exe \ +python3 toolchain/mame_interactive.py /путь/к/Examples/mdview2/mdview2.exe \ --data doc.md --step "13:mdview2 doc.md\n" --snap 16 --timeout 18 ``` @@ -82,11 +86,12 @@ python3 toolchain/mame_interactive.py examples/mdview2/mdview2.exe \ ### 3.1 Запуск MAME -Нативный `mame.arm` (arm64) в `mame/v306/`, драйвер `sprinter`, BIOS -v3.06. Полный набор аргументов зашит в `COMMON_ARGS` внутри скрипта: -две дискеты (A: наш `mc.img`, B: DSS 1.71u), два HDD-образа (система + -медиа), CD-ROM, ZX-Bus карта NeoGS, `-video opengl -window`, -`-snapshot_directory`, `-autoboot_script <сгенерированный .lua>`. +Профиль `MAME_HOME` выбирает `mame.arm`, `roms/`, DSS-дискету и системный +CHD. A: — временная дискета этого запуска; B: — DSS. При наличии в +установленной среде CD/NeoGS/медиа скрипт подключает их как необязательные +устройства. `-cfg_directory`, `-nvram_directory`, `-diff_directory` и +`-snapshot_directory` указывают в изолированный каталог сессии; +`-autoboot_script` получает сгенерированный Lua-файл. ### 3.2 Ввод с клавиатуры — ключевой момент @@ -118,7 +123,7 @@ AT-клавиатуре есть (поле `P2.1`/0x4) — путь `a:\name.exe ### 3.3 Тайминг (Lua) -Генерируется `_interactive_gen.lua`, который через +Временный Lua-файл через `emu.register_periodic` на каждом кадре сверяет **эмулированное время** и проигрывает события ввода, снимает скриншоты и завершает сессию. @@ -132,32 +137,33 @@ AT-клавиатуре есть (поле `P2.1`/0x4) — путь `a:\name.exe ### 3.4 Скриншоты `manager.machine.video:snapshot()` пишет PNG в каталог из -`-snapshot_directory` (`mame/v306/snap_auto/sprinter/`). Скрипт перед -запуском чистит этот каталог, потом печатает пути к готовым файлам. -Claude открывает их инструментом Read (визуальный анализ). +`-snapshot_directory` внутри `build/mame-autotest//sprinter/`. +Каждый запуск создаёт свой каталог и печатает пути к PNG; другие сессии не +затрагиваются. ### 3.5 Завершение сессии -Два рубежа, чтобы MAME гарантированно не «завис»: +Два рубежа, чтобы MAME гарантированно завершился: - в Lua: при `elapsed >= timeout` → `manager.machine:exit()` (чистый выход); -- снаружи: shell-обёртка `timeout ./mame.arm …` как страховка. +- снаружи: `subprocess` ожидает с ограничением wall-clock времени, затем + завершает только собственный процесс MAME. --- ## 4. Предпосылки (окружение) -- **MAME**: `mame/v306/mame.arm` + образы в `mame/v306/IMG/` - (`mc.img` — наш перезаписываемый A:, `dss171u.img`, HDD `.chd`, CD - `.iso`). Каталог `mame/` целиком в `.gitignore` — поэтому инструмент - живёт в `toolchain/`, а не рядом с MAME. +- **MAME**: задайте `MAME_HOME=/путь/к/MAME/runtime` с бинарником, + `roms/`, `IMG/dss171u.img` и `IMG/sp_hdd_sys.chd`. Исходники fork не нужны. - **Загрузка должна доходить до `C:\>`.** `system.bat` на системном диске НЕ должен автоматически запускать Flex Navigator или приложение — иначе мы не попадём на командную строку и ввод уйдёт в чужую программу. (Это файл на HDD-образе, вне репозитория; правится один раз.) -- **Нет висящих копий MAME.** Несколько одновременных инстансов пишут в - один `mc.img` и дают недостоверный результат. Скрипт проверяет это сам - (`pgrep`), но при ручных запусках MAME — проверяйте `ps aux | grep mame`. +- **Одновременный запуск нескольких MAME не проверялся.** Автотест создаёт + отдельные A:, state и снимки и при необходимости копирует системный CHD, + но это ещё не доказывает корректность параллельной работы. Особенно не + гарантируется работа нескольких процессов через MCP bridge: выбор процесса, + идентификатор сессии и арбитраж требуют отдельной проверки. --- @@ -237,14 +243,14 @@ end анимацией/звуком, где скриншотов мало. Пока не подключено к скрипту. - **Ручная отладка ввода.** Запуск MAME с `-console` даёт интерактивный Lua-REPL — удобно нащупывать поля/тайминги вживую перед скриптованием. -- **Второй видеорежим/варианты BIOS** — при необходимости менять - `COMMON_ARGS`. +- **Второй видеорежим/варианты BIOS** — выбирать `MAME_BIOS` или отдельные + аргументы профиля. -## 10. Тайминги MCP-моста (run_bridge.sh) — НЕ ждать дольше +## 10. Архивные тайминги старого MCP-моста -Запуск через `mame/v306/run_bridge.sh` + `bridge_cmd.sh` / MCP `mame-z80`. -Паузы ниже — измеренные на этой машине; ждать дольше бессмысленно, а -привычка ставить `sleep 30..60` съедает минуты на каждый прогон: +Ниже сохранены измерения прежнего ручного запуска через `run_bridge.sh` и +`bridge_cmd.sh`. Они не задают таймауты нового автотеста и DAP: автотест +использует изолированные носители, а DAP ждёт стабильный prompt DSS в VRAM. | шаг | пауза ПОСЛЕ шага | |-----|------------------| diff --git a/docs/mame-source-debug-status.md b/docs/mame-source-debug-status.md index d953061..1bb1b42 100644 --- a/docs/mame-source-debug-status.md +++ b/docs/mame-source-debug-status.md @@ -293,6 +293,9 @@ host-моста пока не готова: FileBridge использует `fcn живой bridge-тест не использует такой скрипт. Повторная загрузка autoboot на reset защищена от дублирования callback. Старый mamebridge параллельно с sdbgbridge не загружать: общая арбитрирующая сессия ещё не реализована. +Два одновременно запущенных процесса MAME через MCP bridge не проверялись; +корректная маршрутизация команд между ними не гарантируется. Это отдельная +отложенная задача в [TODO.md](TODO.md). ## Размерный регресс и оставшаяся работа diff --git a/docs/mame-source-debug.md b/docs/mame-source-debug.md index 8202b36..d08a556 100644 --- a/docs/mame-source-debug.md +++ b/docs/mame-source-debug.md @@ -55,8 +55,10 @@ CLI или MCP. [sprinter-cc](../bin/sprinter-cc), [app.mk](../app.mk), [mame_interactive.py](../toolchain/mame_interactive.py), [bank.s](../runtime/bank.s), [crt0_banked.s](../runtime/crt0_banked.s). -Локальная интеграция: `mame/sources/MAME/plugins/mamebridge/init.lua`, -`mame/sources/MAME/src/mame_mcp.py`. +MAME fork теперь самостоятельный проект: `MAME/plugins/mamebridge/init.lua`, +`MAME/src/mame_mcp.py`; среда запуска задаётся `MAME_HOME`, а исходники +fork приложению не нужны. Расширение VS Code находится в отдельном +`VSCode-Sprinter`; Python DAP backend остаётся в SDK. ## 2. Доказательства и пределы выводов @@ -559,7 +561,7 @@ Stdout адаптера содержит только DAP, диагностик ### 8.3 Разработка и запуск из редактора **Архитектурное решение:** Sprinter-специфичный цикл реализуется собственным -расширением `toolchain/vscode-sprinter-debug`. Готовые C/C++ или clangd можно +расширением `VSCode-Sprinter`. Готовые C/C++ или clangd можно использовать для подсветки, completion и навигации, а VS Code Tasks и Debug UI — как стандартные интерфейсы. Они не знают ABI SDCC/Z80, пакет `.sprinter-cc-*`, DSS, банковую адресацию и протокол MAME, поэтому не могут diff --git a/docs/mdview_guide_and_test_patterns.md b/docs/mdview_guide_and_test_patterns.md index 222baf6..4207591 100644 --- a/docs/mdview_guide_and_test_patterns.md +++ b/docs/mdview_guide_and_test_patterns.md @@ -41,7 +41,7 @@ ## Сборка и запуск ```bash -make -C examples/mdview +make -C ../Examples/mdview ``` Запуск на целевой системе: diff --git a/docs/project-reorganization.md b/docs/project-reorganization.md index 6e059f2..147e9c9 100644 --- a/docs/project-reorganization.md +++ b/docs/project-reorganization.md @@ -1,7 +1,7 @@ # Разделение Sprinter-CC, MAME и приложений -Статус: реализация. Контракт SDK и локальных образов готов; выделение Git -репозиториев и физический перенос выполняются. Дата: 2026-09-15. +Статус: физическое разделение выполнено; завершаются локальные коммиты, +проверка документов и публикация Sprinter-CC. Дата обновления: 2026-09-16. ## Цель и границы @@ -26,24 +26,40 @@ MAME приложению не нужны. └── VSCode-Sprinter/ # Git-репозиторий расширения VS Code ``` -Имена `C-Compiler`, `Volkov` и `PoP-Archive` пока локальные; адреса Git remote -новых репозиториев будут добавлены после создания доступных для записи URL. +Имена `C-Compiler`, `Volkov` и `PoP-Archive` приняты для локальной раскладки; +адреса Git remote новых репозиториев будут добавлены после создания доступных +для записи URL. Отсутствие remote не мешает сохранить независимую локальную историю. Папка `Applications/` не становится общим репозиторием: каждый продукт имеет свою историю, релизы, игнорируемые ресурсы и тесты. Физическое расположение рядом удобно, но не входит в контракт сборки. +Локальные точки истории после разделения: + +| Репозиторий | Split/base | Итоговый локальный commit | +|---|---|---| +| Examples | `502d735` | `a76852d` | +| SprPoP | `4507ba9` | `bd300b3` | +| Volkov | `a71e3c5` | `3c1956b` | +| PoP-Archive | `716c639` | `54d0b3d` | +| VSCode-Sprinter | `68a821e` | `901d91a` | +| MAME | baseline `b0c4527c`, backend/MCP `fe7ee37a` | `37b89b78` | + +Первые пять base-коммитов получены из подготовительного коммита Sprinter-CC +через `git subtree split`; поэтому их история прослеживается до исходного +монорепозитория. У выделенных репозиториев пока нет настроенных remote. + ## Что принадлежит каждому проекту | Проект | Что остаётся/переходит | Причина | |---|---|---| | Sprinter-CC | `bin/`, `runtime/`, `libc/`, `libbgi/`, `lib/`, общие `toolchain/`, `tests/`, `testkit/`, `app.mk`, справочник API, платформенные исследования, `release_docs/`, рецепт SDCC в `third_party/` | Это target SDK, его ABI, инструменты и собственные регрессионные тесты. libc/libbgi пока остаются вместе: их версия и ABI тесно связаны с `sprinter-cc`; дальнейшее выделение возможно после стабильного интерфейса. | -| MAME | Нынешний `mame/sources/MAME` со своим Git, изменения драйвера/OSD, патч debugger backend, общие `mamebridge` и `mame_mcp.py`, рецепты stock/sdbg сборок | Изменения ядра и общий транспорт MCP должны проверяться и выпускаться вместе с конкретной ревизией MAME. | -| Examples | Нынешние `examples/balls`, `mdview`, `mdview2`, `rpgwalk`, `scroll`, `space` и относящиеся к ним ресурсы/документы | Это демонстрации SDK с общей версией. Один репозиторий избегает множества мелких релизов. | -| SprPoP | Всё `applications/SprPoP/`: исходники, конверторы, тесты, собственные ресурсы и планы | Уже почти автономное приложение; оригинальные ресурсы и дальше остаются внешними. | -| Volkov | Всё `applications/Volkov/`: исходники, сценарии MAME, собственные тестовые носители и документы | Продукт и его проверки должны развиваться без дерева тулкита. | -| PoP-Archive | `applications/PoP/` как архив прежних PoC/roomtest и исследований | Не смешивать прежнюю историю с активным SprPoP. Если архив не нужен как рабочий клон, сохранить Git-историю и документы в отдельном архивном репозитории. | -| VSCode-Sprinter | Нынешний `toolchain/vscode-sprinter-debug`: extension, задачи сборки, конфигурации и тесты клиентской части | У расширения свои версии, упаковка VSIX и цикл обновления. Отладочный Python backend остаётся в SDK. | +| MAME | Выделенный `../MAME` со своим Git, изменения драйвера/OSD, патч debugger backend, общие `mamebridge` и `mame_mcp.py`, рецепты stock/sdbg сборок | Изменения ядра и общий транспорт MCP должны проверяться и выпускаться вместе с конкретной ревизией MAME. | +| Examples | Выделенный `../Examples`: `balls`, `mdview`, `mdview2`, `rpgwalk`, `scroll`, `space` и относящиеся к ним ресурсы/документы | Это демонстрации SDK с общей версией. Один репозиторий избегает множества мелких релизов. | +| SprPoP | Всё из прежнего `applications/SprPoP/`: исходники, конверторы, тесты, собственные ресурсы и планы | Уже почти автономное приложение; оригинальные ресурсы и дальше остаются внешними. | +| Volkov | Всё из прежнего `applications/Volkov/`: исходники, сценарии MAME, собственные тестовые носители и документы | Продукт и его проверки должны развиваться без дерева тулкита. | +| PoP-Archive | Прежний `applications/PoP/` как архив PoC/roomtest и исследований | Не смешивать прежнюю историю с активным SprPoP. Архив сохранён самостоятельным рабочим репозиторием. | +| VSCode-Sprinter | Выделенный `../VSCode-Sprinter`: extension, задачи сборки, конфигурации и тесты клиентской части | У расширения свои версии, упаковка VSIX и цикл обновления. Отладочный Python backend остаётся в SDK. | `applications/DN/DosNavigator` и вложенные чужие клоны в PoP/Volkov — референсы, не Sprinter-приложения. Их не превращать в продуктовые репозитории @@ -67,8 +83,8 @@ MAME приложению не нужны. переноса такой путь может случайно указывать на чужой каталог. `MAME_HOME` — необязательный для сборки путь к подготовленной **среде запуска** -MAME, а не к его исходникам. Стандартные пути соответствуют нынешнему -`mame/v306` и допускают переопределение из окружения, аргументов `make` или +MAME, а не к его исходникам. Стандартные пути внутри `MAME_HOME` задают +контракт установленной среды и допускают переопределение из окружения, аргументов `make` или локального игнорируемого файла настроек. Если `MAME_HOME` не задан, необходимые пути можно указать отдельно; отсутствие обоих источников диагностировать в цели запуска, не превращая пустое значение в `/mame.arm`: @@ -103,7 +119,9 @@ DAP-launch должны брать один источник настройки, `IMG/mc.img` и `IMG/test_hdd.chd` больше не являются выходами сборки приложения. `make hdd` готовит локальный образ; `make run` запускает его без `mame-link`. Для нескольких сценариев создавать отдельные носители и -state-каталоги, чтобы параллельный запуск не перезаписывал чужой тест. +state-каталоги, чтобы один запуск не перезаписывал чужие файлы. Одновременная +работа нескольких процессов MAME, особенно с MCP bridge, не проверялась и +не гарантируется; отдельная отложенная задача есть в [TODO.md](TODO.md). Конфигурация разработчика не содержит абсолютных личных путей в Git. Для `.img` нужен упаковщик из SDK; для `.chd` нынешний `make_hdd.sh` дополнительно использует внешние `mtools` и `chdman`. Зафиксировать эти @@ -179,16 +197,13 @@ MAME документируются вместе; extension не копируе ## История Git, внешние ресурсы и совместимость -До выделения репозиториев инвентаризировать незакоммиченные/неотслеживаемые -файлы в корне и отдельный dirty checkout MAME. Среди новых файлов сейчас -находятся DAP, extension, MCP-плагин, MAME patch и документы: простое -`git subtree split` по старому HEAD их потеряет. Сначала сохранить работу -в подходящих коммитах/ветках или проверенных патчах; не сбрасывать и не -перезаписывать пользовательские изменения. Затем выделить историю -`examples/`, `applications/SprPoP/`, `applications/Volkov/` и PoP через -`git subtree split` либо `git filter-repo`, проверяя состав каждого нового -Git дерева. Для MAME использовать его существующую историю, а не историю -родительского SDK, где `mame/` игнорируется. +Перед выделением были инвентаризированы незакоммиченные/неотслеживаемые +файлы в корне и отдельный dirty checkout MAME. Работу DAP, extension, +MCP-плагина, MAME patch и документов сначала сохранили в подготовительном +коммите. Истории `examples/`, `applications/SprPoP/`, +`applications/Volkov/` и PoP выделены через `git subtree split`, после чего +каждый результат импортирован как `main` самостоятельного Git-репозитория. +MAME сохранил свою исходную историю и не получил историю родительского SDK. Новый `.gitignore` каждого проекта покрывает собственные `.exe`, объекты, debug packages, носители, снимки, внешние источники и секреты локального @@ -207,47 +222,66 @@ debug packages, носители, снимки, внешние источник ## Порядок работ и критерии готовности -1. **Зафиксировать текущую базу.** Список tracked/untracked файлов, +1. **Выполнено — зафиксировать текущую базу.** Список tracked/untracked файлов, отдельный Git MAME, лицензии/внешние ресурсы, базовые результаты сборок и smoke-тестов. Сохранить незавершённую отладочную и прикладную работу. Критерий: никакой исходник не теряется при выделении истории. -2. **Стабилизировать контракт SDK.** В `app.mk` разделить build и run, +2. **Выполнено — стабилизировать контракт SDK.** В `app.mk` разделить build и run, реализовать `SPRINTER_ROOT`, `MAME_HOME` и переопределения, упаковку локального образа с проверкой `mtools`/`chdman`, общий профиль путей и диагностику отсутствующих файлов. Перевести автотесты/launcher без переноса дерева. Критерий: приложение собирается без MAME и запускается с нестандартным `MAME_BIN`/ROM/DSS/System HDD. -3. **Подготовить MAME отдельно.** Сохранить fork с точной baseline-revision, +3. **Выполнено локально — подготовить MAME отдельно.** Сохранить fork с точной baseline-revision, stock/sdbg сборки и проверенным способом подготовки `MAME_HOME`. Перенести MAME patch/общий MCP к их владельцу, проверить обоих провайдеров. Критерий: два бинарника существуют одновременно, ROM/CHD и state не коммитятся, patched DAP launch проверен. -4. **Выделить Examples.** Переписать относительные пути и источник RPG +4. **Выполнено — выделить Examples.** Переписать относительные пути и источник RPG графики, определить формат релиза SDK+Examples. Критерий: каждый пример собирается из собственного клона Examples при одном `SPRINTER_ROOT`, локальные диски не затрагивают MAME или соседние проекты. -5. **Выделить приложения по одному.** Сначала SprPoP как наиболее близкий +5. **Выполнено — выделить приложения по одному.** Сначала SprPoP как наиболее близкий к автономному контракту, затем Volkov с локальным viewer fixture, затем архив PoP с его референсами. Переписать тесты и run-скрипты, сохраняя их документы и историю. Критерий: каждый продукт собирается и проходит доступные host/MAME проверки из изолированного клона без Examples и других приложений. -6. **Выделить extension.** Научить VS Code находить SDK DAP независимо от +6. **Реализовано; остаётся ручная проверка установленного VSIX после разделения — выделить extension.** Научить VS Code находить SDK DAP независимо от workspace, запускать build/run/debug приложения и показывать ошибки разрешения путей/версий. Проверить VSIX в отдельном workspace SprPoP или Volkov, а не только в `C-Compiler`. Критерий: F5 строит debug package, ждёт DSS, доходит до `main`, принимает breakpoint/logpoint и клавиатуру; опция родного окна MAME работает на macOS. Windows остаётся явно ограниченной до отдельной реализации host transport. -7. **Очистить SDK и документы.** Заменить корневые цели, release-скрипт, +7. **Завершается — очистить SDK и документы.** Заменить корневые цели, release-скрипт, README, `AGENTS.md`, инструкции автотеста/отладки и рабочие ссылки. Проверить `make`, `make -C libc`, `make -C libbgi`, `make size-check`, SDK host/sdbg tests и отдельные проекты. Критерий: в SDK нет tracked приложений/примеров и игнорируемого вложенного MAME; инструкции используют новые пути, а архивные исследования остаются доступны. -Каждый этап заканчивается проверкой в отдельном клоне и просмотром Git diff, -а не одним успешным запуском в старом общем дереве. Реальные переносы, -новые Git remote и публикация релизов выполняются отдельной задачей после -согласования этого плана. +## Проверка реализации на 2026-09-16 + +Успешно выполнены сборка SDK (`make`), отдельные сборки libc/libbgi, +создание `build/media/toolkit-tests.img`, release-smoke, 36 Python-тестов +source debugger (один platform skip), 12 тестов extension, все Examples, +SprPoP в обычном и `SRC_DEBUG=1` режимах с локальным CHD, 17 host-наборов +SprPoP, Volkov и PoP-Archive PoC. Живые последовательные DAP-прогоны +подтвердили patched `sdbg`, stock MAME с `osx`, клавиатурный ввод, +`SDBG_LOG` в обеих консолях и запуск SprPoP с собственного `hard2` до +`main`. + +`make size-check` пока не принят: текущий baseline показывает 12 старых +увеличений (обычно +9 байт, `openenv` +188) и несколько исчезнувших прежних +тестов. Реорганизация не меняла libc/libbgi, поэтому эталон автоматически не +перезаписывался; расхождения нужно разобрать отдельно. После разделения ещё +нужна ручная проверка установленного VSIX в чистом workspace. Несколько +одновременных MAME, маршрутизация MCP между ними и Windows transport остаются +явно непроверенными сценариями. + +Каждый этап заканчивается проверкой в отдельном репозитории и просмотром Git +diff. Новые Git remote для выделенных репозиториев и публикация их релизов +остаются отдельной задачей. MAME пока не публикуется: существующий `origin` +доступен только для чтения. Sprinter-CC публикуется в своём прежнем remote. diff --git a/docs/sprite-api-design.md b/docs/sprite-api-design.md index ee1b5c0..22dd245 100644 --- a/docs/sprite-api-design.md +++ b/docs/sprite-api-design.md @@ -335,7 +335,7 @@ while (game) { } ``` -Курсор мыши (пример examples/, не API): то же самое с +Курсор мыши (пример в отдельном репозитории `../Examples`, не API): то же самое с `GFX_BANK_SPRITE`; фон под курсором живёт в ОЗУ-копии, никакой getimage/буфер не нужен. Перерисовка — из главного цикла по `mouse_getxy()`. @@ -451,7 +451,7 @@ getimage/буфер не нужен. Перерисовка — из главн цела (проверки A/B PASS). size-check: роста существующих программ нет (новые модули тянутся только пользователями API). - Демо **examples/balls** (2026-07-11): 8 разноцветных шаров 16×16 + Демо **../Examples/balls** (2026-07-11): 8 разноцветных шаров 16×16 (по спрайту на цвет, прозрачные углы) над чёрно-белой шахматкой; скорости 1..4 привязаны к кадрам (gfx_wait_vsync, speed = кадров на шаг: 50/25/~17/12.5 px/с — подтверждено покадровыми скриншотами), @@ -463,7 +463,7 @@ getimage/буфер не нужен. Перерисовка — из главн Требует зеркалирования палитры в палитру 1 (initgraph грузит EGA только в 0) и координат drawn[2][N] per-page. Побочно ушли и одно-кадровые артефакты перекрытий/снапшотов. -- **Фаза C — пример курсора** (examples/): мышь + putsprite/gfx_heal, +- **Фаза C — пример курсора** (`../Examples`): мышь + putsprite/gfx_heal, он же живой тест temp-режима. - Документация: libc-reference (раздел «Спрайты»), обновить TODO. @@ -505,7 +505,7 @@ getimage/буфер не нужен. Перерисовка — из главн ## 9.1 Эскиз managed-движка: sprite_t + retained-модель (v2, НЕ реализовано) -Мотивация — опыт examples/balls (2026-07-11): при двойной буферизации +Мотивация — опыт `../Examples/balls` (2026-07-11): при двойной буферизации приложение обязано помнить, где каждый спрайт РЕАЛЬНО нарисован на КАЖДОЙ из двух страниц (drawn[2][N]), и соблюдать двухпроходную дисциплину «heal все → блит все». Оба правила легко нарушить (heal по @@ -643,7 +643,7 @@ src[0] предчитывался до армирования и подстав перед триггером. **ФИКС СНЯТ 2026-07-13.** Точная dev-MAME (сборка разработчиков -Sprinter, `mame/sources/MAME`) эмулирует ПЛМ, подавляющую CPU-байт +Sprinter, `../MAME`) эмулирует ПЛМ, подавляющую CPU-байт триггера при активном burst'е — квирк был артефактом стоковой MAME 0.283. Проверено pixel-точно: tests/blitw, col0 @(200,150) = 0x02 GREEN по байтам VRAM (read_vram через MCP-мост). Для heal фикс был diff --git a/docs/vscode-sprinter-debug.md b/docs/vscode-sprinter-debug.md index 701bf8b..e5f21b5 100644 --- a/docs/vscode-sprinter-debug.md +++ b/docs/vscode-sprinter-debug.md @@ -15,8 +15,8 @@ ## Какие расширения нужны -Для build/run/debug используется собственное расширение этого проекта — -`toolchain/vscode-sprinter-debug`. Только оно знает формат source-debug +Для build/run/debug используется отдельный проект `VSCode-Sprinter`. Только +его расширение знает формат source-debug пакета, загрузку приложения через DSS, банки Sprinter и DAP-сессию MAME. Microsoft C/C++ или clangd можно поставить дополнительно ради completion, @@ -41,7 +41,7 @@ pyenv exec make -C tests/hello SRC_DEBUG=1 Из корня репозитория откройте VS Code с распакованным расширением: ```sh -code --extensionDevelopmentPath="$PWD/toolchain/vscode-sprinter-debug" "$PWD" +code --extensionDevelopmentPath="/путь/к/VSCode-Sprinter" "$PWD" ``` Если команда `code` не добавлена в `PATH`, на macOS этого проекта доступен @@ -50,10 +50,16 @@ code --extensionDevelopmentPath="$PWD/toolchain/vscode-sprinter-debug" "$PWD" ```sh "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code" \ --new-window \ - --extensionDevelopmentPath="$PWD/toolchain/vscode-sprinter-debug" \ + --extensionDevelopmentPath="/путь/к/VSCode-Sprinter" \ "$PWD" ``` +В локальных настройках workspace задайте `sprinterDebugger.sdkRoot` и +`sprinterDebugger.mameHome`. Первый указывает на установленный C-Compiler, +второй — на среду `MAME/runtime` с бинарником, ROM и DSS/CHD. Для выбора +stock/sdbg или нестандартной установки используются поля `mameBin`, +`mameRompath`, `mameDssImage`, `mameSystemHddImage`, `mameBios` профиля launch. + Расширение в режиме `auto` использует `~/.pyenv/shims/python` при наличии local `.python-version`; для внешнего workspace ищет установленный `~/.pyenv/versions/3.12*/bin/python`, затем `.venv/bin/python` и известные @@ -98,8 +104,29 @@ service-точка `main` и вводится `a:\\HELLO.EXE`. После сов наличие prompt всё равно обязательно. Перед вводом сохраняется диагностический снимок DSS. -Дополнительные файлы на floppy задаются массивом `data`, путь к другому MAME — -полем `mame`. +Для перенесённого SprPoP его workspace содержит локальный профиль F5: + +```json +{ + "type": "sprinter-mame", + "request": "launch", + "name": "Sprinter: SprPoP (VS Code)", + "build": "${workspaceFolder}/build/.sprinter-cc-sprpop", + "buildTarget": "hdd", + "appHdd": "${workspaceFolder}/build/hdd/sprpop.chd", + "launchPath": "d:\\games\\sprpop\\sprpop.exe" +} +``` + +Расширение перед F5 запускает `make SRC_DEBUG=1 hdd` в каталоге игры, +затем загружает EXE с её локального диска. Для такого профиля нужен +`SPRINTER_ROOT` только на этапе сборки и `MAME_HOME` при запуске. + +Дополнительные файлы на floppy задаются массивом `data`; для приложения с +собственным CHD задайте `appHdd`, `launchPath` и `buildTarget: "hdd"`. +Launcher монтирует временную копию CHD как `-hard2`, а DSS вводит путь EXE +из `launchPath`. Сам debug EXE остаётся также на временной A: для проверки +сигнатуры; несовпадение кода на диске с пакетом отладки останавливает launch. ## Ручная проверка VS Code @@ -117,7 +144,7 @@ service-точка `main` и вводится `a:\\HELLO.EXE`. После сов launcher дождётся prompt DSS, введёт `A:\\HELLO.EXE` и VS Code остановится в `main`. 4. Проверьте Call Stack, scope Registers и Debug Console. После Continue - должна сработать подтверждённая точка строки 31 по адресу `0x824b`. + должна сработать подтверждённая точка строки 31. 5. На остановке проверьте F11 и F10. Курсор должен переходить только после фактической остановки CPU, а не сразу после отправки команды. На строке 62 (`getchar`) нажмите F10, щёлкните окно Sprinter MAME и нажмите латинскую `x`: @@ -205,16 +232,19 @@ reset в native console обходят модель состояния VS Code; локального RPC и launcher; выбор `windows` решает только сторону MAME и не обеспечивает работу VS Code-интеграции. -Backend `sdbg` входит как воспроизводимый patch к MAME 0.287. Для локального -checkout достаточно: +Backend `sdbg` входит как воспроизводимый patch к MAME 0.287. Исходники и +рецепты теперь принадлежат самостоятельному проекту MAME: ```sh -make mame-sdbg +cd /путь/к/MAME +scripts/sprinter/build-variants.sh stock +scripts/sprinter/build-variants.sh sdbg ``` -Команда идемпотентно применяет -`toolchain/mame-patches/0001-sdbg-debugger-backend.patch`, инкрементально -собирает MAME и устанавливает `mame/v306/mame.arm`. +В fork patch уже зафиксирован в Git; для чистого baseline сохранён +`scripts/sprinter/apply-sdbg-patch.sh`. Каждый вариант копируется в +`MAME_HOME/bin/stock/mame.arm` или `MAME_HOME/bin/sdbg/mame.arm` без подмены +активного `MAME_HOME/mame.arm`. ## Logpoints diff --git a/examples/.gitignore b/examples/.gitignore deleted file mode 100644 index 1e6abea..0000000 --- a/examples/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -local.mk -build/ -.sprinter-cc-*/ -.resource-stamps/ -.disk_tmp/ -*.exe -*.asm -*.lst -*.lk -*.ihx -*.noi -*.sym -*.map -*.rel -*.cdb -*.mem -*.rst -__pycache__/ -*.py[cod] -.DS_Store -._* -.vscode/ diff --git a/examples/.python-version b/examples/.python-version deleted file mode 100644 index e4fba21..0000000 --- a/examples/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.12 diff --git a/examples/LICENSE b/examples/LICENSE deleted file mode 100644 index a738358..0000000 --- a/examples/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Sprinter C Compiler contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/examples/Makefile b/examples/Makefile deleted file mode 100644 index 455cf6c..0000000 --- a/examples/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Примеры Sprinter-CC: каждый образ и сборка принадлежат своему примеру. -EXAMPLES := balls mdview mdview2 rpgwalk scroll space -.PHONY: all clean $(EXAMPLES) - -all: $(EXAMPLES) - -$(EXAMPLES): - $(MAKE) -C $@ - -clean: - @for project in $(EXAMPLES); do $(MAKE) -C $$project clean || exit 1; done diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 2b8f604..0000000 --- a/examples/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Примеры Sprinter-CC - -Один репозиторий демонстраций SDK: `balls`, `mdview`, `mdview2`, -`rpgwalk`, `scroll`, `space`. Каждая программа собирается отдельно, а её -дискета находится в `build/media/` внутри каталога примера. - -Для сборки укажите путь к установленному SDK: - -```sh -export SPRINTER_ROOT=/путь/к/C-Compiler -make # все примеры -make -C mdview2 # один пример -make -C mdview2 floppy -``` - -Для запуска дополнительно укажите `MAME_HOME` — каталог подготовленной -среды MAME с `mame.arm`, `roms/` и `IMG/`. `MAME_BIN`, `MAME_ROMPATH`, -`MAME_DSS_IMAGE`, `MAME_SYSTEM_HDD_IMAGE`, `MAME_BIOS` переопределяют -отдельные пути/вариант. Персональные значения можно хранить в игнорируемом -`local.mk`; после перемещения репозитория соседство с SDK не требуется. -Исходник RPG-спрайта хранится в `third_party/16x16-RPG-characters` вместе -с лицензией и нужен только `rpgwalk`. diff --git a/examples/balls/Makefile b/examples/balls/Makefile deleted file mode 100644 index c47c1d3..0000000 --- a/examples/balls/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -EXAMPLE := balls -EXTRA_FLAGS ?= --gfx 256 -include $(CURDIR)/../sdk.mk diff --git a/examples/balls/balls.c b/examples/balls/balls.c deleted file mode 100644 index c3edad1..0000000 --- a/examples/balls/balls.c +++ /dev/null @@ -1,185 +0,0 @@ -/* - * balls — демо спрайтового движка v2 (): N шаров 16×16 летают - * над чёрно-белой шахматкой, отражаясь от краёв. Esc — выход. - * - * РАНЬШЕ (до движка) приложение вело drawn[2][N] координаты вручную и - * само соблюдало дисциплину «heal ВСЕ → блит ВСЕ» на скрытой странице. - * Теперь это работа движка: sprite_move меняет только состояние, - * sprite_flip(sp, N) за кадр делает draw-page=скрытая → heal все старые → - * блит все видимые (одна W3-скобка + один банк на проход) → vsync → флип. - * Учёт drawn[2] спрятан в sprite_t. Профиль/дизайн: - * docs/sprite-api-design.md §9.1. - * - * BOUNDARY-ТЕСТ клипа: диапазон движения выходит ЗА края экрана на - * половину спрайта — шары штатно наполовину уходят за все 4 края, - * включая ОТРИЦАТЕЛЬНЫЕ x/y слева/сверху. Клип _gfx_blit_clip (общий - * для putsprite и движка) обязан авто-обрезать: правый/нижний — урезать - * w/h, левый/верхний — сдвинуть начало источника (sx/sy) и урезать. - * - * Двойная буферизация: рисуем на скрытой странице, флип по vsync (иначе - * heal+блит на видимой ловятся лучом — мерцание). Палитру страницы 1 - * зеркалим из 0 (initgraph грузит EGA только в палитру 0). - * - * Скорость привязана к КАДРАМ (50 Гц): один проход цикла = один шаг. - */ - -#include -#include -#include -#include -#include - -#define FPS_METER 1 /* 1 = счётчик кадров/с слева-сверху (диагностика) */ - -#define NBALLS 16 /* количество шаров */ -#define BW 16 /* сторона спрайта */ -#define CELL 16 /* клетка шахматки */ - -/* Диапазон отражения — на пол-спрайта ЗА каждым краем (boundary-тест): - * x ∈ [-8 .. 312], y ∈ [-8 .. 248]. */ -#define MINY (0) /* -8: половина за левым/верхним краем */ -#define MINX (0) /* -8: половина за левым/верхним краем */ -#define MAXX (320 - BW) /* 312: половина за правым краем */ -#define MAXY (256 - BW) /* 248: половина за нижним краем */ - -/* Цвета шаров (не совпадают с фоном BLACK/WHITE) */ -static const uint8_t ball_colors[] = { - YELLOW, LIGHTRED, LIGHTGREEN, LIGHTCYAN, - LIGHTMAGENTA, LIGHTBLUE, RED, GREEN -}; -#define NCOLORS ((uint8_t)sizeof(ball_colors)) - -static sprite_t sp[NBALLS]; /* спрайты — движок ведёт drawn[2] */ -static int8_t vx[NBALLS], vy[NBALLS]; /* направление ±1 (физика — наша) */ -static uint8_t spr[NCOLORS][4 + BW * BW]; /* по спрайту на цвет */ -static uint8_t egapal[16 * 4]; - -static void build_sprites(void) -{ - uint8_t c; - int x, y; - - for (c = 0; c < NCOLORS; c++) { - uint8_t *p = spr[c]; - - *p++ = BW; *p++ = 0; *p++ = BW; *p++ = 0; - for (y = 0; y < BW; y++) - for (x = 0; x < BW; x++) { - int dx = 2 * x - (BW - 1), dy = 2 * y - (BW - 1); /* ×2: центр между пикселями */ - *p++ = (dx * dx + dy * dy <= 15 * 15) - ? ball_colors[c] : GFX_TRANSPARENT; - } - } -} - -static void draw_board(void) -{ - int cx, cy; - - for (cy = 0; cy < 256; cy += CELL) - for (cx = 0; cx < 320; cx += CELL) { - setfillstyle(SOLID_FILL, - (((cx ^ cy) >> 4) & 1) ? WHITE : BLACK); - bar(cx, cy, cx + CELL - 1, cy + CELL - 1); - } -} - -static void init_balls(void) -{ - uint8_t i; - - for (i = 0; i < NBALLS; i++) { - sprite_init(&sp[i], spr[i % NCOLORS]); - sp[i].x = (20 + i * 37) % (MAXX - 1) + 1; - sp[i].y = (20 + i * 53) % (MAXY - 1) + 1; - sprite_show(&sp[i]); - vx[i] = (i & 1) ? 1 : -1; - vy[i] = (i & 2) ? 1 : -1; - } -} - -#if FPS_METER -/* Кадров за последнюю RTC-секунду — белым в чёрной плашке слева-сверху - * на ТЕКУЩЕЙ draw-странице (рисуем ДО sprite_update, чтобы шары легли - * поверх и heal учитывал плашку через ОЗУ-копию). */ -static void show_fps(uint8_t v) -{ - char buf[4]; - - buf[0] = (char)('0' + (v / 100) % 10); - buf[1] = (char)('0' + (v / 10) % 10); - buf[2] = (char)('0' + v % 10); - buf[3] = 0; - setcolor(WHITE); - setfillstyle(SOLID_FILL, BLACK); - bar(0, 0, 27, 9); - outtextxy(2, 1, buf); -} -#endif - -int main(void) -{ - uint8_t i, page, hidden; -#if FPS_METER - datetime_t dt; - uint8_t frames = 0, last_sec; -#endif - - build_sprites(); - initgraph(); - gfx_sprite_clip(0); - - /* Палитра страницы 1 = палитре страницы 0. */ - gfx_pal_get(0, 0, 16, egapal); - gfx_pal_load(1, 0, 16, egapal); - - init_balls(); - - /* Обе страницы: шахматка (в ОЗУ-копию — бэкап для heal) + шары в - * стартовых позициях (sprite_update рисует на текущей draw-странице - * и снимает её dirty-бит). */ - for (page = 0; page < 2; page++) { - gfx_set_draw_page(page); - draw_board(); - sprite_update(sp, NBALLS); - } - gfx_set_visible_page(0); -#if FPS_METER - getdatetime(&dt); - last_sec = dt.second; -#endif - - for (;;) { - if (kbhit() && getch() == 27) - break; - - /* Физика: обновить позиции (только состояние спрайтов). */ - for (i = 0; i < NBALLS; i++) { - int nx = sp[i].x + vx[i]; - int ny = sp[i].y + vy[i]; - if (nx <= MINX || nx >= MAXX) vx[i] = -vx[i]; - if (ny <= MINY || ny >= MAXY) vy[i] = -vy[i]; - sprite_move(&sp[i], nx, ny); - } - - /* Кадр: рисуем на скрытой странице. sprite_flip развёрнут, чтобы - * плашку FPS нарисовать на скрытой странице ДО sprite_update. */ - hidden = gfx_get_visible_page() ^ 1; - gfx_set_draw_page(hidden); -#if FPS_METER - frames++; - getdatetime(&dt); - if (dt.second != last_sec) { - last_sec = dt.second; - show_fps(frames); - frames = 0; - } -#endif - sprite_update(sp, NBALLS); - gfx_wait_vsync(); - gfx_set_visible_page(hidden); /* флип в vblank — без мерцания */ - } - - closegraph(); - return 0; -} diff --git a/examples/balls/run.sh b/examples/balls/run.sh deleted file mode 100755 index e9e8c78..0000000 --- a/examples/balls/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -make clean -make run diff --git a/examples/mdview/Makefile b/examples/mdview/Makefile deleted file mode 100644 index 67c7ba1..0000000 --- a/examples/mdview/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -# Build mdview.exe — Markdown viewer for Sprinter. -# -# small memory mode: code in W1, data/stack/heap in W2 (32 KB total). -# W3 stays free for the file buffer (EMM-mapped). - -EXAMPLE := mdview -MEMORY := small - -include $(CURDIR)/../sdk.mk - -# ------------------------------------------------------------------ -# Образ дискеты: только mdview.exe + README.MD (перекодированный -# из UTF-8 в CP866 — рабочую кодировку Sprinter). -# -# README.MD хранится в репозитории в UTF-8; iconv -c конвертирует -# его в CP866, отбрасывая символы без аналога в целевой кодировке. -# Результат кладётся в .disk_tmp/README.MD, чтобы make_disk.py -# использовал правильное имя файла на диске. -# -# iconv -c возвращает ненулевой код, если хоть один символ отброшен -# (даже с -c) — это ОЖИДАЕМО при потере символов без аналога в CP866, -# не ошибка конвертации; вывод при этом всё равно корректно записан. -# Поэтому код возврата iconv игнорируется (|| true). -# ------------------------------------------------------------------ -DISK_TMP := .disk_tmp -README_DISK := $(DISK_TMP)/README.MD - -$(DISK_TMP): - mkdir -p $@ - -$(README_DISK): README.MD | $(DISK_TMP) - iconv -c -f UTF-8 -t CP866 README.MD > $@ || true - -floppy: $(EXAMPLE).exe $(README_DISK) - $(PYTHON) $(MAKE_DISK) "$(FLOPPY_IMG)" $(EXAMPLE).exe $(README_DISK) - @echo - @echo "Floppy ready: $(FLOPPY_IMG)" - @echo "Run: make run MAME_HOME=/путь/к/MAME/runtime" - -clean: - rm -rf .sprinter-cc-* $(EXAMPLE).exe $(DISK_TMP) - -.PHONY: all clean floppy run diff --git a/examples/mdview/README.MD b/examples/mdview/README.MD deleted file mode 100644 index f0d40d8..0000000 --- a/examples/mdview/README.MD +++ /dev/null @@ -1,133 +0,0 @@ -# MDView — Просмотрщик Markdown для Sprinter - -**MDView** — программа для просмотра документов в формате *Markdown* на компьютере -Sprinter (процессор Z80). Документ хранится в отдельном W3 окне и не занимает -основную RAM программы. - -## Возможности - -- Документы до **128 КБ** (8 страниц EMM по 16 КБ каждая) -- До **16 384** экранных строк в индексе -- Автоматический перенос слов по ширине экрана (80 столбцов) -- Горизонтальный сдвиг для широких строк (блоки кода, таблицы) -- Статус-бар: имя файла, диапазон строк, процент прокрутки -- Спиннер в строке состояния во время загрузки и индексации -- Поддержка «мягкого» склеивания строк в абзацах и цитатах - -## Запуск - -``` -mdview [имя_файла.md] -``` - -Если имя файла не задано, загружается `README.MD`. - -## Управление - -``` -Клавиша Действие -───────────── ──────────────────────────────────────── -Up Down Прокрутка на одну строку вверх / вниз -PgUp PgDn Прокрутка на страницу (30 строк) -Home Начало документа -End Конец документа -Left Right Горизонтальный сдвиг (только nowrap-строки) -F1 Окно справки -F10 / Esc Выход из программы -``` - -## Синтаксис Markdown - -### Заголовки - -Поддерживаются уровни H1–H4. Уровни H5 и H6 отображаются как H4. - - # Заголовок первого уровня - ## Заголовок второго уровня - ### Заголовок третьего уровня - #### Заголовок четвёртого уровня - -### Текстовое форматирование - -**Жирный текст** выделяется двойными звёздочками: `**текст**` - -*Курсив* выделяется одиночными звёздочками `*текст*` или знаком подчёркивания `_текст_` - -`Встроенный код` обозначается обратными кавычками - -~~Зачёркнутый текст~~ — двойные тильды: `~~текст~~` - -### Ненумерованный список - -Маркеры `-`, `*` или `+`: - -- Первый пункт списка -- Второй пункт списка -- Третий пункт с достаточно длинным текстом, который при необходимости - будет перенесён на следующую строку с сохранением отступа - -### Нумерованный список - -1. Первый элемент -2. Второй элемент -3. Третий элемент - -### Цитата - -> Блок цитаты начинается с символа `>`. Несколько последовательных -> строк одной цитаты склеиваются в единый абзац с автоматическим -> переносом слов. - -### Блок кода (verbatim) - -Блок кода заключается в тройные обратные кавычки. Внутри блока -текст отображается «как есть» без разбора Markdown: - -``` -#include -#include - -int main(void) { - puts("Hello, Sprinter!"); - return 0; -} -``` - -### Горизонтальная линия - -Три или более символов `---`, `***` или `___` на отдельной строке: - ---- - -## Технические характеристики - -- **Платформа:** Sprinter, процессор Z80 @ 21 МГц -- **Кодировка:** CP866 (DOS Cyrillic) -- **Максимальный размер файла:** 128 КБ -- **Максимальное число строк в индексе:** 16 384 -- **Режим памяти:** small - - Код программы, cтек, данные, куча — окнa W1-W2 (32 КБ, адреса 0x4000–0xBFFF). - - Буфер файла — страницы EMM, отображаемые в W3 (0xC000–0xFFFF) - -## TODO - -1. **Увеличение размера документов.** Снять лимит 128 КБ: Достаточно - разрешить работать с большим кол-вом страниц памяти, пока оттестированно - на работе с 8-мю страницами по 16Кб. - -2. **Форматированные таблицы.** Разбирать строки вида `| ячейка | ячейка |` - с автоматическим выравниванием столбцов и отрисовкой разделительных - линий (строки `|---|---|`). На текущий момент таблицы отображаются - как обычные nowrap-строки без выравнивания. - -3. **Поддержка кодировок CP1251 и UTF-8.** Автоопределение кодировки - по BOM, явное указание через аргумент командной строки (`--encoding cp1251`), - возможность переключения кодировки во время просмотра (`F8`). - Нужно прежде всего для документов на русском языке; CP866 кодировака уже поддерживается. - -4. **Ускорение рендеринга.** Кэш строк экрана. Оптимизация цикла вывода - символов через BIOS WRCHAR (пакетный вывод, DMA). - ---- - -*MDView v0.2 · (c) 2026 Петров А.Г.* diff --git a/examples/mdview/SAMPLE.MD b/examples/mdview/SAMPLE.MD deleted file mode 100644 index 130155c..0000000 --- a/examples/mdview/SAMPLE.MD +++ /dev/null @@ -1,5 +0,0 @@ -MDVIEW Sample Document - -This is a sample Markdown file for testing the Sprinter *mdview text -viewer*. Phase 3 adds inline emphasis: bold, italic and underscore -runs render with distinct background colours. diff --git a/examples/mdview/docs/mdview-модель-документа-и-рендеринг.md b/examples/mdview/docs/mdview-модель-документа-и-рендеринг.md deleted file mode 100644 index df7e910..0000000 --- a/examples/mdview/docs/mdview-модель-документа-и-рендеринг.md +++ /dev/null @@ -1,103 +0,0 @@ -# mdview — модель документа и рендеринг (единый смешанный режим) -Дизайн-документ переработки `mdview.c` под новое ТЗ форматирования (`examples/mdview/todo2`). Описывает решение по хранению данных, разбор документа при загрузке, упрощение рендера и снятие лимита на число строк. -## 1. Контекст и главный вопрос -Мы отказываемся от двух режимов показа (Wrap/UnWrap-переключатель) и переходим к единому смешанному режиму: тип переноса задаётся типом блока (обычный текст/заголовки/списки/цитаты — Wrap; код/таблицы — UnWrap). -Главный вопрос: нужно ли хранить оригинальный байтовый контент файла, или при загрузке сразу преобразовать его в «готовый к показу» текст (склеить строки параграфов, убрать маркеры, развернуть отступы и т.п.)? -Ответ: **оригинал храним; отдельный «готовый» текстовый/ячеечный буфер не строим.** «Подготовка при загрузке» реализуется как построение компактного *индекса метаданных*, а не как преобразование *содержимого*. -## 2. Решение по архитектуре -### 2.1 Текущее состояние (база) -* Файл целиком лежит в EMM-страницах (до 8×16 КБ). В окно W3 (`0xC000`) в каждый момент замаплена ровно одна страница; доступ к байту — через `fb()`/`map_page()` (`mdview.c:198-213`). -* Индекс — это параллельные массивы по экранным сегментам: `line_offset[]` (смещение в файле) + битфлаги `cont/nowrap/blank/in_code`, 2-битный `line_kind[]`, `init_style[]` (`mdview.c:122-135`). -* `index_lines()` за один проход уже делает склейку параграфов обычного текста, перенос по словам на 80 колонок и перенос emphasis через soft/hard break (`mdview.c:692-911`). -* `render_line()` при каждой отрисовке заново читает байты из оригинала и заново парсит inline-разметку (`mdview.c:918-1156`). -Вывод: «готовим при загрузке» мы уже делаем — но готовим **индекс**, а не текст. -### 2.2 Почему не материализуем содержимое -* **Одно свободное окно W3.** Режим памяти `small`: код в W1, данные/стек/куча в W2, для банкуемых данных свободен только W3. Преобразование «оригинал → готовый буфер» требует одновременно держать замапленными исходную страницу (чтение) и страницу-приёмник (запись). При единственном окне это поток swap-ов на каждую границу. In-place преобразование тоже невозможно: склейка меняет длины, смещения «съезжают», параграф пересекает границу 16 КБ. -* **Удвоение памяти и срыв гарантии 128 КБ.** Оригинал может занимать все 8 страниц; готовой копии нужны свои страницы — гарантировать, что влезут обе, нельзя. -* **Готовая форма не обязательно меньше.** Снятие маркеров экономит байты, но добавляются отступы-продолжения у переносов списков/цитат. В лучшем случае ≈ размер оригинала, в худшем — больше. Ячеечная модель (символ+атрибут) — это ×2 (до 256 КБ), невозможно. -* **Покадровая стоимость и так мала.** За кадр рисуются только 30 видимых строк (~30×80 чтений). Единственный дорогой проход — `index_lines()` — неизбежен в любой архитектуре (нужен полный скан для `n_lines` и процента прокрутки). -### 2.3 Что реально оптимизировать -Не текст, а повторную работу `render_line()`: -* вызов `classify_line()` на каждый кадр (`mdview.c:984`); -* обратный проход к первому не-cont сегменту ради отступа продолжения (`mdview.c:944-970`). -Это снимается переносом результата классификации (`kind`, ширина отступа/контент-колонка) в сам индекс на этапе `index_lines()`. -## 3. Модель данных индекса -### 3.1 Запись сегмента -Единая запись на экранный сегмент полностью заменяет нынешние параллельные массивы (`line_offset`, `cont_flag`, `in_code`, `nowrap_flag`, `blank_flag`, `line_kind`, `init_style`); `index_lines()` переписывается с нуля под эту модель. Цель размера записи — 5–6 байт: -* `offset` — 3 байта (24-битное смещение в файле, покрывает 128 КБ). -* `flags` — 1 байт: биты `cont`, `nowrap`, `blank`, `in_code` + 2-битный `ckind` (тип продолжения: PLAIN/QUOTE/LIST/OTHER). -* `style` — 1 байт: стартовый стиль сегмента (`init_style`, теперь включая STRIKE) + при необходимости глубина вложенности. -* `indent` — 1 байт: предвычисленная контент-колонка/ширина префикса, чтобы рендер не вызывал `classify_line()` и не делал обратный проход. -### 3.2 Размещение и снятие лимита `MAX_LINES` -Проблема: `line_offset` сейчас `uint32_t[2048]` = 8 КБ, `init_style` = 2 КБ; суммарно статический индекс ~11.6 КБ near-памяти (W2). Рост лимита в near невозможен — W2 переполнится. -Решение: -* **Индекс храним в отдельном EMM-блоке** (свои страницы, помимо файловых), доступ — через тот же W3. -* **Near-кэш viewport**: перед отрисовкой кадра разово вычитываем записи для `VIEW_H+1` видимых сегментов в маленький near-массив (≈ `(VIEW_H+1)×6` ≈ 186 байт). Рендер работает по near-кэшу + читает только контент-страницы. Это устраняет per-byte thrashing между страницей индекса и страницей контента: переключений на кадр — единицы, а не тысячи. -* **Динамический размер**: число страниц под индекс выделяем пропорционально размеру файла (число сегментов ∈ размеру). `MAX_LINES` становится функцией от выделенных страниц индекса. Это прямо ложится на заметку v2 («чем больше банков под файл, тем больше буферы»). -* **Без регресса скорости при индексе в EMM**: чтобы вынос индекса в банки не замедлил сборку (W3 делится между чтением контента и записью индекса), записи копим в near-буфере батчами и сбрасываем в EMM-блок через `bank_write()` (он сам сохраняет/восстанавливает маппинг W3). Переключений окна на всю сборку — единицы, а не на каждый сегмент. -* Если индекс-страницы выделить не удалось — деградируем до текущего near-лимита и показываем явную диагностику обрезки (а не молчаливый обрыв в `emit_seg()` — `mdview.c:574`). -## 4. `index_lines()` — разбор по новому ТЗ -Единый проход по файлу строит сегменты. Деление на параграфы — по пустым строкам; несколько пустых строк подряд схлопываются в одну. -### 4.0 Скорость подготовки — главный приоритет -Требование: подготовка максимально быстрая (сейчас ~25 КБ готовятся 6–10 с). Две структурные причины медленности и их устранение: -* **Per-byte `fb(uint32_t)`.** Каждый доступ к байту пересчитывает страницу 32-битными `p >> 14` и `p & 0x3FFF` (`mdview.c:209-213`). На Z80 32-битная арифметика — это программные подпрограммы на каждый символ. Замена: **потоковый разбор** — мапим страницу один раз, идём по окну `char *`/16-битным индексом, страницу переключаем только на границе 16 КБ. 32-битным остаётся лишь сохраняемый в индекс `offset`. -* **Многократные пере-сканы.** На каждом `\n` внутри параграфа вызываются `is_fence_raw()`, `is_hr_raw()`, `classify_line()`, `is_line_blank()` (`mdview.c:800-809`) — каждая заново сканирует следующую строку, а `classify_line()` ещё и повторяет цикл детекции HR. Для параграфа из N строк — O(N×длина) лишней работы. Замена: **один проход** — каждую строку классифицируем ровно один раз в момент её начала, lookahead — минимальный (несколько первых байт). -* **32-битные сравнения.** Курсор скана — (страница:8 бит, смещение:16 бит); сравнение с концом — сначала по странице, потом 16-битно. -Ожидаемый эффект: подготовка — по сути один линейный проход с 16-битными операциями, кратное ускорение относительно текущего multi-pass + 32-bit. (`render_line()` может остаться на `fb()` — там только ~30×80 байт за кадр.) -### 4.1 Обычный текст (Wrap) -* **Soft break** (одиночный `\n`): склейка, следующая строка продолжается через пробел. -* **Wide break**: 2+ пробелов перед `\n` **или** символ `\` перед `\n` → принудительный перенос внутри параграфа, стиль сохраняется. (Текущий код ловит только 2 пробела — `mdview.c:813-818`; добавить ветку для `\`.) -* **Hard break** (пустая строка): новый параграф, отделяется ОДНОЙ пустой строкой независимо от числа пустых строк в оригинале. -* **Модификаторы** bold/italic/strike/code действуют через soft/wide break внутри параграфа и сбрасываются на границе параграфа. (Добавить STRIKE `~~…~~` — сейчас его нет в `INIT_STYLE_*`/`ATTR_*`.) -### 4.2 Заголовки (Wrap) -Один оригинальный абзац-строка; стартовый стиль по уровню. Во входе распознаём H1–H6, но H4/H5/H6 далее обрабатываются одинаково как H4 (сливаются в один стиль; `classify_line()` уже сворачивает `lvl>4` → `LK_H4`, `mdview.c:488`). Внутри допустимы bold/italic/code/strike. После заголовка всегда пустая строка. -### 4.3 Горизонтальный разделитель HR -Всегда одна строка, после неё всегда пустая строка (новый абзац). -### 4.4 Списки (Wrap) — НОВОЕ: многострочная склейка -* Пункт может занимать несколько оригинальных строк; soft break внутри пункта склеивается через пробел (как обычный текст). Сейчас списки эмитятся построчно (`mdview.c:762-772`) — переписать на paragraph-модель. -* Новая строка с префиксом пункта → новый пункт. -* Пустая строка завершает пункт. Следующая непустая НЕ-пункт строка не является продолжением. -* **Группировка**: если после ОДНОЙ пустой строки идёт снова пункт — это тот же список, пустая строка в показе подавляется (пункты идут вплотную). Только ДВЕ+ пустые строки между пунктами разрывают на разные списки (в показе — одна пустая строка между ними). -* Перенос продолжения пункта печатается с отступом до контент-колонки (для уровня 1 — 2 пробела). -* Незакрытые модификаторы НЕ переносятся на следующий пункт (каждый пункт — свой параграф). -* Вложенные списки поддерживаются (отступ растёт с ведущими пробелами). -* Отдельные стили: префикс маркера и текст списка. -Пример соответствует разделу «Списки» в `todo2` (строки 1–6). -### 4.5 Цитаты (Wrap) -* Отдельный параграф; перед текстом — префикс цитаты, перенесённые строки тоже предваряются префиксом. -* Многострочная склейка как у текста; пустая строка-цитата (`>`) показывается как пустая строка с префиксом. -* Вложенность (`> >` → двойной префикс). Отдельные стили: префикс и текст цитаты. -### 4.6 Блок кода ``` ``` ``` (UnWrap) -Весь блок одним стилем кода, без inline-модификаторов. После блока обязательна пустая строка. Строки не переносятся (truncate + горизонтальный скролл). -### 4.7 Таблицы (UnWrap) -Пока as-is, без переноса. Выравнивание столбцов — v2. -## 5. `render_line()` — упрощение -* Убрать ветку truncate-режима и `wrap_mode` (уже частично снято; `toggle_wrap()` — мёртвая заглушка `mdview.c:1286-1291`, удалить вместе с упоминаниями F2). -* Не вызывать `classify_line()` и не делать обратный проход: использовать `kind`/`indent`/`style` из индекса. -* Для cont-сегментов списков/цитат — печать отступа/префикса по `kind`+`indent` из записи сегмента. -* Inline-парсинг emphasis выполняется только в пределах видимого сегмента (дёшево); для кода/таблиц — отключён. -## 6. Горизонтальный скроллинг (UnWrap) -* Скроллится только UnWrap-текст (код/таблицы). Грануляция 8 символов (`HPAN_STEP`). -* Правый край: индикатор `>` своим стилем, если есть скрытый контент справа (есть — `mdview.c:1135-1155`). -* **Добавить** левый индикатор `<` в первой колонке, когда `viewport_x > 0`. -* **Границы скролла по факту**: текущий кламп жёстко до 240 (`mdview.c:1275`). Заменить на вычисление максимального переполнения среди UnWrap-строк в текущем viewport, чтобы вправо нельзя было уйти за самую длинную строку, а влево — до колонки 0. -## 7. Чеклист расхождений с текущим кодом -* [индекс] Перейти на запись-на-сегмент в EMM-банке + near-кэш viewport; снять `MAX_LINES=2048`. -* [скорость] Потоковый разбор: один проход, 16-битный курсор в окне (без per-byte `fb()` с 32-битной арифметикой), классификация строки один раз, минимальный lookahead; батч-флеш индекса. -* [текст] Wide break по символу `\`. -* [текст] Модификатор strikethrough `~~…~~` (+ стиль). -* [списки] Многострочная склейка пунктов и правило группировки по одной/двум пустым строкам. -* [цитаты] Многострочная склейка и повтор префикса (в т.ч. вложенные) на переносах. -* [заголовки] Читать H1–H6; H4/H5/H6 трактовать как H4 (частично уже есть — `mdview.c:488`). -* [скролл] Индикатор `<` и корректные границы по фактическому переполнению. -* [рендер] Снять per-кадровый `classify_line()` и обратный проход (данные — из индекса). -* [очистка] Удалить `toggle_wrap()` и упоминания F2 (`mdview.c:1286-1291`, `1311`). -## 8. Память: бюджет -* near (W2): текущий статический индекс ~11.6 КБ — у предела окна. После переноса `line_offset`/`init_style` в EMM в near остаётся near-кэш viewport (~0.2 КБ) + мелкие флаги → запас под стек/кучу растёт. -* EMM: файл до 8 страниц + индекс ~1–2 страницы (при 5–6 байт/сегмент и нескольких тысячах сегментов). Перед выделением проверять `mem_info()` на доступность страниц. -## 9. Заметки для v2 -* **Прогрессивный показ**: отрисовать первую страницу (первые ~30 сегментов) ДО завершения полной подготовки; остальное доиндексировать дальше или по мере прокрутки. Процент и `End` показывать как «вычисляется», пока не готов полный `n_lines`. (Синергия с потоковым разбором §4.0: первый экран готов после разбора лишь нескольких КБ.) -* Разделители переноса Wrap: точка/запятая/`!`/`?`/дефис; правило «новая строка не начинается с разделителя», серия разделителей остаётся на первой строке. -* Таблицы: вычисление ширины столбцов и выравнивание. -* Подсветка синтаксиса внутри блоков кода. -* URL/Images и прочие типы строк. diff --git a/examples/mdview/mdview — унифицированный рендеринг (Unified Wrap + Paragraph Model).md b/examples/mdview/mdview — унифицированный рендеринг (Unified Wrap + Paragraph Model).md deleted file mode 100644 index cf30db4..0000000 --- a/examples/mdview/mdview — унифицированный рендеринг (Unified Wrap + Paragraph Model).md +++ /dev/null @@ -1,90 +0,0 @@ -# mdview — унифицированный рендеринг (Unified Wrap + Paragraph Model) - - -## Цель -Отказаться от двух режимов Wrap/Unwrap. Ввести единую модель отображения Markdown-документов, соответствующую стандарту CommonMark: -* Обычный текст: параграфы склеиваются по soft breaks (`\n` → пробел), перенос по словам на 80 колонок. -* Hard break (` \n`): принудительный перенос внутри параграфа. -* Paragraph break (`\n\n`): новый абзац (пустая строка между блоками). -* Fenced code block и таблицы (будущее): не переносятся, работают как truncate + горизонтальный скроллинг + индикатор `>`. -* Горизонтальный скроллинг активен только когда в текущем viewport есть nowrap-строки. - -## Текущее состояние -* `mdview.c` использует `wrap_mode` (0 = truncate, 1 = wrap) и `toggle_wrap()` (F2). -* `index_lines()` делает два разных прохода: truncate (1 строка файла = 1 строка экрана) и wrap (сегментация по SCREEN_W). -* `render_line()` имеет ветвление по `wrap_mode` и `cont`. -* `line_offset[]`, `cont_flag[]`, `in_code[]`, `init_style[]` — существующие структуры. - -## Предлагаемые изменения - -### 1. Новые структуры данных -* Добавить `nowrap_flag[]` (битмап, аналогично `cont_flag[]`): строка не должна переноситься (code block, HR, таблица). -* Добавить `line_kind[]` (2 бита на строку, 512 байт для 2048 строк): хранит классификацию (PLAIN, H1-H4, HR, ULIST, OLIST, QUOTE, CODE, TABLE). Нужен для continuation-сегментов, чтобы render_line знал, какой префикс/отступ повторять. -* Убрать `wrap_mode` и `toggle_wrap()`. -* Убрать truncate-ветку из `index_lines()` и `render_line()`. -* `viewport_x` остаётся глобальным, но применяется только к `nowrap` строкам. - -### 2. Новый `index_lines()` — paragraph scanner -Единый проход по файлу, без двух режимов. - -#### 2.1 Сканирование «сущностей» -Walk по файлу от `p = 0` до `file_size`: -1. Если `fb(p) == '`' × 3 → Fenced code block. Все строки до закрывающего ` ``` ` — `nowrap_flag = 1`, `in_code = 1` (для body), `line_kind = CODE`. Длинные строки не wrap'аются. -2. Если строка начинается с `|...|` (будущее) → Table. `nowrap_flag = 1`, `line_kind = TABLE`. -3. Если строка — HR (`---`/`***`/`___`) → `nowrap_flag = 1`, `line_kind = HR`. 1 строка экрана. -4. Если строка — Header (`#...`), List (`- ` / `* ` / `+ ` / `N. ` / `N) `), Quote (`> `) → начало **Normal paragraph / block**. Обрабатывается как единый параграф до `\n\n` или EOF или code block. -5. Иначе — Normal paragraph (plain text). - -#### 2.2 Normal paragraph — обработка -Внутри параграфа символы читаются как единый поток (не прерываясь на `\n`): -* `\n\n` → конец параграфа. Emit текущую сегментную строку (если есть). Добавить пустую строку (1 строка экрана с нулевой длиной, или offset указывающий на второй `\n`). -* ` \n` (два пробела или таб + пробел перед `\n`) → **Hard break**. Emit текущую сегментную строку. Начать новую сегментную строку с того же абзаца, **сохранив `line_style`**. `prev_ch = ' '`. -* Обычный `\n` → **Soft break**. Считаем как 1 пробел: `visible col++`, `prev_ch = ' '`. Продолжаем текущую сегментную строку. -* Wrap at `SCREEN_W` — так же как сейчас, по `last_space`. При создании continuation-сегмента: `set_cont()`, `set_init_style_raw()` (carry emphasis), `set_line_kind()` (carry list/quote kind для отступа/префикса). -* Emphasis tracking (`line_style`) работает через весь параграф, через soft/hard breaks. - -#### 2.3 Fenced code block — обработка -* Delimiter строка (` `): `line_kind = CODE`, `nowrap_flag = 1`, рендерится как пустая строка (или строка с ` ` — но сейчас пустая). -* Body строки: каждая физическая строка = 1 строка экрана. `nowrap_flag = 1`, `in_code = 1`, `line_kind = CODE`. Нет inline parsing, нет wrap. -* Если строка длиннее `SCREEN_W` — truncation (не wrap). `render_line()` рисует `>` в последней колонке, если есть content за пределами `SCREEN_W + viewport_x`. - -#### 2.4 List continuation -* Первая строка list item: `line_kind = ULIST/OLIST`, `marker_visible_col` определяет отступ. Текст начинается после маркера. -* Wrap внутри list item: `set_line_kind()` = тот же `ULIST`/`OLIST`. `render_line()` для continuation-сегмента не рисует маркер, но добавляет отступ = `marker_visible_col` (т.е. количество пробелов до текста первой строки). -* Для вложенных списков отступ растёт, потому что `marker_visible_col` учитывает ведущие пробелы. - -#### 2.5 Quote continuation -* Первая строка quote: `line_kind = QUOTE`, `marker_visible_col` = отступ + 2 (для `│ `). -* Wrap внутри quote: `set_line_kind()` = `QUOTE`. `render_line()` для continuation рисует префикс `│ ` (или пробелы, если символ не нужен на всех кроме первой строки). - -### 3. Изменения в `render_line()` -* Убрать ветку `if (!wrap_mode)` (truncate mode больше не существует). -* Добавить `if (is_nowrap(line_idx))`: применяется `viewport_x`, отображается `>` в колонке 79 если контент выходит за пределы. -* Для `cont` сегментов (wrap continuation): - * Если `line_kind` == `QUOTE`: повторить префикс отступа + `│ ` (или только отступ, если решено показывать `│` только на первой строке). - * Если `line_kind` == `ULIST`/`OLIST`: повторить отступ = `marker_visible_col` (без маркера/цифр). Текст начинается с той же колонки, что и на первой строке item. - * Для plain text: просто продолжить текст без префиксов. -* Для `nowrap` строк: применять `viewport_x` как смещение. Текст сдвигается влево, скрытые символы не рисуются. Если за пределами видимой зоны есть ещё контент — `>` в колонке 79. - -### 4. Горизонтальный скроллинг -* `scroll_h()` проверяет: есть ли в текущем viewport (top_line .. top_line+VIEW_H-1) хотя бы одна строка с `nowrap_flag == 1`. Если нет — return (no-op). -* Для `nowrap` строк `render_line()` использует `viewport_x` при отображении: символы с `cc < viewport_x` пропускаются, `cc >= viewport_x` рисуются. -* Для wrap-строк `viewport_x` игнорируется (эффективно = 0). -* При `Home`/`End`/`PgUp`/`PgDn`/`Up`/`Down` — `viewport_x` не сбрасывается (пользователь может скроллить по вертикали, оставаясь на горизонтальном смещении для code block). - -### 5. Очистка и навигация -* Убрать `wrap_mode`, `toggle_wrap()`. -* Убрать F2 из меню и help. -* `scroll_up`/`scroll_down`/`scroll_h`/`clamp_top` — обновить без ссылок на `wrap_mode`. -* `calc_pct` и статусная строка — обновить, убрать ссылку на wrap/unwrap. - -### 6. Порядок реализации (по файлу mdview.c) -1. **Структуры данных**: добавить `nowrap_flag[]`, `line_kind[]`, убрать `wrap_mode`. -2. **`index_lines()`**: полностью переписать в paragraph scanner. Это самый сложный и объёмный блок. -3. **`render_line()`**: убрать truncate, добавить nowrap/quote/list continuation, добавить `viewport_x` для nowrap. -4. **`scroll_h()`**: добавить проверку наличия nowrap в viewport. -5. **Очистка**: убрать toggle_wrap, обновить меню/help, обновить main() (убрать F2). -6. **Тестирование**: soft breaks, hard breaks, paragraph breaks, emphasis через soft breaks, fenced code blocks, list wrap с отступом, quote wrap с префиксом, горизонтальный скролл только для code. - -## Orchestration -План реализуется одним агентом (последовательно в одном файле), параллелизм не требуется. Дочерние агенты не используются. \ No newline at end of file diff --git a/examples/mdview/mdview.c b/examples/mdview/mdview.c deleted file mode 100644 index ab472f3..0000000 --- a/examples/mdview/mdview.c +++ /dev/null @@ -1,2024 +0,0 @@ -/* - * mdview — просмотрщик Markdown для Sprinter. - * - * Разметка экрана (80x32): - * строка 0 — статус-бар (имя файла / диапазон строк / всего / %) - * строки 1..30 — область документа (30 видимых строк текста) - * строка 31 — меню (F1 Help / F2 Reserved / F10 Exit) - * - * Навигация: - * стрелки, PgUp/PgDn, Home/End, F1 (справка), F10/Esc (выход). - * Горизонтальный сдвиг доступен только для nowrap-строк. - * - * Память: - * CODE → W1 (режим sprinter-cc --memory small) - * STACK/HEAP/DATA → W2 - * Буфер файла → страницы EMM (до 8 × 16 КБ = 128 КБ), - * отображаемые в W3 по требованию через fb()/map_page(). - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* ---- Геометрия экрана -------------------------------------------- */ - -#define SCREEN_W 80 -#define SCREEN_H 32 -#define VIEW_TOP_ROW 1 -#define VIEW_H 30 /* видимая область: строки 1..30 включительно */ -#define MENU_ROW 31 -#define TAB_STOP 4 - -/* ---- Параметры файла и памяти ------------------------------------ */ - -#define PAGE_BITS 14u -#define PAGE_SIZE (1u << PAGE_BITS) /* размер EMM-страницы: 16 КБ */ -#define PAGE_MASK ((uint16_t)(PAGE_SIZE - 1u)) -#define MAX_PAGES 8 /* 8 страниц × 16 КБ = 128 КБ */ -#define MAX_FILE ((uint32_t)MAX_PAGES * PAGE_SIZE) /* максимальный размер файла: 131072 байт */ -// #define MAX_LINES 2048 -#define FILE_BUF ((char *)0xC000) /* окно W3, куда мапится текущая EMM-страница */ - -/* ---- Палитра атрибутов ------------------------------------------- */ - -static const uint8_t md_pallete[16 * 4] = { - 0x00, 0x00, 0x00, 0x00, - 0x60, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x00, 0x00, - 0x60, 0x60, 0x00, 0x00, - 0x00, 0x00, 0x60, 0x00, - 0x60, 0x00, 0x60, 0x00, - 0x00, 0x60, 0x60, 0x00, - 0xC8, 0xC8, 0xC8, 0x00, - 0xA0, 0xA0, 0x80, 0x00, - 0xFF, 0xAA, 0xAA, 0x00, - 0xAA, 0xFF, 0xAA, 0x00, - 0xFF, 0xFF, 0xAA, 0x00, - 0xAA, 0xAA, 0xFF, 0x00, - 0xFF, 0xAA, 0xFF, 0x00, - 0xAA, 0xFF, 0xFF, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF -}; - -#define ATTR_RESET COLOR(COLOR_LIGHTGRAY, COLOR_BLACK) -#define ATTR_TEXT COLOR(COLOR_LIGHTGRAY, COLOR_BLUE) -#define ATTR_TEXT_TITLE1 COLOR(COLOR_YELLOW, COLOR_BLUE) -#define ATTR_TEXT_TITLE2 COLOR(COLOR_LIGHTBLUE, COLOR_BLUE) -#define ATTR_TEXT_TITLE3 COLOR(COLOR_LIGHTGREEN, COLOR_BLUE) -#define ATTR_TEXT_TITLE4 COLOR(COLOR_LIGHTGREEN, COLOR_BLUE) -#define ATTR_TEXT_BOLD COLOR(COLOR_LIGHTRED, COLOR_BLUE) -#define ATTR_TEXT_ITALIC COLOR(COLOR_LIGHTGREEN, COLOR_BLUE) -#define ATTR_TEXT_UNDERSORE COLOR(COLOR_LIGHTMAGENTA, COLOR_BLUE) -#define ATTR_TEXT_CODE COLOR(COLOR_WHITE, COLOR_BLUE) -#define ATTR_TEXT_STRIKE COLOR(COLOR_DARKGRAY, COLOR_BLUE) -#define ATTR_LIST_MARKER COLOR(COLOR_LIGHTCYAN, COLOR_BLUE) -#define ATTR_QUOTE_MARKER COLOR(COLOR_CYAN, COLOR_BLUE) -#define ATTR_HR COLOR(COLOR_LIGHTGRAY, COLOR_BLUE) -#define ATTR_TRUNC COLOR(COLOR_YELLOW, COLOR_BLUE) -#define ATTR_BAR COLOR(COLOR_BLACK, COLOR_LIGHTCYAN) -#define ATTR_BAR_SPINNER COLOR(COLOR_WHITE, COLOR_LIGHTCYAN) -#define ATTR_MENU_T COLOR(COLOR_BLACK, COLOR_LIGHTCYAN) -#define ATTR_MENU_K COLOR(COLOR_YELLOW, COLOR_BLACK) - -/* ---- Атрибуты диалога справки ------------------------------------ */ - -#define ATTR_HELP_BG COLOR(COLOR_LIGHTGRAY, COLOR_BLACK) -#define ATTR_HELP_BDR COLOR(COLOR_WHITE, COLOR_BLACK) -#define ATTR_HELP_TIT COLOR(COLOR_YELLOW, COLOR_BLACK) -#define ATTR_HELP_HDR COLOR(COLOR_WHITE, COLOR_BLACK) -#define ATTR_HELP_HINT COLOR(COLOR_YELLOW, COLOR_BLACK) - -/* Геометрия диалога справки (в символьных координатах 80×32). */ -#define HELP_X 8u /* левая граница рамки */ -#define HELP_Y 4u /* верхняя граница рамки */ -#define HELP_W 64u /* ширина рамки (включая │) */ -#define HELP_H 22u /* высота рамки (включая ─) */ - -/* ---- Глобальное состояние ---------------------------------------- */ - -/* Файл до 128 КБ хранится в наборе EMM-страниц (до 8 шт. по 16 КБ). - * В окно W3 одновременно отображается только одна страница (FILE_BUF): - * функция fb() при необходимости переключает маппинг и кэширует текущую - * страницу в cur_page, чтобы не делать лишние переключения. - * - * Индекс экранных сегментов хранится отдельно в EMM (записи по 8 байт, - * одна запись на видимую строку). Каждая запись содержит: - * - смещение первого байта сегмента в исходном файле, - * - флаги (continuation/nowrap/blank/code), - * - начальный inline-стиль сегмента. - * Правая граница сегмента определяется смещением следующей записи. - * Такой подход освобождает около 11 КБ ближней RAM и снимает старый - * лимит MAX_LINES для near-массивов. - */ - -/* В SDCC/z80 критично явно инициализировать static-переменные (`= 0`). - * Неинициализированные static-объекты могут конфликтовать по размещению - * и перезаписывать друг друга. См. заметку memory/sdcc_static_storage_gotcha. */ -/* Компактная запись индекса сегмента. - * Каждый элемент соответствует одной строке рендера в viewport. */ -typedef struct idx_rec_s { - uint32_t off; /* смещение первого байта исходного текста для сегмента */ - uint8_t flags; /* битовые флаги IF_* ниже */ - uint8_t style; /* начальный стиль INIT_STYLE_* в начале сегмента */ - uint8_t pad0; /* добивка до 8 байт (2048 записей на страницу 16 КБ) */ - uint8_t pad1; -} idx_rec_t; - -#define INDEX_REC_SIZE 8u -#define INDEX_RECS_PER_PAGE 2048u /* 16384 / 8; запись всегда целиком в одной странице */ -#define MAX_INDEX_PAGES 8u /* максимум 8 * 2048 = 16384 сегментов индекса */ - -#define IF_CONT 0x01u /* сегмент является продолжением перенесённой строки */ -#define IF_NOWRAP 0x02u /* строка не переносится (кодовый блок / HR / таблица) */ -#define IF_BLANK 0x04u /* визуально пустая строка */ -#define IF_CODE 0x08u /* тело fenced code-блока (verbatim-режим) */ - -static uint16_t n_lines = 0; -static uint16_t max_lines = 0; /* ёмкость индекса: index_pages * 2048 */ -static uint16_t top_line = 0; -static uint32_t file_size = 0; -static uint8_t file_blk = 0; -static uint8_t file_pages = 0; -static uint8_t file_phys[MAX_PAGES] = {0}; -static uint8_t index_blk = 0; -static uint8_t index_pages = 0; -static uint8_t index_phys[MAX_INDEX_PAGES] = {0}; -static uint8_t index_truncated = 0; /* выставляется при исчерпании ёмкости индекса */ -static uint32_t cur_seg_off = 0; /* смещение последнего emit_seg (кэш в near-памяти) */ -static idx_rec_t cur_rec; /* near-копия последней записанной idx-записи */ -static uint8_t cur_page = 0xFF; /* 0xFF = в W3 ещё не отображена ни одна страница */ -static uint8_t viewport_x = 0; /* горизонтальный сдвиг для nowrap-строк */ -static char filename[64] = {0}; - -#define HPAN_STEP 8u - -/* Спиннер в статус-баре (col=8,row=0). - * Включается во время загрузки/индексации, чтобы показать, - * что программа занята и не зависла. */ -#define SPINNER_COL 8 -static const char spinner_chars[4] = { '|', '/', '-', '\\' }; -static uint8_t spinner_phase = 0; -static uint8_t spinner_active = 0; - -/* ================================================================== - * Вспомогательные функции - * ================================================================== */ - -/* Печатает строку с указанным атрибутом, начиная с позиции (x,y), - * и останавливается на конце строки или правой границе экрана. - * `s` — литерал/статический буфер (CODE/DATA, окна 1/2), поэтому его - * можно передать в BIOS LP_PRINT_LN напрямую, без промежуточной копии. */ -static void put_str_attr(uint8_t x, uint8_t y, const char *s, uint8_t attr) -{ - uint8_t maxlen = (x < SCREEN_W) ? (uint8_t)(SCREEN_W - x) : 0; - uint8_t len = (uint8_t)strlen(s); - if (len > maxlen) len = maxlen; - if (len == 0) return; - bios_set_place(y, x); - bios_writeattr(s, len, attr); -} - - -/* Полностью очищает указанную строку экрана символами пробела с attr. */ -static void fill_row(uint8_t y, uint8_t attr) -{ - bios_set_place(y, 0); - bios_fillcharattr(' ', attr, SCREEN_W); -} - -/* Продвигает спиннер на один кадр. - * Если спиннер выключен — функция ничего не делает. */ -static void spinner_tick(void) -{ - if (!spinner_active) return; - wrchar(SPINNER_COL, 0, spinner_chars[spinner_phase & 3], ATTR_BAR_SPINNER); - spinner_phase++; -} - -/* Включает/выключает спиннер. - * При выключении очищает его позицию пробелом в статус-баре. */ -static void spinner_show(uint8_t on) -{ - spinner_active = on; - if (!on) wrchar(SPINNER_COL, 0, ' ', ATTR_BAR); -} - -/* Отображает указанную EMM-страницу файла в окно W3. - * Если эта страница уже активна, переключение не выполняется. */ -static inline void map_page(uint8_t page) -{ - if (page != cur_page) { - /* file_phys[] позволяет не вызывать mem_get_page() на каждом свопе. */ - sprinter_page_w3(file_phys[page]); - cur_page = page; - } -} -/* Читает один байт логического файла по смещению p. - * При переходе между страницами автоматически переключает W3. - * Проверка границ выполняется вызывающей стороной. - */ - -static char fb(uint32_t p) -{ - uint8_t *pb = (uint8_t *)&p; - uint8_t page = (uint8_t)((pb[1] >> 6) | (pb[2] << 2)); - uint16_t off = (uint16_t)(((uint16_t)(pb[1] & 0x3F) << 8) | pb[0]); - map_page(page); - return FILE_BUF[off]; -/* - // p = HLDE - (void)p; - - __asm -; uint16_t off = (uint16_t)(((uint16_t)(pb[1] | 0xC0) << 8) | pb[0]); - ld a, d - or a, #0xC0 - ld b, a - ld c, e - -; uint8_t page = (uint8_t)((pb[1] >> 6) | (pb[2] << 2)); - ld a, l ;; pb[2] << 2 - add a, a - add a, a - ld e, a ;; e = pb[2] << 2 - - ld a, d ;; pb[1] >> 6 - rlca - rlca - and a, #0x03 - - or a, e ;; a = (pb[2] << 2) | (pb[1] >> 6) - ld e, a -; map_page(page); -; if (page != cur_page) { - ld a, (_cur_page) - cp e - jr Z, __fbl0 -; cur_page = page; - ld a, e - ld (_cur_page), a -; sprinter_page_w3(file_phys[page]); - ld hl, #_file_phys - ld d, #0x00 - add hl, de - ld a, (hl) - out (__io_page_w3), a -; } -__fbl0: -; return FILE_BUF[off]; - ld a, (bc) - ret - __endasm; -*/ -} - -/* Для проверки границ выделения считаем пробел/таб/конец строки/0 пробельными. */ -static uint8_t ws_or_eol(char c) -{ - return (uint8_t)(c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == 0); -} - -static uint8_t ws_or_eol_or_delim(char c) -{ - return (uint8_t)(c == ' ' || c == ',' || c == '.' || c == '!' || c == '?' || c == ':' || c == ';' || c == '/' - || c == '\t' || c == '\n' || c == '\r' || c == 0); -} - -/* Маркер inline-выделения (*, **, _) распознаётся только если - * пробельная граница есть ровно с одной стороны (XOR-правило). - * Это защищает выражения вроде "2 * 3", "2 ** 3" и внутрисловные - * случаи (например COLOR_YELLOW) от ложного форматирования. */ -static uint8_t is_emph_flanked(char prev_ch, char next_ch) -{ - return (uint8_t)(ws_or_eol(prev_ch) != ws_or_eol_or_delim(next_ch)); -} - -/* Тип сегмента-продолжения, передаваемый в emit_seg(). - * Явно в индексе не хранится: рендер при необходимости восстанавливает - * тип через classify_line(), но параметр оставлен для читаемости вызовов. */ -#define CK_PLAIN 0 -#define CK_QUOTE 1 -#define CK_LIST 2 -#define CK_OTHER 3 - -/* Доступ к индексу в EMM. - * Размер записи 8 байт, поэтому запись не пересекает границу страницы - * и bank_read/bank_write работают с цельным блоком. */ -static void idx_get(uint16_t idx, idx_rec_t *r) -{ - uint8_t page = (uint8_t)(idx >> 11); /* номер страницы: idx / 2048 */ - uint16_t off = (uint16_t)((idx & 2047u) << 3); /* смещение записи: (idx % 2048) * 8 */ - bank_read(index_phys[page], off, r, INDEX_REC_SIZE); -} - -static void idx_put(uint16_t idx, const idx_rec_t *r) -{ - uint8_t page = (uint8_t)(idx >> 11); - uint16_t off = (uint16_t)((idx & 2047u) << 3); - bank_write(index_phys[page], off, r, INDEX_REC_SIZE); -} - -/* Чтение полей индекса для рендера и навигации (построчный доступ). */ -static uint8_t seg_flags(uint16_t idx) -{ - idx_rec_t r; - if (idx >= n_lines) return 0; - idx_get(idx, &r); - return r.flags; -} - -static uint32_t seg_off(uint16_t idx) -{ - idx_rec_t r; - if (idx >= n_lines) return file_size; - idx_get(idx, &r); - return r.off; -} - -static uint8_t is_cont(uint16_t idx) { return (uint8_t)((seg_flags(idx) & IF_CONT) != 0); } -static uint8_t is_nowrap(uint16_t idx) { return (uint8_t)((seg_flags(idx) & IF_NOWRAP) != 0); } -static uint8_t is_blank(uint16_t idx) { return (uint8_t)((seg_flags(idx) & IF_BLANK) != 0); } -static uint8_t is_code_body(uint16_t idx){ return (uint8_t)((seg_flags(idx) & IF_CODE) != 0); } - -/* Установка флагов всегда идёт в последний emit_seg (n_lines-1). - * Используется near-копия cur_rec, чтобы не делать обратное чтение из банка. */ -static void set_nowrap_cur(void) { cur_rec.flags |= IF_NOWRAP; idx_put((uint16_t)(n_lines - 1), &cur_rec); } -static void set_blank_cur(void) { cur_rec.flags |= IF_BLANK; idx_put((uint16_t)(n_lines - 1), &cur_rec); } -static void set_code_cur(void) { cur_rec.flags |= IF_CODE; idx_put((uint16_t)(n_lines - 1), &cur_rec); } - -/* Загружает 16 текстовых атрибутов в палитры ink/paper/blink. */ -static void set_pallete(void) { - uint8_t buff[64]; - for(int i = 0; i < 16; i++) { - for(int j = 0; j < 16; j++) { - buff[j * 4] = md_pallete[i * 4]; - buff[j * 4 + 1] = md_pallete[i * 4 + 1]; - buff[j * 4 + 2] = md_pallete[i * 4 + 2]; - buff[j * 4 + 3] = md_pallete[i * 4 + 3]; - } - - text_pal_load(TEXT_PAL_INK, i * 16, 16, md_pallete); - text_pal_load(TEXT_PAL_BLINK_INK, i * 16, 16, md_pallete); - text_pal_load(TEXT_PAL_PAPER, i * 16, 16, buff); - text_pal_load(TEXT_PAL_BLINK_PAPER, i * 16, 8, buff + 32); - text_pal_load(TEXT_PAL_BLINK_PAPER, i * 16 + 8, 8, buff + 32); - } -} - -/* ================================================================== - * Загрузка/выгрузка файла - * ================================================================== */ - -/* Загружает файл в EMM-страницы и подготавливает EMM-индекс. - * Возврат: 0 — успех, отрицательное значение — код ошибки. */ -static int load_file(const char *path) -{ - int fd = open(path, O_RDONLY); - if (fd < 0) return -1; - - long sz = lseek(fd, 0, SEEK_END); - if (sz < 0 || (uint32_t)sz > MAX_FILE) { - close(fd); - return -2; - } - file_size = (uint32_t)sz; - lseek(fd, 0, SEEK_SET); - - /* Выделяем ровно столько EMM-страниц, сколько нужно под файл. */ - file_pages = (uint8_t)((file_size + PAGE_SIZE - 1u) / PAGE_SIZE); - if (file_pages == 0) file_pages = 1; - file_blk = mem_alloc_pages(file_pages); - if (file_blk == 0) { - close(fd); - return -3; - } - - /* Читаем файл кусками по 16 КБ, последовательно маппя страницы в W3. - * На последней итерации читаем остаток меньше PAGE_SIZE. */ - uint32_t remaining = file_size; - for (uint8_t page = 0; page < file_pages; page++) { - spinner_tick(); - uint8_t phys = mem_get_page(file_blk, page); - file_phys[page] = phys; - sprinter_page_w3(phys); - cur_page = page; - uint16_t chunk = (remaining > PAGE_SIZE) - ? (uint16_t)PAGE_SIZE - : (uint16_t)remaining; - int n = read(fd, FILE_BUF, chunk); - if (n != (int)chunk) { - close(fd); - return -4; - } - remaining -= chunk; - } - close(fd); - - /* Выделяем блок индекса в EMM (8 байт на сегмент, 2048 записей/страница). - * Размер берем с запасом относительно файла; если памяти мало — - * уменьшаем количество страниц индекса до доступного значения. */ - { - uint8_t want = (uint8_t)(file_pages + 1u); - if (want > MAX_INDEX_PAGES) want = MAX_INDEX_PAGES; - index_blk = 0; - while (want >= 1u) { - index_blk = mem_alloc_pages(want); - if (index_blk) break; - want--; - } - if (index_blk == 0) return -5; - index_pages = want; - for (uint8_t i = 0; i < index_pages; i++) - index_phys[i] = mem_get_page(index_blk, i); - max_lines = (uint16_t)index_pages * INDEX_RECS_PER_PAGE; - } - return 0; -} - -/* Освобождает выделенные EMM-блоки файла и индекса. */ -static void unload_file(void) -{ - if (file_blk) { - mem_free_block(file_blk); - file_blk = 0; - } - if (index_blk) { - mem_free_block(index_blk); - index_blk = 0; - } -} - -/* ================================================================== - * Индексация строк - * ================================================================== */ - -/* Возвращает 1, если сегмент idx — это старт логической строки, - * и эта строка начинается с ``` (граница блока кода). - * Сегменты-продолжения никогда не считаются такими границами. */ -static uint8_t is_fence_delim(uint16_t idx) -{ - if (idx >= n_lines) return 0; - if (is_cont(idx)) return 0; - uint32_t off = seg_off(idx); - if (off + 2 >= file_size) return 0; - return (uint8_t)(fb(off) == '`' && - fb(off + 1) == '`' && - fb(off + 2) == '`'); -} - -#define INIT_STYLE_PLAIN 0x0 -#define INIT_STYLE_BOLD 0x1 -#define INIT_STYLE_ITALIC 0x2 -#define INIT_STYLE_UNDER 0x3 -#define INIT_STYLE_CODE 0x4 -#define INIT_STYLE_STRIKE 0x5 - -/* Таблица сопоставления init-style -> атрибут. - * Порядок должен совпадать с INIT_STYLE_* / EM_* ниже. */ -static const uint8_t styles_map[] = { - ATTR_TEXT, ATTR_TEXT_BOLD, ATTR_TEXT_ITALIC, ATTR_TEXT_UNDERSORE, ATTR_TEXT_CODE, ATTR_TEXT_STRIKE -}; - -/* Возвращает стартовый атрибут сегмента из индексной записи. */ -static uint8_t get_init_style(uint16_t idx) -{ - idx_rec_t r; - if (idx >= n_lines) return styles_map[INIT_STYLE_PLAIN]; - idx_get(idx, &r); - return styles_map[r.style & 7u]; -} - -/* Возвращает «сырой» INIT_STYLE_* из индексной записи. */ -static uint8_t get_init_style_raw(uint16_t idx) -{ - idx_rec_t r; - if (idx >= n_lines) return INIT_STYLE_PLAIN; - idx_get(idx, &r); - return (uint8_t)(r.style & 7u); -} - - -/* Тип строки, который возвращает classify_line(). */ -#define LK_PLAIN 0 -#define LK_H1 1 -#define LK_H2 2 -#define LK_H3 3 -#define LK_H4 4 /* также используется для H5/H6 */ -#define LK_HR 5 -#define LK_ULIST 6 /* маркер ненумерованного списка */ -#define LK_OLIST 7 /* маркер нумерованного списка */ -#define LK_QUOTE 8 /* маркер цитаты */ - -/* Классифицирует логическую строку. - * - Заголовки: 1..4 символа '#' и затем пробел/таб (H5/H6 сводятся к H4). - * - Горизонтальная линия: только '-', '*' или '_' (одного вида, >=3), - * возможно с пробелами/табами между символами. - * - Ненумерованный список: "- ", "* " или "+ " перед содержимым. - * - Нумерованный список: цифры + '.'/ ')' + пробел. - * - Цитата: '>' с опциональным пробелом. - * - Иначе обычный текст. - * Для заголовков/списков/цитат out_content указывает на первый байт - * содержимого после маркера и завершающего пробела. */ -static uint8_t classify_line(uint32_t p_start, uint32_t *out_content) -{ - if (p_start >= file_size) return LK_PLAIN; - - char c0 = fb(p_start); - - /* Заголовок? */ - if (c0 == '#') { - uint8_t lvl = 0; - uint32_t p = p_start; - while (p < file_size && fb(p) == '#' && lvl < 6) { - lvl++; p++; - } - if (p < file_size) { - char ch = fb(p); - if (ch == ' ' || ch == '\t') { - p++; - *out_content = p; - if (lvl > 4) lvl = 4; - return lvl; /* LK_H1 .. LK_H4 */ - } - } - /* Символ '#' без пробела после него не считается заголовком. */ - } - - /* Горизонтальный разделитель. - * Проверяется до списков, чтобы `- - -` не стало маркером списка. */ - if (c0 == '-' || c0 == '*' || c0 == '_') { - char marker = 0; - uint8_t count = 0; - uint8_t ok = 1; - uint32_t p = p_start; - while (p < file_size) { - char ch = fb(p++); - if (ch == '\n' || ch == '\r') break; - if (ch == ' ' || ch == '\t') continue; - if (ch != '-' && ch != '*' && ch != '_') { ok = 0; break; } - if (marker == 0) marker = ch; - else if (ch != marker) { ok = 0; break; } - count++; - } - if (ok && count >= 3) return LK_HR; - } - - /* Для списков/цитат дополнительно допускаем ведущие пробелы - * (базовая поддержка вложенности). */ - uint32_t lp = p_start; - while (lp < file_size && fb(lp) == ' ') lp++; - char cl = (lp < file_size) ? fb(lp) : 0; - - /* Ненумерованный список? */ - if ((cl == '-' || cl == '*' || cl == '+') && - (lp + 1) < file_size && - fb(lp + 1) == ' ') { - *out_content = lp + 2; - return LK_ULIST; - } - - /* Нумерованный список? */ - if (cl >= '0' && cl <= '9') { - uint32_t p = lp; - while (p < file_size) { - char ch = fb(p); - if (ch < '0' || ch > '9') break; - p++; - } - if (p < file_size) { - char ch = fb(p); - if ((ch == '.' || ch == ')') && - (p + 1) < file_size && fb(p + 1) == ' ') { - *out_content = p + 2; - return LK_OLIST; - } - } - } - - /* Цитата? */ - if (cl == '>') { - uint32_t p = lp + 1; - if (p < file_size && fb(p) == ' ') p++; - *out_content = p; - return LK_QUOTE; - } - - return LK_PLAIN; -} - -/* Возвращает видимую ширину префикса (отступ + маркер) в колонках. */ -static uint8_t marker_visible_col(uint8_t kind, uint32_t p_start, uint32_t content_off) -{ - switch (kind) { - case LK_ULIST: - case LK_QUOTE: - case LK_OLIST: return (uint8_t)(content_off - p_start); - default: return 0; /* plain/headers спец-префикса не имеют */ - } -} - -/* Добавляет запись сегмента в EMM-индекс. - * near-копии cur_rec/cur_seg_off используются для быстрых последующих - * операций без повторного чтения последней записи из банка. */ -static void emit_seg(uint32_t off, uint8_t style, uint8_t ckind, uint8_t cont) -{ - (void)ckind; /* тип сегмента восстанавливается на этапе рендера */ - if (n_lines >= max_lines) { index_truncated = 1; return; } - cur_rec.off = off; - cur_rec.flags = (uint8_t)(cont ? IF_CONT : 0); - cur_rec.style = (uint8_t)(style & 7u); - cur_rec.pad0 = 0; - cur_rec.pad1 = 0; - idx_put(n_lines, &cur_rec); - cur_seg_off = off; - n_lines++; -} - -/* Проверяет, что физическая строка пуста (только пробелы/таб + перевод строки). */ -static uint8_t is_line_blank(uint32_t p) -{ - while (p < file_size) { - char c = fb(p); - if (c == '\n') return 1; - if (c != ' ' && c != '\t') return 0; - p++; - } - return 1; -} - -/* Быстрая проверка начала блока кода по маркеру ``` . */ -static uint8_t is_fence_raw(uint32_t p) -{ - if (p + 2 >= file_size) return 0; - return (fb(p) == '`' && fb(p + 1) == '`' && fb(p + 2) == '`'); -} - -/* Быстрая проверка горизонтального разделителя (`---`, `***`, `___`). */ -static uint8_t is_hr_raw(uint32_t p) -{ - char c0 = fb(p); - if (c0 != '-' && c0 != '*' && c0 != '_') return 0; - char marker = 0; - uint8_t count = 0, ok = 1; - uint32_t hr_p = p; - while (hr_p < file_size) { - char ch = fb(hr_p++); - if (ch == '\n' || ch == '\r') break; - if (ch == ' ' || ch == '\t') continue; - if (ch != '-' && ch != '*' && ch != '_') { ok = 0; break; } - if (marker == 0) marker = ch; - else if (ch != marker) { ok = 0; break; } - count++; - } - return ok && count >= 3; -} - -/* Строка таблицы: первый непустой символ — '|'. - * Ведущие пробелы разрешены; такие строки не склеиваются и не переносятся. */ -static uint8_t is_table_raw(uint32_t p) -{ - while (p < file_size) { - char c = fb(p); - if (c == ' ' || c == '\t') { p++; continue; } - return (uint8_t)(c == '|'); - } - return 0; -} - -/* Общий сканер inline-форматирования и переносов. - * Идёт от q до q_end, учитывает стартовую колонку col и при необходимости - * эмитит continuation-сегменты. Возвращает итоговый line_style. */ -static uint8_t inline_scan(uint32_t q, uint32_t q_end, uint8_t col, - uint8_t ckind, uint8_t line_style) -{ - uint32_t last_space = 0xFFFFFFFFu; - uint8_t last_space_style = line_style; /* снимок line_style на момент last_space */ - char prev_ch = ' '; - uint8_t seg_col = col; - - while (q < q_end && n_lines < max_lines) { - char ch = fb(q); - - if (ch == '\n') { - /* Жёсткий перенос (уже проверен снаружи) или конец потока. */ - q++; - emit_seg(q, line_style, ckind, (ckind == CK_LIST || ckind == CK_QUOTE) ? 1 : 0); - last_space = 0xFFFFFFFFu; prev_ch = ' '; seg_col = col; - continue; - } - - if (ch == '`') { - if (line_style == INIT_STYLE_CODE) line_style = INIT_STYLE_PLAIN; - else line_style = INIT_STYLE_CODE; - q++; continue; - } - if (ch == '*' && (q + 1) < q_end && fb(q + 1) == '*') { - char next_ch = ((q + 2) < q_end) ? fb(q + 2) : '\n'; - if (is_emph_flanked(prev_ch, next_ch)) { - if (line_style == INIT_STYLE_PLAIN && ws_or_eol(prev_ch)) { - line_style = INIT_STYLE_BOLD; q += 2; continue; - } else if (line_style == INIT_STYLE_BOLD && ws_or_eol_or_delim(next_ch)) { - line_style = INIT_STYLE_PLAIN; q += 2; continue; - } - } - seg_col += 2; q += 2; prev_ch = '*'; continue; - } - if (ch == '~' && (q + 1) < q_end && fb(q + 1) == '~') { - char next_ch = ((q + 2) < q_end) ? fb(q + 2) : '\n'; - if (is_emph_flanked(prev_ch, next_ch)) { - if (line_style == INIT_STYLE_PLAIN && ws_or_eol(prev_ch)) { - line_style = INIT_STYLE_STRIKE; q += 2; continue; - } else if (line_style == INIT_STYLE_STRIKE && ws_or_eol_or_delim(next_ch)) { - line_style = INIT_STYLE_PLAIN; q += 2; continue; - } - } - seg_col += 2; q += 2; prev_ch = '~'; continue; - } - if (ch == '*' || ch == '_') { - char next_ch = ((q + 1) < q_end) ? fb(q + 1) : '\n'; - if (is_emph_flanked(prev_ch, next_ch)) { - uint8_t new_style = (ch == '*') ? INIT_STYLE_ITALIC : INIT_STYLE_UNDER; - if (line_style == INIT_STYLE_PLAIN && ws_or_eol(prev_ch)) { - line_style = new_style; q++; continue; - } else if (line_style == new_style && ws_or_eol_or_delim(next_ch)) { - line_style = INIT_STYLE_PLAIN; q++; continue; - } - } - seg_col++; q++; prev_ch = ch; continue; - } - if (ch == '\t') { - uint8_t tgt = (uint8_t)((seg_col & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP); - if (tgt > SCREEN_W) tgt = SCREEN_W; - seg_col = tgt; q++; prev_ch = ' '; continue; - } - if (ch == ' ') { - last_space = q + 1; - last_space_style = line_style; - seg_col++; q++; prev_ch = ' '; - } else { - seg_col++; q++; prev_ch = ch; - } - if (seg_col >= SCREEN_W) { - uint8_t use_ls = (uint8_t)(last_space != 0xFFFFFFFFu && last_space > cur_seg_off); - uint32_t wrap_at = use_ls ? last_space : q; - if (wrap_at >= q_end || wrap_at == cur_seg_off) break; - uint8_t wrap_style = use_ls ? last_space_style : line_style; - emit_seg(wrap_at, wrap_style, ckind, 1); - seg_col = col; last_space = 0xFFFFFFFFu; prev_ch = ' '; q = wrap_at; - /* Откатываем line_style к снимку на момент wrap_at: диапазон - * [wrap_at, q_old) будет пересканирован заново, и переключающие - * символы (обратная кавычка, звёздочка, подчёркивание, тильда) - * внутри него иначе применились бы дважды — именно так возникал - * баг: code-маркер на границе переноса ломает цвет следующего слова. */ - line_style = wrap_style; - } - } - return line_style; -} - -#define JOIN_MODE_LIST 1u -#define JOIN_MODE_QUOTE 2u -#define JOIN_MODE_PLAIN 3u - -/* Общий потоковый сканер для index_lines(). - * Убирает дублирование между ветками list/quote/plain: - * - mode задаёт правила обработки перевода строки, - * - остальная inline-логика (emphasis/таб/перенос) едина. - * Возвращает позицию q, на которой сканер остановился, и обновляет стиль. */ -static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col, - uint8_t ckind, uint8_t *io_line_style, uint8_t mode) -{ - uint32_t last_space = 0xFFFFFFFFu; - uint8_t last_space_style = *io_line_style; /* снимок line_style на момент last_space */ - char prev_ch = ' '; - uint8_t seg_col = col; - uint8_t line_style = *io_line_style; - - while (q < file_size && n_lines < max_lines) { - char ch = fb(q); - uint8_t soft_break = 0; - - if (ch == '\n') { - uint32_t next = q + 1; - if (next >= file_size) break; - - if (mode == JOIN_MODE_LIST) { - if (is_line_blank(next)) break; - if (is_fence_raw(next) || is_hr_raw(next) || is_table_raw(next)) break; - - { - uint32_t next_content = next; - uint8_t next_kind = classify_line(next, &next_content); - if (next_kind == LK_ULIST || next_kind == LK_OLIST || - next_kind == LK_QUOTE || - (next_kind >= LK_H1 && next_kind <= LK_H4) || - next_kind == LK_HR) { - break; - } - } - while (next < file_size) { - char ws = fb(next); - if (ws == ' ' || ws == '\t') next++; - else break; - } - q = next; - soft_break = 1; - ch = ' '; - } else if (mode == JOIN_MODE_QUOTE) { - if (is_line_blank(next)) break; - if (is_fence_raw(next) || is_hr_raw(next) || is_table_raw(next)) break; - - { - uint32_t next_content = next; - uint8_t next_kind = classify_line(next, &next_content); - if (next_kind != LK_QUOTE) break; - - { - uint32_t next_line_end = next_content; - while (next_line_end < file_size && fb(next_line_end) != '\n') next_line_end++; - - { - uint8_t empty_quote = 1; - for (uint32_t z = next_content; z < next_line_end; z++) { - char c = fb(z); - if (c != ' ' && c != '\t') { empty_quote = 0; break; } - } - if (empty_quote) break; - } - - { - uint32_t t = next_content; - while (t < next_line_end && (fb(t) == ' ' || fb(t) == '\t')) t++; - if (t < next_line_end && fb(t) == '>') break; - } - - while (next_content < next_line_end) { - char ws = fb(next_content); - if (ws == ' ' || ws == '\t') next_content++; - else break; - } - if (next_content < next_line_end && fb(next_content) == '>') { - next_content++; - if (next_content < next_line_end && fb(next_content) == ' ') next_content++; - } - q = next_content; - soft_break = 1; - ch = ' '; - } - } - } else { /* JOIN_MODE_PLAIN */ - if (fb(next) == '\n') break; - if (is_fence_raw(next) || is_hr_raw(next) || is_table_raw(next)) break; - { - uint32_t dummy; - if (classify_line(next, &dummy) != LK_PLAIN) break; - } - if (is_line_blank(next)) break; - - { - uint8_t is_hard = 0; - if (q >= para_start + 1) { - char b1 = fb(q - 1); - if (b1 == '\\') { - is_hard = 1; - } else if (q >= para_start + 2) { - char b2 = fb(q - 2); - if ((b1 == ' ' || b1 == '\t') && (b2 == ' ' || b2 == '\t')) - is_hard = 1; - } - } - if (is_hard) { - q++; - emit_seg(q, line_style, ckind, 0); - last_space = 0xFFFFFFFFu; - prev_ch = ' '; - seg_col = col; - continue; - } - } - - q++; - soft_break = 1; - ch = ' '; - } - } - - if (ch == '`') { - if (line_style == INIT_STYLE_CODE) line_style = INIT_STYLE_PLAIN; - else line_style = INIT_STYLE_CODE; - if (!soft_break) q++; - continue; - } - if (ch == '*' && (q + 1) < file_size && fb(q + 1) == '*') { - char next_ch = ((q + 2) < file_size) ? fb(q + 2) : '\n'; - if (is_emph_flanked(prev_ch, next_ch)) { - if (line_style == INIT_STYLE_PLAIN && ws_or_eol(prev_ch)) { - line_style = INIT_STYLE_BOLD; - if (!soft_break) q += 2; else q++; - continue; - } else if (line_style == INIT_STYLE_BOLD && ws_or_eol_or_delim(next_ch)) { - line_style = INIT_STYLE_PLAIN; - if (!soft_break) q += 2; else q++; - continue; - } - } - seg_col += 2; - if (!soft_break) q += 2; else q++; - prev_ch = '*'; - continue; - } - if (ch == '~' && (q + 1) < file_size && fb(q + 1) == '~') { - char next_ch = ((q + 2) < file_size) ? fb(q + 2) : '\n'; - if (is_emph_flanked(prev_ch, next_ch)) { - if (line_style == INIT_STYLE_PLAIN && ws_or_eol(prev_ch)) { - line_style = INIT_STYLE_STRIKE; - if (!soft_break) q += 2; else q++; - continue; - } else if (line_style == INIT_STYLE_STRIKE && ws_or_eol_or_delim(next_ch)) { - line_style = INIT_STYLE_PLAIN; - if (!soft_break) q += 2; else q++; - continue; - } - } - seg_col += 2; - if (!soft_break) q += 2; else q++; - prev_ch = '~'; - continue; - } - if (ch == '*' || ch == '_') { - char next_ch = ((q + 1) < file_size) ? fb(q + 1) : '\n'; - if (is_emph_flanked(prev_ch, next_ch)) { - uint8_t new_style = (ch == '*') ? INIT_STYLE_ITALIC : INIT_STYLE_UNDER; - if (line_style == INIT_STYLE_PLAIN && ws_or_eol(prev_ch)) { - line_style = new_style; - if (!soft_break) q++; else q++; - continue; - } else if (line_style == new_style && ws_or_eol_or_delim(next_ch)) { - line_style = INIT_STYLE_PLAIN; - if (!soft_break) q++; else q++; - continue; - } - } - seg_col++; - if (!soft_break) q++; else q++; - prev_ch = ch; - continue; - } - if (ch == '\t') { - uint8_t tgt = (uint8_t)((seg_col & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP); - if (tgt > SCREEN_W) tgt = SCREEN_W; - seg_col = tgt; - if (!soft_break) q++; else q++; - prev_ch = ' '; - continue; - } - if (ch == ' ') { - if (soft_break) { - last_space = q; - last_space_style = line_style; - seg_col++; - } else { - last_space = q + 1; - last_space_style = line_style; - seg_col++; - q++; - } - prev_ch = ' '; - } else { - seg_col++; - if (!soft_break) q++; else q++; - prev_ch = ch; - } - if (seg_col >= SCREEN_W) { - uint8_t use_ls = (uint8_t)(last_space != 0xFFFFFFFFu && last_space > cur_seg_off); - uint32_t wrap_at = use_ls ? last_space : q; - if (wrap_at >= file_size || wrap_at == cur_seg_off) break; - uint8_t wrap_style = use_ls ? last_space_style : line_style; - emit_seg(wrap_at, wrap_style, ckind, 1); - seg_col = col; - last_space = 0xFFFFFFFFu; - prev_ch = ' '; - q = wrap_at; - /* Откатываем line_style к снимку на момент wrap_at: диапазон - * [wrap_at, q_old) будет пересканирован заново, и переключающие - * символы (обратная кавычка, звёздочка, подчёркивание, тильда) - * внутри него иначе применились бы дважды — именно так возникал - * баг: code-маркер на границе переноса ломает цвет следующего слова (mdview.c, отчёт пользователя - * 2026-06-23). */ - line_style = wrap_style; - } - } - - *io_line_style = line_style; - return q; -} - -/* Строит индекс сегментов рендера по загруженному файлу. */ -static void index_lines(void) -{ - uint8_t in_block = 0; - n_lines = 0; - index_truncated = 0; - cur_seg_off = 0xFFFFFFFFu; /* пока не эмитили ни одного сегмента */ - if (file_size == 0) return; - - uint32_t p = 0; - while (p < file_size && n_lines < max_lines) { - - if ((n_lines & 15) == 0) - spinner_tick(); - - /* Пустая строка — разделитель параграфов. - * В индекс добавляем не более одной пустой экранной строки подряд. */ - if (is_line_blank(p)) { - while (p < file_size && fb(p) != '\n') p++; - if (p < file_size) p++; - /* Серия пустых строк схлопывается в одну визуальную строку. */ - if (n_lines == 0 || !(cur_rec.flags & IF_BLANK)) { - emit_seg(p, INIT_STYLE_PLAIN, CK_PLAIN, 0); - set_blank_cur(); - } - continue; - } - - /* ---- fenced code-блок (граница или тело) ---- */ - if (is_fence_raw(p)) { - in_block = !in_block; - emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_nowrap_cur(); - while (p < file_size && fb(p) != '\n') p++; - if (p < file_size) p++; - continue; - } - if (in_block) { - emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_nowrap_cur(); - set_code_cur(); - while (p < file_size && fb(p) != '\n') p++; - if (p < file_size) p++; - continue; - } - - /* ---- горизонтальный разделитель ---- */ - if (is_hr_raw(p)) { - emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_nowrap_cur(); - while (p < file_size && fb(p) != '\n') p++; - if (p < file_size) p++; - continue; - } - - /* ---- строка таблицы (| ... |): один nowrap-сегмент на исходную строку ---- */ - if (is_table_raw(p)) { - emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_nowrap_cur(); - while (p < file_size && fb(p) != '\n') p++; - if (p < file_size) p++; - continue; - } - - /* ---- классификация текущей строки ---- */ - uint32_t content_off = p; - uint8_t kind = classify_line(p, &content_off); - - /* ---- Заголовок: отдельная строка, не склеивается с параграфом ---- */ - if (kind >= LK_H1 && kind <= LK_H4) { - emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - uint32_t line_end = content_off; - while (line_end < file_size && fb(line_end) != '\n') line_end++; - (void)inline_scan(content_off, line_end, 0, CK_OTHER, INIT_STYLE_PLAIN); - while (p < file_size && fb(p) != '\n') p++; - if (p < file_size) p++; - continue; - } - - /* ---- Пункт списка: может занимать несколько физических строк ---- */ - if (kind == LK_ULIST || kind == LK_OLIST) { - uint8_t col = marker_visible_col(kind, p, content_off); - uint8_t line_style = INIT_STYLE_PLAIN; - uint32_t q = content_off; - - emit_seg(p, line_style, CK_LIST, 0); - q = scan_join_stream(q, p, col, CK_LIST, &line_style, JOIN_MODE_LIST); - - /* Переходим к началу следующей физической строки после пункта. */ - if (q < file_size) { - while (q < file_size && fb(q) != '\n') q++; - if (q < file_size) q++; - } - - /* Одна пустая строка между соседними маркерами списка подавляется. - * Две и более пустых строки оставляют видимый разделитель. */ - if (q < file_size && is_line_blank(q)) { - uint32_t t = q; - uint8_t blanks = 0; - while (t < file_size && is_line_blank(t)) { - while (t < file_size && fb(t) != '\n') t++; - if (t < file_size) t++; - blanks++; - // if (blanks >= 2) break; - } - if (blanks == 1 && t < file_size) { - uint32_t d = t; - uint8_t nk = classify_line(t, &d); - if (nk == LK_ULIST || nk == LK_OLIST) { - p = t; /* подавляем единственную пустую строку */ - continue; - } - } - } - - p = q; - continue; - } - - /* ---- Параграф цитаты: может покрывать несколько физических строк ---- */ - if (kind == LK_QUOTE) { - uint8_t col = marker_visible_col(kind, p, content_off); - uint8_t line_style = INIT_STYLE_PLAIN; - uint32_t q = content_off; - - emit_seg(p, line_style, CK_QUOTE, 0); - q = scan_join_stream(q, p, col, CK_QUOTE, &line_style, JOIN_MODE_QUOTE); - - while (q < file_size && fb(q) != '\n') q++; - if (q < file_size) q++; - p = q; - continue; - } - - /* ---- Обычный текстовый параграф: склейка через soft-break ---- */ - { - uint8_t line_style = INIT_STYLE_PLAIN; - emit_seg(p, line_style, CK_PLAIN, 0); - - uint32_t q = p; - q = scan_join_stream(q, p, 0, CK_PLAIN, &line_style, JOIN_MODE_PLAIN); - - if (q >= file_size) { - p = file_size; - } else { - /* q стоит на завершающем newline параграфа. - * Переходим на следующую строку, чтобы внешняя петля её - * корректно переклассифицировала. */ - while (q < file_size && fb(q) != '\n') q++; - if (q < file_size) q++; - p = q; - } - } - } -} -#define EM_NONE 0 -#define EM_BOLD 1 -#define EM_ITALIC 2 -#define EM_UNDER 3 -#define EM_CODE 4 -#define EM_STRIKE 5 - -/* Преобразует внутреннее состояние emphasis в экранный атрибут. */ -static uint8_t emph_to_attr(uint8_t emph, uint8_t base_attr) -{ - switch (emph) { - case EM_BOLD: return ATTR_TEXT_BOLD; - case EM_ITALIC: return ATTR_TEXT_ITALIC; - case EM_UNDER: return ATTR_TEXT_UNDERSORE; - case EM_CODE: return ATTR_TEXT_CODE; - case EM_STRIKE: return ATTR_TEXT_STRIKE; - default: return base_attr; - } -} - -/* Единая обработка inline-маркеров в render_line(). - * Возврат: - * 0 = маркер не поглощён (символ выводится как есть), - * 1 = маркер поглощён как управляющий (ничего не рисуем), - * 2 = литеральный двойной маркер (`**` или `~~`), нужно вывести две копии out_ch. */ -static uint8_t handle_inline_marker(char ch, uint32_t *io_p, uint32_t seg_end, - char prev_ch, uint8_t *io_emph, char *out_ch) -{ - uint32_t p = *io_p; - - *out_ch = 0; - - if (ch == '`') { - if (*io_emph == EM_NONE) { - *io_emph = EM_CODE; - *io_p = p + 1; - return 1; - } - if (*io_emph == EM_CODE) { - *io_emph = EM_NONE; - *io_p = p + 1; - return 1; - } - return 0; - } - - if ((ch == '*' || ch == '~') && (p + 1) < seg_end && fb(p + 1) == ch) { - uint8_t pair_emph = (ch == '*') ? EM_BOLD : EM_STRIKE; - char next_ch = ((p + 2) < seg_end) ? fb(p + 2) : '\n'; - - if (is_emph_flanked(prev_ch, next_ch)) { - if (*io_emph == EM_NONE && ws_or_eol(prev_ch)) { - *io_emph = pair_emph; - *io_p = p + 2; - return 1; - } else if (*io_emph == pair_emph && ws_or_eol_or_delim(next_ch)) { - *io_emph = EM_NONE; - *io_p = p + 2; - return 1; - } - } - - /* Невалидный по границам двойной маркер отображаем как литерал. */ - *io_p = p + 2; - *out_ch = ch; - return 2; - } - - if (ch == '*' || ch == '_') { - char next_ch = ((p + 1) < seg_end) ? fb(p + 1) : '\n'; - if (is_emph_flanked(prev_ch, next_ch)) { - uint8_t active = (ch == '*') ? EM_ITALIC : EM_UNDER; - if (*io_emph == EM_NONE && ws_or_eol(prev_ch)) { - *io_emph = active; - *io_p = p + 1; - return 1; - } else if (*io_emph != EM_NONE && ws_or_eol_or_delim(next_ch)) { - *io_emph = EM_NONE; - *io_p = p + 1; - return 1; - } - } - } - - return 0; -} - -/* Сбрасывает накопленный "прогон" из *col символов одного атрибута - * одним bios_writeattr() вместо посимвольных wrchar(). *need_place - * лениво ставит BIOS-курсор перед первым выводом в текущей строке — - * далее place продвигается сам (verified), повторный bios_set_place - * не нужен пока *col растёт монотонно. */ -static void flush_run(uint8_t row, uint8_t *col, const char *buf, - uint8_t len, uint8_t attr, uint8_t *need_place) -{ - if (len == 0) return; - /* *col уже указывает на конец прогона (вызывающий цикл инкрементит - * col на каждый символ ещё во время накопления в буфер — это нужно - * для верных проверок col < SCREEN_W до сброса). Поэтому начало - * прогона на экране = *col - len, а не *col; и *col второй раз - * наращивать НЕ нужно — он уже корректен. */ - if (*need_place) { - bios_set_place(row, (uint8_t)(*col - len)); - *need_place = 0; - } - bios_writeattr(buf, len, attr); -} - -static void render_line(uint16_t line_idx, uint8_t row) -{ - /* Один idx_get() на line_idx вместо ~13 отдельных is_blank/is_cont/ - * is_nowrap/is_code_body/seg_off/get_init_style* вызовов. Каждый из - * них шёл через seg_flags()/seg_off() -> idx_get() -> bank_read(), - * а bank_read() ДВАЖДЫ переключает банк окна 3 (на страницу индекса - * и обратно) плюс memcpy — это оказалось главным узким местом - * рендера (≈700+ переключений банка на кадр), а не сам вывод на - * экран. Все нужные поля (off/flags/style) лежат в одной 8-байтовой - * записи idx_rec_t — читаем её один раз и используем дальше. */ - uint8_t have_rec = (line_idx < n_lines); - idx_rec_t rec; - if (have_rec) idx_get(line_idx, &rec); - - uint8_t col = 0; - uint8_t base_attr = ATTR_TEXT; - uint8_t is_code = (uint8_t)(have_rec && (rec.flags & IF_CODE)); - uint8_t cur_attr = is_code ? ATTR_TEXT_CODE - : styles_map[(have_rec ? rec.style : INIT_STYLE_PLAIN) & 7u]; - uint8_t emph = is_code ? EM_CODE - : (uint8_t)((have_rec ? rec.style : INIT_STYLE_PLAIN) & 7u); - uint8_t parse_inline = 1; /* 1: включён парсер выделений, 0: выводим код как есть */ - - if (have_rec && (rec.flags & IF_BLANK)) { - bios_set_place(row, col); - bios_fillcharattr(' ', ATTR_TEXT, (uint8_t)(SCREEN_W - col)); - return; - } - - if (have_rec) { - uint32_t p = rec.off; - uint32_t content_off = p; - uint8_t cont = (uint8_t)((rec.flags & IF_CONT) != 0); - /* Правая граница сегмента: начало следующего сегмента или конец файла. */ - uint32_t seg_end; - if ((uint16_t)(line_idx + 1) < n_lines) { - idx_rec_t rec_next; - idx_get((uint16_t)(line_idx + 1), &rec_next); - seg_end = rec_next.off; - } else { - seg_end = file_size; - } - if (seg_end > file_size) seg_end = file_size; - uint8_t kind; - uint8_t quote_stream = 0; /* 1: текущий сегмент относится к потоку цитаты */ - - /* Для сегмента-продолжения повторяем префикс/отступ - * от первого не-CONT сегмента логической строки. */ - if (cont) { - uint16_t first = line_idx; - idx_rec_t rec_first = rec; /* rec.flags здесь уже содержит IF_CONT */ - /* Для любого сегмента-продолжения откатываемся к первому - * НЕ-cont сегменту этой логической строки — один idx_get() - * на шаг вместо двух (is_cont + повторный seg_off). */ - while (first > 0 && (rec_first.flags & IF_CONT)) { - first--; - idx_get(first, &rec_first); - } - uint32_t p_start = rec_first.off; - uint32_t content_off = p_start; - uint8_t first_kind = classify_line(p_start, &content_off); - uint8_t indent = marker_visible_col(first_kind, p_start, content_off); - if (first_kind == LK_QUOTE) { - /* Для продолжения цитаты нужно помнить тип потока, - * чтобы далее на newline корректно пропускать сырой `>`. */ - quote_stream = 1; - uint8_t q = 0; - while (q + 2 < content_off && q < SCREEN_W && fb(p_start + q) == ' ') q++; - if (q > (uint8_t)(SCREEN_W - col)) q = (uint8_t)(SCREEN_W - col); - if (q > 0) { - bios_set_place(row, col); - bios_fillcharattr(' ', ATTR_TEXT, q); - col = (uint8_t)(col + q); - } - if (col < SCREEN_W) wrchar(col++, row, 0xB3, ATTR_QUOTE_MARKER); - if (col < SCREEN_W) wrchar(col++, row, ' ', ATTR_TEXT); - } else if (first_kind == LK_ULIST || first_kind == LK_OLIST) { - uint8_t q = indent; - if (q > (uint8_t)(SCREEN_W - col)) q = (uint8_t)(SCREEN_W - col); - if (q > 0) { - bios_set_place(row, col); - bios_fillcharattr(' ', ATTR_TEXT, q); - col = (uint8_t)(col + q); - } - } - /* p/content_off уже равны rec.off — внутри cont-блока они не - * менялись (там свои локальные тени с тем же именем), - * пересчитывать через seg_off() заново не нужно. */ - } - - /* Кодовый блок: - * - строка-разделитель показывается как пустая; - * - тело рисуется «как есть» с ATTR_TEXT_CODE без inline-парсинга. - * is_fence_delim(line_idx) разворачено на месте: cont и p уже - * известны, повторный idx_get()/is_cont() не нужен. */ - if (!cont && p + 2 < file_size && - fb(p) == '`' && fb(p + 1) == '`' && fb(p + 2) == '`') { - bios_set_place(row, col); - bios_fillcharattr(' ', ATTR_TEXT, (uint8_t)(SCREEN_W - col)); - return; - } - if (is_code) { - base_attr = ATTR_TEXT_CODE; - cur_attr = ATTR_TEXT_CODE; - parse_inline = 0; - kind = LK_PLAIN; - quote_stream = 0; - } else if (cont) { - /* Строка-продолжение: префикс уже нарисован выше. - * Содержимое рисуем как plain, без повторной классификации, - * иначе начало слова могло бы ложно стать маркером. */ - kind = LK_PLAIN; - } else { - kind = classify_line(p, &content_off); - if (kind == LK_QUOTE) quote_stream = 1; - } - - if (kind == LK_HR) { - bios_set_place(row, col); - bios_fillcharattr(0xC4, ATTR_HR, (uint8_t)(SCREEN_W - col)); - return; - } - if (kind >= LK_H1 && kind <= LK_H4) { - switch (kind) { - case LK_H1: base_attr = ATTR_TEXT_TITLE1; break; - case LK_H2: base_attr = ATTR_TEXT_TITLE2; break; - case LK_H3: base_attr = ATTR_TEXT_TITLE3; break; - default: base_attr = ATTR_TEXT_TITLE4; break; - } - cur_attr = base_attr; - p = content_off; - } else if (kind == LK_ULIST) { - /* Базовая поддержка вложенности: ведущие пробелы сохраняем, - * затем рисуем маркер списка и пробел перед содержимым. */ - uint32_t q = p; - uint32_t qmax = p + SCREEN_W; - while (q + 2 < content_off && q < qmax && fb(q) == ' ') q++; - uint8_t nsp = (uint8_t)(q - p); - if (nsp > (uint8_t)(SCREEN_W - col)) nsp = (uint8_t)(SCREEN_W - col); - if (nsp > 0) { - bios_set_place(row, col); - bios_fillcharattr(' ', ATTR_TEXT, nsp); - col = (uint8_t)(col + nsp); - } - if (col < SCREEN_W) wrchar(col++, row, 0x07, ATTR_LIST_MARKER); /* символ маркера списка */ - if (col < SCREEN_W) wrchar(col++, row, ' ', ATTR_TEXT); - p = content_off; - } else if (kind == LK_OLIST) { - /* Отступ -> пробелы; цифры и '.' / ')' рисуем как маркер; - * завершающий пробел — обычным атрибутом текста. */ - uint32_t q = p; - uint32_t qmax = p + SCREEN_W; - while (q < content_off && q < qmax && fb(q) == ' ') q++; - uint8_t nsp = (uint8_t)(q - p); - if (nsp > (uint8_t)(SCREEN_W - col)) nsp = (uint8_t)(SCREEN_W - col); - if (nsp > 0) { - bios_set_place(row, col); - bios_fillcharattr(' ', ATTR_TEXT, nsp); - col = (uint8_t)(col + nsp); - } - /* Маркер (цифры/'.'/')') читается из fb() — это окно 3, - * BIOS принимает указатели только из #4000..#BFFF, поэтому - * сначала копируем во временный буфер на стеке (окно 2), - * чанками по 8 байт (маркер почти всегда короче, но длинный - * корректно не обрезается, просто шлётся несколькими - * bios_writeattr вместо одного). */ - { - char mkbuf[8]; - uint8_t first_chunk = 1; - while (q < content_off - 1 && col < SCREEN_W) { - uint8_t n = 0; - while (q < content_off - 1 && - (uint8_t)(col + n) < SCREEN_W && - n < sizeof(mkbuf)) { - mkbuf[n] = fb(q); - q++; - n++; - } - if (n == 0) break; - if (first_chunk) { bios_set_place(row, col); first_chunk = 0; } - bios_writeattr(mkbuf, n, ATTR_LIST_MARKER); - col = (uint8_t)(col + n); - } - } - if (col < SCREEN_W) wrchar(col++, row, ' ', ATTR_TEXT); - p = content_off; - } else if (kind == LK_QUOTE) { - /* Отступ -> пробелы; затем quote-префикс '│' (0xB3), - * пробел и далее содержимое. */ - uint32_t q = p; - uint32_t qmax = p + SCREEN_W; - while (q + 1 < content_off && q < qmax && fb(q) == ' ') q++; - uint8_t nsp = (uint8_t)(q - p); - if (nsp > (uint8_t)(SCREEN_W - col)) nsp = (uint8_t)(SCREEN_W - col); - if (nsp > 0) { - bios_set_place(row, col); - bios_fillcharattr(' ', ATTR_TEXT, nsp); - col = (uint8_t)(col + nsp); - } - if (col < SCREEN_W) wrchar(col++, row, 0xB3, ATTR_QUOTE_MARKER); - if (col < SCREEN_W) wrchar(col++, row, ' ', ATTR_TEXT); - p = content_off; - } - - /* Эффективный горизонтальный сдвиг: применяется только к nowrap-строкам. */ - uint8_t effective_vx = (rec.flags & IF_NOWRAP) ? viewport_x : 0; - /* cc — видимая колонка внутри области контента - * (после фиксированного префикса строки). */ - uint8_t cc = 0; - char prev_ch = ' '; /* используется в проверке границ emphasis */ - - /* Буферизация "ровного" прогона одного атрибута: символ и атрибут - * внутри этого while переменные (emphasis/tab/soft-wrap), поэтому - * весь цикл одним BIOS-вызовом не свернуть — но между сменами - * атрибута почти всегда идёт протяжённый одноцветный текст, и - * именно его стоит копить в runbuf и сбрасывать одним - * bios_writeattr() вместо посимвольных wrchar(). - * runbuf вмещает целую строку экрана — переполнение невозможно, - * т.к. весь цикл и так ограничен col < SCREEN_W. */ - char runbuf[SCREEN_W]; - uint8_t run_len = 0; - uint8_t run_attr = cur_attr; - uint8_t need_place = 1; - - while (p < seg_end && col < SCREEN_W) { - uint8_t soft_join = 0; /* 1: вставлен виртуальный пробел мягкой склейки, p уже сдвинут */ - char ch = fb(p); - /* '\' - * перед концом сегмента — это маркер wide-break: - * поглощаем его без вывода (кроме verbatim code-body). */ - if (parse_inline && ch == '\\' && (p + 1) < seg_end) { - char nb = fb(p + 1); - if (nb == '\n' || nb == '\r') { p++; continue; } - } - /* Мягкий перенос (перевод строки внутри одного сегмента). - * Рендер должен повторить логику индексатора: - * - quote: пропустить сырой `> ` следующей строки; - * - list/plain: схлопнуть перенос + ведущий отступ в 1 пробел. */ - if (parse_inline && (ch == '\n' || ch == '\r')) { - uint32_t next = p + 1; - if (quote_stream && next < seg_end) { - uint32_t next_content = next; - if (classify_line(next, &next_content) == LK_QUOTE) { - /* Мягкая склейка quote-строк: `> ` не рендерим. */ - p = next_content; - ch = ' '; - soft_join = 1; - } else { - /* Защита: если внутри quote-сегмента встретился - * не-цитатный перенос, работаем как обычная мягкая склейка. */ - while (next < seg_end) { - char ws = fb(next); - if (ws == ' ' || ws == '\t') next++; - else break; - } - if (next > (p + 1)) { - p = next; - soft_join = 1; - } - ch = ' '; - } - } else { - while (next < seg_end) { - char ws = fb(next); - if (ws == ' ' || ws == '\t') next++; - else break; - } - if (next > (p + 1)) { - /* Схлопываем перенос + отступ (ленивое продолжение). */ - p = next; - soft_join = 1; - } - ch = ' '; - } - } else if (ch == '\n' || ch == '\r') { - ch = ' '; - } - - /* inline-маркеры (**, ~~, *, _, `): - * helper синхронизирует состояние emphasis и возвращает режим - * обработки текущего токена (поглотить/вывести литерал/обычный символ). */ - if (parse_inline) { - char lit_ch = 0; - uint8_t mk = handle_inline_marker(ch, &p, seg_end, prev_ch, &emph, &lit_ch); - if (mk == 1) { - /* Атрибут меняется -> сбросить накопленный прогон - * со СТАРЫМ атрибутом, прежде чем переключиться. */ - flush_run(row, &col, runbuf, run_len, run_attr, &need_place); - run_len = 0; - cur_attr = emph_to_attr(emph, base_attr); - run_attr = cur_attr; - continue; - } - if (mk == 2) { - if (cc >= effective_vx && col < SCREEN_W && run_len < sizeof(runbuf)) { - runbuf[run_len++] = lit_ch; - col++; - } - cc++; - if (cc >= effective_vx && col < SCREEN_W && run_len < sizeof(runbuf)) { - runbuf[run_len++] = lit_ch; - col++; - } - cc++; - prev_ch = lit_ch; - continue; - } - } - - if (!soft_join) p++; - if (ch == '\t') { - /* Таб расширяется в координатах контента (cc). - * Сначала сбросить накопленный текстовый прогон — таб - * выводится отдельным bios_fillcharattr(), не через runbuf. */ - flush_run(row, &col, runbuf, run_len, run_attr, &need_place); - run_len = 0; - - uint8_t cc_start = cc; - uint8_t tgt = (uint8_t)((cc & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP); - uint8_t n_total = (uint8_t)(tgt - cc_start); - uint8_t skip = (effective_vx > cc_start) ? (uint8_t)(effective_vx - cc_start) : 0; - if (skip > n_total) skip = n_total; - uint8_t want = (uint8_t)(n_total - skip); - if (want > (uint8_t)(SCREEN_W - col)) want = (uint8_t)(SCREEN_W - col); - if (want > 0) { - bios_set_place(row, col); - bios_fillcharattr(' ', cur_attr, want); - col = (uint8_t)(col + want); - need_place = 0; - } - cc = (uint8_t)(cc_start + skip + want); - prev_ch = ' '; - } else { - if (cc >= effective_vx && col < SCREEN_W && run_len < sizeof(runbuf)) { - runbuf[run_len++] = ch; - col++; - } - cc++; - prev_ch = ch; - } - } - flush_run(row, &col, runbuf, run_len, run_attr, &need_place); - - /* Индикатор обрезки справа для nowrap-строки: - * если после текущей позиции есть ещё видимый контент, - * поверх последней колонки рисуем '>'. */ - if ((rec.flags & IF_NOWRAP) && col >= SCREEN_W) { - uint32_t pp = p; - uint32_t left_bound = rec.off; - while (pp < seg_end) { - char c = fb(pp); - if (c == '\n' || c == '\r') break; - if (c == '`') { pp++; continue; } - if (c == '*' && (pp + 1) < seg_end && fb(pp + 1) == '*') { - char pc = (pp > left_bound) ? fb(pp - 1) : ' '; - char nc = (pp + 2 < seg_end) ? fb(pp + 2) : '\n'; - if (is_emph_flanked(pc, nc)) { pp += 2; continue; } - /* Литерал `**` тоже считается видимым контентом справа. */ - } else if (c == '*' || c == '_') { - char pc = (pp > left_bound) ? fb(pp - 1) : ' '; - char nc = (pp + 1 < seg_end) ? fb(pp + 1) : '\n'; - if (is_emph_flanked(pc, nc)) { pp++; continue; } - } - wrchar(SCREEN_W - 1, row, '>', ATTR_TRUNC); - break; - } - } - - /* Индикатор скрытого контента слева при горизонтальном сдвиге. */ - if ((rec.flags & IF_NOWRAP) && viewport_x > 0) { - wrchar(0, row, '<', ATTR_TRUNC); - } - } - /* Дозаполняем остаток строки пробелами, чтобы стереть хвост - * от предыдущего кадра рендера. */ - { - uint8_t pad_attr = (parse_inline ? ATTR_TEXT : ATTR_TEXT_CODE); - if (col < SCREEN_W) { - bios_set_place(row, col); - bios_fillcharattr(' ', pad_attr, (uint8_t)(SCREEN_W - col)); - } - } -} - -/* Рендерит все 30 строк окна, начиная от top_line. */ -static void render_viewport(void) -{ - for (uint8_t i = 0; i < VIEW_H; i++) { - render_line((uint16_t)(top_line + i), (uint8_t)(VIEW_TOP_ROW + i)); - } -} - -/* Возвращает процент прокрутки (0..100) относительно доступного диапазона. */ -static uint8_t calc_pct(void) -{ - uint8_t pct; - if (n_lines <= VIEW_H) - pct = 100; - else - pct = (uint8_t)((uint32_t)top_line * 100UL / (uint32_t)(n_lines - VIEW_H)); - return pct; -} - -/* Рисует динамическую часть статус-бара без printf. - * Полностью перезаписывает колонки 45..79, чтобы не оставлять «хвосты». */ -static void render_status_numbers(void) -{ - uint16_t last = top_line + VIEW_H; - if (last > n_lines) last = n_lines; - uint8_t pct = calc_pct(); - - textattr(ATTR_BAR); - bios_set_place(0, 45); - bios_fillcharattr(' ', ATTR_BAR, (uint8_t)(SCREEN_W - 45)); - - wrchar(45, 0, 0xB3, ATTR_BAR); /* вертикальный разделитель */ - gotoxy(47, 0); - cputs("L "); - dec16(top_line + 1); - cputs("-"); - dec16(last); - cputs(" / "); - dec16(n_lines); - - - wrchar(70, 0, 0xB3, ATTR_BAR); /* вертикальный разделитель */ - gotoxy( 73, 0); - dec8(pct); - cputs("%"); -} - -/* Полностью перерисовывает статус-бар (заголовок + числа). */ -static void render_full_status(void) -{ - - fill_row(0, ATTR_BAR); - /* Размещение в шапке: "MDVIEW", затем слот спиннера и имя файла. */ - put_str_attr(1, 0, "MDVIEW", ATTR_BAR); - put_str_attr(10, 0, filename, ATTR_BAR); - render_status_numbers(); -} - -/* Обновляет только изменяемую числовую часть статус-бара. */ -static void render_updated_status(void) -{ - render_status_numbers(); -} - -/* Отрисовывает нижнюю строку меню. */ -static void render_menu(void) -{ - fill_row(MENU_ROW, ATTR_MENU_T); - put_str_attr(1, MENU_ROW, "F1", ATTR_MENU_K); - put_str_attr(4, MENU_ROW, "Help", ATTR_MENU_T); - /* F2 зарезервирован: режимов wrap/unwrap больше нет. */ - put_str_attr(SCREEN_W - 10, MENU_ROW, "F10", ATTR_MENU_K); - put_str_attr(SCREEN_W - 5, MENU_ROW, "Exit", ATTR_MENU_T); -} - -/* ================================================================== - * Прокрутка - * ================================================================== */ - -/* Ограничивает top_line допустимым диапазоном с учётом высоты окна. */ -static void clamp_top(void) -{ - if (n_lines <= VIEW_H) { - top_line = 0; - } else if (top_line > n_lines - VIEW_H) { - top_line = (uint16_t)(n_lines - VIEW_H); - } -} - -/* Прокрутка вверх на n строк с частичной перерисовкой при n == 1. */ -static void md_scroll_up(uint16_t n) -{ - uint16_t new_top_line = (top_line >= n) ? (uint16_t)(top_line - n) : 0; - if( new_top_line != top_line) { - top_line = new_top_line; - if (n == 1) { - scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 2, 0); - render_line((uint16_t)(top_line), (uint8_t)(VIEW_TOP_ROW)); - } else { - render_viewport(); - } - } -} - -/* Прокрутка вниз на n строк с частичной перерисовкой при n == 1. */ -static void md_scroll_down(uint16_t n) -{ - uint16_t new_top_line = (top_line + n < n_lines - VIEW_H) ? - (uint16_t)(top_line + n) : n_lines - VIEW_H; - if( new_top_line != top_line) { - top_line = new_top_line; - if (n == 1) { - scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 1, 0); - render_line((uint16_t)(top_line + VIEW_H - 1), (uint8_t)(VIEW_TOP_ROW + VIEW_H - 1)); - } else { - clamp_top(); - render_viewport(); - } - } -} - -/* Горизонтальный сдвиг (только если в окне есть nowrap-строки). - * Максимум сдвига ограничен самой широкой nowrap-строкой на экране. */ -static void md_scroll_horizon(int8_t delta) -{ - uint16_t maxw = 0; - for (uint8_t i = 0; i < VIEW_H; i++) { - uint16_t li = (uint16_t)(top_line + i); - if (li >= n_lines) break; - if (!is_nowrap(li)) continue; - uint32_t a = seg_off(li); - uint32_t b = seg_off((uint16_t)(li + 1)); - if (b > file_size) b = file_size; - uint16_t w = (b > a) ? (uint16_t)(b - a) : 0; /* приблизительная видимая ширина */ - if (w > maxw) maxw = w; - } - if (maxw == 0) return; /* в окне нет nowrap-контента */ - - /* Максимальный сдвиг = ширина за пределами экрана, в границах uint8. */ - uint16_t over = (maxw > SCREEN_W) ? (uint16_t)(maxw - SCREEN_W) : 0; - if (over > 248u) over = 248u; - uint8_t max_vx = (uint8_t)over; - - int16_t nx = (int16_t)viewport_x + delta; - if (nx < 0) nx = 0; - if (nx > (int16_t)max_vx) nx = max_vx; - if ((uint8_t)nx != viewport_x) { - viewport_x = (uint8_t)nx; - render_viewport(); - } -} - - -/* ================================================================== - * Вывод ошибки - * ================================================================== */ - -/* Печатает сообщение об ошибке и ждёт нажатия клавиши. */ -static void die(const char *msg) -{ - clrscr_attr(ATTR_TEXT); - puts(msg); - puts("Press any key to exit..."); - (void)getkey(); -} - -/* ================================================================== - * Диалог справки (F1) - * ================================================================== */ - -/* Заполняет одну внутреннюю строку диалога (r=0 — первая строка за рамкой). - * Строка s в кодировке CP866; остаток до края дополняется пробелами. */ -static void help_line(uint8_t r, const char *s, uint8_t attr) -{ - uint8_t x = HELP_X + 1u; - uint8_t y = HELP_Y + 1u + r; - uint8_t len = (uint8_t)strlen(s); - if (len > HELP_W - 2u) len = HELP_W - 2u; - - bios_set_place(y, x); - if (len > 0) bios_writeattr(s, len, attr); - /* place уже продвинут bios_writeattr на len колонок (verified). */ - if (len < HELP_W - 2u) { - bios_fillcharattr(' ', ATTR_HELP_BG, (uint8_t)(HELP_W - 2u - len)); - } -} - -static void show_help(void) -{ - /* " Помощь " в CP866 (8 байт) */ - static const char title[] = " \x8F\xAE\xAC\xAE\xE9\xEC "; - uint8_t tlen = 8u; - uint8_t lft = (uint8_t)((HELP_W - 2u - tlen) / 2u); /* = 27 */ - uint8_t rgt = (uint8_t)(HELP_W - 2u - tlen - lft); - /* Верхняя граница рамки с заголовком по центру. - * place продвигается каждым BIOS-вызовом (verified), поэтому - * достаточно одной установки места на всю строку рамки. */ - wrchar(HELP_X, HELP_Y, 0xDA, ATTR_HELP_BDR); /* ┌ */ - bios_set_place(HELP_Y, (uint8_t)(HELP_X + 1u)); - if (lft > 0) bios_fillcharattr(0xC4, ATTR_HELP_BDR, lft); - if (tlen > 0) bios_writeattr(title, tlen, ATTR_HELP_TIT); - if (rgt > 0) bios_fillcharattr(0xC4, ATTR_HELP_BDR, rgt); - wrchar(HELP_X + HELP_W - 1u, HELP_Y, 0xBF, ATTR_HELP_BDR); /* ┐ */ - - /* Боковые границы (левый и правый │ для каждой строки тела) */ - for (uint8_t r = 1u; r < HELP_H - 1u; r++) { - wrchar(HELP_X, HELP_Y + r, 0xB3, ATTR_HELP_BDR); /* │ */ - wrchar(HELP_X + HELP_W - 1u, HELP_Y + r, 0xB3, ATTR_HELP_BDR); /* │ */ - } - - /* Нижняя граница рамки */ - wrchar(HELP_X, HELP_Y + HELP_H - 1u, 0xC0, ATTR_HELP_BDR); /* └ */ - bios_set_place((uint8_t)(HELP_Y + HELP_H - 1u), (uint8_t)(HELP_X + 1u)); - bios_fillcharattr(0xC4, ATTR_HELP_BDR, (uint8_t)(HELP_W - 2u)); - wrchar(HELP_X + HELP_W - 1u, HELP_Y + HELP_H - 1u, 0xD9, ATTR_HELP_BDR); /* ┘ */ - - /* Содержимое (20 внутренних строк) */ - uint8_t r = 0; - help_line(r++, "", ATTR_HELP_BG); - help_line(r++, " MDView v0.2 -- Markdown Viewer for Sprinter", ATTR_HELP_HDR); - help_line(r++, " (c) 2026 Petrov A.G.", ATTR_HELP_BG); - help_line(r++, "", ATTR_HELP_BG); - help_line(r++, " Navigation:", ATTR_HELP_HDR); - help_line(r++, " \x18 \x19 Scroll one line up / down", ATTR_HELP_BG); - help_line(r++, " PgUp PgDn Scroll one page up / down", ATTR_HELP_BG); - help_line(r++, " Home End Jump to beginning / end of document", ATTR_HELP_BG); - help_line(r++, " \x1B \x1A Horizontal pan (code blocks/tables)", ATTR_HELP_BG); - help_line(r++, " Esc F10 Exit", ATTR_HELP_BG); - help_line(r++, "", ATTR_HELP_BG); - help_line(r++, " Markdown elements:", ATTR_HELP_HDR); - help_line(r++, " # ## ### Headings H1-H6", ATTR_HELP_BG); - help_line(r++, " **bold** *italic* `code` ~~strike~~", ATTR_HELP_BG); - help_line(r++, " > quote ``` ... ``` Fenced code block", ATTR_HELP_BG); - help_line(r++, " - * + item 1. 2. Ordered list", ATTR_HELP_BG); - help_line(r++, " |----|----| Tables", ATTR_HELP_BG); - help_line(r++, "", ATTR_HELP_BG); - help_line(r++, " File size: up to 128 KB (EMM). Lines: up to 16384.", ATTR_HELP_BG); - help_line(r++, "", ATTR_HELP_BG); - - (void)getkey(); - - render_full_status(); - render_viewport(); - render_menu(); -} - -/* ================================================================== - * Точка входа - * ================================================================== */ - -int main(int argc, char **argv) -{ - const char *path; - if (argc >= 2) { - path = argv[1]; - } else { - path = "README.MD"; - /* Дефолтный файл отсутствует — выводим подсказку без смены режима экрана. */ - { - int chk = open(path, O_RDONLY); - if (chk < 0) { - cputs("MDView v0.2 -- Markdown Viewer for Sprinter\r\n"); - cputs("(c) 2026 Petrov A.G.\r\n\r\n"); - cputs("Usage: mdview \r\n"); - cputs(" Displays a Markdown document (CP866, up to 128 KB).\r\n"); - return 1; - } - close(chk); - } - } - - /* Копируем путь в static filename[] для отображения в статус-баре. */ - { - uint8_t i = 0; - while (i < (uint8_t)(sizeof(filename) - 1) && path[i]) { - filename[i] = path[i]; - i++; - } - filename[i] = 0; - } - - /* Сначала поднимаем UI, чтобы пользователь видел шапку/меню/спиннер - * уже во время загрузки файла и построения индекса. */ - settextmode(TEXT_MODE_80x32); - clrscr_attr(ATTR_TEXT); - set_pallete(); - render_menu(); - render_full_status(); - spinner_show(1); - - int rc = load_file(path); - if (rc < 0) { - spinner_show(0); - clrscr_attr(ATTR_RESET); - textattr(ATTR_RESET); - cputs("MDView: cannot load file '"); - cputs(path); - cputs("'\n\rError: "); - switch (rc) { - case -1: cputs("open failed"); break; - case -2: cputs("size > 128K or seek failed"); break; - case -3: cputs("mem_alloc_pages failed"); break; - case -4: cputs("short read"); break; - case -5: cputs("index alloc failed"); break; - } - cputs("\n\r\n\r"); - return 1; - } - index_lines(); - spinner_show(0); - if (n_lines == 0) { - die("mdview: empty file"); - unload_file(); - return 1; - } - - render_full_status(); - render_viewport(); - - for (;;) { - uint16_t k = getkey(); - uint8_t ascii = (uint8_t)(k & 0xFF); - uint8_t scan = (uint8_t)((k >> 8) & 0x7F); - - if (ascii) { - if (ascii == 0x1B) break; /* клавиша Esc */ - continue; - } - switch (scan) { - case KEY_F1: show_help(); break; - case KEY_F10: goto exit_loop; - case KEY_UP: md_scroll_up(1); break; - case KEY_DOWN: md_scroll_down(1); break; - case KEY_LEFT: md_scroll_horizon(-(int8_t)HPAN_STEP); break; - case KEY_RIGHT: md_scroll_horizon(+(int8_t)HPAN_STEP); break; - case KEY_PGUP: md_scroll_up(VIEW_H); break; - case KEY_PGDN: md_scroll_down(VIEW_H); break; - case KEY_HOME: top_line = 0; - viewport_x = 0; - render_viewport(); break; - case KEY_END: top_line = (n_lines > VIEW_H) - ? (uint16_t)(n_lines - VIEW_H) : 0; - viewport_x = 0; - render_viewport(); break; - default: continue; - } - render_updated_status(); - } -exit_loop: - unload_file(); - pal_reset(PAL_CGA); - clrscr_attr(ATTR_TEXT); - return 0; -} diff --git a/examples/mdview/todo2 b/examples/mdview/todo2 deleted file mode 100644 index 0244b94..0000000 --- a/examples/mdview/todo2 +++ /dev/null @@ -1,180 +0,0 @@ -Новое требование к первоначальной подготовке документа. - -Правила форматирования и показа - -1) Деление на параграфы. Параграфы разделяются пустыми строками. -2) Заголовки H1-H6 - отдельные параграфы (после них всегда пустые строки) -3) Разделители (---) - тоже - после них всегда пустые строки. -4) Таблица/код (через ```)/списки - это единый параграф. - -Типы текста (строк) - - -Обычный текст - -Может в оригинале находиться на нескольких строках. -Строки могут разделяться -- обычный break - это когда на строке после текста идет перевод строки - -такой текст просто объединяется (в нашем случае если возможно то следующая строка продолжает -выводиться на той же строке что и предыдущая, только отделяется от нее уже не переводом строки -а через пробел. -- широкий break - это когда перед переводом строки есть два или более пробелов или перед переводом -строки находится символ обратный слэш '\' - в этом случае и когда мы выводим текст на экран следующая -строка начинается с новой строки. -- жесткий break - это когда две строки обычного текста разделяются двумя или более переводами -стрки (то есть между ними как минимум есть одна пустая строка) - в этом случае вторая строка -будет считаться новым параграфом и отделяться от предыдущей строки ОДНОЙ пустой строкой (вне -зависимости от того сколько пустых строк в оригинале. -Должно сохраняться действие модификатора bold/italic/strike начатое на одной строке на следующие -если они так же находятся в этом параграфе. Новый параграф (после пустой строки) теряет воздействие -незакрытого модификатора из предыдущего параграфа. -Тип переноса строк - Wrap. - -Заголовки - - -абзацы из одной оригинальной строки. Начальный стиль зависит от типа заголовка (в нем могут встречаться -модификаторы bold/italic/code/strike. -Тип переноса строк - Wrap. - -Разделитель (horizontal rules) - - -Всегда одна строка. После нее выводим пустую строку всегда (следующий текст - новый абзац) - -Списки - -как и в обычном тексте - один пункт списка может находиться в оригинале на нескольких строках. -если появляется новая строка с префиксом пункта списка - это означает что с этой строки начинается -новый пункт списка. если появляется пустая строка - это означает что следующая за пустой строкой -непустая строка уже не является продолжением пункта списка. -Если следующая за пустой строкой строка так же является пунктом списка то такая строка считается -продолжением текущего списка. Только если две строки списков разделены ДВУМЯ и более пустыми -строками то вторая строка с пунктом списка будет считаться началом нового списка - -Пример - - -- строка 1 -- строка 2 -строка 3 -- строка 4 - -- строка 5 - - -- строка 6 - -Должно отображаться так - - -- строка 1 -- строка 2 строка 3 -- строка 4 -- строка 5 - -- строка 6 - -Тип переноса строк - Wrap. -Замечание - перенесенная строка продолжение пункта списка должна начинаться с отступа в -несколько пробелов (для списка первого уровня - два пробела) - то есть с той же позиции -что и начальный текст строки - -Пример - - -- длинная строка которая не может поместиться и будет перенесена по слову 'будет' - -Должно отображаться так - -- длинная строка которая не может поместиться и - будет перенесена по слову 'будет' - -Незакрытые модификаторы типа (bold/etc.) не переносят свою модификацию не последующие пункты списка. -Действуют только в пределах одного пункта. -(то есть фактически - пункт списка - это отдельный параграф но следующий пункт списка (тоже отдельный -параграф) не отделяется от него пустой строкой. -Вложенные списки - поддерживаются. -Для отображения префиксов пунктов списка используется свой стиль. Так же для текста списков используется -отдельный (от обычного текста) стиль. - -Блок Кода - - -Весь блок кода отображается только одним стилем - стилем Кода. В нем не действуют модификаторы -bold/italic/etc (возможно в дальнейшей использование парсера языка для кода что бы отобразить -этот код с подсветкой синтаксиса этого языка, но сейчас весь блок рисуется только одним стилем) -После блока кода - обязательна пустая строка. -Тип переноса строк - UnWrap. - То есть строки НЕ ПЕРЕНОСЯТСЯ. - -Quoted - - -Отображается как отдельный параграф. Перед отображением текста отображается символ префикса Цитирования -Способы переноса текста аналогичны обычному тексту, за исключение того что перенесенные строки так же -предваряются префиксом Цитирования -Для отображения префиксов Цитирования используется свой стиль. Так же для текста цитирования используется -отдельный (от обычного текста) стиль. -Тип переноса строк - Wrap. -Пример форматирования - - -> Первый параграф -> -> Второй параграф -> > Вложенный параграф -> -> Продолжение основной цитаты - длинная строка (переносится по слову 'строка') - -Будет отображаться так - -| Первый параграф -| -| Второй параграф -| | Вложенный параграф -| -| Продолжение основной цитаты - длинная -| строка (переносится по слову 'строка') - - -Таблицы - - -Пока отображаются as is. -Дальше возможно предусмотрим вариант вычисления ширины столбцов и форматирование -вывода что бы все ячейки столбца имели одинаковую ширину. -Тип переноса строк - UnWrap. - -Типы переноса строк - - -Wrap - - -происходит перенос текста с одной строки на другую по разделителям пробелам (предусмотреть -во второй версии возможность использовать разделителем знаков точка, запятая, восклицательный -знак, вопросительный знак, дефис. Замечание - новая строка не может начинаться со знаков -разделителей - то есть если у нас идет многоточие (три точки) то нельзя что бы одна точка -была в конце первой строки а остальные две на другой - если идут несколько разделителей подряд -то они считаются как один и должны оставаться на первой строке). - -UnWrap - - -Текст НЕ ПЕРЕНОСИТСЯ. -Если строка не помещается на экране - то в конец строки на экране выводим символ-знак наличия -продолжения строки справа за краем экрана (свой стиль для этого символа). -Символ '>'. -Если есть хотя бы одна строка которая не помещается на экране - то разрешаем горизонтальный -скроллинг. -ЗАМЕЧАНИЕ - скроллируется только текст выводящийся в режиме UnWrap. -Если произведен скроллинг влево (строки UnWrap начинаются показываться не с первой позиции) -то на первой позиции отображем другой символ '<' сообщающий пользователю что есть текст -за левым краем экрана. -Скроллирование не бесконечно - если на экране нет строк UnWrap для которых есть скрытый текст -за правым краем экрана то скроллирование влево больше не возможно, и наоборот - если при -скроллировании вправо дошли до показа строк UnWrap с первой позиции то дальше скроллинг в этом -направлении так же невозможен. Скроллинг идет с грануляцией по 8 символов. -(сейчас UnWrap текст это только блоки кода и таблицы). - - -Проанализируй данную постановку задачи. - -Что требуется - - -Мы более не поддерживаем два режима показа - Wrap/UnWrap - только один (смешанный). -Потому вопрос - надо ли нам сохранять оригинальный контент считанного файла с диска ? -Или при подготовке данных считываемых с диска можно сразу преобразовывать его в формат -готовый для показа (объединять строки в одном параграфе и так далее) ? -Ограничения - не забывать о том что это все работает на компьютере с 8-ми битным процессором. -Потому очень экономно относимся к памяти и лишней работе процессора - все должно быть весьма -быстрым и компактным. -Требования для работы с файлами 128 Кб сохраняется. - -Для версии 2 - -Подумай о возможности использования банков памяти не только для содержимого файла а например -для буферов типа line_offset/init_style и прочих (тогда их размер может так же быть динамическим -и чем больше банков памяти будет использовано для чтения файла тем больше станут размеры этих -буферов. \ No newline at end of file diff --git a/examples/mdview2/CHANGELOG.md b/examples/mdview2/CHANGELOG.md deleted file mode 100644 index 377c447..0000000 --- a/examples/mdview2/CHANGELOG.md +++ /dev/null @@ -1,79 +0,0 @@ -# MDView - CHANGELOG - -ଠ: ᨨ git-ਨ ஥. ࠭ CP866 - - 㤮 ᠬ MDView ( ⮤⥪ ஢). - -## v1.0 (b3) - 2026-07-05 - -- ⮢ ண ஢: 짮⥫ ⠥ - 㬥, 蠬 ண - (UTF-8 8-⭮ ࢨ筮 ). ࢮ F8 筮 - 㦥 ⮢ - ४祭 . F8 ⮢ ந - , 稢 ⮥ 䮭 ( ). -- : ࠭ ண, ᫥ - 뤥ন 宫 - ஫ ⮯஬ 䮭 . -- ॢ १㥬 蠣 (index_begin/index_step); - 筮 ࠩ: UTF-樨 ᢥ夫 (>4 ) - १ ⮪ 㬥. -- ஢ - 뢠 ࠧ: ⥪஢ - - 砫 ࢮ 樨, F8 - ४祭, - ᡮન . - -## v1.0 (b1) - 2026-07-05 - -- HEX-० (F4): 䠩 ଠ - "0x012340 : 16 hex : 16 ᨬ". - ⭠ 뢠 ⥪ ஢; UTF-8 - ⠢ 樨 -, continuation- - 窮. - ஢ (F8) HEX ⮫쪮 . -- ப⪠ HEX ப - ஫ ᮢ - 鸞 ( MD/RAW). -- ࠢ: F4, v1.0 (b1). - -## v1.0 (a4) - 2026-07-04 - -- ४祭 ஢: F2 (MD/RAW), - F8 (8- / UTF-8) ࠭ ⥪饥 㬥. - ࠬ ࠧ ࠧ ७ ய樮쭮 - ⠬ 筮 ப. -- ࠢ: F8 RAW-० ᮢ MD- RAW. -- ⨬ ࠧ: exe 28215 -> 25100 (-11%): - ᨢ ॣ஢ SDCC (--max-allocs), 㫥 - _DATA crt0 ( "= 0" ⨪), 㯫 , - ᫨ﭨ ᪠஢ inline-ࠧ⪨ scan_stream. - -## v1.0 (a3) - 2026-06-30 - -- 䠩 256 : 訩 䠩 १ ।०, - ⠭ 㯭. -- ௠ / 蠥 ப-ᮮ饭 - 稭 ( 宩 ப). -- ஢ - ࠢ; 業 ப⪨ 16-⭮ - 䬥⨪ ( 32-⭮ ). -- ⮢ 䠩 ࠭ 砥 (testfiles/, ). - -## v1.0 (a2) - 2026-06-26 - -- 饯 㫨: + index/md/raw/enc/table/status/help - (ࠧ-ࠫ쭮, ⠥). -- ࠢ 㡫 ப RAW ஫ 䠩. - -## v1.0 (a1) - 2026-06-25 - -- প ஢ CP866/CP1251/KOI8-R/UTF-8 (F8): - ⮮। ⨨, ६ 8-, - UTF-8 ⤥ (६⠫쭮). -- RAW-ᬮ 室 (F2): ७ ⭮ 80 ࠬ (F3). -- ⨬ ࠧ (-1.9 ). - -## v1.0 (a0) - 2026-06-23..24 - -- : render-cache ⥪ - 室 , - ⮢ ப (ᨬ+ਡ) EMM, ப⪠. -- Markdown: , ᯨ᪨, , ࠧ⥫, fenced-, - ⠡ ࠬ -ਭ , inline-ࠧ⪠ - (/ᨢ/ભ/ભ/, ࠭஢). -- ணᨢ 㧪: ࠭ ࠧ, 䮭, - ⮢ , Esc/F10 뢠. -- ਧ⠫ ஫ ⠡. -- ᪮७ ९ᨭ (22 -> 10 讬 䠩). diff --git a/examples/mdview2/DEMO.MD b/examples/mdview2/DEMO.MD deleted file mode 100644 index e4b414e..0000000 --- a/examples/mdview2/DEMO.MD +++ /dev/null @@ -1,840 +0,0 @@ -# MDView — Просмотрщик Markdown для Sprinter - -**MDView** — программа для просмотра документов в формате *Markdown* на компьютере -Sprinter (процессор Z80). Документ хранится в отдельном W3 окне и не занимает -основную RAM программы. - -## Возможности - -- Документы до **128 КБ** (8 страниц EMM по 16 КБ каждая) -- До **16 384** экранных строк в индексе -- Автоматический перенос слов по ширине экрана (80 столбцов) -- Горизонтальный сдвиг для широких строк (блоки кода, таблицы) -- Статус-бар: имя файла, диапазон строк, процент прокрутки -- Спиннер в строке состояния во время загрузки и индексации -- Поддержка «мягкого» склеивания строк в абзацах и цитатах - -## Запуск - -``` -mdview [имя_файла.md] -``` - -Если имя файла не задано, загружается `README.MD`. - -## Управление - -``` -Клавиша Действие -───────────── ──────────────────────────────────────── -Up Down Прокрутка на одну строку вверх / вниз -PgUp PgDn Прокрутка на страницу (30 строк) -Home Начало документа -End Конец документа -Left Right Горизонтальный сдвиг (только nowrap-строки) -F1 Окно справки -F8 Кодировка: CP866 → CP1251 → KOI8-R → UTF-8 → ... -F10 / Esc Выход из программы -``` - -Кодировка определяется автоматически при открытии (BOM + эвристика по -первым 4 КБ); `F8` переключает её вручную, если детекция ошиблась. -8-битные кодировки (CP866/CP1251/KOI8-R) переключаются мгновенно (ремап на -отрисовке). Второй набор индекс/кэша (например UTF-8) строится лениво — при -первом переключении в него (короткая пауза со спиннером), дальше мгновенно. - -## Синтаксис Markdown - -### Заголовки - -Поддерживаются уровни H1–H4. Уровни H5 и H6 отображаются как H4. - - # Заголовок первого уровня - ## Заголовок второго уровня - ### Заголовок третьего уровня - #### Заголовок четвёртого уровня - -### Текстовое форматирование - -**Жирный текст** выделяется двойными звёздочками: `**текст**` - -*Курсив* выделяется одиночными звёздочками `*текст*` или знаком подчёркивания `_текст_` - -`Встроенный код` обозначается обратными кавычками - -~~Зачёркнутый текст~~ — двойные тильды: `~~текст~~` - -### Ненумерованный список - -Маркеры `-`, `*` или `+`: - -- Первый пункт списка -- Второй пункт списка -- Третий пункт с достаточно длинным текстом, который при необходимости - будет перенесён на следующую строку с сохранением отступа - -### Нумерованный список - -1. Первый элемент -2. Второй элемент -3. Третий элемент - -### Цитата - -> Блок цитаты начинается с символа `>`. Несколько последовательных -> строк одной цитаты склеиваются в единый абзац с автоматическим -> переносом слов. - -### Блок кода (verbatim) - -Блок кода заключается в тройные обратные кавычки. Внутри блока -текст отображается «как есть» без разбора Markdown: - -``` -#include -#include - -int main(void) { - puts("Hello, Sprinter!"); - return 0; -} -``` - -### Горизонтальная линия - -Три или более символов `---`, `***` или `___` на отдельной строке: - ---- - -## Технические характеристики - -- **Платформа:** Sprinter, процессор Z80 @ 21 МГц -- **Кодировки:** CP866 / CP1251 / KOI8-R / UTF-8 (автоопределение, `F8`) -- **Максимальный размер файла:** 128 КБ -- **Максимальное число строк в индексе:** 16 384 -- **Максимальная длина строки в рендер-кэше:** 255 ячеек (см. ограничение ниже) -- **Режим памяти:** small - - Код программы, cтек, данные, куча — окнa W1-W2 (32 КБ, адреса 0x4000–0xBFFF). - - Буфер файла — страницы EMM, отображаемые в W3 (0xC000–0xFFFF) - -## Известные ограничения - -### Длина строки в MD-режиме — 255 ячеек - -Рендер-кэш хранит каждую логическую строку как **не более 255 пар (символ, атрибут)** -— константа `MAX_CACHE_LINE_LEN`. Лимит задан типами: `g_ncells` и -`cache_rec_t.len` — `uint8_t`. Касается всех строк, но заметнее всего на -**горизонтально скроллируемых** строках (блоки кода и строки таблиц, флаг -`IF_HSCROLL`), которые в MD-режиме можно листать вправо. - -**Что происходит с более длинной строкой:** при индексации `gc_put()` молча -отбрасывает каждую ячейку после 255-й (`if (g_ncells < MAX_CACHE_LINE_LEN)`). -В кэш попадают только первые 255 ячеек, остаток **теряется** — до него нельзя -доскроллить и **нет маркера обрезки** на 255-й позиции (маркер `>` означает лишь -«есть ещё в пределах кэша»). Переполнения буфера нет — `gc_put` проверяет границу. - -Важно: «255 ячеек» — это **отрендеренная ширина**, не байты исходника. Табы в -коде разворачиваются в пробелы (до `TAB_STOP`), а ячейки таблицы добиваются -пробелами до ширины колонки + рамки `│` — поэтому кап достигается раньше, чем -255 «полезных» символов. - -> **RAW-режим (`F2`) этого лимита не имеет** — он рисует прямо из файла -> побайтово, длинные строки видны целиком (через wrap `F3` или гориз. скролл). - -**Идея снятия лимита** (оценка, не реализовано) — расширить длину до `uint16_t`: -- `cache_rec_t.len` `uint8_t→uint16_t` — структура остаётся **ровно 8 байт** - (len съедает один pad-байт), адресация `idx<<3` не меняется. Бесплатно. -- `g_ncells` / `g_ncells_at_space` → `uint16_t` — главная цена по **коду/скорости**: - 16-битная арифметика на Z80 в горячем `gc_put` (вызов на каждую ячейку) и в - scan-циклах. Ориентир: **+0.2…0.4 КБ кода** + замедление индексации. -- Буфер `g_cells[MAX_CACHE_LINE_LEN*2]` в near-RAM (W2) — главная цена по - **памяти**: `2 × кап` байт. Сейчас 510 Б; кап 512 → +0.5 КБ, 1024 → +1.5 КБ. - EMM-кэш контента (1 МБ) длинные строки тянет легко — узкое место именно near. -- `viewport_x` (и копия в `docset_t`) + потолок `max_vx` (сейчас 248) → `uint16_t`, - иначе хранить >255 можно, а доскроллить нельзя. Плюс `widths[]`/`ccx` в таблицах/коде. - - Реалистичный компромисс — кап 512–1023: хватит почти всем листингам/таблицам, - цена ~+0.3 КБ кода и +0.5…1.5 КБ near-RAM. **Дешёвая полумера без `uint16_t`** — - ставить честный маркер обрезки на 255-й позиции, чтобы потеря была видна. - -## TODO - -1. **Увеличение размера документов.** Снять лимит 128 КБ: Достаточно - разрешить работать с большим кол-вом страниц памяти, пока оттестированно - на работе с 8-мю страницами по 16Кб. - -Сделано: форматированные таблицы с рамкой; поддержка кодировок -CP866 / CP1251 / KOI8-R / UTF-8 с автоопределением и переключением по `F8` -(второй набор строится лениво, по первому переключению). - -4. **Ускорение рендеринга.** Кэш строк экрана. Оптимизация цикла вывода - символов через BIOS WRCHAR (пакетный вывод, DMA). - ---- - -*MDView v0.2 · (c) 2026 Петров А.Г.* - - ---- - -# Sprinter C Compiler — v1.0 - -C toolchain for **Sprinter** — the Z80-based home computer by Peters Plus, running -ESTEX DSS. Host: macOS / Linux. Target: `.EXE` files in SprintEXE format. - -Built on top of **SDCC 4.5** (vendored in `third_party/sdcc/`). This repository adds -everything Sprinter-specific: crt0, linker integration, libc wrappers over ESTEX, -banked-call trampolines, graphics & accelerator API, mouse driver wrappers, and the -`mkexe` utility for producing SprintEXE images. - -## What you get - -* **`bin/sprinter-cc`** — one-line driver: `sprinter-cc -o foo.exe foo.c` -* **Memory modes**: `tiny`, `small`, `big`, `huge`, `manual` — see below. -* **stdio + conio**: printf, puts, putchar, getchar, fopen/fread/..., cprintf, cputs, putch, textcolor/textbackground/textattr, gotoxy, kbhit/getch. -* **Graphics**: 320×256×256 and 640×256×16 modes, accelerator-backed primitives (hline / vline / rect / fill_rect / line via Bresenham, plus clear), bitmap-font text in both modes via BIOS character generator. -* **File I/O**: POSIX (`open`/`read`/`write`/`close`/`lseek`/`unlink`/`creat`), FILE\* streams (`fopen`/`fgets`/`fwrite`/...), directory listing (`ffirst`/`fnext`), `chdir`/`getcwd`/`mkdir`/`rmdir`, `stat`/`fstat`. -* **Memory**: 32 KB heap (W2-resident), banking-aware page allocator (`mem_alloc_pages`/`bank_read`/`bank_write`), explicit memory modes for sub-16 KB programs. -* **Mouse**: full Sprinter driver wrapper (14 functions including custom cursor bitmaps). -* **Environment**: `getenv`/`putenv`/`sysenv` over ESTEX `$46`. -* **Time**: `getdatetime`/`setdatetime` + POSIX `time`/`localtime`/`mktime`/`asctime`/`ctime`. -* **Misc**: `errno`/`strerror`/`perror`, `atexit`, `setjmp`/`longjmp`, `sleep`, full argv parsing in crt0. - -## Quick start - -```sh -git clone sprinter-c -cd sprinter-c -make sdcc # one-time: fetch SDCC 4.5 binary (~25 MB) -make all # build mkexe + libsprinter.lib + 27 examples -make floppy # pack everything into mame/v306/IMG/mc.img -cd mame/v306 && ./run_mame.sh # boot Sprinter in MAME -``` - -Compile a single program: - -```sh -cat > hello.c < -int main(void) { puts("Hello, Sprinter!"); return 0; } -EOF - -bin/sprinter-cc -o hello.exe hello.c -``` - -That's it — `hello.exe` is now a valid SprintEXE you can `RUN HELLO` from the ESTEX shell. - -## Memory modes - -Sprinter's address space is four 16 KB windows (W0 / W1 / W2 / W3). DSS allocates -pages by program size — small programs get only one page. Pick a memory mode based -on what your program needs: - -| Mode | Code lives in | Banking | Use when | Note | -|---|---|---|---|---| -| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | | -| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | | -| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | | -| `huge` | W1-W2 + W3 banking | yes (W3) | small + extra code modules | | -| `manual` | user-specified | optional | special layouts | Not implemented | - -```sh -sprinter-cc --memory small -o big.exe bigprog.c -sprinter-cc --memory huge -o app.exe main.c --bank 1=engine.c --bank 2=ai.c -``` - -Banked functions are declared with `__banked`: - -```c -void engine_tick(int dt) __banked; // lives in BANK1, automatically swapped -``` - -## Examples (27 total) - -| Example | What it demonstrates | -|---|---| -| `hello` | Hello world with stdio + conio Turbo-C-style colors | -| `argv` | argv parsing in crt0 | -| `cat` | File I/O — read & print TEST.TXT | -| `seek` | 32-bit lseek over a 100 KB file | -| `ls` | Directory listing via ffirst/fnext | -| `filetest` | FILE\* streams (fopen/fread/...) | -| `stattest` | `stat`/`fstat` on files and directories | -| `errno` | errno / strerror / perror | -| `mem_test` | Page allocator + bank\_read/bank\_write | -| `malloc` | Heap stress test (200+ allocations) | -| `banked` | Banked code in W3 (huge mode) | -| `bankedbg` | Banked code in W1 (big mode) | -| `banklocl` | Bank-local static data and BSS | -| `mouse` | Mouse driver in text mode | -| `gfx_mous` | Mouse with custom bitmap cursor in graphics mode | -| `gfx_demo` | 320×256×256 lines / rects / accelerator | -| `gfx_d16` | 640×256×16 same primitives | -| `gfx_text` | Bitmap-font text on graphics screen | -| `timedir` | Date/time + directory listing | -| `ptime` | POSIX time API | -| `openenv` | open() flags + environment vars | -| `conio` | conio API smoke test | -| `attrprob` | Probe Sprinter text attribute byte layout | -| `strtest` | string.h test (from SDCC's z80.lib) | -| `stdlib` | stdlib.h test (qsort / rand / strtol / etc.) | -| `assrtest` | assert() | -| `rt_test` | Runtime helpers (sleep, setjmp, atexit) | - -## Headers - -Standard: -* `` — puts / printf / FILE\* + Sprinter-specific dec/hex helpers -* `` — atoi / atof / malloc / qsort / ... (from SDCC z80.lib) -* `` / `` / `` — from SDCC z80.lib -* `` — read / write / close / lseek / unlink -* `` — open / creat + O\_RDONLY / O\_CREAT / ... -* `` — errno + error names + strerror -* `` — stat / fstat -* `` / `` — from SDCC - -Sprinter-specific: -* `` — putch / cputs / cprintf, textcolor / textbackground / textattr, kbhit / getch, clrscr, gotoxy, wherex/y -* `` — gfx_init/done, palette, putpixel, hline/vline/rect/fill_rect/line, text — for both 320 and 640 modes (gfx_\*16 variants) -* `` — full 14-function driver wrapper + mouse_cursor_t with bitmap support -* `` — chdir / getcwd / mkdir / rmdir / ffirst / fnext / ffblk -* `` — getdatetime / setdatetime + POSIX time / localtime / etc. -* `` — raw ports, ESTEX/BIOS function numbers, env API -* `` — exit / \_exit / atexit -* `` — mem\_alloc\_pages / mem\_free\_block / bank\_read / bank\_write -* `` — Solid-C compatibility layer (aliases + BOOL/WORD/uint types) - -## Toolchain commands - -```sh -make all # build mkexe + lib + every example -make floppy # repack mame/v306/IMG/mc.img with all .exe files -make check # 17 mkexe unit-tests -make clean # remove all build artefacts -make sdcc # one-time: fetch SDCC 4.5 binary -``` - -### sprinter-cc options - -``` -sprinter-cc -o foo.exe foo.c [more.c ...] [options] - - --memory MODE tiny | small | big | huge | manual (default: tiny) - --memory-manual SPEC explicit placement (CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3) - --stack-size N bytes reserved for the stack (default ~1278) - --crt0=TYPE default | minimal | banked | small - --bank N=FILE.c compile FILE.c into bank N (repeatable, max 15) - --debug enable runtime diagnostics (defines DEBUG_RT) - -I PATH extra include path - -L 0xADDR / -E / -S override load / entry / stack addresses - -Wl FLAG pass FLAG to sdldz80 - --mkexe FLAG pass FLAG to mkexe (e.g. --mkexe -p --mkexe 0 for bank padding) - -v verbose -``` - -## Status - -What works in v1.0: -* Compile / link / pack to SprintEXE — verified on all 27 examples -* Four memory modes (tiny / small / big / huge) -* Graphics (both modes) with accelerator -* Mouse (text + graphics cursor) -* File I/O, directories, environment, time -* All headers listed above - -Deferred to v2.0 (see `docs/TODO.md`): -* **Turbo-C-style BGI graphics API** — `initgraph` / `setcolor` / `circle` / - `getimage` / `putimage` / etc. on top of our `gfx_*` primitives -* Remaining Solid-C compatibility gaps (Phase 2/3) — see `docs/solid_c_compatibility.md` -* Manual memory mode -* Rewrite FILE\* stream API (current implementation is very primitive and doesn't use buffers) - -Deferred to v3.0: -* **IM2 interrupt handlers** — research complete (`docs/im2_isr_design.md`), - implementation scheduled for v3 -* **Audio API** (AY-3-8910 + COVOX) — requires IM2 -* **ISA-8 slot drivers** — requires IM2 (???) - -## Documentation - -* `docs/TODO.md` — roadmap and open work items -* `docs/solid_c_compatibility.md` — gap analysis vs Solid-C 2004 -* `docs/im2_isr_design.md` — interrupt handler design (v2) -* `docs/converted/` — source documentation (ESTEX, BIOS, architecture) - converted to plain text for `grep` -* `docs/reference/`, `docs/samples/`, `docs/memory management/` — original - Russian docs and code samples from Peters Plus - -## Repository layout - -``` -bin/sprinter-cc one-line compiler driver (bash) -toolchain/mkexe/ host-side tool: .ihx -> .exe SprintEXE -toolchain/check_banks.py post-link bank size enforcer -runtime/ crt0 variants (default, minimal, small, banked) - bank trampolines, heap, heap_top -libc/include/ headers -libc/io|stdio|mem|gfx/ C and asm sources for libsprinter.lib -lib/ Makefile that archives libsprinter.lib via sdar -examples/ 27 example programs -mame/v306/ MAME binary + Sprinter ROM/HDD images + floppy script -third_party/sdcc/ vendored SDCC 4.5 (fetched via `make sdcc`) -third_party/solid-c/ reference: original Sprinter native C (for compat target) -docs/ documentation -``` - -## License - -This repository contains: -* Original code in `bin/`, `toolchain/`, `runtime/`, `libc/`, `lib/`, `examples/` — - MIT-licensed. -* `third_party/sdcc/` — SDCC 4.5 under GPLv2 with linking exception - (see `third_party/sdcc/COPYING.txt`) -* `third_party/solid-c/` — original Sprinter Solid C, used only as a reference - -## Credits - -* **Sprinter / Peters Plus** — Иван Мак, Дмитрий Паринов and the original team -* **SDCC** — for the underlying Z80 compiler -* **MAME** — for the Sprinter Sp2000 emulation - ---- - -For questions / patches: see CONTRIBUTING.md (TBD) or open an issue. - ---- - -# TODO / Roadmap - -Открытые задачи в порядке убывания приоритета. По мере появления реальных программ — приоритеты будут смещаться. - -## Этап 5 — malloc / free + banking-aware page allocator ✅ ГОТОВО - -- [x] SDCC's `malloc`/`free` + наш `runtime/heap.s` (полностью заменяет library heap.rel, 14000-байтный heap в окне 2) -- [x] `libc/mem/mem_alloc.c` — page allocator: `mem_alloc_pages`/`mem_free_block`/`mem_get_page`/`mem_info` через ESTEX `$3C/$3D/$3E` + BIOS `$C4` -- [x] `libc/mem/bank_io.c` — HOME-резидентные `bank_read`/`bank_write`/`bank_load_byte`/`bank_store_byte` со свопом W3 внутри -- [x] `examples/malloc_test/` — проверка SDCC's malloc (~210 64-байтных allocations через всю heap) -- [x] `examples/mem_test/` — проверка page allocator: 3 страницы, разные паттерны через bank_write, верификация через bank_read - -## Этап 6 — argv parsing + sprinter-cc wrapper ✅ ГОТОВО - -- [x] crt0 парсит ESTEX command-line из IX-prefix (inline asm в `runtime/crt0.s`) -- [x] Strip leading CP/M-style space (DSS quirk) -- [x] Передача `argc`/`argv` в main() через HL/DE (SDCC __sdcccall(1) ABI) -- [x] argv[0] = basename .EXE через ESTEX APPINFO ($47 subfn 2) -- [x] `runtime/crt0_minimal.s` — opt-out для очень маленьких программ -- [x] `runtime/crt0_banked.s` — теперь тоже парсит argv (parse_argv + get_progname скопированы из crt0.s; будет factored в argv.s когда возьмёмся за libsprinter.lib) -- [x] Bash-обёртка `bin/sprinter-cc`: `sprinter-cc -o foo.exe foo.c` одной строкой -- [x] Поддержка опций: `--memory`, `--memory-manual`, `--stack-size`, `--crt0=`, `--bank N=FILE.c`, `--debug`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` - -## Этап 8 — графика (320×256×256 + 640×256×16 + accel + bitmap font) ✅ ГОТОВО - -- [x] **8a** Graphics core: `gfx_init`/`gfx_done`/`gfx_clear`/`gfx_putpixel`/`gfx_pal_load`/`gfx_pal_set` (libc/gfx/gfx_core.c). Палитра через BIOS PIC_SET_PAL ($A4). Verified 320×256×256. -- [x] **8b** Линии/прямоугольники/fill через accelerator (libc/gfx/gfx_lines.c): `gfx_hline`/`gfx_vline` через accel Fill (LD C,C / LD E,E + SMC block-size), `gfx_rect`/`gfx_fill_rect` с heuristic выбором ориентации (h/v bursts count), `gfx_line` с Bresenham для диагоналей. `gfx_clear` тоже переписан на column-major accel (~4× быстрее). -- [x] **8c** 640×256×16 mode (libc/gfx/gfx_16.c): `gfx_*16` API, HIGH nibble = LEFT pixel (документация misleading), per-row RMW для vline (один байт = 2 горизонтальных пикселя). -- [x] **8d** Bitmap font + gfx_text (libc/gfx/gfx_text.c): шрифт через BIOS WIN_GET_ZG ($B8), interleaved layout `font[row*256+char]`, `gfx_text`/`gfx_putchar` для 320 mode, `gfx_text16`/`gfx_putchar16` для 640 mode с pair-table lookup. - -См. memory/sprinter_graphics.md, sprinter_accelerator.md, sprinter_graphics_16.md, sprinter_font_format.md. - -Открытые мелочи (не блокируют): -- [ ] Шрифт-quad для 640: per-cell палитра (mode 0x82 разрешает 1 из 4 палитр per 16×8 cell) — через прямой доступ к area-описания экрана 0x0300..0x039F - -## Auto-banking (см. `memory/banking_roadmap.md` для деталей) - -Phase 1 — file-level bin-packing — реализовывать когда проект перерастёт ~30 KB кода. - -- [ ] `toolchain/auto_bank.py`: - - Парсит размеры из `.rel`-файлов (или из .map после dry-run link'а) - - First-fit-decreasing bin-packing - - Уважает `#pragma codeseg BANKn` как manual override - - Перелинковывает с новыми `-Wl-b_BANKn=...` параметрами - - Печатает план распределения - -Phase 2-5: incremental rebalance, declarative `banks.toml`, function-level, call-graph-aware. Только если/когда понадобится. - -## Bank-local static data (mutable data в том же банке что и код) — ✅ ГОТОВО - -- [x] Пример `examples/bank_local_data/` — функция в BANK1 со своим writable BSS array + const table + malloc-тест -- [x] `mkexe -p 0` для нулевого padding банков (BSS-storage обнуляется при загрузке) -- [x] Канонический рецепт: `--codeseg BANK1 --constseg BANK1 --dataseg BANK1` для bank1.c + `-Wl-b_BANK1=0x1C000` для линковки. **`--dataseg BANK1` РАБОТАЕТ** — раньше казалось обратное из-за trampoline bug который маскировал результат. -- [x] **Критичный фикс trampoline'a в runtime/bank.s** — старый `pop af; out (n), a` клобберил A → все banked-функции возвращающие uint8_t тихо возвращали мусор. Новый `pop bc; out (c), b` сохраняет A. -- [x] **malloc из banked-функции работает прозрачно** — heap живёт в W2 (HOME), W2 никогда не свапается trampoline'ом, pointer валиден из любого контекста. См. memory/bank_local_data_pattern.md. -- [x] Документация в memory: `memory/bank_local_data_pattern.md` (полный рецепт + malloc + nuances), `memory/sdcc_banking.md` (trampoline fix) -- [ ] Опционально — расширить `check_banks.py` чтобы показывать разбивку size = code + const + bss per bank (cosmetic) - -Зачем: для модулей с большим private state (level loader, audio engine, scene data). Экономит W2 heap для динамики, а статика остаётся в бэке. - -## Подсказки из solid-c (нативный Sprinter C — `third_party/solid-c/`) - -После анализа solid-c'овской libc (см. `memory/solid_c_findings.md`) выявлены готовые паттерны для следующих недостающих функций. Приоритет от **высокого** к низкому: - -### High-priority gaps (легко портировать, большая польза) -- [x] **`errno` + `strerror`/`perror`** — табличка 32 ошибок (libc/io/errno.c) -- [x] **Расширенный `open()`** для O_CREAT/O_TRUNC/O_APPEND/O_EXCL state machine -- [x] **`atexit`** — 8-callback LIFO + `exit()` + `_exit()` (libc/io/atexit.c) -- [x] **`setjmp`/`longjmp`** — 6-байт jmp_buf={sp,ix,pc} (libc/io/setjmp.c) -- [x] **`sleep(seconds)`** — 50Hz halt-loop (libc/io/sleep.c) -- [x] **ESTEX ENV API** ($46, getenv/putenv) — libc/io/env.c. Учли doc-bug: реально A=0 это NOT FOUND - -### Medium-priority (нужно для shell-like утилит) -- [ ] **Mouse driver** — `rst $30h`, 17 функций. **Сначала тест что работает в MAME**. -- [x] **`ffirst`/`fnext` + ffblk_t struct** для directory listing — реализовано, demo: ls.exe -- [x] **`getdatetime`/`setdatetime`** через ESTEX $21/$22 — libc/io/time.c, demo: time_dir_test -- [x] **`chdir`/`getcwd`/`mkdir`/`rmdir`** — wrappers для ESTEX $1B-$1E — libc/io/fsdir.c -- [x] **conio: `kbhit`/`getch`/`getche`/`cputs`/`clrscr`/`gotoxy`** — реализовано -- [x] **conio extras**: `wherex`/`wherey` ($53), `wrchar`/`rdchar` ($58/$57), `textmode_get/set` ($50/$51), `clrscr_attr` ($56) + COLOR macros - -### Low-priority — ✅ FILE* stack ГОТОВО - -- [x] **Минимальный unbuffered FILE\*** — `libc/stdio/file.c` + `libc/include/stdio.h`. fopen/fclose/fputs/fgets/fread/fwrite/fseek/ftell/rewind/feof/ferror/clearerr/fflush + stdin/stdout/stderr как pseudo-streams. См. `memory/file_star_design.md` и `examples/filetest`. -- [ ] fprintf / fscanf — нужна printf-через-callback machinery. Пока пользователь может `sprintf(buf, ...) + fputs(buf, fp)`. -- [ ] Опциональный buffered mode (setvbuf, line/block buffering) — если когда-то понадобится. - -### POSIX time API — ✅ ГОТОВО -- [x] `libc/io/posix_time.c` — time/localtime/gmtime/mktime/asctime/ctime поверх getdatetime. SDCC's time.rel избегаем (нельзя override _RtcRead). См. `examples/ptime`. - -### sys/stat — ✅ ГОТОВО -- [x] `libc/io/stat.c` — POSIX stat/fstat. Гибрид open+fstat для файлов, ffirst+iter для папок (включая "."/".."). См. `examples/stattest` и `memory/estex_ffirst_dotdot.md`. - -### assert — ✅ ГОТОВО (используем SDCC's __assert через fallback include path) - -## libc/stdlib — ✅ не нужно делать (см. memory/sdcc_stdlib_works.md) - -Проверено через `examples/stdlib_test/`: SDCC z80.lib содержит работающие реализации: -- `atoi/atol/atof, strtol/strtoul, rand/srand, qsort/bsearch, abs/labs, div/ldiv` -- Полный `` (memchr/cmp/set/cpy, strcat/cmp/cpy/len/chr/spn/etc.) -- `` (toupper/tolower) -- `` (sinf/cosf/sqrtf/etc.) - -Линкер автоматически тянет из z80.lib когда нужно. **НЕ переписывать**. - -Наши Sprinter-specific обязательные модули остаются: atexit, env, errno, setjmp, putchar/puts/getchar, conio, fsdir, time, mouse, open/read/lseek/close. - -## Build-system: libsprinter.lib + sprinter-cc — ✅ ГОТОВО - -- [x] `lib/Makefile` — собирает каждый libc/*.c в `.rel`, архивирует через sdar в `lib/sprinter.lib` -- [x] Включает runtime/bank.s и runtime/heap.s (auto-pulled при __banked/malloc) -- [x] `bin/sprinter-cc` — bash-wrapper: `sprinter-cc -o foo.exe foo.c` одной строкой -- [x] Поддержка опций `--crt0=default|minimal|banked`, `--bank N=FILE.c`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` -- [x] `examples/hello_sccc/` — демо: `hello.c` собирается за один shell-вызов, размер совпадает с ручным Makefile (925 байт) -- [x] Split `putchar.c` → `putchar.c` + `puts.c` для per-function granularity (puts override SDCC's z80.lib version) -- [x] Включён в `make all` (зависимость `lib` перед `examples`) - -Возможные улучшения (опционально): -- [ ] Мигрировать остальные examples на sprinter-cc вместо ручных Makefile (косметика) -- [ ] Дальнейшая декомпозиция libc/*.c per-function (но текущая granularity уже даёт нужный размер — линкер пакетует .rel целиком, и для большинства файлов это одна функция) - -## Этап 9 — memory modes для sprinter-cc - -DSS выделяет страницы памяти по размеру приложения: < 16 KB → одна страница, в остальные окна подключается «страница #FF» (read=0xFF, write игнорится). Из-за этого CODE-в-W1 + DATA-в-W2 для маленькой программы молча ломается. См. [memory/sprinter_memory_modes.md](../../.claude/projects/-Volumes-SAM8-Projects-DIY-Z80-Sprinter-C-Compiler/memory/sprinter_memory_modes.md). - -- [x] **`tiny`**: всё (CODE+DATA+стек) в W2. Default. Verified hello/argv/conio/malloc/file/etc. -- [x] **`--memory MODE` флаг в sprinter-cc**: parser + per-mode дефолты CODE_LOC/DATA_LOC, override через явные `--code-loc`/`--data-loc`. tiny работает; small/big/huge компилируются с warning'ом (runtime не готов). Реализовано 2026-05-30. -- [x] **`--memory-manual SPEC`**: парсит `CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3`. Реализовано 2026-05-30. -- [x] **`small` runtime**: `runtime/crt0_small.s` использует ESTEX `$3D GETMEM` + `$3A SETWIN2` чтобы выделить и замапить W2-страницу ДО gsinit. **НЕ** BIOS `$C4` — стек на этом этапе в W1 (boot_stack в HOME), а BIOS требует стек в W2. После маппинга SP переключается на 0xBFFE, дальше стандартный flow. Реализовано 2026-05-30, verified hello.exe. -- [x] **`small` auto-detect для >16 KB программ**: `crt0_small.s` читает порт `0xC2` (текущая страница в W2 — не `0xA2`! это W1). Если 0xFF — выделяет page; иначе DSS уже сделала это (программа сама вылезла в W2). Один crt0 покрывает 0..30 KB. mkexe также разрешает HOME span W1+W2 (0x4000..0xBFFF). Verified hello: small (5 KB файл, SETWIN2 path) + 32 KB файл (auto-skip). Реализовано 2026-05-30. -- [x] **`big` runtime** (tiny + banked code в W1): параметризовали `crt0_banked.s` + `bank.s` через `.ifdef BANK_W1` — другой banking port (0xA2 vs 0xE2), другой load-addr (0x4000 vs 0xC000). sprinter-cc prepend'ит `BANK_W1 = 1` при `--memory big`, передаёт `mkexe -B 0x4000`. Пример `examples/banked_big/`. Реализовано 2026-05-30. -- [x] **`huge` runtime** (small + banked code в W3): merge W2-detect логики из `crt0_small.s` в `crt0_banked.s`. Существующий пример `examples/banked/` теперь использует MEMORY=huge. Реализовано 2026-05-30. -- [x] **`--debug` флаг**: prepend `DEBUG_RT = 1` в crt0 + `-DDEBUG_RT` в sdcc. Открывает symbol `_w2_self_allocated` (uint8_t) — runtime diagnostic кто аллоцировал W2. Реализовано 2026-05-30. - -### Дизайн-решения по libc и crt0 - -**Одна `sprinter.lib`** работает для всех memory mode — `.rel`-члены relocatable, SDLD делает dead-code elimination per-member (без графики не подтягивает `gfx_core.rel` и т.д.). Verified hello vs malloc_test через map-файлы. - -**`gfx.lib` отдельно — НЕ нужен**: dead-code elimination уже работает. - -**`libc_banked` (libc в bank вместо HOME)** — идея на потом, когда HOME (16 KB) забит user-кодом + libc в `huge` mode. Реализуется через `--codeseg BANK0` при компиляции libc; trade-off: trampoline ~30 циклов на каждый libc-вызов. Триггер: реальная программа упрётся в HOME budget. - -**HW-зависимые модули — `sprinter_home.lib` отдельно.** Часть libc физически не может быть забанкована в W3, потому что она РАБОТАЕТ с W3: -- `gfx_*` — пишет в видеопамять `0xC000+` после swap W3 на video page -- `bank_io` (mem_alloc_pages/bank_read/bank_write) — swap'ит W3 через `OUT (0xE2)` -- Будущие ISR — прерывание может прийти когда W3 на чём угодно - -В huge mode эти модули ДОЛЖНЫ остаться в HOME (W1). Когда будем делать `libc_banked`, **одновременно** выделяем `sprinter_home.lib` (HOME-only) из `sprinter.lib` (bankable). Финальная схема: -``` -sprinter_home.lib HOME-only: gfx, bank_io, ISR shims -sprinter.lib bankable: printf, malloc, string, conio, stdio, env, ... -sprinter_banked.lib тот же sprinter.lib но --codeseg BANK0 (для huge) -``` -Триггер: реализация `--memory huge` runtime. - -**crt0 — по одному на mode:** -- `crt0.s` — текущий, для **tiny/big**: SP=0xBFFE, парсит argv (W2-ресурс уже выделен DSS). -- `crt0_minimal.s` — текущий, для tiny без argv. -- `crt0_small.s` — **новый, step 3**: для **small/huge**, аллоцирует W2 через `mem_alloc_pages` ДО gsinit, маппит в порт `0xA2`, потом стандартный flow. -- `crt0_banked.s` — текущий, для **big**: trampoline-таблица для W3 банков, CODE в W2. -- `crt0_banked_small.s` — **новый**: huge = small (W2-alloc) + banked (W3 trampolines). - -sprinter-cc подбирает crt0 по `--memory` mode (сейчас `--crt0=` это override). -- [x] **Настраиваемый размер стека**: флаг `sprinter-cc --stack-size BYTES`. Wrapper генерирует `heap_top.s` с `___sdcc_heap_end = stack_top + 1 - stack_size`, отдельный .rel линкуется per-program. Default ≈1278 байт (heap_top=0xBB00) из `runtime/heap_top.s`. Реализовано 2026-05-30. - -Интерфейс: `sprinter-cc --memory [tiny|small|big|huge|manual] [--memory-manual SPEC] [--stack-size N] foo.c`. `--memory-manual` имеет смысл только с `--memory manual`. - -## Known issues / quirks - -- **ESTEX $46 ENV API**: ✅ работает. Док-ция в `DiskSyscalls.txt v1.6` ошибочно описывает return-status — A=0 это NOT FOUND, не FOUND. Зафиксировано в `memory/sprinter_platform.md`. - -## ОБЯЗАТЕЛЬНЫЕ ЗАДАЧИ ДЛЯ V2 (после релиза v1) - -### Turbo-C-style graphics API (BGI-like) — **MUST для v2** - -Расширить наш `gfx_*` API до уровня **Turbo-C ``** (BGI) для MS-DOS. -Программисты привыкшие к Turbo-C должны переносить графический код 1-в-1. - -**Что должно быть** (на основе Borland BGI): - -Setup/teardown: -- `initgraph()` / `closegraph()` — у нас сейчас `gfx_init`/`gfx_done`, добавить alias -- `getmaxx()` / `getmaxy()` — макрос на GFX_WIDTH-1 / GFX_HEIGHT-1 -- `cleardevice()` — alias to gfx_clear -- `getgraphmode()` / `setgraphmode()` — у нас get_videomode/set_videomode - -Color/palette: -- `setcolor(c)`, `getcolor()` — current draw color -- `setbkcolor(c)`, `getbkcolor()` — background color -- `setpalette(idx, c)` — палитра entry -- `getpalette(&info)` — read all palette - -Primitives (мы уже имеем эквиваленты — добавить BGI-имена как aliases): -- `putpixel(x, y, c)` — есть как gfx_putpixel -- `getpixel(x, y)` — нужно реализовать (RMW обратное — IN) -- `moveto(x, y)`, `lineto(x, y)`, `linerel(dx, dy)` — current point + line drawing -- `line(x1, y1, x2, y2)` — есть как gfx_line -- `rectangle(x1, y1, x2, y2)` — есть как gfx_rect (но другой API: x1,y1,x2,y2 vs x,y,w,h!) -- `bar(x1, y1, x2, y2)` — есть как gfx_fill_rect -- `bar3d(x1, y1, x2, y2, depth, topflag)` — новое: rect + 3d edges -- `circle(x, y, r)`, `arc(...)`, `ellipse(...)`, `pieslice(...)` — новые primitives -- `fillpoly()`, `drawpoly()` — полигоны -- `floodfill(x, y, border_color)` — заливка - -Text on graphics screen: -- `outtext(s)` / `outtextxy(x, y, s)` — есть как gfx_text (alias) -- `settextstyle(font, dir, size)` — multiple bitmap fonts -- `gettextsettings(&info)` -- `textwidth(s)` / `textheight(s)` — measure - -Image manipulation: -- `imagesize(x1, y1, x2, y2)` — bytes needed for getimage -- `getimage(x1, y1, x2, y2, buf)` — save rect to buffer -- `putimage(x, y, buf, op)` — paste back with COPY_PUT/XOR_PUT/AND_PUT/OR_PUT/NOT_PUT - -Clipping/viewport: -- `setviewport(x1, y1, x2, y2, clip)` — drawing clip rect -- `getviewsettings(&info)` -- `clearviewport()` -- `setactivepage(p)` / `setvisualpage(p)` — двойная буферизация (Sprinter имеет 2 screen) - -Line style: -- `setlinestyle(style, pattern, thickness)` — SOLID_LINE / DOTTED_LINE / etc. -- `getlinesettings(&info)` - -**Acceptance:** перенос типичной Turbo-C BGI программы (рисующей с использованием -moveto/lineto/circle/bar/setcolor) должен работать без существенных правок. - -**Notes:** -- BGI fonts (TRIPLEX/SANS_SERIF/GOTHIC) — у нас один BIOS font, остальные нужно - добавить (как bitmap data в lib) -- imagesize/getimage/putimage — самые востребованные для game/animation -- Active/visual page (двойная буферизация) — Sprinter поддерживает 2 graphics pages, - нужен API switching - -См. также `examples/` Turbo C 2.x BGIDEMO как reference что нужно. - -### IM2 Interrupt Handlers — **MUST для v2** - -User-задаваемые ISR через Z80 IM 2 mode. Нужны для: -- Timer ticks (50 Hz frame counter, плавная анимация) -- Music playback (AY, COVOX) -- Real-time games (input + game logic + render в interrupt-driven) -- Async keyboard / mouse handling - -**Status:** ОТЛОЖЕНО до v2. Полный research + design в `docs/im2_isr_design.md`. - -**Решение по архитектуре:** реализовать как отдельный memory mode `--memory im2` -(вместо того чтобы лезть во все существующие crt0). Detail'и в design-doc. - -**Резюме research'а** (полный текст в `docs/im2_isr_design.md`): -- Vector 0xFF — frame + keyboard + CBL. Disambiguation по портам 0x19 / 0xFE -- Mouse hardware-IRQ не приходит (на текущей плате) -- Vector table / ISR / stack ОБЯЗАНЫ быть в W2 (0x8000..0xBFFF) -- DSS имеет свой IM 2 handler — нужно chain'иться (иначе клавиатура / SYSTIME ломаются) - -### Прочие крупные пункты для v2 - -- [ ] **FILE API rewrite — buffered streams** — текущая реализация в - `libc/stdio/file.c` это provisional unbuffered shim (каждый fputc/fgetc - = один read/write syscall). Нужна полноценная buffered семантика - как в Solid-C: - - ```c - typedef struct { - uint flags; // +0..1 file status flags - int level; // +2..3 empty/fill level of buffer - char *curp; // +4..5 current active pointer - int fd; // +6..7 underlying low-level fd - char *buffer; // +8..9 data transfer buffer - char hold; // +10 ungetc byte if no buffer - short token; // +11..12 reserved - char dummy; // +13 reserved - } FILE; - ``` - - stdin/stdout/stderr — fd-маркеры `0 / -1 / -2`. Отрицательные для - stdout/stderr выбраны намеренно: ESTEX OPEN может вернуть positive - small fd (1, 2, …) для обычного файла → если бы stdout=1, реальный - fd=1 сталкивался бы с идентификатором. fd=0 для stdin безопасно - (ESTEX 0 не возвращает). Сами fd не передаются в syscall'ы — - диспетчеризация по флагам `_F_CONIN/_F_CONOUT`. - - Принтер-потоки (stdaux/stdprn) НЕ реализуем — Sprinter принтерной - API не имеет. - - Альтернатива — взять реализацию из third_party/solid-c (sources в - `SRC/CLIB/`); там есть готовый buffered FILE + fopen/fread/fwrite/ - fseek/setvbuf и т.д. Адаптировать к нашим open/read/write/lseek. - - При rewrite заодно решить deferred issues stdio-review: - - `fwrite` short-write должен ставить `_F_ERROR` - - `fgets(buf, 1, fp)` — стандарт говорит "empty string", мы вернули NULL - - `mode_to_flags` — break-out на '+' (cosmetic) - -- [ ] **Audio API** — AY-3-8910 + COVOX через прерывания (требует IM2) -- [ ] **ISA-8 slot support** — ZX-Bus карты (sound, network, etc.) — требует IM2 + чтения portов - -## Прочие задачи (v1 backlog, не блокирующие) - -- [x] **#9: text I/O split (Turbo-C style)** — stdio (puts/printf/putchar) теперь fast no-attr через PCHARS/PUTCHAR. conio (cputs/cprintf/putch) применяет attr через textcolor/textbackground/textattr. KEEP_EXIST_ATTR → conio fallback на fast path. Verified в hello.exe. См. `memory/text_output_api_split.md`. Реализовано 2026-05-31. -- [x] **Mouse API полный** (резидентный driver, RST 30h) — все 14 функций обёрнуты (init/show/hide/refresh/read/goto/bounds/text_cursor/load_cursor/get_cursor/get/set_sensitivity/video_mode_changed). См. `memory/mouse_api.md`. Verified в MAME 2026-05-31. Sensitivity = divider (меньше = быстрее). -- [ ] Interrupt handlers — IM 2 vector table в HOME для user ISR'ов -- [ ] Поддержка `restore SP on EXIT` (паттерн из z88dk +pps) — проверить нужно ли -- [ ] CI: автоматически запускать MAME с `-aviwrite` для screenshot-сравнения, чтобы тесты примеров проходили без человека - -## Идеи на потом - -- Поддержка `` (есть в SDCC stdlib — нужно протестировать что наш crt0 совместим) -- `` через ESTEX SYSTIME (`$21`) и CMOS BIOS-функции -- ZX Spectrum-совместимый режим как отдельный target (для портирования спектрумовских программ) -- Поддержка ZX-Bus карт (sound, network, etc.) — нужны драйверы -- Profile-guided optimization tools (hot/cold detection) для крупных программ - -## Linker duplicate-symbol warnings (благоприятные, отфильтрованы) - -Когда мы сознательно overrides'им SDCC z80.lib функции собственной версией в `sprinter.lib`, `sdldz80` пишет `?ASlink-Warning-Definition of public symbol '...' found more than once`. Линкер берёт первое найденное определение (наше), поэтому поведение корректное — warning только noise. - -Текущие overrides: -- `_puts` — наша версия через PCHARS+\r\n vs SDCC posix puts -- `___sdcc_heap` — наш heap в W2 vs SDCC's стандартный -- `_asctime`, `_localtime` (и возможно другие из time) — наш `posix_time.c` через ESTEX SYSTIME vs SDCC's `time.rel` который зависит от `_RtcRead` - -**Текущее решение:** `bin/sprinter-cc` отфильтровывает warning-блок (warning + 2 follow-up `Library:` строки) из вывода `sdcc`. Через `-v` (verbose) всё показывается. Реализовано через awk-pipe. - -**Возможные улучшения:** -- Перейти на explicit `--nostdlib` + ручной список нужных модулей из z80.lib (string, math, stdlib без override'нутых) — убрать ИСТОЧНИК warning'ов, не маскировать -- Или: переименовать наши `_puts` → `_puts_sprinter` + alias через linker flag (не уверен что SDCC поддерживает) -- Или: оставить как сейчас (рабочее и benign) — приоритет низкий - -## TODO: проверить на реальном железе - -- [ ] **Port_Y banking trick** (`docs/part2/SprinterGraphics programming.txt`): - доку утверждает что после `OUT (0x89), Y` адреса 0xC000+0x400*N в окне W3 - маппятся на строки Y..Y+15 (одно программирование → 16 строк). - Empirical 2026-06-01 в MAME 0.283 этот trick **не работает** — пиксели - по адресам выше 0xC000+row_width уходят в невидимую область. Канонический - `docs/samples/plasma2.asm` тоже не использует banking, переустанавливает - Port_Y per row. - План: - 1. Получить доступ к реальному Sprinter - 2. Запустить тест dual-write (`_gfx_putpixel_raw` + второй write в `0xD000+x`) - 3. Если на железе видны двойные линии → бага MAME, открыть issue с - минимальным репро - 4. Если на железе тоже одна линия → документ неверный, удалить упоминание - из доки и просто оставить текущую реализацию (Port_Y per pixel) - 5. Если banking работает на железе → внедрить кэширование Port_Y в - `_gfx_putpixel_raw` (sentinel out-of-range, см. memory/gfx_port_y_banking.md) - - Связанный выигрыш для Bresenham (60-pixel диагональ) — около 8× меньше - OUT (0x89) операций, для `gfx_fill_rect 320x256` — 16× меньше. Не блокирует - release v1. - -## GFX: расширения по `docs/part2/accelerator_doc.txt` - -После прочтения детального accelerator doc выявлены незакрытые направления. -Сейчас в коде используется только горизонтальный/вертикальный Fill mode. - -### Quick wins для текущих primitives - -- [ ] **Заменить SMC на `LD A, (var)` для block-size**. Документ явно - разрешает `LD A, (HL)`, `LD A, (BC)`, `LD A, (DE)` (но не `LD A, r`). - Это уберёт SMC complexity в `gfx_lines.c:hfill_chunk/vfill_chunk` и - `gfx_16.c:g16_hfill_chunk`. Запрещено только register-to-register. -- [ ] **Кэширование block-size**. Документ показывает что accel запоминает - block size между bursts (см. `Horizontal_Line_Fill`: устанавливают - size + `LD B,B` отключение, потом включают Fill mode и используют - сохранённый size). Для `gfx_fill_rect` с 100 одинаковыми - строками — установить size 1 раз, а не 100. - -### Bank-prefix modes (port 0xE2 bits) - -Документ показывает три варианта банка видеостраницы помимо стандартного 0x50: - -| Bank byte | Effect | -|---|---| -| 0x50 | Normal write — пишется в shadow + видимый | -| 0x54 | "no copy in main shadow RAM" | -| 0x58 | **"FF is transparent"** — байт 0xFF при write оставляет background | -| 0x5C | both | - -Bank 0x58 объясняет почему mouse cursor рисуется с 0xFF-прозрачностью. -Это путь к **sprite-blending через accel block copy**: - -- [ ] **`gfx_set_bank_transparent(on)`** или флаг в `gfx_set_bank` для - выбора 0x50/0x58 при отрисовке sprite'ов -- [ ] Использовать в новом `gfx_blit()` чтобы по факту получать - transparent sprites через accel-копию - -### Block copy mode (sprite blit'ы) - -`LD L,L` (horizontal) и `LD A,A` (vertical) — режим копирования блока через -256-байтную accel memory. Это базис для blit'ов. - -- [ ] **`gfx_blit(src_data, x, y, w, h)`** — копирование sprite'а - (произвольный размер, через accel) -- [ ] **`gfx_blit_transparent(src, x, y, w, h)`** — с использованием bank 0x58 - -См. `Draw_Restangle_Data` в accelerator_doc.txt как референс. - -### AND / OR / XOR operations через accel - -Документ показывает что accel поддерживает логические операции с блоками -данных. Применения: -- XOR — инверсия области (выделение selection в UI) -- OR / AND — masking, alpha-style blending -- См. пример в accelerator_doc.txt: "256 bytes block coding via XOR" - -- [ ] **`gfx_xor_rect`** / **`gfx_or_rect`** / **`gfx_and_rect`** — - примитивы логических операций над прямоугольником -- [ ] **`gfx_invert_rect(x, y, w, h)`** — alias на xor с 0xFF - -### Bitmap fonts разных размеров - -Сейчас `gfx_text` / `gfx_putchar` хардкоженно работают с 8×8 шрифтом -(BIOS WIN_GET_ZG возвращает 256×8 байт). Для будущих UI / титульников -нужны: -- [ ] **`gfx_set_font_size(w, h)`** — переключить ширину/высоту glyph'а -- [ ] **`gfx_set_font_data(ptr, w, h, advance)`** — заменить указатель - на пользовательский шрифт + размеры -- [ ] Поддержка **proportional** (advance != w) шрифтов — добавить - array advance[256] на ширину каждого glyph'а -- [ ] **Big-font режимы**: 8×16, 16×16, 16×8 (для титульников) -- [ ] Возможно отдельный API `gfx_text_ex(x, y, str, font_id)` где - font_id выбирает один из загруженных шрифтов -- [ ] **Anti-alias 2-bit шрифты** (бит фон / бит граница / 2-бит alpha?) - — far future, для smooth UI - -## Финальный этап оптимизаций (не сейчас) - -- **`gfx_line` через accel для пологих диагоналей** — Bresenham для линии с |dy| << |dx| (или наоборот) выдаёт длинные runs одинакового Y (или X): пиксель, пиксель, пиксель, шаг Y, пиксель, пиксель... Каждый такой run — это готовый аргумент для `gfx_hline` (или `vline`). - План исследования: посчитать длину runs как функцию от наклона; решить минимальный run length, при котором выгоднее accel hline чем N×putpixel (overhead accel ~20µs, putpixel ~5µs — accel выгоднее при run ≥ 4-5 px); для крутых диагоналей (dx ≈ dy) оставить Bresenham, для пологих — run-length-based fill. - Сейчас `gfx_line` orthogonal cases уже через accel — оптимизировать только косые. - -- **`gfx_fill_rect` с одним W3-swap на всю операцию** — сейчас каждый внутренний `gfx_hline`/`gfx_vline` делает свой DI/save-W3/restore-W3/EI. Можно сделать internal `_fill_rect_inner` который держит W3 замапленным и DI весь цикл; ~20µs × количество строк/столбцов экономии. Применимо ко всем композитным примитивам. diff --git a/examples/mdview2/Makefile b/examples/mdview2/Makefile deleted file mode 100644 index 9933047..0000000 --- a/examples/mdview2/Makefile +++ /dev/null @@ -1,56 +0,0 @@ -# Build mdview2.exe — Markdown viewer for Sprinter (render-cache version). -# -# Фаза 0: скаффолдинг — копия mdview.c как baseline, без изменений -# логики. План — docs/mdview2-plan.md. -# -# small memory mode: code in W1, data/stack/heap in W2 (32 KB total). -# W3 stays free for the file buffer (EMM-mapped). - -EXAMPLE := mdview2 -EXTRA_SRCS := mdview2_raw.c mdview2_hex.c mdview2_help.c mdview2_table.c mdview2_enc.c mdview2_md.c mdview2_status.c mdview2_index.c -MEMORY := small -# --max-allocs 100000: агрессивнее аллокация регистров SDCC — минус ~850 байт -# кода на всём приложении (замер 2026-07-04) ценой более долгой компиляции. -EXTRA_FLAGS := --max-allocs 100000 - -include $(CURDIR)/../sdk.mk - -# ------------------------------------------------------------------ -# Образ дискеты: mdview2.exe + документация. Каждый документ — в СВОЕЙ -# кодировке, чтобы дискета заодно покрывала все пути автодетекта: -# README.MD — документация, ОРИГИНАЛЬНЫЙ UTF-8 (как в репозитории); -# CHANGES.MD — история версий, CP866 (хранится так, кладётся как есть; -# имя укорочено: "CHANGELOG" не влезает в FAT 8.3); -# DEMO.MD — демонстрация разметки, конвертируется UTF-8 → CP1251. -# -# iconv -c возвращает ненулевой код, если хоть один символ отброшен -# (даже с -c) — это ОЖИДАЕМО при потере символов без аналога в целевой -# кодировке, не ошибка конвертации; вывод при этом корректно записан. -# Поэтому код возврата iconv игнорируется (|| true). -# -# Тестовые файлы лимита 256 КБ лежат в testfiles/ (генератор gen_testfiles.py); -# на диск НЕ кладутся — при необходимости добавить вручную в список ниже. -# ------------------------------------------------------------------ -DISK_TMP := .disk_tmp -DEMO_DISK := $(DISK_TMP)/DEMO.MD -CHLOG_DISK := $(DISK_TMP)/CHANGES.MD - -$(DISK_TMP): - mkdir -p $@ - -$(CHLOG_DISK): CHANGELOG.md | $(DISK_TMP) - cp CHANGELOG.md $@ - -$(DEMO_DISK): DEMO.MD | $(DISK_TMP) - iconv -c -f UTF-8 -t CP1251 DEMO.MD > $@ || true - -floppy: $(EXAMPLE).exe $(DEMO_DISK) $(CHLOG_DISK) - $(PYTHON) $(MAKE_DISK) "$(FLOPPY_IMG)" $(EXAMPLE).exe README.md $(DEMO_DISK) $(CHLOG_DISK) - @echo - @echo "Floppy ready: $(FLOPPY_IMG)" - @echo "Run: make run MAME_HOME=/путь/к/MAME/runtime" - -clean: - rm -rf .sprinter-cc-* $(EXAMPLE).exe $(DISK_TMP) - -.PHONY: all clean floppy run diff --git a/examples/mdview2/README.md b/examples/mdview2/README.md deleted file mode 100644 index 0cc3c6a..0000000 --- a/examples/mdview2/README.md +++ /dev/null @@ -1,153 +0,0 @@ -# MDView v1.0 (b3) — просмотрщик Markdown для Sprinter - -**MDView** — программа просмотра документов Markdown для компьютера -**Sprinter-2000** (Z80, ОС ESTEX). Файл загружается в расширенную память (EMM) -и один раз «прогоняется» через парсер: готовые к показу строки (пары -символ+атрибут) складываются в **рендер-кэш** в EMM, после чего прокрутка в -любую сторону — это просто копирование готовых строк на экран, без повторного -парсинга. Даже на файлах в сотни килобайт листание остаётся мгновенным. - -Текстовый режим 80×32, цветное оформление элементов разметки, три режима -просмотра (MD / RAW / HEX) и четыре кодировки с автоопределением. - ---- - -## Возможности - -* **Markdown-рендеринг** с цветовым оформлением: - * заголовки `#`…`######` (H5/H6 отображаются как H4); - * **жирный** (`**текст**`), *курсив* (`*текст*`), подчёркнутый (`_текст_`), - ~~зачёркнутый~~ (`~~текст~~`), `встроенный код` (`` `текст` ``); - * экранирование `\*`, `\_`, `` \` `` и любой ASCII-пунктуации; - * чекбоксы `[x]` / `[ ]` в списках; - * ненумерованные (`-`, `*`, `+`) и нумерованные (`1.`, `1)`) списки - с базовой вложенностью по отступам; - * цитаты `>` (склейка многострочных, маркер │ на переносах); - * fenced-блоки кода ` ``` ` (без переносов, горизонтальный скролл); - * таблицы `| … | … |` — рисуются псевдографической рамкой, ширины колонок - вычисляются по содержимому (до 16 колонок); - * горизонтальные разделители `---` / `***` / `___`; - * жёсткие переносы (два пробела или `\` в конце строки); - * мягкая склейка абзацев с переносом по словам под ширину экрана. -* **Кодировки: CP866, CP1251, KOI8-R, UTF-8.** - * автоопределение при открытии (BOM → UTF-8; валидность multibyte-структуры; - частотный анализ ходовых русских букв для 8-битных); - * переключение по кругу клавишей **F8** в любой момент; - * 8-битные кодировки отличаются только перекодировкой глифов на отрисовке — - переключение мгновенно; - * UTF-8 декодируется в CP866 в **отдельный набор** (файл + индекс + кэш); - второй набор готовится **в фоне**, пока вы читаете документ, — обычно - к первому нажатию F8 он уже построен и переключение мгновенно, - с сохранением позиции. Если фон не успел, F8 докручивает начатую - сборку (со спиннером), а не начинает её заново. -* **Три режима просмотра:** - * **MD** — форматированный Markdown (по умолчанию); - * **RAW** (**F2**) — исходный текст без разметки: перенос строк кратно 80 - (**F3** — режим панорамы с горизонтальным скроллом); - * **HEX** (**F4**) — дамп *оригинального* файла: - `0x012340 │ 16 байт hex │ 16 печатных символов`. Печатная колонка - интерпретируется текущей кодировкой; для UTF-8 глиф ставится на позиции - лид-байта, continuation-байты показываются точкой. -* **Единая позиция** при любых переключениях: MD ↔ RAW ↔ HEX и смена - кодировки сохраняют текущее место в документе (между наборами разного - размера — пропорционально, с точностью до строки). -* **Прогрессивная загрузка**: первый экран показывается сразу, индексация - продолжается в фоне; по готовой части документа уже можно листать, - **Esc**/**F10** прерывают загрузку. -* **Фоновая работа незаметна**: второй набор кодировки строится только в - паузах между клавишами (после нажатия выдерживается пауза), поэтому - скролл — в том числе с автоповтором — не теряет плавности. - ---- - -## Запуск - -``` -MDVIEW2.EXE <файл.md> -``` - -Без аргумента открывается `README.MD` из текущего каталога. - -## Клавиши - -| Клавиша | Действие | -|--------------|-------------------------------------------------------------| -| ↑ / ↓ | прокрутка на одну строку | -| PgUp / PgDn | прокрутка на экран (30 строк) | -| Home / End | в начало / в конец документа | -| ← / → | горизонтальный сдвиг: код/таблицы в MD, панорама в RAW | -| F1 | справка | -| F2 | RAW-режим ↔ MD | -| F3 | в RAW: перенос строк ↔ панорама | -| F4 | HEX-режим ↔ прежний вид | -| F8 | кодировка: CP866 → CP1251 → KOI8-R → UTF-8 → … | -| Esc / F10 | выход (во время загрузки — прервать её) | - -Статус-бар (верхняя строка): имя файла, кодировка, диапазон видимых строк -и процент прокрутки. Нижняя строка — меню доступных F-клавиш. - ---- - -## Ограничения - -| Параметр | Значение | -|---------------------------------|-------------------------------------------| -| Размер файла | до 256 КБ (больший — обрезается с предупреждением) | -| Логических строк (после переносов) | до 18 432 | -| Длина строки в рендер-кэше | 255 ячеек (RAW/HEX ограничения не имеют) | -| Колонок в таблице | до 16 | -| Шаг табуляции | 4 | - -При исчерпании любого лимита документ завершается строкой-сообщением -с указанием причины обрыва; всё, что вошло, доступно для просмотра. - -## Требования - -* Sprinter-2000 с ОС ESTEX; -* расширенная память (EMM): в худшем случае (файл 256 КБ + оба набора - кодировок) — до ~150 страниц по 16 КБ (~2,4 МБ). Для типичных файлов - в десятки килобайт достаточно нескольких десятков страниц. - -Код, данные, стек и куча программы занимают окна W1+W2 (32 КБ, режим -памяти `small`); окно W3 используется только для доступа к EMM-страницам. - ---- - -## Сборка - -Требуется тулчейн этого репозитория (обёртка `sprinter-cc` над SDCC 4.5). -Из каталога `examples/mdview2`: - -``` -make # собрать mdview2.exe -make floppy # собрать и упаковать дискету для MAME (mc.img) -make run # floppy + автоматический прогон в MAME (скриншоты) -``` - -На дискету кладутся: `MDVIEW2.EXE` и три документа, каждый в своей кодировке -(заодно покрывают все пути автодетекта): `README.MD` — этот файл, как есть -(UTF-8); `DEMO.MD` — демонстрация всех элементов разметки (CP1251); -`CHANGES.MD` — история версий (CP866). - -## Структура исходников - -| Файл | Назначение | -|------------------|--------------------------------------------------------------| -| `mdview2.c` | ядро: EMM-аллокации, рендер-кэш, наборы кодировок, загрузка файла, главный цикл | -| `mdview2_index.c`| парсер/индексатор Markdown — единственный проход по файлу | -| `mdview2_md.c` | MD-вид: отрисовка из кэша, прокрутка | -| `mdview2_raw.c` | RAW-вид (F2/F3) | -| `mdview2_hex.c` | HEX-вид (F4) | -| `mdview2_enc.c` | кодировки: детект, ремап-таблицы, конвертер UTF-8 → CP866 | -| `mdview2_table.c`| отрисовка таблиц | -| `mdview2_status.c`| статус-бар, меню, спиннер | -| `mdview2_help.c` | справка (F1) | -| `mdview2_conf.h` | конфигурация: `WITH_RAW` / `WITH_HEX` (модули отключаемы) | -| `mdview2.h` | общие константы, атрибуты, межмодульный API | - -Подробности архитектуры — в `docs/mdview2-plan.md`. - -## Лицензия и авторы - -© 2026 Петров А.Г. Часть проекта Sprinter C Compiler -(см. LICENSE в корне репозитория). diff --git a/examples/mdview2/READMEBG.MD b/examples/mdview2/READMEBG.MD deleted file mode 100644 index 4952b65..0000000 --- a/examples/mdview2/READMEBG.MD +++ /dev/null @@ -1,799 +0,0 @@ -# MDView — Просмотрщик Markdown для Sprinter - -**MDView** — программа для просмотра документов в формате *Markdown* на компьютере -Sprinter (процессор Z80). Документ хранится в отдельном W3 окне и не занимает -основную RAM программы. - -## Возможности - -- Документы до **128 КБ** (8 страниц EMM по 16 КБ каждая) -- До **16 384** экранных строк в индексе -- Автоматический перенос слов по ширине экрана (80 столбцов) -- Горизонтальный сдвиг для широких строк (блоки кода, таблицы) -- Статус-бар: имя файла, диапазон строк, процент прокрутки -- Спиннер в строке состояния во время загрузки и индексации -- Поддержка «мягкого» склеивания строк в абзацах и цитатах - -## Запуск - -``` -mdview [имя_файла.md] -``` - -Если имя файла не задано, загружается `README.MD`. - -## Управление - -``` -Клавиша Действие -───────────── ──────────────────────────────────────── -Up Down Прокрутка на одну строку вверх / вниз -PgUp PgDn Прокрутка на страницу (30 строк) -Home Начало документа -End Конец документа -Left Right Горизонтальный сдвиг (только nowrap-строки) -F1 Окно справки -F8 Кодировка: CP866 → CP1251 → KOI8-R → UTF-8 → ... -F10 / Esc Выход из программы -``` - -Кодировка определяется автоматически при открытии (BOM + эвристика по -первым 4 КБ); `F8` переключает её вручную, если детекция ошиблась. -8-битные кодировки (CP866/CP1251/KOI8-R) переключаются мгновенно (ремап на -отрисовке). Второй набор индекс/кэша (например UTF-8) строится лениво — при -первом переключении в него (короткая пауза со спиннером), дальше мгновенно. - -## Синтаксис Markdown - -### Заголовки - -Поддерживаются уровни H1–H4. Уровни H5 и H6 отображаются как H4. - - # Заголовок первого уровня - ## Заголовок второго уровня - ### Заголовок третьего уровня - #### Заголовок четвёртого уровня - -### Текстовое форматирование - -**Жирный текст** выделяется двойными звёздочками: `**текст**` - -*Курсив* выделяется одиночными звёздочками `*текст*` или знаком подчёркивания `_текст_` - -`Встроенный код` обозначается обратными кавычками - -~~Зачёркнутый текст~~ — двойные тильды: `~~текст~~` - -### Ненумерованный список - -Маркеры `-`, `*` или `+`: - -- Первый пункт списка -- Второй пункт списка -- Третий пункт с достаточно длинным текстом, который при необходимости - будет перенесён на следующую строку с сохранением отступа - -### Нумерованный список - -1. Первый элемент -2. Второй элемент -3. Третий элемент - -### Цитата - -> Блок цитаты начинается с символа `>`. Несколько последовательных -> строк одной цитаты склеиваются в единый абзац с автоматическим -> переносом слов. - -### Блок кода (verbatim) - -Блок кода заключается в тройные обратные кавычки. Внутри блока -текст отображается «как есть» без разбора Markdown: - -``` -#include -#include - -int main(void) { - puts("Hello, Sprinter!"); - return 0; -} -``` - -### Горизонтальная линия - -Три или более символов `---`, `***` или `___` на отдельной строке: - ---- - -## Технические характеристики - -- **Платформа:** Sprinter, процессор Z80 @ 21 МГц -- **Кодировки:** CP866 / CP1251 / KOI8-R / UTF-8 (автоопределение, `F8`) -- **Максимальный размер файла:** 128 КБ -- **Максимальное число строк в индексе:** 16 384 -- **Режим памяти:** small - - Код программы, cтек, данные, куча — окнa W1-W2 (32 КБ, адреса 0x4000–0xBFFF). - - Буфер файла — страницы EMM, отображаемые в W3 (0xC000–0xFFFF) - -## TODO - -1. **Увеличение размера документов.** Снять лимит 128 КБ: Достаточно - разрешить работать с большим кол-вом страниц памяти, пока оттестированно - на работе с 8-мю страницами по 16Кб. - -Сделано: форматированные таблицы с рамкой; поддержка кодировок -CP866 / CP1251 / KOI8-R / UTF-8 с автоопределением и переключением по `F8` -(второй набор строится лениво, по первому переключению). - -4. **Ускорение рендеринга.** Кэш строк экрана. Оптимизация цикла вывода - символов через BIOS WRCHAR (пакетный вывод, DMA). - ---- - -*MDView v0.2 · (c) 2026 Петров А.Г.* - - ---- - -# Sprinter C Compiler — v1.0 - -C toolchain for **Sprinter** — the Z80-based home computer by Peters Plus, running -ESTEX DSS. Host: macOS / Linux. Target: `.EXE` files in SprintEXE format. - -Built on top of **SDCC 4.5** (vendored in `third_party/sdcc/`). This repository adds -everything Sprinter-specific: crt0, linker integration, libc wrappers over ESTEX, -banked-call trampolines, graphics & accelerator API, mouse driver wrappers, and the -`mkexe` utility for producing SprintEXE images. - -## What you get - -* **`bin/sprinter-cc`** — one-line driver: `sprinter-cc -o foo.exe foo.c` -* **Memory modes**: `tiny`, `small`, `big`, `huge`, `manual` — see below. -* **stdio + conio**: printf, puts, putchar, getchar, fopen/fread/..., cprintf, cputs, putch, textcolor/textbackground/textattr, gotoxy, kbhit/getch. -* **Graphics**: 320×256×256 and 640×256×16 modes, accelerator-backed primitives (hline / vline / rect / fill_rect / line via Bresenham, plus clear), bitmap-font text in both modes via BIOS character generator. -* **File I/O**: POSIX (`open`/`read`/`write`/`close`/`lseek`/`unlink`/`creat`), FILE\* streams (`fopen`/`fgets`/`fwrite`/...), directory listing (`ffirst`/`fnext`), `chdir`/`getcwd`/`mkdir`/`rmdir`, `stat`/`fstat`. -* **Memory**: 32 KB heap (W2-resident), banking-aware page allocator (`mem_alloc_pages`/`bank_read`/`bank_write`), explicit memory modes for sub-16 KB programs. -* **Mouse**: full Sprinter driver wrapper (14 functions including custom cursor bitmaps). -* **Environment**: `getenv`/`putenv`/`sysenv` over ESTEX `$46`. -* **Time**: `getdatetime`/`setdatetime` + POSIX `time`/`localtime`/`mktime`/`asctime`/`ctime`. -* **Misc**: `errno`/`strerror`/`perror`, `atexit`, `setjmp`/`longjmp`, `sleep`, full argv parsing in crt0. - -## Quick start - -```sh -git clone sprinter-c -cd sprinter-c -make sdcc # one-time: fetch SDCC 4.5 binary (~25 MB) -make all # build mkexe + libsprinter.lib + 27 examples -make floppy # pack everything into mame/v306/IMG/mc.img -cd mame/v306 && ./run_mame.sh # boot Sprinter in MAME -``` - -Compile a single program: - -```sh -cat > hello.c < -int main(void) { puts("Hello, Sprinter!"); return 0; } -EOF - -bin/sprinter-cc -o hello.exe hello.c -``` - -That's it — `hello.exe` is now a valid SprintEXE you can `RUN HELLO` from the ESTEX shell. - -## Memory modes - -Sprinter's address space is four 16 KB windows (W0 / W1 / W2 / W3). DSS allocates -pages by program size — small programs get only one page. Pick a memory mode based -on what your program needs: - -| Mode | Code lives in | Banking | Use when | Note | -|---|---|---|---|---| -| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | | -| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | | -| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | | -| `huge` | W1-W2 + W3 banking | yes (W3) | small + extra code modules | | -| `manual` | user-specified | optional | special layouts | Not implemented | - -```sh -sprinter-cc --memory small -o big.exe bigprog.c -sprinter-cc --memory huge -o app.exe main.c --bank 1=engine.c --bank 2=ai.c -``` - -Banked functions are declared with `__banked`: - -```c -void engine_tick(int dt) __banked; // lives in BANK1, automatically swapped -``` - -## Examples (27 total) - -| Example | What it demonstrates | -|---|---| -| `hello` | Hello world with stdio + conio Turbo-C-style colors | -| `argv` | argv parsing in crt0 | -| `cat` | File I/O — read & print TEST.TXT | -| `seek` | 32-bit lseek over a 100 KB file | -| `ls` | Directory listing via ffirst/fnext | -| `filetest` | FILE\* streams (fopen/fread/...) | -| `stattest` | `stat`/`fstat` on files and directories | -| `errno` | errno / strerror / perror | -| `mem_test` | Page allocator + bank\_read/bank\_write | -| `malloc` | Heap stress test (200+ allocations) | -| `banked` | Banked code in W3 (huge mode) | -| `bankedbg` | Banked code in W1 (big mode) | -| `banklocl` | Bank-local static data and BSS | -| `mouse` | Mouse driver in text mode | -| `gfx_mous` | Mouse with custom bitmap cursor in graphics mode | -| `gfx_demo` | 320×256×256 lines / rects / accelerator | -| `gfx_d16` | 640×256×16 same primitives | -| `gfx_text` | Bitmap-font text on graphics screen | -| `timedir` | Date/time + directory listing | -| `ptime` | POSIX time API | -| `openenv` | open() flags + environment vars | -| `conio` | conio API smoke test | -| `attrprob` | Probe Sprinter text attribute byte layout | -| `strtest` | string.h test (from SDCC's z80.lib) | -| `stdlib` | stdlib.h test (qsort / rand / strtol / etc.) | -| `assrtest` | assert() | -| `rt_test` | Runtime helpers (sleep, setjmp, atexit) | - -## Headers - -Standard: -* `` — puts / printf / FILE\* + Sprinter-specific dec/hex helpers -* `` — atoi / atof / malloc / qsort / ... (from SDCC z80.lib) -* `` / `` / `` — from SDCC z80.lib -* `` — read / write / close / lseek / unlink -* `` — open / creat + O\_RDONLY / O\_CREAT / ... -* `` — errno + error names + strerror -* `` — stat / fstat -* `` / `` — from SDCC - -Sprinter-specific: -* `` — putch / cputs / cprintf, textcolor / textbackground / textattr, kbhit / getch, clrscr, gotoxy, wherex/y -* `` — gfx_init/done, palette, putpixel, hline/vline/rect/fill_rect/line, text — for both 320 and 640 modes (gfx_\*16 variants) -* `` — full 14-function driver wrapper + mouse_cursor_t with bitmap support -* `` — chdir / getcwd / mkdir / rmdir / ffirst / fnext / ffblk -* `` — getdatetime / setdatetime + POSIX time / localtime / etc. -* `` — raw ports, ESTEX/BIOS function numbers, env API -* `` — exit / \_exit / atexit -* `` — mem\_alloc\_pages / mem\_free\_block / bank\_read / bank\_write -* `` — Solid-C compatibility layer (aliases + BOOL/WORD/uint types) - -## Toolchain commands - -```sh -make all # build mkexe + lib + every example -make floppy # repack mame/v306/IMG/mc.img with all .exe files -make check # 17 mkexe unit-tests -make clean # remove all build artefacts -make sdcc # one-time: fetch SDCC 4.5 binary -``` - -### sprinter-cc options - -``` -sprinter-cc -o foo.exe foo.c [more.c ...] [options] - - --memory MODE tiny | small | big | huge | manual (default: tiny) - --memory-manual SPEC explicit placement (CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3) - --stack-size N bytes reserved for the stack (default ~1278) - --crt0=TYPE default | minimal | banked | small - --bank N=FILE.c compile FILE.c into bank N (repeatable, max 15) - --debug enable runtime diagnostics (defines DEBUG_RT) - -I PATH extra include path - -L 0xADDR / -E / -S override load / entry / stack addresses - -Wl FLAG pass FLAG to sdldz80 - --mkexe FLAG pass FLAG to mkexe (e.g. --mkexe -p --mkexe 0 for bank padding) - -v verbose -``` - -## Status - -What works in v1.0: -* Compile / link / pack to SprintEXE — verified on all 27 examples -* Four memory modes (tiny / small / big / huge) -* Graphics (both modes) with accelerator -* Mouse (text + graphics cursor) -* File I/O, directories, environment, time -* All headers listed above - -Deferred to v2.0 (see `docs/TODO.md`): -* **Turbo-C-style BGI graphics API** — `initgraph` / `setcolor` / `circle` / - `getimage` / `putimage` / etc. on top of our `gfx_*` primitives -* Remaining Solid-C compatibility gaps (Phase 2/3) — see `docs/solid_c_compatibility.md` -* Manual memory mode -* Rewrite FILE\* stream API (current implementation is very primitive and doesn't use buffers) - -Deferred to v3.0: -* **IM2 interrupt handlers** — research complete (`docs/im2_isr_design.md`), - implementation scheduled for v3 -* **Audio API** (AY-3-8910 + COVOX) — requires IM2 -* **ISA-8 slot drivers** — requires IM2 (???) - -## Documentation - -* `docs/TODO.md` — roadmap and open work items -* `docs/solid_c_compatibility.md` — gap analysis vs Solid-C 2004 -* `docs/im2_isr_design.md` — interrupt handler design (v2) -* `docs/converted/` — source documentation (ESTEX, BIOS, architecture) - converted to plain text for `grep` -* `docs/reference/`, `docs/samples/`, `docs/memory management/` — original - Russian docs and code samples from Peters Plus - -## Repository layout - -``` -bin/sprinter-cc one-line compiler driver (bash) -toolchain/mkexe/ host-side tool: .ihx -> .exe SprintEXE -toolchain/check_banks.py post-link bank size enforcer -runtime/ crt0 variants (default, minimal, small, banked) - bank trampolines, heap, heap_top -libc/include/ headers -libc/io|stdio|mem|gfx/ C and asm sources for libsprinter.lib -lib/ Makefile that archives libsprinter.lib via sdar -examples/ 27 example programs -mame/v306/ MAME binary + Sprinter ROM/HDD images + floppy script -third_party/sdcc/ vendored SDCC 4.5 (fetched via `make sdcc`) -third_party/solid-c/ reference: original Sprinter native C (for compat target) -docs/ documentation -``` - -## License - -This repository contains: -* Original code in `bin/`, `toolchain/`, `runtime/`, `libc/`, `lib/`, `examples/` — - MIT-licensed. -* `third_party/sdcc/` — SDCC 4.5 under GPLv2 with linking exception - (see `third_party/sdcc/COPYING.txt`) -* `third_party/solid-c/` — original Sprinter Solid C, used only as a reference - -## Credits - -* **Sprinter / Peters Plus** — Иван Мак, Дмитрий Паринов and the original team -* **SDCC** — for the underlying Z80 compiler -* **MAME** — for the Sprinter Sp2000 emulation - ---- - -For questions / patches: see CONTRIBUTING.md (TBD) or open an issue. - ---- - -# TODO / Roadmap - -Открытые задачи в порядке убывания приоритета. По мере появления реальных программ — приоритеты будут смещаться. - -## Этап 5 — malloc / free + banking-aware page allocator ✅ ГОТОВО - -- [x] SDCC's `malloc`/`free` + наш `runtime/heap.s` (полностью заменяет library heap.rel, 14000-байтный heap в окне 2) -- [x] `libc/mem/mem_alloc.c` — page allocator: `mem_alloc_pages`/`mem_free_block`/`mem_get_page`/`mem_info` через ESTEX `$3C/$3D/$3E` + BIOS `$C4` -- [x] `libc/mem/bank_io.c` — HOME-резидентные `bank_read`/`bank_write`/`bank_load_byte`/`bank_store_byte` со свопом W3 внутри -- [x] `examples/malloc_test/` — проверка SDCC's malloc (~210 64-байтных allocations через всю heap) -- [x] `examples/mem_test/` — проверка page allocator: 3 страницы, разные паттерны через bank_write, верификация через bank_read - -## Этап 6 — argv parsing + sprinter-cc wrapper ✅ ГОТОВО - -- [x] crt0 парсит ESTEX command-line из IX-prefix (inline asm в `runtime/crt0.s`) -- [x] Strip leading CP/M-style space (DSS quirk) -- [x] Передача `argc`/`argv` в main() через HL/DE (SDCC __sdcccall(1) ABI) -- [x] argv[0] = basename .EXE через ESTEX APPINFO ($47 subfn 2) -- [x] `runtime/crt0_minimal.s` — opt-out для очень маленьких программ -- [x] `runtime/crt0_banked.s` — теперь тоже парсит argv (parse_argv + get_progname скопированы из crt0.s; будет factored в argv.s когда возьмёмся за libsprinter.lib) -- [x] Bash-обёртка `bin/sprinter-cc`: `sprinter-cc -o foo.exe foo.c` одной строкой -- [x] Поддержка опций: `--memory`, `--memory-manual`, `--stack-size`, `--crt0=`, `--bank N=FILE.c`, `--debug`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` - -## Этап 8 — графика (320×256×256 + 640×256×16 + accel + bitmap font) ✅ ГОТОВО - -- [x] **8a** Graphics core: `gfx_init`/`gfx_done`/`gfx_clear`/`gfx_putpixel`/`gfx_pal_load`/`gfx_pal_set` (libc/gfx/gfx_core.c). Палитра через BIOS PIC_SET_PAL ($A4). Verified 320×256×256. -- [x] **8b** Линии/прямоугольники/fill через accelerator (libc/gfx/gfx_lines.c): `gfx_hline`/`gfx_vline` через accel Fill (LD C,C / LD E,E + SMC block-size), `gfx_rect`/`gfx_fill_rect` с heuristic выбором ориентации (h/v bursts count), `gfx_line` с Bresenham для диагоналей. `gfx_clear` тоже переписан на column-major accel (~4× быстрее). -- [x] **8c** 640×256×16 mode (libc/gfx/gfx_16.c): `gfx_*16` API, HIGH nibble = LEFT pixel (документация misleading), per-row RMW для vline (один байт = 2 горизонтальных пикселя). -- [x] **8d** Bitmap font + gfx_text (libc/gfx/gfx_text.c): шрифт через BIOS WIN_GET_ZG ($B8), interleaved layout `font[row*256+char]`, `gfx_text`/`gfx_putchar` для 320 mode, `gfx_text16`/`gfx_putchar16` для 640 mode с pair-table lookup. - -См. memory/sprinter_graphics.md, sprinter_accelerator.md, sprinter_graphics_16.md, sprinter_font_format.md. - -Открытые мелочи (не блокируют): -- [ ] Шрифт-quad для 640: per-cell палитра (mode 0x82 разрешает 1 из 4 палитр per 16×8 cell) — через прямой доступ к area-описания экрана 0x0300..0x039F - -## Auto-banking (см. `memory/banking_roadmap.md` для деталей) - -Phase 1 — file-level bin-packing — реализовывать когда проект перерастёт ~30 KB кода. - -- [ ] `toolchain/auto_bank.py`: - - Парсит размеры из `.rel`-файлов (или из .map после dry-run link'а) - - First-fit-decreasing bin-packing - - Уважает `#pragma codeseg BANKn` как manual override - - Перелинковывает с новыми `-Wl-b_BANKn=...` параметрами - - Печатает план распределения - -Phase 2-5: incremental rebalance, declarative `banks.toml`, function-level, call-graph-aware. Только если/когда понадобится. - -## Bank-local static data (mutable data в том же банке что и код) — ✅ ГОТОВО - -- [x] Пример `examples/bank_local_data/` — функция в BANK1 со своим writable BSS array + const table + malloc-тест -- [x] `mkexe -p 0` для нулевого padding банков (BSS-storage обнуляется при загрузке) -- [x] Канонический рецепт: `--codeseg BANK1 --constseg BANK1 --dataseg BANK1` для bank1.c + `-Wl-b_BANK1=0x1C000` для линковки. **`--dataseg BANK1` РАБОТАЕТ** — раньше казалось обратное из-за trampoline bug который маскировал результат. -- [x] **Критичный фикс trampoline'a в runtime/bank.s** — старый `pop af; out (n), a` клобберил A → все banked-функции возвращающие uint8_t тихо возвращали мусор. Новый `pop bc; out (c), b` сохраняет A. -- [x] **malloc из banked-функции работает прозрачно** — heap живёт в W2 (HOME), W2 никогда не свапается trampoline'ом, pointer валиден из любого контекста. См. memory/bank_local_data_pattern.md. -- [x] Документация в memory: `memory/bank_local_data_pattern.md` (полный рецепт + malloc + nuances), `memory/sdcc_banking.md` (trampoline fix) -- [ ] Опционально — расширить `check_banks.py` чтобы показывать разбивку size = code + const + bss per bank (cosmetic) - -Зачем: для модулей с большим private state (level loader, audio engine, scene data). Экономит W2 heap для динамики, а статика остаётся в бэке. - -## Подсказки из solid-c (нативный Sprinter C — `third_party/solid-c/`) - -После анализа solid-c'овской libc (см. `memory/solid_c_findings.md`) выявлены готовые паттерны для следующих недостающих функций. Приоритет от **высокого** к низкому: - -### High-priority gaps (легко портировать, большая польза) -- [x] **`errno` + `strerror`/`perror`** — табличка 32 ошибок (libc/io/errno.c) -- [x] **Расширенный `open()`** для O_CREAT/O_TRUNC/O_APPEND/O_EXCL state machine -- [x] **`atexit`** — 8-callback LIFO + `exit()` + `_exit()` (libc/io/atexit.c) -- [x] **`setjmp`/`longjmp`** — 6-байт jmp_buf={sp,ix,pc} (libc/io/setjmp.c) -- [x] **`sleep(seconds)`** — 50Hz halt-loop (libc/io/sleep.c) -- [x] **ESTEX ENV API** ($46, getenv/putenv) — libc/io/env.c. Учли doc-bug: реально A=0 это NOT FOUND - -### Medium-priority (нужно для shell-like утилит) -- [ ] **Mouse driver** — `rst $30h`, 17 функций. **Сначала тест что работает в MAME**. -- [x] **`ffirst`/`fnext` + ffblk_t struct** для directory listing — реализовано, demo: ls.exe -- [x] **`getdatetime`/`setdatetime`** через ESTEX $21/$22 — libc/io/time.c, demo: time_dir_test -- [x] **`chdir`/`getcwd`/`mkdir`/`rmdir`** — wrappers для ESTEX $1B-$1E — libc/io/fsdir.c -- [x] **conio: `kbhit`/`getch`/`getche`/`cputs`/`clrscr`/`gotoxy`** — реализовано -- [x] **conio extras**: `wherex`/`wherey` ($53), `wrchar`/`rdchar` ($58/$57), `textmode_get/set` ($50/$51), `clrscr_attr` ($56) + COLOR macros - -### Low-priority — ✅ FILE* stack ГОТОВО - -- [x] **Минимальный unbuffered FILE\*** — `libc/stdio/file.c` + `libc/include/stdio.h`. fopen/fclose/fputs/fgets/fread/fwrite/fseek/ftell/rewind/feof/ferror/clearerr/fflush + stdin/stdout/stderr как pseudo-streams. См. `memory/file_star_design.md` и `examples/filetest`. -- [ ] fprintf / fscanf — нужна printf-через-callback machinery. Пока пользователь может `sprintf(buf, ...) + fputs(buf, fp)`. -- [ ] Опциональный buffered mode (setvbuf, line/block buffering) — если когда-то понадобится. - -### POSIX time API — ✅ ГОТОВО -- [x] `libc/io/posix_time.c` — time/localtime/gmtime/mktime/asctime/ctime поверх getdatetime. SDCC's time.rel избегаем (нельзя override _RtcRead). См. `examples/ptime`. - -### sys/stat — ✅ ГОТОВО -- [x] `libc/io/stat.c` — POSIX stat/fstat. Гибрид open+fstat для файлов, ffirst+iter для папок (включая "."/".."). См. `examples/stattest` и `memory/estex_ffirst_dotdot.md`. - -### assert — ✅ ГОТОВО (используем SDCC's __assert через fallback include path) - -## libc/stdlib — ✅ не нужно делать (см. memory/sdcc_stdlib_works.md) - -Проверено через `examples/stdlib_test/`: SDCC z80.lib содержит работающие реализации: -- `atoi/atol/atof, strtol/strtoul, rand/srand, qsort/bsearch, abs/labs, div/ldiv` -- Полный `` (memchr/cmp/set/cpy, strcat/cmp/cpy/len/chr/spn/etc.) -- `` (toupper/tolower) -- `` (sinf/cosf/sqrtf/etc.) - -Линкер автоматически тянет из z80.lib когда нужно. **НЕ переписывать**. - -Наши Sprinter-specific обязательные модули остаются: atexit, env, errno, setjmp, putchar/puts/getchar, conio, fsdir, time, mouse, open/read/lseek/close. - -## Build-system: libsprinter.lib + sprinter-cc — ✅ ГОТОВО - -- [x] `lib/Makefile` — собирает каждый libc/*.c в `.rel`, архивирует через sdar в `lib/sprinter.lib` -- [x] Включает runtime/bank.s и runtime/heap.s (auto-pulled при __banked/malloc) -- [x] `bin/sprinter-cc` — bash-wrapper: `sprinter-cc -o foo.exe foo.c` одной строкой -- [x] Поддержка опций `--crt0=default|minimal|banked`, `--bank N=FILE.c`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` -- [x] `examples/hello_sccc/` — демо: `hello.c` собирается за один shell-вызов, размер совпадает с ручным Makefile (925 байт) -- [x] Split `putchar.c` → `putchar.c` + `puts.c` для per-function granularity (puts override SDCC's z80.lib version) -- [x] Включён в `make all` (зависимость `lib` перед `examples`) - -Возможные улучшения (опционально): -- [ ] Мигрировать остальные examples на sprinter-cc вместо ручных Makefile (косметика) -- [ ] Дальнейшая декомпозиция libc/*.c per-function (но текущая granularity уже даёт нужный размер — линкер пакетует .rel целиком, и для большинства файлов это одна функция) - -## Этап 9 — memory modes для sprinter-cc - -DSS выделяет страницы памяти по размеру приложения: < 16 KB → одна страница, в остальные окна подключается «страница #FF» (read=0xFF, write игнорится). Из-за этого CODE-в-W1 + DATA-в-W2 для маленькой программы молча ломается. См. [memory/sprinter_memory_modes.md](../../.claude/projects/-Volumes-SAM8-Projects-DIY-Z80-Sprinter-C-Compiler/memory/sprinter_memory_modes.md). - -- [x] **`tiny`**: всё (CODE+DATA+стек) в W2. Default. Verified hello/argv/conio/malloc/file/etc. -- [x] **`--memory MODE` флаг в sprinter-cc**: parser + per-mode дефолты CODE_LOC/DATA_LOC, override через явные `--code-loc`/`--data-loc`. tiny работает; small/big/huge компилируются с warning'ом (runtime не готов). Реализовано 2026-05-30. -- [x] **`--memory-manual SPEC`**: парсит `CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3`. Реализовано 2026-05-30. -- [x] **`small` runtime**: `runtime/crt0_small.s` использует ESTEX `$3D GETMEM` + `$3A SETWIN2` чтобы выделить и замапить W2-страницу ДО gsinit. **НЕ** BIOS `$C4` — стек на этом этапе в W1 (boot_stack в HOME), а BIOS требует стек в W2. После маппинга SP переключается на 0xBFFE, дальше стандартный flow. Реализовано 2026-05-30, verified hello.exe. -- [x] **`small` auto-detect для >16 KB программ**: `crt0_small.s` читает порт `0xC2` (текущая страница в W2 — не `0xA2`! это W1). Если 0xFF — выделяет page; иначе DSS уже сделала это (программа сама вылезла в W2). Один crt0 покрывает 0..30 KB. mkexe также разрешает HOME span W1+W2 (0x4000..0xBFFF). Verified hello: small (5 KB файл, SETWIN2 path) + 32 KB файл (auto-skip). Реализовано 2026-05-30. -- [x] **`big` runtime** (tiny + banked code в W1): параметризовали `crt0_banked.s` + `bank.s` через `.ifdef BANK_W1` — другой banking port (0xA2 vs 0xE2), другой load-addr (0x4000 vs 0xC000). sprinter-cc prepend'ит `BANK_W1 = 1` при `--memory big`, передаёт `mkexe -B 0x4000`. Пример `examples/banked_big/`. Реализовано 2026-05-30. -- [x] **`huge` runtime** (small + banked code в W3): merge W2-detect логики из `crt0_small.s` в `crt0_banked.s`. Существующий пример `examples/banked/` теперь использует MEMORY=huge. Реализовано 2026-05-30. -- [x] **`--debug` флаг**: prepend `DEBUG_RT = 1` в crt0 + `-DDEBUG_RT` в sdcc. Открывает symbol `_w2_self_allocated` (uint8_t) — runtime diagnostic кто аллоцировал W2. Реализовано 2026-05-30. - -### Дизайн-решения по libc и crt0 - -**Одна `sprinter.lib`** работает для всех memory mode — `.rel`-члены relocatable, SDLD делает dead-code elimination per-member (без графики не подтягивает `gfx_core.rel` и т.д.). Verified hello vs malloc_test через map-файлы. - -**`gfx.lib` отдельно — НЕ нужен**: dead-code elimination уже работает. - -**`libc_banked` (libc в bank вместо HOME)** — идея на потом, когда HOME (16 KB) забит user-кодом + libc в `huge` mode. Реализуется через `--codeseg BANK0` при компиляции libc; trade-off: trampoline ~30 циклов на каждый libc-вызов. Триггер: реальная программа упрётся в HOME budget. - -**HW-зависимые модули — `sprinter_home.lib` отдельно.** Часть libc физически не может быть забанкована в W3, потому что она РАБОТАЕТ с W3: -- `gfx_*` — пишет в видеопамять `0xC000+` после swap W3 на video page -- `bank_io` (mem_alloc_pages/bank_read/bank_write) — swap'ит W3 через `OUT (0xE2)` -- Будущие ISR — прерывание может прийти когда W3 на чём угодно - -В huge mode эти модули ДОЛЖНЫ остаться в HOME (W1). Когда будем делать `libc_banked`, **одновременно** выделяем `sprinter_home.lib` (HOME-only) из `sprinter.lib` (bankable). Финальная схема: -``` -sprinter_home.lib HOME-only: gfx, bank_io, ISR shims -sprinter.lib bankable: printf, malloc, string, conio, stdio, env, ... -sprinter_banked.lib тот же sprinter.lib но --codeseg BANK0 (для huge) -``` -Триггер: реализация `--memory huge` runtime. - -**crt0 — по одному на mode:** -- `crt0.s` — текущий, для **tiny/big**: SP=0xBFFE, парсит argv (W2-ресурс уже выделен DSS). -- `crt0_minimal.s` — текущий, для tiny без argv. -- `crt0_small.s` — **новый, step 3**: для **small/huge**, аллоцирует W2 через `mem_alloc_pages` ДО gsinit, маппит в порт `0xA2`, потом стандартный flow. -- `crt0_banked.s` — текущий, для **big**: trampoline-таблица для W3 банков, CODE в W2. -- `crt0_banked_small.s` — **новый**: huge = small (W2-alloc) + banked (W3 trampolines). - -sprinter-cc подбирает crt0 по `--memory` mode (сейчас `--crt0=` это override). -- [x] **Настраиваемый размер стека**: флаг `sprinter-cc --stack-size BYTES`. Wrapper генерирует `heap_top.s` с `___sdcc_heap_end = stack_top + 1 - stack_size`, отдельный .rel линкуется per-program. Default ≈1278 байт (heap_top=0xBB00) из `runtime/heap_top.s`. Реализовано 2026-05-30. - -Интерфейс: `sprinter-cc --memory [tiny|small|big|huge|manual] [--memory-manual SPEC] [--stack-size N] foo.c`. `--memory-manual` имеет смысл только с `--memory manual`. - -## Known issues / quirks - -- **ESTEX $46 ENV API**: ✅ работает. Док-ция в `DiskSyscalls.txt v1.6` ошибочно описывает return-status — A=0 это NOT FOUND, не FOUND. Зафиксировано в `memory/sprinter_platform.md`. - -## ОБЯЗАТЕЛЬНЫЕ ЗАДАЧИ ДЛЯ V2 (после релиза v1) - -### Turbo-C-style graphics API (BGI-like) — **MUST для v2** - -Расширить наш `gfx_*` API до уровня **Turbo-C ``** (BGI) для MS-DOS. -Программисты привыкшие к Turbo-C должны переносить графический код 1-в-1. - -**Что должно быть** (на основе Borland BGI): - -Setup/teardown: -- `initgraph()` / `closegraph()` — у нас сейчас `gfx_init`/`gfx_done`, добавить alias -- `getmaxx()` / `getmaxy()` — макрос на GFX_WIDTH-1 / GFX_HEIGHT-1 -- `cleardevice()` — alias to gfx_clear -- `getgraphmode()` / `setgraphmode()` — у нас get_videomode/set_videomode - -Color/palette: -- `setcolor(c)`, `getcolor()` — current draw color -- `setbkcolor(c)`, `getbkcolor()` — background color -- `setpalette(idx, c)` — палитра entry -- `getpalette(&info)` — read all palette - -Primitives (мы уже имеем эквиваленты — добавить BGI-имена как aliases): -- `putpixel(x, y, c)` — есть как gfx_putpixel -- `getpixel(x, y)` — нужно реализовать (RMW обратное — IN) -- `moveto(x, y)`, `lineto(x, y)`, `linerel(dx, dy)` — current point + line drawing -- `line(x1, y1, x2, y2)` — есть как gfx_line -- `rectangle(x1, y1, x2, y2)` — есть как gfx_rect (но другой API: x1,y1,x2,y2 vs x,y,w,h!) -- `bar(x1, y1, x2, y2)` — есть как gfx_fill_rect -- `bar3d(x1, y1, x2, y2, depth, topflag)` — новое: rect + 3d edges -- `circle(x, y, r)`, `arc(...)`, `ellipse(...)`, `pieslice(...)` — новые primitives -- `fillpoly()`, `drawpoly()` — полигоны -- `floodfill(x, y, border_color)` — заливка - -Text on graphics screen: -- `outtext(s)` / `outtextxy(x, y, s)` — есть как gfx_text (alias) -- `settextstyle(font, dir, size)` — multiple bitmap fonts -- `gettextsettings(&info)` -- `textwidth(s)` / `textheight(s)` — measure - -Image manipulation: -- `imagesize(x1, y1, x2, y2)` — bytes needed for getimage -- `getimage(x1, y1, x2, y2, buf)` — save rect to buffer -- `putimage(x, y, buf, op)` — paste back with COPY_PUT/XOR_PUT/AND_PUT/OR_PUT/NOT_PUT - -Clipping/viewport: -- `setviewport(x1, y1, x2, y2, clip)` — drawing clip rect -- `getviewsettings(&info)` -- `clearviewport()` -- `setactivepage(p)` / `setvisualpage(p)` — двойная буферизация (Sprinter имеет 2 screen) - -Line style: -- `setlinestyle(style, pattern, thickness)` — SOLID_LINE / DOTTED_LINE / etc. -- `getlinesettings(&info)` - -**Acceptance:** перенос типичной Turbo-C BGI программы (рисующей с использованием -moveto/lineto/circle/bar/setcolor) должен работать без существенных правок. - -**Notes:** -- BGI fonts (TRIPLEX/SANS_SERIF/GOTHIC) — у нас один BIOS font, остальные нужно - добавить (как bitmap data в lib) -- imagesize/getimage/putimage — самые востребованные для game/animation -- Active/visual page (двойная буферизация) — Sprinter поддерживает 2 graphics pages, - нужен API switching - -См. также `examples/` Turbo C 2.x BGIDEMO как reference что нужно. - -### IM2 Interrupt Handlers — **MUST для v2** - -User-задаваемые ISR через Z80 IM 2 mode. Нужны для: -- Timer ticks (50 Hz frame counter, плавная анимация) -- Music playback (AY, COVOX) -- Real-time games (input + game logic + render в interrupt-driven) -- Async keyboard / mouse handling - -**Status:** ОТЛОЖЕНО до v2. Полный research + design в `docs/im2_isr_design.md`. - -**Решение по архитектуре:** реализовать как отдельный memory mode `--memory im2` -(вместо того чтобы лезть во все существующие crt0). Detail'и в design-doc. - -**Резюме research'а** (полный текст в `docs/im2_isr_design.md`): -- Vector 0xFF — frame + keyboard + CBL. Disambiguation по портам 0x19 / 0xFE -- Mouse hardware-IRQ не приходит (на текущей плате) -- Vector table / ISR / stack ОБЯЗАНЫ быть в W2 (0x8000..0xBFFF) -- DSS имеет свой IM 2 handler — нужно chain'иться (иначе клавиатура / SYSTIME ломаются) - -### Прочие крупные пункты для v2 - -- [ ] **FILE API rewrite — buffered streams** — текущая реализация в - `libc/stdio/file.c` это provisional unbuffered shim (каждый fputc/fgetc - = один read/write syscall). Нужна полноценная buffered семантика - как в Solid-C: - - ```c - typedef struct { - uint flags; // +0..1 file status flags - int level; // +2..3 empty/fill level of buffer - char *curp; // +4..5 current active pointer - int fd; // +6..7 underlying low-level fd - char *buffer; // +8..9 data transfer buffer - char hold; // +10 ungetc byte if no buffer - short token; // +11..12 reserved - char dummy; // +13 reserved - } FILE; - ``` - - stdin/stdout/stderr — fd-маркеры `0 / -1 / -2`. Отрицательные для - stdout/stderr выбраны намеренно: ESTEX OPEN может вернуть positive - small fd (1, 2, …) для обычного файла → если бы stdout=1, реальный - fd=1 сталкивался бы с идентификатором. fd=0 для stdin безопасно - (ESTEX 0 не возвращает). Сами fd не передаются в syscall'ы — - диспетчеризация по флагам `_F_CONIN/_F_CONOUT`. - - Принтер-потоки (stdaux/stdprn) НЕ реализуем — Sprinter принтерной - API не имеет. - - Альтернатива — взять реализацию из third_party/solid-c (sources в - `SRC/CLIB/`); там есть готовый buffered FILE + fopen/fread/fwrite/ - fseek/setvbuf и т.д. Адаптировать к нашим open/read/write/lseek. - - При rewrite заодно решить deferred issues stdio-review: - - `fwrite` short-write должен ставить `_F_ERROR` - - `fgets(buf, 1, fp)` — стандарт говорит "empty string", мы вернули NULL - - `mode_to_flags` — break-out на '+' (cosmetic) - -- [ ] **Audio API** — AY-3-8910 + COVOX через прерывания (требует IM2) -- [ ] **ISA-8 slot support** — ZX-Bus карты (sound, network, etc.) — требует IM2 + чтения portов - -## Прочие задачи (v1 backlog, не блокирующие) - -- [x] **#9: text I/O split (Turbo-C style)** — stdio (puts/printf/putchar) теперь fast no-attr через PCHARS/PUTCHAR. conio (cputs/cprintf/putch) применяет attr через textcolor/textbackground/textattr. KEEP_EXIST_ATTR → conio fallback на fast path. Verified в hello.exe. См. `memory/text_output_api_split.md`. Реализовано 2026-05-31. -- [x] **Mouse API полный** (резидентный driver, RST 30h) — все 14 функций обёрнуты (init/show/hide/refresh/read/goto/bounds/text_cursor/load_cursor/get_cursor/get/set_sensitivity/video_mode_changed). См. `memory/mouse_api.md`. Verified в MAME 2026-05-31. Sensitivity = divider (меньше = быстрее). -- [ ] Interrupt handlers — IM 2 vector table в HOME для user ISR'ов -- [ ] Поддержка `restore SP on EXIT` (паттерн из z88dk +pps) — проверить нужно ли -- [ ] CI: автоматически запускать MAME с `-aviwrite` для screenshot-сравнения, чтобы тесты примеров проходили без человека - -## Идеи на потом - -- Поддержка `` (есть в SDCC stdlib — нужно протестировать что наш crt0 совместим) -- `` через ESTEX SYSTIME (`$21`) и CMOS BIOS-функции -- ZX Spectrum-совместимый режим как отдельный target (для портирования спектрумовских программ) -- Поддержка ZX-Bus карт (sound, network, etc.) — нужны драйверы -- Profile-guided optimization tools (hot/cold detection) для крупных программ - -## Linker duplicate-symbol warnings (благоприятные, отфильтрованы) - -Когда мы сознательно overrides'им SDCC z80.lib функции собственной версией в `sprinter.lib`, `sdldz80` пишет `?ASlink-Warning-Definition of public symbol '...' found more than once`. Линкер берёт первое найденное определение (наше), поэтому поведение корректное — warning только noise. - -Текущие overrides: -- `_puts` — наша версия через PCHARS+\r\n vs SDCC posix puts -- `___sdcc_heap` — наш heap в W2 vs SDCC's стандартный -- `_asctime`, `_localtime` (и возможно другие из time) — наш `posix_time.c` через ESTEX SYSTIME vs SDCC's `time.rel` который зависит от `_RtcRead` - -**Текущее решение:** `bin/sprinter-cc` отфильтровывает warning-блок (warning + 2 follow-up `Library:` строки) из вывода `sdcc`. Через `-v` (verbose) всё показывается. Реализовано через awk-pipe. - -**Возможные улучшения:** -- Перейти на explicit `--nostdlib` + ручной список нужных модулей из z80.lib (string, math, stdlib без override'нутых) — убрать ИСТОЧНИК warning'ов, не маскировать -- Или: переименовать наши `_puts` → `_puts_sprinter` + alias через linker flag (не уверен что SDCC поддерживает) -- Или: оставить как сейчас (рабочее и benign) — приоритет низкий - -## TODO: проверить на реальном железе - -- [ ] **Port_Y banking trick** (`docs/part2/SprinterGraphics programming.txt`): - доку утверждает что после `OUT (0x89), Y` адреса 0xC000+0x400*N в окне W3 - маппятся на строки Y..Y+15 (одно программирование → 16 строк). - Empirical 2026-06-01 в MAME 0.283 этот trick **не работает** — пиксели - по адресам выше 0xC000+row_width уходят в невидимую область. Канонический - `docs/samples/plasma2.asm` тоже не использует banking, переустанавливает - Port_Y per row. - План: - 1. Получить доступ к реальному Sprinter - 2. Запустить тест dual-write (`_gfx_putpixel_raw` + второй write в `0xD000+x`) - 3. Если на железе видны двойные линии → бага MAME, открыть issue с - минимальным репро - 4. Если на железе тоже одна линия → документ неверный, удалить упоминание - из доки и просто оставить текущую реализацию (Port_Y per pixel) - 5. Если banking работает на железе → внедрить кэширование Port_Y в - `_gfx_putpixel_raw` (sentinel out-of-range, см. memory/gfx_port_y_banking.md) - - Связанный выигрыш для Bresenham (60-pixel диагональ) — около 8× меньше - OUT (0x89) операций, для `gfx_fill_rect 320x256` — 16× меньше. Не блокирует - release v1. - -## GFX: расширения по `docs/part2/accelerator_doc.txt` - -После прочтения детального accelerator doc выявлены незакрытые направления. -Сейчас в коде используется только горизонтальный/вертикальный Fill mode. - -### Quick wins для текущих primitives - -- [ ] **Заменить SMC на `LD A, (var)` для block-size**. Документ явно - разрешает `LD A, (HL)`, `LD A, (BC)`, `LD A, (DE)` (но не `LD A, r`). - Это уберёт SMC complexity в `gfx_lines.c:hfill_chunk/vfill_chunk` и - `gfx_16.c:g16_hfill_chunk`. Запрещено только register-to-register. -- [ ] **Кэширование block-size**. Документ показывает что accel запоминает - block size между bursts (см. `Horizontal_Line_Fill`: устанавливают - size + `LD B,B` отключение, потом включают Fill mode и используют - сохранённый size). Для `gfx_fill_rect` с 100 одинаковыми - строками — установить size 1 раз, а не 100. - -### Bank-prefix modes (port 0xE2 bits) - -Документ показывает три варианта банка видеостраницы помимо стандартного 0x50: - -| Bank byte | Effect | -|---|---| -| 0x50 | Normal write — пишется в shadow + видимый | -| 0x54 | "no copy in main shadow RAM" | -| 0x58 | **"FF is transparent"** — байт 0xFF при write оставляет background | -| 0x5C | both | - -Bank 0x58 объясняет почему mouse cursor рисуется с 0xFF-прозрачностью. -Это путь к **sprite-blending через accel block copy**: - -- [ ] **`gfx_set_bank_transparent(on)`** или флаг в `gfx_set_bank` для - выбора 0x50/0x58 при отрисовке sprite'ов -- [ ] Использовать в новом `gfx_blit()` чтобы по факту получать - transparent sprites через accel-копию - -### Block copy mode (sprite blit'ы) - -`LD L,L` (horizontal) и `LD A,A` (vertical) — режим копирования блока через -256-байтную accel memory. Это базис для blit'ов. - -- [ ] **`gfx_blit(src_data, x, y, w, h)`** — копирование sprite'а - (произвольный размер, через accel) -- [ ] **`gfx_blit_transparent(src, x, y, w, h)`** — с использованием bank 0x58 - -См. `Draw_Restangle_Data` в accelerator_doc.txt как референс. - -### AND / OR / XOR operations через accel - -Документ показывает что accel поддерживает логические операции с блоками -данных. Применения: -- XOR — инверсия области (выделение selection в UI) -- OR / AND — masking, alpha-style blending -- См. пример в accelerator_doc.txt: "256 bytes block coding via XOR" - -- [ ] **`gfx_xor_rect`** / **`gfx_or_rect`** / **`gfx_and_rect`** — - примитивы логических операций над прямоугольником -- [ ] **`gfx_invert_rect(x, y, w, h)`** — alias на xor с 0xFF - -### Bitmap fonts разных размеров - -Сейчас `gfx_text` / `gfx_putchar` хардкоженно работают с 8×8 шрифтом -(BIOS WIN_GET_ZG возвращает 256×8 байт). Для будущих UI / титульников -нужны: -- [ ] **`gfx_set_font_size(w, h)`** — переключить ширину/высоту glyph'а -- [ ] **`gfx_set_font_data(ptr, w, h, advance)`** — заменить указатель - на пользовательский шрифт + размеры -- [ ] Поддержка **proportional** (advance != w) шрифтов — добавить - array advance[256] на ширину каждого glyph'а -- [ ] **Big-font режимы**: 8×16, 16×16, 16×8 (для титульников) -- [ ] Возможно отдельный API `gfx_text_ex(x, y, str, font_id)` где - font_id выбирает один из загруженных шрифтов -- [ ] **Anti-alias 2-bit шрифты** (бит фон / бит граница / 2-бит alpha?) - — far future, для smooth UI - -## Финальный этап оптимизаций (не сейчас) - -- **`gfx_line` через accel для пологих диагоналей** — Bresenham для линии с |dy| << |dx| (или наоборот) выдаёт длинные runs одинакового Y (или X): пиксель, пиксель, пиксель, шаг Y, пиксель, пиксель... Каждый такой run — это готовый аргумент для `gfx_hline` (или `vline`). - План исследования: посчитать длину runs как функцию от наклона; решить минимальный run length, при котором выгоднее accel hline чем N×putpixel (overhead accel ~20µs, putpixel ~5µs — accel выгоднее при run ≥ 4-5 px); для крутых диагоналей (dx ≈ dy) оставить Bresenham, для пологих — run-length-based fill. - Сейчас `gfx_line` orthogonal cases уже через accel — оптимизировать только косые. - -- **`gfx_fill_rect` с одним W3-swap на всю операцию** — сейчас каждый внутренний `gfx_hline`/`gfx_vline` делает свой DI/save-W3/restore-W3/EI. Можно сделать internal `_fill_rect_inner` который держит W3 замапленным и DI весь цикл; ~20µs × количество строк/столбцов экономии. Применимо ко всем композитным примитивам. diff --git a/examples/mdview2/UTF8TEST.MD b/examples/mdview2/UTF8TEST.MD deleted file mode 100644 index e9891b2..0000000 --- a/examples/mdview2/UTF8TEST.MD +++ /dev/null @@ -1,28 +0,0 @@ -# Тест UTF-8 - -Это **проверка** кодировки _UTF-8_ в `mdview2`. -Кириллица должна читаться: съешь же ещё этих мягких булок. - -## Символы и пунктуация - -Тире — длинное, и – короткое. Кавычки: «ёлочки» и “лапки”. -Многоточие… стрелки → ← ↑ ↓, галочка ✓ и крестик ✗. -Градус 25°, пункт списка • буллет, номер № 7. - -## Список - -- Первый пункт -- Второй пункт с длинным текстом, чтобы проверить перенос строки по словам на границе экрана восемьдесят символов -- Ёжик, ёлка, объём - -> Цитата: «Краткость — сестра таланта». - -## Таблица - -| Язык | Привет | Число | -|----------|---------------|-------| -| Русский | Здравствуйте | 42 | -| English | Hello | 7 | -| Deutsch | Grüße | 100 | - -Конец файла. diff --git a/examples/mdview2/docs/encoding-plan.md b/examples/mdview2/docs/encoding-plan.md deleted file mode 100644 index cf70860..0000000 --- a/examples/mdview2/docs/encoding-plan.md +++ /dev/null @@ -1,144 +0,0 @@ -# mdview2 — поддержка кодировок (CP866 / CP1251 / KOI8-R / UTF-8) - -Статус: РЕАЛИЗОВАНО (2026-06-25). Все фазы сделаны; Фаза 4 — eager-кооперативным -вариантом (см. ниже), а не idle-build. Осталась только проверка на железе и -возможная оптимизация «не строить заведомо бесполезный вторичный набор». - -## Цель - -- CP1251, KOI8-R — простой 1:1 маппинг байтов [128-255] в CP866 (кириллица - + основная пунктуация). KOI8-R равнозначна, входит в общий цикл. -- UTF-8 — декодирование с маппингом кириллицы в CP866 + подстановка части - некириллических символов (стрелки, галочки, тире, буллеты, box-drawing) в - CP866/ASCII-глифы. -- Автоопределение кодировки при открытии (BOM + дешёвая эвристика). -- F8 (Codepage) — переключение по циклу CP866 → CP1251 → KOI8-R → UTF8. - -## Ключевые наблюдения (определяют архитектуру) - -1. **8-битные кодировки имеют ОДНУ структуру.** markdown-разметка вся в ASCII - (`# * | -` …); 8-битные различаются только глифами [128-255]. Значит индекс - и кэш (смещения, переносы, ширины таблиц) для CP866/CP1251/KOI8-R — - **общие**; переключение между ними = ремап глифов [128-255], БЕЗ - переиндексации. -2. **UTF-8 — другая структура** (кириллица 2 байта). Нужен отдельный - конвертированный CP866-буфер со своим индексом/кэшем. -3. **Проблема рамок таблиц / маркеров.** В кэше намешаны контентные глифы - (байты-из-источника [128-255] — НАДО ремапить) и вставленные нами CP866- - глифы (box `│─┼…`, маркер списка 0x07, цитата 0xB3, HR 0xC4 — ремапить - НЕЛЬЗЯ). По значению байта не различить → различаем по **атрибуту**. - -## Архитектура - -### Буферы -- `src_phys[]` — оригинальные байты файла (грузим как сейчас; храним всегда — - под ремап и raw-view, см. memory/mdview2_file_phys_preserve). -- 8-битный режим: `fb()` читает `src_phys` напрямую. Кэш контентных ячеек - хранит **исходный байт** (не пред-конвертированный). -- UTF-8 режим: отдельные EMM-страницы `utf_phys[]` = UTF8→CP866 конвертация - src; свой индекс/кэш; `fb()` читает `utf_phys`. - -### Различение контент/структура по attr -Каждый структурный глиф получает НЕ-контентный attr: -- box-рамка таблиц → новый `ATTR_BOX` (сейчас TBL_ATTR=ATTR_TEXT — поменять); -- HR (0xC4) → ATTR_HR; маркер списка (0x07) → ATTR_LIST_MARKER; цитата - (0xB3) → ATTR_QUOTE_MARKER — уже различимы. -Правило ремапа: ремапить `char>=128` только если attr ∈ контентных -(TEXT/BOLD/ITALIC/UNDER/CODE/STRIKE/TITLE1-4). Структурные — как есть. - -### Ремап на этапе отрисовки -`draw_line_from_cache`: -- encoding == CP866 или UTF-8: прямой `win_rest` из кэш-страницы (ремап не - нужен — CP866 identity; UTF-8-кэш уже CP866). -- encoding == CP1251/KOI8-R: читаем кэш-строку в near-буфер, ремапим - контентные байты [128-255] через активную таблицу (структурные пропускаем - по attr), пишем в scratch-страницу, `win_rest` из неё. Только видимые ~30 - строк, на скролле — дёшево. - -Итого: переключение между 8-битными — мгновенно (меняем активную таблицу + -redraw, draw ремапит). Переиндексация только при переходе в/из UTF-8. - -## Детекция кодировки (дешёвый скан байтов, ДО построения) - -1. **BOM**: первые 3 байта EF BB BF → UTF8 (и пропустить BOM). -2. **UTF-8 валидность** (если нет BOM): проход, проверка структуры - (лид-байты 0xC2-0xDF/0xE0-0xEF/0xF0-0xF4 + континюэйшны 0x80-0xBF; - одиночный 0x80-0xBF, 0xC0/0xC1, 0xF5+ → нарушение). 0 нарушений И есть - ≥1 multibyte → UTF8. Любое нарушение → 8-бит. -3. **8-бит дизамбигуация** (CP866/CP1251/KOI8-R): счёт попаданий в байты - самых ходовых строчных русских букв (о е а и н т с р в л) каждой кодировки; - максимум выигрывает. (Предрасчёт байт-наборов по таблицам.) -4. **Фолбэк**: нет байт ≥0x80 или неоднозначно → CP866 (родная). - -## Таблицы (static const, CODE/const-сегмент) - -- `cp1251_to_866[256]`, `koi8r_to_866[256]` — байт→байт (ASCII identity; - кириллица по раскладкам; en/em-dash, «ёлочки», … → CP866-аналоги или '?'). -- UTF-8: `utf_cyr_to_866[]` для U+0400..U+045F + компактная таблица символов - `utf_sym[]` (codepoint→CP866): U+2192→'>'/стрелка, U+2190→'<', U+2713/14 ✓ - →'v'/box, U+2022 •→0x07/0xF9, U+2014/2013 —→'-', U+2026 …→"...", - U+00A0→' ', U+2500.. box→CP866 box; прочее → '?'. - -## Поток загрузки - -1. Грузим в `src_phys`. Детект-скан → кодировка E. -2. Если E ∈ {UTF8}: строим UTF-8-набор (utf_phys + конвертация + индекс), - показываем UTF-8. Иначе: показываем 8-битный (общий индекс на src, - активная таблица = E). -3. **Ленивый build второго набора в простое.** Главный цикл — НЕ блокирующий - getkey, а kbhit-поллинг: пока нет клавиш и второй набор (UTF-8 при - стартовом 8-бит, либо 8-бит при стартовом UTF-8) не построен — докручиваем - его инкрементально. После — F8 в любую сторону мгновенно. - -## F8 — переключение - -- Цикл g_encoding: CP866 → CP1251 → KOI8-R → UTF8 → CP866. -- 8-бит↔8-бит: сменить активную таблицу + redraw (без переиндексации). -- в/из UTF-8: переключить активный индекс/кэш на соответствующий набор - (если построен; иначе достроить — но при ленивом build обычно уже готов). -- Статус-бар: имя кодировки; меню (строка 31): «F8 Codepage». - -## Фазы реализации (ИТОГ) - -1. ✅ **Ядро 8-бит**: ATTR_BOX; кэш хранит исходный байт; таблицы CP1251/KOI8R; - ремап в draw (`win_rest_remap`). -2. ✅ **UTF-8 набор**: `alloc_and_convert_utf8` + `utf8_convert` (декодер 1/2/3- - байт, 4-байтные/битые → `?`); `utf_cyr_to_866[96]` + символьные подстановки - в `conv_emit_cp` (стрелки 0x18-0x1B, галка 0xFB, буллет 0xF9, тире/кавычки/ - box/° и т.п.; «» → `<`/`>`, т.к. в CP866 гильеметов нет). -3. ✅ **Детекция** (BOM + эвристика), сэмпл — первые **4 КБ** (быстро); хвост- - обрезка multibyte на границе сэмпла не штрафуется. -4. ✅ **Сосуществование 2 наборов** (`docset_t g_doc[2]` + `doc_save/load/switch`, - свап «живых» глобалов). Сборка — **ленивая (build-on-demand)**: при старте - строится только первичный (показываемый) набор; UTF-8 конвертация тоже - ленивая (в `build_doc`, не до первого экрана → старт быстрый). Второй набор - достраивается `switch_encoding()` при первом F8-переходе в него (спиннер, - потом кэш). Eager-вариант отвергнут: удваивал старт и блокировал F8 на время - фоновой сборки. -5. ✅ **F8** полный цикл CP866→CP1251→KOI8R→UTF8→CP866 (внутри 8-бит — ремап, - на границе — `doc_switch`/ленивая сборка). Статус: `enc_name` кол.37. Меню - «F8 Codepage» показывается только когда переключение возможно (`g_f8_enabled`): - во время сборки 8-битного первичного F8 разрешён в `load_key` (только цикл - 8-бит); во время сборки UTF-8 первичного метка F8 скрыта. F1-справка: секция - Encoding. - -NB: меню-строка разбита на 10 блоков по 8 колонок, метки Fn кладутся в блок -(n-1)*8 (F1→0, F8→7, F10→9). - -## Память -- 8-бит: src + scratch-страница для ремапа (1 стр.). Доп. индекса нет. -- UTF-8 набор: utf_phys (≤8 стр.) + свой индекс/кэш-контент. Строится лениво. -- Бюджет 215 свободных страниц (sprinter_emm_budget) — с запасом. - -## Открытые вопросы (к реализации) -1. Имя кодировки в статусе — где (зона имени файла / отдельный слот). -2. Набор UTF-8-подстановок символов — приоритет (→ ← ✓ ✗ • — … « » box). -3. Глубина таблиц пунктуации CP1251/KOI8 (минимум кириллица+dash или полнее). -4. Объём детект-сэмпла (весь файл или первые N КБ). - -## Риски -- Корректность различения контент/структура по attr — нужно, чтобы ВСЕ - вставленные глифы имели не-контентный attr (проверить box/markers). -- Два набора индекс/кэш (8-бит + UTF-8) + переключение активного — учёт - страниц, чтобы не течь и не путать. -- Точность таблиц (особенно UTF-8 символы) — итеративно по факту. diff --git a/examples/mdview2/docs/mdview2-plan.md b/examples/mdview2/docs/mdview2-plan.md deleted file mode 100644 index 6bfa8a9..0000000 --- a/examples/mdview2/docs/mdview2-plan.md +++ /dev/null @@ -1,164 +0,0 @@ -# mdview2 — план: рендер-кэш вместо живого парсинга на каждый скролл - -Контекст и обоснование — см. обсуждение 2026-06-23 (после оптимизации mdview через -``, см. `mdview-модель-документа-и-рендеринг.md`). Идея: вместо того -чтобы при каждом скролле повторно идти в файл (`fb()`, W3-банкинг) и гонять -markdown inline-парсер (`handle_inline_marker`/emphasis state machine), один раз -отрендерить каждую логическую строку в готовый байтовый буфер и дальше выводить -его на экран напрямую — без парсинга, без обращения к исходному файлу. - -Ключевые подтверждённые факты (эмпирически в MAME, не из документации): - -- **Формат буфера ESTEX `WINCOPY`(59h)/`WINREST`(5Ah)** — пара байт `(char, attr)` - на ячейку, по строкам, шаг строки = `width*2`, без паддинга. Можно генерировать - самим, не вызывая `WINCOPY`. `WINREST` копирует сразу `H` строк одним вызовом. - Детали ABI и регистров — см. `tests/winrest/winrest.c`. -- **Бюджет EMM**: 256 страниц (4 МБ) total, 215 (3440 КБ) free на старте программы. - Файл+индекс в худшем случае (128 КБ файл) съедают 16 страниц (256 КБ) — остаётся - ≈199 страниц (3184 КБ). Кэш всего документа целиком (даже худший случай: - 16384 строк × 160 байт = 2.6 МБ) укладывается без LRU/частичного кэша. - -## Единый формат строки (пересмотрено 2026-06-23) - -Исходно планировалось два типа строк (тип 1 — многоцветные, помещающиеся; -тип 2 — nowrap/code, один стиль, чистый текст для экономии памяти). От этого -деления отказались: строки таблиц (`IF_NOWRAP`, но не `IF_CODE`) всё равно -проходят inline-парсер и могут содержать несколько атрибутов (bold/italic в -ячейках) — предположение "один стиль" для них неверно. Бюджет EMM -([[sprinter_emm_budget]]) с большим запасом покрывает (char,attr)-формат для -ВСЕХ строк без исключения, поэтому усложнение не оправдано. - -**Единый формат**: кэш-запись = `len` пар `(char,attr)` — реальная длина -контента в ячейках, без паддинга до 80, капается на `MAX_CACHE_LINE_LEN`=255. -Вывод: `bios_fillcharattr(' ', base_attr, SCREEN_W)` (очистить строку) → -`win_rest(row, left_margin, 1, len, page)` (контент). Для широких nowrap-строк -горизонтальный скролл (Фаза 5) — это просто смещение НАЧАЛА среза внутри ТОГО -ЖЕ (char,attr)-буфера на `hscroll*2` байт, тот же `win_rest`, без отдельного -плain-текстового формата и без необходимости на лету "разворачивать" текст+ -атрибут в пары. - -## Фазы реализации - -### Фаза 0 — скаффолдинг `examples/mdview2/` [СДЕЛАНО 2026-06-23] -Новая директория со своим `Makefile`/`app.mk` (по аналогии с `mdview/`, не -модифицируем `mdview.c`). `mdview2.c` — копия `mdview.c` без изменений логики -(только usage-строка/заголовок комментария). Собирается чисто, дискета собрана. - -### Фаза 1 — формат кэша и директория строк [СДЕЛАНО и ЗАКРЫТО 2026-06-23] -Реализовано в `mdview2.c`: -- `cache_rec_t` (РОВНО 8 байт: `page`, `off` (uint16_t), `len`, `flags`, - `reserved`, `pad[2]`; размер задаётся `CACHE_DIR_REC_SIZE = sizeof(cache_rec_t)`, - НЕ хардкодом — см. разобранный инцидент ниже) — **отдельная** директория - (`cache_dir_phys[]`/`cache_dir_get`/`cache_dir_put`), своя ёмкость = - `file_pages+1` страниц (как у индекса). **Важное уточнение к исходному тексту - плана ниже**: директория НЕ переиспользует слоты `idx_rec_t` in-place — - рендер-воркер (Фаза 2) при обработке строки N может заглядывать в idx-записи - СОСЕДНИХ строк (откат cont-сегментов, line_idx+1 для границы сегмента); если - бы строка N-1 была перезатёрта сразу после своего рендера, воркер строки N - прочитал бы уже не исходные off/flags, а указатель в кэш — поломав откат. - Бюджет EMM ([[sprinter_emm_budget]]) позволяет отдельный массив — он безопаснее. -- Пул контента — отдельные EMM-страницы (`cache_content_phys[]`), ленивый рост - по 1 странице в `cache_reserve()` (bump-allocator, запись никогда не - разбивается через границу страницы; длина капается на `MAX_CACHE_LINE_LEN`=255 - ячеек). -- `cache_commit()` = один `bank_write()` на строку (буфер строки собирается - локально в W1/W2 заранее, не в W3 — иначе конфликт с `fb()` при чтении - исходника во время рендера). -- `win_rest()` (ESTEX WINREST 5Ah) — вывод готового буфера на экран. -- `phase1_selftest()` — самопроверка (резервирует/коммитит 4 тестовые ячейки, - кладёт запись в директорию по индексу 0, рисует через `win_rest` дважды - подряд с координатами через параметры функции) — убрать в Фазе 2. - -**Подтверждено визуально в MAME (2026-06-23)**: оба квадрата 2×2 на месте, без -единой задержки, с координатами через переменные — Фаза 1 полностью закрыта. - -**Разобранный инцидент (НЕ платформенный квирк)**: по пути долго казалось, что -`win_rest()` после "всплеска" обычных BIOS print-вызовов рисует буфер в -неправильном месте, причём воспроизводилось только когда `row`/`col` приходили -через переменные, а не как константы — это и было ключом. Настоящая причина: -`cache_rec_t` фактически занимал 7 байт (SDCC z80 не паддит структуры), а -`CACHE_DIR_REC_SIZE` был захардкожен как 8 — `bank_read`/`bank_write` копировали -8 байт в 7-байтный буфер на стеке, затирая соседнюю переменную (параметр `col`) -вызывающей функции. Фикс — `cache_rec_t` явно до 8 байт + размер через -`sizeof()`. Полная история и общий урок — [[sprinter_winrest_format]] и -[[defer_unexplained_quirks]]. - -### Фаза 2 — рендер-воркер (бывший `render_line`) [СДЕЛАНО 2026-06-23] -`render_line_to_cache(line_idx)` — адаптация `render_line()`: та же классификация -строк/префиксов/inline-парсинг (`handle_inline_marker`, emphasis state machine, -soft-wrap join) БЕЗ ИЗМЕНЕНИЙ, но вместо `bios_writeattr`/`flush_run`/`wrchar`/ -`bios_fillcharattr` на экран — пишем `(char,attr)` пары через `cc_put`/`cc_fill` -в локальный `cellbuf` (без батчинга через `runbuf`/`flush_run` — тот паттерн был -нужен только чтобы минимизировать число BIOS-вызовов, при записи в локальный -буфер смысла нет, пишем посимвольно сразу по месту), затем ОДНИМ -`cache_reserve()`+`cache_commit()`+`cache_dir_put()` коммитим всю строку. -Кэшируется ПОЛНЫЙ контент строки до `MAX_CACHE_LINE_LEN`, без обрезки по -`SCREEN_W` и без среза по `viewport_x` (view/scroll-time понятия, Фаза 4-5). -Также найден и исправлен по ходу баг в `win_rest()`: `IX` всегда указывал на -начало страницы (`#0xC000`), полностью игнорируя `off` — работало только в -Фазе 1, где в кэше была ровно одна запись со смещением 0; как только Фаза 2 -начала пакетировать много строк на одной странице с разными `off`, все строки -стали читаться с начала страницы. Фикс: `win_rest()` принимает `off` -(uint16_t), `IX = 0xC000 + off` (новая раскладка ABI сверена через `sdcc -S` -с непустым телом — у `__naked` с пустым телом SDCC не генерирует код доступа -к параметрам, нужен пробный non-naked враппер). Подтверждено визуально в MAME. - -### Фаза 3 — фоновый пре-рендер с прогрессом [СДЕЛАНО 2026-06-23] -`emit_seg()` вызывает `render_line_to_cache()` **interleaved** с построением -индекса — но с отставанием на одну строку: рендер строки N требует уже -существующей idx-записи N+1 (источник `seg_end`), которой ещё нет в момент, -когда строка N только создана. Поэтому `emit_seg()` для новой строки рендерит -ПРЕДЫДУЩУЮ (`n_lines-2` после инкремента) — её флаги (`IF_NOWRAP`/`IF_CODE`/ -`IF_BLANK`) к этому моменту уже дописаны вызовом `set_*_cur()` на предыдущей -итерации `index_lines()`. Последнюю строку файла (у которой "следующей" не -будет) дорендеривает сам `index_lines()` после выхода из цикла, как и -`render_line()` делал для последней строки при живом рендере (`seg_end = -file_size`). Требует, чтобы `cache_dir_phys[]` был выделен ДО `index_lines()` — -это уже так (`load_file()` выделяет директорию кэша, затем вызывается -`index_lines()`). - -**Важное сужение скоупа относительно исходного текста плана ниже**: пункты -"спиннер крутится, пока `rendered_up_to < n_lines`" и "скролл ограничен -диапазоном `[0, rendered_up_to]`" **не реализованы и не нужны** в этой -архитектуре — рендеринг происходит СИНХРОННО внутри той же однопроходной -`index_lines()`, без событийного цикла во время загрузки; пользователь -физически не может начать скроллить, пока `index_lines()` не вернёт -управление, а к этому моменту весь документ уже полностью в кэше. Спиннер -из `index_lines()` (каждые 16 строк) сохранён как есть — он покрывает -индексацию+рендер вместе, отдельный прогресс-индикатор не нужен. - -### Фаза 4 — cache-only draw path при скролле -Цикл перерисовки видимой области (после пре-рендера) идёт **только** по -директории строк → `win_rest` (тип 1) или срез текста+`bios_writeattr` (тип 2). -Никаких обращений к `fb()`/исходному файлу в steady-state скролле. - -### Фаза 5 — горизонтальный скролл для широких nowrap-строк -Срез ТОГО ЖЕ (char,attr)-кэш-буфера по текущему `hscroll`-офсету (смещение -начала на `hscroll*2` байт внутри буфера строки), `win_rest(row, col, 1, -visible_len, page)` с новым `off`. Никакого отдельного плоско-текстового -формата не нужно (см. пересмотр "Единый формат строки" выше). - -### Фаза 6 — тестирование в MAME -Тот же набор паттернов, что использовался для mdview1 (заголовки/списки/цитаты/ -code-block/bold-italic/nowrap-обрезка), ПЛЮС: реальный замер EMM на большом -документе (не синтетика — проверить, что бюджет из [[sprinter_emm_budget]] -действительно держится на чём-то близком к 128 КБ); UX пре-рендера (спиннер + -ограничение скролла, отсутствие "дыр" в недорендеренной области); проверка -`win_rest` на РЕАЛЬНОМ отрендеренном контенте (не только синтетический A/B/C/D -тест из `tests/winrest`). - -## Открытые вопросы (решить по ходу, не блокируют старт Фазы 0) - -- ~~Хранение длины строки в директории~~ — решено в Фазе 1: поле `len` (uint8_t) - прямо в `cache_rec_t`. -- ~~Деление строк на тип 1/тип 2~~ — отказались (см. "Единый формат строки" - выше): единый (char,attr)-формат для всех строк, капается на - `MAX_CACHE_LINE_LEN`=255 ячеек (с тем же индикатором обрезки на этапе вывода, - что уже есть в рендере для nowrap-строк). -- Освобождать ли страницы исходного файла после того, как все его строки - отрендерены (вернуть EMM в пул) — даёт больше места про запас, но усложняет - (нужна гарантия, что назад к файлу обращаться больше не придётся — а это не - так, если позже добавится поиск по тексту). Не делать в v1. -- BIOS-вариант `WIN_COPY_WIN`/`WIN_RESTORE_WIN` (0B2h/0B3h, RST 8) не проверен - (см. [[sprinter_winrest_format]]) — ESTEX-варианта достаточно для v1, проверять - BIOS-вариант только если понадобится экономия на RST-диспетчеризации. diff --git a/examples/mdview2/mdview2.c b/examples/mdview2/mdview2.c deleted file mode 100644 index b228b46..0000000 --- a/examples/mdview2/mdview2.c +++ /dev/null @@ -1,1338 +0,0 @@ -/* - * mdview2 — просмотрщик Markdown для Sprinter (render-cache версия). - * - * Этот файл — ЯДРО (инфраструктура + точка входа). Прикладные подсистемы - * вынесены в отдельные модули (общий интерфейс — mdview2.h): - * mdview2_index.c — парсер/индексатор markdown (построение рендер-кэша) - * mdview2_md.c — представление MD-документа (отрисовка из кэша + скролл) - * mdview2_raw.c — RAW-просмотр исходного текста (F2/F3) - * mdview2_enc.c — кодировки CP866/CP1251/KOI8R + UTF-8 конвертер - * mdview2_table.c — выровненная отрисовка таблиц - * mdview2_help.c — диалог справки (F1) - * - * В ядре остаётся: пул рендер-кэша, доступ к EMM/окну W3 (fb/win_rest), - * примитивы экрана, загрузка файла, наборы документа (doc-slots), кооператив- - * ная loading-loop и диспетчер клавиш, сборка наборов и main(). - * - * Разметка экрана (80x32): - * строка 0 — статус-бар (имя файла / диапазон строк / всего / %) - * строки 1..30 — область документа (30 видимых строк текста) - * строка 31 — меню (F1 Help / F2 RAW / F3 Wrap / F8 CodePg / F10 Exit) - * - * Память: - * CODE → W1 (режим sprinter-cc --memory small) - * STACK/HEAP/DATA → W2 - * Буфер файла → страницы EMM (до 8 × 16 КБ = 128 КБ), - * отображаемые в W3 по требованию через fb(). - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mdview2.h" /* геометрия, атрибуты, общий интерфейс модулей */ - -/* ---- Палитра атрибутов ------------------------------------------- */ - -static const uint8_t md_pallete[16 * 4] = { - 0x00, 0x00, 0x00, 0x00, - 0x60, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x00, 0x00, - 0x60, 0x60, 0x00, 0x00, - 0x00, 0x00, 0x60, 0x00, - 0x60, 0x00, 0x60, 0x00, - 0x00, 0x60, 0x60, 0x00, - 0xC8, 0xC8, 0xC8, 0x00, - 0xA0, 0xA0, 0x80, 0x00, - 0xFF, 0xAA, 0xAA, 0x00, - 0xAA, 0xFF, 0xAA, 0x00, - 0xFF, 0xFF, 0xAA, 0x00, - 0xAA, 0xAA, 0xFF, 0x00, - 0xFF, 0xAA, 0xFF, 0x00, - 0xAA, 0xFF, 0xFF, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF -}; - -/* Атрибуты экрана/справки и геометрия — в mdview2.h (общие со всеми модулями). - * Типы записей (idx_rec_t — в mdview2_index.c, cache_rec_t — в mdview2.h), - * словари флагов/стилей (IF_, CK_, INIT_STYLE_) и константы индекса - * (INDEX_RECS_PER_PAGE, MAX_CACHE_LINE_LEN) — в mdview2.h. - * - * В SDCC/z80 критично явно инициализировать static-переменные (`= 0`): - * неинициализированные static-объекты могут конфликтовать по размещению - * и перезаписывать друг друга (см. memory/sdcc_static_storage_gotcha). */ - -#define MAX_INDEX_PAGES 9u /* максимум 9 * 2048 = 18432 сегментов индекса (256 КБ) */ - -/* ================================================================== - * Рендер-кэш: директория и пул EMM-страниц - * ================================================================== - * Каждая логическая строка экрана рендерится РОВНО ОДИН РАЗ (Фаза 2) - * в готовый буфер и больше никогда не парсится повторно при скролле. - * Содержимое живёт в ОТДЕЛЬНОМ пуле EMM-страниц (cache_content_*), - * не путать с idx_rec_t/index_phys[] выше — те остаются неизменными, - * ими пользуется рендер-воркер ПРИ построении кэша (Фаза 2), и их - * нельзя перезатирать, т.к. воркер для одной строки может заглядывать - * в idx-записи СОСЕДНИХ строк (откат cont-сегментов). - * - * ЕДИНЫЙ формат для ВСЕХ строк (решено 2026-06-23 — изначальный план - * различал тип 1/тип 2 по экономии памяти, но строки таблиц — IF_NOWRAP, - * но НЕ IF_CODE — всё равно проходят inline-парсер и могут содержать - * несколько атрибутов, так что "один стиль" для них неверно; бюджет EMM - * (см. sprinter_emm_budget) с большим запасом покрывает (char,attr) - * для всех строк без исключения): контент — `len` пар (char,attr), - * `len` — реальная длина в ЯЧЕЙКАХ без паддинга до SCREEN_W, капается на - * 255 (патологически длинные nowrap-строки урезаются; индикатор обрезки - * добавляется на этапе вывода из кэша, не при рендере в кэш). - */ -/* cache_rec_t (РОВНО 8 байт, см. sprinter_winrest_format) — в mdview2.h. */ - -#define CACHE_DIR_REC_SIZE ((uint16_t)sizeof(cache_rec_t)) -#define CACHE_DIR_RECS_PER_PAGE (16384 / CACHE_DIR_REC_SIZE) /* 2048 */ -#define MAX_CACHE_DIR_PAGES 9u /* симметрично индексу: 9 * 2048 = 18432 записей */ -/* MAX_CACHE_LINE_LEN — в mdview2.h (буфер ячеек g_cells живёт в mdview2_index.c). */ -#define MAX_CACHE_CONTENT_PAGES 40u /* кап контента на набор: 40 * 16 КБ = 640 КБ (80 на оба набора) */ - -static uint8_t cache_dir_blk; -static uint8_t cache_dir_pages; -static uint8_t cache_dir_phys[MAX_CACHE_DIR_PAGES]; - -static uint8_t cache_content_blk[MAX_CACHE_CONTENT_PAGES]; /* id блока на каждую выделенную страницу */ -static uint8_t cache_content_phys[MAX_CACHE_CONTENT_PAGES]; -static uint8_t cache_content_n_pages; /* сколько страниц уже выделено */ -static uint8_t cache_cur_page_idx; /* индекс текущей страницы записи в cache_content_phys */ -static uint16_t cache_cur_off; /* текущее смещение записи в этой странице */ - -/* Доступ к директории рендер-кэша — тот же приём, что у idx_get/idx_put. */ -void cache_dir_get(uint16_t idx, cache_rec_t *r) -{ - uint8_t page = (uint8_t)(idx >> 11); - uint16_t off = (uint16_t)((idx & 0x07FF) << 3); /* 2047 */ - bank_read(cache_dir_phys[page], off, r, CACHE_DIR_REC_SIZE); -} - -void cache_dir_put(uint16_t idx, const cache_rec_t *r) -{ - uint8_t page = (uint8_t)(idx >> 11); - uint16_t off = (uint16_t)((idx & 0x07FF) << 3); /* 2047 */ - bank_write(cache_dir_phys[page], off, r, CACHE_DIR_REC_SIZE); -} - -/* Резервирует nbytes подряд в пуле контента рендер-кэша. - * Запись никогда не разбивается через границу страницы: если остаток - * текущей страницы недостаточен — переходим на следующую целиком, - * жертвуя остатком (проще и безопаснее, чем page-crossing в fb()). - * При нехватке текущего пула — лениво доращиваем его на 1 страницу. - * Возврат: 1 — успех (out_page и out_off заполнены), 0 — EMM кончилась. */ -uint8_t cache_reserve(uint16_t nbytes, uint8_t *out_page, uint16_t *out_off) -{ - if (nbytes == 0 || nbytes > PAGE_SIZE) return 0; - - if (cache_content_n_pages == 0 || - (uint16_t)(cache_cur_off + nbytes) > PAGE_SIZE) { - /* Нужна новая страница: либо пул ещё пуст, либо текущая страница - * не вмещает запись целиком. */ - uint8_t next_idx = (cache_content_n_pages == 0) ? 0 : (uint8_t)(cache_cur_page_idx + 1); - if (next_idx >= cache_content_n_pages) { - if (cache_content_n_pages >= MAX_CACHE_CONTENT_PAGES) - return 0; - uint8_t blk = mem_alloc_pages(1); - if (blk == 0) - return 0; - cache_content_blk[cache_content_n_pages] = blk; - cache_content_phys[cache_content_n_pages] = mem_get_page(blk, 0); - cache_content_n_pages++; - } - cache_cur_page_idx = next_idx; - cache_cur_off = 0; - } - - *out_page = cache_content_phys[cache_cur_page_idx]; - *out_off = cache_cur_off; - cache_cur_off = (uint16_t)(cache_cur_off + nbytes); - return 1; -} - -/* Копирует уже готовый буфер (рендер одной строки, собранный в W1/W2 — - * НЕ в W3, чтобы не конфликтовать с fb()/чтением исходника во время - * рендера) в зарезервированное место в пуле контента. Один bank_write - * на строку, а не по байту. len — РОВНО как у cache_reserve()/bank_write - * — uint16_t, не uint8_t: строка до MAX_CACHE_LINE_LEN=255 ячеек даёт - * до 510 байт (char,attr), это уже не лезет в uint8_t. */ -void cache_commit(uint8_t page, uint16_t off, const void *buf, uint16_t len) -{ - bank_write(page, off, buf, len); -} - -/* emph_to_attr определена ниже (рядом с inline-логикой), нужна в inline_scan. */ -/* emph_to_attr — в mdview2_index.c. */ - -uint16_t n_lines; /* extern: всего строк в индексе текущего набора */ -uint16_t max_lines; /* extern: ёмкость индекса: index_pages * 2048 */ -uint16_t top_line; /* extern: верхняя видимая строка */ -uint32_t file_size; /* extern: размер активного буфера документа */ -static uint8_t file_blk; -static uint8_t file_pages; -static uint8_t file_phys[MAX_PAGES]; -uint8_t index_blk; /* extern: дескриптор EMM-блока индекса */ -uint8_t index_pages; /* */ -uint8_t index_phys[MAX_INDEX_PAGES]; /* extern: физ. страницы индекса */ -uint8_t index_truncated; /* выставляется при исчерпании ёмкости индекса */ -uint8_t g_file_clamped; /* extern: файл >256 КБ обрезан до MAX_FILE при загрузке */ - -/* cur_seg_off/cur_rec (near-состояние индексатора) — в mdview2_index.c. */ -uint8_t cur_page = 0xFF; /* extern: 0xFF = в W3 ещё не показана ни одна страница (extern: кодек инвалидирует) */ -uint8_t viewport_x; /* extern: горизонтальный сдвиг для nowrap-строк */ -char filename[64]; /* extern: статус-бар */ - -/* Прогрессивная загрузка: рисуем первый экран и пускаем скролл, пока - * index_lines() ещё досканирует файл (кооперативно, без прерываний). */ -uint8_t g_view = VIEW_MD; /* extern: активный режим просмотра (существует даже когда доступен только MD режим) */ -#if WITH_RAW -uint8_t g_ready; /* extern: 1 когда первичный документ построен — F2 (RAW) рабочий */ -static uint8_t g_raw_last = VIEW_RAW_WRAP; /* последний под-режим RAW (возврат по F2) */ -#if WITH_HEX -static uint8_t g_hex_last = VIEW_MD; /* вид, из которого вошли в HEX (возврат по F4) */ -#endif -#endif -uint8_t g_loading; /* extern: 1 во время видимой индексации (build_doc) */ -static uint8_t g_first_drawn; /* первый экран уже нарисован */ -uint8_t g_f8_enabled; /* extern: можно ли сейчас переключать кодировку */ -static uint8_t g_utf_failed; /* UTF-8 набор не удалось построить (нет EMM) — пропускаем UTF-8 в цикле F8 */ -uint8_t g_bg_building; /* extern: 1 во время фонового index_step — гасит спиннер индексатора */ -static uint8_t g_bg_doc = 0xFF; /* слот, строящийся в фоне (bg_step); 0xFF = фоновой сборки нет */ -uint8_t g_abort; /* extern: F10/Esc во время загрузки → прервать индексацию */ -static uint16_t g_last_prog; /* n_lines на последнем обновлении статуса */ - -/* Кодировки (ENC_*) и g_encoding — в mdview2.h / mdview2_enc.c. */ -const uint8_t *g_remap; /* extern: активная таблица ремапа (CP1251/KOI8), 0 = без ремапа (CP866)*/ -uint8_t g_scratch_phys; /* extern: EMM-страница под ремап-буфер строки для win_rest */ -static uint8_t g_scratch_blk; /* handle scratch-блока: нужен для освобождения при exit */ - -/* UTF-8 набор (utf_blk/utf_pages/utf_phys/utf_size/utf_avail, конвертер и его - * состояние) — в mdview2_enc.c; разделяемые символы и API — в mdview2.h. - * build_doc() ниже перенаправляет живой file_* на utf_phys и оркеструет - * инкрементальную конвертацию (utf_conv_reset/utf_convert_more). */ - -/* Снимок исходного (8-битного) файлового буфера — набор DOC_8BIT читает его - * как есть, набор DOC_UTF8 читает utf_phys. */ -static uint8_t orig_file_blk; -uint8_t orig_file_pages; /* extern: читает кодек (utf_alloc) */ -uint32_t orig_file_size; /* extern: читает кодек (utf_convert_more) */ -uint8_t orig_file_phys[MAX_PAGES]; /* extern: читает кодек (cv_read) */ - -/* ----------------- Два сосуществующих набора индекс/кэш ----------------- - * DOC_8BIT — исходные байты (CP866/CP1251/KOI8R, различаются ремапом на - * отрисовке); DOC_UTF8 — UTF-8, декодированный в CP866. Оба строятся eager - * при старте (кооперативно), поэтому F8 переключает активный набор мгновенно. - * fb()/idx/cache-аксессоры работают по «живым» глобалам; doc_switch() свапает - * их с сохранённой копией нужного набора. */ -typedef struct { - uint8_t built; - uint8_t enc; /* кодировка отображения этого набора */ - uint8_t file_blk, file_pages; - uint32_t file_size; - uint8_t file_phys[MAX_PAGES]; - uint8_t index_blk, index_pages, index_truncated; - uint16_t n_lines, max_lines; - uint8_t index_phys[MAX_INDEX_PAGES]; - uint8_t cache_dir_blk, cache_dir_pages; - uint8_t cache_dir_phys[MAX_CACHE_DIR_PAGES]; - uint8_t cache_content_n_pages, cache_cur_page_idx; - uint16_t cache_cur_off; - uint8_t cache_content_blk[MAX_CACHE_CONTENT_PAGES]; - uint8_t cache_content_phys[MAX_CACHE_CONTENT_PAGES]; - uint16_t top_line; - uint8_t viewport_x; -} docset_t; - -#define DOC_8BIT 0 -#define DOC_UTF8 1 -static docset_t g_doc[2]; /* built=0 для обоих: _DATA зануляется в crt0 gsinit - * (с 2026-07-04), явный {0}-инициализатор больше - * не нужен — он стоил ~260 байт _INITIALIZER */ -static uint8_t g_active_doc = DOC_8BIT; /* какой набор сейчас в живых глобалах */ -static uint8_t g_disp_doc = DOC_8BIT; /* какой набор показан пользователю */ - -static void doc_switch(uint8_t target); /* forward (нужна в progress_tick) */ - -/* Статус-бар, меню и спиннер — в mdview2_status.c. */ - -/* ================================================================== - * Примитивы экрана, доступ к EMM/окну W3 (fb/win_rest), палитра - * ================================================================== */ - -/* Печатает строку с указанным атрибутом, начиная с позиции (x,y), - * и останавливается на конце строки или правой границе экрана. - * `s` — литерал/статический буфер (CODE/DATA, окна 1/2), поэтому его - * можно передать в BIOS LP_PRINT_LN напрямую, без промежуточной копии. */ -void put_str_attr(uint8_t x, uint8_t y, const char *s, uint8_t attr) -{ - uint8_t len = (x < SCREEN_W) ? (uint8_t)(SCREEN_W - x) : 0; - len = min(len, strlen(s)); - if (len == 0) return; - bios_set_place(y, x); - bios_writeattr_until(s, len, attr, 0); -} - - -/* Полностью очищает указанную строку экрана символами пробела с attr. */ -void fill_row(uint8_t y, uint8_t attr) -{ - bios_clearwin(y, 0, 1, SCREEN_W, attr); -} - -/* Процент 0..100 = num*100/den на чистой 16-битной арифметике — чтобы НЕ - * тянуть 32-битное деление __divulong (~760 Б) ради показа процента в статусе. - * Оба вызова (MD/RAW) дают операнды, влезающие в 16 бит (≤18432 и ≤1024), - * переполняет только *100 — поэтому масштабируем оба вниз, пока num*100 не - * влезёт в uint16, затем считаем долю циклом-вычитанием (без div-хелпера). - * Инвариант вызовов: num ≤ den; результат всё равно ограничен 100. */ -uint8_t pct16(uint16_t num, uint16_t den) -{ - if (den == 0) return 0; - while (num > 655) { num >>= 1; den >>= 1; } /* num*100 ≤ 65500 < 65536 */ - if (den == 0) return 100; /* страховка от num>den */ - uint16_t n = (uint16_t)(num * 100u); - uint8_t p = 0; - while (n >= den && p < 100) { n = (uint16_t)(n - den); p++; } - return p; -} - - -/* Отображает указанную EMM-страницу файла в окно W3. - * Если эта страница уже активна, переключение не выполняется. */ -// static inline void map_page(uint8_t page) -// { -// if (page != cur_page) { -// /* file_phys[] позволяет не вызывать mem_get_page() на каждом свопе. */ -// sprinter_page_w3(file_phys[page]); -// cur_page = page; -// } -// } - - -/* Читает один байт логического файла по смещению p. - * При переходе между страницами автоматически переключает W3. - * Проверка границ выполняется вызывающей стороной. - * - * Эквивалент C-кода (оставлен для справки): - * uint8_t *pb = (uint8_t *)&p; - * uint8_t page = (pb[1] >> 6) | (pb[2] << 2); - * uint16_t off = ((pb[1] & 0x3F) << 8) | pb[0]; - * map_page(page); // if (page!=cur_page){cur_page=page; OUT W3,file_phys[page];} - * return FILE_BUF[off]; // *(0xC000 + off) - * - * Ручной asm — это горячая примитивная операция (вызывается сотни тысяч раз - * при индексации 50КБ-файла); C-версия тратила вызов функции + 32-битную - * арифметику на КАЖДЫЙ байт. Раскладка __sdcccall(1): аргумент uint32_t в - * HLDE (E=pb[0], D=pb[1], L=pb[2], H=pb[3] — проверено sdcc -S), возврат - * char в A. Координация маппинга с bank_read/bank_write держится на том, - * что _io_page_w3 — это ПОРТ (__sfr 0xE2): здесь пишем его через OUT, - * bank_read читает через IN — общий аппаратный регистр, без теневой RAM. - * NB: __naked обязателен (внутри собственный ret); IX/IY не трогаем - * (callee-saved), BC/DE/HL/AF — caller-saved, можно клобберить. */ -char fb(uint32_t p) __naked -{ - (void)p; /* p = HLDE; доступ только из asm */ - __asm - ; off -> BC = 0xC000 + ((pb[1]&0x3F)<<8 | pb[0]) ; D=pb[1], E=pb[0] - ld a, d - or a, #0xC0 ; (D|0xC0) == (D&0x3F)|0xC0 — старший байт окна W3 - ld b, a - ld c, e ; C = pb[0] - - ; page = (pb[1]>>6) | (pb[2]<<2) ; L=pb[2] - ld a, l - add a, a - add a, a ; A = pb[2]<<2 - ld e, a ; E свободен (pb[0] уже сохранён в C) - - ld a, d - rlca - rlca - and a, #0x03 ; A = pb[1]>>6 - or a, e ; A = page - ld e, a ; E = page - - ; map_page(page) - ld a, (_cur_page) - cp e - jr Z, fb_same - ld a, e - ld (_cur_page), a - ld hl, #_file_phys - ld d, #0x00 - add hl, de ; hl = &file_phys[page] - ld a, (hl) - out (#0xE2), a ; OUT PORT_PAGE_W3 (== _io_page_w3 sfr) - fb_same: - ld a, (bc) ; A = *(0xC000 + off) - ret - __endasm; -} - -/* ESTEX WINREST (5Ah, RST 0x10) — копирует готовый буфер (char,attr) - * из смещения `off` ВНУТРИ EMM-страницы `page` прямо в текстовое окно - * экрана, минуя любые BIOS print-вызовы. Формат буфера подтверждён - * эмпирически в MAME (см. memory/sprinter_winrest_format.md и - * tests/winrest/winrest.c): пары (char,attr) по ячейкам, по строкам, - * шаг строки = width*2, без паддинга. row/col — левый верхний угол - * вывода на экране, h/w — высота/ширина в символах. - * - * Регистровая раскладка SDCC __sdcccall(1) для (row,col,h,w,page,off) - * проверена через `sdcc -S` (пробный non-naked враппер): A=row, L=col, - * стек [SP+2]=h [SP+3]=w [SP+4]=page [SP+5..6]=off (little-endian). - * - * История: до 2026-06-23 `off` не было — IX всегда указывал на самое - * начало страницы (#0xC000), это работало только пока в кэше было - * РОВНО ОДНА запись на странице со смещением 0 (Фаза 1 self-test). - * Когда Фаза 2 начала пакетировать много строк на одной странице с - * РАЗНЫМИ off, всё стало читать с начала страницы (т.е. контент первой - * записи) независимо от реальной строки — добавлен off, IX = 0xC000+off. */ -void win_rest(uint8_t row, uint8_t col, uint8_t h, uint8_t w, uint8_t page, uint16_t off) __naked -{ - (void)row; (void)col; (void)h; (void)w; (void)page; (void)off; - __asm - ld iy, #2 - add iy, sp - ld d, a ; D = row - ld e, l ; E = col - push ix ; сохранить IX вызывающей функции (callee-saved) - ld l, 3 (iy) ; off (младший байт), SP+5 - ld h, 4 (iy) ; off (старший байт), SP+6 - ld bc, #0xC000 - add hl, bc - push hl - pop ix ; IX = 0xC000 + off - ld h, 0 (iy) ; H = height - ld a, 1 (iy) ; width -> L - ld l, a - ld a, 2 (iy) ; page -> B - ld b, a - ld c, #0x5A ; ESTEX WINREST - di - rst #0x10 - ei - pop ix ; восстановить IX вызывающей функции - pop hl ; адрес возврата - inc sp - inc sp - inc sp - inc sp - inc sp ; снять h,w,page,off (1+1+1+2 байт) со стека - jp (hl) - __endasm; -} - -/* Char-хелперы, idx_get/idx_put/seg_off, set_*_cur — в mdview2_index.c. */ - -/* Загружает 16 текстовых атрибутов в палитры ink/paper/blink. */ -static void set_pallete(void) { - uint8_t buff[64]; - for(int i = 0; i < 16; i++) { - for(int j = 0; j < 16; j++) { - buff[j * 4] = md_pallete[i * 4]; - buff[j * 4 + 1] = md_pallete[i * 4 + 1]; - buff[j * 4 + 2] = md_pallete[i * 4 + 2]; - buff[j * 4 + 3] = md_pallete[i * 4 + 3]; - } - - text_pal_load(TEXT_PAL_INK, i * 16, 16, md_pallete); - text_pal_load(TEXT_PAL_BLINK_INK, i * 16, 16, md_pallete); - text_pal_load(TEXT_PAL_PAPER, i * 16, 16, buff); - text_pal_load(TEXT_PAL_BLINK_PAPER, i * 16, 8, buff + 32); - text_pal_load(TEXT_PAL_BLINK_PAPER, i * 16 + 8, 8, buff + 32); - } -} - -/* ================================================================== - * Загрузка/выгрузка файла - * ================================================================== */ - -/* Загружает исходные байты файла в EMM-страницы (без индекса/кэша — те - * выделяются отдельно на КАЖДЫЙ набор через alloc_set_storage()). - * Возврат: 0 — успех, отрицательное значение — код ошибки. */ -static int load_file(const char *path) -{ - int fd = open(path, O_RDONLY); - if (fd < 0) - return -1; - - long sz = lseek(fd, 0, SEEK_END); - if (sz < 0) { - close(fd); - return -2; - } - /* Файл больше MAX_FILE (256 КБ) не отвергаем, а КЛАМПим: читаем первые - * 256 КБ, индексатор допишет строку-предупреждение (TRUNC_FILE). */ - g_file_clamped = 0; - if ((uint32_t)sz > MAX_FILE) { - sz = (long)MAX_FILE; - g_file_clamped = 1; - } - - file_size = (uint32_t)sz; - lseek(fd, 0, SEEK_SET); - - /* Выделяем ровно столько EMM-страниц, сколько нужно под файл. */ - file_pages = (uint8_t)((file_size + PAGE_SIZE - 1u) / PAGE_SIZE); - if (file_pages == 0) file_pages = 1; - file_blk = mem_alloc_pages(file_pages); - if (file_blk == 0) { - close(fd); - return -3; - } - - /* Читаем файл кусками по 16 КБ, последовательно маппя страницы в W3. - * На последней итерации читаем остаток меньше PAGE_SIZE. */ - uint32_t remaining = file_size; - for (uint8_t page = 0; page < file_pages; page++) { - spinner_tick(); - uint8_t phys = mem_get_page(file_blk, page); - file_phys[page] = phys; - sprinter_page_w3(phys); - cur_page = page; - uint16_t chunk = (remaining > PAGE_SIZE) - ? (uint16_t)PAGE_SIZE - : (uint16_t)remaining; - int n = read(fd, FILE_BUF, chunk); - if (n != (int)chunk) { - close(fd); - return -4; - } - remaining -= chunk; - } - close(fd); - return 0; -} - -/* Выделяет в живые глобалы блок индекса + директорию рендер-кэша под текущий - * набор. file_pages должно быть уже выставлено (размер набора). Сбрасывает - * пул контента в пустое состояние (страницы доращиваются лениво при рендере). - * Возврат: 0 — успех, -5/-6 — нехватка EMM. */ -static int alloc_set_storage(void) -{ - uint8_t want; - - want = (uint8_t)((file_pages >> 1) + 1u); - if (want > MAX_INDEX_PAGES) want = MAX_INDEX_PAGES; - index_blk = mem_alloc_pages(want); - if (index_blk == 0) return -5; - index_pages = want; - for (uint8_t i = 0; i < index_pages; i++) - index_phys[i] = mem_get_page(index_blk, i); - max_lines = (uint16_t)index_pages * INDEX_RECS_PER_PAGE; - - want = (uint8_t)((file_pages >> 1) + 1u); - if (want > MAX_CACHE_DIR_PAGES) want = MAX_CACHE_DIR_PAGES; - cache_dir_blk = mem_alloc_pages(want); - if (cache_dir_blk == 0) return -6; - cache_dir_pages = want; - for (uint8_t i = 0; i < cache_dir_pages; i++) - cache_dir_phys[i] = mem_get_page(cache_dir_blk, i); - - cache_content_n_pages = 0; - cache_cur_page_idx = 0; - cache_cur_off = 0; - return 0; -} - -static void doc_save_live(uint8_t i); /* forward */ - -/* Освобождает все EMM-блоки обоих наборов (индекс/директория/пул контента) - * плюс два файловых буфера (исходный 8-битный и UTF-8). */ -static void unload_file(void) -{ - doc_save_live(g_active_doc); /* зафиксировать живой набор в g_doc */ - for (uint8_t s = 0; s < 2; s++) { - docset_t *d = &g_doc[s]; - if (d->index_blk) { mem_free_block(d->index_blk); d->index_blk = 0; } - if (d->cache_dir_blk) { mem_free_block(d->cache_dir_blk); d->cache_dir_blk = 0; } - for (uint8_t i = 0; i < d->cache_content_n_pages; i++) - if (d->cache_content_blk[i]) mem_free_block(d->cache_content_blk[i]); - d->cache_content_n_pages = 0; - } - if (orig_file_blk) { mem_free_block(orig_file_blk); orig_file_blk = 0; } - if (utf_blk) { mem_free_block(utf_blk); utf_blk = 0; } - if (g_scratch_blk) { mem_free_block(g_scratch_blk); g_scratch_blk = 0; } - file_blk = 0; -} - -/* Парсер/индексатор (classify_line, scan_join_stream, inline_scan, index_lines, emph_to_attr) — в mdview2_index.c. */ - - -/* Таблицы ремапа, set_encoding, кодировки и UTF-8 конвертер — в mdview2_enc.c. */ - -/* ================================================================== - * Наборы документа (doc-slots): сохранение/свап - * ================================================================== - * Два сосуществующих набора (DOC_8BIT / DOC_UTF8) живут как копии всего - * состояния (file/index/cache/позиция) в g_doc[]; «живые» глобалы — это - * активный набор. doc_save_live: живые → g_doc[i]; doc_load_live: g_doc[i] - * → живые (+ инвалидация маппинга fb() и активной кодировки). */ -static void doc_save_live(uint8_t i) -{ - docset_t *d = &g_doc[i]; - d->enc = g_encoding; - d->file_blk = file_blk; d->file_pages = file_pages; d->file_size = file_size; - memcpy(d->file_phys, file_phys, MAX_PAGES); - d->index_blk = index_blk; d->index_pages = index_pages; d->index_truncated = index_truncated; - d->n_lines = n_lines; d->max_lines = max_lines; - memcpy(d->index_phys, index_phys, MAX_INDEX_PAGES); - d->cache_dir_blk = cache_dir_blk; d->cache_dir_pages = cache_dir_pages; - memcpy(d->cache_dir_phys, cache_dir_phys, MAX_CACHE_DIR_PAGES); - d->cache_content_n_pages = cache_content_n_pages; - d->cache_cur_page_idx = cache_cur_page_idx; d->cache_cur_off = cache_cur_off; - memcpy(d->cache_content_blk, cache_content_blk, MAX_CACHE_CONTENT_PAGES); - memcpy(d->cache_content_phys, cache_content_phys, MAX_CACHE_CONTENT_PAGES); - d->top_line = top_line; d->viewport_x = viewport_x; -} - -static void doc_load_live(uint8_t i) -{ - docset_t *d = &g_doc[i]; - file_blk = d->file_blk; file_pages = d->file_pages; file_size = d->file_size; - memcpy(file_phys, d->file_phys, MAX_PAGES); - index_blk = d->index_blk; index_pages = d->index_pages; index_truncated = d->index_truncated; - n_lines = d->n_lines; max_lines = d->max_lines; - memcpy(index_phys, d->index_phys, MAX_INDEX_PAGES); - cache_dir_blk = d->cache_dir_blk; cache_dir_pages = d->cache_dir_pages; - memcpy(cache_dir_phys, d->cache_dir_phys, MAX_CACHE_DIR_PAGES); - cache_content_n_pages = d->cache_content_n_pages; - cache_cur_page_idx = d->cache_cur_page_idx; cache_cur_off = d->cache_cur_off; - memcpy(cache_content_blk, d->cache_content_blk, MAX_CACHE_CONTENT_PAGES); - memcpy(cache_content_phys, d->cache_content_phys, MAX_CACHE_CONTENT_PAGES); - top_line = d->top_line; viewport_x = d->viewport_x; - cur_page = 0xFF; /* следующий fb() пере-смаппит страницу */ - set_encoding(d->enc); -} - -static void doc_switch(uint8_t target) -{ - if (target == g_active_doc) - return; - doc_save_live(g_active_doc); - doc_load_live(target); - g_active_doc = target; -} - -/* Автоопределение/имя кодировки, ремап-вывод и UTF-8→CP866 конвертер — в mdview2_enc.c. */ - -/* Отрисовка области/статус-бара/меню и прокрутка — в модуле mdview2_md.c. */ - - - -/* ================================================================== - * Прогрессивная загрузка и диспетчер клавиш (MD) - * ================================================================== - * index_lines() строит индекс кооперативно и периодически зовёт - * progress_tick(): тот рисует первый готовый экран, крутит спиннер и - * обрабатывает клавиши на лету (load_key). Диалог справки (F1) — в - * mdview2_help.c, RAW-навигация — в mdview2_raw.c. */ - -/* Обработка клавиши во время прогрессивной загрузки: скролл строго в - * пределах готовых строк (drawable_lines); End = последняя готовая страница; - * F1 = справка (show_help сам блокирует и перерисовывает). */ -static void load_key(uint16_t k) -{ - uint8_t ascii = (uint8_t)(k & 0xFF); - uint8_t scan = (uint8_t)((k >> 8) & 0x7F); - uint16_t dr = drawable_lines(); - uint16_t maxtop = (dr > VIEW_H) ? (uint16_t)(dr - VIEW_H) : 0; - uint16_t nt = top_line; - - if (ascii == 0x1B || scan == KEY_F10) { - g_abort = 1; - return; - } /* Esc/F10 → выход */ - if (ascii) - return; /* прочие ascii игнорируем */ - - switch (scan) { - case KEY_UP: - nt = (top_line >= 1) ? (uint16_t)(top_line - 1) : 0; - break; - case KEY_DOWN: - nt = top_line + 1; - break; - case KEY_PGUP: - nt = (top_line >= VIEW_H) ? (uint16_t)(top_line - VIEW_H) : 0; - break; - case KEY_PGDN: - nt = top_line + VIEW_H; - break; - case KEY_HOME: - top_line = 0; - viewport_x = 0; - draw_viewport_from_cache(); - render_md_status_numbers(); - return; - case KEY_END: - top_line = maxtop; - viewport_x = 0; - draw_viewport_from_cache(); - render_md_status_numbers(); - return; - case KEY_LEFT: - md_scroll_horizon(-(int8_t)HPAN_STEP); - render_md_status_numbers(); - return; - case KEY_RIGHT: - md_scroll_horizon(+(int8_t)HPAN_STEP); - render_md_status_numbers(); - return; - case KEY_F1: - show_help(); - return; - case KEY_F8: - /* во время сборки крутим ТОЛЬКО 8-бит и только если набор 8-битный - * (ремап на отрисовке; индексация не зависит от кодировки) */ - if (g_f8_enabled && g_active_doc == DOC_8BIT) { - uint8_t nx = (g_encoding == ENC_CP866) ? ENC_CP1251 : - (g_encoding == ENC_CP1251) ? ENC_KOI8R : ENC_CP866; - set_encoding(nx); - g_doc[DOC_8BIT].enc = nx; - draw_viewport_from_cache(); - render_full_status(); - } - return; - default: - return; - } - - if (nt > maxtop) - nt = maxtop; - if (nt != top_line) { - top_line = nt; - draw_viewport_from_cache(); - render_md_status_numbers(); - } -} - -/* Кооперативный «тик» прогрессивной загрузки — вызывается из index_lines(). */ -void progress_tick(void) -{ -#if WITH_RAW - /* В RAW-режиме буфер документа готов сразу после конвертации (до индекса), - * поэтому первый экран RAW можно показать немедленно — рисуем raw_draw, а - * не MD-вид (иначе мелькал бы MD). Индекс при этом строится дальше (нужен - * для MD-вида). */ - if (g_view != VIEW_MD) { -#if WITH_HEX - if (g_view == VIEW_HEX) { /* HEX показывает оригинал — экран уже актуален */ - spinner_tick(); - return; - } -#endif - if (!g_first_drawn) { - raw_draw(); - // render_raw_status(); - g_first_drawn = 1; - cur_page = 0xFF; /* raw_draw сбил W3-маппинг, на который опирается fb() */ - } - spinner_tick(); - return; - } -#endif - /* Прогрессивная отрисовка строящегося (всегда показываемого) набора. */ - uint8_t touched = 0; - if (!g_first_drawn && drawable_lines() >= VIEW_H) { - draw_viewport_from_cache(); - render_md_status_numbers(); - g_first_drawn = 1; - touched = 1; - } - if (n_lines >= (uint16_t)(g_last_prog + 10)) { - g_last_prog = n_lines; - spinner_tick(); - if (g_first_drawn) - render_md_status_numbers(); - touched = 1; - } - if (g_first_drawn && kbhit()) { - load_key(getkey()); - touched = 1; - } - /* Отрисовка/WINREST/BIOS могли сбить маппинг W3, на который опирается fb() - * при индексации → форсируем ре-маппинг страницы файла на следующем fb(). */ - if (touched) - cur_page = 0xFF; -} - -/* ================================================================== - * Точка входа - * ================================================================== */ - -/* Строит набор `slot` кооперативно (index_step + progress_tick). - * visible=1 — первичный (показываемый): прогрессивная отрисовка первого экрана - * и скролл по готовым строкам. visible=0 — вторичный: индексация идёт в живых - * глобалах, а progress_tick через свап показывает/листает уже готовый - * g_disp_doc. Возврат: 0 — успех, <0 — нехватка EMM (-5/-6/-7). - * - * Если slot прямо сейчас строится ФОНОМ (g_bg_doc, см. bg_step ниже) — - * не начинаем заново, а ДОКРУЧИВАЕМ: загружаем частично построенный набор - * и продолжаем с сохранённой позиции индексатора (его межшаговое состояние - * живёт в статиках модуля index и переживает свапы наборов). - * - * UTF-8 набор конвертируется ИНКРЕМЕНТАЛЬНО: выделяем страницы, переключаем - * живой буфер на utf и запускаем конвертацию первого чанка; дальше index_step - * докручивает её впереди позиции чтения (g_utf_building). Конвертер читает - * оригинал из orig_file_phys[] через cv_read (не через fb, который теперь - * указывает на utf). */ -/* Настраивает живые глобалы под сборку набора slot: файловый буфер (utf/orig), - * индекс + директория кэша, сброс индексатора. Общая часть build_doc и - * bg_build_start. Возврат: 0 — успех, <0 — нехватка EMM (-5/-6/-7). */ -static int doc_setup(uint8_t slot) -{ - if (slot == DOC_UTF8) { - if (!utf_avail) { /* первый вход: выделить страницы */ - if (!utf_alloc()) - return -7; /* выделяет страницы + сбрасывает конвертер */ - utf_avail = 1; - } - file_blk = utf_blk; - file_pages = utf_pages; - memcpy(file_phys, utf_phys, MAX_PAGES); - if (g_utf_building) { - file_size = 0; - cur_page = 0xFF; - utf_convert_more(CONV_MARGIN); /* первый чанк — для первого экрана */ - } else { - file_size = utf_size; - } - } else { - file_blk = orig_file_blk; - file_pages = orig_file_pages; - file_size = orig_file_size; - memcpy(file_phys, orig_file_phys, MAX_PAGES); - } - cur_page = 0xFF; - - int rc = alloc_set_storage(); - if (rc < 0) - return rc; - index_begin(); - return 0; -} - -static int build_doc(uint8_t slot, uint8_t enc, uint8_t visible) -{ - if (g_bg_doc == slot) { /* фон уже строит его — продолжить */ - doc_load_live(slot); - g_bg_doc = 0xFF; - } else { - int rc = doc_setup(slot); - if (rc < 0) - return rc; - } - - set_encoding(enc); - top_line = 0; - viewport_x = 0; - g_active_doc = slot; - if (visible) { - g_disp_doc = slot; - g_first_drawn = 0; - status_encoding(); /* показать кодировку сразу (детект/F8), не дожидаясь индексации */ - cur_page = 0xFF; /* BIOS-вывод мог сбить W3-маппинг, на который опирается fb() */ - } - - g_loading = 1; - g_last_prog = 0; - while (!index_step(255)) {} - g_loading = 0; - - /* Зафиксировать конвертацию (на случай обрыва индекса по max_lines до конца - * исходника) — иначе g_utf_building остался бы 1 и сломал бы следующую сборку. */ - if (g_utf_building) { - utf_size = file_size; - g_utf_building = 0; - } - - g_doc[slot].built = 1; - doc_save_live(slot); - return 0; -} - -/* ================================================================== - * Фоновая сборка второго набора (idle-time, между клавишами) - * ================================================================== - * Пока пользователь читает документ, главный цикл в паузах между клавишами - * докручивает индексацию второго набора (UTF-8 при 8-битном первичном и - * наоборот) порциями index_step: свап на bg-набор → несколько итераций → - * свап обратно. Экран при этом не трогается (g_bg_building гасит спиннер), - * задержка реакции на клавишу — не больше одного шага. По готовности F8 - * переключает наборы мгновенно; F8 ДО готовности докручивает сборку видимо - * (ветка resume в build_doc), не начиная её заново. */ - -#define BG_STEP_BUDGET 2u /* итераций индексатора на idle-шаг: латентность клавиши - * = длительность шага (~единицы мс на обычном абзаце) */ -#define BG_KEY_HOLDOFF 300u /* пустых опросов kbhit после клавиши до возобновления - * фона — при автоповторе (скролл) фон не вклинивается */ - -/* Готовит фоновую сборку второго набора: настраивает живые глобалы (буфер, - * индекс, кэш, index_begin) так же, как build_doc, но БЕЗ индексации, и - * возвращает живым прежний набор. При нехватке EMM фон просто не стартует — - * остаётся сегодняшний ленивый путь по F8. */ -static void bg_build_start(void) -{ - uint8_t slot = (uint8_t)((g_active_doc == DOC_8BIT) ? DOC_UTF8 : DOC_8BIT); - uint8_t vis = g_active_doc; - if (g_doc[slot].built || (slot == DOC_UTF8 && g_utf_failed)) - return; /* строить нечего */ - - doc_save_live(vis); - if (doc_setup(slot) < 0) { /* нет EMM — как и в ленивом пути */ - if (slot == DOC_UTF8) g_utf_failed = 1; - doc_load_live(vis); - return; - } - top_line = 0; - viewport_x = 0; - - g_active_doc = slot; /* живые глобалы = bg-набор... */ - doc_switch(vis); /* ...сохранить его и вернуть видимый */ - g_bg_doc = slot; -} - -/* Один idle-шаг фона: свап на строящийся набор, порция индексации, свап - * обратно. Межшаговое состояние индексатора/UTF-конвертера — статики их - * модулей, обработчики клавиш видимого набора их не трогают. По завершении - * финализирует набор (дальше F8 переключается мгновенно). */ -static void bg_step(void) -{ - uint8_t vis = g_active_doc; - doc_switch(g_bg_doc); - g_bg_building = 1; - uint8_t done = index_step(BG_STEP_BUDGET); - g_bg_building = 0; - if (done) { - if (g_utf_building) { /* как build_doc: обрыв индекса до конца исходника */ - utf_size = file_size; - g_utf_building = 0; - } - g_doc[g_bg_doc].built = 1; /* doc_save_live поле built не трогает */ - g_bg_doc = 0xFF; - } - doc_switch(vis); -} - -/* Пропорциональный перенос байт-позиции между буферами разного размера - * (8bit ↔ UTF-8 при F8): off * to / from БЕЗ __mullong/__divulong. Один - * цикл restoring-деления: бит k дроби off/from (сравнение с from/2^k) - * сразу добавляет to/2^k в результат — сумма to/2^k по установленным - * битам и есть off*to/from. Точность from/65536 (≤4 байта на максимальном - * файле); ляжет на границу строки через line_at_off / raw_reanchor. */ -static uint32_t map_off(uint32_t off, uint32_t from, uint32_t to) -{ - if (from == to || from == 0) return off; - uint32_t res = 0; - for (uint8_t i = 0; i < 16; i++) { - from >>= 1; - to >>= 1; - if (off >= from) { off -= from; res += to; } - } - return res; -} - -/* Байт-позиция текущего вида (верх экрана) — единая «валюта» для переноса - * позиции между видами/наборами. Валюта = offset АКТИВНОГО буфера; HEX живёт - * в байтах оригинала, для UTF-набора конвертируем через map_off. */ -static uint32_t view_pos(void) -{ -#if WITH_HEX - if (g_view == VIEW_HEX) - return (g_active_doc == DOC_UTF8) - ? map_off(hex_pos(), orig_file_size, file_size) - : hex_pos(); -#endif -#if WITH_RAW - if (g_view != VIEW_MD) return raw_pos(); -#endif - return seg_off(top_line); -} - -/* Поставить текущий вид на байт-позицию off (активного буфера). */ -static void view_reanchor(uint32_t off) -{ -#if WITH_HEX - if (g_view == VIEW_HEX) { - hex_reanchor((g_active_doc == DOC_UTF8) - ? map_off(off, file_size, orig_file_size) - : off); - return; - } -#endif -#if WITH_RAW - if (g_view != VIEW_MD) { raw_reanchor(off); return; } -#endif - top_line = line_at_off(off); -} - -/* Переключает кодировку на `next` (вызывается из главного цикла по F8). - * 8-бит↔8-бит внутри одного набора — мгновенный ремап; переход в другой набор - * — мгновенный doc_switch, если он построен, иначе ЛЕНИВАЯ сборка по месту - * (со спиннером), результат кэшируется. */ -/* Меняет кодировку: тот же набор — ремап; другой готовый — doc_switch; - * непостроенный — ЛЕНИВАЯ ТИХАЯ сборка (спиннер, без отрисовки документа). - * НЕ перерисовывает документ/статус — это делает вызывающий в своём режиме - * (MD: draw_viewport_from_cache; RAW: raw_draw), чтобы не мелькал чужой вид. */ -static void switch_encoding(uint8_t next) -{ - uint8_t target = (next == ENC_UTF8) ? DOC_UTF8 : DOC_8BIT; - - if (target == g_active_doc) { /* тот же набор — только ремап */ - set_encoding(next); - g_doc[target].enc = next; - } else if (g_doc[target].built) { /* набор готов — мгновенный свап */ - /* Позицию переносим пропорцией байтов (буферы наборов разного - * размера), а не из сохранённой в g_doc копии — иначе каждый набор - * скачет по своей старой позиции. */ - uint32_t old_off = view_pos(); - uint32_t old_size = file_size; - doc_switch(target); - g_disp_doc = target; - set_encoding(next); - g_doc[target].enc = next; - view_reanchor(map_off(old_off, old_size, file_size)); - } else { /* строим набор лениво */ - doc_save_live(g_active_doc); /* сохранить текущий (позиция и т.п.) */ -#if WITH_RAW - uint32_t raw_save = raw_pos(); - if (VIEW_IS_RAW(g_view)) /* HEX показывает оригинал — его позиция инвариантна */ - raw_reanchor(0); /* прогрессивный показ нового буфера — с начала */ - g_ready = 0; - render_menu(); /* идёт индексация — прячем F2 */ -#endif - spinner_show(1); - int rc = build_doc(target, next, 1); /* visible: прогрессивный показ первого экрана - * (progress_tick рисует по текущему виду — MD/RAW) */ - spinner_show(0); -#if WITH_RAW - g_ready = 1; - render_menu(); -#endif - if (rc < 0) { /* нет EMM — откат на текущий набор */ - if (target == DOC_UTF8) - g_utf_failed = 1; - doc_load_live(g_active_doc); -#if WITH_RAW - if (VIEW_IS_RAW(g_view)) - raw_reanchor(raw_save); /* буфер прежний — вернуть позицию RAW */ -#endif - return; - } - g_disp_doc = target; /* build_doc уже выставил active/enc */ - } - clamp_top(); /* привести top_line к новому набору (для MD) */ -} - -/* ================================================================== - * Вывод ошибки - * ================================================================== */ - -static void on_exit(void) { - unload_file(); - pal_reset(PAL_CGA); -} - -int main(int argc, char **argv) -{ - const char *path; - if (argc >= 2) { - path = argv[1]; - } else { - path = "README.MD"; - /* Дефолтный файл отсутствует — выводим подсказку без смены режима экрана. */ - { - int chk = open(path, O_RDONLY); - if (chk < 0) { - // settextmode(TEXT_MODE_80x32); - clrscr_attr(ATTR_TEXT); - cputs("MDView v0.3 -- Markdown Viewer for Sprinter\r\n" \ - "(c) 2026 Petrov A.G.\r\n\r\n" \ - "Usage: mdview2 \r\n" \ - " Displays a Markdown document (CP866/CP1251/KOI8R/UTF8, up to 128 KB).\r\n"); - exit(-1); - } - close(chk); - } - } - - atexit(on_exit); - - /* Копируем путь в static filename[] для отображения в статус-баре. */ - { - uint8_t i = 0; - while (i < (uint8_t)(sizeof(filename) - 1) && path[i]) { - filename[i] = path[i]; - i++; - } - filename[i] = 0; - } - - /* Сначала поднимаем UI, чтобы пользователь видел шапку/меню/спиннер - * уже во время загрузки файла и построения индекса. */ - settextmode(TEXT_MODE_80x32); - clrscr_attr(ATTR_TEXT); - set_pallete(); - prerender_status(); - render_menu(); - render_full_status(); - render_percent_progress(0); - spinner_show(1); - - int rc = load_file(path); - if (rc < 0) { - spinner_show(0); - clrscr_attr(ATTR_RESET); - textattr(ATTR_RESET); - cputs("MDView: cannot load file '"); - cputs(path); - cputs("'\n\rError: "); - switch (rc) { - case -1: cputs("open failed"); break; - case -2: cputs("seek failed"); break; - case -3: cputs("mem_alloc_pages failed"); break; - case -4: cputs("short read"); break; - case -5: cputs("index alloc failed"); break; - case -6: cputs("render-cache dir alloc failed"); break; - } - cputs("\n\r\n\r"); - exit(-1); - } - /* Scratch-страница под ремап-буфер строки (win_rest_remap). Handle - * сохраняем отдельно: физический номер страницы нельзя передавать - * mem_free_block(), а on_exit() обязан вернуть EMM системе. */ - g_scratch_blk = mem_alloc_pages(1); - if (g_scratch_blk == 0) { - spinner_show(0); - clrscr_attr(ATTR_RESET); - textattr(ATTR_RESET); - cputs("MDView: scratch page allocation failed\n\r"); - exit(-1); - } - g_scratch_phys = mem_get_page(g_scratch_blk, 0); - - /* Оба набора пока не построены (страховка к инициализатору g_doc). */ - g_doc[DOC_8BIT].built = 0; - g_doc[DOC_UTF8].built = 0; - - /* Снимок исходного 8-битного буфера (его читает набор DOC_8BIT). */ - orig_file_blk = file_blk; orig_file_pages = file_pages; orig_file_size = file_size; - memcpy(orig_file_phys, file_phys, MAX_PAGES); - - /* Детекция кодировки по оригиналу. UTF-8 конвертацию НЕ делаем здесь — - * она дорогая (полный проход) и нужна только для UTF-8 набора, который - * строится через build_doc лениво. Так первый экран рисуется по индексации - * первичного набора без лишнего прохода. */ - uint8_t det = detect_encoding(); - - /* Первичный (показываемый) набор — по детекции; второй строится ЛЕНИВО по - * первому F8 (switch_encoding). Во время сборки 8-битного первичного F8 - * разрешён (ремап 8-бит, см. load_key) → показываем метку F8; для UTF-8 - * первичного переключаться пока некуда → метку прячем. */ - uint8_t prim_enc = det; - uint8_t prim_doc = (prim_enc == ENC_UTF8) ? DOC_UTF8 : DOC_8BIT; - g_f8_enabled = (prim_doc == DOC_8BIT); - render_menu(); - - rc = build_doc(prim_doc, prim_enc, 1); /* первичный — видимый */ - if (rc < 0 && prim_doc == DOC_UTF8) { /* UTF-8 не вышел (нет EMM) → 8-бит */ - prim_doc = DOC_8BIT; prim_enc = ENC_CP866; - rc = build_doc(DOC_8BIT, ENC_CP866, 1); - } - if (rc < 0) { - spinner_show(0); - clrscr_attr(ATTR_RESET); - textattr(ATTR_RESET); - cputs("MDView: index/cache alloc failed\n\r\n\r"); - exit(-1); - } - if (g_abort) - exit(0); /* F10/Esc во время загрузки — корректный выход */ - if (n_lines == 0) { - clrscr_attr(ATTR_RESET); - textattr(ATTR_RESET); - cputs("mdview: empty file"); - exit(-1); - } - - /* Первичный готов — гасим спиннер, включаем F8 (второй набор дособерётся - * лениво по первому переключению) и рисуем финальный статус с ПОЛНЫМ - * счётчиком строк. */ - spinner_show(0); - g_f8_enabled = 1; -#if WITH_RAW - g_ready = 1; /* документ построен — теперь доступен F2 (RAW) */ -#endif - render_menu(); - clamp_top(); - render_full_status(); - draw_viewport_from_cache(); - - /* Второй набор (UTF-8 при 8-битном первичном и наоборот) готовим фоном - * в паузах между клавишами — к первому F8 он обычно уже построен. */ - bg_build_start(); - - for (;;) { - /* Idle-фон: пока второй набор не достроен, вместо блокирующего getkey - * опрашиваем клавиатуру и тратим паузы на bg_step. Холдаун после - * каждой клавиши: при активном скролле (автоповтор) фон молчит и - * плавность не страдает. После достройки — снова блокирующий getkey. */ - if (g_bg_doc != 0xFF) { - uint16_t idle = 0; - while (!kbhit()) { - if (idle < BG_KEY_HOLDOFF) { - idle++; - continue; - } - bg_step(); - if (g_bg_doc == 0xFF) - break; - } - } - uint16_t k = getkey(); - uint8_t ascii = (uint8_t)(k & 0xFF); - uint8_t scan = (uint8_t)((k >> 8) & 0x7F); - - if (ascii) { - if (ascii == 0x1B) - break; /* клавиша Esc */ - continue; - } - if (scan == KEY_F10) { - break; - } - if (scan == KEY_F1) { - show_help(); - continue; - } /* show_help сам перерисует */ - /* F2 — тумблер RAW ↔ MD (из HEX тоже уводит в MD). - * Рабочий только после постройки документа. */ - if (scan == KEY_F2) { - if (!g_ready) continue; - if (g_view == VIEW_MD) { - g_view = g_raw_last; /* → RAW (последний под-режим) */ - raw_seed_from(top_line); /* позиция из MD */ - raw_screen_init(); - status_encoding(); - render_menu(); - render_raw_status(); - raw_draw(); - } else { - uint32_t off = view_pos(); /* позиция из RAW/HEX */ - if (VIEW_IS_RAW(g_view)) - g_raw_last = g_view; /* запомнить под-режим RAW */ - g_view = VIEW_MD; - top_line = line_at_off(off); - clamp_top(); - render_full_status(); render_menu(); - draw_viewport_from_cache(); - } - continue; - } -#if WITH_HEX - /* F4 — тумблер HEX ↔ прежний вид (MD/RAW). */ - if (scan == KEY_F4) { - if (!g_ready) continue; - uint32_t off = view_pos(); /* позиция текущего вида */ - if (g_view == VIEW_HEX) { /* HEX → прежний вид */ - g_view = g_hex_last; - view_reanchor(off); - if (g_view == VIEW_MD) { - clamp_top(); - render_full_status(); render_menu(); - draw_viewport_from_cache(); - } else { - raw_screen_init(); - status_encoding(); render_menu(); - render_raw_status(); raw_draw(); - } - } else { /* MD/RAW → HEX */ - g_hex_last = g_view; - g_view = VIEW_HEX; - view_reanchor(off); /* active → байты оригинала */ - hex_screen_init(); - status_encoding(); render_menu(); - render_hex_status(); hex_draw(); - } - continue; - } -#endif - /* цикл CP866→CP1251→KOI8R→UTF8→CP866; набор дособерётся лениво */ - if (scan == KEY_F8) { - uint8_t next = (g_encoding == ENC_CP866) ? ENC_CP1251 : - (g_encoding == ENC_CP1251) ? ENC_KOI8R : - (g_encoding == ENC_KOI8R) ? ENC_UTF8 : ENC_CP866; - if (next == ENC_UTF8 && g_utf_failed) - next = ENC_CP866; /* UTF-8 недоступен */ - switch_encoding(next); - if (g_abort) - exit(0); /* Esc/F10 во время ленивой досборки */ - /* Перерисовка — по текущему виду (switch_encoding сам не рисует). */ -#if WITH_HEX - if (g_view == VIEW_HEX) { - status_encoding(); - render_hex_status(); - hex_draw(); /* printable-колонка в новой кодировке */ - continue; - } -#endif -#if WITH_RAW - if (g_view != VIEW_MD) { - status_encoding(); - render_raw_status(); - raw_draw(); - continue; - } -#endif - render_full_status(); - draw_viewport_from_cache(); - continue; - } - switch(g_view) { - case VIEW_MD: - /* MD-режим: F1/F8/F10 здесь, навигация — в md_key() (peer к raw_key). */ - md_key(scan); /* навигация MD (сама рисует+статус); незнакомые игнорит */ - continue; -#if WITH_HEX - case VIEW_HEX: - hex_key(scan); /* навигация HEX (сама рисует+процент) */ - continue; -#endif -#if WITH_RAW - case VIEW_RAW_WRAP: - case VIEW_RAW_HSCROLL: - /* RAW-режим: F3 Wrap/Unwrap, своя навигация; F8 — кодировка; F1/F10. */ - /* Wrap ↔ Unwrap */ - if (scan == KEY_F3) { - g_view = (g_view == VIEW_RAW_WRAP) ? VIEW_RAW_HSCROLL : VIEW_RAW_WRAP; - g_raw_last = g_view; - raw_renorm(); - raw_screen_init(); - render_menu(); - render_raw_status(); - raw_draw(); - continue; - } - raw_key(scan); - continue; -#endif /* WITH_RAW */ - default: - break; - } - } - clrscr_attr(ATTR_TEXT); - exit(0); - return 0; -} diff --git a/examples/mdview2/mdview2.h b/examples/mdview2/mdview2.h deleted file mode 100644 index 1c53d6f..0000000 --- a/examples/mdview2/mdview2.h +++ /dev/null @@ -1,304 +0,0 @@ -/* - * mdview2.h — общие определения и интерфейс между модулями mdview2. - * - * Монолит mdview2.c расщеплён на ядро + модули: index (парсер), md/raw (виды), - * enc (кодировки), table, help. Здесь — разделяемые константы экрана/атрибутов, - * словари флагов/стилей, тип cache_rec_t, extern-объявления разделяемого - * состояния ядра и API каждого модуля (сгруппированы по секциям ниже). - */ -#ifndef MDVIEW2_H -#define MDVIEW2_H - -#include -#include /* COLOR(), COLOR_* */ -#include "mdview2_conf.h" /* WITH_RAW / WITH_HEX — выбор опциональных модулей */ - -/* ---- Геометрия экрана -------------------------------------------- */ -#define SCREEN_W 80 -#define SCREEN_H 32 -#define VIEW_TOP_ROW 1 -#define VIEW_H 30 /* видимая область: строки 1..30 включительно */ -#define MENU_ROW 31 -#define TAB_STOP 4 -#define HPAN_STEP 8u /* шаг горизонтального сдвига (←/→) */ - -/* ---- Геометрия статус-бара (строка 0): фиксированные поля и разделители ---- */ -#define SPINNER_COL 8 /* слот спиннера загрузки */ -#define DIV1_X 45 /* разделитель │ перед числами диапазона строк */ -#define DIV2_X 71 /* разделитель │ перед процентом */ -/* Числа/метка пишутся с DIVn_X+2; область между разделителями — [DIV1_X+1 .. DIV2_X-1]. */ - -/* ---- Параметры файла и памяти ------------------------------------ */ -#define PAGE_BITS 14u -#define PAGE_SIZE (1u << PAGE_BITS) /* размер EMM-страницы: 16 КБ */ -#define PAGE_MASK ((uint16_t)(PAGE_SIZE - 1u)) -#define MAX_PAGES 16 /* 16 страниц × 16 КБ = 256 КБ */ -#define MAX_FILE ((uint32_t)MAX_PAGES * PAGE_SIZE) -#define FILE_BUF ((char *)0xC000) /* окно W3, куда мапится текущая EMM-страница */ - -/* ---- Палитра атрибутов ------------------------------------------- */ -#define ATTR_RESET COLOR(COLOR_LIGHTGRAY, COLOR_BLACK) -#define ATTR_TEXT COLOR(COLOR_LIGHTGRAY, COLOR_BLUE) -#define ATTR_TEXT_TITLE1 COLOR(COLOR_YELLOW, COLOR_BLUE) -#define ATTR_TEXT_TITLE2 COLOR(COLOR_LIGHTBLUE, COLOR_BLUE) -#define ATTR_TEXT_TITLE3 COLOR(COLOR_LIGHTGREEN, COLOR_BLUE) -#define ATTR_TEXT_TITLE4 COLOR(COLOR_LIGHTGREEN, COLOR_BLUE) -#define ATTR_TEXT_BOLD COLOR(COLOR_LIGHTRED, COLOR_BLUE) -#define ATTR_TEXT_ITALIC COLOR(COLOR_LIGHTGREEN, COLOR_BLUE) -#define ATTR_TEXT_UNDERSORE COLOR(COLOR_LIGHTMAGENTA, COLOR_BLUE) -#define ATTR_TEXT_CODE COLOR(COLOR_WHITE, COLOR_BLUE) -#define ATTR_TEXT_STRIKE COLOR(COLOR_DARKGRAY, COLOR_BLUE) -#define ATTR_LIST_MARKER COLOR(COLOR_LIGHTCYAN, COLOR_BLUE) -#define ATTR_QUOTE_MARKER COLOR(COLOR_CYAN, COLOR_BLUE) -#define ATTR_HR COLOR(COLOR_CYAN, COLOR_BLUE) -#define ATTR_BOX COLOR(COLOR_CYAN, COLOR_BLUE) -#define ATTR_TRUNC COLOR(COLOR_YELLOW, COLOR_BLUE) -#define ATTR_BAR COLOR(COLOR_BLACK, COLOR_LIGHTCYAN) -#define ATTR_BAR_SPINNER COLOR(COLOR_WHITE, COLOR_LIGHTCYAN) -#define ATTR_MENU_T COLOR(COLOR_BLACK, COLOR_LIGHTCYAN) -#define ATTR_MENU_K COLOR(COLOR_YELLOW, COLOR_BLACK) -#define ATTR_WARN COLOR(COLOR_YELLOW, COLOR_RED) /* строка-обрыв (исчерпан кэш/лимит) */ - -/* ---- Атрибуты диалога справки ------------------------------------ */ -#define ATTR_HELP_BG COLOR(COLOR_LIGHTGRAY, COLOR_BLACK) -#define ATTR_HELP_BDR COLOR(COLOR_WHITE, COLOR_BLACK) -#define ATTR_HELP_TIT COLOR(COLOR_YELLOW, COLOR_BLACK) -#define ATTR_HELP_HDR COLOR(COLOR_WHITE, COLOR_BLACK) - -/* ---- Режимы просмотра -------------------------------------------- */ -#define VIEW_MD 0 /* markdown с форматированием (по умолчанию) */ -#define VIEW_RAW_WRAP 1 /* RAW: длинные строки переносятся кратно 80 */ -#define VIEW_RAW_HSCROLL 2 /* RAW: длинные строки в одну, гориз. скролл */ -#define VIEW_HEX 3 /* HEX-дамп оригинального файла */ -#define VIEW_IS_RAW(v) ((v) == VIEW_RAW_WRAP || (v) == VIEW_RAW_HSCROLL) - -/* ---- Кодировки --------------------------------------------------- */ -/* 8-битные (CP866/CP1251/KOI8R) различаются только ремапом глифов [128-255] - * на отрисовке; UTF-8 — отдельный декодированный набор. */ -#define ENC_CP866 0 -#define ENC_CP1251 1 -#define ENC_KOI8R 2 -#define ENC_UTF8 3 -#define ENC_UNSUPPORTED -1 /* UTF16 / UTF32 */ -#define CONV_MARGIN 4096u /* на сколько байт держать UTF-конвертацию впереди индексатора */ - -/* ---- Флаги сегмента индекса (IF_*) — общие для индексатора/кэша/вью ---- */ -#define IF_CONT 0x01u /* сегмент является продолжением перенесённой строки */ -#define IF_NOWRAP 0x02u /* строка не переносится (кодовый блок / HR / таблица) */ -#define IF_BLANK 0x04u /* визуально пустая строка */ -#define IF_CODE 0x08u /* тело fenced code-блока (verbatim-режим) */ -#define IF_HSCROLL 0x10u /* блок горизонтально скроллируется ЦЕЛИКОМ (код/таблица), - * включая строки короче 80 — двигаются как единый блок. - * НЕ ставится на HR и границы fence (им нечего прятать). */ -#define IF_TRUNC_MSG 0x20u /* строка-обрыв: рисуется ВЖИВУЮ фикс. текстом (без контент-кэша); - * cache_rec.reserved несёт код причины (TRUNC_*). */ - -/* ---- Причина обрыва индексации (cache_rec.reserved при IF_TRUNC_MSG) ---- */ -#define TRUNC_CONTENT 1 /* исчерпан контент-кэш (MAX_CACHE_CONTENT_PAGES) */ -#define TRUNC_LINES 2 /* исчерпана ёмкость индекса (max_lines) */ -#define TRUNC_FILE 3 /* файл больше MAX_FILE — прочитаны первые 256 КБ */ - -/* ---- Словарь сегментов/стилей (общий для индексатора и таблиц) ---- */ -/* Тип сегмента-продолжения, передаваемый в emit_seg()/inline_scan(). */ -#define CK_PLAIN 0 -#define CK_QUOTE 1 -#define CK_LIST 2 -#define CK_OTHER 3 - -/* Начальный inline-стиль сегмента (INIT_STYLE_*). */ -#define INIT_STYLE_PLAIN 0x0 -#define INIT_STYLE_BOLD 0x1 -#define INIT_STYLE_ITALIC 0x2 -#define INIT_STYLE_UNDER 0x3 -#define INIT_STYLE_CODE 0x4 -#define INIT_STYLE_STRIKE 0x5 - -/* ---- Таблицы: CP866 light box-drawing (общий для индексатора и модуля) ---- */ -#define TBL_MAX_COLS 16 -#define TBL_ATTR ATTR_BOX -#define TBL_H 0xC4 /* ─ */ -#define TBL_V 0xB3 /* │ */ -#define TBL_TL 0xDA /* ┌ */ -#define TBL_TM 0xC2 /* ┬ */ -#define TBL_TR 0xBF /* ┐ */ -#define TBL_ML 0xC3 /* ├ */ -#define TBL_MM 0xC5 /* ┼ */ -#define TBL_MR 0xB4 /* ┤ */ -#define TBL_BL 0xC0 /* └ */ -#define TBL_BM 0xC1 /* ┴ */ -#define TBL_BR 0xD9 /* ┘ */ - -/* ================================================================== * - * Разделяемые символы mdview2.c, используемые модулем RAW. - * ================================================================== */ -extern uint8_t g_view; /* активный режим просмотра (VIEW_*) */ -extern uint32_t file_size; /* размер активного буфера документа */ -extern const uint8_t *g_remap; /* таблица ремапа [128-255] (0 = нет) */ -extern uint8_t g_scratch_phys; /* scratch EMM-страница для win_rest */ - -char fb(uint32_t p); /* байт активного буфера (W3-маппинг) */ -void win_rest(uint8_t row, uint8_t col, uint8_t h, uint8_t w, uint8_t page, uint16_t off); -void fill_row(uint8_t y, uint8_t attr); -uint8_t pct16(uint16_t num, uint16_t den); /* num*100/den (0..100), 16-бит, без __divulong */ -uint32_t seg_off(uint16_t idx); /* offset исходника для логической строки idx */ -uint16_t line_at_off(uint32_t off); /* обратно: последняя строка с seg_off <= off */ - -/* ================================================================== * - * API модуля RAW (mdview2_raw.c). - * ================================================================== */ -void raw_seed_from(uint16_t md_top_line); /* вход в RAW: позиция по top_line */ -uint32_t raw_pos(void); /* текущий байт-offset верха экрана RAW */ -void raw_reanchor(uint32_t off); /* поставить RAW на строку с байтом off */ -void raw_draw(void); /* перерисовать область документа в RAW */ -uint8_t raw_pct(void); /* % прокрутки по байтам (для render_raw_status_numbers) */ -uint8_t raw_key(uint8_t scan); /* навигация RAW; 1 = обработано */ -void raw_screen_init(void); /* очистка экрана с нужным аттрибутом */ -void raw_renorm(void); /* выровнять позицию при смене под-режима (F3) */ - -/* ================================================================== * - * API модуля HEX (mdview2_hex.c) — дамп ОРИГИНАЛЬНОГО файла (F4). - * Позиция hex_pos/hex_reanchor — в байтах ОРИГИНАЛА (orig_file_*); - * конвертацию в/из активного буфера делает ядро (map_off). - * ================================================================== */ -uint32_t hex_pos(void); /* offset верхнего ряда (кратен 16) */ -void hex_reanchor(uint32_t orig_off); /* поставить на ряд с байтом orig_off */ -void hex_draw(void); /* перерисовать область документа */ -uint8_t hex_key(uint8_t scan); /* навигация HEX; 1 = обработано */ -void hex_screen_init(void); /* очистка экрана */ - -/* ================================================================== * - * Запись директории рендер-кэша (одна на видимую строку, РОВНО 8 байт — - * cache_dir_get/put адресуют сдвигом idx<<3). Контент — len пар (char,attr). - * ================================================================== */ -typedef struct cache_rec_s { - uint8_t page; /* EMM-страница рендер-кэша (физический номер) */ - uint16_t off; /* смещение в странице, 0..16383 (байты, не ячейки) */ - uint8_t len; /* длина контента в ЯЧЕЙКАХ (char,attr пар), 0..255; 0 = пустая/HR */ - uint8_t flags; /* копия IF_NOWRAP/IF_CODE/IF_BLANK на момент рендера */ - uint8_t reserved; /* не используется */ - uint8_t pad[2]; /* явный резерв, добивка до 8 байт */ -} cache_rec_t; - -void cache_dir_get(uint16_t idx, cache_rec_t *r); /* читатель директории кэша (ядро) */ -void put_str_attr(uint8_t x, uint8_t y, const char *s, uint8_t attr); /* печать строки с атрибутом */ - -/* ================================================================== * - * Состояние навигации/представления (определено в mdview2.c). - * ================================================================== */ -extern uint16_t n_lines; /* всего строк в индексе текущего набора */ -extern uint16_t top_line; /* верхняя видимая строка */ -extern uint8_t viewport_x; /* горизонтальный сдвиг (nowrap-строки) */ -extern char filename[]; /* имя файла для статус-бара */ -extern uint8_t g_loading; /* 1 во время index_lines() */ -extern uint8_t g_ready; /* 1 когда первичный документ построен (F2 RAW) */ -extern uint8_t g_f8_enabled; /* можно ли сейчас переключать кодировку */ - -/* ================================================================== * - * Статус-бар (строка 0), меню (строка 31), спиннер — mdview2_status.c. - * Атомарные части: prerender (фикс. хром, один раз) / encoding (поле кодировки) - * / numbers (диапазон строк + %) / menu / spinner — обновляются по отдельности. - * ================================================================== */ -void prerender_status(void); /* фикс. часть: фон, разделители, MDVIEW, имя файла (1 раз) */ -void status_encoding(void); /* только поле кодировки (col 37) */ -void render_md_status_numbers(void); /* числа MD: диапазон строк + % */ -void render_raw_status(void); /* RAW mode: метка режима */ -void render_hex_status(void); /* HEX mode: метка режима */ -void render_full_status(void); /* encoding + numbers (MD) */ -void render_menu(void); /* строка меню (по смене режима/готовности) */ -uint16_t drawable_lines(void); /* число строк, готовых к показу */ -void spinner_tick(void); /* кадр спиннера загрузки */ -void spinner_show(uint8_t on); /* вкл/выкл спиннер */ -void render_percent_progress(uint8_t pct); /* показать процентный прогресс просмотра */ - -/* ================================================================== * - * Отрисовка области MD-документа и прокрутка (mdview2_md.c). - * ================================================================== */ -void draw_viewport_from_cache(void); /* перерисовать область документа из кэша */ -void clamp_top(void); /* привести top_line к диапазону */ -void md_scroll_up(uint16_t n); -void md_scroll_down(uint16_t n); -void md_scroll_horizon(int8_t delta); /* горизонтальный сдвиг nowrap-блоков */ -uint8_t md_key(uint8_t scan); /* навигация MD (после загрузки); 1 = обработано */ - -/* ================================================================== * - * API модуля справки (mdview2_help.c). - * ================================================================== */ -void show_help(void); /* модальный диалог F1 */ - -/* ================================================================== * - * Парсер/индексатор (mdview2_index.c) и разделяемое с ним состояние. - * Хранилище индекса (index_blk/pages/phys, n_lines/max_lines) — в ядре - * (аллокация/doc-slot), индексатор пишет в него; cache_*-писатели и - * progress_tick ядро экспортирует индексатору (spinner_tick — из status). - * ================================================================== */ -#define INDEX_RECS_PER_PAGE 2048u /* 16384 / 8; запись всегда в одной странице */ -#define MAX_CACHE_LINE_LEN 255u /* кап длины контента строки в ячейках (буфер g_cells) */ - -extern uint16_t max_lines; /* ёмкость индекса: index_pages * 2048 */ -extern uint8_t index_blk, index_pages; /* дескриптор EMM-блока индекса */ -extern uint8_t index_phys[]; /* физ. страницы индекса */ -extern uint8_t index_truncated; /* индекс упёрся в ёмкость */ -extern uint8_t g_abort; /* F10/Esc во время загрузки → прервать индексацию */ -extern uint8_t g_file_clamped; /* файл был обрезан до MAX_FILE при загрузке (>256 КБ) */ - -uint8_t cache_reserve(uint16_t nbytes, uint8_t *out_page, uint16_t *out_off); -void cache_commit(uint8_t page, uint16_t off, const void *buf, uint16_t len); -void cache_dir_put(uint16_t idx, const cache_rec_t *r); -void progress_tick(void); /* кооперативный шаг loading-loop (рисует/листает) */ - -/* Резюмируемая индексация (для фоновой сборки второго набора): begin сбрасывает - * проход, step выполняет до budget итераций (блоков исходника) и возвращает 1, - * когда индекс финализирован. Межшаговое состояние — статики модуля index; - * оно не входит в docset_t (см. bg_step ядра). */ -void index_begin(void); -uint8_t index_step(uint8_t budget); -extern uint8_t g_bg_building; /* 1 = фоновый шаг индексации: не трогать экран (спиннер) */ - -/* ================================================================== * - * Буфер ячеек рендера (mdview2.c) — общий с модулем таблиц. - * Таблицы эмитят свои строки теми же примитивами, что и индексатор. - * ================================================================== */ -extern uint8_t g_ncells; /* ячеек в текущем сегменте */ -void gc_put(char ch, uint8_t attr); /* добавить ячейку в g_cells */ -void gc_fill(char ch, uint8_t attr, uint8_t n); /* n одинаковых ячеек */ -uint8_t inline_scan(uint32_t q, uint32_t q_end, uint8_t col, - uint8_t ckind, uint8_t line_style, uint8_t base_attr, - uint8_t nowrap); - -/* ================================================================== * - * API модуля таблиц (mdview2_table.c) — вызывается из index_lines(). - * ================================================================== */ -uint32_t row_end(uint32_t p); /* offset завершающего '\n' (или file_size) */ -uint32_t table_first_cell(uint32_t row_start); /* контент первой ячейки строки */ -uint8_t table_next_cell(uint32_t *pp, uint32_t lineend, uint32_t *cs, uint32_t *ce); -uint8_t table_is_sep_row(uint32_t row_start, uint32_t lineend); -void table_border(const uint8_t *widths, uint8_t ncols, char left, char mid, char right); -void table_data_row(uint32_t row_start, uint32_t lineend, const uint8_t *widths, uint8_t ncols); - -/* ================================================================== * - * Кодировки и UTF-8 конвертация (mdview2_enc.c). - * Состояние кодека владеется модулем; build_doc()/index_lines() ядра - * оркестрируют его (привязка UTF-буфера к живому file_*, сборка наборов). - * ================================================================== */ -extern uint8_t g_encoding; /* активная кодировка (ENC_*) */ -extern uint8_t g_utf_building; /* 1 пока UTF-конвертация не дошла до конца исходника */ -extern uint8_t utf_avail; /* 1 = страницы UTF-8 выделены */ -extern uint8_t utf_blk, utf_pages; /* дескриптор EMM-буфера UTF-набора */ -extern uint8_t utf_phys[]; /* физ. страницы UTF-буфера */ -extern uint32_t utf_size; /* размер сконвертированного UTF-потока */ - -/* Ядро-owned состояние, читаемое кодеком (определено в mdview2.c). */ -extern uint8_t cur_page; /* текущая страница файла в W3 (инвалидация маппинга) */ -extern uint8_t orig_file_phys[]; /* физ. страницы снимка исходного файла */ -extern uint8_t orig_file_pages; -extern uint32_t orig_file_size; - -void set_encoding(uint8_t enc); /* активная кодировка + таблица ремапа */ -const char *enc_name(uint8_t enc); /* короткое имя для статус-бара */ -uint8_t detect_encoding(void); /* автоопределение по сэмплу байт */ -void win_rest_remap(uint8_t row, uint8_t w, uint8_t page, uint16_t off); /* вывод среза с ремапом */ -void utf_convert_more(uint32_t target); /* инкрементальная конвертация до target байт */ -uint8_t utf_alloc(void); /* выделить EMM-страницы под UTF-набор; 0 = нет EMM */ -uint8_t utf_cp_glyph(uint32_t cp); /* кодпойнт → один CP866-глиф ('?' без аналога) */ - -#endif /* MDVIEW2_H */ diff --git a/examples/mdview2/mdview2_conf.h b/examples/mdview2/mdview2_conf.h deleted file mode 100644 index 330505e..0000000 --- a/examples/mdview2/mdview2_conf.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * mdview2_conf.h — конфигурация сборки mdview2: какие опциональные модули - * просмотра включать. 1 = включить (код входит в бинарь), 0 = исключить - * полностью (нулевой расход кода/памяти). - * - * Модули обёрнуты в #if WITH_xxx целиком, поэтому файлы можно держать в - * сборке всегда (при 0 они компилируются в пустой объектник) — менять - * нужно только эти define. - */ -#ifndef MDVIEW2_CONF_H -#define MDVIEW2_CONF_H - -#define WITH_RAW 1 /* RAW-просмотр исходника (F2): mdview2_raw.c */ -#define WITH_HEX 1 /* HEX-дамп оригинального файла (F4): mdview2_hex.c */ - -#endif /* MDVIEW2_CONF_H */ diff --git a/examples/mdview2/mdview2_enc.c b/examples/mdview2/mdview2_enc.c deleted file mode 100644 index 14d0ad9..0000000 --- a/examples/mdview2/mdview2_enc.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * mdview2_enc.c — кодировки и конвертация UTF-8 → CP866. - * - * Две связанные подсистемы: - * 1. 8-битные кодировки (CP866/CP1251/KOI8-R) — различаются только ремапом - * старших байтов [128-255] на ОТРИСОВКЕ (g_remap, win_rest_remap). - * Автоопределение — detect_encoding по частоте ходовых букв. - * 2. UTF-8 — отдельный набор: исходник инкрементально декодируется в - * utf_phys[] как чистый CP866 (utf_convert_more), после чего весь - * конвейер индексации/кэша работает по нему без ремапа. - * - * Владеет состоянием кодека (conv-буфер, позиция конвертации, UTF-страницы); - * описание разделяемых символов и API — в mdview2.h. Оркестрацией (привязка - * UTF-буфера к живому file_*, сборка наборов) занимается build_doc() ядра. - */ - -#include -#include /* COLOR()/COLOR_* для ATTR_* в attr_is_content */ -#include /* sprinter_page_w3 */ -#include /* bank_read/bank_write/mem_alloc_pages/mem_get_page */ -#include "mdview2.h" - -/* ---- Состояние кодировки/UTF (владелец — этот модуль) ------------- */ -uint8_t g_encoding = ENC_CP866; /* активная кодировка */ - -uint8_t utf_blk; -uint8_t utf_pages; -uint8_t utf_phys[MAX_PAGES]; -uint32_t utf_size; -uint8_t utf_avail; /* 1 = страницы UTF-8 выделены */ -uint8_t g_utf_building; /* 1 пока конвертация не дошла до конца исходника */ - -/* Приватное состояние конвертера (наружу не торчит — сброс через - * utf_conv_reset(), чтение оригинала через cv_read). */ -static uint32_t utf_src; /* позиция чтения ОРИГИНАЛА (utf-8) */ -static uint8_t cv_page = 0xFF; /* orig-страница в W3 для cv_read (своя от cur_page) */ - -/* Таблицы ремапа СТАРШИХ байтов [128-255] в CP866. Индексируются (ch-0x80): - * младшие 128 (ASCII) — identity, в ремапе не участвуют (win_rest_remap - * трогает только ch>=0x80), поэтому в таблицах их нет. Кириллица + ходовая - * пунктуация; неизвестное → '?' (0x3F). Сгенерированы Python codecs. */ -static const uint8_t cp1251_to_866[128] = { - /* 80 */ 0x3F, 0x3F, 0x27, 0x3F, 0x22, 0x2E, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3C, 0x3F, 0x3F, 0x3F, 0x3F, - /* 90 */ 0x3F, 0x27, 0x27, 0x22, 0x22, 0x07, 0x2D, 0x2D, 0x3F, 0x3F, 0x3F, 0x3E, 0x3F, 0x3F, 0x3F, 0x3F, - /* A0 */ 0xFF, 0xF6, 0xF7, 0x3F, 0xFD, 0x3F, 0x3F, 0x3F, 0xF0, 0x63, 0xF2, 0x3C, 0x3F, 0x3F, 0x72, 0xF4, - /* B0 */ 0xF8, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xFA, 0xF1, 0xFC, 0xF3, 0x3E, 0x3F, 0x3F, 0x3F, 0xF5, - /* C0 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, - /* D0 */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, - /* E0 */ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, - /* F0 */ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, -}; -static const uint8_t koi8r_to_866[128] = { - /* 80 */ 0xC4, 0xB3, 0xDA, 0xBF, 0xC0, 0xD9, 0xC3, 0xB4, 0xC2, 0xC1, 0xC5, 0xDF, 0xDC, 0xDB, 0xDD, 0xDE, - /* 90 */ 0xB0, 0xB1, 0xB2, 0x3F, 0xFE, 0xF9, 0xFB, 0x3F, 0x3F, 0x3F, 0xFF, 0x3F, 0xF8, 0x3F, 0xFA, 0x3F, - /* A0 */ 0xCD, 0xBA, 0xD5, 0xF1, 0xD6, 0xC9, 0xB8, 0xB7, 0xBB, 0xD4, 0xD3, 0xC8, 0xBE, 0xBD, 0xBC, 0xC6, - /* B0 */ 0xC7, 0xCC, 0xB5, 0xF0, 0xB6, 0xB9, 0xD1, 0xD2, 0xCB, 0xCF, 0xD0, 0xCA, 0xD8, 0xD7, 0xCE, 0x63, - /* C0 */ 0xEE, 0xA0, 0xA1, 0xE6, 0xA4, 0xA5, 0xE4, 0xA3, 0xE5, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, - /* D0 */ 0xAF, 0xEF, 0xE0, 0xE1, 0xE2, 0xE3, 0xA6, 0xA2, 0xEC, 0xEB, 0xA7, 0xE8, 0xED, 0xE9, 0xE7, 0xEA, - /* E0 */ 0x9E, 0x80, 0x81, 0x96, 0x84, 0x85, 0x94, 0x83, 0x95, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, - /* F0 */ 0x8F, 0x9F, 0x90, 0x91, 0x92, 0x93, 0x86, 0x82, 0x9C, 0x9B, 0x87, 0x98, 0x9D, 0x99, 0x97, 0x9A, -}; - -/* Устанавливает активную кодировку и таблицу ремапа (для 8-битных). */ -void set_encoding(uint8_t enc) -{ - g_encoding = enc; - g_remap = (enc == ENC_CP1251) ? cp1251_to_866 : - (enc == ENC_KOI8R) ? koi8r_to_866 : (const uint8_t *)0; -} - -/* Самые ходовые строчные русские буквы (о е а и н т с р в л) — их байты - * различают 8-битные кодировки по частоте. */ -// static const uint8_t common866 [10] = {0xAE,0xA5,0xA0,0xA8,0xAD,0xE2,0xE1,0xE0,0xA2,0xAB}; -// static const uint8_t common1251[10] = {0xEE,0xE5,0xE0,0xE8,0xED,0xF2,0xF1,0xF0,0xE2,0xEB}; -// static const uint8_t commonkoi [10] = {0xCF,0xC5,0xC1,0xC9,0xCE,0xD4,0xD3,0xD2,0xD7,0xCC}; -// static uint8_t in_set10(const uint8_t *s, uint8_t b) -// { -// for (uint8_t i = 0; i < 10; i++) if (s[i] == b) return 1; -// return 0; -// } - -/* Проверяем только пять самых популярных символов (о е а и н) */ -static const uint8_t common866 [5] = {0xAE,0xA5,0xA0,0xA8,0xAD}; -static const uint8_t common1251[5] = {0xEE,0xE5,0xE0,0xE8,0xED}; -static const uint8_t commonkoi [5] = {0xCF,0xC5,0xC1,0xC9,0xCE}; -static uint8_t in_set10(const uint8_t *s, uint8_t b) -{ - for (uint8_t i = 0; i < 5; i++) if (s[i] == b) return 1; - return 0; -} - -/* Автоопределение кодировки дешёвым сканом байтов (до построения индекса). - * BOM → UTF8; иначе валидность UTF-8 (структура multibyte); иначе 8-бит по - * частоте ходовых букв; фолбэк CP866. */ -uint8_t detect_encoding(void) -{ - if (file_size >= 3 && (uint8_t)fb(0) == 0xEF && (uint8_t)fb(1) == 0xBB && (uint8_t)fb(2) == 0xBF) - return ENC_UTF8; - - uint32_t n = file_size; - if (n > 1024u) n = 1024u; /* сэмпл: первый 1 КБ — детекции хватает */ - uint8_t utf_ok = 1, has_mb = 0, has_high = 0, cont = 0; - uint16_t s866 = 0, s1251 = 0, skoi = 0; - - for (uint32_t p = 0; p < n; p++) { - uint8_t b = (uint8_t)fb(p); - if (b < 0x80) { if (cont) { utf_ok = 0; cont = 0; } continue; } - has_high = 1; - if (in_set10(common866, b)) s866++; - if (in_set10(common1251, b)) s1251++; - if (in_set10(commonkoi, b)) skoi++; - if (cont) { - if ((b & 0xC0) == 0x80) cont--; - else { utf_ok = 0; cont = 0; } - } - else if (b >= 0xC2 && b <= 0xDF) { cont = 1; has_mb = 1; } - else if (b >= 0xE0 && b <= 0xEF) { cont = 2; has_mb = 1; } - else if (b >= 0xF0 && b <= 0xF4) { cont = 3; has_mb = 1; } - else utf_ok = 0; /* битый лид/одиночный континюэйшн */ - } - /* Незавершённая multibyte-последовательность на КОНЦЕ — нарушение только - * если это настоящий EOF; на границе сэмпла (n= s866 && s1251 >= skoi) return ENC_CP1251; - if (skoi >= s866) return ENC_KOI8R; - return ENC_CP866; -} - -const char *enc_name(uint8_t enc) -{ - return (enc == ENC_CP1251) ? "CP1251" : - (enc == ENC_KOI8R) ? "KOI8-R" : - (enc == ENC_UTF8) ? "UTF-8 " : "CP866 "; -} - -/* Контентный глиф (ремапим при смене кодировки) vs структурный (рамка/HR/ - * маркеры — уже CP866, не трогаем). Различаем по attr. */ -static uint8_t attr_is_content(uint8_t a) -{ - return (uint8_t)(a != ATTR_BOX && a != ATTR_HR && - a != ATTR_LIST_MARKER && a != ATTR_QUOTE_MARKER); -} - -/* Вывод среза строки кэша с ремапом контентных глифов [128-255] через - * g_remap (CP1251/KOI8). Без активной таблицы — прямой win_rest. */ -void win_rest_remap(uint8_t row, uint8_t w, uint8_t page, uint16_t off) -{ - if (!g_remap || w == 0) { - win_rest(row, 0, 1, w, page, off); - return; - } - uint8_t buf[SCREEN_W * 2]; - bank_read(page, off, buf, (uint16_t)w * 2u); - for (uint8_t i = 0; i < w; i++) { - uint8_t ch = buf[(uint16_t)i * 2u]; - if (ch >= 0x80 && attr_is_content(buf[(uint16_t)i * 2u + 1u])) - buf[(uint16_t)i * 2u] = g_remap[ch - 0x80]; /* таблицы хранят только старшие 128 */ - } - bank_write(g_scratch_phys, 0, buf, (uint16_t)w * 2u); - win_rest(row, 0, 1, w, g_scratch_phys, 0); -} - -/* ========================= UTF-8 → CP866 (Фаза 2) ======================== */ - -/* Кириллица U+0400..U+045F → CP866. Русский набор + Ё/ё и часть украинских - * (Є є Ї ї Ў ў), которые есть в CP866; пропуски → '?' (0x3F). */ -static const uint8_t utf_cyr_to_866[96] = { - /* 0400 */ 0x3F, 0xF0, 0x3F, 0x3F, 0xF2, 0x3F, 0x3F, 0xF4, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xF6, 0x3F, - /* 0410 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, - /* 0420 */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, - /* 0430 */ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, - /* 0440 */ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - /* 0450 */ 0x3F, 0xF1, 0x3F, 0x3F, 0xF3, 0x3F, 0x3F, 0xF5, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xF7, 0x3F, -}; - -/* Поток вывода конвертера: пишем в utf_phys чанками по странице, буфер в - * near-памяти, флаш через один bank_write (не побайтовый swap W3). */ -static uint8_t conv_buf[256]; -static uint16_t conv_n; /* байт в буфере, ещё не сброшено */ -static uint8_t conv_page; /* индекс текущей страницы в utf_phys */ -static uint16_t conv_off; /* уже сброшенное смещение в этой странице */ - -static void conv_flush(void) -{ - if (conv_n) { - bank_write(utf_phys[conv_page], conv_off, conv_buf, conv_n); - conv_off = (uint16_t)(conv_off + conv_n); - conv_n = 0; - } -} - -/* Добавляет один CP866-байт в выходной поток. Запись никогда не пересекает - * границу страницы (256 делит 16384 нацело). */ -static void conv_put(uint8_t b) -{ - if ((uint16_t)(conv_off + conv_n) == PAGE_SIZE) { /* страница заполнена */ - conv_flush(); - conv_page++; - conv_off = 0; - } - conv_buf[conv_n++] = b; - if (conv_n == (uint16_t)sizeof(conv_buf)) conv_flush(); -} - -/* Подстановки одиночных не-кириллических символов UTF-8 → один CP866-байт. - * Таблица вместо switch (экономия кода); линейный поиск дёшев — вызывается - * лишь при конвертации на редких символьных кодпойнтах. Чтобы добавить новый - * символ — достаточно дописать одну строку {кодпойнт, байт-CP866}. - * (Символы с НЕ-1:1 заменой, напр. … -> "...", обрабатываются отдельно ниже.) */ -typedef struct { uint16_t utf8; uint8_t cp866; } utf_sym_t; -static const utf_sym_t utf_sym[] = { - {0x00A0, 0x20}, {0x00AB, 0x3C}, {0x00BB, 0x3E}, /* nbsp « » → space < > */ - {0x00B0, 0xF8}, {0x00B7, 0xFA}, /* ° · */ - {0x2013, 0x2D}, {0x2014, 0x2D}, /* – — → '-' */ - {0x2018, 0x27}, {0x2019, 0x27}, /* ‘ ’ → '\'' */ - {0x201C, 0x22}, {0x201D, 0x22}, /* “ ” → '"' */ - {0x2022, 0xF9}, {0x2116, 0xFC}, /* • → ∙ № */ - {0x2190, 0x1B}, {0x2191, 0x18}, {0x2192, 0x1A}, {0x2193, 0x19}, /* ← ↑ → ↓ */ - {0x2500, 0xC4}, {0x2502, 0xB3}, {0x250C, 0xDA}, {0x2510, 0xBF}, /* ─ │ ┌ ┐ */ - {0x2514, 0xC0}, {0x2518, 0xD9}, {0x251C, 0xC3}, {0x2524, 0xB4}, /* └ ┘ ├ ┤ */ - {0x252C, 0xC2}, {0x2534, 0xC1}, {0x253C, 0xC5}, /* ┬ ┴ ┼ */ - {0x2580, 0xDF}, {0x2584, 0xDC}, {0x2588, 0xDB}, /* ▀ ▄ █ */ - {0x2591, 0xB0}, {0x2592, 0xB1}, {0x2593, 0xB2}, {0x25A0, 0xFE}, /* ░ ▒ ▓ ■ */ - {0x2713, 0xFB}, {0x2714, 0xFB}, /* ✓ ✔ → галка */ - {0x2715, 0x78}, {0x2717, 0x78}, {0x2718, 0x78}, /* ✕ ✗ ✘ → 'x' */ -}; -#define UTF_SYM_N (uint8_t)(sizeof(utf_sym) / sizeof(utf_sym[0])) - -/* Кодпойнт → ОДИН CP866-глиф: кириллица по таблице, ходовые символы по - * utf_sym[], прочее → '?'. Общая для конвертера и HEX-printable (F4). */ -uint8_t utf_cp_glyph(uint32_t cp) -{ - if (cp < 0x80) return (uint8_t)cp; - if (cp >= 0x0400 && cp <= 0x045F) return utf_cyr_to_866[cp - 0x0400]; - for (uint8_t i = 0; i < UTF_SYM_N; i++) - if (utf_sym[i].utf8 == (uint16_t)cp) return utf_sym[i].cp866; - return '?'; -} - -/* Кодпойнт → CP866-глиф(ы) в поток конвертера. Спецслучаи с НЕ-1:1 заменой - * (… → "...", BOM → ничего) здесь; остальное через utf_cp_glyph(). */ -static void conv_emit_cp(uint32_t cp) -{ - if (cp == 0x2026) { conv_put('.'); conv_put('.'); conv_put('.'); return; } /* … → "..." */ - if (cp == 0xFEFF) return; /* BOM/ZWNBSP — выкинуть */ - conv_put(utf_cp_glyph(cp)); -} - -/* Последовательное чтение байта ОРИГИНАЛА (utf-8) конвертером. Оригинал лежит - * в orig_file_phys[]; мапим его страницу в W3 напрямую, со своей кэш-переменной - * cv_page (отдельной от cur_page индексатора, т.к. оба используют W3 и - * чередуются). На границе с fb()-чтением utf-буфера кэши взаимно сбрасываются. */ -static uint8_t cv_read(uint32_t s) -{ - uint8_t pg = (uint8_t)(s >> PAGE_BITS); - if (pg != cv_page) { sprinter_page_w3(orig_file_phys[pg]); cv_page = pg; } - return *((volatile uint8_t *)(0xC000u + (uint16_t)(s & PAGE_MASK))); -} - -/* Возобновляемая конвертация: дописывает utf_phys из оригинала, пока - * сконвертированных (flushed) байт меньше target и не достигнут конец - * исходника. По выходу file_size = доступная (сконвертированная) часть; при - * достижении конца фиксирует utf_size и снимает g_utf_building. */ -void utf_convert_more(uint32_t target) -{ - cv_page = 0xFF; /* W3 был на utf (fb индексатора) — пере-смаппим orig */ - uint32_t n = orig_file_size; - while (utf_src < n && - (uint32_t)((uint32_t)conv_page * PAGE_SIZE + conv_off) < target) { - uint8_t b = (uint8_t)cv_read(utf_src++); - if (b < 0x80) { - conv_emit_cp(b); - continue; - } - - uint32_t cp; - uint8_t need; - if ((b & 0xE0) == 0xC0) { cp = (uint32_t)(b & 0x1F); need = 1; } - else if ((b & 0xF0) == 0xE0) { cp = (uint32_t)(b & 0x0F); need = 2; } - else if ((b & 0xF8) == 0xF0) { cp = (uint32_t)(b & 0x07); need = 3; } - else { conv_put('?'); continue; } - - uint8_t ok = 1; - for (uint8_t k = 0; k < need; k++) { - if (utf_src >= n) { ok = 0; break; } - uint8_t cb = (uint8_t)cv_read(utf_src); - if ((cb & 0xC0) != 0x80) { ok = 0; break; } - cp = (cp << 6) | (uint32_t)(cb & 0x3F); - utf_src++; - } - if (!ok || need == 3) { conv_put('?'); continue; } - conv_emit_cp(cp); - } - conv_flush(); - file_size = (uint32_t)conv_page * PAGE_SIZE + conv_off; /* доступно индексатору */ - if (utf_src >= n) { utf_size = file_size; g_utf_building = 0; } - cur_page = 0xFF; /* W3 был на orig — fb индексатора пере-смаппит utf */ -} - -/* Выделяет страницы под UTF-8 набор (конвертированный ≤ оригинала по размеру) - * и сбрасывает приватное состояние конвертера в начало (conv-буфер, utf_src, - * g_utf_building). Сама конвертация — инкрементальная, через utf_convert_more(). - * Возврат: 1 — успех, 0 — нет EMM. */ -uint8_t utf_alloc(void) -{ - utf_pages = orig_file_pages; - utf_blk = mem_alloc_pages(utf_pages); - if (utf_blk == 0) return 0; - for (uint8_t i = 0; i < utf_pages; i++) - utf_phys[i] = mem_get_page(utf_blk, i); - conv_n = 0; conv_page = 0; conv_off = 0; /* converter reset (вызывается один раз) */ - utf_src = 0; - g_utf_building = 1; - return 1; -} diff --git a/examples/mdview2/mdview2_help.c b/examples/mdview2/mdview2_help.c deleted file mode 100644 index 3ea3b5c..0000000 --- a/examples/mdview2/mdview2_help.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * mdview2_help.c — диалог справки (F1). - * - * Модальное окно поверх документа: рамка с заголовком, список горячих - * клавиш и поддерживаемых markdown-элементов. Блокирует до нажатия любой - * клавиши, после чего восстанавливает статус-бар, область документа и меню. - * - * Зависимости от ядра (mdview2.c): render_full_status / draw_viewport_from_cache - * / render_menu для восстановления экрана. Текст справки — в CP866. - */ - -#include -#include -#include -#include -#include "mdview2.h" - -/* Геометрия диалога справки (в символьных координатах 80×32). */ -#define HELP_X 8u /* левая граница рамки */ -#define HELP_Y 3u /* верхняя граница рамки */ -#define HELP_W 64u /* ширина рамки (включая │) */ -#define HELP_H 26u /* высота рамки (включая ─) */ - -/* Заполняет одну внутреннюю строку диалога (r=0 — первая строка за рамкой). - * Строка s в кодировке CP866; остаток до края дополняется пробелами. */ -static void help_line(uint8_t r, const char *s, uint8_t attr) -{ - uint8_t x = HELP_X + 1u; - uint8_t y = HELP_Y + 1u + r; - uint8_t len = (uint8_t)strlen(s); - - if (len > HELP_W - 2u) - len = HELP_W - 2u; - - bios_set_place(y, x); - if (len > 0) - bios_writeattr(s, len, attr); - /* place уже продвинут bios_writeattr на len колонок (verified). */ - if (len < HELP_W - 2u) { - bios_fillcharattr(' ', ATTR_HELP_BG, (uint8_t)(HELP_W - 2u - len)); - } -} - -void show_help(void) -{ - /* " Помощь " в CP866 (8 байт) */ - static const char title[] = " \x8F\xAE\xAC\xAE\xE9\xEC "; - uint8_t tlen = 8u; - uint8_t lft = (uint8_t)((HELP_W - 2u - tlen) / 2u); /* = 27 */ - uint8_t rgt = (uint8_t)(HELP_W - 2u - tlen - lft); - /* Верхняя граница рамки с заголовком по центру. - * place продвигается каждым BIOS-вызовом (verified), поэтому - * достаточно одной установки места на всю строку рамки. */ - wrchar(HELP_X, HELP_Y, 0xDA, ATTR_HELP_BDR); /* ┌ */ - bios_set_place(HELP_Y, (uint8_t)(HELP_X + 1u)); - if (lft > 0) bios_fillcharattr(0xC4, ATTR_HELP_BDR, lft); - if (tlen > 0) bios_writeattr(title, tlen, ATTR_HELP_TIT); - if (rgt > 0) bios_fillcharattr(0xC4, ATTR_HELP_BDR, rgt); - wrchar(HELP_X + HELP_W - 1u, HELP_Y, 0xBF, ATTR_HELP_BDR); /* ┐ */ - - /* Боковые границы (левый и правый │ для каждой строки тела) */ - for (uint8_t r = 1u; r < HELP_H - 1u; r++) { - wrchar(HELP_X, HELP_Y + r, 0xB3, ATTR_HELP_BDR); /* │ */ - wrchar(HELP_X + HELP_W - 1u, HELP_Y + r, 0xB3, ATTR_HELP_BDR); /* │ */ - } - - /* Нижняя граница рамки */ - wrchar(HELP_X, HELP_Y + HELP_H - 1u, 0xC0, ATTR_HELP_BDR); /* └ */ - bios_set_place((uint8_t)(HELP_Y + HELP_H - 1u), (uint8_t)(HELP_X + 1u)); - bios_fillcharattr(0xC4, ATTR_HELP_BDR, (uint8_t)(HELP_W - 2u)); - wrchar(HELP_X + HELP_W - 1u, HELP_Y + HELP_H - 1u, 0xD9, ATTR_HELP_BDR); /* ┘ */ - - /* Содержимое (20 внутренних строк) */ - uint8_t r = 0; - help_line(r++, "", ATTR_HELP_BG); - help_line(r++, " MDView v1.0 (b3) -- Markdown Viewer for Sprinter", ATTR_HELP_HDR); - help_line(r++, " (c) 2026 \x8F\xA5\xE2\xE0\xAE\xA2 \x80\x2E\x83\x2E", - ATTR_HELP_BG); - help_line(r++, "", ATTR_HELP_BG); - help_line(r++, " Navigation:", ATTR_HELP_HDR); - help_line(r++, " \x18 \x19 Scroll one line up / down", ATTR_HELP_BG); - help_line(r++, " PgUp PgDn Scroll one page up / down", ATTR_HELP_BG); - help_line(r++, " Home End Jump to beginning / end of document", ATTR_HELP_BG); - help_line(r++, " \x1B \x1A Horizontal pan (code blocks/tables/unwrap)", ATTR_HELP_BG); - help_line(r++, " Esc F10 Exit", ATTR_HELP_BG); - help_line(r++, "", ATTR_HELP_BG); - help_line(r++, " Markdown elements:", ATTR_HELP_HDR); - help_line(r++, " # ## ### Headings H1-H6, **bold**, *italic*,", ATTR_HELP_BG); - help_line(r++, " `code`, ``` code ```, ~~strike~~, > quote", ATTR_HELP_BG); - help_line(r++, " - * + 1. 2. Ordered list, |----|----| Tables", ATTR_HELP_BG); - help_line(r++, "", ATTR_HELP_BG); - help_line(r++, " Encoding and View modes:", ATTR_HELP_HDR); - help_line(r++, " F2 RAW Mode", ATTR_HELP_BG); - help_line(r++, " F3 Wrap/Unwrap Mode for RAW View", ATTR_HELP_BG); - help_line(r++, " F4 HEX Mode (dump of original file)", ATTR_HELP_BG); - help_line(r++, " F8 Cycle CP866 / CP1251 / KOI8-R / UTF-8", ATTR_HELP_BG); - help_line(r++, " Auto-detected on open; F8 to override", ATTR_HELP_BG); - help_line(r++, "", ATTR_HELP_BG); - help_line(r++, " File size: up to 256 KB (EMM). Lines: up to 18432.", ATTR_HELP_BG); - - (void)getkey(); - - render_full_status(); - draw_viewport_from_cache(); - render_menu(); -} diff --git a/examples/mdview2/mdview2_hex.c b/examples/mdview2/mdview2_hex.c deleted file mode 100644 index c1eb72e..0000000 --- a/examples/mdview2/mdview2_hex.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * mdview2_hex.c — HEX-дамп ОРИГИНАЛЬНОГО файла (F4). - * - * Показывает исходные байты файла (orig_file_phys), а не активный буфер: - * hex-дамп декодированного UTF-набора был бы искажением. Printable-колонка - * интерпретируется ТЕКУЩЕЙ кодировкой: CP866 как есть, CP1251/KOI8 через - * g_remap, UTF-8 — глиф на позиции лид-байта (continuation-байты → '.'). - * - * Формат ряда (79 колонок, один атрибут ATTR_TEXT — как RAW): - * 0x012340 │ XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX │ 16 символов - * - * Ряд выровнен на 16 байт и никогда не пересекает границу EMM-страницы - * (16 делит 16384 нацело) — один bank_read на ряд, fb()/W3-маппинг не нужны. - * Позиция (hex_top) живёт в байтах ОРИГИНАЛА; конвертацию в/из байт-позиции - * активного буфера делает ядро (view_pos/view_reanchor через map_off). - */ - -#include -#include -#include -#include -#include /* bank_read (чтение оригинала по страницам EMM) */ -#include "mdview2.h" - -#if WITH_HEX /* весь модуль опционален (mdview2_conf.h); при 0 пустой объектник */ - -#define HEX_BPR 16u /* байт на ряд */ -#define HEX_SPAN ((uint32_t)(VIEW_H - 1) * HEX_BPR) - -/* Геометрия ряда (колонки). */ -#define HX_OFF 1u /* "0x012340" */ -#define HX_SEP1 10u /* │ */ -#define HX_DUMP 12u /* пары hex-цифр */ -#define HX_SEP2 61u /* │ */ -#define HX_PRINT 63u /* printable */ - -static uint32_t hex_top; /* offset верхнего ряда (кратен 16) */ - -uint32_t hex_pos(void) { return hex_top; } - -/* Максимальный hex_top: последний ряд файла на нижней строке экрана. */ -static uint32_t hex_max_top(void) -{ - uint32_t last = (orig_file_size == 0) ? 0 - : ((orig_file_size - 1u) & ~(uint32_t)(HEX_BPR - 1u)); - return (last > HEX_SPAN) ? (last - HEX_SPAN) : 0; -} - -void hex_reanchor(uint32_t orig_off) -{ - uint32_t mt = hex_max_top(); - orig_off &= ~(uint32_t)(HEX_BPR - 1u); - hex_top = (orig_off > mt) ? mt : orig_off; -} - -void hex_screen_init(void) -{ - bios_clearwin(VIEW_TOP_ROW, 0, VIEW_H, SCREEN_W, ATTR_TEXT); -} - -/* ---- отрисовка ----------------------------------------------------- */ - -static uint8_t hexdig(uint8_t v) -{ - v &= 15u; - return (uint8_t)((v < 10u) ? ('0' + v) : ('A' - 10 + v)); -} - -/* Один байт оригинала (для UTF-lookahead за границей ряда — редкий путь). */ -static uint8_t fbo(uint32_t off) -{ - uint8_t b; - bank_read(orig_file_phys[off >> PAGE_BITS], (uint16_t)(off & PAGE_MASK), &b, 1); - return b; -} - -/* Printable-глиф для n байт ряда b[] (off — offset b[0] в файле) → out[]. */ -static void hex_print_cells(const uint8_t *b, uint32_t off, uint8_t n, uint8_t *out) -{ - for (uint8_t i = 0; i < n; i++) { - uint8_t c = b[i]; - uint8_t g; - if (c < 0x20u) { - g = '.'; - } else if (c < 0x80u || g_encoding != ENC_UTF8) { - g = (uint8_t)((c >= 0x80u && g_remap) ? g_remap[c - 0x80u] : c); - } else if ((c & 0xC0u) == 0x80u) { - g = '.'; /* continuation-байт */ - } else { - /* Лид-байт: собрать кодпойнт (хвост может выйти за ряд → fbo). - * 4-байтовые (за пределами CP866) и битые последовательности → '.'. */ - uint32_t cp; - uint8_t need, ok = 1; - if ((c & 0xE0u) == 0xC0u) { cp = (uint32_t)(c & 0x1Fu); need = 1; } - else if ((c & 0xF0u) == 0xE0u) { cp = (uint32_t)(c & 0x0Fu); need = 2; } - else { out[i] = '.'; continue; } - for (uint8_t k = 1; k <= need; k++) { - uint32_t p = off + i + k; - if (p >= orig_file_size) { ok = 0; break; } - uint8_t cb = (uint8_t)((i + k < n) ? b[i + k] : fbo(p)); - if ((cb & 0xC0u) != 0x80u) { ok = 0; break; } - cp = (cp << 6) | (uint32_t)(cb & 0x3Fu); - } - g = ok ? utf_cp_glyph(cp) : '.'; - if (g < 0x20u) g = '.'; /* overlong мог дать управляющий */ - } - out[i] = g; - } -} - -static uint8_t hex_line[SCREEN_W]; /* собранный ряд (только символы, единый attr) */ - -static void hex_draw_row(uint32_t off, uint8_t row) -{ - uint8_t buf[HEX_BPR]; - uint8_t n = 0; - - memset(hex_line, ' ', SCREEN_W); - if (off < orig_file_size) { - uint32_t rem = orig_file_size - off; - n = (rem >= HEX_BPR) ? (uint8_t)HEX_BPR : (uint8_t)rem; - bank_read(orig_file_phys[off >> PAGE_BITS], (uint16_t)(off & PAGE_MASK), buf, n); - } - if (n) { /* ряды за EOF — пустые */ - hex_line[HX_OFF] = '0'; - hex_line[HX_OFF + 1] = 'x'; - hex_line[HX_OFF + 2] = hexdig((uint8_t)(off >> 20)); - hex_line[HX_OFF + 3] = hexdig((uint8_t)(off >> 16)); - hex_line[HX_OFF + 4] = hexdig((uint8_t)(off >> 12)); - hex_line[HX_OFF + 5] = hexdig((uint8_t)(off >> 8)); - hex_line[HX_OFF + 6] = hexdig((uint8_t)(off >> 4)); - hex_line[HX_OFF + 7] = hexdig((uint8_t)off); - hex_line[HX_SEP1] = TBL_V; - for (uint8_t i = 0; i < n; i++) { - uint8_t x = (uint8_t)(HX_DUMP + i * 3u + ((i >= 8u) ? 1u : 0u)); - hex_line[x] = hexdig((uint8_t)(buf[i] >> 4)); - hex_line[x + 1] = hexdig(buf[i]); - } - hex_line[HX_SEP2] = TBL_V; - hex_print_cells(buf, off, n, hex_line + HX_PRINT); - } - bios_set_place(row, 0); - bios_write_until((const char *)hex_line, SCREEN_W, 0); -} - -void hex_draw(void) -{ - uint32_t off = hex_top; - for (uint8_t r = 0; r < VIEW_H; r++) { - hex_draw_row(off, (uint8_t)(VIEW_TOP_ROW + r)); - off += HEX_BPR; - } -} - -/* ---- статус-бар: % по байтам (как calc_raw_pct) --------------------- */ - -static uint8_t calc_hex_pct(void) -{ - return pct16((uint16_t)(hex_top >> 8), (uint16_t)(orig_file_size >> 8)); -} - -/* ---- навигация (вызывается из главного цикла при g_view == VIEW_HEX) */ - -uint8_t hex_key(uint8_t scan) -{ - uint32_t mt = hex_max_top(); - switch (scan) { - /* Одна строка — аппаратный скролл окна + подрисовка одного ряда - * (как в MD/RAW), без перерисовки всего экрана. */ - case KEY_DOWN: - if (hex_top < mt) { - hex_top += HEX_BPR; - scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 1, 0); /* содержимое вверх */ - hex_draw_row(hex_top + HEX_SPAN, (uint8_t)(VIEW_TOP_ROW + VIEW_H - 1)); - } - break; - case KEY_UP: - if (hex_top) { - hex_top -= HEX_BPR; - scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 2, 0); /* содержимое вниз */ - hex_draw_row(hex_top, VIEW_TOP_ROW); - } - break; - case KEY_PGDN: { - uint32_t nt = hex_top + (uint32_t)VIEW_H * HEX_BPR; - if (nt > mt) nt = mt; - if (nt != hex_top) { hex_top = nt; hex_draw(); } - break; - } - case KEY_PGUP: { - uint32_t d = (uint32_t)VIEW_H * HEX_BPR; - uint32_t nt = (hex_top >= d) ? hex_top - d : 0; - if (nt != hex_top) { hex_top = nt; hex_draw(); } - break; - } - case KEY_HOME: - if (hex_top) { hex_top = 0; hex_draw(); } - break; - case KEY_END: - if (hex_top != mt) { hex_top = mt; hex_draw(); } - break; - default: - return 0; /* не наша клавиша */ - } - render_percent_progress(calc_hex_pct()); - return 1; -} - -#endif /* WITH_HEX */ diff --git a/examples/mdview2/mdview2_index.c b/examples/mdview2/mdview2_index.c deleted file mode 100644 index 2740fbb..0000000 --- a/examples/mdview2/mdview2_index.c +++ /dev/null @@ -1,1031 +0,0 @@ -/* - * mdview2_index.c — парсер/индексатор markdown (сердце приложения). - * - * Единственный проход по исходному файлу (index_begin/index_step): классификация строк - * (classify_line, is_*_raw), сборка абзацев с переносом (scan_join_stream), - * inline-разметка (inline_marker/inline_scan) и эмиссия готовых (char,attr)- - * ячеек в рендер-кэш (gc_put → seg_commit_cells → cache_*). Ведёт индекс - * сегментов в EMM (idx_get/idx_put) и докручивает UTF-конвертацию впереди - * позиции чтения. Разделяемые символы и API — в mdview2.h. - * - * Приватно (не торчит в .h): idx_rec_t, cur_rec/cur_seg_off, буфер ячеек - * g_cells, idx_get/idx_put и все классификаторы/сканеры. - */ - -#include -#include -#include /* COLOR()/COLOR_* для ATTR_* в сканерах */ -#include -#include /* bank_read/bank_write (idx_get/idx_put) */ -#include "mdview2.h" - -static uint8_t emph_to_attr(uint8_t emph, uint8_t base_attr); /* forward (нужна в inline-сканере) */ -static uint8_t is_hr_raw(uint32_t p); /* forward (нужна в classify_line) */ - -/* Начало следующей физической строки (байт после '\n', либо file_size). */ -static uint32_t next_line(uint32_t p) -{ - p = row_end(p); - return (p < file_size) ? p + 1 : p; -} - -typedef struct idx_rec_s { - uint32_t off; /* смещение первого байта исходного текста для сегмента */ - uint8_t flags; /* битовые флаги IF_* ниже */ - uint8_t style; /* начальный стиль INIT_STYLE_* в начале сегмента */ - uint8_t pad0; /* добивка до 8 байт (2048 записей на страницу 16 КБ) */ - uint8_t pad1; -} idx_rec_t; - -#define INDEX_REC_SIZE 8u - -static uint32_t cur_seg_off; /* смещение последнего emit_seg (кэш в near-памяти) */ -static idx_rec_t cur_rec; /* near-копия последней записанной idx-записи */ - -/* Для проверки границ выделения считаем пробел/таб/конец строки/0 пробельными. */ -static uint8_t ws_or_eol(char c) -{ - return (uint8_t)(c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == 0); -} - -static uint8_t ws_or_eol_or_delim(char c) -{ - return (uint8_t)(c == ' ' || c == ',' || c == '.' || c == '!' || c == '?' || c == ':' || c == ';' || c == '/' - || c == '\t' || c == '\n' || c == '\r' || c == 0); -} - -/* Маркер inline-выделения (*, **, _) распознаётся только если - * пробельная граница есть ровно с одной стороны (XOR-правило). - * Это защищает выражения вроде "2 * 3", "2 ** 3" и внутрисловные - * случаи (например COLOR_YELLOW) от ложного форматирования. */ -static uint8_t is_emph_flanked(char prev_ch, char next_ch) -{ - return (uint8_t)(ws_or_eol(prev_ch) != ws_or_eol_or_delim(next_ch)); -} - -/* Символ можно экранировать обратным слешем (\X): любая печатная ASCII- - * пунктуация (как в CommonMark). \* \_ \` \[ и т.п. → литерал X. */ -static uint8_t is_escapable(char c) -{ - return (uint8_t)(c >= '!' && c <= '~' && - !((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))); -} - -/* Типы сегмента CK_* и стили INIT_STYLE_* — в mdview2.h (общие с таблицами). */ - -/* Доступ к индексу в EMM. - * Размер записи 8 байт, поэтому запись не пересекает границу страницы - * и bank_read/bank_write работают с цельным блоком. */ -static void idx_get(uint16_t idx, idx_rec_t *r) -{ - uint8_t page = (uint8_t)(idx >> 11); /* номер страницы: idx / 2048 */ - uint16_t off = (uint16_t)((idx & 2047u) << 3); /* смещение записи: (idx % 2048) * 8 */ - bank_read(index_phys[page], off, r, INDEX_REC_SIZE); -} - -static void idx_put(uint16_t idx, const idx_rec_t *r) -{ - uint8_t page = (uint8_t)(idx >> 11); - uint16_t off = (uint16_t)((idx & 2047u) << 3); - bank_write(index_phys[page], off, r, INDEX_REC_SIZE); -} - -uint32_t seg_off(uint16_t idx) -{ - idx_rec_t r; - if (idx >= n_lines) return file_size; - idx_get(idx, &r); - return r.off; -} - -/* Последняя строка индекса с исходным offset <= off — обратный перевод - * байт-позиции в MD-строку (позиционирование при F2/F8). Оффсеты сегментов - * монотонны, поэтому бинарный поиск (~15 bank-чтений). */ -uint16_t line_at_off(uint32_t off) -{ - uint16_t lo = 0, hi = n_lines; - while (lo < hi) { - uint16_t mid = (uint16_t)((lo + hi) >> 1); - if (seg_off(mid) <= off) lo = (uint16_t)(mid + 1); - else hi = mid; - } - return (lo > 0) ? (uint16_t)(lo - 1) : 0; -} - -/* Установка флагов (маска IF_*) всегда идёт в последний emit_seg (n_lines-1). - * Используется near-копия cur_rec, чтобы не делать обратное чтение из банка. */ -static void set_cur_flags(uint8_t f) { cur_rec.flags |= f; idx_put((uint16_t)(n_lines - 1), &cur_rec); } - -/* ================================================================== - * Индексация строк - * ================================================================== */ - -/* INIT_STYLE_* — в mdview2.h (общие с модулем таблиц). */ - -/* Тип строки, который возвращает classify_line(). */ -#define LK_PLAIN 0 -#define LK_H1 1 -#define LK_H2 2 -#define LK_H3 3 -#define LK_H4 4 /* также используется для H5/H6 */ -#define LK_HR 5 -#define LK_ULIST 6 /* маркер ненумерованного списка */ -#define LK_OLIST 7 /* маркер нумерованного списка */ -#define LK_QUOTE 8 /* маркер цитаты */ - -/* Классифицирует логическую строку. - * - Заголовки: 1..4 символа '#' и затем пробел/таб (H5/H6 сводятся к H4). - * - Горизонтальная линия: только '-', '*' или '_' (одного вида, >=3), - * возможно с пробелами/табами между символами. - * - Ненумерованный список: "- ", "* " или "+ " перед содержимым. - * - Нумерованный список: цифры + '.'/ ')' + пробел. - * - Цитата: '>' с опциональным пробелом. - * - Иначе обычный текст. - * Для заголовков/списков/цитат out_content указывает на первый байт - * содержимого после маркера и завершающего пробела. */ -static uint8_t classify_line(uint32_t p_start, uint32_t *out_content) -{ - if (p_start >= file_size) return LK_PLAIN; - - char c0 = fb(p_start); - - /* Заголовок? */ - if (c0 == '#') { - uint8_t lvl = 0; - uint32_t p = p_start; - while (p < file_size && fb(p) == '#' && lvl < 6) { - lvl++; p++; - } - if (p < file_size) { - char ch = fb(p); - if (ch == ' ' || ch == '\t') { - p++; - *out_content = p; - if (lvl > 4) lvl = 4; - return lvl; /* LK_H1 .. LK_H4 */ - } - } - /* Символ '#' без пробела после него не считается заголовком. */ - } - - /* Горизонтальный разделитель. - * Проверяется до списков, чтобы `- - -` не стало маркером списка. */ - if (is_hr_raw(p_start)) return LK_HR; - - /* Для списков/цитат дополнительно допускаем ведущие пробелы - * (базовая поддержка вложенности). */ - uint32_t lp = p_start; - while (lp < file_size && fb(lp) == ' ') lp++; - char cl = (lp < file_size) ? fb(lp) : 0; - - /* Ненумерованный список? */ - if ((cl == '-' || cl == '*' || cl == '+') && - (lp + 1) < file_size && - fb(lp + 1) == ' ') { - *out_content = lp + 2; - return LK_ULIST; - } - - /* Нумерованный список? */ - if (cl >= '0' && cl <= '9') { - uint32_t p = lp; - while (p < file_size) { - char ch = fb(p); - if (ch < '0' || ch > '9') break; - p++; - } - if (p < file_size) { - char ch = fb(p); - if ((ch == '.' || ch == ')') && - (p + 1) < file_size && fb(p + 1) == ' ') { - *out_content = p + 2; - return LK_OLIST; - } - } - } - - /* Цитата? */ - if (cl == '>') { - uint32_t p = lp + 1; - if (p < file_size && fb(p) == ' ') p++; - *out_content = p; - return LK_QUOTE; - } - - return LK_PLAIN; -} - -/* Возвращает видимую ширину префикса (отступ + маркер) в колонках. */ -static uint8_t marker_visible_col(uint8_t kind, uint32_t p_start, uint32_t content_off) -{ - switch (kind) { - case LK_ULIST: - case LK_QUOTE: - case LK_OLIST: return (uint8_t)(content_off - p_start); - default: return 0; /* plain/headers спец-префикса не имеют */ - } -} - -/* ================== Merge: сборка ячеек на этапе индекс-скана ============== - * Форвард-сканер (scan_join_stream/inline_scan + прямые emit'ы в index_lines) - * собирает готовые (char,attr)-ячейки в g_cells ПО ХОДУ единственного прохода - * по файлу — без отдельного второго скана. На каждом emit_seg ячейки - * ПРЕДЫДУЩего сегмента флашатся в кэш (lag-1: к этому моменту сегмент уже - * полностью просканирован); последний сегмент флашится после цикла index_lines. - * g_ncells_at_space — снимок длины на последнем пробеле для усечения буфера - * при переносе строки (отбрасываем уехавшее за границу слово). */ -static uint8_t g_cells[MAX_CACHE_LINE_LEN * 2]; -uint8_t g_ncells; /* ячеек в текущем сегменте (общий с таблицами) */ -static uint8_t g_ncells_at_space; /* снимок g_ncells на последнем пробеле (для усечения при переносе) */ -static uint8_t g_skip_flush; /* следующий emit_seg пропустит lag-флаш (pending уже зафлашен вручную, напр. перед таблицей) */ - -/* Обрыв индексации: причина (0 / TRUNC_CONTENT / TRUNC_LINES) и строка, на - * которой упёрлись. Выставляются ОДИН раз (первая причина побеждает); по ним - * index_lines() в конце пишет строку-сообщение (IF_TRUNC_MSG, рисуется вживую). */ -static uint8_t g_trunc_cause; -static uint16_t g_trunc_line; - -/* Пишет одну (char,attr)-ячейку в g_cells, молча игнорируя переполнение - * MAX_CACHE_LINE_LEN. Оставлена ФУНКЦИЕЙ намеренно: инлайн в сильно - * регистро-нагруженный скан-цикл на Z80/SDCC увеличивает спиллы и замедляет - * (проверено замером — макро-инлайн дал регрессию ~11→13с). */ -void gc_put(char ch, uint8_t attr) -{ - if (g_ncells < MAX_CACHE_LINE_LEN) { - uint16_t i = (uint16_t)g_ncells * 2u; - g_cells[i] = (uint8_t)ch; - g_cells[i + 1] = attr; - g_ncells++; - } -} -void gc_fill(char ch, uint8_t attr, uint8_t n) { while (n-- > 0) gc_put(ch, attr); } - -/* Пишет g_cells как контент кэша для line_idx: flags берём из idx-записи, - * пустой буфер/blank → len 0; иначе резервируем место в пуле и коммитим. */ -static void seg_commit_cells(uint16_t line_idx) -{ - cache_rec_t out_rec; - out_rec.flags = 0; out_rec.reserved = 0; out_rec.pad[0] = 0; out_rec.pad[1] = 0; - idx_rec_t r; - uint8_t have = (uint8_t)(line_idx < n_lines); - if (have) { idx_get(line_idx, &r); out_rec.flags = r.flags; } - if (!have || (r.flags & IF_BLANK) || g_ncells == 0) { - out_rec.page = 0; out_rec.off = 0; out_rec.len = 0; - cache_dir_put(line_idx, &out_rec); - return; - } - out_rec.len = g_ncells; - if (cache_reserve((uint16_t)(g_ncells * 2u), &out_rec.page, &out_rec.off)) { - cache_commit(out_rec.page, out_rec.off, g_cells, (uint16_t)(g_ncells * 2u)); - } else { - /* Контент-кэш исчерпан: фиксируем причину и строку обрыва (один раз). - * index_lines() оборвёт цикл и перезапишет эту строку сообщением. */ - if (!g_trunc_cause) { g_trunc_cause = TRUNC_CONTENT; g_trunc_line = line_idx; } - out_rec.page = 0; out_rec.off = 0; out_rec.len = 0; - } - cache_dir_put(line_idx, &out_rec); -} - - -static void emit_seg(uint32_t off, uint8_t style, uint8_t ckind, uint8_t cont) -{ - (void)ckind; /* тип сегмента восстанавливается флагами idx-записи */ - if (n_lines >= max_lines) { - index_truncated = 1; - if (!g_trunc_cause) { g_trunc_cause = TRUNC_LINES; g_trunc_line = (uint16_t)(max_lines - 1); } - return; - } - cur_rec.off = off; - cur_rec.flags = (uint8_t)(cont ? IF_CONT : 0); - cur_rec.style = (uint8_t)(style & 7u); - cur_rec.pad0 = 0; - cur_rec.pad1 = 0; - idx_put(n_lines, &cur_rec); - cur_seg_off = off; - n_lines++; - /* Флаш ячеек ПРЕДЫДУЩего сегмента (lag-1): он полностью собран форвард-сканером. */ - if (n_lines >= 2) { - if (g_skip_flush) g_skip_flush = 0; /* pending уже зафлашен вручную */ - else seg_commit_cells((uint16_t)(n_lines - 2)); - } - g_ncells = 0; /* буфер ячеек под новый сегмент */ - g_ncells_at_space = 0; -} - -/* Проверяет, что физическая строка пуста (только пробелы/таб + перевод строки). */ -static uint8_t is_line_blank(uint32_t p) -{ - while (p < file_size) { - char c = fb(p); - if (c == '\n') return 1; - if (c != ' ' && c != '\t') return 0; - p++; - } - return 1; -} - -/* Число ведущих пробелов строки (отступ). Используется для отделения - * dedent-пунктов списка (#3) и для отступа обычного абзаца (#4). */ -static uint8_t leading_spaces(uint32_t p) -{ - uint8_t n = 0; - while (p < file_size && fb(p) == ' ' && n < 250) { n++; p++; } - return n; -} - -/* Быстрая проверка границы fenced-блока по маркеру ``` . - * Ведущие пробелы/табы пропускаются — fence может быть с отступом - * (например, ``` ```c ``` внутри элемента списка с отступом 2+ пробела). - * Язык после бэктиков (```c, ```sh) игнорируется: достаточно трёх ` подряд. */ -static uint8_t is_fence_raw(uint32_t p) -{ - while (p < file_size && (fb(p) == ' ' || fb(p) == '\t')) p++; - if (p + 2 >= file_size) return 0; - return (fb(p) == '`' && fb(p + 1) == '`' && fb(p + 2) == '`'); -} - -/* Быстрая проверка горизонтального разделителя (`---`, `***`, `___`). */ -static uint8_t is_hr_raw(uint32_t p) -{ - char c0 = fb(p); - if (c0 != '-' && c0 != '*' && c0 != '_') return 0; - char marker = 0; - uint8_t count = 0, ok = 1; - uint32_t hr_p = p; - while (hr_p < file_size) { - char ch = fb(hr_p++); - if (ch == '\n' || ch == '\r') break; - if (ch == ' ' || ch == '\t') continue; - if (ch != '-' && ch != '*' && ch != '_') { ok = 0; break; } - if (marker == 0) marker = ch; - else if (ch != marker) { ok = 0; break; } - count++; - } - return ok && count >= 3; -} - -/* Строка таблицы: первый непустой символ — '|'. - * Ведущие пробелы разрешены; такие строки не склеиваются и не переносятся. */ -static uint8_t is_table_raw(uint32_t p) -{ - while (p < file_size) { - char c = fb(p); - if (c == ' ' || c == '\t') { p++; continue; } - return (uint8_t)(c == '|'); - } - return 0; -} - -/* Разбор одного inline-МАРКЕРА в позиции *pq (< bound): \X, [x], `, **, ~~, - * *, _, \t. Общий код для inline_scan() и scan_join_stream(). Возврат: 1 — - * маркер обработан (вызывающий делает continue), 0 — символ обычный (пробел/ - * текст), его кладёт сам вызывающий (и проверяет перенос). Меняет - * *pq/*pls/*pseg/*pprev. attr берётся как emph_to_attr(line_style, base) — - * для join base=ATTR_TEXT (атрибут «обычного» текста). */ -static uint8_t inline_marker(uint32_t *pq, uint32_t bound, uint8_t *pls, - uint8_t *pseg, char *pprev, uint8_t base) -{ - uint32_t q = *pq; - uint8_t ls = *pls; - char ch = fb(q); - uint8_t at = emph_to_attr(ls, base); /* атрибут литералов текущего стиля */ - - if (ls != INIT_STYLE_CODE && ch == '\\' && (q + 1) < bound && is_escapable(fb(q + 1))) { - char nb = fb(q + 1); - gc_put(nb, at); - *pseg += 1; *pq = q + 2; *pprev = nb; return 1; - } - if (ls != INIT_STYLE_CODE && ch == '[' && (q + 2) < bound && fb(q + 2) == ']') { - gc_put('[', ATTR_TEXT_BOLD); gc_put(fb(q + 1), ATTR_TEXT_BOLD); gc_put(']', ATTR_TEXT_BOLD); - *pseg += 3; *pq = q + 3; *pprev = ']'; return 1; - } - if (ch == '`') { - if (ls == INIT_STYLE_PLAIN) { *pls = INIT_STYLE_CODE; *pq = q + 1; return 1; } - if (ls == INIT_STYLE_CODE) { *pls = INIT_STYLE_PLAIN; *pq = q + 1; return 1; } - gc_put('`', at); *pseg += 1; *pq = q + 1; *pprev = '`'; return 1; - } - if (ch == '*' && (q + 1) < bound && fb(q + 1) == '*') { - char nx = ((q + 2) < bound) ? fb(q + 2) : '\n'; - if (is_emph_flanked(*pprev, nx)) { - if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = INIT_STYLE_BOLD; *pq = q + 2; return 1; } - if (ls == INIT_STYLE_BOLD && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 2; return 1; } - } - gc_put('*', at); gc_put('*', at); - *pseg += 2; *pq = q + 2; *pprev = '*'; return 1; - } - if (ch == '~' && (q + 1) < bound && fb(q + 1) == '~') { - char nx = ((q + 2) < bound) ? fb(q + 2) : '\n'; - if (is_emph_flanked(*pprev, nx)) { - if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = INIT_STYLE_STRIKE; *pq = q + 2; return 1; } - if (ls == INIT_STYLE_STRIKE && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 2; return 1; } - } - gc_put('~', at); gc_put('~', at); - *pseg += 2; *pq = q + 2; *pprev = '~'; return 1; - } - if (ch == '*' || ch == '_') { - char nx = ((q + 1) < bound) ? fb(q + 1) : '\n'; - if (is_emph_flanked(*pprev, nx)) { - uint8_t ns = (ch == '*') ? INIT_STYLE_ITALIC : INIT_STYLE_UNDER; - if (ls == INIT_STYLE_PLAIN && ws_or_eol(*pprev)) { *pls = ns; *pq = q + 1; return 1; } - if (ls != INIT_STYLE_PLAIN && ws_or_eol_or_delim(nx)) { *pls = INIT_STYLE_PLAIN; *pq = q + 1; return 1; } - } - gc_put(ch, at); *pseg += 1; *pq = q + 1; *pprev = ch; return 1; - } - if (ch == '\t') { - uint8_t old_sc = *pseg; - uint8_t tgt_full = (uint8_t)((old_sc & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP); - uint8_t tgt = tgt_full; if (tgt > SCREEN_W) tgt = SCREEN_W; - gc_fill(' ', at, (uint8_t)(tgt_full - old_sc)); - *pseg = tgt; *pq = q + 1; *pprev = ' '; return 1; - } - return 0; /* пробел или обычный символ — обрабатывает вызывающий */ -} - -#define JOIN_MODE_NONE 0u -#define JOIN_MODE_LIST 1u -#define JOIN_MODE_QUOTE 2u -#define JOIN_MODE_PLAIN 3u - -/* ================== Единый сканер сегмента ================================ - * Общий цикл inline-форматирования, переносов и (в join-режимах) склейки - * физических строк — бывшие inline_scan + scan_join_stream. mode: - * JOIN_MODE_NONE — сканировать РОВНО [q, bound) без склейки; '\n' в - * диапазоне не ожидается (вызывающие дают строку/ячейку до EOL); - * активны base_attr (title-attr заголовка) и nowrap (замер таблиц); - * ckind всегда CK_OTHER (continuation-префикс не эмитится). - * JOIN_MODE_LIST/QUOTE/PLAIN — правила '\n'-склейки; bound == file_size, - * base_attr == ATTR_TEXT (emph_to_attr(ls, ATTR_TEXT) тождественно - * бывшему styles_map[ls]), nowrap == 0. - * Возвращает позицию останова; итоговый стиль — через *io_line_style. */ -static uint32_t scan_stream(uint32_t q, uint32_t bound, uint32_t para_start, - uint8_t col, uint8_t ckind, uint8_t *io_line_style, - uint8_t base_attr, uint8_t nowrap, uint8_t mode) -{ - uint32_t last_space = 0xFFFFFFFFu; - uint8_t last_space_style = *io_line_style; /* снимок line_style на момент last_space */ - char prev_ch = ' '; - uint8_t seg_col = col; - uint8_t line_style = *io_line_style; - /* base — атрибут «обычного» текста сегмента. Для заголовка первый сегмент - * имеет title-attr, а continuation-сегменты render рисует как PLAIN — - * переключаем base после 1-го переноса (в join-режимах base == ATTR_TEXT - * и присваивание ничего не меняет). */ - uint8_t base = base_attr; - - while (q < bound && n_lines < max_lines) { - char ch = fb(q); - uint8_t soft_break = 0; - - if (ch == '\n') { - uint32_t next = q + 1; - if (mode == JOIN_MODE_NONE) break; /* NONE: '\n' в [q,bound) не ожидается */ - if (next >= bound) break; - - if (mode == JOIN_MODE_LIST) { - if (is_line_blank(next)) break; - if (is_fence_raw(next) || is_hr_raw(next) || is_table_raw(next)) break; - - { - uint32_t next_content = next; - uint8_t next_kind = classify_line(next, &next_content); - if (next_kind == LK_ULIST || next_kind == LK_OLIST || - next_kind == LK_QUOTE || - (next_kind >= LK_H1 && next_kind <= LK_H4) || - next_kind == LK_HR) { - break; - } - } - while (next < bound) { - char ws = fb(next); - if (ws == ' ' || ws == '\t') next++; - else break; - } - q = next; - soft_break = 1; - ch = ' '; - } else if (mode == JOIN_MODE_QUOTE) { - if (is_line_blank(next)) break; - if (is_fence_raw(next) || is_hr_raw(next) || is_table_raw(next)) break; - - { - uint32_t next_content = next; - uint8_t next_kind = classify_line(next, &next_content); - if (next_kind != LK_QUOTE) break; - - { - uint32_t next_line_end = row_end(next_content); - - { - uint8_t empty_quote = 1; - for (uint32_t z = next_content; z < next_line_end; z++) { - char c = fb(z); - if (c != ' ' && c != '\t') { empty_quote = 0; break; } - } - if (empty_quote) break; - } - - { - uint32_t t = next_content; - while (t < next_line_end && (fb(t) == ' ' || fb(t) == '\t')) t++; - if (t < next_line_end && fb(t) == '>') break; - } - - while (next_content < next_line_end) { - char ws = fb(next_content); - if (ws == ' ' || ws == '\t') next_content++; - else break; - } - if (next_content < next_line_end && fb(next_content) == '>') { - next_content++; - if (next_content < next_line_end && fb(next_content) == ' ') next_content++; - } - q = next_content; - soft_break = 1; - ch = ' '; - } - } - } else { /* JOIN_MODE_PLAIN */ - if (fb(next) == '\n') break; - if (is_fence_raw(next) || is_hr_raw(next) || is_table_raw(next)) break; - { - uint32_t dummy; - if (classify_line(next, &dummy) != LK_PLAIN) break; - } - if (is_line_blank(next)) break; - - { - uint8_t is_hard = 0; - if (q >= para_start + 1) { - char b1 = fb(q - 1); - if (b1 == '\\') { - is_hard = 1; - } else if (q >= para_start + 2) { - char b2 = fb(q - 2); - if ((b1 == ' ' || b1 == '\t') && (b2 == ' ' || b2 == '\t')) - is_hard = 1; - } - } - if (is_hard) { - q++; - emit_seg(q, line_style, ckind, 0); - last_space = 0xFFFFFFFFu; - prev_ch = ' '; - seg_col = col; - continue; - } - } - - q++; - /* как render: пропускаем ведущие пробелы/табы строки-продолжения, - * склейка даёт РОВНО один пробел (иначе merge эмитит отступ как лишние ячейки) */ - while (q < bound && (fb(q) == ' ' || fb(q) == '\t')) q++; - soft_break = 1; - ch = ' '; - } - } - - /* Inline-маркеры (\X [x] ` ** ~~ * _ \t) — общий разбор; при - * soft_break ch==' ' (синтетический пробел склейки) — маркеры - * пропускаем, его кладёт ветка пробела ниже. */ - if (!soft_break && inline_marker(&q, bound, &line_style, &seg_col, &prev_ch, base)) - continue; - - if (ch == ' ') { - gc_put(' ', emph_to_attr(line_style, base)); - if (soft_break) { - last_space = q; /* синтетический пробел склейки */ - } else { - last_space = q + 1; - q++; - } - last_space_style = line_style; - g_ncells_at_space = g_ncells; /* снимок для усечения */ - seg_col++; - prev_ch = ' '; - } else { - gc_put(ch, emph_to_attr(line_style, base)); - seg_col++; - q++; /* soft_break здесь всегда 0 (ch != ' ') */ - prev_ch = ch; - } - if (!nowrap && seg_col >= SCREEN_W) { - uint8_t use_ls = (uint8_t)(last_space != 0xFFFFFFFFu && last_space > cur_seg_off); - uint32_t wrap_at = use_ls ? last_space : q; - if (wrap_at >= bound || wrap_at == cur_seg_off) break; - uint8_t wrap_style = use_ls ? last_space_style : line_style; - if (use_ls) g_ncells = g_ncells_at_space; /* усечь до пробела */ - emit_seg(wrap_at, wrap_style, ckind, 1); - base = ATTR_TEXT; /* continuation заголовка → PLAIN (в join base и так ATTR_TEXT) */ - /* Префикс continuation-сегмента: список и отступленный абзац (#4) — - * col пробелов выравнивания; цитата — отступ + маркер 0xB3 + пробел. - * Для обычного абзаца без отступа col==0 → префикса нет; CK_OTHER - * (заголовки/таблицы, NONE-режим) — префикса нет. */ - if (ckind == CK_LIST || ckind == CK_PLAIN) { - gc_fill(' ', ATTR_TEXT, col); - } else if (ckind == CK_QUOTE) { - uint32_t content_off2 = para_start + col; - uint8_t qsp = 0; - while (qsp + 2 < content_off2 && qsp < SCREEN_W && fb(para_start + qsp) == ' ') qsp++; - gc_fill(' ', ATTR_TEXT, qsp); - gc_put(0xB3, ATTR_QUOTE_MARKER); - gc_put(' ', ATTR_TEXT); - } - seg_col = col; - last_space = 0xFFFFFFFFu; - prev_ch = ' '; - q = wrap_at; - /* Откатываем line_style к снимку на момент wrap_at: диапазон - * [wrap_at, q_old) будет пересканирован заново, и переключающие - * символы (обратная кавычка, звёздочка, подчёркивание, тильда) - * внутри него иначе применились бы дважды — именно так возникал - * баг: code-маркер на границе переноса ломает цвет следующего слова (mdview.c, отчёт пользователя - * 2026-06-23). */ - line_style = wrap_style; - } - } - - *io_line_style = line_style; - return q; -} - -/* Тонкая обёртка прежнего API: скан ровно [q, q_end) без склейки строк. - * Вызывается для заголовков и ячеек таблиц (всегда CK_OTHER, col=0). */ -uint8_t inline_scan(uint32_t q, uint32_t q_end, uint8_t col, - uint8_t ckind, uint8_t line_style, uint8_t base_attr, - uint8_t nowrap) -{ - (void)scan_stream(q, q_end, q, col, ckind, &line_style, base_attr, nowrap, - JOIN_MODE_NONE); - return line_style; -} - -/* Потоковый сканер для index_lines(): mode задаёт правила '\n'-склейки. */ -static uint32_t scan_join_stream(uint32_t q, uint32_t para_start, uint8_t col, - uint8_t ckind, uint8_t *io_line_style, uint8_t mode) -{ - return scan_stream(q, file_size, para_start, col, ckind, io_line_style, - ATTR_TEXT, 0, mode); -} - -/* Отрисовка таблиц (row_end/table_*) — в модуле mdview2_table.c. */ - -/* ================== Резюмируемый проход индексации ======================= - * Бывший монолит index_lines() порезан на begin/step/finish, чтобы фоновая - * сборка второго набора (bg_step ядра) могла выполнять индексацию порциями - * в паузах между клавишами. Всё межшаговое состояние — в статиках модуля - * (idx_p/idx_in_block ниже + g_cells/cur_rec/g_trunc_* выше); оно НЕ входит - * в docset_t и переживает свапы наборов, потому что обработчики клавиш - * видимого набора индексатор не трогают. */ -static uint32_t idx_p; /* позиция чтения исходника между шагами */ -static uint8_t idx_in_block; /* внутри fenced code-блока */ -static uint8_t idx_active; /* 1 = проход начат и ещё не финализирован */ - -/* Начинает новый проход (сброс позиции/буферов/причин обрыва). */ -void index_begin(void) -{ - idx_p = 0; - idx_in_block = 0; - n_lines = 0; - index_truncated = 0; - g_trunc_cause = 0; - g_trunc_line = 0; - cur_seg_off = 0xFFFFFFFFu; /* пока не эмитили ни одного сегмента */ - g_ncells = 0; - g_ncells_at_space = 0; - g_skip_flush = 0; - idx_active = 1; -} - -/* Хвост прохода: финальный флаш ячеек + строка-обрыв (бывший финал index_lines). */ -static void index_finish(void) -{ - idx_active = 0; - /* emit_seg() флашит ячейки с отставанием на одну строку (см. её - * комментарий) — последняя эмитированная строка файла так и остаётся - * незафлашенной, её ячейки ещё в g_cells; коммитим здесь. */ - if (n_lines > 0) seg_commit_cells((uint16_t)(n_lines - 1)); - - /* Лимит строк ловим ЗДЕСЬ, а не в emit_seg: главный цикл выходит по - * `n_lines < max_lines` ДО того, как emit_seg будет вызван в переполненном - * состоянии (для code-block — один emit_seg на строку, эта ветка не - * достигается). Признак — остановились, а файл не кончился; g_abort и - * исчерпание контента (TRUNC_CONTENT) уже обработаны/исключены. */ - if (!g_trunc_cause && !g_abort && idx_p < file_size) { - g_trunc_cause = TRUNC_LINES; - g_trunc_line = (uint16_t)(max_lines - 1); - } - - /* Файл был обрезан до 256 КБ при загрузке, а индексация дошла до конца - * клампнутых данных без исчерпания контента/строк → ДОПИСЫВАЕМ строку - * (место есть: n_lines < max_lines). Приоритет ниже content/lines — - * те означают, что обрезали ещё раньше, их сообщение точнее. */ - if (!g_trunc_cause && !g_abort && g_file_clamped) { - g_trunc_cause = TRUNC_FILE; - g_trunc_line = n_lines; - } - - /* Обрыв по контенту/лимиту строк (не путать с пользовательским g_abort): - * документ заканчиваем строкой-сообщением на g_trunc_line. Сообщение - * рисуется вживую (IF_TRUNC_MSG) — место в контент-кэше ему не нужно, что - * и снимает тупик «кэш полон, а сообщение тоже надо закэшировать». */ - if (g_trunc_cause && !g_abort) { - cache_rec_t r; - r.page = 0; r.off = 0; r.len = 0; - r.flags = IF_TRUNC_MSG; r.reserved = g_trunc_cause; - r.pad[0] = 0; r.pad[1] = 0; - cache_dir_put(g_trunc_line, &r); - n_lines = (uint16_t)(g_trunc_line + 1); /* документ кончается на строке-обрыве */ - } -} - -/* Одна итерация бывшего главного цикла index_lines(): классифицирует и - * эмитит очередной блок исходника (параграф/строку кода/таблицу целиком). - * do{}while(0) сохраняет исходные `continue` тела как выход из итерации - * с записью локальной p обратно в idx_p. */ -static void index_iter(void) -{ - if (g_loading) - progress_tick(); /* первый экран + статус + скролл по готовым данным */ - else if (!g_bg_building && (n_lines & 15) == 0) - spinner_tick(); - - uint32_t p = idx_p; - do { - /* Пустая строка — разделитель параграфов. - * В индекс добавляем не более одной пустой экранной строки подряд. */ - if (is_line_blank(p)) { - p = next_line(p); - /* Серия пустых строк схлопывается в одну визуальную строку. */ - if (n_lines == 0 || !(cur_rec.flags & IF_BLANK)) { - emit_seg(p, INIT_STYLE_PLAIN, CK_PLAIN, 0); - set_cur_flags(IF_BLANK); - } - continue; - } - - /* ---- fenced code-блок (граница или тело) ---- */ - if (is_fence_raw(p)) { - idx_in_block = !idx_in_block; - emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_cur_flags(IF_NOWRAP); - p = next_line(p); - continue; - } - if (idx_in_block) { - emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_cur_flags(IF_NOWRAP | IF_CODE | IF_HSCROLL); /* код скроллится целым блоком */ - { - uint8_t ccx = 0; - uint32_t pp = p; - while (pp < file_size && fb(pp) != '\n') { - char ch = fb(pp); - if (ch == '\t') { - uint8_t tgt = (uint8_t)((ccx & (uint8_t)~(TAB_STOP - 1)) + TAB_STOP); - gc_fill(' ', ATTR_TEXT_CODE, (uint8_t)(tgt - ccx)); - ccx = tgt; - } else if (ch == '\r') { - gc_put(' ', ATTR_TEXT_CODE); ccx++; /* как render: \r → пробел */ - } else { - gc_put(ch, ATTR_TEXT_CODE); ccx++; - } - pp++; - } - } - p = next_line(p); - continue; - } - - /* ---- горизонтальный разделитель ---- */ - if (is_hr_raw(p)) { - emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_cur_flags(IF_NOWRAP); - gc_fill(0xC4, ATTR_HR, SCREEN_W); - p = next_line(p); - continue; - } - - /* ---- строка таблицы (| ... |): один nowrap-сегмент на исходную строку ---- */ - if (is_table_raw(p)) { - /* Освобождаем g_cells под измерение ширин: коммитим pending-сегмент - * сейчас; первый emit_seg таблицы пропустит свой lag-флаш. */ - if (n_lines >= 1) { seg_commit_cells((uint16_t)(n_lines - 1)); g_skip_flush = 1; } - g_ncells = 0; - - /* Проход 1: границы блока + ОТРЕНДЕРЕННЫЕ ширины колонок (мерим тем же - * inline_scan, что и при отрисовке — невидимые маркеры стиля не считаются). */ - uint8_t widths[TBL_MAX_COLS]; - for (uint8_t i = 0; i < TBL_MAX_COLS; i++) widths[i] = 0; - uint8_t ncols = 0; - uint32_t r = p; - while (r < file_size && is_table_raw(r)) { - uint32_t le = row_end(r); - uint8_t sep = table_is_sep_row(r, le); - uint32_t cp = table_first_cell(r); - uint32_t cs, ce; - uint8_t c = 0; - while (c < TBL_MAX_COLS && table_next_cell(&cp, le, &cs, &ce)) { - if (!sep && ce > cs) { - g_ncells = 0; - (void)inline_scan(cs, ce, 0, CK_OTHER, INIT_STYLE_PLAIN, ATTR_TEXT, 1); - if (g_ncells > widths[c]) widths[c] = g_ncells; - } - c++; - } - if (c > ncols) ncols = c; - r = (le < file_size) ? le + 1 : le; - } - g_ncells = 0; /* чисто для верхней рамки */ - uint32_t tbl_end = r; - if (ncols == 0) ncols = 1; - - /* Проход 2: верхняя рамка → строки/разделитель → нижняя рамка. */ - emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_cur_flags(IF_NOWRAP | IF_HSCROLL); - table_border(widths, ncols, TBL_TL, TBL_TM, TBL_TR); - - r = p; - while (r < tbl_end) { - uint32_t le = row_end(r); - emit_seg(r, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_cur_flags(IF_NOWRAP | IF_HSCROLL); - if (table_is_sep_row(r, le)) table_border(widths, ncols, TBL_ML, TBL_MM, TBL_MR); - else table_data_row(r, le, widths, ncols); - r = (le < file_size) ? le + 1 : le; - } - - emit_seg(tbl_end, INIT_STYLE_PLAIN, CK_OTHER, 0); - set_cur_flags(IF_NOWRAP | IF_HSCROLL); - table_border(widths, ncols, TBL_BL, TBL_BM, TBL_BR); - - p = tbl_end; - continue; - } - - /* ---- классификация текущей строки ---- */ - uint32_t content_off = p; - uint8_t kind = classify_line(p, &content_off); - - /* ---- Заголовок: отдельная строка, не склеивается с параграфом ---- */ - if (kind >= LK_H1 && kind <= LK_H4) { - uint8_t hbase; /* title-attr, как в render */ - switch (kind) { - case LK_H1: hbase = ATTR_TEXT_TITLE1; break; - case LK_H2: hbase = ATTR_TEXT_TITLE2; break; - case LK_H3: hbase = ATTR_TEXT_TITLE3; break; - default: hbase = ATTR_TEXT_TITLE4; break; - } - emit_seg(p, INIT_STYLE_PLAIN, CK_OTHER, 0); - uint32_t line_end = row_end(content_off); - (void)inline_scan(content_off, line_end, 0, CK_OTHER, INIT_STYLE_PLAIN, hbase, 0); - p = next_line(p); - continue; - } - - /* ---- Пункт списка: может занимать несколько физических строк ---- */ - if (kind == LK_ULIST || kind == LK_OLIST) { - uint8_t col = marker_visible_col(kind, p, content_off); - uint8_t line_style = INIT_STYLE_PLAIN; - uint32_t q = content_off; - - emit_seg(p, line_style, CK_LIST, 0); - /* Префикс ПЕРВОГО сегмента: отступ + маркер + пробел (как render). */ - { - uint32_t qq = p, qmax = p + SCREEN_W; - if (kind == LK_ULIST) { - while (qq + 2 < content_off && qq < qmax && fb(qq) == ' ') qq++; - gc_fill(' ', ATTR_TEXT, (uint8_t)(qq - p)); - gc_put(0x07, ATTR_LIST_MARKER); - gc_put(' ', ATTR_TEXT); - } else { - while (qq < content_off && qq < qmax && fb(qq) == ' ') qq++; - gc_fill(' ', ATTR_TEXT, (uint8_t)(qq - p)); - while (qq < content_off - 1) { gc_put(fb(qq), ATTR_LIST_MARKER); qq++; } - gc_put(' ', ATTR_TEXT); - } - } - q = scan_join_stream(q, p, col, CK_LIST, &line_style, JOIN_MODE_LIST); - - /* Переходим к началу следующей физической строки после пункта. */ - q = next_line(q); - - /* Одна пустая строка между соседними маркерами списка подавляется. - * Две и более пустых строки оставляют видимый разделитель. */ - if (q < file_size && is_line_blank(q)) { - uint32_t t = q; - uint8_t blanks = 0; - while (t < file_size && is_line_blank(t)) { - t = next_line(t); - blanks++; - // if (blanks >= 2) break; - } - if (blanks == 1 && t < file_size) { - uint32_t d = t; - uint8_t nk = classify_line(t, &d); - if (nk == LK_ULIST || nk == LK_OLIST) { - /* #3: подавляем blank только если следующий пункт того же - * уровня или глубже; при МЕНЬШЕМ отступе (dedent) пустую - * строку оставляем — пункты должны визуально разделиться. */ - if (leading_spaces(t) >= leading_spaces(p)) { - p = t; /* подавляем единственную пустую строку */ - continue; - } - } - } - } - - p = q; - continue; - } - - /* ---- Параграф цитаты: может покрывать несколько физических строк ---- */ - if (kind == LK_QUOTE) { - uint8_t col = marker_visible_col(kind, p, content_off); - uint8_t line_style = INIT_STYLE_PLAIN; - uint32_t q = content_off; - - emit_seg(p, line_style, CK_QUOTE, 0); - /* Префикс ПЕРВОГО сегмента: отступ + маркер 0xB3 + пробел (как render LK_QUOTE). */ - { - uint32_t qq = p, qmax = p + SCREEN_W; - while (qq + 1 < content_off && qq < qmax && fb(qq) == ' ') qq++; - gc_fill(' ', ATTR_TEXT, (uint8_t)(qq - p)); - gc_put(0xB3, ATTR_QUOTE_MARKER); - gc_put(' ', ATTR_TEXT); - } - q = scan_join_stream(q, p, col, CK_QUOTE, &line_style, JOIN_MODE_QUOTE); - - p = next_line(q); - continue; - } - - /* ---- Обычный текстовый параграф: склейка через soft-break ---- */ - { - uint8_t line_style = INIT_STYLE_PLAIN; - /* #4: если абзац начинается с отступа — все его перенесённые строки - * получают такой же отступ (как continuation-префикс списка, но без - * маркера). Контент сканируем после отступа. Патологически большой - * отступ игнорируем. */ - uint8_t indent = leading_spaces(p); - if (indent >= SCREEN_W / 2) indent = 0; - - emit_seg(p, line_style, CK_PLAIN, 0); - if (indent) gc_fill(' ', ATTR_TEXT, indent); /* префикс первого сегмента */ - - uint32_t q = p + indent; - q = scan_join_stream(q, p, indent, CK_PLAIN, &line_style, JOIN_MODE_PLAIN); - - if (q >= file_size) { - p = file_size; - } else { - /* q стоит на завершающем newline параграфа. - * Переходим на следующую строку, чтобы внешняя петля её - * корректно переклассифицировала. */ - p = next_line(q); - } - } - } while (0); - idx_p = p; -} - -/* Выполняет до budget итераций индексации. Возврат: 1 — проход завершён - * (индекс финализирован index_finish), 0 — есть ещё работа. - * Конвертация UTF-8 держится впереди позиции чтения ЗДЕСЬ, до проверки - * условия останова: пока исходник не сконвертирован до конца, idx_p не - * может «догнать» file_size и оборвать проход преждевременно. */ -uint8_t index_step(uint8_t budget) -{ - if (!idx_active) return 1; - while (budget-- > 0) { - /* UTF-8: держим конвертацию впереди позиции чтения (включая look-ahead - * склейки/классификации ~ один абзац). file_size при этом = доступная - * сконвертированная часть и растёт по ходу. */ - if (g_utf_building && (uint32_t)(idx_p + CONV_MARGIN) > file_size) - utf_convert_more((uint32_t)(idx_p + CONV_MARGIN)); - - if (!(idx_p < file_size && n_lines < max_lines && !g_abort && !g_trunc_cause)) { - index_finish(); - return 1; - } - index_iter(); - } - return 0; -} -#define EM_NONE 0 -#define EM_BOLD 1 -#define EM_ITALIC 2 -#define EM_UNDER 3 -#define EM_CODE 4 -#define EM_STRIKE 5 - -/* Преобразует внутреннее состояние emphasis в экранный атрибут. */ -static uint8_t emph_to_attr(uint8_t emph, uint8_t base_attr) -{ - switch (emph) { - case EM_BOLD: return ATTR_TEXT_BOLD; - case EM_ITALIC: return ATTR_TEXT_ITALIC; - case EM_UNDER: return ATTR_TEXT_UNDERSORE; - case EM_CODE: return ATTR_TEXT_CODE; - case EM_STRIKE: return ATTR_TEXT_STRIKE; - default: return base_attr; - } -} diff --git a/examples/mdview2/mdview2_md.c b/examples/mdview2/mdview2_md.c deleted file mode 100644 index 712d949..0000000 --- a/examples/mdview2/mdview2_md.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * mdview2_md.c — представление MD-документа (peer к mdview2_raw.c): отрисовка - * области из рендер-кэша + прокрутка (верт./гориз.) + навигация md_key(). - * Статус-бар, меню и спиннер — в mdview2_status.c (общие для MD/RAW). - * - * Работает поверх готового рендер-кэша (cache_dir_get → cache_rec_t) и - * состояния навигации (top_line/viewport_x/n_lines, см. mdview2.h). Никакого - * парсинга/обращения к исходному файлу: всё рисуется из (char,attr)-ячеек кэша - * через win_rest_remap (ремап 8-битных кодировок — в mdview2_enc.c). - * - * Пара видов: mdview2_md.c (MD, из кэша) ↔ mdview2_raw.c (RAW, из fb()). - */ - -#include -#include /* wrchar/scroll/COLOR */ -#include /* bios_set_place/bios_fillcharattr */ -#include "mdview2.h" - -/* Фаза 4-5 — cache-only draw path: рисует одну строку ИЗ КЭША, без - * единого обращения к fb()/исходному файлу. Горизонтальный скролл для - * nowrap-строк (Фаза 5) — это просто смещение начала среза на vx*2 - * байт ВНУТРИ ТОГО ЖЕ кэш-буфера (cache_reserve гарантирует, что вся - * строка лежит в одной странице, так что off+vx*2 не пересекает - * границу страницы); не nowrap-строки игнорируют viewport_x, как и - * раньше в живом render_line() (там это называлось effective_vx). */ -static void draw_line_from_cache(uint16_t line_idx, uint8_t row) -{ - cache_rec_t rec; - cache_dir_get(line_idx, &rec); - - /* Строка-обрыв (исчерпан контент-кэш / лимит строк): фикс. текст вживую, - * без обращения к контент-кэшу (см. index_lines финализацию). */ - if (rec.flags & IF_TRUNC_MSG) { - const char *msg; - switch (rec.reserved) { - case TRUNC_LINES: msg = "*** Line limit reached - document truncated ***"; break; - case TRUNC_FILE: msg = "*** File too large - truncated at 256 KB ***"; break; - default: msg = "*** Content cache exhausted - document truncated ***"; break; - } - bios_set_place(row, 0); - bios_fillcharattr(' ', ATTR_WARN, SCREEN_W); - put_str_attr(2, row, msg, ATTR_WARN); - return; - } - - bios_set_place(row, 0); - bios_fillcharattr(' ', ATTR_TEXT, SCREEN_W); - - /* Горизонтальный сдвиг применяется ко ВСЕМ строкам hscroll-блока (код, - * таблицы) — по ТИПУ, а не по длине: блок двигается целиком, включая - * строки короче 80 (короткая строка, ушедшая за левый край, просто - * показывает маркер '<' на пустом месте). HR и границы fence не имеют - * IF_HSCROLL и не сдвигаются. */ - uint8_t vx = (rec.flags & IF_HSCROLL) ? viewport_x : 0; - - if (rec.len > vx) { - uint16_t remaining = (uint16_t)(rec.len - vx); - uint8_t w = (remaining > SCREEN_W) ? SCREEN_W : (uint8_t)remaining; - uint16_t off = (uint16_t)(rec.off + (uint16_t)vx * 2u); - win_rest_remap(row, w, rec.page, off); - if (remaining > SCREEN_W) { - wrchar(SCREEN_W - 1, row, '>', ATTR_TRUNC); - } - } - if (vx > 0) { - wrchar(0, row, '<', ATTR_TRUNC); - } -} - -/* Перерисовывает все VIEW_H строк окна из кэша — основной путь - * перерисовки видимой области после загрузки файла (Фаза 3-4). */ -void draw_viewport_from_cache(void) -{ - for (uint8_t i = 0; i < VIEW_H; i++) { - draw_line_from_cache((uint16_t)(top_line + i), (uint8_t)(VIEW_TOP_ROW + i)); - } -} - - -/* ================================================================== - * Прокрутка - * ================================================================== */ - -/* Ограничивает top_line допустимым диапазоном с учётом высоты окна. */ -void clamp_top(void) -{ - if (n_lines <= VIEW_H) { - top_line = 0; - } else if (top_line > n_lines - VIEW_H) { - top_line = (uint16_t)(n_lines - VIEW_H); - } -} - -/* Прокрутка вверх на n строк с частичной перерисовкой при n == 1. */ -void md_scroll_up(uint16_t n) -{ - uint16_t new_top_line = (top_line >= n) ? (uint16_t)(top_line - n) : 0; - if( new_top_line != top_line) { - top_line = new_top_line; - if (n == 1) { - scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 2, 0); - draw_line_from_cache((uint16_t)(top_line), (uint8_t)(VIEW_TOP_ROW)); - } else { - draw_viewport_from_cache(); - } - } -} - -/* Прокрутка вниз на n строк с частичной перерисовкой при n == 1. */ -void md_scroll_down(uint16_t n) -{ - uint16_t new_top_line = (top_line + n < n_lines - VIEW_H) ? - (uint16_t)(top_line + n) : n_lines - VIEW_H; - if( new_top_line != top_line) { - top_line = new_top_line; - if (n == 1) { - scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 1, 0); - draw_line_from_cache((uint16_t)(top_line + VIEW_H - 1), (uint8_t)(VIEW_TOP_ROW + VIEW_H - 1)); - } else { - clamp_top(); - draw_viewport_from_cache(); - } - } -} - -/* Горизонтальный сдвиг (только если в окне есть nowrap-строки). - * Максимум сдвига ограничен самой широкой nowrap-строкой на экране. - * Фаза 5: ширина берётся прямо из директории кэша (rec.len — уже - * посчитанная реальная длина в ячейках после рендера), без единого - * обращения к fb()/seg_off()/исходному файлу. */ -void md_scroll_horizon(int8_t delta) -{ - uint16_t maxw = 0; - for (uint8_t i = 0; i < VIEW_H; i++) { - uint16_t li = (uint16_t)(top_line + i); - if (li >= n_lines) break; - cache_rec_t rec; - cache_dir_get(li, &rec); - if (!(rec.flags & IF_HSCROLL)) continue; /* только код/таблицы */ - if (rec.len > maxw) maxw = rec.len; /* самая широкая строка блока */ - } - if (maxw == 0) return; /* нет строк шире экрана */ - - /* Максимальный сдвиг = ширина за пределами экрана, в границах uint8. */ - uint16_t over = (maxw > SCREEN_W) ? (uint16_t)(maxw - SCREEN_W) : 0; - if (over > 248u) over = 248u; - uint8_t max_vx = (uint8_t)over; - - int16_t nx = (int16_t)viewport_x + delta; - if (nx < 0) nx = 0; - if (nx > (int16_t)max_vx) nx = max_vx; - - /* ПОРЯДОК ВАЖЕН: записываем viewport_x ДО сравнения, а сравниваем уже - * сохранённую копию old_vx с new_vx. Иначе SDCC генерирует для - * `if (new_vx != viewport_x) viewport_x = new_vx;` такой код: - * ld a, new_vx ; sub a,(viewport_x) ; jr Z,.. ; ld (viewport_x),a - * — т.е. для записи переиспользует регистр A, испорченный вычитанием в - * сравнении, и кладёт в viewport_x не new_vx, а (new_vx - old_vx). - * (asm-дамп 2026-06-24 подтвердил; даёт 10-8=2, 10-2=8 — ровно - * наблюдавшийся "прыжок" скролла.) Записав viewport_x первой, мы убираем - * портящий sub с пути записи; сравнение ниже только решает, перерисовывать - * ли экран. */ - uint8_t new_vx = (uint8_t)nx; - uint8_t old_vx = viewport_x; - viewport_x = new_vx; - if (new_vx != old_vx) - draw_viewport_from_cache(); -} - -/* Процент прокрутки (0..100) относительно доступного диапазона. */ -static uint8_t calc_md_pct(void) -{ - uint16_t total = drawable_lines(); - if (total <= VIEW_H) return 100; - return pct16(top_line, (uint16_t)(total - VIEW_H)); -} - -/* ---- навигация MD после загрузки (peer к raw_key) ---------------- */ - -/* Обрабатывает навигационную клавишу в MD-режиме (вызывается из главного - * цикла). Сама перерисовывает область/статус. Возврат: 1 — обработано, - * 0 — не наша клавиша (F1/F8/F10 разбирает main). */ -uint8_t md_key(uint8_t scan) -{ - switch (scan) { - case KEY_UP: - md_scroll_up(1); - break; - case KEY_DOWN: - md_scroll_down(1); - break; - case KEY_LEFT: - md_scroll_horizon(-(int8_t)HPAN_STEP); - break; - case KEY_RIGHT: - md_scroll_horizon(+(int8_t)HPAN_STEP); - break; - case KEY_PGUP: - md_scroll_up(VIEW_H); - break; - case KEY_PGDN: - md_scroll_down(VIEW_H); - break; - case KEY_HOME: - if(top_line != 0 || viewport_x != 0 ) { - top_line = 0; - viewport_x = 0; - draw_viewport_from_cache(); - } - break; - case KEY_END: - uint16_t new_top_line = (n_lines > VIEW_H) ? (uint16_t)(n_lines - VIEW_H) : 0; - if(top_line != new_top_line || viewport_x != 0 ) { - top_line = new_top_line; - viewport_x = 0; - draw_viewport_from_cache(); - } - break; - - default: - return 0; /* не наша клавиша */ - } - - render_percent_progress(calc_md_pct()); - render_md_status_numbers(); - return 1; -} diff --git a/examples/mdview2/mdview2_raw.c b/examples/mdview2/mdview2_raw.c deleted file mode 100644 index db2be7b..0000000 --- a/examples/mdview2/mdview2_raw.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * mdview2_raw.c — RAW-просмотр исходного текста (без markdown-форматирования). - * - * Работает по АКТИВНОМУ буферу документа через fb() (8-бит = исходные байты, - * UTF-8 = декодированный в CP866 буфер), ремап ≥0x80 (CP1251/KOI8) — на - * отрисовке. Индекс/кэш markdown не используются: позиция — байт-offset, - * 1 байт = 1 ячейка, \t показываем пробелом. Два режима: - * VIEW_RAW_WRAP — длинные строки переносятся кратно 80 (рвём слова); - * VIEW_RAW_HSCROLL — строка в один ряд, горизонтальный скролл. - * - * Память: 0 доп. EMM; near-буфер строки + переиспользование g_scratch_phys. - */ - -#include -#include -#include -#include -#include "mdview2.h" - -#if WITH_RAW /* весь модуль — опционален (mdview_conf.h); при 0 пустой объектник */ - -static uint32_t raw_top; /* байт-offset верхней строки экрана */ -static uint16_t raw_hpan; /* гориз. сдвиг (только HSCROLL) */ - -/* ---- примитивы навигации по физическим строкам / переносам -------- */ - -static uint32_t raw_line_start(uint32_t off) -{ - while (off && fb(off - 1) != '\n') off--; - return off; -} - -/* WRAP: начало следующего ряда от off (\n завершает строку; иначе +80). */ -static uint32_t raw_next_wrap(uint32_t off) -{ - uint32_t e = off, lim = off + SCREEN_W; - while (e < file_size) { - if (fb(e) == '\n') return e + 1; - e++; - if (e >= lim) { /* прошли 80 без \n */ - if (e < file_size && fb(e) == '\n') return e + 1; /* ровно 80 + \n */ - return e; /* длинная строка → продолжение */ - } - } - return e; /* EOF */ -} - -static uint32_t raw_prev_wrap(uint32_t off) -{ - if (off == 0) return 0; - uint32_t pls = raw_line_start(off - 1); /* off-1 — это \n пред. строки */ - while (pls + SCREEN_W < (off - 1)) pls += SCREEN_W; - return pls; -} - -/* HSCROLL: начало следующей/предыдущей физической строки. */ -static uint32_t raw_next_line(uint32_t off) -{ - while (off < file_size && fb(off) != '\n') off++; - return (off < file_size) ? off + 1 : off; -} - -static uint32_t raw_prev_line(uint32_t off) -{ - if (off == 0) return 0; - return raw_line_start(off - 1); -} - -/* ---- отрисовка ряда (общий near-буфер → g_scratch_phys → win_rest) - */ - -static uint8_t raw_buf[SCREEN_W + 1]; /* только символы (атрибут единый, задаётся при выводе) */ - -/* В RAW атрибут единый (ATTR_TEXT): буфер — только 80 символов, без пар - * (char,attr). Кладём один байт-глиф (\t→пробел, ремап ≥0x80). */ -static void raw_cell(uint8_t i, uint8_t ch) -{ - if (ch >= 0x80 && g_remap) ch = g_remap[ch - 0x80]; - raw_buf[i] = ch; -} -/* Вывод готового 80-символьного буфера в строку экрана одним BIOS-вызовом - * (char-буфер + единый attr — без WINREST/scratch-страницы). */ -static void raw_flush(uint8_t row) -{ - bios_set_place(row, 0); - // bios_writeattr((const char *)raw_buf, SCREEN_W, ATTR_TEXT); - // bios_writeattr_until((const char *)raw_buf, SCREEN_W, ATTR_TEXT, 0); - bios_write_until((const char *)raw_buf, SCREEN_W, 0); -} - -static uint32_t raw_draw_wrap_row(uint32_t off, uint8_t row) -{ - uint8_t i = 0; - uint32_t e = off, lim = off + SCREEN_W; - while (i < SCREEN_W && e < file_size) { - char c = fb(e); - if (c == '\n') { - raw_cell(i, 0); - break; - } - raw_cell(i, (uint8_t)(c == '\t' ? ' ' : c)); - i++; e++; - } - raw_flush(row); - return raw_next_wrap(off); -} - -static uint32_t raw_draw_hscroll_row(uint32_t off, uint8_t row) -{ - uint32_t e = off; - while (e < file_size && fb(e) != '\n') e++; /* e = конец строки */ - uint16_t len = e - off; - - uint8_t i = 0; - uint32_t p = off + raw_hpan; - while (i < SCREEN_W && p < e) { - char c = fb(p); - raw_cell(i, (uint8_t)(c == '\t' ? ' ' : c)); - i++; p++; - } - // raw_buf[i] = 0; - - if (raw_hpan > 0) - raw_buf[0] = '<'; - if (len > (uint16_t)raw_hpan + SCREEN_W) - raw_buf[SCREEN_W - 1] = '>'; - if (i < SCREEN_W) - raw_buf[i] = 0; - raw_flush(row); - return (e < file_size) ? e + 1 : e; -} - -/* Рисует один ряд от off (по режиму) и возвращает offset следующего ряда. */ -static uint32_t raw_draw_one(uint32_t off, uint8_t row) -{ - if (off >= file_size) { fill_row(row, ATTR_TEXT); return off; } - return (g_view == VIEW_RAW_WRAP) ? raw_draw_wrap_row(off, row) - : raw_draw_hscroll_row(off, row); -} - -void raw_draw(void) -{ - uint32_t off = raw_top; - for (uint8_t r = 0; r < VIEW_H; r++) - off = raw_draw_one(off, (uint8_t)(VIEW_TOP_ROW + r)); -} - -/* Прокрутка на одну строку: аппаратный scroll окна документа + отрисовка - * ОДНОЙ новой строки (как в MD-режиме). Смещение нижнего ряда вычисляется - * проходом VIEW_H рядов от raw_top — без отдельного инкрементального счётчика, - * который рассинхронизировался, когда контент кончается в середине экрана - * (короче окна после wrap→unwrap у конца файла). */ -static uint32_t raw_next(uint32_t off) -{ - return (g_view == VIEW_RAW_WRAP) ? raw_next_wrap(off) : raw_next_line(off); -} -static uint32_t raw_prev(uint32_t off) -{ - return (g_view == VIEW_RAW_WRAP) ? raw_prev_wrap(off) : raw_prev_line(off); -} - -/* ---- статус-бар: % по байтам (отрисовка — render_raw_status_numbers - * в mdview2_status.c, читает позицию через этот raw_pct) ---- */ - -/* Процент по байтам без 32-битного деления: масштабируем offset'ы (>>8 → - * ≤1024), дальше 16-битный pct16 (без __divulong). raw_top приватен модулю, - * поэтому % считаем здесь и отдаём готовым числом в статус-модуль. */ -uint8_t calc_raw_pct(void) -{ - uint16_t a = (uint16_t)(raw_top >> 8); - uint16_t b = (uint16_t)(file_size >> 8); - return pct16(a, b); /* den==0 (файл < 1 КБ) → pct16 вернёт 0 */ -} - -/* ---- посев позиции / пересев при смене активного буфера ----------- */ - -uint32_t raw_pos(void) -{ - return raw_top; -} - -/* Поставить RAW на начало строки, содержащей байт off (позиционирование - * при F2 MD→RAW и F8-переносе позиции между наборами). */ -void raw_reanchor(uint32_t off) -{ - if (off > file_size) off = file_size; - raw_top = raw_line_start(off); - raw_hpan = 0; -} - -void raw_seed_from(uint16_t md_top_line) -{ - raw_reanchor(seg_off(md_top_line)); -} - -/* Сброс в начало (клавиша Home). */ -static void raw_home(void) -{ - raw_top = 0; - raw_hpan = 0; -} - -void raw_end(void) -{ - raw_top = file_size; /* отмотать VIEW_H рядов назад */ - for (uint8_t k = 0; k < VIEW_H; k++) - raw_top = raw_prev(raw_top); - raw_hpan = 0; -} - -void raw_screen_init(void) { - bios_clearwin(1, 0, 30, 80, ATTR_TEXT); -} - -/* Нормализация позиции при смене под-режима RAW (F3 Wrap/Unwrap): выровнять - * raw_top на начало физической строки и снять горизонтальный сдвиг. */ -void raw_renorm(void) -{ - raw_top = raw_line_start(raw_top); - raw_hpan = 0; -} - -void raw_scroll_down(uint8_t delta) { - uint32_t off = raw_top; - for (uint8_t r = 0; r < VIEW_H; r++) { - if (off >= file_size) - return; /* экран не заполнен контентом */ - off = raw_next(off); - } - if (off >= file_size) - return; /* контент ровно по экран, ниже пусто */ - - if(delta ==1) { - scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 1, 0); /* содержимое вверх */ - off = raw_draw_one(off, (uint8_t)(VIEW_TOP_ROW + VIEW_H - 1)); /* новый нижний ряд */ - raw_top = raw_next(raw_top); - } else { - for (uint8_t k = 0; k < delta; k++) { - uint32_t n2 = raw_next(raw_top); - if (n2 >= file_size) - break; - raw_top = n2; - } - raw_draw(); /* PgUp/PgDn/Home/End — полный экран */ - } -} - -void raw_scroll_up(uint8_t delta) { - if (raw_top == 0) - return; - - if (delta == 1) { - raw_top = raw_prev(raw_top); - scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 2, 0); /* содержимое вниз */ - (void)raw_draw_one(raw_top, VIEW_TOP_ROW); - } else { - for (uint8_t k = 0; k < delta; k++) - raw_top = raw_prev(raw_top); - raw_draw(); /* PgUp/PgDn/Home/End — полный экран */ - } -} - -/* ---- навигация (вызывается из главного цикла при g_view != VIEW_MD) - */ - -uint8_t raw_key(uint8_t scan) -{ - switch (scan) { - case KEY_DOWN: - raw_scroll_down(1); - break; - case KEY_UP: - raw_scroll_up(1); - break; - case KEY_PGDN: - raw_scroll_down(VIEW_H); - break; - case KEY_PGUP: - raw_scroll_up(VIEW_H); - break; - case KEY_HOME: - raw_home(); - raw_draw(); - break; - case KEY_END: - raw_end(); - raw_draw(); - break; - case KEY_LEFT: - if (g_view == VIEW_RAW_HSCROLL && raw_hpan) { - raw_hpan = (raw_hpan >= HPAN_STEP) ? (uint16_t)(raw_hpan - HPAN_STEP) : 0; - raw_draw(); - } - break; - case KEY_RIGHT: - if (g_view == VIEW_RAW_HSCROLL && raw_hpan < 248u) { - raw_hpan = (uint16_t)(raw_hpan + HPAN_STEP); - raw_draw(); - } - break; - default: - return 0; /* не наша клавиша */ - } - - // render_raw_status_numbers(); - render_percent_progress(calc_raw_pct()); - return 1; -} - -#endif /* WITH_RAW */ diff --git a/examples/mdview2/mdview2_status.c b/examples/mdview2/mdview2_status.c deleted file mode 100644 index 6a0c32f..0000000 --- a/examples/mdview2/mdview2_status.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * mdview2_status.c — статус-бар (строка 0), нижнее меню (строка 31) и спиннер. - * - * Хром бара АТОМИЗИРОВАН — обновляется по частям, без перерисовки всего бара: - * prerender_status() — фон + разделители │ + "MDVIEW" + имя файла (1 раз); - * status_encoding() — только поле кодировки (col 37), по F8; - * render_md_status_numbers() — числа MD: диапазон строк + % (при скролле); - * render_raw_status_numbers()— числа RAW: метка режима + % по байтам; - * render_full_status() — encoding + numbers (MD), при смене набора/режима; - * render_menu() — строка меню, по смене режима/готовности; - * spinner_tick/show — индикатор занятости во время загрузки. - * Оба *_status_numbers() самоочищаются по геометрии разделителей (DIV1_X/DIV2_X - * в mdview2.h) — фикс. хром не перерисовывается. RAW-позицию даёт raw_pct() - * из mdview2_raw.c (raw_top там приватен). - * - * Раскладка строки 0: - * 0 1..6 8 10.. 37..43 45 46..70 71 72..79 - * . MDVIEW spinner filename encod. │ L a-b / total │ pct% - */ - -#include -#include /* dec8/dec16, cputs */ -#include /* textattr/gotoxy/wrchar/COLOR */ -#include -#include /* bios_fillcharattr/bios_write_until */ -#include "mdview2.h" - -/* ---- Спиннер (слот SPINNER_COL в баре) --------------------------- */ -static const char spinner_chars[4] = { '|', '/', '-', '\\' }; -static uint8_t spinner_phase = 0; -static uint8_t spinner_active = 0; - -/* Продвигает спиннер на один кадр (если включён). */ -void spinner_tick(void) -{ - if (!spinner_active) - return; - wrchar(SPINNER_COL, 0, spinner_chars[spinner_phase & 3], ATTR_BAR_SPINNER); - spinner_phase++; -} - -/* Вкл/выкл спиннер; при выключении гасит его позицию. */ -void spinner_show(uint8_t on) -{ - spinner_active = on; - if (!on) wrchar(SPINNER_COL, 0, ' ', ATTR_BAR); -} - -/* ---- Вычисления для чисел статус-бара ----------------------------- */ - -/* Число строк, готовых к показу. Во время загрузки последняя эмитированная - * строка ещё в g_cells (не зафлашена), поэтому доступны [0..n_lines-2]. */ -uint16_t drawable_lines(void) -{ - if (g_loading) - return (n_lines >= 1) ? (uint16_t)(n_lines - 1) : 0; - return n_lines; -} - - -/* ---- Атомарные части статус-бара ---------------------------------- */ - -/* Фиксированный хром бара — рисуется ОДИН раз при старте: фон, два - * разделителя │, метка "MDVIEW" и имя файла (они не меняются за сессию). */ -void prerender_status(void) -{ - fill_row(0, ATTR_BAR); - wrchar(DIV1_X, 0, 0xB3, ATTR_BAR); /* │ */ - wrchar(DIV2_X, 0, 0xB3, ATTR_BAR); /* │ */ - put_str_attr(1, 0, "MDVIEW", ATTR_BAR); - put_str_attr(10, 0, filename, ATTR_BAR); -} - -/* Поле текущей кодировки (col 37, 7 ячеек). Меняется только по F8. */ -void status_encoding(void) -{ - textattr(ATTR_BAR); - gotoxy(DIV1_X - 10, 0); - bios_write_until(enc_name(g_encoding), 8, 0); -} - -static uint16_t local_total = UINT_MAX; -static uint16_t local_last = UINT_MAX; -static uint8_t local_loading = UCHAR_MAX; -static uint8_t local_pct = UCHAR_MAX; - -/* Числовая часть (MD): "L a-b / total" между разделителями + "pct%" справа. - * Перезаписывает ТОЛЬКО области между/после разделителей, сами │ и фикс. - * хром не трогает. */ -void render_md_status_numbers(void) -{ - uint16_t total = drawable_lines(); - uint16_t last = top_line + VIEW_H; - if (last > total) - last = total; - - if(local_total != total || local_last != last || local_loading != g_loading) { - local_total = total; local_last = last; local_loading = g_loading; - textattr(ATTR_BAR); - gotoxy(DIV1_X + 2, 0); - bios_fillcharattr(' ', ATTR_BAR, DIV2_X - DIV1_X - 2); /* очистить [DIV1_X+1 .. DIV2_X-1] */ - gotoxy(DIV1_X + 2, 0); - cputs("L "); - dec16(top_line + 1); - cputs("-"); - dec16(last); - cputs(" / "); - dec16(total); - if (g_loading) cputs("..."); /* ещё грузится */ - } - -} - -/* Полное обновление переменной части (MD): кодировка + числа. */ -void render_full_status(void) -{ - local_loading = UCHAR_MAX; - status_encoding(); - render_md_status_numbers(); -} - -void render_percent_progress(uint8_t pct) { - if (local_pct != pct) { - local_pct = pct; - gotoxy(DIV2_X + 2, 0); - bios_fillcharattr(' ', ATTR_BAR, SCREEN_W - DIV2_X - 2); /* очистить [DIV2_X+1 .. конец] */ - gotoxy(DIV2_X + 2, 0); - dec8(pct); - cputs("%"); - } -} - -#if WITH_RAW -/* RAW-вариант числовой части бара (peer к render_md_status_numbers): метка - * режима между разделителями + процент справа. Сам очищает свои области (та же - * геометрия DIVn_X), атомарен — не требует общего fill всего бара. Позиция - * берётся готовым числом из raw_pct() (raw_top приватен mdview2_raw.c). */ -void render_raw_status(void) -{ - // uint8_t pct = raw_pct(); - textattr(ATTR_BAR); - gotoxy(DIV1_X + 1, 0); - bios_writeattr_until(g_view == VIEW_RAW_WRAP ? " RAW wrap" : " RAW pan", - DIV2_X - DIV1_X - 1, ATTR_BAR, 0); /* метка + пад до DIV2_X-1 */ -} -#endif /* WITH_RAW */ - -#if WITH_HEX -/* HEX-вариант числовой части бара (peer к render_raw_status). */ -void render_hex_status(void) -{ - textattr(ATTR_BAR); - gotoxy(DIV1_X + 1, 0); - bios_writeattr_until(" HEX", DIV2_X - DIV1_X - 1, ATTR_BAR, 0); -} -#endif /* WITH_HEX */ - -/* ---- Нижнее меню (строка 31) -------------------------------------- */ - -void render_menu(void) -{ - /* Блок i = колонка i*8 (8 шириной): 2 поз. НОМЕР клавиши без 'F' (стиль - * ATTR_MENU_K, ведущий «чёрный» пробел кроме F10) + 6 поз. ТЕКСТ-функция - * (ATTR_MENU_T) сразу за номером. Номера рисуем для ВСЕХ 10 клавиш; текст — - * только у задействованных и доступных сейчас. */ - char num[3]; - - fill_row(MENU_ROW, ATTR_MENU_T); - - num[2] = 0; - for (uint8_t i = 0; i < 9; i++) { /* F1..F9: ' 1'..' 9' */ - num[0] = ' '; num[1] = (char)('1' + i); - put_str_attr((uint8_t)(i * 8), MENU_ROW, num, ATTR_MENU_K); - } - put_str_attr(9 * 8, MENU_ROW, "10", ATTR_MENU_K); /* F10 */ - - put_str_attr(0 * 8 + 2, MENU_ROW, "Help", ATTR_MENU_T); -#if WITH_RAW - if (g_ready) /* RAW доступен после постройки документа */ - put_str_attr(1 * 8 + 2, MENU_ROW, (g_view == VIEW_MD) ? "RAW" : "MD", ATTR_MENU_T); - if (VIEW_IS_RAW(g_view)) /* Wrap/Unwrap — только в RAW */ - put_str_attr(2 * 8 + 2, MENU_ROW, - (g_view == VIEW_RAW_WRAP) ? "UnWrap" : "Wrap", ATTR_MENU_T); -#endif -#if WITH_HEX - if (g_ready) /* HEX доступен после постройки документа */ - put_str_attr(3 * 8 + 2, MENU_ROW, (g_view == VIEW_HEX) ? "Back" : "Hex", ATTR_MENU_T); -#endif - if (g_f8_enabled) /* смена кодировки сейчас возможна */ - put_str_attr(7 * 8 + 2, MENU_ROW, "CodePg", ATTR_MENU_T); - put_str_attr(9 * 8 + 2, MENU_ROW, "Exit", ATTR_MENU_T); -} diff --git a/examples/mdview2/mdview2_table.c b/examples/mdview2/mdview2_table.c deleted file mode 100644 index d0d3aa5..0000000 --- a/examples/mdview2/mdview2_table.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * mdview2_table.c — выровненная отрисовка markdown-таблиц. - * - * Разбор |-разделённых ячеек и эмиссия рамок/строк в общий буфер ячеек - * рендера (gc_put/gc_fill, см. mdview2.h). Вызывается из index_lines() - * ядра при обнаружении таблицы. Геометрия рамок (TBL_n), словарь стилей - * (CK_n, INIT_STYLE_n) и cell-buffer API объявлены в mdview2.h. - */ - -#include -#include /* COLOR() / COLOR_* для атрибутов TBL_ATTR/ATTR_TEXT */ -#include "mdview2.h" - -uint32_t row_end(uint32_t p) -{ - while (p < file_size && fb(p) != '\n') p++; - return p; -} - -/* Начало контента первой ячейки строки (после ведущих пробелов и '|'). */ -uint32_t table_first_cell(uint32_t row_start) -{ - uint32_t p = row_start; - while (p < file_size && fb(p) == ' ') p++; - if (p < file_size && fb(p) == '|') p++; - return p; -} - -/* Следующая ячейка: [*cs,*ce) — обрезанный диапазон; *pp продвигается за '|'. - * 0 — ячеек больше нет (включая хвостовую пустоту после последнего '|'). */ -uint8_t table_next_cell(uint32_t *pp, uint32_t lineend, uint32_t *cs, uint32_t *ce) -{ - uint32_t p = *pp; - if (p >= lineend) return 0; - uint32_t a = p; - while (p < lineend && fb(p) != '|') p++; - uint32_t b = p; - uint8_t had_pipe = (uint8_t)(p < lineend && fb(p) == '|'); - if (had_pipe) p++; - *pp = p; - while (a < b && fb(a) == ' ') a++; - while (b > a && fb(b - 1) == ' ') b--; - if (!had_pipe && a == b) return 0; - *cs = a; *ce = b; - return 1; -} - -/* Строка-разделитель (|---|:-:|): каждая ячейка непустая и только '-'/':'. */ -uint8_t table_is_sep_row(uint32_t row_start, uint32_t lineend) -{ - uint32_t p = table_first_cell(row_start); - uint32_t cs, ce; - uint8_t n = 0; - while (table_next_cell(&p, lineend, &cs, &ce)) { - if (cs >= ce) return 0; - for (uint32_t q = cs; q < ce; q++) { char c = fb(q); if (c != '-' && c != ':') return 0; } - n++; - } - return (uint8_t)(n > 0); -} - -/* Горизонтальная рамка в g_cells: left + (H×(w+2) + mid|right) по колонкам. */ -void table_border(const uint8_t *widths, uint8_t ncols, char left, char mid, char right) -{ - gc_put(left, TBL_ATTR); - for (uint8_t c = 0; c < ncols; c++) { - gc_fill(TBL_H, TBL_ATTR, (uint8_t)(widths[c] + 2)); - gc_put((c == (uint8_t)(ncols - 1)) ? right : mid, TBL_ATTR); - } -} - -/* Строка данных в g_cells: │ <ячейка, добитая до widths[c]> │ … */ -void table_data_row(uint32_t row_start, uint32_t lineend, const uint8_t *widths, uint8_t ncols) -{ - uint32_t p = table_first_cell(row_start); - uint32_t cs, ce; - gc_put(TBL_V, TBL_ATTR); - for (uint8_t c = 0; c < ncols; c++) { - uint8_t got = table_next_cell(&p, lineend, &cs, &ce); - gc_put(' ', TBL_ATTR); - uint8_t rendered = 0; - if (got && ce > cs) { - uint8_t before = g_ncells; - (void)inline_scan(cs, ce, 0, CK_OTHER, INIT_STYLE_PLAIN, ATTR_TEXT, 1); - rendered = (uint8_t)(g_ncells - before); - } - if (rendered < widths[c]) gc_fill(' ', ATTR_TEXT, (uint8_t)(widths[c] - rendered)); - gc_put(' ', TBL_ATTR); - gc_put(TBL_V, TBL_ATTR); - } -} diff --git a/examples/mdview2/testfiles/BIG.MD b/examples/mdview2/testfiles/BIG.MD deleted file mode 100644 index c425eab..0000000 --- a/examples/mdview2/testfiles/BIG.MD +++ /dev/null @@ -1,3278 +0,0 @@ -# MDView ? ᬮ騪 Markdown Sprinter - -**MDView** ? ணࠬ ᬮ 㬥⮢ ଠ *Markdown* -Sprinter ( Z80). 㬥 ࠭ ⤥쭮 W3 -᭮ RAM ணࠬ. - -## - -- 㬥 **128 ** (8 ࠭ EMM 16 ) -- **16 384** ࠭ ப -- ⮬᪨ ७ ᫮ ਭ ࠭ (80 ⮫殢) -- ਧ⠫ ᤢ ப ப ( , ⠡) -- -: 䠩, ப, 業 ப⪨ -- ப ﭨ ६ 㧪 樨 -- প ?? ᪫ ப - -## - -``` -mdview [_䠩.md] -``` - -᫨ 䠩 , 㦠 `README.MD`. - -## ࠢ - -``` - ⢨ - -Up Down ப⪠ ப / -PgUp PgDn ப⪠ ࠭ (30 ப) -Home 砫 㬥 -End 㬥 -Left Right ਧ⠫ ᤢ (⮫쪮 nowrap-ப) -F1 ࠢ -F8 ஢: CP866 ? CP1251 ? KOI8-R ? UTF-8 ? ... -F10 / Esc 室 ணࠬ -``` - -஢ । ⮬᪨ ⨨ (BOM + ⨪ - 4 ); `F8` ४砥 , ᫨ ⥪ 訡. -8- ஢ (CP866/CP1251/KOI8-R) ४ (६ -ᮢ). ன / (ਬ UTF-8) ந ? -ࢮ ४祭 (⪠ 㧠 ᯨ஬), . - -## ⠪ Markdown - -### - -ন ஢ H1?H4. ஢ H5 H6 ⮡ࠦ H4. - - # ࢮ ஢ - ## ண ஢ - ### 쥣 ஢ - #### ⮣ ஢ - -### ⮢ ଠ஢ - -** ⥪** 뤥 묨 񧤮窠: `**⥪**` - -*ᨢ* 뤥 묨 񧤮窠 `*⥪*` ન `_⥪_` - -`஥ ` 砥 묨 窠 - -~~ભ ⥪~~ ? ⨫: `~~⥪~~` - -### 㬥஢ ᯨ᮪ - -થ `-`, `*` `+`: - -- 㭪 ᯨ᪠ -- ன 㭪 ᯨ᪠ -- ⨩ 㭪 筮 ⥪⮬, 室 - 㤥 ७ ᫥ ப ࠭ 㯠 - -### 㬥஢ ᯨ᮪ - -1. -2. ன -3. ⨩ - -### - -> 稭 ᨬ `>`. ᪮쪮 ᫥⥫ -> ப ᪫ ⮬᪨ -> ७ᮬ ᫮. - -### (verbatim) - - 砥 ன 窨. -⥪ ⮡ࠦ ? ? ࠧ Markdown: - -``` -#include -#include - -int main(void) { - puts("Hello, Sprinter!"); - return 0; -} -``` - -### ਧ⠫쭠 - - ᨬ `---`, `***` `___` ⤥쭮 ப: - ---- - -## 孨᪨ ࠪ⨪ - -- **ଠ:** Sprinter, Z80 @ 21 -- **஢:** CP866 / CP1251 / KOI8-R / UTF-8 (⮮।, `F8`) -- **ᨬ ࠧ 䠩:** 128 -- **ᨬ쭮 ᫮ ப :** 16 384 -- **ᨬ쭠 ப ७-:** 255 祥 (. ࠭祭 ) -- ** :** small - - ணࠬ, c⥪, , ? a W1-W2 (32 , 0x4000?0xBFFF). - - 䠩 ? ࠭ EMM, ⮡ࠦ W3 (0xC000?0xFFFF) - -## ࠭祭 - -### ப MD-० ? 255 祥 - -- ࠭ ப ** 255 (ᨬ, ਡ)** -? ⠭ `MAX_CACHE_LINE_LEN`. ⨯: `g_ncells` -`cache_rec_t.len` ? `uint8_t`. ᠥ ப, ⭥ ᥣ -**ਧ⠫쭮 ஫㥬** ப ( ப ⠡, 䫠 -`IF_HSCROLL`), MD-० ࠢ. - -** ந室 ப:** 樨 `gc_put()` -뢠 祩 ᫥ 255- (`if (g_ncells < MAX_CACHE_LINE_LEN)`). - ⮫쪮 255 祥, ⮪ **** ? -஫ ** થ १** 255- 樨 (થ `>` 砥 -? । ?). ९ ? `gc_put` ஢ ࠭. - -: ?255 祥? ? **७७ ਭ**, 室. - ࠧ稢 ஡ ( `TAB_STOP`), 祩 ⠡ -஡ ਭ + ࠬ `` ? ⮬ ⨣ ࠭, 祬 -255 ?? ᨬ. - -> **RAW-० (`F2`) ⮣ ** ? אַ 䠩 -> ⮢, ப 楫 (१ wrap `F3` ਧ. ஫). - -** ** (業, ॠ) ? `uint16_t`: -- `cache_rec_t.len` `uint8_t?uint16_t` ? **஢ 8 ** - (len ꥤ pad-), `idx<<3` . ᯫ⭮. -- `g_ncells` / `g_ncells_at_space` ? `uint16_t` ? 業 **/᪮**: - 16-⭠ 䬥⨪ Z80 祬 `gc_put` (맮 祩) - scan-横. ਥ: **+0.2?0.4 ** + 樨. -- `g_cells[MAX_CACHE_LINE_LEN*2]` near-RAM (W2) ? 業 - ****: `2 ? ` . 510 ; 512 ? +0.5 , 1024 ? +1.5 . - EMM- ⥭ (1 ) ப ﭥ ? 㧪 near. -- `viewport_x` ( `docset_t`) + ⮫ `max_vx` (ᥩ 248) ? `uint16_t`, - ࠭ >255 , ஫ . `widths[]`/`ccx` ⠡/. - - ஬ ? 512?1023: 墠 ᥬ ⨭/⠡栬, - 業 ~+0.3 +0.5?1.5 near-RAM. ** 㬥 `uint16_t`** ? - ⠢ થ १ 255- 樨, ⮡ 뫠 . - -## TODO - -1. **祭 ࠧ 㬥⮢.** 128 : 筮 - ࠧ ࠡ 訬 - ࠭ , ஢ - ࠡ 8- ࠭栬 16. - -: ଠ஢ ⠡ ࠬ; প ஢ -CP866 / CP1251 / KOI8-R / UTF-8 ⮮। ४祭 `F8` -(ன ந , ࢮ ४祭). - -4. **᪮७ ७ਭ.** ப ࠭. ⨬ 横 뢮 - ᨬ १ BIOS WRCHAR ( 뢮, DMA). - ---- - -*MDView v0.2 (c) 2026 ஢ ..* - - ---- - -# Sprinter C Compiler ? v1.0 - -C toolchain for **Sprinter** ? the Z80-based home computer by Peters Plus, running -ESTEX DSS. Host: macOS / Linux. Target: `.EXE` files in SprintEXE format. - -Built on top of **SDCC 4.5** (vendored in `third_party/sdcc/`). This repository adds -everything Sprinter-specific: crt0, linker integration, libc wrappers over ESTEX, -banked-call trampolines, graphics & accelerator API, mouse driver wrappers, and the -`mkexe` utility for producing SprintEXE images. - -## What you get - -* **`bin/sprinter-cc`** ? one-line driver: `sprinter-cc -o foo.exe foo.c` -* **Memory modes**: `tiny`, `small`, `big`, `huge`, `manual` ? see below. -* **stdio + conio**: printf, puts, putchar, getchar, fopen/fread/..., cprintf, cputs, putch, textcolor/textbackground/textattr, gotoxy, kbhit/getch. -* **Graphics**: 320?256?256 and 640?256?16 modes, accelerator-backed primitives (hline / vline / rect / fill_rect / line via Bresenham, plus clear), bitmap-font text in both modes via BIOS character generator. -* **File I/O**: POSIX (`open`/`read`/`write`/`close`/`lseek`/`unlink`/`creat`), FILE\* streams (`fopen`/`fgets`/`fwrite`/...), directory listing (`ffirst`/`fnext`), `chdir`/`getcwd`/`mkdir`/`rmdir`, `stat`/`fstat`. -* **Memory**: 32 KB heap (W2-resident), banking-aware page allocator (`mem_alloc_pages`/`bank_read`/`bank_write`), explicit memory modes for sub-16 KB programs. -* **Mouse**: full Sprinter driver wrapper (14 functions including custom cursor bitmaps). -* **Environment**: `getenv`/`putenv`/`sysenv` over ESTEX `$46`. -* **Time**: `getdatetime`/`setdatetime` + POSIX `time`/`localtime`/`mktime`/`asctime`/`ctime`. -* **Misc**: `errno`/`strerror`/`perror`, `atexit`, `setjmp`/`longjmp`, `sleep`, full argv parsing in crt0. - -## Quick start - -```sh -git clone sprinter-c -cd sprinter-c -make sdcc # one-time: fetch SDCC 4.5 binary (~25 MB) -make all # build mkexe + libsprinter.lib + 27 examples -make floppy # pack everything into mame/v306/IMG/mc.img -cd mame/v306 && ./run_mame.sh # boot Sprinter in MAME -``` - -Compile a single program: - -```sh -cat > hello.c < -int main(void) { puts("Hello, Sprinter!"); return 0; } -EOF - -bin/sprinter-cc -o hello.exe hello.c -``` - -That's it ? `hello.exe` is now a valid SprintEXE you can `RUN HELLO` from the ESTEX shell. - -## Memory modes - -Sprinter's address space is four 16 KB windows (W0 / W1 / W2 / W3). DSS allocates -pages by program size ? small programs get only one page. Pick a memory mode based -on what your program needs: - -| Mode | Code lives in | Banking | Use when | Note | -|---|---|---|---|---| -| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | | -| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | | -| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | | -| `huge` | W1-W2 + W3 banking | yes (W3) | small + extra code modules | | -| `manual` | user-specified | optional | special layouts | Not implemented | - -```sh -sprinter-cc --memory small -o big.exe bigprog.c -sprinter-cc --memory huge -o app.exe main.c --bank 1=engine.c --bank 2=ai.c -``` - -Banked functions are declared with `__banked`: - -```c -void engine_tick(int dt) __banked; // lives in BANK1, automatically swapped -``` - -## Examples (27 total) - -| Example | What it demonstrates | -|---|---| -| `hello` | Hello world with stdio + conio Turbo-C-style colors | -| `argv` | argv parsing in crt0 | -| `cat` | File I/O ? read & print TEST.TXT | -| `seek` | 32-bit lseek over a 100 KB file | -| `ls` | Directory listing via ffirst/fnext | -| `filetest` | FILE\* streams (fopen/fread/...) | -| `stattest` | `stat`/`fstat` on files and directories | -| `errno` | errno / strerror / perror | -| `mem_test` | Page allocator + bank\_read/bank\_write | -| `malloc` | Heap stress test (200+ allocations) | -| `banked` | Banked code in W3 (huge mode) | -| `bankedbg` | Banked code in W1 (big mode) | -| `banklocl` | Bank-local static data and BSS | -| `mouse` | Mouse driver in text mode | -| `gfx_mous` | Mouse with custom bitmap cursor in graphics mode | -| `gfx_demo` | 320?256?256 lines / rects / accelerator | -| `gfx_d16` | 640?256?16 same primitives | -| `gfx_text` | Bitmap-font text on graphics screen | -| `timedir` | Date/time + directory listing | -| `ptime` | POSIX time API | -| `openenv` | open() flags + environment vars | -| `conio` | conio API smoke test | -| `attrprob` | Probe Sprinter text attribute byte layout | -| `strtest` | string.h test (from SDCC's z80.lib) | -| `stdlib` | stdlib.h test (qsort / rand / strtol / etc.) | -| `assrtest` | assert() | -| `rt_test` | Runtime helpers (sleep, setjmp, atexit) | - -## Headers - -Standard: -* `` ? puts / printf / FILE\* + Sprinter-specific dec/hex helpers -* `` ? atoi / atof / malloc / qsort / ... (from SDCC z80.lib) -* `` / `` / `` ? from SDCC z80.lib -* `` ? read / write / close / lseek / unlink -* `` ? open / creat + O\_RDONLY / O\_CREAT / ... -* `` ? errno + error names + strerror -* `` ? stat / fstat -* `` / `` ? from SDCC - -Sprinter-specific: -* `` ? putch / cputs / cprintf, textcolor / textbackground / textattr, kbhit / getch, clrscr, gotoxy, wherex/y -* `` ? gfx_init/done, palette, putpixel, hline/vline/rect/fill_rect/line, text ? for both 320 and 640 modes (gfx_\*16 variants) -* `` ? full 14-function driver wrapper + mouse_cursor_t with bitmap support -* `` ? chdir / getcwd / mkdir / rmdir / ffirst / fnext / ffblk -* `` ? getdatetime / setdatetime + POSIX time / localtime / etc. -* `` ? raw ports, ESTEX/BIOS function numbers, env API -* `` ? exit / \_exit / atexit -* `` ? mem\_alloc\_pages / mem\_free\_block / bank\_read / bank\_write -* `` ? Solid-C compatibility layer (aliases + BOOL/WORD/uint types) - -## Toolchain commands - -```sh -make all # build mkexe + lib + every example -make floppy # repack mame/v306/IMG/mc.img with all .exe files -make check # 17 mkexe unit-tests -make clean # remove all build artefacts -make sdcc # one-time: fetch SDCC 4.5 binary -``` - -### sprinter-cc options - -``` -sprinter-cc -o foo.exe foo.c [more.c ...] [options] - - --memory MODE tiny | small | big | huge | manual (default: tiny) - --memory-manual SPEC explicit placement (CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3) - --stack-size N bytes reserved for the stack (default ~1278) - --crt0=TYPE default | minimal | banked | small - --bank N=FILE.c compile FILE.c into bank N (repeatable, max 15) - --debug enable runtime diagnostics (defines DEBUG_RT) - -I PATH extra include path - -L 0xADDR / -E / -S override load / entry / stack addresses - -Wl FLAG pass FLAG to sdldz80 - --mkexe FLAG pass FLAG to mkexe (e.g. --mkexe -p --mkexe 0 for bank padding) - -v verbose -``` - -## Status - -What works in v1.0: -* Compile / link / pack to SprintEXE ? verified on all 27 examples -* Four memory modes (tiny / small / big / huge) -* Graphics (both modes) with accelerator -* Mouse (text + graphics cursor) -* File I/O, directories, environment, time -* All headers listed above - -Deferred to v2.0 (see `docs/TODO.md`): -* **Turbo-C-style BGI graphics API** ? `initgraph` / `setcolor` / `circle` / - `getimage` / `putimage` / etc. on top of our `gfx_*` primitives -* Remaining Solid-C compatibility gaps (Phase 2/3) ? see `docs/solid_c_compatibility.md` -* Manual memory mode -* Rewrite FILE\* stream API (current implementation is very primitive and doesn't use buffers) - -Deferred to v3.0: -* **IM2 interrupt handlers** ? research complete (`docs/im2_isr_design.md`), - implementation scheduled for v3 -* **Audio API** (AY-3-8910 + COVOX) ? requires IM2 -* **ISA-8 slot drivers** ? requires IM2 (???) - -## Documentation - -* `docs/TODO.md` ? roadmap and open work items -* `docs/solid_c_compatibility.md` ? gap analysis vs Solid-C 2004 -* `docs/im2_isr_design.md` ? interrupt handler design (v2) -* `docs/converted/` ? source documentation (ESTEX, BIOS, architecture) - converted to plain text for `grep` -* `docs/reference/`, `docs/samples/`, `docs/memory management/` ? original - Russian docs and code samples from Peters Plus - -## Repository layout - -``` -bin/sprinter-cc one-line compiler driver (bash) -toolchain/mkexe/ host-side tool: .ihx -> .exe SprintEXE -toolchain/check_banks.py post-link bank size enforcer -runtime/ crt0 variants (default, minimal, small, banked) - bank trampolines, heap, heap_top -libc/include/ headers -libc/io|stdio|mem|gfx/ C and asm sources for libsprinter.lib -lib/ Makefile that archives libsprinter.lib via sdar -examples/ 27 example programs -mame/v306/ MAME binary + Sprinter ROM/HDD images + floppy script -third_party/sdcc/ vendored SDCC 4.5 (fetched via `make sdcc`) -third_party/solid-c/ reference: original Sprinter native C (for compat target) -docs/ documentation -``` - -## License - -This repository contains: -* Original code in `bin/`, `toolchain/`, `runtime/`, `libc/`, `lib/`, `examples/` ? - MIT-licensed. -* `third_party/sdcc/` ? SDCC 4.5 under GPLv2 with linking exception - (see `third_party/sdcc/COPYING.txt`) -* `third_party/solid-c/` ? original Sprinter Solid C, used only as a reference - -## Credits - -* **Sprinter / Peters Plus** ? , ਩ ਭ and the original team -* **SDCC** ? for the underlying Z80 compiler -* **MAME** ? for the Sprinter Sp2000 emulation - ---- - -For questions / patches: see CONTRIBUTING.md (TBD) or open an issue. - ---- - -# TODO / Roadmap - - 浪 뢠 ਮ.  ॠ ணࠬ ? ਮ ᬥ. - -## ⠯ 5 ? malloc / free + banking-aware page allocator ? - -- [x] SDCC's `malloc`/`free` + `runtime/heap.s` ( library heap.rel, 14000- heap 2) -- [x] `libc/mem/mem_alloc.c` ? page allocator: `mem_alloc_pages`/`mem_free_block`/`mem_get_page`/`mem_info` १ ESTEX `$3C/$3D/$3E` + BIOS `$C4` -- [x] `libc/mem/bank_io.c` ? HOME-१ `bank_read`/`bank_write`/`bank_load_byte`/`bank_store_byte` ᢮ W3 -- [x] `examples/malloc_test/` ? ஢ઠ SDCC's malloc (~210 64- allocations १ heap) -- [x] `examples/mem_test/` ? ஢ઠ page allocator: 3 ࠭, ࠧ १ bank_write, 䨪 १ bank_read - -## ⠯ 6 ? argv parsing + sprinter-cc wrapper ? - -- [x] crt0 ESTEX command-line IX-prefix (inline asm `runtime/crt0.s`) -- [x] Strip leading CP/M-style space (DSS quirk) -- [x] । `argc`/`argv` main() १ HL/DE (SDCC __sdcccall(1) ABI) -- [x] argv[0] = basename .EXE १ ESTEX APPINFO ($47 subfn 2) -- [x] `runtime/crt0_minimal.s` ? opt-out 祭 쪨 ணࠬ -- [x] `runtime/crt0_banked.s` ? ⥯ ⮦ argv (parse_argv + get_progname ᪮஢ crt0.s; 㤥 factored argv.s libsprinter.lib) -- [x] Bash-⪠ `bin/sprinter-cc`: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権: `--memory`, `--memory-manual`, `--stack-size`, `--crt0=`, `--bank N=FILE.c`, `--debug`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` - -## ⠯ 8 ? 䨪 (320?256?256 + 640?256?16 + accel + bitmap font) ? - -- [x] **8a** Graphics core: `gfx_init`/`gfx_done`/`gfx_clear`/`gfx_putpixel`/`gfx_pal_load`/`gfx_pal_set` (libc/gfx/gfx_core.c). १ BIOS PIC_SET_PAL ($A4). Verified 320?256?256. -- [x] **8b** /אַ㣮쭨/fill १ accelerator (libc/gfx/gfx_lines.c): `gfx_hline`/`gfx_vline` १ accel Fill (LD C,C / LD E,E + SMC block-size), `gfx_rect`/`gfx_fill_rect` heuristic 롮஬ ਥ樨 (h/v bursts count), `gfx_line` Bresenham . `gfx_clear` ⮦ ९ᠭ column-major accel (~4? ॥). -- [x] **8c** 640?256?16 mode (libc/gfx/gfx_16.c): `gfx_*16` API, HIGH nibble = LEFT pixel (㬥 misleading), per-row RMW vline ( = 2 ਧ⠫ ᥫ). -- [x] **8d** Bitmap font + gfx_text (libc/gfx/gfx_text.c): १ BIOS WIN_GET_ZG ($B8), interleaved layout `font[row*256+char]`, `gfx_text`/`gfx_putchar` 320 mode, `gfx_text16`/`gfx_putchar16` 640 mode pair-table lookup. - -. memory/sprinter_graphics.md, sprinter_accelerator.md, sprinter_graphics_16.md, sprinter_font_format.md. - - ( ): -- [ ] -quad 640: per-cell (mode 0x82 ࠧ蠥 1 4 per 16?8 cell) ? १ אַ area-ᠭ ࠭ 0x0300..0x039F - -## Auto-banking (. `memory/banking_roadmap.md` ⠫) - -Phase 1 ? file-level bin-packing ? ॠ뢠 ஥ ~30 KB . - -- [ ] `toolchain/auto_bank.py`: - - ࠧ `.rel`-䠩 ( .map ᫥ dry-run link') - - First-fit-decreasing bin-packing - - `#pragma codeseg BANKn` manual override - - ५뢠 묨 `-Wl-b_BANKn=...` ࠬࠬ - - ⠥ । - -Phase 2-5: incremental rebalance, declarative `banks.toml`, function-level, call-graph-aware. 쪮 ᫨/ . - -## Bank-local static data (mutable data ⮬ ) ? ? - -- [x] ਬ `examples/bank_local_data/` ? 㭪 BANK1 ᢮ writable BSS array + const table + malloc- -- [x] `mkexe -p 0` 㫥 padding (BSS-storage 㧪) -- [x] ᪨ 楯: `--codeseg BANK1 --constseg BANK1 --dataseg BANK1` bank1.c + `-Wl-b_BANK1=0x1C000` . **`--dataseg BANK1` ** ? ࠭ ⭮ - trampoline bug ᪨஢ १. -- [x] ** 䨪 trampoline'a runtime/bank.s** ? `pop af; out (n), a` ਫ A ? banked-㭪樨 騥 uint8_t 頫 . `pop bc; out (c), b` ࠭ A. -- [x] **malloc banked-㭪樨 ࠡ⠥ ஧筮** ? heap W2 (HOME), W2 ᢠ trampoline', pointer  ⥪. . memory/bank_local_data_pattern.md. -- [x] 㬥 memory: `memory/bank_local_data_pattern.md` ( 楯 + malloc + nuances), `memory/sdcc_banking.md` (trampoline fix) -- [ ] 樮쭮 ? `check_banks.py` ⮡ 뢠 ࠧ size = code + const + bss per bank (cosmetic) - -祬: 㫥 訬 private state (level loader, audio engine, scene data). W2 heap , ⨪ . - -## ᪠ solid-c (⨢ Sprinter C ? `third_party/solid-c/`) - -᫥ solid-c'᪮ libc (. `memory/solid_c_findings.md`)  ⮢ ᫥ 㭪権. ਮ **᮪** : - -### High-priority gaps ( ஢, 짠) -- [x] **`errno` + `strerror`/`perror`** ? ⠡窠 32 訡 (libc/io/errno.c) -- [x] **७ `open()`** O_CREAT/O_TRUNC/O_APPEND/O_EXCL state machine -- [x] **`atexit`** ? 8-callback LIFO + `exit()` + `_exit()` (libc/io/atexit.c) -- [x] **`setjmp`/`longjmp`** ? 6- jmp_buf={sp,ix,pc} (libc/io/setjmp.c) -- [x] **`sleep(seconds)`** ? 50Hz halt-loop (libc/io/sleep.c) -- [x] **ESTEX ENV API** ($46, getenv/putenv) ? libc/io/env.c. 竨 doc-bug: ॠ쭮 A=0 NOT FOUND - -### Medium-priority (㦭 shell-like ⨫) -- [ ] **Mouse driver** ? `rst $30h`, 17 㭪権. **砫 ࠡ⠥ MAME**. -- [x] **`ffirst`/`fnext` + ffblk_t struct** directory listing ? ॠ, demo: ls.exe -- [x] **`getdatetime`/`setdatetime`** १ ESTEX $21/$22 ? libc/io/time.c, demo: time_dir_test -- [x] **`chdir`/`getcwd`/`mkdir`/`rmdir`** ? wrappers ESTEX $1B-$1E ? libc/io/fsdir.c -- [x] **conio: `kbhit`/`getch`/`getche`/`cputs`/`clrscr`/`gotoxy`** ? ॠ -- [x] **conio extras**: `wherex`/`wherey` ($53), `wrchar`/`rdchar` ($58/$57), `textmode_get/set` ($50/$51), `clrscr_attr` ($56) + COLOR macros - -### Low-priority ? ? FILE* stack - -- [x] ** unbuffered FILE\*** ? `libc/stdio/file.c` + `libc/include/stdio.h`. fopen/fclose/fputs/fgets/fread/fwrite/fseek/ftell/rewind/feof/ferror/clearerr/fflush + stdin/stdout/stderr pseudo-streams. . `memory/file_star_design.md` `examples/filetest`. -- [ ] fprintf / fscanf ? 㦭 printf-१-callback machinery. 짮⥫ `sprintf(buf, ...) + fputs(buf, fp)`. -- [ ] 樮 buffered mode (setvbuf, line/block buffering) ? ᫨ - . - -### POSIX time API ? ? -- [x] `libc/io/posix_time.c` ? time/localtime/gmtime/mktime/asctime/ctime getdatetime. SDCC's time.rel ( override _RtcRead). . `examples/ptime`. - -### sys/stat ? ? -- [x] `libc/io/stat.c` ? POSIX stat/fstat. ਤ open+fstat 䠩, ffirst+iter ( "."/".."). . `examples/stattest` `memory/estex_ffirst_dotdot.md`. - -### assert ? ? (ᯮ㥬 SDCC's __assert १ fallback include path) - -## libc/stdlib ? ? 㦭 (. memory/sdcc_stdlib_works.md) - -஢७ १ `examples/stdlib_test/`: SDCC z80.lib ᮤন ࠡ騥 ॠ樨: -- `atoi/atol/atof, strtol/strtoul, rand/srand, qsort/bsearch, abs/labs, div/ldiv` -- `` (memchr/cmp/set/cpy, strcat/cmp/cpy/len/chr/spn/etc.) -- `` (toupper/tolower) -- `` (sinf/cosf/sqrtf/etc.) - - ⮬᪨ ﭥ z80.lib 㦭. ** ९뢠**. - - Sprinter-specific 易⥫ 㫨 : atexit, env, errno, setjmp, putchar/puts/getchar, conio, fsdir, time, mouse, open/read/lseek/close. - -## Build-system: libsprinter.lib + sprinter-cc ? ? - -- [x] `lib/Makefile` ? ᮡࠥ libc/*.c `.rel`, 娢 १ sdar `lib/sprinter.lib` -- [x] 砥 runtime/bank.s runtime/heap.s (auto-pulled __banked/malloc) -- [x] `bin/sprinter-cc` ? bash-wrapper: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権 `--crt0=default|minimal|banked`, `--bank N=FILE.c`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` -- [x] `examples/hello_sccc/` ? : `hello.c` ᮡࠥ shell-맮, ࠧ ᮢ Makefile (925 ) -- [x] Split `putchar.c` ? `putchar.c` + `puts.c` per-function granularity (puts override SDCC's z80.lib version) -- [x] `make all` (ᨬ `lib` । `examples`) - - 襭 (樮쭮): -- [ ] ஢ ⠫ examples sprinter-cc Makefile (ᬥ⨪) -- [ ] 쭥 libc/*.c per-function ( ⥪ granularity 㦥 㦭 ࠧ ? .rel 楫, 設⢠ 䠩 㭪) - -## ⠯ 9 ? memory modes sprinter-cc - -DSS 뤥 ࠭ ࠧ ਫ: < 16 KB ? ࠭, ⠫ 砥 ?࠭ #FF? (read=0xFF, write ). - ⮣ CODE--W1 + DATA--W2 쪮 ணࠬ . . [memory/sprinter_memory_modes.md](../../.claude/projects/-Volumes-SAM8-Projects-DIY-Z80-Sprinter-C-Compiler/memory/sprinter_memory_modes.md). - -- [x] **`tiny`**: (CODE+DATA+⥪) W2. Default. Verified hello/argv/conio/malloc/file/etc. -- [x] **`--memory MODE` 䫠 sprinter-cc**: parser + per-mode 䮫 CODE_LOC/DATA_LOC, override १  `--code-loc`/`--data-loc`. tiny ࠡ⠥; small/big/huge warning' (runtime ⮢). 2026-05-30. -- [x] **`--memory-manual SPEC`**: `CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3`. 2026-05-30. -- [x] **`small` runtime**: `runtime/crt0_small.s` ᯮ ESTEX `$3D GETMEM` + `$3A SETWIN2` ⮡ 뤥 W2-࠭ gsinit. **** BIOS `$C4` ? ⥪ ⮬ ⠯ W1 (boot_stack HOME), BIOS ॡ ⥪ W2. ᫥ SP ४砥 0xBFFE, ⠭ flow. 2026-05-30, verified hello.exe. -- [x] **`small` auto-detect >16 KB ணࠬ**: `crt0_small.s` ⠥ `0xC2` (⥪ ࠭ W2 ? `0xA2`! W1). ᫨ 0xFF ? 뤥 page; DSS 㦥 ᤥ (ணࠬ ᠬ 뫥 W2). crt0 뢠 0..30 KB. mkexe ⠪ ࠧ蠥 HOME span W1+W2 (0x4000..0xBFFF). Verified hello: small (5 KB 䠩, SETWIN2 path) + 32 KB 䠩 (auto-skip). 2026-05-30. -- [x] **`big` runtime** (tiny + banked code W1): ࠬਧ `crt0_banked.s` + `bank.s` १ `.ifdef BANK_W1` ? 㣮 banking port (0xA2 vs 0xE2), 㣮 load-addr (0x4000 vs 0xC000). sprinter-cc prepend' `BANK_W1 = 1` `--memory big`, । `mkexe -B 0x4000`. ਬ `examples/banked_big/`. 2026-05-30. -- [x] **`huge` runtime** (small + banked code W3): merge W2-detect `crt0_small.s` `crt0_banked.s`. 騩 ਬ `examples/banked/` ⥯ ᯮ MEMORY=huge. 2026-05-30. -- [x] **`--debug` 䫠**: prepend `DEBUG_RT = 1` crt0 + `-DDEBUG_RT` sdcc. 뢠 symbol `_w2_self_allocated` (uint8_t) ? runtime diagnostic ஢ W2. 2026-05-30. - -### -襭 libc crt0 - -** `sprinter.lib`** ࠡ⠥ memory mode ? `.rel`-童 relocatable, SDLD dead-code elimination per-member ( 䨪  `gfx_core.rel` ..). Verified hello vs malloc_test १ map-䠩. - -**`gfx.lib` ⤥쭮 ? 㦥**: dead-code elimination 㦥 ࠡ⠥. - -**`libc_banked` (libc bank HOME)** ? ⮬, HOME (16 KB) user- + libc `huge` mode. १ `--codeseg BANK0` 樨 libc; trade-off: trampoline ~30 横 libc-맮. ਣ: ॠ쭠 ணࠬ HOME budget. - -**HW-ᨬ 㫨 ? `sprinter_home.lib` ⤥쭮.** libc 䨧᪨ W3, ⮬ W3: -- `gfx_*` ? `0xC000+` ᫥ swap W3 video page -- `bank_io` (mem_alloc_pages/bank_read/bank_write) ? swap' W3 १ `OUT (0xE2)` -- 騥 ISR ? 뢠 ਩ W3 㣮 - - huge mode 㫨 HOME (W1). 㤥 `libc_banked`, **६** 뤥塞 `sprinter_home.lib` (HOME-only) `sprinter.lib` (bankable). 쭠 奬: -``` -sprinter_home.lib HOME-only: gfx, bank_io, ISR shims -sprinter.lib bankable: printf, malloc, string, conio, stdio, env, ... -sprinter_banked.lib sprinter.lib --codeseg BANK0 ( huge) -``` -ਣ: ॠ `--memory huge` runtime. - -**crt0 ? mode:** -- `crt0.s` ? ⥪騩, **tiny/big**: SP=0xBFFE, argv (W2- 㦥 뤥 DSS). -- `crt0_minimal.s` ? ⥪騩, tiny argv. -- `crt0_small.s` ? **, step 3**: **small/huge**, W2 १ `mem_alloc_pages` gsinit, `0xA2`, ⮬ ⠭ flow. -- `crt0_banked.s` ? ⥪騩, **big**: trampoline-⠡ W3 , CODE W2. -- `crt0_banked_small.s` ? ****: huge = small (W2-alloc) + banked (W3 trampolines). - -sprinter-cc ࠥ crt0 `--memory` mode (ᥩ `--crt0=` override). -- [x] **ࠨ ࠧ ⥪**: 䫠 `sprinter-cc --stack-size BYTES`. Wrapper `heap_top.s` `___sdcc_heap_end = stack_top + 1 - stack_size`, ⤥ .rel per-program. Default ?1278 (heap_top=0xBB00) `runtime/heap_top.s`. 2026-05-30. - -䥩: `sprinter-cc --memory [tiny|small|big|huge|manual] [--memory-manual SPEC] [--stack-size N] foo.c`. `--memory-manual` ⮫쪮 `--memory manual`. - -## Known issues / quirks - -- **ESTEX $46 ENV API**: ? ࠡ⠥. - `DiskSyscalls.txt v1.6` 訡筮 뢠 return-status ? A=0 NOT FOUND, FOUND. 䨪஢ `memory/sprinter_platform.md`. - -## V2 (᫥ ५ v1) - -### Turbo-C-style graphics API (BGI-like) ? **MUST v2** - - `gfx_*` API ஢ **Turbo-C ``** (BGI) MS-DOS. -ணࠬ ਢ訥 Turbo-C ७ ᪨ 1--1. - -** ** ( ᭮ Borland BGI): - -Setup/teardown: -- `initgraph()` / `closegraph()` ? ᥩ `gfx_init`/`gfx_done`, alias -- `getmaxx()` / `getmaxy()` ? GFX_WIDTH-1 / GFX_HEIGHT-1 -- `cleardevice()` ? alias to gfx_clear -- `getgraphmode()` / `setgraphmode()` ? get_videomode/set_videomode - -Color/palette: -- `setcolor(c)`, `getcolor()` ? current draw color -- `setbkcolor(c)`, `getbkcolor()` ? background color -- `setpalette(idx, c)` ? entry -- `getpalette(&info)` ? read all palette - -Primitives ( 㦥 ? BGI- aliases): -- `putpixel(x, y, c)` ? gfx_putpixel -- `getpixel(x, y)` ? 㦭 ॠ (RMW ⭮ ? IN) -- `moveto(x, y)`, `lineto(x, y)`, `linerel(dx, dy)` ? current point + line drawing -- `line(x1, y1, x2, y2)` ? gfx_line -- `rectangle(x1, y1, x2, y2)` ? gfx_rect ( 㣮 API: x1,y1,x2,y2 vs x,y,w,h!) -- `bar(x1, y1, x2, y2)` ? gfx_fill_rect -- `bar3d(x1, y1, x2, y2, depth, topflag)` ? : rect + 3d edges -- `circle(x, y, r)`, `arc(...)`, `ellipse(...)`, `pieslice(...)` ? primitives -- `fillpoly()`, `drawpoly()` ? -- `floodfill(x, y, border_color)` ? - -Text on graphics screen: -- `outtext(s)` / `outtextxy(x, y, s)` ? gfx_text (alias) -- `settextstyle(font, dir, size)` ? multiple bitmap fonts -- `gettextsettings(&info)` -- `textwidth(s)` / `textheight(s)` ? measure - -Image manipulation: -- `imagesize(x1, y1, x2, y2)` ? bytes needed for getimage -- `getimage(x1, y1, x2, y2, buf)` ? save rect to buffer -- `putimage(x, y, buf, op)` ? paste back with COPY_PUT/XOR_PUT/AND_PUT/OR_PUT/NOT_PUT - -Clipping/viewport: -- `setviewport(x1, y1, x2, y2, clip)` ? drawing clip rect -- `getviewsettings(&info)` -- `clearviewport()` -- `setactivepage(p)` / `setvisualpage(p)` ? ਧ (Sprinter 2 screen) - -Line style: -- `setlinestyle(style, pattern, thickness)` ? SOLID_LINE / DOTTED_LINE / etc. -- `getlinesettings(&info)` - -**Acceptance:** ७ ⨯筮 Turbo-C BGI ணࠬ (饩 ᯮ짮 -moveto/lineto/circle/bar/setcolor) ࠡ ⢥ ࠢ. - -**Notes:** -- BGI fonts (TRIPLEX/SANS_SERIF/GOTHIC) ? BIOS font, ⠫ 㦭 - ( bitmap data lib) -- imagesize/getimage/putimage ? ᠬ ॡ game/animation -- Active/visual page ( ਧ) ? Sprinter ন 2 graphics pages, - 㦥 API switching - -. ⠪ `examples/` Turbo C 2.x BGIDEMO reference 㦭. - -### IM2 Interrupt Handlers ? **MUST v2** - -User- ISR १ Z80 IM 2 mode. 㦭 : -- Timer ticks (50 Hz frame counter, ) -- Music playback (AY, COVOX) -- Real-time games (input + game logic + render interrupt-driven) -- Async keyboard / mouse handling - -**Status:** v2. research + design `docs/im2_isr_design.md`. - -**襭 ⥪:** ॠ ⤥ memory mode `--memory im2` -( ⮣ ⮡ 騥 crt0). Detail' design-doc. - -** research'** ( ⥪ `docs/im2_isr_design.md`): -- Vector 0xFF ? frame + keyboard + CBL. Disambiguation ⠬ 0x19 / 0xFE -- Mouse hardware-IRQ 室 ( ⥪饩 ) -- Vector table / ISR / stack W2 (0x8000..0xBFFF) -- DSS ᢮ IM 2 handler ? 㦭 chain' ( / SYSTIME ) - -### 稥 㯭 㭪 v2 - -- [ ] **FILE API rewrite ? buffered streams** ? ⥪ ॠ - `libc/stdio/file.c` provisional unbuffered shim ( fputc/fgetc - = read/write syscall). 㦭 業 buffered ᥬ⨪ - Solid-C: - - ```c - typedef struct { - uint flags; // +0..1 file status flags - int level; // +2..3 empty/fill level of buffer - char *curp; // +4..5 current active pointer - int fd; // +6..7 underlying low-level fd - char *buffer; // +8..9 data transfer buffer - char hold; // +10 ungetc byte if no buffer - short token; // +11..12 reserved - char dummy; // +13 reserved - } FILE; - ``` - - stdin/stdout/stderr ? fd-થ `0 / -1 / -2`. ⥫ - stdout/stderr ࠭ ७: ESTEX OPEN positive - small fd (1, 2, ?) 筮 䠩 ? ᫨ stdout=1, ॠ - fd=1 ⠫ 䨪஬. fd=0 stdin ᭮ - (ESTEX 0 頥). fd । syscall' ? - ᯥਧ 䫠 `_F_CONIN/_F_CONOUT`. - - ਭ-⮪ (stdaux/stdprn) ॠ㥬 ? Sprinter ਭ୮ - API . - - ୠ⨢ ? ॠ third_party/solid-c (sources - `SRC/CLIB/`); ⠬ ⮢ buffered FILE + fopen/fread/fwrite/ - fseek/setvbuf .. ஢ 訬 open/read/write/lseek. - - rewrite deferred issues stdio-review: - - `fwrite` short-write ⠢ `_F_ERROR` - - `fgets(buf, 1, fp)` ? ⠭ "empty string", 㫨 NULL - - `mode_to_flags` ? break-out '+' (cosmetic) - -- [ ] **Audio API** ? AY-3-8910 + COVOX १ 뢠 (ॡ IM2) -- [ ] **ISA-8 slot support** ? ZX-Bus (sound, network, etc.) ? ॡ IM2 + ⥭ port - -## 稥 (v1 backlog, 騥) - -- [x] **#9: text I/O split (Turbo-C style)** ? stdio (puts/printf/putchar) ⥯ fast no-attr १ PCHARS/PUTCHAR. conio (cputs/cprintf/putch) ਬ attr १ textcolor/textbackground/textattr. KEEP_EXIST_ATTR ? conio fallback fast path. Verified hello.exe. . `memory/text_output_api_split.md`. 2026-05-31. -- [x] **Mouse API ** (१ driver, RST 30h) ? 14 㭪権 (init/show/hide/refresh/read/goto/bounds/text_cursor/load_cursor/get_cursor/get/set_sensitivity/video_mode_changed). . `memory/mouse_api.md`. Verified MAME 2026-05-31. Sensitivity = divider ( = ॥). -- [ ] Interrupt handlers ? IM 2 vector table HOME user ISR' -- [ ] প `restore SP on EXIT` ( z88dk +pps) ? ஢ 㦭 -- [ ] CI: ⮬᪨ ᪠ MAME `-aviwrite` screenshot-ࠢ, ⮡ ਬ஢ 室 祫 - -## ⮬ - -- প `` ( SDCC stdlib ? 㦭 ஢ crt0 ᮢ⨬) -- `` १ ESTEX SYSTIME (`$21`) CMOS BIOS-㭪樨 -- ZX Spectrum-ᮢ⨬ ० ⤥ target ( ஢ ᯥ㬮᪨ ணࠬ) -- প ZX-Bus (sound, network, etc.) ? 㦭 ࠩ -- Profile-guided optimization tools (hot/cold detection) 㯭 ணࠬ - -## Linker duplicate-symbol warnings (, 䨫஢) - - ᮧ⥫쭮 overrides' SDCC z80.lib 㭪樨 ᮡ⢥ ᨥ `sprinter.lib`, `sdldz80` `?ASlink-Warning-Definition of public symbol '...' found more than once`. ࢮ । (), ⮬ ४⭮ ? warning ⮫쪮 noise. - -騥 overrides: -- `_puts` ? १ PCHARS+\r\n vs SDCC posix puts -- `___sdcc_heap` ? heap W2 vs SDCC's ⠭ -- `_asctime`, `_localtime` ( 㣨 time) ? `posix_time.c` १ ESTEX SYSTIME vs SDCC's `time.rel` `_RtcRead` - -**饥 襭:** `bin/sprinter-cc` 䨫஢뢠 warning- (warning + 2 follow-up `Library:` ப) 뢮 `sdcc`. १ `-v` (verbose) 뢠. १ awk-pipe. - -** 襭:** -- ३ explicit `--nostdlib` + 筮 ᯨ᮪ 㦭 㫥 z80.lib (string, math, stdlib override') ? warning', ᪨஢ -- : २ `_puts` ? `_puts_sprinter` + alias १ linker flag ( 㢥७ SDCC ন) -- : ⠢ ᥩ (ࠡ祥 benign) ? ਮ - -## TODO: ஢ ॠ쭮 - -- [ ] **Port_Y banking trick** (`docs/part2/SprinterGraphics programming.txt`): - ⢥ত ᫥ `OUT (0x89), Y` 0xC000+0x400*N W3 - ப Y..Y+15 ( ணࠬ஢ ? 16 ப). - Empirical 2026-06-01 MAME 0.283 trick ** ࠡ⠥** ? ᥫ - ᠬ 0xC000+row_width 室 . ᪨ - `docs/samples/plasma2.asm` ⮦ ᯮ banking, ⠭ - Port_Y per row. - : - 1. ॠ쭮 Sprinter - 2. dual-write (`_gfx_putpixel_raw` + ன write `0xD000+x`) - 3. ᫨ ? MAME, issue - ९ - 4. ᫨ ⮦ ? 㬥 , 㤠 㯮 - ⠢ ⥪ ॠ (Port_Y per pixel) - 5. ᫨ banking ࠡ⠥ ? ஢ Port_Y - `_gfx_putpixel_raw` (sentinel out-of-range, . memory/gfx_port_y_banking.md) - - 易 먣 Bresenham (60-pixel ) ? 8? - OUT (0x89) 権, `gfx_fill_rect 320x256` ? 16? . - release v1. - -## GFX: ७ `docs/part2/accelerator_doc.txt` - -᫥ ⥭ ⠫쭮 accelerator doc  ࠢ. - ᯮ ⮫쪮 ਧ⠫/⨪ Fill mode. - -### Quick wins ⥪ primitives - -- [ ] ** SMC `LD A, (var)` block-size**. 㬥  - ࠧ蠥 `LD A, (HL)`, `LD A, (BC)`, `LD A, (DE)` ( `LD A, r`). - 㡥 SMC complexity `gfx_lines.c:hfill_chunk/vfill_chunk` - `gfx_16.c:g16_hfill_chunk`. 饭 ⮫쪮 register-to-register. -- [ ] **஢ block-size**. 㬥 뢠 accel - block size bursts (. `Horizontal_Line_Fill`: ⠭ - size + `LD B,B` ⪫祭, ⮬ Fill mode ᯮ - ࠭ size). `gfx_fill_rect` 100 묨 - ப ? ⠭ size 1 ࠧ, 100. - -### Bank-prefix modes (port 0xE2 bits) - -㬥 뢠 ਠ ࠭ ⠭⭮ 0x50: - -| Bank byte | Effect | -|---|---| -| 0x50 | Normal write ? shadow + | -| 0x54 | "no copy in main shadow RAM" | -| 0x58 | **"FF is transparent"** ? 0xFF write ⠢ background | -| 0x5C | both | - -Bank 0x58 祬 mouse cursor 0xFF-஧筮. - **sprite-blending १ accel block copy**: - -- [ ] **`gfx_set_bank_transparent(on)`** 䫠 `gfx_set_bank` - 롮 0x50/0x58 ᮢ sprite' -- [ ] ᯮ짮 `gfx_blit()` ⮡ 䠪 - transparent sprites १ accel- - -### Block copy mode (sprite blit') - -`LD L,L` (horizontal) `LD A,A` (vertical) ? ० ஢ १ -256- accel memory. blit'. - -- [ ] **`gfx_blit(src_data, x, y, w, h)`** ? ஢ sprite' - (ந ࠧ, १ accel) -- [ ] **`gfx_blit_transparent(src, x, y, w, h)`** ? ᯮ짮 bank 0x58 - -. `Draw_Restangle_Data` accelerator_doc.txt ७. - -### AND / OR / XOR operations १ accel - -㬥 뢠 accel ন ᪨ 樨 -. ਬ: -- XOR ? (뤥 selection UI) -- OR / AND ? masking, alpha-style blending -- . ਬ accelerator_doc.txt: "256 bytes block coding via XOR" - -- [ ] **`gfx_xor_rect`** / **`gfx_or_rect`** / **`gfx_and_rect`** ? - ਬ⨢ ᪨ 権 אַ㣮쭨 -- [ ] **`gfx_invert_rect(x, y, w, h)`** ? alias xor 0xFF - -### Bitmap fonts ࠧ ࠧ஢ - - `gfx_text` / `gfx_putchar` प ࠡ 8?8 ⮬ -(BIOS WIN_GET_ZG 頥 256?8 ). UI / 쭨 -㦭: -- [ ] **`gfx_set_font_size(w, h)`** ? ४ ਭ/ glyph' -- [ ] **`gfx_set_font_data(ptr, w, h, advance)`** ? 㪠⥫ - 짮⥫᪨ + ࠧ -- [ ] প **proportional** (advance != w) ⮢ ? - array advance[256] ਭ glyph' -- [ ] **Big-font ०**: 8?16, 16?16, 16?8 ( 쭨) -- [ ] ⤥ API `gfx_text_ex(x, y, str, font_id)` - font_id 롨ࠥ 㦥 ⮢ -- [ ] **Anti-alias 2-bit ** ( 䮭 / ࠭ / 2- alpha?) - ? far future, smooth UI - -## ⠯ ⨬権 ( ᥩ) - -- **`gfx_line` १ accel ** ? Bresenham |dy| << |dx| ( ) 뤠 runs Y ( X): ᥫ, ᥫ, ᥫ, 蠣 Y, ᥫ, ᥫ... ⠪ run ? ⮢ 㬥 `gfx_hline` ( `vline`). - ᫥: runs 㭪 ; run length, ஬ 룮 accel hline 祬 N?putpixel (overhead accel ~20?s, putpixel ~5?s ? accel 룮 run ? 4-5 px); (dx ? dy) ⠢ Bresenham, ? run-length-based fill. - `gfx_line` orthogonal cases 㦥 १ accel ? ⨬஢ ⮫쪮 . - -- **`gfx_fill_rect` W3-swap ** ? ᥩ ७ `gfx_hline`/`gfx_vline` ᢮ DI/save-W3/restore-W3/EI. ᤥ internal `_fill_rect_inner` ন W3 DI 横; ~20?s ? ⢮ ப/⮫殢 . ਬ ᥬ ਬ⨢. -# MDView ? ᬮ騪 Markdown Sprinter - -**MDView** ? ணࠬ ᬮ 㬥⮢ ଠ *Markdown* -Sprinter ( Z80). 㬥 ࠭ ⤥쭮 W3 -᭮ RAM ணࠬ. - -## - -- 㬥 **128 ** (8 ࠭ EMM 16 ) -- **16 384** ࠭ ப -- ⮬᪨ ७ ᫮ ਭ ࠭ (80 ⮫殢) -- ਧ⠫ ᤢ ப ப ( , ⠡) -- -: 䠩, ப, 業 ப⪨ -- ப ﭨ ६ 㧪 樨 -- প ?? ᪫ ப - -## - -``` -mdview [_䠩.md] -``` - -᫨ 䠩 , 㦠 `README.MD`. - -## ࠢ - -``` - ⢨ - -Up Down ப⪠ ப / -PgUp PgDn ப⪠ ࠭ (30 ப) -Home 砫 㬥 -End 㬥 -Left Right ਧ⠫ ᤢ (⮫쪮 nowrap-ப) -F1 ࠢ -F8 ஢: CP866 ? CP1251 ? KOI8-R ? UTF-8 ? ... -F10 / Esc 室 ணࠬ -``` - -஢ । ⮬᪨ ⨨ (BOM + ⨪ - 4 ); `F8` ४砥 , ᫨ ⥪ 訡. -8- ஢ (CP866/CP1251/KOI8-R) ४ (६ -ᮢ). ன / (ਬ UTF-8) ந ? -ࢮ ४祭 (⪠ 㧠 ᯨ஬), . - -## ⠪ Markdown - -### - -ন ஢ H1?H4. ஢ H5 H6 ⮡ࠦ H4. - - # ࢮ ஢ - ## ண ஢ - ### 쥣 ஢ - #### ⮣ ஢ - -### ⮢ ଠ஢ - -** ⥪** 뤥 묨 񧤮窠: `**⥪**` - -*ᨢ* 뤥 묨 񧤮窠 `*⥪*` ન `_⥪_` - -`஥ ` 砥 묨 窠 - -~~ભ ⥪~~ ? ⨫: `~~⥪~~` - -### 㬥஢ ᯨ᮪ - -થ `-`, `*` `+`: - -- 㭪 ᯨ᪠ -- ன 㭪 ᯨ᪠ -- ⨩ 㭪 筮 ⥪⮬, 室 - 㤥 ७ ᫥ ப ࠭ 㯠 - -### 㬥஢ ᯨ᮪ - -1. -2. ன -3. ⨩ - -### - -> 稭 ᨬ `>`. ᪮쪮 ᫥⥫ -> ப ᪫ ⮬᪨ -> ७ᮬ ᫮. - -### (verbatim) - - 砥 ன 窨. -⥪ ⮡ࠦ ? ? ࠧ Markdown: - -``` -#include -#include - -int main(void) { - puts("Hello, Sprinter!"); - return 0; -} -``` - -### ਧ⠫쭠 - - ᨬ `---`, `***` `___` ⤥쭮 ப: - ---- - -## 孨᪨ ࠪ⨪ - -- **ଠ:** Sprinter, Z80 @ 21 -- **஢:** CP866 / CP1251 / KOI8-R / UTF-8 (⮮।, `F8`) -- **ᨬ ࠧ 䠩:** 128 -- **ᨬ쭮 ᫮ ப :** 16 384 -- ** :** small - - ணࠬ, c⥪, , ? a W1-W2 (32 , 0x4000?0xBFFF). - - 䠩 ? ࠭ EMM, ⮡ࠦ W3 (0xC000?0xFFFF) - -## TODO - -1. **祭 ࠧ 㬥⮢.** 128 : 筮 - ࠧ ࠡ 訬 - ࠭ , ஢ - ࠡ 8- ࠭栬 16. - -: ଠ஢ ⠡ ࠬ; প ஢ -CP866 / CP1251 / KOI8-R / UTF-8 ⮮। ४祭 `F8` -(ன ந , ࢮ ४祭). - -4. **᪮७ ७ਭ.** ப ࠭. ⨬ 横 뢮 - ᨬ १ BIOS WRCHAR ( 뢮, DMA). - ---- - -*MDView v0.2 (c) 2026 ஢ ..* - - ---- - -# Sprinter C Compiler ? v1.0 - -C toolchain for **Sprinter** ? the Z80-based home computer by Peters Plus, running -ESTEX DSS. Host: macOS / Linux. Target: `.EXE` files in SprintEXE format. - -Built on top of **SDCC 4.5** (vendored in `third_party/sdcc/`). This repository adds -everything Sprinter-specific: crt0, linker integration, libc wrappers over ESTEX, -banked-call trampolines, graphics & accelerator API, mouse driver wrappers, and the -`mkexe` utility for producing SprintEXE images. - -## What you get - -* **`bin/sprinter-cc`** ? one-line driver: `sprinter-cc -o foo.exe foo.c` -* **Memory modes**: `tiny`, `small`, `big`, `huge`, `manual` ? see below. -* **stdio + conio**: printf, puts, putchar, getchar, fopen/fread/..., cprintf, cputs, putch, textcolor/textbackground/textattr, gotoxy, kbhit/getch. -* **Graphics**: 320?256?256 and 640?256?16 modes, accelerator-backed primitives (hline / vline / rect / fill_rect / line via Bresenham, plus clear), bitmap-font text in both modes via BIOS character generator. -* **File I/O**: POSIX (`open`/`read`/`write`/`close`/`lseek`/`unlink`/`creat`), FILE\* streams (`fopen`/`fgets`/`fwrite`/...), directory listing (`ffirst`/`fnext`), `chdir`/`getcwd`/`mkdir`/`rmdir`, `stat`/`fstat`. -* **Memory**: 32 KB heap (W2-resident), banking-aware page allocator (`mem_alloc_pages`/`bank_read`/`bank_write`), explicit memory modes for sub-16 KB programs. -* **Mouse**: full Sprinter driver wrapper (14 functions including custom cursor bitmaps). -* **Environment**: `getenv`/`putenv`/`sysenv` over ESTEX `$46`. -* **Time**: `getdatetime`/`setdatetime` + POSIX `time`/`localtime`/`mktime`/`asctime`/`ctime`. -* **Misc**: `errno`/`strerror`/`perror`, `atexit`, `setjmp`/`longjmp`, `sleep`, full argv parsing in crt0. - -## Quick start - -```sh -git clone sprinter-c -cd sprinter-c -make sdcc # one-time: fetch SDCC 4.5 binary (~25 MB) -make all # build mkexe + libsprinter.lib + 27 examples -make floppy # pack everything into mame/v306/IMG/mc.img -cd mame/v306 && ./run_mame.sh # boot Sprinter in MAME -``` - -Compile a single program: - -```sh -cat > hello.c < -int main(void) { puts("Hello, Sprinter!"); return 0; } -EOF - -bin/sprinter-cc -o hello.exe hello.c -``` - -That's it ? `hello.exe` is now a valid SprintEXE you can `RUN HELLO` from the ESTEX shell. - -## Memory modes - -Sprinter's address space is four 16 KB windows (W0 / W1 / W2 / W3). DSS allocates -pages by program size ? small programs get only one page. Pick a memory mode based -on what your program needs: - -| Mode | Code lives in | Banking | Use when | Note | -|---|---|---|---|---| -| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | | -| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | | -| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | | -| `huge` | W1-W2 + W3 banking | yes (W3) | small + extra code modules | | -| `manual` | user-specified | optional | special layouts | Not implemented | - -```sh -sprinter-cc --memory small -o big.exe bigprog.c -sprinter-cc --memory huge -o app.exe main.c --bank 1=engine.c --bank 2=ai.c -``` - -Banked functions are declared with `__banked`: - -```c -void engine_tick(int dt) __banked; // lives in BANK1, automatically swapped -``` - -## Examples (27 total) - -| Example | What it demonstrates | -|---|---| -| `hello` | Hello world with stdio + conio Turbo-C-style colors | -| `argv` | argv parsing in crt0 | -| `cat` | File I/O ? read & print TEST.TXT | -| `seek` | 32-bit lseek over a 100 KB file | -| `ls` | Directory listing via ffirst/fnext | -| `filetest` | FILE\* streams (fopen/fread/...) | -| `stattest` | `stat`/`fstat` on files and directories | -| `errno` | errno / strerror / perror | -| `mem_test` | Page allocator + bank\_read/bank\_write | -| `malloc` | Heap stress test (200+ allocations) | -| `banked` | Banked code in W3 (huge mode) | -| `bankedbg` | Banked code in W1 (big mode) | -| `banklocl` | Bank-local static data and BSS | -| `mouse` | Mouse driver in text mode | -| `gfx_mous` | Mouse with custom bitmap cursor in graphics mode | -| `gfx_demo` | 320?256?256 lines / rects / accelerator | -| `gfx_d16` | 640?256?16 same primitives | -| `gfx_text` | Bitmap-font text on graphics screen | -| `timedir` | Date/time + directory listing | -| `ptime` | POSIX time API | -| `openenv` | open() flags + environment vars | -| `conio` | conio API smoke test | -| `attrprob` | Probe Sprinter text attribute byte layout | -| `strtest` | string.h test (from SDCC's z80.lib) | -| `stdlib` | stdlib.h test (qsort / rand / strtol / etc.) | -| `assrtest` | assert() | -| `rt_test` | Runtime helpers (sleep, setjmp, atexit) | - -## Headers - -Standard: -* `` ? puts / printf / FILE\* + Sprinter-specific dec/hex helpers -* `` ? atoi / atof / malloc / qsort / ... (from SDCC z80.lib) -* `` / `` / `` ? from SDCC z80.lib -* `` ? read / write / close / lseek / unlink -* `` ? open / creat + O\_RDONLY / O\_CREAT / ... -* `` ? errno + error names + strerror -* `` ? stat / fstat -* `` / `` ? from SDCC - -Sprinter-specific: -* `` ? putch / cputs / cprintf, textcolor / textbackground / textattr, kbhit / getch, clrscr, gotoxy, wherex/y -* `` ? gfx_init/done, palette, putpixel, hline/vline/rect/fill_rect/line, text ? for both 320 and 640 modes (gfx_\*16 variants) -* `` ? full 14-function driver wrapper + mouse_cursor_t with bitmap support -* `` ? chdir / getcwd / mkdir / rmdir / ffirst / fnext / ffblk -* `` ? getdatetime / setdatetime + POSIX time / localtime / etc. -* `` ? raw ports, ESTEX/BIOS function numbers, env API -* `` ? exit / \_exit / atexit -* `` ? mem\_alloc\_pages / mem\_free\_block / bank\_read / bank\_write -* `` ? Solid-C compatibility layer (aliases + BOOL/WORD/uint types) - -## Toolchain commands - -```sh -make all # build mkexe + lib + every example -make floppy # repack mame/v306/IMG/mc.img with all .exe files -make check # 17 mkexe unit-tests -make clean # remove all build artefacts -make sdcc # one-time: fetch SDCC 4.5 binary -``` - -### sprinter-cc options - -``` -sprinter-cc -o foo.exe foo.c [more.c ...] [options] - - --memory MODE tiny | small | big | huge | manual (default: tiny) - --memory-manual SPEC explicit placement (CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3) - --stack-size N bytes reserved for the stack (default ~1278) - --crt0=TYPE default | minimal | banked | small - --bank N=FILE.c compile FILE.c into bank N (repeatable, max 15) - --debug enable runtime diagnostics (defines DEBUG_RT) - -I PATH extra include path - -L 0xADDR / -E / -S override load / entry / stack addresses - -Wl FLAG pass FLAG to sdldz80 - --mkexe FLAG pass FLAG to mkexe (e.g. --mkexe -p --mkexe 0 for bank padding) - -v verbose -``` - -## Status - -What works in v1.0: -* Compile / link / pack to SprintEXE ? verified on all 27 examples -* Four memory modes (tiny / small / big / huge) -* Graphics (both modes) with accelerator -* Mouse (text + graphics cursor) -* File I/O, directories, environment, time -* All headers listed above - -Deferred to v2.0 (see `docs/TODO.md`): -* **Turbo-C-style BGI graphics API** ? `initgraph` / `setcolor` / `circle` / - `getimage` / `putimage` / etc. on top of our `gfx_*` primitives -* Remaining Solid-C compatibility gaps (Phase 2/3) ? see `docs/solid_c_compatibility.md` -* Manual memory mode -* Rewrite FILE\* stream API (current implementation is very primitive and doesn't use buffers) - -Deferred to v3.0: -* **IM2 interrupt handlers** ? research complete (`docs/im2_isr_design.md`), - implementation scheduled for v3 -* **Audio API** (AY-3-8910 + COVOX) ? requires IM2 -* **ISA-8 slot drivers** ? requires IM2 (???) - -## Documentation - -* `docs/TODO.md` ? roadmap and open work items -* `docs/solid_c_compatibility.md` ? gap analysis vs Solid-C 2004 -* `docs/im2_isr_design.md` ? interrupt handler design (v2) -* `docs/converted/` ? source documentation (ESTEX, BIOS, architecture) - converted to plain text for `grep` -* `docs/reference/`, `docs/samples/`, `docs/memory management/` ? original - Russian docs and code samples from Peters Plus - -## Repository layout - -``` -bin/sprinter-cc one-line compiler driver (bash) -toolchain/mkexe/ host-side tool: .ihx -> .exe SprintEXE -toolchain/check_banks.py post-link bank size enforcer -runtime/ crt0 variants (default, minimal, small, banked) - bank trampolines, heap, heap_top -libc/include/ headers -libc/io|stdio|mem|gfx/ C and asm sources for libsprinter.lib -lib/ Makefile that archives libsprinter.lib via sdar -examples/ 27 example programs -mame/v306/ MAME binary + Sprinter ROM/HDD images + floppy script -third_party/sdcc/ vendored SDCC 4.5 (fetched via `make sdcc`) -third_party/solid-c/ reference: original Sprinter native C (for compat target) -docs/ documentation -``` - -## License - -This repository contains: -* Original code in `bin/`, `toolchain/`, `runtime/`, `libc/`, `lib/`, `examples/` ? - MIT-licensed. -* `third_party/sdcc/` ? SDCC 4.5 under GPLv2 with linking exception - (see `third_party/sdcc/COPYING.txt`) -* `third_party/solid-c/` ? original Sprinter Solid C, used only as a reference - -## Credits - -* **Sprinter / Peters Plus** ? , ਩ ਭ and the original team -* **SDCC** ? for the underlying Z80 compiler -* **MAME** ? for the Sprinter Sp2000 emulation - ---- - -For questions / patches: see CONTRIBUTING.md (TBD) or open an issue. - ---- - -# TODO / Roadmap - - 浪 뢠 ਮ.  ॠ ணࠬ ? ਮ ᬥ. - -## ⠯ 5 ? malloc / free + banking-aware page allocator ? - -- [x] SDCC's `malloc`/`free` + `runtime/heap.s` ( library heap.rel, 14000- heap 2) -- [x] `libc/mem/mem_alloc.c` ? page allocator: `mem_alloc_pages`/`mem_free_block`/`mem_get_page`/`mem_info` १ ESTEX `$3C/$3D/$3E` + BIOS `$C4` -- [x] `libc/mem/bank_io.c` ? HOME-१ `bank_read`/`bank_write`/`bank_load_byte`/`bank_store_byte` ᢮ W3 -- [x] `examples/malloc_test/` ? ஢ઠ SDCC's malloc (~210 64- allocations १ heap) -- [x] `examples/mem_test/` ? ஢ઠ page allocator: 3 ࠭, ࠧ १ bank_write, 䨪 १ bank_read - -## ⠯ 6 ? argv parsing + sprinter-cc wrapper ? - -- [x] crt0 ESTEX command-line IX-prefix (inline asm `runtime/crt0.s`) -- [x] Strip leading CP/M-style space (DSS quirk) -- [x] । `argc`/`argv` main() १ HL/DE (SDCC __sdcccall(1) ABI) -- [x] argv[0] = basename .EXE १ ESTEX APPINFO ($47 subfn 2) -- [x] `runtime/crt0_minimal.s` ? opt-out 祭 쪨 ணࠬ -- [x] `runtime/crt0_banked.s` ? ⥯ ⮦ argv (parse_argv + get_progname ᪮஢ crt0.s; 㤥 factored argv.s libsprinter.lib) -- [x] Bash-⪠ `bin/sprinter-cc`: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権: `--memory`, `--memory-manual`, `--stack-size`, `--crt0=`, `--bank N=FILE.c`, `--debug`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` - -## ⠯ 8 ? 䨪 (320?256?256 + 640?256?16 + accel + bitmap font) ? - -- [x] **8a** Graphics core: `gfx_init`/`gfx_done`/`gfx_clear`/`gfx_putpixel`/`gfx_pal_load`/`gfx_pal_set` (libc/gfx/gfx_core.c). १ BIOS PIC_SET_PAL ($A4). Verified 320?256?256. -- [x] **8b** /אַ㣮쭨/fill १ accelerator (libc/gfx/gfx_lines.c): `gfx_hline`/`gfx_vline` १ accel Fill (LD C,C / LD E,E + SMC block-size), `gfx_rect`/`gfx_fill_rect` heuristic 롮஬ ਥ樨 (h/v bursts count), `gfx_line` Bresenham . `gfx_clear` ⮦ ९ᠭ column-major accel (~4? ॥). -- [x] **8c** 640?256?16 mode (libc/gfx/gfx_16.c): `gfx_*16` API, HIGH nibble = LEFT pixel (㬥 misleading), per-row RMW vline ( = 2 ਧ⠫ ᥫ). -- [x] **8d** Bitmap font + gfx_text (libc/gfx/gfx_text.c): १ BIOS WIN_GET_ZG ($B8), interleaved layout `font[row*256+char]`, `gfx_text`/`gfx_putchar` 320 mode, `gfx_text16`/`gfx_putchar16` 640 mode pair-table lookup. - -. memory/sprinter_graphics.md, sprinter_accelerator.md, sprinter_graphics_16.md, sprinter_font_format.md. - - ( ): -- [ ] -quad 640: per-cell (mode 0x82 ࠧ蠥 1 4 per 16?8 cell) ? १ אַ area-ᠭ ࠭ 0x0300..0x039F - -## Auto-banking (. `memory/banking_roadmap.md` ⠫) - -Phase 1 ? file-level bin-packing ? ॠ뢠 ஥ ~30 KB . - -- [ ] `toolchain/auto_bank.py`: - - ࠧ `.rel`-䠩 ( .map ᫥ dry-run link') - - First-fit-decreasing bin-packing - - `#pragma codeseg BANKn` manual override - - ५뢠 묨 `-Wl-b_BANKn=...` ࠬࠬ - - ⠥ । - -Phase 2-5: incremental rebalance, declarative `banks.toml`, function-level, call-graph-aware. 쪮 ᫨/ . - -## Bank-local static data (mutable data ⮬ ) ? ? - -- [x] ਬ `examples/bank_local_data/` ? 㭪 BANK1 ᢮ writable BSS array + const table + malloc- -- [x] `mkexe -p 0` 㫥 padding (BSS-storage 㧪) -- [x] ᪨ 楯: `--codeseg BANK1 --constseg BANK1 --dataseg BANK1` bank1.c + `-Wl-b_BANK1=0x1C000` . **`--dataseg BANK1` ** ? ࠭ ⭮ - trampoline bug ᪨஢ १. -- [x] ** 䨪 trampoline'a runtime/bank.s** ? `pop af; out (n), a` ਫ A ? banked-㭪樨 騥 uint8_t 頫 . `pop bc; out (c), b` ࠭ A. -- [x] **malloc banked-㭪樨 ࠡ⠥ ஧筮** ? heap W2 (HOME), W2 ᢠ trampoline', pointer  ⥪. . memory/bank_local_data_pattern.md. -- [x] 㬥 memory: `memory/bank_local_data_pattern.md` ( 楯 + malloc + nuances), `memory/sdcc_banking.md` (trampoline fix) -- [ ] 樮쭮 ? `check_banks.py` ⮡ 뢠 ࠧ size = code + const + bss per bank (cosmetic) - -祬: 㫥 訬 private state (level loader, audio engine, scene data). W2 heap , ⨪ . - -## ᪠ solid-c (⨢ Sprinter C ? `third_party/solid-c/`) - -᫥ solid-c'᪮ libc (. `memory/solid_c_findings.md`)  ⮢ ᫥ 㭪権. ਮ **᮪** : - -### High-priority gaps ( ஢, 짠) -- [x] **`errno` + `strerror`/`perror`** ? ⠡窠 32 訡 (libc/io/errno.c) -- [x] **७ `open()`** O_CREAT/O_TRUNC/O_APPEND/O_EXCL state machine -- [x] **`atexit`** ? 8-callback LIFO + `exit()` + `_exit()` (libc/io/atexit.c) -- [x] **`setjmp`/`longjmp`** ? 6- jmp_buf={sp,ix,pc} (libc/io/setjmp.c) -- [x] **`sleep(seconds)`** ? 50Hz halt-loop (libc/io/sleep.c) -- [x] **ESTEX ENV API** ($46, getenv/putenv) ? libc/io/env.c. 竨 doc-bug: ॠ쭮 A=0 NOT FOUND - -### Medium-priority (㦭 shell-like ⨫) -- [ ] **Mouse driver** ? `rst $30h`, 17 㭪権. **砫 ࠡ⠥ MAME**. -- [x] **`ffirst`/`fnext` + ffblk_t struct** directory listing ? ॠ, demo: ls.exe -- [x] **`getdatetime`/`setdatetime`** १ ESTEX $21/$22 ? libc/io/time.c, demo: time_dir_test -- [x] **`chdir`/`getcwd`/`mkdir`/`rmdir`** ? wrappers ESTEX $1B-$1E ? libc/io/fsdir.c -- [x] **conio: `kbhit`/`getch`/`getche`/`cputs`/`clrscr`/`gotoxy`** ? ॠ -- [x] **conio extras**: `wherex`/`wherey` ($53), `wrchar`/`rdchar` ($58/$57), `textmode_get/set` ($50/$51), `clrscr_attr` ($56) + COLOR macros - -### Low-priority ? ? FILE* stack - -- [x] ** unbuffered FILE\*** ? `libc/stdio/file.c` + `libc/include/stdio.h`. fopen/fclose/fputs/fgets/fread/fwrite/fseek/ftell/rewind/feof/ferror/clearerr/fflush + stdin/stdout/stderr pseudo-streams. . `memory/file_star_design.md` `examples/filetest`. -- [ ] fprintf / fscanf ? 㦭 printf-१-callback machinery. 짮⥫ `sprintf(buf, ...) + fputs(buf, fp)`. -- [ ] 樮 buffered mode (setvbuf, line/block buffering) ? ᫨ - . - -### POSIX time API ? ? -- [x] `libc/io/posix_time.c` ? time/localtime/gmtime/mktime/asctime/ctime getdatetime. SDCC's time.rel ( override _RtcRead). . `examples/ptime`. - -### sys/stat ? ? -- [x] `libc/io/stat.c` ? POSIX stat/fstat. ਤ open+fstat 䠩, ffirst+iter ( "."/".."). . `examples/stattest` `memory/estex_ffirst_dotdot.md`. - -### assert ? ? (ᯮ㥬 SDCC's __assert १ fallback include path) - -## libc/stdlib ? ? 㦭 (. memory/sdcc_stdlib_works.md) - -஢७ १ `examples/stdlib_test/`: SDCC z80.lib ᮤন ࠡ騥 ॠ樨: -- `atoi/atol/atof, strtol/strtoul, rand/srand, qsort/bsearch, abs/labs, div/ldiv` -- `` (memchr/cmp/set/cpy, strcat/cmp/cpy/len/chr/spn/etc.) -- `` (toupper/tolower) -- `` (sinf/cosf/sqrtf/etc.) - - ⮬᪨ ﭥ z80.lib 㦭. ** ९뢠**. - - Sprinter-specific 易⥫ 㫨 : atexit, env, errno, setjmp, putchar/puts/getchar, conio, fsdir, time, mouse, open/read/lseek/close. - -## Build-system: libsprinter.lib + sprinter-cc ? ? - -- [x] `lib/Makefile` ? ᮡࠥ libc/*.c `.rel`, 娢 १ sdar `lib/sprinter.lib` -- [x] 砥 runtime/bank.s runtime/heap.s (auto-pulled __banked/malloc) -- [x] `bin/sprinter-cc` ? bash-wrapper: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権 `--crt0=default|minimal|banked`, `--bank N=FILE.c`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` -- [x] `examples/hello_sccc/` ? : `hello.c` ᮡࠥ shell-맮, ࠧ ᮢ Makefile (925 ) -- [x] Split `putchar.c` ? `putchar.c` + `puts.c` per-function granularity (puts override SDCC's z80.lib version) -- [x] `make all` (ᨬ `lib` । `examples`) - - 襭 (樮쭮): -- [ ] ஢ ⠫ examples sprinter-cc Makefile (ᬥ⨪) -- [ ] 쭥 libc/*.c per-function ( ⥪ granularity 㦥 㦭 ࠧ ? .rel 楫, 設⢠ 䠩 㭪) - -## ⠯ 9 ? memory modes sprinter-cc - -DSS 뤥 ࠭ ࠧ ਫ: < 16 KB ? ࠭, ⠫ 砥 ?࠭ #FF? (read=0xFF, write ). - ⮣ CODE--W1 + DATA--W2 쪮 ணࠬ . . [memory/sprinter_memory_modes.md](../../.claude/projects/-Volumes-SAM8-Projects-DIY-Z80-Sprinter-C-Compiler/memory/sprinter_memory_modes.md). - -- [x] **`tiny`**: (CODE+DATA+⥪) W2. Default. Verified hello/argv/conio/malloc/file/etc. -- [x] **`--memory MODE` 䫠 sprinter-cc**: parser + per-mode 䮫 CODE_LOC/DATA_LOC, override १  `--code-loc`/`--data-loc`. tiny ࠡ⠥; small/big/huge warning' (runtime ⮢). 2026-05-30. -- [x] **`--memory-manual SPEC`**: `CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3`. 2026-05-30. -- [x] **`small` runtime**: `runtime/crt0_small.s` ᯮ ESTEX `$3D GETMEM` + `$3A SETWIN2` ⮡ 뤥 W2-࠭ gsinit. **** BIOS `$C4` ? ⥪ ⮬ ⠯ W1 (boot_stack HOME), BIOS ॡ ⥪ W2. ᫥ SP ४砥 0xBFFE, ⠭ flow. 2026-05-30, verified hello.exe. -- [x] **`small` auto-detect >16 KB ணࠬ**: `crt0_small.s` ⠥ `0xC2` (⥪ ࠭ W2 ? `0xA2`! W1). ᫨ 0xFF ? 뤥 page; DSS 㦥 ᤥ (ணࠬ ᠬ 뫥 W2). crt0 뢠 0..30 KB. mkexe ⠪ ࠧ蠥 HOME span W1+W2 (0x4000..0xBFFF). Verified hello: small (5 KB 䠩, SETWIN2 path) + 32 KB 䠩 (auto-skip). 2026-05-30. -- [x] **`big` runtime** (tiny + banked code W1): ࠬਧ `crt0_banked.s` + `bank.s` १ `.ifdef BANK_W1` ? 㣮 banking port (0xA2 vs 0xE2), 㣮 load-addr (0x4000 vs 0xC000). sprinter-cc prepend' `BANK_W1 = 1` `--memory big`, । `mkexe -B 0x4000`. ਬ `examples/banked_big/`. 2026-05-30. -- [x] **`huge` runtime** (small + banked code W3): merge W2-detect `crt0_small.s` `crt0_banked.s`. 騩 ਬ `examples/banked/` ⥯ ᯮ MEMORY=huge. 2026-05-30. -- [x] **`--debug` 䫠**: prepend `DEBUG_RT = 1` crt0 + `-DDEBUG_RT` sdcc. 뢠 symbol `_w2_self_allocated` (uint8_t) ? runtime diagnostic ஢ W2. 2026-05-30. - -### -襭 libc crt0 - -** `sprinter.lib`** ࠡ⠥ memory mode ? `.rel`-童 relocatable, SDLD dead-code elimination per-member ( 䨪  `gfx_core.rel` ..). Verified hello vs malloc_test १ map-䠩. - -**`gfx.lib` ⤥쭮 ? 㦥**: dead-code elimination 㦥 ࠡ⠥. - -**`libc_banked` (libc bank HOME)** ? ⮬, HOME (16 KB) user- + libc `huge` mode. १ `--codeseg BANK0` 樨 libc; trade-off: trampoline ~30 横 libc-맮. ਣ: ॠ쭠 ணࠬ HOME budget. - -**HW-ᨬ 㫨 ? `sprinter_home.lib` ⤥쭮.** libc 䨧᪨ W3, ⮬ W3: -- `gfx_*` ? `0xC000+` ᫥ swap W3 video page -- `bank_io` (mem_alloc_pages/bank_read/bank_write) ? swap' W3 १ `OUT (0xE2)` -- 騥 ISR ? 뢠 ਩ W3 㣮 - - huge mode 㫨 HOME (W1). 㤥 `libc_banked`, **६** 뤥塞 `sprinter_home.lib` (HOME-only) `sprinter.lib` (bankable). 쭠 奬: -``` -sprinter_home.lib HOME-only: gfx, bank_io, ISR shims -sprinter.lib bankable: printf, malloc, string, conio, stdio, env, ... -sprinter_banked.lib sprinter.lib --codeseg BANK0 ( huge) -``` -ਣ: ॠ `--memory huge` runtime. - -**crt0 ? mode:** -- `crt0.s` ? ⥪騩, **tiny/big**: SP=0xBFFE, argv (W2- 㦥 뤥 DSS). -- `crt0_minimal.s` ? ⥪騩, tiny argv. -- `crt0_small.s` ? **, step 3**: **small/huge**, W2 १ `mem_alloc_pages` gsinit, `0xA2`, ⮬ ⠭ flow. -- `crt0_banked.s` ? ⥪騩, **big**: trampoline-⠡ W3 , CODE W2. -- `crt0_banked_small.s` ? ****: huge = small (W2-alloc) + banked (W3 trampolines). - -sprinter-cc ࠥ crt0 `--memory` mode (ᥩ `--crt0=` override). -- [x] **ࠨ ࠧ ⥪**: 䫠 `sprinter-cc --stack-size BYTES`. Wrapper `heap_top.s` `___sdcc_heap_end = stack_top + 1 - stack_size`, ⤥ .rel per-program. Default ?1278 (heap_top=0xBB00) `runtime/heap_top.s`. 2026-05-30. - -䥩: `sprinter-cc --memory [tiny|small|big|huge|manual] [--memory-manual SPEC] [--stack-size N] foo.c`. `--memory-manual` ⮫쪮 `--memory manual`. - -## Known issues / quirks - -- **ESTEX $46 ENV API**: ? ࠡ⠥. - `DiskSyscalls.txt v1.6` 訡筮 뢠 return-status ? A=0 NOT FOUND, FOUND. 䨪஢ `memory/sprinter_platform.md`. - -## V2 (᫥ ५ v1) - -### Turbo-C-style graphics API (BGI-like) ? **MUST v2** - - `gfx_*` API ஢ **Turbo-C ``** (BGI) MS-DOS. -ணࠬ ਢ訥 Turbo-C ७ ᪨ 1--1. - -** ** ( ᭮ Borland BGI): - -Setup/teardown: -- `initgraph()` / `closegraph()` ? ᥩ `gfx_init`/`gfx_done`, alias -- `getmaxx()` / `getmaxy()` ? GFX_WIDTH-1 / GFX_HEIGHT-1 -- `cleardevice()` ? alias to gfx_clear -- `getgraphmode()` / `setgraphmode()` ? get_videomode/set_videomode - -Color/palette: -- `setcolor(c)`, `getcolor()` ? current draw color -- `setbkcolor(c)`, `getbkcolor()` ? background color -- `setpalette(idx, c)` ? entry -- `getpalette(&info)` ? read all palette - -Primitives ( 㦥 ? BGI- aliases): -- `putpixel(x, y, c)` ? gfx_putpixel -- `getpixel(x, y)` ? 㦭 ॠ (RMW ⭮ ? IN) -- `moveto(x, y)`, `lineto(x, y)`, `linerel(dx, dy)` ? current point + line drawing -- `line(x1, y1, x2, y2)` ? gfx_line -- `rectangle(x1, y1, x2, y2)` ? gfx_rect ( 㣮 API: x1,y1,x2,y2 vs x,y,w,h!) -- `bar(x1, y1, x2, y2)` ? gfx_fill_rect -- `bar3d(x1, y1, x2, y2, depth, topflag)` ? : rect + 3d edges -- `circle(x, y, r)`, `arc(...)`, `ellipse(...)`, `pieslice(...)` ? primitives -- `fillpoly()`, `drawpoly()` ? -- `floodfill(x, y, border_color)` ? - -Text on graphics screen: -- `outtext(s)` / `outtextxy(x, y, s)` ? gfx_text (alias) -- `settextstyle(font, dir, size)` ? multiple bitmap fonts -- `gettextsettings(&info)` -- `textwidth(s)` / `textheight(s)` ? measure - -Image manipulation: -- `imagesize(x1, y1, x2, y2)` ? bytes needed for getimage -- `getimage(x1, y1, x2, y2, buf)` ? save rect to buffer -- `putimage(x, y, buf, op)` ? paste back with COPY_PUT/XOR_PUT/AND_PUT/OR_PUT/NOT_PUT - -Clipping/viewport: -- `setviewport(x1, y1, x2, y2, clip)` ? drawing clip rect -- `getviewsettings(&info)` -- `clearviewport()` -- `setactivepage(p)` / `setvisualpage(p)` ? ਧ (Sprinter 2 screen) - -Line style: -- `setlinestyle(style, pattern, thickness)` ? SOLID_LINE / DOTTED_LINE / etc. -- `getlinesettings(&info)` - -**Acceptance:** ७ ⨯筮 Turbo-C BGI ணࠬ (饩 ᯮ짮 -moveto/lineto/circle/bar/setcolor) ࠡ ⢥ ࠢ. - -**Notes:** -- BGI fonts (TRIPLEX/SANS_SERIF/GOTHIC) ? BIOS font, ⠫ 㦭 - ( bitmap data lib) -- imagesize/getimage/putimage ? ᠬ ॡ game/animation -- Active/visual page ( ਧ) ? Sprinter ন 2 graphics pages, - 㦥 API switching - -. ⠪ `examples/` Turbo C 2.x BGIDEMO reference 㦭. - -### IM2 Interrupt Handlers ? **MUST v2** - -User- ISR १ Z80 IM 2 mode. 㦭 : -- Timer ticks (50 Hz frame counter, ) -- Music playback (AY, COVOX) -- Real-time games (input + game logic + render interrupt-driven) -- Async keyboard / mouse handling - -**Status:** v2. research + design `docs/im2_isr_design.md`. - -**襭 ⥪:** ॠ ⤥ memory mode `--memory im2` -( ⮣ ⮡ 騥 crt0). Detail' design-doc. - -** research'** ( ⥪ `docs/im2_isr_design.md`): -- Vector 0xFF ? frame + keyboard + CBL. Disambiguation ⠬ 0x19 / 0xFE -- Mouse hardware-IRQ 室 ( ⥪饩 ) -- Vector table / ISR / stack W2 (0x8000..0xBFFF) -- DSS ᢮ IM 2 handler ? 㦭 chain' ( / SYSTIME ) - -### 稥 㯭 㭪 v2 - -- [ ] **FILE API rewrite ? buffered streams** ? ⥪ ॠ - `libc/stdio/file.c` provisional unbuffered shim ( fputc/fgetc - = read/write syscall). 㦭 業 buffered ᥬ⨪ - Solid-C: - - ```c - typedef struct { - uint flags; // +0..1 file status flags - int level; // +2..3 empty/fill level of buffer - char *curp; // +4..5 current active pointer - int fd; // +6..7 underlying low-level fd - char *buffer; // +8..9 data transfer buffer - char hold; // +10 ungetc byte if no buffer - short token; // +11..12 reserved - char dummy; // +13 reserved - } FILE; - ``` - - stdin/stdout/stderr ? fd-થ `0 / -1 / -2`. ⥫ - stdout/stderr ࠭ ७: ESTEX OPEN positive - small fd (1, 2, ?) 筮 䠩 ? ᫨ stdout=1, ॠ - fd=1 ⠫ 䨪஬. fd=0 stdin ᭮ - (ESTEX 0 頥). fd । syscall' ? - ᯥਧ 䫠 `_F_CONIN/_F_CONOUT`. - - ਭ-⮪ (stdaux/stdprn) ॠ㥬 ? Sprinter ਭ୮ - API . - - ୠ⨢ ? ॠ third_party/solid-c (sources - `SRC/CLIB/`); ⠬ ⮢ buffered FILE + fopen/fread/fwrite/ - fseek/setvbuf .. ஢ 訬 open/read/write/lseek. - - rewrite deferred issues stdio-review: - - `fwrite` short-write ⠢ `_F_ERROR` - - `fgets(buf, 1, fp)` ? ⠭ "empty string", 㫨 NULL - - `mode_to_flags` ? break-out '+' (cosmetic) - -- [ ] **Audio API** ? AY-3-8910 + COVOX १ 뢠 (ॡ IM2) -- [ ] **ISA-8 slot support** ? ZX-Bus (sound, network, etc.) ? ॡ IM2 + ⥭ port - -## 稥 (v1 backlog, 騥) - -- [x] **#9: text I/O split (Turbo-C style)** ? stdio (puts/printf/putchar) ⥯ fast no-attr १ PCHARS/PUTCHAR. conio (cputs/cprintf/putch) ਬ attr १ textcolor/textbackground/textattr. KEEP_EXIST_ATTR ? conio fallback fast path. Verified hello.exe. . `memory/text_output_api_split.md`. 2026-05-31. -- [x] **Mouse API ** (१ driver, RST 30h) ? 14 㭪権 (init/show/hide/refresh/read/goto/bounds/text_cursor/load_cursor/get_cursor/get/set_sensitivity/video_mode_changed). . `memory/mouse_api.md`. Verified MAME 2026-05-31. Sensitivity = divider ( = ॥). -- [ ] Interrupt handlers ? IM 2 vector table HOME user ISR' -- [ ] প `restore SP on EXIT` ( z88dk +pps) ? ஢ 㦭 -- [ ] CI: ⮬᪨ ᪠ MAME `-aviwrite` screenshot-ࠢ, ⮡ ਬ஢ 室 祫 - -## ⮬ - -- প `` ( SDCC stdlib ? 㦭 ஢ crt0 ᮢ⨬) -- `` १ ESTEX SYSTIME (`$21`) CMOS BIOS-㭪樨 -- ZX Spectrum-ᮢ⨬ ० ⤥ target ( ஢ ᯥ㬮᪨ ணࠬ) -- প ZX-Bus (sound, network, etc.) ? 㦭 ࠩ -- Profile-guided optimization tools (hot/cold detection) 㯭 ணࠬ - -## Linker duplicate-symbol warnings (, 䨫஢) - - ᮧ⥫쭮 overrides' SDCC z80.lib 㭪樨 ᮡ⢥ ᨥ `sprinter.lib`, `sdldz80` `?ASlink-Warning-Definition of public symbol '...' found more than once`. ࢮ । (), ⮬ ४⭮ ? warning ⮫쪮 noise. - -騥 overrides: -- `_puts` ? १ PCHARS+\r\n vs SDCC posix puts -- `___sdcc_heap` ? heap W2 vs SDCC's ⠭ -- `_asctime`, `_localtime` ( 㣨 time) ? `posix_time.c` १ ESTEX SYSTIME vs SDCC's `time.rel` `_RtcRead` - -**饥 襭:** `bin/sprinter-cc` 䨫஢뢠 warning- (warning + 2 follow-up `Library:` ப) 뢮 `sdcc`. १ `-v` (verbose) 뢠. १ awk-pipe. - -** 襭:** -- ३ explicit `--nostdlib` + 筮 ᯨ᮪ 㦭 㫥 z80.lib (string, math, stdlib override') ? warning', ᪨஢ -- : २ `_puts` ? `_puts_sprinter` + alias १ linker flag ( 㢥७ SDCC ন) -- : ⠢ ᥩ (ࠡ祥 benign) ? ਮ - -## TODO: ஢ ॠ쭮 - -- [ ] **Port_Y banking trick** (`docs/part2/SprinterGraphics programming.txt`): - ⢥ত ᫥ `OUT (0x89), Y` 0xC000+0x400*N W3 - ப Y..Y+15 ( ணࠬ஢ ? 16 ப). - Empirical 2026-06-01 MAME 0.283 trick ** ࠡ⠥** ? ᥫ - ᠬ 0xC000+row_width 室 . ᪨ - `docs/samples/plasma2.asm` ⮦ ᯮ banking, ⠭ - Port_Y per row. - : - 1. ॠ쭮 Sprinter - 2. dual-write (`_gfx_putpixel_raw` + ன write `0xD000+x`) - 3. ᫨ ? MAME, issue - ९ - 4. ᫨ ⮦ ? 㬥 , 㤠 㯮 - ⠢ ⥪ ॠ (Port_Y per pixel) - 5. ᫨ banking ࠡ⠥ ? ஢ Port_Y - `_gfx_putpixel_raw` (sentinel out-of-range, . memory/gfx_port_y_banking.md) - - 易 먣 Bresenham (60-pixel ) ? 8? - OUT (0x89) 権, `gfx_fill_rect 320x256` ? 16? . - release v1. - -## GFX: ७ `docs/part2/accelerator_doc.txt` - -᫥ ⥭ ⠫쭮 accelerator doc  ࠢ. - ᯮ ⮫쪮 ਧ⠫/⨪ Fill mode. - -### Quick wins ⥪ primitives - -- [ ] ** SMC `LD A, (var)` block-size**. 㬥  - ࠧ蠥 `LD A, (HL)`, `LD A, (BC)`, `LD A, (DE)` ( `LD A, r`). - 㡥 SMC complexity `gfx_lines.c:hfill_chunk/vfill_chunk` - `gfx_16.c:g16_hfill_chunk`. 饭 ⮫쪮 register-to-register. -- [ ] **஢ block-size**. 㬥 뢠 accel - block size bursts (. `Horizontal_Line_Fill`: ⠭ - size + `LD B,B` ⪫祭, ⮬ Fill mode ᯮ - ࠭ size). `gfx_fill_rect` 100 묨 - ப ? ⠭ size 1 ࠧ, 100. - -### Bank-prefix modes (port 0xE2 bits) - -㬥 뢠 ਠ ࠭ ⠭⭮ 0x50: - -| Bank byte | Effect | -|---|---| -| 0x50 | Normal write ? shadow + | -| 0x54 | "no copy in main shadow RAM" | -| 0x58 | **"FF is transparent"** ? 0xFF write ⠢ background | -| 0x5C | both | - -Bank 0x58 祬 mouse cursor 0xFF-஧筮. - **sprite-blending १ accel block copy**: - -- [ ] **`gfx_set_bank_transparent(on)`** 䫠 `gfx_set_bank` - 롮 0x50/0x58 ᮢ sprite' -- [ ] ᯮ짮 `gfx_blit()` ⮡ 䠪 - transparent sprites १ accel- - -### Block copy mode (sprite blit') - -`LD L,L` (horizontal) `LD A,A` (vertical) ? ० ஢ १ -256- accel memory. blit'. - -- [ ] **`gfx_blit(src_data, x, y, w, h)`** ? ஢ sprite' - (ந ࠧ, १ accel) -- [ ] **`gfx_blit_transparent(src, x, y, w, h)`** ? ᯮ짮 bank 0x58 - -. `Draw_Restangle_Data` accelerator_doc.txt ७. - -### AND / OR / XOR operations १ accel - -㬥 뢠 accel ন ᪨ 樨 -. ਬ: -- XOR ? (뤥 selection UI) -- OR / AND ? masking, alpha-style blending -- . ਬ accelerator_doc.txt: "256 bytes block coding via XOR" - -- [ ] **`gfx_xor_rect`** / **`gfx_or_rect`** / **`gfx_and_rect`** ? - ਬ⨢ ᪨ 権 אַ㣮쭨 -- [ ] **`gfx_invert_rect(x, y, w, h)`** ? alias xor 0xFF - -### Bitmap fonts ࠧ ࠧ஢ - - `gfx_text` / `gfx_putchar` प ࠡ 8?8 ⮬ -(BIOS WIN_GET_ZG 頥 256?8 ). UI / 쭨 -㦭: -- [ ] **`gfx_set_font_size(w, h)`** ? ४ ਭ/ glyph' -- [ ] **`gfx_set_font_data(ptr, w, h, advance)`** ? 㪠⥫ - 짮⥫᪨ + ࠧ -- [ ] প **proportional** (advance != w) ⮢ ? - array advance[256] ਭ glyph' -- [ ] **Big-font ०**: 8?16, 16?16, 16?8 ( 쭨) -- [ ] ⤥ API `gfx_text_ex(x, y, str, font_id)` - font_id 롨ࠥ 㦥 ⮢ -- [ ] **Anti-alias 2-bit ** ( 䮭 / ࠭ / 2- alpha?) - ? far future, smooth UI - -## ⠯ ⨬権 ( ᥩ) - -- **`gfx_line` १ accel ** ? Bresenham |dy| << |dx| ( ) 뤠 runs Y ( X): ᥫ, ᥫ, ᥫ, 蠣 Y, ᥫ, ᥫ... ⠪ run ? ⮢ 㬥 `gfx_hline` ( `vline`). - ᫥: runs 㭪 ; run length, ஬ 룮 accel hline 祬 N?putpixel (overhead accel ~20?s, putpixel ~5?s ? accel 룮 run ? 4-5 px); (dx ? dy) ⠢ Bresenham, ? run-length-based fill. - `gfx_line` orthogonal cases 㦥 १ accel ? ⨬஢ ⮫쪮 . - -- **`gfx_fill_rect` W3-swap ** ? ᥩ ७ `gfx_hline`/`gfx_vline` ᢮ DI/save-W3/restore-W3/EI. ᤥ internal `_fill_rect_inner` ন W3 DI 横; ~20?s ? ⢮ ப/⮫殢 . ਬ ᥬ ਬ⨢. -# MDView ? ᬮ騪 Markdown Sprinter - -**MDView** ? ணࠬ ᬮ 㬥⮢ ଠ *Markdown* -Sprinter ( Z80). 㬥 ࠭ ⤥쭮 W3 -᭮ RAM ணࠬ. - -## - -- 㬥 **128 ** (8 ࠭ EMM 16 ) -- **16 384** ࠭ ப -- ⮬᪨ ७ ᫮ ਭ ࠭ (80 ⮫殢) -- ਧ⠫ ᤢ ப ப ( , ⠡) -- -: 䠩, ப, 業 ப⪨ -- ப ﭨ ६ 㧪 樨 -- প ?? ᪫ ப - -## - -``` -mdview [_䠩.md] -``` - -᫨ 䠩 , 㦠 `README.MD`. - -## ࠢ - -``` - ⢨ - -Up Down ப⪠ ப / -PgUp PgDn ப⪠ ࠭ (30 ப) -Home 砫 㬥 -End 㬥 -Left Right ਧ⠫ ᤢ (⮫쪮 nowrap-ப) -F1 ࠢ -F8 ஢: CP866 ? CP1251 ? KOI8-R ? UTF-8 ? ... -F10 / Esc 室 ணࠬ -``` - -஢ । ⮬᪨ ⨨ (BOM + ⨪ - 4 ); `F8` ४砥 , ᫨ ⥪ 訡. -8- ஢ (CP866/CP1251/KOI8-R) ४ (६ -ᮢ). ன / (ਬ UTF-8) ந ? -ࢮ ४祭 (⪠ 㧠 ᯨ஬), . - -## ⠪ Markdown - -### - -ন ஢ H1?H4. ஢ H5 H6 ⮡ࠦ H4. - - # ࢮ ஢ - ## ண ஢ - ### 쥣 ஢ - #### ⮣ ஢ - -### ⮢ ଠ஢ - -** ⥪** 뤥 묨 񧤮窠: `**⥪**` - -*ᨢ* 뤥 묨 񧤮窠 `*⥪*` ન `_⥪_` - -`஥ ` 砥 묨 窠 - -~~ભ ⥪~~ ? ⨫: `~~⥪~~` - -### 㬥஢ ᯨ᮪ - -થ `-`, `*` `+`: - -- 㭪 ᯨ᪠ -- ன 㭪 ᯨ᪠ -- ⨩ 㭪 筮 ⥪⮬, 室 - 㤥 ७ ᫥ ப ࠭ 㯠 - -### 㬥஢ ᯨ᮪ - -1. -2. ன -3. ⨩ - -### - -> 稭 ᨬ `>`. ᪮쪮 ᫥⥫ -> ப ᪫ ⮬᪨ -> ७ᮬ ᫮. - -### (verbatim) - - 砥 ன 窨. -⥪ ⮡ࠦ ? ? ࠧ Markdown: - -``` -#include -#include - -int main(void) { - puts("Hello, Sprinter!"); - return 0; -} -``` - -### ਧ⠫쭠 - - ᨬ `---`, `***` `___` ⤥쭮 ப: - ---- - -## 孨᪨ ࠪ⨪ - -- **ଠ:** Sprinter, Z80 @ 21 -- **஢:** CP866 / CP1251 / KOI8-R / UTF-8 (⮮।, `F8`) -- **ᨬ ࠧ 䠩:** 128 -- **ᨬ쭮 ᫮ ப :** 16 384 -- **ᨬ쭠 ப ७-:** 255 祥 (. ࠭祭 ) -- ** :** small - - ணࠬ, c⥪, , ? a W1-W2 (32 , 0x4000?0xBFFF). - - 䠩 ? ࠭ EMM, ⮡ࠦ W3 (0xC000?0xFFFF) - -## ࠭祭 - -### ப MD-० ? 255 祥 - -- ࠭ ப ** 255 (ᨬ, ਡ)** -? ⠭ `MAX_CACHE_LINE_LEN`. ⨯: `g_ncells` -`cache_rec_t.len` ? `uint8_t`. ᠥ ப, ⭥ ᥣ -**ਧ⠫쭮 ஫㥬** ப ( ப ⠡, 䫠 -`IF_HSCROLL`), MD-० ࠢ. - -** ந室 ப:** 樨 `gc_put()` -뢠 祩 ᫥ 255- (`if (g_ncells < MAX_CACHE_LINE_LEN)`). - ⮫쪮 255 祥, ⮪ **** ? -஫ ** થ १** 255- 樨 (થ `>` 砥 -? । ?). ९ ? `gc_put` ஢ ࠭. - -: ?255 祥? ? **७७ ਭ**, 室. - ࠧ稢 ஡ ( `TAB_STOP`), 祩 ⠡ -஡ ਭ + ࠬ `` ? ⮬ ⨣ ࠭, 祬 -255 ?? ᨬ. - -> **RAW-० (`F2`) ⮣ ** ? אַ 䠩 -> ⮢, ப 楫 (१ wrap `F3` ਧ. ஫). - -** ** (業, ॠ) ? `uint16_t`: -- `cache_rec_t.len` `uint8_t?uint16_t` ? **஢ 8 ** - (len ꥤ pad-), `idx<<3` . ᯫ⭮. -- `g_ncells` / `g_ncells_at_space` ? `uint16_t` ? 業 **/᪮**: - 16-⭠ 䬥⨪ Z80 祬 `gc_put` (맮 祩) - scan-横. ਥ: **+0.2?0.4 ** + 樨. -- `g_cells[MAX_CACHE_LINE_LEN*2]` near-RAM (W2) ? 業 - ****: `2 ? ` . 510 ; 512 ? +0.5 , 1024 ? +1.5 . - EMM- ⥭ (1 ) ப ﭥ ? 㧪 near. -- `viewport_x` ( `docset_t`) + ⮫ `max_vx` (ᥩ 248) ? `uint16_t`, - ࠭ >255 , ஫ . `widths[]`/`ccx` ⠡/. - - ஬ ? 512?1023: 墠 ᥬ ⨭/⠡栬, - 業 ~+0.3 +0.5?1.5 near-RAM. ** 㬥 `uint16_t`** ? - ⠢ થ १ 255- 樨, ⮡ 뫠 . - -## TODO - -1. **祭 ࠧ 㬥⮢.** 128 : 筮 - ࠧ ࠡ 訬 - ࠭ , ஢ - ࠡ 8- ࠭栬 16. - -: ଠ஢ ⠡ ࠬ; প ஢ -CP866 / CP1251 / KOI8-R / UTF-8 ⮮। ४祭 `F8` -(ன ந , ࢮ ४祭). - -4. **᪮७ ७ਭ.** ப ࠭. ⨬ 横 뢮 - ᨬ १ BIOS WRCHAR ( 뢮, DMA). - ---- - -*MDView v0.2 (c) 2026 ஢ ..* - - ---- - -# Sprinter C Compiler ? v1.0 - -C toolchain for **Sprinter** ? the Z80-based home computer by Peters Plus, running -ESTEX DSS. Host: macOS / Linux. Target: `.EXE` files in SprintEXE format. - -Built on top of **SDCC 4.5** (vendored in `third_party/sdcc/`). This repository adds -everything Sprinter-specific: crt0, linker integration, libc wrappers over ESTEX, -banked-call trampolines, graphics & accelerator API, mouse driver wrappers, and the -`mkexe` utility for producing SprintEXE images. - -## What you get - -* **`bin/sprinter-cc`** ? one-line driver: `sprinter-cc -o foo.exe foo.c` -* **Memory modes**: `tiny`, `small`, `big`, `huge`, `manual` ? see below. -* **stdio + conio**: printf, puts, putchar, getchar, fopen/fread/..., cprintf, cputs, putch, textcolor/textbackground/textattr, gotoxy, kbhit/getch. -* **Graphics**: 320?256?256 and 640?256?16 modes, accelerator-backed primitives (hline / vline / rect / fill_rect / line via Bresenham, plus clear), bitmap-font text in both modes via BIOS character generator. -* **File I/O**: POSIX (`open`/`read`/`write`/`close`/`lseek`/`unlink`/`creat`), FILE\* streams (`fopen`/`fgets`/`fwrite`/...), directory listing (`ffirst`/`fnext`), `chdir`/`getcwd`/`mkdir`/`rmdir`, `stat`/`fstat`. -* **Memory**: 32 KB heap (W2-resident), banking-aware page allocator (`mem_alloc_pages`/`bank_read`/`bank_write`), explicit memory modes for sub-16 KB programs. -* **Mouse**: full Sprinter driver wrapper (14 functions including custom cursor bitmaps). -* **Environment**: `getenv`/`putenv`/`sysenv` over ESTEX `$46`. -* **Time**: `getdatetime`/`setdatetime` + POSIX `time`/`localtime`/`mktime`/`asctime`/`ctime`. -* **Misc**: `errno`/`strerror`/`perror`, `atexit`, `setjmp`/`longjmp`, `sleep`, full argv parsing in crt0. - -## Quick start - -```sh -git clone sprinter-c -cd sprinter-c -make sdcc # one-time: fetch SDCC 4.5 binary (~25 MB) -make all # build mkexe + libsprinter.lib + 27 examples -make floppy # pack everything into mame/v306/IMG/mc.img -cd mame/v306 && ./run_mame.sh # boot Sprinter in MAME -``` - -Compile a single program: - -```sh -cat > hello.c < -int main(void) { puts("Hello, Sprinter!"); return 0; } -EOF - -bin/sprinter-cc -o hello.exe hello.c -``` - -That's it ? `hello.exe` is now a valid SprintEXE you can `RUN HELLO` from the ESTEX shell. - -## Memory modes - -Sprinter's address space is four 16 KB windows (W0 / W1 / W2 / W3). DSS allocates -pages by program size ? small programs get only one page. Pick a memory mode based -on what your program needs: - -| Mode | Code lives in | Banking | Use when | Note | -|---|---|---|---|---| -| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | | -| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | | -| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | | -| `huge` | W1-W2 + W3 banking | yes (W3) | small + extra code modules | | -| `manual` | user-specified | optional | special layouts | Not implemented | - -```sh -sprinter-cc --memory small -o big.exe bigprog.c -sprinter-cc --memory huge -o app.exe main.c --bank 1=engine.c --bank 2=ai.c -``` - -Banked functions are declared with `__banked`: - -```c -void engine_tick(int dt) __banked; // lives in BANK1, automatically swapped -``` - -## Examples (27 total) - -| Example | What it demonstrates | -|---|---| -| `hello` | Hello world with stdio + conio Turbo-C-style colors | -| `argv` | argv parsing in crt0 | -| `cat` | File I/O ? read & print TEST.TXT | -| `seek` | 32-bit lseek over a 100 KB file | -| `ls` | Directory listing via ffirst/fnext | -| `filetest` | FILE\* streams (fopen/fread/...) | -| `stattest` | `stat`/`fstat` on files and directories | -| `errno` | errno / strerror / perror | -| `mem_test` | Page allocator + bank\_read/bank\_write | -| `malloc` | Heap stress test (200+ allocations) | -| `banked` | Banked code in W3 (huge mode) | -| `bankedbg` | Banked code in W1 (big mode) | -| `banklocl` | Bank-local static data and BSS | -| `mouse` | Mouse driver in text mode | -| `gfx_mous` | Mouse with custom bitmap cursor in graphics mode | -| `gfx_demo` | 320?256?256 lines / rects / accelerator | -| `gfx_d16` | 640?256?16 same primitives | -| `gfx_text` | Bitmap-font text on graphics screen | -| `timedir` | Date/time + directory listing | -| `ptime` | POSIX time API | -| `openenv` | open() flags + environment vars | -| `conio` | conio API smoke test | -| `attrprob` | Probe Sprinter text attribute byte layout | -| `strtest` | string.h test (from SDCC's z80.lib) | -| `stdlib` | stdlib.h test (qsort / rand / strtol / etc.) | -| `assrtest` | assert() | -| `rt_test` | Runtime helpers (sleep, setjmp, atexit) | - -## Headers - -Standard: -* `` ? puts / printf / FILE\* + Sprinter-specific dec/hex helpers -* `` ? atoi / atof / malloc / qsort / ... (from SDCC z80.lib) -* `` / `` / `` ? from SDCC z80.lib -* `` ? read / write / close / lseek / unlink -* `` ? open / creat + O\_RDONLY / O\_CREAT / ... -* `` ? errno + error names + strerror -* `` ? stat / fstat -* `` / `` ? from SDCC - -Sprinter-specific: -* `` ? putch / cputs / cprintf, textcolor / textbackground / textattr, kbhit / getch, clrscr, gotoxy, wherex/y -* `` ? gfx_init/done, palette, putpixel, hline/vline/rect/fill_rect/line, text ? for both 320 and 640 modes (gfx_\*16 variants) -* `` ? full 14-function driver wrapper + mouse_cursor_t with bitmap support -* `` ? chdir / getcwd / mkdir / rmdir / ffirst / fnext / ffblk -* `` ? getdatetime / setdatetime + POSIX time / localtime / etc. -* `` ? raw ports, ESTEX/BIOS function numbers, env API -* `` ? exit / \_exit / atexit -* `` ? mem\_alloc\_pages / mem\_free\_block / bank\_read / bank\_write -* `` ? Solid-C compatibility layer (aliases + BOOL/WORD/uint types) - -## Toolchain commands - -```sh -make all # build mkexe + lib + every example -make floppy # repack mame/v306/IMG/mc.img with all .exe files -make check # 17 mkexe unit-tests -make clean # remove all build artefacts -make sdcc # one-time: fetch SDCC 4.5 binary -``` - -### sprinter-cc options - -``` -sprinter-cc -o foo.exe foo.c [more.c ...] [options] - - --memory MODE tiny | small | big | huge | manual (default: tiny) - --memory-manual SPEC explicit placement (CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3) - --stack-size N bytes reserved for the stack (default ~1278) - --crt0=TYPE default | minimal | banked | small - --bank N=FILE.c compile FILE.c into bank N (repeatable, max 15) - --debug enable runtime diagnostics (defines DEBUG_RT) - -I PATH extra include path - -L 0xADDR / -E / -S override load / entry / stack addresses - -Wl FLAG pass FLAG to sdldz80 - --mkexe FLAG pass FLAG to mkexe (e.g. --mkexe -p --mkexe 0 for bank padding) - -v verbose -``` - -## Status - -What works in v1.0: -* Compile / link / pack to SprintEXE ? verified on all 27 examples -* Four memory modes (tiny / small / big / huge) -* Graphics (both modes) with accelerator -* Mouse (text + graphics cursor) -* File I/O, directories, environment, time -* All headers listed above - -Deferred to v2.0 (see `docs/TODO.md`): -* **Turbo-C-style BGI graphics API** ? `initgraph` / `setcolor` / `circle` / - `getimage` / `putimage` / etc. on top of our `gfx_*` primitives -* Remaining Solid-C compatibility gaps (Phase 2/3) ? see `docs/solid_c_compatibility.md` -* Manual memory mode -* Rewrite FILE\* stream API (current implementation is very primitive and doesn't use buffers) - -Deferred to v3.0: -* **IM2 interrupt handlers** ? research complete (`docs/im2_isr_design.md`), - implementation scheduled for v3 -* **Audio API** (AY-3-8910 + COVOX) ? requires IM2 -* **ISA-8 slot drivers** ? requires IM2 (???) - -## Documentation - -* `docs/TODO.md` ? roadmap and open work items -* `docs/solid_c_compatibility.md` ? gap analysis vs Solid-C 2004 -* `docs/im2_isr_design.md` ? interrupt handler design (v2) -* `docs/converted/` ? source documentation (ESTEX, BIOS, architecture) - converted to plain text for `grep` -* `docs/reference/`, `docs/samples/`, `docs/memory management/` ? original - Russian docs and code samples from Peters Plus - -## Repository layout - -``` -bin/sprinter-cc one-line compiler driver (bash) -toolchain/mkexe/ host-side tool: .ihx -> .exe SprintEXE -toolchain/check_banks.py post-link bank size enforcer -runtime/ crt0 variants (default, minimal, small, banked) - bank trampolines, heap, heap_top -libc/include/ headers -libc/io|stdio|mem|gfx/ C and asm sources for libsprinter.lib -lib/ Makefile that archives libsprinter.lib via sdar -examples/ 27 example programs -mame/v306/ MAME binary + Sprinter ROM/HDD images + floppy script -third_party/sdcc/ vendored SDCC 4.5 (fetched via `make sdcc`) -third_party/solid-c/ reference: original Sprinter native C (for compat target) -docs/ documentation -``` - -## License - -This repository contains: -* Original code in `bin/`, `toolchain/`, `runtime/`, `libc/`, `lib/`, `examples/` ? - MIT-licensed. -* `third_party/sdcc/` ? SDCC 4.5 under GPLv2 with linking exception - (see `third_party/sdcc/COPYING.txt`) -* `third_party/solid-c/` ? original Sprinter Solid C, used only as a reference - -## Credits - -* **Sprinter / Peters Plus** ? , ਩ ਭ and the original team -* **SDCC** ? for the underlying Z80 compiler -* **MAME** ? for the Sprinter Sp2000 emulation - ---- - -For questions / patches: see CONTRIBUTING.md (TBD) or open an issue. - ---- - -# TODO / Roadmap - - 浪 뢠 ਮ.  ॠ ணࠬ ? ਮ ᬥ. - -## ⠯ 5 ? malloc / free + banking-aware page allocator ? - -- [x] SDCC's `malloc`/`free` + `runtime/heap.s` ( library heap.rel, 14000- heap 2) -- [x] `libc/mem/mem_alloc.c` ? page allocator: `mem_alloc_pages`/`mem_free_block`/`mem_get_page`/`mem_info` १ ESTEX `$3C/$3D/$3E` + BIOS `$C4` -- [x] `libc/mem/bank_io.c` ? HOME-१ `bank_read`/`bank_write`/`bank_load_byte`/`bank_store_byte` ᢮ W3 -- [x] `examples/malloc_test/` ? ஢ઠ SDCC's malloc (~210 64- allocations १ heap) -- [x] `examples/mem_test/` ? ஢ઠ page allocator: 3 ࠭, ࠧ १ bank_write, 䨪 १ bank_read - -## ⠯ 6 ? argv parsing + sprinter-cc wrapper ? - -- [x] crt0 ESTEX command-line IX-prefix (inline asm `runtime/crt0.s`) -- [x] Strip leading CP/M-style space (DSS quirk) -- [x] । `argc`/`argv` main() १ HL/DE (SDCC __sdcccall(1) ABI) -- [x] argv[0] = basename .EXE १ ESTEX APPINFO ($47 subfn 2) -- [x] `runtime/crt0_minimal.s` ? opt-out 祭 쪨 ணࠬ -- [x] `runtime/crt0_banked.s` ? ⥯ ⮦ argv (parse_argv + get_progname ᪮஢ crt0.s; 㤥 factored argv.s libsprinter.lib) -- [x] Bash-⪠ `bin/sprinter-cc`: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権: `--memory`, `--memory-manual`, `--stack-size`, `--crt0=`, `--bank N=FILE.c`, `--debug`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` - -## ⠯ 8 ? 䨪 (320?256?256 + 640?256?16 + accel + bitmap font) ? - -- [x] **8a** Graphics core: `gfx_init`/`gfx_done`/`gfx_clear`/`gfx_putpixel`/`gfx_pal_load`/`gfx_pal_set` (libc/gfx/gfx_core.c). १ BIOS PIC_SET_PAL ($A4). Verified 320?256?256. -- [x] **8b** /אַ㣮쭨/fill १ accelerator (libc/gfx/gfx_lines.c): `gfx_hline`/`gfx_vline` १ accel Fill (LD C,C / LD E,E + SMC block-size), `gfx_rect`/`gfx_fill_rect` heuristic 롮஬ ਥ樨 (h/v bursts count), `gfx_line` Bresenham . `gfx_clear` ⮦ ९ᠭ column-major accel (~4? ॥). -- [x] **8c** 640?256?16 mode (libc/gfx/gfx_16.c): `gfx_*16` API, HIGH nibble = LEFT pixel (㬥 misleading), per-row RMW vline ( = 2 ਧ⠫ ᥫ). -- [x] **8d** Bitmap font + gfx_text (libc/gfx/gfx_text.c): १ BIOS WIN_GET_ZG ($B8), interleaved layout `font[row*256+char]`, `gfx_text`/`gfx_putchar` 320 mode, `gfx_text16`/`gfx_putchar16` 640 mode pair-table lookup. - -. memory/sprinter_graphics.md, sprinter_accelerator.md, sprinter_graphics_16.md, sprinter_font_format.md. - - ( ): -- [ ] -quad 640: per-cell (mode 0x82 ࠧ蠥 1 4 per 16?8 cell) ? १ אַ area-ᠭ ࠭ 0x0300..0x039F - -## Auto-banking (. `memory/banking_roadmap.md` ⠫) - -Phase 1 ? file-level bin-packing ? ॠ뢠 ஥ ~30 KB . - -- [ ] `toolchain/auto_bank.py`: - - ࠧ `.rel`-䠩 ( .map ᫥ dry-run link') - - First-fit-decreasing bin-packing - - `#pragma codeseg BANKn` manual override - - ५뢠 묨 `-Wl-b_BANKn=...` ࠬࠬ - - ⠥ । - -Phase 2-5: incremental rebalance, declarative `banks.toml`, function-level, call-graph-aware. 쪮 ᫨/ . - -## Bank-local static data (mutable data ⮬ ) ? ? - -- [x] ਬ `examples/bank_local_data/` ? 㭪 BANK1 ᢮ writable BSS array + const table + malloc- -- [x] `mkexe -p 0` 㫥 padding (BSS-storage 㧪) -- [x] ᪨ 楯: `--codeseg BANK1 --constseg BANK1 --dataseg BANK1` bank1.c + `-Wl-b_BANK1=0x1C000` . **`--dataseg BANK1` ** ? ࠭ ⭮ - trampoline bug ᪨஢ १. -- [x] ** 䨪 trampoline'a runtime/bank.s** ? `pop af; out (n), a` ਫ A ? banked-㭪樨 騥 uint8_t 頫 . `pop bc; out (c), b` ࠭ A. -- [x] **malloc banked-㭪樨 ࠡ⠥ ஧筮** ? heap W2 (HOME), W2 ᢠ trampoline', pointer  ⥪. . memory/bank_local_data_pattern.md. -- [x] 㬥 memory: `memory/bank_local_data_pattern.md` ( 楯 + malloc + nuances), `memory/sdcc_banking.md` (trampoline fix) -- [ ] 樮쭮 ? `check_banks.py` ⮡ 뢠 ࠧ size = code + const + bss per bank (cosmetic) - -祬: 㫥 訬 private state (level loader, audio engine, scene data). W2 heap , ⨪ . - -## ᪠ solid-c (⨢ Sprinter C ? `third_party/solid-c/`) - -᫥ solid-c'᪮ libc (. `memory/solid_c_findings.md`)  ⮢ ᫥ 㭪権. ਮ **᮪** : - -### High-priority gaps ( ஢, 짠) -- [x] **`errno` + `strerror`/`perror`** ? ⠡窠 32 訡 (libc/io/errno.c) -- [x] **७ `open()`** O_CREAT/O_TRUNC/O_APPEND/O_EXCL state machine -- [x] **`atexit`** ? 8-callback LIFO + `exit()` + `_exit()` (libc/io/atexit.c) -- [x] **`setjmp`/`longjmp`** ? 6- jmp_buf={sp,ix,pc} (libc/io/setjmp.c) -- [x] **`sleep(seconds)`** ? 50Hz halt-loop (libc/io/sleep.c) -- [x] **ESTEX ENV API** ($46, getenv/putenv) ? libc/io/env.c. 竨 doc-bug: ॠ쭮 A=0 NOT FOUND - -### Medium-priority (㦭 shell-like ⨫) -- [ ] **Mouse driver** ? `rst $30h`, 17 㭪権. **砫 ࠡ⠥ MAME**. -- [x] **`ffirst`/`fnext` + ffblk_t struct** directory listing ? ॠ, demo: ls.exe -- [x] **`getdatetime`/`setdatetime`** १ ESTEX $21/$22 ? libc/io/time.c, demo: time_dir_test -- [x] **`chdir`/`getcwd`/`mkdir`/`rmdir`** ? wrappers ESTEX $1B-$1E ? libc/io/fsdir.c -- [x] **conio: `kbhit`/`getch`/`getche`/`cputs`/`clrscr`/`gotoxy`** ? ॠ -- [x] **conio extras**: `wherex`/`wherey` ($53), `wrchar`/`rdchar` ($58/$57), `textmode_get/set` ($50/$51), `clrscr_attr` ($56) + COLOR macros - -### Low-priority ? ? FILE* stack - -- [x] ** unbuffered FILE\*** ? `libc/stdio/file.c` + `libc/include/stdio.h`. fopen/fclose/fputs/fgets/fread/fwrite/fseek/ftell/rewind/feof/ferror/clearerr/fflush + stdin/stdout/stderr pseudo-streams. . `memory/file_star_design.md` `examples/filetest`. -- [ ] fprintf / fscanf ? 㦭 printf-१-callback machinery. 짮⥫ `sprintf(buf, ...) + fputs(buf, fp)`. -- [ ] 樮 buffered mode (setvbuf, line/block buffering) ? ᫨ - . - -### POSIX time API ? ? -- [x] `libc/io/posix_time.c` ? time/localtime/gmtime/mktime/asctime/ctime getdatetime. SDCC's time.rel ( override _RtcRead). . `examples/ptime`. - -### sys/stat ? ? -- [x] `libc/io/stat.c` ? POSIX stat/fstat. ਤ open+fstat 䠩, ffirst+iter ( "."/".."). . `examples/stattest` `memory/estex_ffirst_dotdot.md`. - -### assert ? ? (ᯮ㥬 SDCC's __assert १ fallback include path) - -## libc/stdlib ? ? 㦭 (. memory/sdcc_stdlib_works.md) - -஢७ १ `examples/stdlib_test/`: SDCC z80.lib ᮤন ࠡ騥 ॠ樨: -- `atoi/atol/atof, strtol/strtoul, rand/srand, qsort/bsearch, abs/labs, div/ldiv` -- `` (memchr/cmp/set/cpy, strcat/cmp/cpy/len/chr/spn/etc.) -- `` (toupper/tolower) -- `` (sinf/cosf/sqrtf/etc.) - - ⮬᪨ ﭥ z80.lib 㦭. ** ९뢠**. - - Sprinter-specific 易⥫ 㫨 : atexit, env, errno, setjmp, putchar/puts/getchar, conio, fsdir, time, mouse, open/read/lseek/close. - -## Build-system: libsprinter.lib + sprinter-cc ? ? - -- [x] `lib/Makefile` ? ᮡࠥ libc/*.c `.rel`, 娢 १ sdar `lib/sprinter.lib` -- [x] 砥 runtime/bank.s runtime/heap.s (auto-pulled __banked/malloc) -- [x] `bin/sprinter-cc` ? bash-wrapper: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権 `--crt0=default|minimal|banked`, `--bank N=FILE.c`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` -- [x] `examples/hello_sccc/` ? : `hello.c` ᮡࠥ shell-맮, ࠧ ᮢ Makefile (925 ) -- [x] Split `putchar.c` ? `putchar.c` + `puts.c` per-function granularity (puts override SDCC's z80.lib version) -- [x] `make all` (ᨬ `lib` । `examples`) - - 襭 (樮쭮): -- [ ] ஢ ⠫ examples sprinter-cc Makefile (ᬥ⨪) -- [ ] 쭥 libc/*.c per-function ( ⥪ granularity 㦥 㦭 ࠧ ? .rel 楫, 設⢠ 䠩 㭪) - -## ⠯ 9 ? memory modes sprinter-cc - -DSS 뤥 ࠭ ࠧ ਫ: < 16 KB ? ࠭, ⠫ 砥 ?࠭ #FF? (read=0xFF, write ). - ⮣ CODE--W1 + DATA--W2 쪮 ணࠬ . . [memory/sprinter_memory_modes.md](../../.claude/projects/-Volumes-SAM8-Projects-DIY-Z80-Sprinter-C-Compiler/memory/sprinter_memory_modes.md). - -- [x] **`tiny`**: (CODE+DATA+⥪) W2. Default. Verified hello/argv/conio/malloc/file/etc. -- [x] **`--memory MODE` 䫠 sprinter-cc**: parser + per-mode 䮫 CODE_LOC/DATA_LOC, override १  `--code-loc`/`--data-loc`. tiny ࠡ⠥; small/big/huge warning' (runtime ⮢). 2026-05-30. -- [x] **`--memory-manual SPEC`**: `CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3`. 2026-05-30. -- [x] **`small` runtime**: `runtime/crt0_small.s` ᯮ ESTEX `$3D GETMEM` + `$3A SETWIN2` ⮡ 뤥 W2-࠭ gsinit. **** BIOS `$C4` ? ⥪ ⮬ ⠯ W1 (boot_stack HOME), BIOS ॡ ⥪ W2. ᫥ SP ४砥 0xBFFE, ⠭ flow. 2026-05-30, verified hello.exe. -- [x] **`small` auto-detect >16 KB ணࠬ**: `crt0_small.s` ⠥ `0xC2` (⥪ ࠭ W2 ? `0xA2`! W1). ᫨ 0xFF ? 뤥 page; DSS 㦥 ᤥ (ணࠬ ᠬ 뫥 W2). crt0 뢠 0..30 KB. mkexe ⠪ ࠧ蠥 HOME span W1+W2 (0x4000..0xBFFF). Verified hello: small (5 KB 䠩, SETWIN2 path) + 32 KB 䠩 (auto-skip). 2026-05-30. -- [x] **`big` runtime** (tiny + banked code W1): ࠬਧ `crt0_banked.s` + `bank.s` १ `.ifdef BANK_W1` ? 㣮 banking port (0xA2 vs 0xE2), 㣮 load-addr (0x4000 vs 0xC000). sprinter-cc prepend' `BANK_W1 = 1` `--memory big`, । `mkexe -B 0x4000`. ਬ `examples/banked_big/`. 2026-05-30. -- [x] **`huge` runtime** (small + banked code W3): merge W2-detect `crt0_small.s` `crt0_banked.s`. 騩 ਬ `examples/banked/` ⥯ ᯮ MEMORY=huge. 2026-05-30. -- [x] **`--debug` 䫠**: prepend `DEBUG_RT = 1` crt0 + `-DDEBUG_RT` sdcc. 뢠 symbol `_w2_self_allocated` (uint8_t) ? runtime diagnostic ஢ W2. 2026-05-30. - -### -襭 libc crt0 - -** `sprinter.lib`** ࠡ⠥ memory mode ? `.rel`-童 relocatable, SDLD dead-code elimination per-member ( 䨪  `gfx_core.rel` ..). Verified hello vs malloc_test १ map-䠩. - -**`gfx.lib` ⤥쭮 ? 㦥**: dead-code elimination 㦥 ࠡ⠥. - -**`libc_banked` (libc bank HOME)** ? ⮬, HOME (16 KB) user- + libc `huge` mode. १ `--codeseg BANK0` 樨 libc; trade-off: trampoline ~30 横 libc-맮. ਣ: ॠ쭠 ணࠬ HOME budget. - -**HW-ᨬ 㫨 ? `sprinter_home.lib` ⤥쭮.** libc 䨧᪨ W3, ⮬ W3: -- `gfx_*` ? `0xC000+` ᫥ swap W3 video page -- `bank_io` (mem_alloc_pages/bank_read/bank_write) ? swap' W3 १ `OUT (0xE2)` -- 騥 ISR ? 뢠 ਩ W3 㣮 - - huge mode 㫨 HOME (W1). 㤥 `libc_banked`, **६** 뤥塞 `sprinter_home.lib` (HOME-only) `sprinter.lib` (bankable). 쭠 奬: -``` -sprinter_home.lib HOME-only: gfx, bank_io, ISR shims -sprinter.lib bankable: printf, malloc, string, conio, stdio, env, ... -sprinter_banked.lib sprinter.lib --codeseg BANK0 ( huge) -``` -ਣ: ॠ `--memory huge` runtime. - -**crt0 ? mode:** -- `crt0.s` ? ⥪騩, **tiny/big**: SP=0xBFFE, argv (W2- 㦥 뤥 DSS). -- `crt0_minimal.s` ? ⥪騩, tiny argv. -- `crt0_small.s` ? **, step 3**: **small/huge**, W2 १ `mem_alloc_pages` gsinit, `0xA2`, ⮬ ⠭ flow. -- `crt0_banked.s` ? ⥪騩, **big**: trampoline-⠡ W3 , CODE W2. -- `crt0_banked_small.s` ? ****: huge = small (W2-alloc) + banked (W3 trampolines). - -sprinter-cc ࠥ crt0 `--memory` mode (ᥩ `--crt0=` override). -- [x] **ࠨ ࠧ ⥪**: 䫠 `sprinter-cc --stack-size BYTES`. Wrapper `heap_top.s` `___sdcc_heap_end = stack_top + 1 - stack_size`, ⤥ .rel per-program. Default ?1278 (heap_top=0xBB00) `runtime/heap_top.s`. 2026-05-30. - -䥩: `sprinter-cc --memory [tiny|small|big|huge|manual] [--memory-manual SPEC] [--stack-size N] foo.c`. `--memory-manual` ⮫쪮 `--memory manual`. - -## Known issues / quirks - -- **ESTEX $46 ENV API**: ? ࠡ⠥. - `DiskSyscalls.txt v1.6` 訡筮 뢠 return-status ? A=0 NOT FOUND, FOUND. 䨪஢ `memory/sprinter_platform.md`. - -## V2 (᫥ ५ v1) - -### Turbo-C-style graphics API (BGI-like) ? **MUST v2** - - `gfx_*` API ஢ **Turbo-C ``** (BGI) MS-DOS. -ணࠬ ਢ訥 Turbo-C ७ ᪨ 1--1. - -** ** ( ᭮ Borland BGI): - -Setup/teardown: -- `initgraph()` / `closegraph()` ? ᥩ `gfx_init`/`gfx_done`, alias -- `getmaxx()` / `getmaxy()` ? GFX_WIDTH-1 / GFX_HEIGHT-1 -- `cleardevice()` ? alias to gfx_clear -- `getgraphmode()` / `setgraphmode()` ? get_videomode/set_videomode - -Color/palette: -- `setcolor(c)`, `getcolor()` ? current draw color -- `setbkcolor(c)`, `getbkcolor()` ? background color -- `setpalette(idx, c)` ? entry -- `getpalette(&info)` ? read all palette - -Primitives ( 㦥 ? BGI- aliases): -- `putpixel(x, y, c)` ? gfx_putpixel -- `getpixel(x, y)` ? 㦭 ॠ (RMW ⭮ ? IN) -- `moveto(x, y)`, `lineto(x, y)`, `linerel(dx, dy)` ? current point + line drawing -- `line(x1, y1, x2, y2)` ? gfx_line -- `rectangle(x1, y1, x2, y2)` ? gfx_rect ( 㣮 API: x1,y1,x2,y2 vs x,y,w,h!) -- `bar(x1, y1, x2, y2)` ? gfx_fill_rect -- `bar3d(x1, y1, x2, y2, depth, topflag)` ? : rect + 3d edges -- `circle(x, y, r)`, `arc(...)`, `ellipse(...)`, `pieslice(...)` ? primitives -- `fillpoly()`, `drawpoly()` ? -- `floodfill(x, y, border_color)` ? - -Text on graphics screen: -- `outtext(s)` / `outtextxy(x, y, s)` ? gfx_text (alias) -- `settextstyle(font, dir, size)` ? multiple bitmap fonts -- `gettextsettings(&info)` -- `textwidth(s)` / `textheight(s)` ? measure - -Image manipulation: -- `imagesize(x1, y1, x2, y2)` ? bytes needed for getimage -- `getimage(x1, y1, x2, y2, buf)` ? save rect to buffer -- `putimage(x, y, buf, op)` ? paste back with COPY_PUT/XOR_PUT/AND_PUT/OR_PUT/NOT_PUT - -Clipping/viewport: -- `setviewport(x1, y1, x2, y2, clip)` ? drawing clip rect -- `getviewsettings(&info)` -- `clearviewport()` -- `setactivepage(p)` / `setvisualpage(p)` ? ਧ (Sprinter 2 screen) - -Line style: -- `setlinestyle(style, pattern, thickness)` ? SOLID_LINE / DOTTED_LINE / etc. -- `getlinesettings(&info)` - -**Acceptance:** ७ ⨯筮 Turbo-C BGI ணࠬ (饩 ᯮ짮 -moveto/lineto/circle/bar/setcolor) ࠡ ⢥ ࠢ. - -**Notes:** -- BGI fonts (TRIPLEX/SANS_SERIF/GOTHIC) ? BIOS font, ⠫ 㦭 - ( bitmap data lib) -- imagesize/getimage/putimage ? ᠬ ॡ game/animation -- Active/visual page ( ਧ) ? Sprinter ন 2 graphics pages, - 㦥 API switching - -. ⠪ `examples/` Turbo C 2.x BGIDEMO reference 㦭. - -### IM2 Interrupt Handlers ? **MUST v2** - -User- ISR १ Z80 IM 2 mode. 㦭 : -- Timer ticks (50 Hz frame counter, ) -- Music playback (AY, COVOX) -- Real-time games (input + game logic + render interrupt-driven) -- Async keyboard / mouse handling - -**Status:** v2. research + design `docs/im2_isr_design.md`. - -**襭 ⥪:** ॠ ⤥ memory mode `--memory im2` -( ⮣ ⮡ 騥 crt0). Detail' design-doc. - -** research'** ( ⥪ `docs/im2_isr_design.md`): -- Vector 0xFF ? frame + keyboard + CBL. Disambiguation ⠬ 0x19 / 0xFE -- Mouse hardware-IRQ 室 ( ⥪饩 ) -- Vector table / ISR / stack W2 (0x8000..0xBFFF) -- DSS ᢮ IM 2 handler ? 㦭 chain' ( / SYSTIME ) - -### 稥 㯭 㭪 v2 - -- [ ] **FILE API rewrite ? buffered streams** ? ⥪ ॠ - `libc/stdio/file.c` provisional unbuffered shim ( fputc/fgetc - = read/write syscall). 㦭 業 buffered ᥬ⨪ - Solid-C: - - ```c - typedef struct { - uint flags; // +0..1 file status flags - int level; // +2..3 empty/fill level of buffer - char *curp; // +4..5 current active pointer - int fd; // +6..7 underlying low-level fd - char *buffer; // +8..9 data transfer buffer - char hold; // +10 ungetc byte if no buffer - short token; // +11..12 reserved - char dummy; // +13 reserved - } FILE; - ``` - - stdin/stdout/stderr ? fd-થ `0 / -1 / -2`. ⥫ - stdout/stderr ࠭ ७: ESTEX OPEN positive - small fd (1, 2, ?) 筮 䠩 ? ᫨ stdout=1, ॠ - fd=1 ⠫ 䨪஬. fd=0 stdin ᭮ - (ESTEX 0 頥). fd । syscall' ? - ᯥਧ 䫠 `_F_CONIN/_F_CONOUT`. - - ਭ-⮪ (stdaux/stdprn) ॠ㥬 ? Sprinter ਭ୮ - API . - - ୠ⨢ ? ॠ third_party/solid-c (sources - `SRC/CLIB/`); ⠬ ⮢ buffered FILE + fopen/fread/fwrite/ - fseek/setvbuf .. ஢ 訬 open/read/write/lseek. - - rewrite deferred issues stdio-review: - - `fwrite` short-write ⠢ `_F_ERROR` - - `fgets(buf, 1, fp)` ? ⠭ "empty string", 㫨 NULL - - `mode_to_flags` ? break-out '+' (cosmetic) - -- [ ] **Audio API** ? AY-3-8910 + COVOX १ 뢠 (ॡ IM2) -- [ ] **ISA-8 slot support** ? ZX-Bus (sound, network, etc.) ? ॡ IM2 + ⥭ port - -## 稥 (v1 backlog, 騥) - -- [x] **#9: text I/O split (Turbo-C style)** ? stdio (puts/printf/putchar) ⥯ fast no-attr १ PCHARS/PUTCHAR. conio (cputs/cprintf/putch) ਬ attr १ textcolor/textbackground/textattr. KEEP_EXIST_ATTR ? conio fallback fast path. Verified hello.exe. . `memory/text_output_api_split.md`. 2026-05-31. -- [x] **Mouse API ** (१ driver, RST 30h) ? 14 㭪権 (init/show/hide/refresh/read/goto/bounds/text_cursor/load_cursor/get_cursor/get/set_sensitivity/video_mode_changed). . `memory/mouse_api.md`. Verified MAME 2026-05-31. Sensitivity = divider ( = ॥). -- [ ] Interrupt handlers ? IM 2 vector table HOME user ISR' -- [ ] প `restore SP on EXIT` ( z88dk +pps) ? ஢ 㦭 -- [ ] CI: ⮬᪨ ᪠ MAME `-aviwrite` screenshot-ࠢ, ⮡ ਬ஢ 室 祫 - -## ⮬ - -- প `` ( SDCC stdlib ? 㦭 ஢ crt0 ᮢ⨬) -- `` १ ESTEX SYSTIME (`$21`) CMOS BIOS-㭪樨 -- ZX Spectrum-ᮢ⨬ ० ⤥ target ( ஢ ᯥ㬮᪨ ணࠬ) -- প ZX-Bus (sound, network, etc.) ? 㦭 ࠩ -- Profile-guided optimization tools (hot/cold detection) 㯭 ணࠬ - -## Linker duplicate-symbol warnings (, 䨫஢) - - ᮧ⥫쭮 overrides' SDCC z80.lib 㭪樨 ᮡ⢥ ᨥ `sprinter.lib`, `sdldz80` `?ASlink-Warning-Definition of public symbol '...' found more than once`. ࢮ । (), ⮬ ४⭮ ? warning ⮫쪮 noise. - -騥 overrides: -- `_puts` ? १ PCHARS+\r\n vs SDCC posix puts -- `___sdcc_heap` ? heap W2 vs SDCC's ⠭ -- `_asctime`, `_localtime` ( 㣨 time) ? `posix_time.c` १ ESTEX SYSTIME vs SDCC's `time.rel` `_RtcRead` - -**饥 襭:** `bin/sprinter-cc` 䨫஢뢠 warning- (warning + 2 follow-up `Library:` ப) 뢮 `sdcc`. १ `-v` (verbose) 뢠. १ awk-pipe. - -** 襭:** -- ३ explicit `--nostdlib` + 筮 ᯨ᮪ 㦭 㫥 z80.lib (string, math, stdlib override') ? warning', ᪨஢ -- : २ `_puts` ? `_puts_sprinter` + alias १ linker flag ( 㢥७ SDCC ন) -- : ⠢ ᥩ (ࠡ祥 benign) ? ਮ - -## TODO: ஢ ॠ쭮 - -- [ ] **Port_Y banking trick** (`docs/part2/SprinterGraphics programming.txt`): - ⢥ত ᫥ `OUT (0x89), Y` 0xC000+0x400*N W3 - ப Y..Y+15 ( ணࠬ஢ ? 16 ப). - Empirical 2026-06-01 MAME 0.283 trick ** ࠡ⠥** ? ᥫ - ᠬ 0xC000+row_width 室 . ᪨ - `docs/samples/plasma2.asm` ⮦ ᯮ banking, ⠭ - Port_Y per row. - : - 1. ॠ쭮 Sprinter - 2. dual-write (`_gfx_putpixel_raw` + ன write `0xD000+x`) - 3. ᫨ ? MAME, issue - ९ - 4. ᫨ ⮦ ? 㬥 , 㤠 㯮 - ⠢ ⥪ ॠ (Port_Y per pixel) - 5. ᫨ banking ࠡ⠥ ? ஢ Port_Y - `_gfx_putpixel_raw` (sentinel out-of-range, . memory/gfx_port_y_banking.md) - - 易 먣 Bresenham (60-pixel ) ? 8? - OUT (0x89) 権, `gfx_fill_rect 320x256` ? 16? . - release v1. - -## GFX: ७ `docs/part2/accelerator_doc.txt` - -᫥ ⥭ ⠫쭮 accelerator doc  ࠢ. - ᯮ ⮫쪮 ਧ⠫/⨪ Fill mode. - -### Quick wins ⥪ primitives - -- [ ] ** SMC `LD A, (var)` block-size**. 㬥  - ࠧ蠥 `LD A, (HL)`, `LD A, (BC)`, `LD A, (DE)` ( `LD A, r`). - 㡥 SMC complexity `gfx_lines.c:hfill_chunk/vfill_chunk` - `gfx_16.c:g16_hfill_chunk`. 饭 ⮫쪮 register-to-register. -- [ ] **஢ block-size**. 㬥 뢠 accel - block size bursts (. `Horizontal_Line_Fill`: ⠭ - size + `LD B,B` ⪫祭, ⮬ Fill mode ᯮ - ࠭ size). `gfx_fill_rect` 100 묨 - ப ? ⠭ size 1 ࠧ, 100. - -### Bank-prefix modes (port 0xE2 bits) - -㬥 뢠 ਠ ࠭ ⠭⭮ 0x50: - -| Bank byte | Effect | -|---|---| -| 0x50 | Normal write ? shadow + | -| 0x54 | "no copy in main shadow RAM" | -| 0x58 | **"FF is transparent"** ? 0xFF write ⠢ background | -| 0x5C | both | - -Bank 0x58 祬 mouse cursor 0xFF-஧筮. - **sprite-blending १ accel block copy**: - -- [ ] **`gfx_set_bank_transparent(on)`** 䫠 `gfx_set_bank` - 롮 0x50/0x58 ᮢ sprite' -- [ ] ᯮ짮 `gfx_blit()` ⮡ 䠪 - transparent sprites १ accel- - -### Block copy mode (sprite blit') - -`LD L,L` (horizontal) `LD A,A` (vertical) ? ० ஢ १ -256- accel memory. blit'. - -- [ ] **`gfx_blit(src_data, x, y, w, h)`** ? ஢ sprite' - (ந ࠧ, १ accel) -- [ ] **`gfx_blit_transparent(src, x, y, w, h)`** ? ᯮ짮 bank 0x58 - -. `Draw_Restangle_Data` accelerator_doc.txt ७. - -### AND / OR / XOR operations १ accel - -㬥 뢠 accel ন ᪨ 樨 -. ਬ: -- XOR ? (뤥 selection UI) -- OR / AND ? masking, alpha-style blending -- . ਬ accelerator_doc.txt: "256 bytes block coding via XOR" - -- [ ] **`gfx_xor_rect`** / **`gfx_or_rect`** / **`gfx_and_rect`** ? - ਬ⨢ ᪨ 権 אַ㣮쭨 -- [ ] **`gfx_invert_rect(x, y, w, h)`** ? alias xor 0xFF - -### Bitmap fonts ࠧ ࠧ஢ - - `gfx_text` / `gfx_putchar` प ࠡ 8?8 ⮬ -(BIOS WIN_GET_ZG 頥 256?8 ). UI / 쭨 -㦭: -- [ ] **`gfx_set_font_size(w, h)`** ? ४ ਭ/ glyph' -- [ ] **`gfx_set_font_data(ptr, w, h, advance)`** ? 㪠⥫ - 짮⥫᪨ + ࠧ -- [ ] প **proportional** (advance != w) ⮢ ? - array advance[256] ਭ glyph' -- [ ] **Big-font ०**: 8?16, 16?16, 16?8 ( 쭨) -- [ ] ⤥ API `gfx_text_ex(x, y, str, font_id)` - font_id 롨ࠥ 㦥 ⮢ -- [ ] **Anti-alias 2-bit ** ( 䮭 / ࠭ / 2- alpha?) - ? far future, smooth UI - -## ⠯ ⨬権 ( ᥩ) - -- **`gfx_line` १ accel ** ? Bresenham |dy| << |dx| ( ) 뤠 runs Y ( X): ᥫ, ᥫ, ᥫ, 蠣 Y, ᥫ, ᥫ... ⠪ run ? ⮢ 㬥 `gfx_hline` ( `vline`). - ᫥: runs 㭪 ; run length, ஬ 룮 accel hline 祬 N?putpixel (overhead accel ~20?s, putpixel ~5?s ? accel 룮 run ? 4-5 px); (dx ? dy) ⠢ Bresenham, ? run-length-based fill. - `gfx_line` orthogonal cases 㦥 १ accel ? ⨬஢ ⮫쪮 . - -- **`gfx_fill_rect` W3-swap ** ? ᥩ ७ `gfx_hline`/`gfx_vline` ᢮ DI/save-W3/restore-W3/EI. ᤥ internal `_fill_rect_inner` ন W3 DI 横; ~20?s ? ⢮ ப/⮫殢 . ਬ ᥬ ਬ⨢. -# MDView ? ᬮ騪 Markdown Sprinter - -**MDView** ? ணࠬ ᬮ 㬥⮢ ଠ *Markdown* -Sprinter ( Z80). 㬥 ࠭ ⤥쭮 W3 -᭮ RAM ணࠬ. - -## - -- 㬥 **128 ** (8 ࠭ EMM 16 ) -- **16 384** ࠭ ப -- ⮬᪨ ७ ᫮ ਭ ࠭ (80 ⮫殢) -- ਧ⠫ ᤢ ப ப ( , ⠡) -- -: 䠩, ப, 業 ப⪨ -- ப ﭨ ६ 㧪 樨 -- প ?? ᪫ ப - -## - -``` -mdview [_䠩.md] -``` - -᫨ 䠩 , 㦠 `README.MD`. - -## ࠢ - -``` - ⢨ - -Up Down ப⪠ ப / -PgUp PgDn ப⪠ ࠭ (30 ப) -Home 砫 㬥 -End 㬥 -Left Right ਧ⠫ ᤢ (⮫쪮 nowrap-ப) -F1 ࠢ -F8 ஢: CP866 ? CP1251 ? KOI8-R ? UTF-8 ? ... -F10 / Esc 室 ணࠬ -``` - -஢ । ⮬᪨ ⨨ (BOM + ⨪ - 4 ); `F8` ४砥 , ᫨ ⥪ 訡. -8- ஢ (CP866/CP1251/KOI8-R) ४ (६ -ᮢ). ன / (ਬ UTF-8) ந ? -ࢮ ४祭 (⪠ 㧠 ᯨ஬), . - -## ⠪ Markdown - -### - -ন ஢ H1?H4. ஢ H5 H6 ⮡ࠦ H4. - - # ࢮ ஢ - ## ண ஢ - ### 쥣 ஢ - #### ⮣ ஢ - -### ⮢ ଠ஢ - -** ⥪** 뤥 묨 񧤮窠: `**⥪**` - -*ᨢ* 뤥 묨 񧤮窠 `*⥪*` ન `_⥪_` - -`஥ ` 砥 묨 窠 - -~~ભ ⥪~~ ? ⨫: `~~⥪~~` - -### 㬥஢ ᯨ᮪ - -થ `-`, `*` `+`: - -- 㭪 ᯨ᪠ -- ன 㭪 ᯨ᪠ -- ⨩ 㭪 筮 ⥪⮬, 室 - 㤥 ७ ᫥ ப ࠭ 㯠 - -### 㬥஢ ᯨ᮪ - -1. -2. ன -3. ⨩ - -### - -> 稭 ᨬ `>`. ᪮쪮 ᫥⥫ -> ப ᪫ ⮬᪨ -> ७ᮬ ᫮. - -### (verbatim) - - 砥 ன 窨. -⥪ ⮡ࠦ ? ? ࠧ Markdown: - -``` -#include -#include - -int main(void) { - puts("Hello, Sprinter!"); - return 0; -} -``` - -### ਧ⠫쭠 - - ᨬ `---`, `***` `___` ⤥쭮 ப: - ---- - -## 孨᪨ ࠪ⨪ - -- **ଠ:** Sprinter, Z80 @ 21 -- **஢:** CP866 / CP1251 / KOI8-R / UTF-8 (⮮।, `F8`) -- **ᨬ ࠧ 䠩:** 128 -- **ᨬ쭮 ᫮ ப :** 16 384 -- ** :** small - - ணࠬ, c⥪, , ? a W1-W2 (32 , 0x4000?0xBFFF). - - 䠩 ? ࠭ EMM, ⮡ࠦ W3 (0xC000?0xFFFF) - -## TODO - -1. **祭 ࠧ 㬥⮢.** 128 : 筮 - ࠧ ࠡ 訬 - ࠭ , ஢ - ࠡ 8- ࠭栬 16. - -: ଠ஢ ⠡ ࠬ; প ஢ -CP866 / CP1251 / KOI8-R / UTF-8 ⮮। ४祭 `F8` -(ன ந , ࢮ ४祭). - -4. **᪮७ ७ਭ.** ப ࠭. ⨬ 横 뢮 - ᨬ १ BIOS WRCHAR ( 뢮, DMA). - ---- - -*MDView v0.2 (c) 2026 ஢ ..* - - ---- - -# Sprinter C Compiler ? v1.0 - -C toolchain for **Sprinter** ? the Z80-based home computer by Peters Plus, running -ESTEX DSS. Host: macOS / Linux. Target: `.EXE` files in SprintEXE format. - -Built on top of **SDCC 4.5** (vendored in `third_party/sdcc/`). This repository adds -everything Sprinter-specific: crt0, linker integration, libc wrappers over ESTEX, -banked-call trampolines, graphics & accelerator API, mouse driver wrappers, and the -`mkexe` utility for producing SprintEXE images. - -## What you get - -* **`bin/sprinter-cc`** ? one-line driver: `sprinter-cc -o foo.exe foo.c` -* **Memory modes**: `tiny`, `small`, `big`, `huge`, `manual` ? see below. -* **stdio + conio**: printf, puts, putchar, getchar, fopen/fread/..., cprintf, cputs, putch, textcolor/textbackground/textattr, gotoxy, kbhit/getch. -* **Graphics**: 320?256?256 and 640?256?16 modes, accelerator-backed primitives (hline / vline / rect / fill_rect / line via Bresenham, plus clear), bitmap-font text in both modes via BIOS character generator. -* **File I/O**: POSIX (`open`/`read`/`write`/`close`/`lseek`/`unlink`/`creat`), FILE\* streams (`fopen`/`fgets`/`fwrite`/...), directory listing (`ffirst`/`fnext`), `chdir`/`getcwd`/`mkdir`/`rmdir`, `stat`/`fstat`. -* **Memory**: 32 KB heap (W2-resident), banking-aware page allocator (`mem_alloc_pages`/`bank_read`/`bank_write`), explicit memory modes for sub-16 KB programs. -* **Mouse**: full Sprinter driver wrapper (14 functions including custom cursor bitmaps). -* **Environment**: `getenv`/`putenv`/`sysenv` over ESTEX `$46`. -* **Time**: `getdatetime`/`setdatetime` + POSIX `time`/`localtime`/`mktime`/`asctime`/`ctime`. -* **Misc**: `errno`/`strerror`/`perror`, `atexit`, `setjmp`/`longjmp`, `sleep`, full argv parsing in crt0. - -## Quick start - -```sh -git clone sprinter-c -cd sprinter-c -make sdcc # one-time: fetch SDCC 4.5 binary (~25 MB) -make all # build mkexe + libsprinter.lib + 27 examples -make floppy # pack everything into mame/v306/IMG/mc.img -cd mame/v306 && ./run_mame.sh # boot Sprinter in MAME -``` - -Compile a single program: - -```sh -cat > hello.c < -int main(void) { puts("Hello, Sprinter!"); return 0; } -EOF - -bin/sprinter-cc -o hello.exe hello.c -``` - -That's it ? `hello.exe` is now a valid SprintEXE you can `RUN HELLO` from the ESTEX shell. - -## Memory modes - -Sprinter's address space is four 16 KB windows (W0 / W1 / W2 / W3). DSS allocates -pages by program size ? small programs get only one page. Pick a memory mode based -on what your program needs: - -| Mode | Code lives in | Banking | Use when | Note | -|---|---|---|---|---| -| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | | -| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | | -| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | | -| `huge` | W1-W2 + W3 banking | yes (W3) | small + extra code modules | | -| `manual` | user-specified | optional | special layouts | Not implemented | - -```sh -sprinter-cc --memory small -o big.exe bigprog.c -sprinter-cc --memory huge -o app.exe main.c --bank 1=engine.c --bank 2=ai.c -``` - -Banked functions are declared with `__banked`: - -```c -void engine_tick(int dt) __banked; // lives in BANK1, automatically swapped -``` - -## Examples (27 total) - -| Example | What it demonstrates | -|---|---| -| `hello` | Hello world with stdio + conio Turbo-C-style colors | -| `argv` | argv parsing in crt0 | -| `cat` | File I/O ? read & print TEST.TXT | -| `seek` | 32-bit lseek over a 100 KB file | -| `ls` | Directory listing via ffirst/fnext | -| `filetest` | FILE\* streams (fopen/fread/...) | -| `stattest` | `stat`/`fstat` on files and directories | -| `errno` | errno / strerror / perror | -| `mem_test` | Page allocator + bank\_read/bank\_write | -| `malloc` | Heap stress test (200+ allocations) | -| `banked` | Banked code in W3 (huge mode) | -| `bankedbg` | Banked code in W1 (big mode) | -| `banklocl` | Bank-local static data and BSS | -| `mouse` | Mouse driver in text mode | -| `gfx_mous` | Mouse with custom bitmap cursor in graphics mode | -| `gfx_demo` | 320?256?256 lines / rects / accelerator | -| `gfx_d16` | 640?256?16 same primitives | -| `gfx_text` | Bitmap-font text on graphics screen | -| `timedir` | Date/time + directory listing | -| `ptime` | POSIX time API | -| `openenv` | open() flags + environment vars | -| `conio` | conio API smoke test | -| `attrprob` | Probe Sprinter text attribute byte layout | -| `strtest` | string.h test (from SDCC's z80.lib) | -| `stdlib` | stdlib.h test (qsort / rand / strtol / etc.) | -| `assrtest` | assert() | -| `rt_test` | Runtime helpers (sleep, setjmp, atexit) | - -## Headers - -Standard: -* `` ? puts / printf / FILE\* + Sprinter-specific dec/hex helpers -* `` ? atoi / atof / malloc / qsort / ... (from SDCC z80.lib) -* `` / `` / `` ? from SDCC z80.lib -* `` ? read / write / close / lseek / unlink -* `` ? open / creat + O\_RDONLY / O\_CREAT / ... -* `` ? errno + error names + strerror -* `` ? stat / fstat -* `` / `` ? from SDCC - -Sprinter-specific: -* `` ? putch / cputs / cprintf, textcolor / textbackground / textattr, kbhit / getch, clrscr, gotoxy, wherex/y -* `` ? gfx_init/done, palette, putpixel, hline/vline/rect/fill_rect/line, text ? for both 320 and 640 modes (gfx_\*16 variants) -* `` ? full 14-function driver wrapper + mouse_cursor_t with bitmap support -* `` ? chdir / getcwd / mkdir / rmdir / ffirst / fnext / ffblk -* `` ? getdatetime / setdatetime + POSIX time / localtime / etc. -* `` ? raw ports, ESTEX/BIOS function numbers, env API -* `` ? exit / \_exit / atexit -* `` ? mem\_alloc\_pages / mem\_free\_block / bank\_read / bank\_write -* `` ? Solid-C compatibility layer (aliases + BOOL/WORD/uint types) - -## Toolchain commands - -```sh -make all # build mkexe + lib + every example -make floppy # repack mame/v306/IMG/mc.img with all .exe files -make check # 17 mkexe unit-tests -make clean # remove all build artefacts -make sdcc # one-time: fetch SDCC 4.5 binary -``` - -### sprinter-cc options - -``` -sprinter-cc -o foo.exe foo.c [more.c ...] [options] - - --memory MODE tiny | small | big | huge | manual (default: tiny) - --memory-manual SPEC explicit placement (CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3) - --stack-size N bytes reserved for the stack (default ~1278) - --crt0=TYPE default | minimal | banked | small - --bank N=FILE.c compile FILE.c into bank N (repeatable, max 15) - --debug enable runtime diagnostics (defines DEBUG_RT) - -I PATH extra include path - -L 0xADDR / -E / -S override load / entry / stack addresses - -Wl FLAG pass FLAG to sdldz80 - --mkexe FLAG pass FLAG to mkexe (e.g. --mkexe -p --mkexe 0 for bank padding) - -v verbose -``` - -## Status - -What works in v1.0: -* Compile / link / pack to SprintEXE ? verified on all 27 examples -* Four memory modes (tiny / small / big / huge) -* Graphics (both modes) with accelerator -* Mouse (text + graphics cursor) -* File I/O, directories, environment, time -* All headers listed above - -Deferred to v2.0 (see `docs/TODO.md`): -* **Turbo-C-style BGI graphics API** ? `initgraph` / `setcolor` / `circle` / - `getimage` / `putimage` / etc. on top of our `gfx_*` primitives -* Remaining Solid-C compatibility gaps (Phase 2/3) ? see `docs/solid_c_compatibility.md` -* Manual memory mode -* Rewrite FILE\* stream API (current implementation is very primitive and doesn't use buffers) - -Deferred to v3.0: -* **IM2 interrupt handlers** ? research complete (`docs/im2_isr_design.md`), - implementation scheduled for v3 -* **Audio API** (AY-3-8910 + COVOX) ? requires IM2 -* **ISA-8 slot drivers** ? requires IM2 (???) - -## Documentation - -* `docs/TODO.md` ? roadmap and open work items -* `docs/solid_c_compatibility.md` ? gap analysis vs Solid-C 2004 -* `docs/im2_isr_design.md` ? interrupt handler design (v2) -* `docs/converted/` ? source documentation (ESTEX, BIOS, architecture) - converted to plain text for `grep` -* `docs/reference/`, `docs/samples/`, `docs/memory management/` ? original - Russian docs and code samples from Peters Plus - -## Repository layout - -``` -bin/sprinter-cc one-line compiler driver (bash) -toolchain/mkexe/ host-side tool: .ihx -> .exe SprintEXE -toolchain/check_banks.py post-link bank size enforcer -runtime/ crt0 variants (default, minimal, small, banked) - bank trampolines, heap, heap_top -libc/include/ headers -libc/io|stdio|mem|gfx/ C and asm sources for libsprinter.lib -lib/ Makefile that archives libsprinter.lib via sdar -examples/ 27 example programs -mame/v306/ MAME binary + Sprinter ROM/HDD images + floppy script -third_party/sdcc/ vendored SDCC 4.5 (fetched via `make sdcc`) -third_party/solid-c/ reference: original Sprinter native C (for compat target) -docs/ documentation -``` - -## License - -This repository contains: -* Original code in `bin/`, `toolchain/`, `runtime/`, `libc/`, `lib/`, `examples/` ? - MIT-licensed. -* `third_party/sdcc/` ? SDCC 4.5 under GPLv2 with linking exception - (see `third_party/sdcc/COPYING.txt`) -* `third_party/solid-c/` ? original Sprinter Solid C, used only as a reference - -## Credits - -* **Sprinter / Peters Plus** ? , ਩ ਭ and the original team -* **SDCC** ? for the underlying Z80 compiler -* **MAME** ? for the Sprinter Sp2000 emulation - ---- - -For questions / patches: see CONTRIBUTING.md (TBD) or open an issue. - ---- - -# TODO / Roadmap - - 浪 뢠 ਮ.  ॠ ணࠬ ? ਮ ᬥ. - -## ⠯ 5 ? malloc / free + banking-aware page allocator ? - -- [x] SDCC's `malloc`/`free` + `runtime/heap.s` ( library heap.rel, 14000- heap 2) -- [x] `libc/mem/mem_alloc.c` ? page allocator: `mem_alloc_pages`/`mem_free_block`/`mem_get_page`/`mem_info` १ ESTEX `$3C/$3D/$3E` + BIOS `$C4` -- [x] `libc/mem/bank_io.c` ? HOME-१ `bank_read`/`bank_write`/`bank_load_byte`/`bank_store_byte` ᢮ W3 -- [x] `examples/malloc_test/` ? ஢ઠ SDCC's malloc (~210 64- allocations १ heap) -- [x] `examples/mem_test/` ? ஢ઠ page allocator: 3 ࠭, ࠧ १ bank_write, 䨪 १ bank_read - -## ⠯ 6 ? argv parsing + sprinter-cc wrapper ? - -- [x] crt0 ESTEX command-line IX-prefix (inline asm `runtime/crt0.s`) -- [x] Strip leading CP/M-style space (DSS quirk) -- [x] । `argc`/`argv` main() १ HL/DE (SDCC __sdcccall(1) ABI) -- [x] argv[0] = basename .EXE १ ESTEX APPINFO ($47 subfn 2) -- [x] `runtime/crt0_minimal.s` ? opt-out 祭 쪨 ணࠬ -- [x] `runtime/crt0_banked.s` ? ⥯ ⮦ argv (parse_argv + get_progname ᪮஢ crt0.s; 㤥 factored argv.s libsprinter.lib) -- [x] Bash-⪠ `bin/sprinter-cc`: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権: `--memory`, `--memory-manual`, `--stack-size`, `--crt0=`, `--bank N=FILE.c`, `--debug`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` - -## ⠯ 8 ? 䨪 (320?256?256 + 640?256?16 + accel + bitmap font) ? - -- [x] **8a** Graphics core: `gfx_init`/`gfx_done`/`gfx_clear`/`gfx_putpixel`/`gfx_pal_load`/`gfx_pal_set` (libc/gfx/gfx_core.c). १ BIOS PIC_SET_PAL ($A4). Verified 320?256?256. -- [x] **8b** /אַ㣮쭨/fill १ accelerator (libc/gfx/gfx_lines.c): `gfx_hline`/`gfx_vline` १ accel Fill (LD C,C / LD E,E + SMC block-size), `gfx_rect`/`gfx_fill_rect` heuristic 롮஬ ਥ樨 (h/v bursts count), `gfx_line` Bresenham . `gfx_clear` ⮦ ९ᠭ column-major accel (~4? ॥). -- [x] **8c** 640?256?16 mode (libc/gfx/gfx_16.c): `gfx_*16` API, HIGH nibble = LEFT pixel (㬥 misleading), per-row RMW vline ( = 2 ਧ⠫ ᥫ). -- [x] **8d** Bitmap font + gfx_text (libc/gfx/gfx_text.c): १ BIOS WIN_GET_ZG ($B8), interleaved layout `font[row*256+char]`, `gfx_text`/`gfx_putchar` 320 mode, `gfx_text16`/`gfx_putchar16` 640 mode pair-table lookup. - -. memory/sprinter_graphics.md, sprinter_accelerator.md, sprinter_graphics_16.md, sprinter_font_format.md. - - ( ): -- [ ] -quad 640: per-cell (mode 0x82 ࠧ蠥 1 4 per 16?8 cell) ? १ אַ area-ᠭ ࠭ 0x0300..0x039F - -## Auto-banking (. `memory/banking_roadmap.md` ⠫) - -Phase 1 ? file-level bin-packing ? ॠ뢠 ஥ ~30 KB . - -- [ ] `toolchain/auto_bank.py`: - - ࠧ `.rel`-䠩 ( .map ᫥ dry-run link') - - First-fit-decreasing bin-packing - - `#pragma codeseg BANKn` manual override - - ५뢠 묨 `-Wl-b_BANKn=...` ࠬࠬ - - ⠥ । - -Phase 2-5: incremental rebalance, declarative `banks.toml`, function-level, call-graph-aware. 쪮 ᫨/ . - -## Bank-local static data (mutable data ⮬ ) ? ? - -- [x] ਬ `examples/bank_local_data/` ? 㭪 BANK1 ᢮ writable BSS array + const table + malloc- -- [x] `mkexe -p 0` 㫥 padding (BSS-storage 㧪) -- [x] ᪨ 楯: `--codeseg BANK1 --constseg BANK1 --dataseg BANK1` bank1.c + `-Wl-b_BANK1=0x1C000` . **`--dataseg BANK1` ** ? ࠭ ⭮ - trampoline bug ᪨஢ १. -- [x] ** 䨪 trampoline'a runtime/bank.s** ? `pop af; out (n), a` ਫ A ? banked-㭪樨 騥 uint8_t 頫 . `pop bc; out (c), b` ࠭ A. -- [x] **malloc banked-㭪樨 ࠡ⠥ ஧筮** ? heap W2 (HOME), W2 ᢠ trampoline', pointer  ⥪. . memory/bank_local_data_pattern.md. -- [x] 㬥 memory: `memory/bank_local_data_pattern.md` ( 楯 + malloc + nuances), `memory/sdcc_banking.md` (trampoline fix) -- [ ] 樮쭮 ? `check_banks.py` ⮡ 뢠 ࠧ size = code + const + bss per bank (cosmetic) - -祬: 㫥 訬 private state (level loader, audio engine, scene data). W2 heap , ⨪ . - -## ᪠ solid-c (⨢ Sprinter C ? `third_party/solid-c/`) - -᫥ solid-c'᪮ libc (. `memory/solid_c_findings.md`)  ⮢ ᫥ 㭪権. ਮ **᮪** : - -### High-priority gaps ( ஢, 짠) -- [x] **`errno` + `strerror`/`perror`** ? ⠡窠 32 訡 (libc/io/errno.c) -- [x] **७ `open()`** O_CREAT/O_TRUNC/O_APPEND/O_EXCL state machine -- [x] **`atexit`** ? 8-callback LIFO + `exit()` + `_exit()` (libc/io/atexit.c) -- [x] **`setjmp`/`longjmp`** ? 6- jmp_buf={sp,ix,pc} (libc/io/setjmp.c) -- [x] **`sleep(seconds)`** ? 50Hz halt-loop (libc/io/sleep.c) -- [x] **ESTEX ENV API** ($46, getenv/putenv) ? libc/io/env.c. 竨 doc-bug: ॠ쭮 A=0 NOT FOUND - -### Medium-priority (㦭 shell-like ⨫) -- [ ] **Mouse driver** ? `rst $30h`, 17 㭪権. **砫 ࠡ⠥ MAME**. -- [x] **`ffirst`/`fnext` + ffblk_t struct** directory listing ? ॠ, demo: ls.exe -- [x] **`getdatetime`/`setdatetime`** १ ESTEX $21/$22 ? libc/io/time.c, demo: time_dir_test -- [x] **`chdir`/`getcwd`/`mkdir`/`rmdir`** ? wrappers ESTEX $1B-$1E ? libc/io/fsdir.c -- [x] **conio: `kbhit`/`getch`/`getche`/`cputs`/`clrscr`/`gotoxy`** ? ॠ -- [x] **conio extras**: `wherex`/`wherey` ($53), `wrchar`/`rdchar` ($58/$57), `textmode_get/set` ($50/$51), `clrscr_attr` ($56) + COLOR macros - -### Low-priority ? ? FILE* stack - -- [x] ** unbuffered FILE\*** ? `libc/stdio/file.c` + `libc/include/stdio.h`. fopen/fclose/fputs/fgets/fread/fwrite/fseek/ftell/rewind/feof/ferror/clearerr/fflush + stdin/stdout/stderr pseudo-streams. . `memory/file_star_design.md` `examples/filetest`. -- [ ] fprintf / fscanf ? 㦭 printf-१-callback machinery. 짮⥫ `sprintf(buf, ...) + fputs(buf, fp)`. -- [ ] 樮 buffered mode (setvbuf, line/block buffering) ? ᫨ - . - -### POSIX time API ? ? -- [x] `libc/io/posix_time.c` ? time/localtime/gmtime/mktime/asctime/ctime getdatetime. SDCC's time.rel ( override _RtcRead). . `examples/ptime`. - -### sys/stat ? ? -- [x] `libc/io/stat.c` ? POSIX stat/fstat. ਤ open+fstat 䠩, ffirst+iter ( "."/".."). . `examples/stattest` `memory/estex_ffirst_dotdot.md`. - -### assert ? ? (ᯮ㥬 SDCC's __assert १ fallback include path) - -## libc/stdlib ? ? 㦭 (. memory/sdcc_stdlib_works.md) - -஢७ १ `examples/stdlib_test/`: SDCC z80.lib ᮤন ࠡ騥 ॠ樨: -- `atoi/atol/atof, strtol/strtoul, rand/srand, qsort/bsearch, abs/labs, div/ldiv` -- `` (memchr/cmp/set/cpy, strcat/cmp/cpy/len/chr/spn/etc.) -- `` (toupper/tolower) -- `` (sinf/cosf/sqrtf/etc.) - - ⮬᪨ ﭥ z80.lib 㦭. ** ९뢠**. - - Sprinter-specific 易⥫ 㫨 : atexit, env, errno, setjmp, putchar/puts/getchar, conio, fsdir, time, mouse, open/read/lseek/close. - -## Build-system: libsprinter.lib + sprinter-cc ? ? - -- [x] `lib/Makefile` ? ᮡࠥ libc/*.c `.rel`, 娢 १ sdar `lib/sprinter.lib` -- [x] 砥 runtime/bank.s runtime/heap.s (auto-pulled __banked/malloc) -- [x] `bin/sprinter-cc` ? bash-wrapper: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権 `--crt0=default|minimal|banked`, `--bank N=FILE.c`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` -- [x] `examples/hello_sccc/` ? : `hello.c` ᮡࠥ shell-맮, ࠧ ᮢ Makefile (925 ) -- [x] Split `putchar.c` ? `putchar.c` + `puts.c` per-function granularity (puts override SDCC's z80.lib version) -- [x] `make all` (ᨬ `lib` । `examples`) - - 襭 (樮쭮): -- [ ] ஢ ⠫ examples sprinter-cc Makefile (ᬥ⨪) -- [ ] 쭥 libc/*.c per-function ( ⥪ granularity 㦥 㦭 ࠧ ? .rel 楫, 設⢠ 䠩 㭪) - -## ⠯ 9 ? memory modes sprinter-cc - -DSS 뤥 ࠭ ࠧ ਫ: < 16 KB ? ࠭, ⠫ 砥 ?࠭ #FF? (read=0xFF, write ). - ⮣ CODE--W1 + DATA--W2 쪮 ணࠬ . . [memory/sprinter_memory_modes.md](../../.claude/projects/-Volumes-SAM8-Projects-DIY-Z80-Sprinter-C-Compiler/memory/sprinter_memory_modes.md). - -- [x] **`tiny`**: (CODE+DATA+⥪) W2. Default. Verified hello/argv/conio/malloc/file/etc. -- [x] **`--memory MODE` 䫠 sprinter-cc**: parser + per-mode 䮫 CODE_LOC/DATA_LOC, override १  `--code-loc`/`--data-loc`. tiny ࠡ⠥; small/big/huge warning' (runtime ⮢). 2026-05-30. -- [x] **`--memory-manual SPEC`**: `CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3`. 2026-05-30. -- [x] **`small` runtime**: `runtime/crt0_small.s` ᯮ ESTEX `$3D GETMEM` + `$3A SETWIN2` ⮡ 뤥 W2-࠭ gsinit. **** BIOS `$C4` ? ⥪ ⮬ ⠯ W1 (boot_stack HOME), BIOS ॡ ⥪ W2. ᫥ SP ४砥 0xBFFE, ⠭ flow. 2026-05-30, verified hello.exe. -- [x] **`small` auto-detect >16 KB ணࠬ**: `crt0_small.s` ⠥ `0xC2` (⥪ ࠭ W2 ? `0xA2`! W1). ᫨ 0xFF ? 뤥 page; DSS 㦥 ᤥ (ணࠬ ᠬ 뫥 W2). crt0 뢠 0..30 KB. mkexe ⠪ ࠧ蠥 HOME span W1+W2 (0x4000..0xBFFF). Verified hello: small (5 KB 䠩, SETWIN2 path) + 32 KB 䠩 (auto-skip). 2026-05-30. -- [x] **`big` runtime** (tiny + banked code W1): ࠬਧ `crt0_banked.s` + `bank.s` १ `.ifdef BANK_W1` ? 㣮 banking port (0xA2 vs 0xE2), 㣮 load-addr (0x4000 vs 0xC000). sprinter-cc prepend' `BANK_W1 = 1` `--memory big`, । `mkexe -B 0x4000`. ਬ `examples/banked_big/`. 2026-05-30. -- [x] **`huge` runtime** (small + banked code W3): merge W2-detect `crt0_small.s` `crt0_banked.s`. 騩 ਬ `examples/banked/` ⥯ ᯮ MEMORY=huge. 2026-05-30. -- [x] **`--debug` 䫠**: prepend `DEBUG_RT = 1` crt0 + `-DDEBUG_RT` sdcc. 뢠 symbol `_w2_self_allocated` (uint8_t) ? runtime diagnostic ஢ W2. 2026-05-30. - -### -襭 libc crt0 - -** `sprinter.lib`** ࠡ⠥ memory mode ? `.rel`-童 relocatable, SDLD dead-code elimination per-member ( 䨪  `gfx_core.rel` ..). Verified hello vs malloc_test १ map-䠩. - -**`gfx.lib` ⤥쭮 ? 㦥**: dead-code elimination 㦥 ࠡ⠥. - -**`libc_banked` (libc bank HOME)** ? ⮬, HOME (16 KB) user- + libc `huge` mode. १ `--codeseg BANK0` 樨 libc; trade-off: trampoline ~30 横 libc-맮. ਣ: ॠ쭠 ணࠬ HOME budget. - -**HW-ᨬ 㫨 ? `sprinter_home.lib` ⤥쭮.** libc 䨧᪨ W3, ⮬ W3: -- `gfx_*` ? `0xC000+` ᫥ swap W3 video page -- `bank_io` (mem_alloc_pages/bank_read/bank_write) ? swap' W3 १ `OUT (0xE2)` -- 騥 ISR ? 뢠 ਩ W3 㣮 - - huge mode 㫨 HOME (W1). 㤥 `libc_banked`, **६** 뤥塞 `sprinter_home.lib` (HOME-only) `sprinter.lib` (bankable). 쭠 奬: -``` -sprinter_home.lib HOME-only: gfx, bank_io, ISR shims -sprinter.lib bankable: printf, malloc, string, conio, stdio, env, ... -sprinter_banked.lib sprinter.lib --codeseg BANK0 ( huge) -``` -ਣ: ॠ `--memory huge` runtime. - -**crt0 ? mode:** -- `crt0.s` ? ⥪騩, **tiny/big**: SP=0xBFFE, argv (W2- 㦥 뤥 DSS). -- `crt0_minimal.s` ? ⥪騩, tiny argv. -- `crt0_small.s` ? **, step 3**: **small/huge**, W2 १ `mem_alloc_pages` gsinit, `0xA2`, ⮬ ⠭ flow. -- `crt0_banked.s` ? ⥪騩, **big**: trampoline-⠡ W3 , CODE W2. -- `crt0_banked_small.s` ? ****: huge = small (W2-alloc) + banked (W3 trampolines). - -sprinter-cc ࠥ crt0 `--memory` mode (ᥩ `--crt0=` override). -- [x] **ࠨ ࠧ ⥪**: 䫠 `sprinter-cc --stack-size BYTES`. Wrapper `heap_top.s` `___sdcc_heap_end = stack_top + 1 - stack_size`, ⤥ .rel per-program. Default ?1278 (heap_top=0xBB00) `runtime/heap_top.s`. 2026-05-30. - -䥩: `sprinter-cc --memory [tiny|small|big|huge|manual] [--memory-manual SPEC] [--stack-size N] foo.c`. `--memory-manual` ⮫쪮 `--memory manual`. - -## Known issues / quirks - -- **ESTEX $46 ENV API**: ? ࠡ⠥. - `DiskSyscalls.txt v1.6` 訡筮 뢠 return-status ? A=0 NOT FOUND, FOUND. 䨪஢ `memory/sprinter_platform.md`. - -## V2 (᫥ ५ v1) - -### Turbo-C-style graphics API (BGI-like) ? **MUST v2** - - `gfx_*` API ஢ **Turbo-C ``** (BGI) MS-DOS. -ணࠬ ਢ訥 Turbo-C ७ ᪨ 1--1. - -** ** ( ᭮ Borland BGI): - -Setup/teardown: -- `initgraph()` / `closegraph()` ? ᥩ `gfx_init`/`gfx_done`, alias -- `getmaxx()` / `getmaxy()` ? GFX_WIDTH-1 / GFX_HEIGHT-1 -- `cleardevice()` ? alias to gfx_clear -- `getgraphmode()` / `setgraphmode()` ? get_videomode/set_videomode - -Color/palette: -- `setcolor(c)`, `getcolor()` ? current draw color -- `setbkcolor(c)`, `getbkcolor()` ? background color -- `setpalette(idx, c)` ? entry -- `getpalette(&info)` ? read all palette - -Primitives ( 㦥 ? BGI- aliases): -- `putpixel(x, y, c)` ? gfx_putpixel -- `getpixel(x, y)` ? 㦭 ॠ (RMW ⭮ ? IN) -- `moveto(x, y)`, `lineto(x, y)`, `linerel(dx, dy)` ? current point + line drawing -- `line(x1, y1, x2, y2)` ? gfx_line -- `rectangle(x1, y1, x2, y2)` ? gfx_rect ( 㣮 API: x1,y1,x2,y2 vs x,y,w,h!) -- `bar(x1, y1, x2, y2)` ? gfx_fill_rect -- `bar3d(x1, y1, x2, y2, depth, topflag)` ? : rect + 3d edges -- `circle(x, y, r)`, `arc(...)`, `ellipse(...)`, `pieslice(...)` ? primitives -- `fillpoly()`, `drawpoly()` ? -- `floodfill(x, y, border_color)` ? - -Text on graphics screen: -- `outtext(s)` / `outtextxy(x, y, s)` ? gfx_text (alias) -- `settextstyle(font, dir, size)` ? multiple bitmap fonts -- `gettextsettings(&info)` -- `textwidth(s)` / `textheight(s)` ? measure - -Image manipulation: -- `imagesize(x1, y1, x2, y2)` ? bytes needed for getimage -- `getimage(x1, y1, x2, y2, buf)` ? save rect to buffer -- `putimage(x, y, buf, op)` ? paste back with COPY_PUT/XOR_PUT/AND_PUT/OR_PUT/NOT_PUT - -Clipping/viewport: -- `setviewport(x1, y1, x2, y2, clip)` ? drawing clip rect -- `getviewsettings(&info)` -- `clearviewport()` -- `setactivepage(p)` / `setvisualpage(p)` ? ਧ (Sprinter 2 screen) - -Line style: -- `setlinestyle(style, pattern, thickness)` ? SOLID_LINE / DOTTED_LINE / etc. -- `getlinesettings(&info)` - -**Acceptance:** ७ ⨯筮 Turbo-C BGI ணࠬ (饩 ᯮ짮 -moveto/lineto/circle/bar/setcolor) ࠡ ⢥ ࠢ. - -**Notes:** -- BGI fonts (TRIPLEX/SANS_SERIF/GOTHIC) ? BIOS font, ⠫ 㦭 - ( bitmap data lib) -- imagesize/getimage/putimage ? ᠬ ॡ game/animation -- Active/visual page ( ਧ) ? Sprinter ন 2 graphics pages, - 㦥 API switching - -. ⠪ `examples/` Turbo C 2.x BGIDEMO reference 㦭. - -### IM2 Interrupt Handlers ? **MUST v2** - -User- ISR १ Z80 IM 2 mode. 㦭 : -- Timer ticks (50 Hz frame counter, ) -- Music playback (AY, COVOX) -- Real-time games (input + game logic + render interrupt-driven) -- Async keyboard / mouse handling - -**Status:** v2. research + design `docs/im2_isr_design.md`. - -**襭 ⥪:** ॠ ⤥ memory mode `--memory im2` -( ⮣ ⮡ 騥 crt0). Detail' design-doc. - -** research'** ( ⥪ `docs/im2_isr_design.md`): -- Vector 0xFF ? frame + keyboard + CBL. Disambiguation ⠬ 0x19 / 0xFE -- Mouse hardware-IRQ 室 ( ⥪饩 ) -- Vector table / ISR / stack W2 (0x8000..0xBFFF) -- DSS ᢮ IM 2 handler ? 㦭 chain' ( / SYSTIME ) - -### 稥 㯭 㭪 v2 - -- [ ] **FILE API rewrite ? buffered streams** ? ⥪ ॠ - `libc/stdio/file.c` provisional unbuffered shim ( fputc/fgetc - = read/write syscall). 㦭 業 buffered ᥬ⨪ - Solid-C: - - ```c - typedef struct { - uint flags; // +0..1 file status flags - int level; // +2..3 empty/fill level of buffer - char *curp; // +4..5 current active pointer - int fd; // +6..7 underlying low-level fd - char *buffer; // +8..9 data transfer buffer - char hold; // +10 ungetc byte if no buffer - short token; // +11..12 reserved - char dummy; // +13 reserved - } FILE; - ``` - - stdin/stdout/stderr ? fd-થ `0 / -1 / -2`. ⥫ - stdout/stderr ࠭ ७: ESTEX OPEN positive - small fd (1, 2, ?) 筮 䠩 ? ᫨ stdout=1, ॠ - fd=1 ⠫ 䨪஬. fd=0 stdin ᭮ - (ESTEX 0 頥). fd । syscall' ? - ᯥਧ 䫠 `_F_CONIN/_F_CONOUT`. - - ਭ-⮪ (stdaux/stdprn) ॠ㥬 ? Sprinter ਭ୮ - API . - - ୠ⨢ ? ॠ third_party/solid-c (sources - `SRC/CLIB/`); ⠬ ⮢ buffered FILE + fopen/fread/fwrite/ - fseek/setvbuf .. ஢ 訬 open/read/write/lseek. - - rewrite deferred issues stdio-review: - - `fwrite` short-write ⠢ `_F_ERROR` - - `fgets(buf, 1, fp)` ? ⠭ "empty string", 㫨 NULL - - `mode_to_flags` ? break-out '+' (cosmetic) - -- [ ] **Audio API** ? AY-3-8910 + COVOX १ 뢠 (ॡ IM2) -- [ ] **ISA-8 slot support** ? ZX-Bus (sound, network, etc.) ? ॡ IM2 + ⥭ port - -## 稥 (v1 backlog, 騥) - -- [x] **#9: text I/O split (Turbo-C style)** ? stdio (puts/printf/putchar) ⥯ fast no-attr १ PCHARS/PUTCHAR. conio (cputs/cprintf/putch) ਬ attr १ textcolor/textbackground/textattr. KEEP_EXIST_ATTR ? conio fallback fast path. Verified hello.exe. . `memory/text_output_api_split.md`. 2026-05-31. -- [x] **Mouse API ** (१ driver, RST 30h) ? 14 㭪権 (init/show/hide/refresh/read/goto/bounds/text_cursor/load_cursor/get_cursor/get/set_sensitivity/video_mode_changed). . `memory/mouse_api.md`. Verified MAME 2026-05-31. Sensitivity = divider ( = ॥). -- [ ] Interrupt handlers ? IM 2 vector table HOME user ISR' -- [ ] প `restore SP on EXIT` ( z88dk +pps) ? ஢ 㦭 -- [ ] CI: ⮬᪨ ᪠ MAME `-aviwrite` screenshot-ࠢ, ⮡ ਬ஢ 室 祫 - -## ⮬ - -- প `` ( SDCC stdlib ? 㦭 ஢ crt0 ᮢ⨬) -- `` १ ESTEX SYSTIME (`$21`) CMOS BIOS-㭪樨 -- ZX Spectrum-ᮢ⨬ ० ⤥ target ( ஢ ᯥ㬮᪨ ணࠬ) -- প ZX-Bus (sound, network, etc.) ? 㦭 ࠩ -- Profile-guided optimization tools (hot/cold detection) 㯭 ணࠬ - -## Linker duplicate-symbol warnings (, 䨫஢) - - ᮧ⥫쭮 overrides' SDCC z80.lib 㭪樨 ᮡ⢥ ᨥ `sprinter.lib`, `sdldz80` `?ASlink-Warning-Definition of public symbol '...' found more than once`. ࢮ । (), ⮬ ४⭮ ? warning ⮫쪮 noise. - -騥 overrides: -- `_puts` ? १ PCHARS+\r\n vs SDCC posix puts -- `___sdcc_heap` ? heap W2 vs SDCC's ⠭ -- `_asctime`, `_localtime` ( 㣨 time) ? `posix_time.c` १ ESTEX SYSTIME vs SDCC's `time.rel` `_RtcRead` - -**饥 襭:** `bin/sprinter-cc` 䨫஢뢠 warning- (warning + 2 follow-up `Library:` ப) 뢮 `sdcc`. १ `-v` (verbose) 뢠. १ awk-pipe. - -** 襭:** -- ३ explicit `--nostdlib` + 筮 ᯨ᮪ 㦭 㫥 z80.lib (string, math, stdlib override') ? warning', ᪨஢ -- : २ `_puts` ? `_puts_sprinter` + alias १ linker flag ( 㢥७ SDCC ন) -- : ⠢ ᥩ (ࠡ祥 benign) ? ਮ - -## TODO: ஢ ॠ쭮 - -- [ ] **Port_Y banking trick** (`docs/part2/SprinterGraphics programming.txt`): - ⢥ত ᫥ `OUT (0x89), Y` 0xC000+0x400*N W3 - ப Y..Y+15 ( ணࠬ஢ ? 16 ப). - Empirical 2026-06-01 MAME 0.283 trick ** ࠡ⠥** ? ᥫ - ᠬ 0xC000+row_width 室 . ᪨ - `docs/samples/plasma2.asm` ⮦ ᯮ banking, ⠭ - Port_Y per row. - : - 1. ॠ쭮 Sprinter - 2. dual-write (`_gfx_putpixel_raw` + ன write `0xD000+x`) - 3. ᫨ ? MAME, issue - ९ - 4. ᫨ ⮦ ? 㬥 , 㤠 㯮 - ⠢ ⥪ ॠ (Port_Y per pixel) - 5. ᫨ banking ࠡ⠥ ? ஢ Port_Y - `_gfx_putpixel_raw` (sentinel out-of-range, . memory/gfx_port_y_banking.md) - - 易 먣 Bresenham (60-pixel ) ? 8? - OUT (0x89) 権, `gfx_fill_rect 320x256` ? 16? . - release v1. - -## GFX: ७ `docs/part2/accelerator_doc.txt` - -᫥ ⥭ ⠫쭮 accelerator doc  ࠢ. - ᯮ ⮫쪮 ਧ⠫/⨪ Fill mode. - -### Quick wins ⥪ primitives - -- [ ] ** SMC `LD A, (var)` block-size**. 㬥  - ࠧ蠥 `LD A, (HL)`, `LD A, (BC)`, `LD A, (DE)` ( `LD A, r`). - 㡥 SMC complexity `gfx_lines.c:hfill_chunk/vfill_chunk` - `gfx_16.c:g16_hfill_chunk`. 饭 ⮫쪮 register-to-register. -- [ ] **஢ block-size**. 㬥 뢠 accel - block size bursts (. `Horizontal_Line_Fill`: ⠭ - size + `LD B,B` ⪫祭, ⮬ Fill mode ᯮ - ࠭ size). `gfx_fill_rect` 100 묨 - ப ? ⠭ size 1 ࠧ, 100. - -### Bank-prefix modes (port 0xE2 bits) - -㬥 뢠 ਠ ࠭ ⠭⭮ 0x50: - -| Bank byte | Effect | -|---|---| -| 0x50 | Normal write ? shadow + | -| 0x54 | "no copy in main shadow RAM" | -| 0x58 | **"FF is transparent"** ? 0xFF write ⠢ background | -| 0x5C | both | - -Bank 0x58 祬 mouse cursor 0xFF-஧筮. - **sprite-blending १ accel block copy**: - -- [ ] **`gfx_set_bank_transparent(on)`** 䫠 `gfx_set_bank` - 롮 0x50/0x58 ᮢ sprite' -- [ ] ᯮ짮 `gfx_blit()` ⮡ 䠪 - transparent sprites १ accel- - -### Block copy mode (sprite blit') - -`LD L,L` (horizontal) `LD A,A` (vertical) ? ० ஢ १ -256- accel memory. blit'. - -- [ ] **`gfx_blit(src_data, x, y, w, h)`** ? ஢ sprite' - (ந ࠧ, १ accel) -- [ ] **`gfx_blit_transparent(src, x, y, w, h)`** ? ᯮ짮 bank 0x58 - -. `Draw_Restangle_Data` accelerator_doc.txt ७. - -### AND / OR / XOR operations १ accel - -㬥 뢠 accel ন ᪨ 樨 -. ਬ: -- XOR ? (뤥 selection UI) -- OR / AND ? masking, alpha-style blending -- . ਬ accelerator_doc.txt: "256 bytes block coding via XOR" - -- [ ] **`gfx_xor_rect`** / **`gfx_or_rect`** / **`gfx_and_rect`** ? - ਬ⨢ ᪨ 権 אַ㣮쭨 -- [ ] **`gfx_invert_rect(x, y, w, h)`** ? alias xor 0xFF - -### Bitmap fonts ࠧ ࠧ஢ - - `gfx_text` / `gfx_putchar` प ࠡ 8?8 ⮬ -(BIOS WIN_GET_ZG 頥 256?8 ). UI / 쭨 -㦭: -- [ ] **`gfx_set_font_size(w, h)`** ? ४ ਭ/ glyph' -- [ ] **`gfx_set_font_data(ptr, w, h, advance)`** ? 㪠⥫ - 짮⥫᪨ + ࠧ -- [ ] প **proportional** (advance != w) ⮢ ? - array advance[256] ਭ glyph' -- [ ] **Big-font ०**: 8?16, 16?16, 16?8 ( 쭨) -- [ ] ⤥ API `gfx_text_ex(x, y, str, font_id)` - font_id 롨ࠥ 㦥 ⮢ -- [ ] **Anti-alias 2-bit ** ( 䮭 / ࠭ / 2- alpha?) - ? far future, smooth UI - -## ⠯ ⨬権 ( ᥩ) - -- **`gfx_line` १ accel ** ? Bresenham |dy| << |dx| ( ) 뤠 runs Y ( X): ᥫ, ᥫ, ᥫ, 蠣 Y, ᥫ, ᥫ... ⠪ run ? ⮢ 㬥 `gfx_hline` ( `vline`). - ᫥: runs 㭪 ; run length, ஬ 룮 accel hline 祬 N?putpixel (overhead accel ~20?s, putpixel ~5?s ? accel 룮 run ? 4-5 px); (dx ? dy) ⠢ Bresenham, ? run-length-based fill. - `gfx_line` orthogonal cases 㦥 १ accel ? ⨬஢ ⮫쪮 . - -- **`gfx_fill_rect` W3-swap ** ? ᥩ ७ `gfx_hline`/`gfx_vline` ᢮ DI/save-W3/restore-W3/EI. ᤥ internal `_fill_rect_inner` ন W3 DI 横; ~20?s ? ⢮ ப/⮫殢 . ਬ ᥬ ਬ⨢. diff --git a/examples/mdview2/testfiles/HUGE.MD b/examples/mdview2/testfiles/HUGE.MD deleted file mode 100644 index cdfc5b7..0000000 --- a/examples/mdview2/testfiles/HUGE.MD +++ /dev/null @@ -1,6556 +0,0 @@ -# MDView ? ᬮ騪 Markdown Sprinter - -**MDView** ? ணࠬ ᬮ 㬥⮢ ଠ *Markdown* -Sprinter ( Z80). 㬥 ࠭ ⤥쭮 W3 -᭮ RAM ணࠬ. - -## - -- 㬥 **128 ** (8 ࠭ EMM 16 ) -- **16 384** ࠭ ப -- ⮬᪨ ७ ᫮ ਭ ࠭ (80 ⮫殢) -- ਧ⠫ ᤢ ப ப ( , ⠡) -- -: 䠩, ப, 業 ப⪨ -- ப ﭨ ६ 㧪 樨 -- প ?? ᪫ ப - -## - -``` -mdview [_䠩.md] -``` - -᫨ 䠩 , 㦠 `README.MD`. - -## ࠢ - -``` - ⢨ - -Up Down ப⪠ ப / -PgUp PgDn ப⪠ ࠭ (30 ப) -Home 砫 㬥 -End 㬥 -Left Right ਧ⠫ ᤢ (⮫쪮 nowrap-ப) -F1 ࠢ -F8 ஢: CP866 ? CP1251 ? KOI8-R ? UTF-8 ? ... -F10 / Esc 室 ணࠬ -``` - -஢ । ⮬᪨ ⨨ (BOM + ⨪ - 4 ); `F8` ४砥 , ᫨ ⥪ 訡. -8- ஢ (CP866/CP1251/KOI8-R) ४ (६ -ᮢ). ன / (ਬ UTF-8) ந ? -ࢮ ४祭 (⪠ 㧠 ᯨ஬), . - -## ⠪ Markdown - -### - -ন ஢ H1?H4. ஢ H5 H6 ⮡ࠦ H4. - - # ࢮ ஢ - ## ண ஢ - ### 쥣 ஢ - #### ⮣ ஢ - -### ⮢ ଠ஢ - -** ⥪** 뤥 묨 񧤮窠: `**⥪**` - -*ᨢ* 뤥 묨 񧤮窠 `*⥪*` ન `_⥪_` - -`஥ ` 砥 묨 窠 - -~~ભ ⥪~~ ? ⨫: `~~⥪~~` - -### 㬥஢ ᯨ᮪ - -થ `-`, `*` `+`: - -- 㭪 ᯨ᪠ -- ன 㭪 ᯨ᪠ -- ⨩ 㭪 筮 ⥪⮬, 室 - 㤥 ७ ᫥ ப ࠭ 㯠 - -### 㬥஢ ᯨ᮪ - -1. -2. ன -3. ⨩ - -### - -> 稭 ᨬ `>`. ᪮쪮 ᫥⥫ -> ப ᪫ ⮬᪨ -> ७ᮬ ᫮. - -### (verbatim) - - 砥 ன 窨. -⥪ ⮡ࠦ ? ? ࠧ Markdown: - -``` -#include -#include - -int main(void) { - puts("Hello, Sprinter!"); - return 0; -} -``` - -### ਧ⠫쭠 - - ᨬ `---`, `***` `___` ⤥쭮 ப: - ---- - -## 孨᪨ ࠪ⨪ - -- **ଠ:** Sprinter, Z80 @ 21 -- **஢:** CP866 / CP1251 / KOI8-R / UTF-8 (⮮।, `F8`) -- **ᨬ ࠧ 䠩:** 128 -- **ᨬ쭮 ᫮ ப :** 16 384 -- **ᨬ쭠 ப ७-:** 255 祥 (. ࠭祭 ) -- ** :** small - - ணࠬ, c⥪, , ? a W1-W2 (32 , 0x4000?0xBFFF). - - 䠩 ? ࠭ EMM, ⮡ࠦ W3 (0xC000?0xFFFF) - -## ࠭祭 - -### ப MD-० ? 255 祥 - -- ࠭ ப ** 255 (ᨬ, ਡ)** -? ⠭ `MAX_CACHE_LINE_LEN`. ⨯: `g_ncells` -`cache_rec_t.len` ? `uint8_t`. ᠥ ப, ⭥ ᥣ -**ਧ⠫쭮 ஫㥬** ப ( ப ⠡, 䫠 -`IF_HSCROLL`), MD-० ࠢ. - -** ந室 ப:** 樨 `gc_put()` -뢠 祩 ᫥ 255- (`if (g_ncells < MAX_CACHE_LINE_LEN)`). - ⮫쪮 255 祥, ⮪ **** ? -஫ ** થ १** 255- 樨 (થ `>` 砥 -? । ?). ९ ? `gc_put` ஢ ࠭. - -: ?255 祥? ? **७७ ਭ**, 室. - ࠧ稢 ஡ ( `TAB_STOP`), 祩 ⠡ -஡ ਭ + ࠬ `` ? ⮬ ⨣ ࠭, 祬 -255 ?? ᨬ. - -> **RAW-० (`F2`) ⮣ ** ? אַ 䠩 -> ⮢, ப 楫 (१ wrap `F3` ਧ. ஫). - -** ** (業, ॠ) ? `uint16_t`: -- `cache_rec_t.len` `uint8_t?uint16_t` ? **஢ 8 ** - (len ꥤ pad-), `idx<<3` . ᯫ⭮. -- `g_ncells` / `g_ncells_at_space` ? `uint16_t` ? 業 **/᪮**: - 16-⭠ 䬥⨪ Z80 祬 `gc_put` (맮 祩) - scan-横. ਥ: **+0.2?0.4 ** + 樨. -- `g_cells[MAX_CACHE_LINE_LEN*2]` near-RAM (W2) ? 業 - ****: `2 ? ` . 510 ; 512 ? +0.5 , 1024 ? +1.5 . - EMM- ⥭ (1 ) ப ﭥ ? 㧪 near. -- `viewport_x` ( `docset_t`) + ⮫ `max_vx` (ᥩ 248) ? `uint16_t`, - ࠭ >255 , ஫ . `widths[]`/`ccx` ⠡/. - - ஬ ? 512?1023: 墠 ᥬ ⨭/⠡栬, - 業 ~+0.3 +0.5?1.5 near-RAM. ** 㬥 `uint16_t`** ? - ⠢ થ १ 255- 樨, ⮡ 뫠 . - -## TODO - -1. **祭 ࠧ 㬥⮢.** 128 : 筮 - ࠧ ࠡ 訬 - ࠭ , ஢ - ࠡ 8- ࠭栬 16. - -: ଠ஢ ⠡ ࠬ; প ஢ -CP866 / CP1251 / KOI8-R / UTF-8 ⮮। ४祭 `F8` -(ன ந , ࢮ ४祭). - -4. **᪮७ ७ਭ.** ப ࠭. ⨬ 横 뢮 - ᨬ १ BIOS WRCHAR ( 뢮, DMA). - ---- - -*MDView v0.2 (c) 2026 ஢ ..* - - ---- - -# Sprinter C Compiler ? v1.0 - -C toolchain for **Sprinter** ? the Z80-based home computer by Peters Plus, running -ESTEX DSS. Host: macOS / Linux. Target: `.EXE` files in SprintEXE format. - -Built on top of **SDCC 4.5** (vendored in `third_party/sdcc/`). This repository adds -everything Sprinter-specific: crt0, linker integration, libc wrappers over ESTEX, -banked-call trampolines, graphics & accelerator API, mouse driver wrappers, and the -`mkexe` utility for producing SprintEXE images. - -## What you get - -* **`bin/sprinter-cc`** ? one-line driver: `sprinter-cc -o foo.exe foo.c` -* **Memory modes**: `tiny`, `small`, `big`, `huge`, `manual` ? see below. -* **stdio + conio**: printf, puts, putchar, getchar, fopen/fread/..., cprintf, cputs, putch, textcolor/textbackground/textattr, gotoxy, kbhit/getch. -* **Graphics**: 320?256?256 and 640?256?16 modes, accelerator-backed primitives (hline / vline / rect / fill_rect / line via Bresenham, plus clear), bitmap-font text in both modes via BIOS character generator. -* **File I/O**: POSIX (`open`/`read`/`write`/`close`/`lseek`/`unlink`/`creat`), FILE\* streams (`fopen`/`fgets`/`fwrite`/...), directory listing (`ffirst`/`fnext`), `chdir`/`getcwd`/`mkdir`/`rmdir`, `stat`/`fstat`. -* **Memory**: 32 KB heap (W2-resident), banking-aware page allocator (`mem_alloc_pages`/`bank_read`/`bank_write`), explicit memory modes for sub-16 KB programs. -* **Mouse**: full Sprinter driver wrapper (14 functions including custom cursor bitmaps). -* **Environment**: `getenv`/`putenv`/`sysenv` over ESTEX `$46`. -* **Time**: `getdatetime`/`setdatetime` + POSIX `time`/`localtime`/`mktime`/`asctime`/`ctime`. -* **Misc**: `errno`/`strerror`/`perror`, `atexit`, `setjmp`/`longjmp`, `sleep`, full argv parsing in crt0. - -## Quick start - -```sh -git clone sprinter-c -cd sprinter-c -make sdcc # one-time: fetch SDCC 4.5 binary (~25 MB) -make all # build mkexe + libsprinter.lib + 27 examples -make floppy # pack everything into mame/v306/IMG/mc.img -cd mame/v306 && ./run_mame.sh # boot Sprinter in MAME -``` - -Compile a single program: - -```sh -cat > hello.c < -int main(void) { puts("Hello, Sprinter!"); return 0; } -EOF - -bin/sprinter-cc -o hello.exe hello.c -``` - -That's it ? `hello.exe` is now a valid SprintEXE you can `RUN HELLO` from the ESTEX shell. - -## Memory modes - -Sprinter's address space is four 16 KB windows (W0 / W1 / W2 / W3). DSS allocates -pages by program size ? small programs get only one page. Pick a memory mode based -on what your program needs: - -| Mode | Code lives in | Banking | Use when | Note | -|---|---|---|---|---| -| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | | -| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | | -| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | | -| `huge` | W1-W2 + W3 banking | yes (W3) | small + extra code modules | | -| `manual` | user-specified | optional | special layouts | Not implemented | - -```sh -sprinter-cc --memory small -o big.exe bigprog.c -sprinter-cc --memory huge -o app.exe main.c --bank 1=engine.c --bank 2=ai.c -``` - -Banked functions are declared with `__banked`: - -```c -void engine_tick(int dt) __banked; // lives in BANK1, automatically swapped -``` - -## Examples (27 total) - -| Example | What it demonstrates | -|---|---| -| `hello` | Hello world with stdio + conio Turbo-C-style colors | -| `argv` | argv parsing in crt0 | -| `cat` | File I/O ? read & print TEST.TXT | -| `seek` | 32-bit lseek over a 100 KB file | -| `ls` | Directory listing via ffirst/fnext | -| `filetest` | FILE\* streams (fopen/fread/...) | -| `stattest` | `stat`/`fstat` on files and directories | -| `errno` | errno / strerror / perror | -| `mem_test` | Page allocator + bank\_read/bank\_write | -| `malloc` | Heap stress test (200+ allocations) | -| `banked` | Banked code in W3 (huge mode) | -| `bankedbg` | Banked code in W1 (big mode) | -| `banklocl` | Bank-local static data and BSS | -| `mouse` | Mouse driver in text mode | -| `gfx_mous` | Mouse with custom bitmap cursor in graphics mode | -| `gfx_demo` | 320?256?256 lines / rects / accelerator | -| `gfx_d16` | 640?256?16 same primitives | -| `gfx_text` | Bitmap-font text on graphics screen | -| `timedir` | Date/time + directory listing | -| `ptime` | POSIX time API | -| `openenv` | open() flags + environment vars | -| `conio` | conio API smoke test | -| `attrprob` | Probe Sprinter text attribute byte layout | -| `strtest` | string.h test (from SDCC's z80.lib) | -| `stdlib` | stdlib.h test (qsort / rand / strtol / etc.) | -| `assrtest` | assert() | -| `rt_test` | Runtime helpers (sleep, setjmp, atexit) | - -## Headers - -Standard: -* `` ? puts / printf / FILE\* + Sprinter-specific dec/hex helpers -* `` ? atoi / atof / malloc / qsort / ... (from SDCC z80.lib) -* `` / `` / `` ? from SDCC z80.lib -* `` ? read / write / close / lseek / unlink -* `` ? open / creat + O\_RDONLY / O\_CREAT / ... -* `` ? errno + error names + strerror -* `` ? stat / fstat -* `` / `` ? from SDCC - -Sprinter-specific: -* `` ? putch / cputs / cprintf, textcolor / textbackground / textattr, kbhit / getch, clrscr, gotoxy, wherex/y -* `` ? gfx_init/done, palette, putpixel, hline/vline/rect/fill_rect/line, text ? for both 320 and 640 modes (gfx_\*16 variants) -* `` ? full 14-function driver wrapper + mouse_cursor_t with bitmap support -* `` ? chdir / getcwd / mkdir / rmdir / ffirst / fnext / ffblk -* `` ? getdatetime / setdatetime + POSIX time / localtime / etc. -* `` ? raw ports, ESTEX/BIOS function numbers, env API -* `` ? exit / \_exit / atexit -* `` ? mem\_alloc\_pages / mem\_free\_block / bank\_read / bank\_write -* `` ? Solid-C compatibility layer (aliases + BOOL/WORD/uint types) - -## Toolchain commands - -```sh -make all # build mkexe + lib + every example -make floppy # repack mame/v306/IMG/mc.img with all .exe files -make check # 17 mkexe unit-tests -make clean # remove all build artefacts -make sdcc # one-time: fetch SDCC 4.5 binary -``` - -### sprinter-cc options - -``` -sprinter-cc -o foo.exe foo.c [more.c ...] [options] - - --memory MODE tiny | small | big | huge | manual (default: tiny) - --memory-manual SPEC explicit placement (CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3) - --stack-size N bytes reserved for the stack (default ~1278) - --crt0=TYPE default | minimal | banked | small - --bank N=FILE.c compile FILE.c into bank N (repeatable, max 15) - --debug enable runtime diagnostics (defines DEBUG_RT) - -I PATH extra include path - -L 0xADDR / -E / -S override load / entry / stack addresses - -Wl FLAG pass FLAG to sdldz80 - --mkexe FLAG pass FLAG to mkexe (e.g. --mkexe -p --mkexe 0 for bank padding) - -v verbose -``` - -## Status - -What works in v1.0: -* Compile / link / pack to SprintEXE ? verified on all 27 examples -* Four memory modes (tiny / small / big / huge) -* Graphics (both modes) with accelerator -* Mouse (text + graphics cursor) -* File I/O, directories, environment, time -* All headers listed above - -Deferred to v2.0 (see `docs/TODO.md`): -* **Turbo-C-style BGI graphics API** ? `initgraph` / `setcolor` / `circle` / - `getimage` / `putimage` / etc. on top of our `gfx_*` primitives -* Remaining Solid-C compatibility gaps (Phase 2/3) ? see `docs/solid_c_compatibility.md` -* Manual memory mode -* Rewrite FILE\* stream API (current implementation is very primitive and doesn't use buffers) - -Deferred to v3.0: -* **IM2 interrupt handlers** ? research complete (`docs/im2_isr_design.md`), - implementation scheduled for v3 -* **Audio API** (AY-3-8910 + COVOX) ? requires IM2 -* **ISA-8 slot drivers** ? requires IM2 (???) - -## Documentation - -* `docs/TODO.md` ? roadmap and open work items -* `docs/solid_c_compatibility.md` ? gap analysis vs Solid-C 2004 -* `docs/im2_isr_design.md` ? interrupt handler design (v2) -* `docs/converted/` ? source documentation (ESTEX, BIOS, architecture) - converted to plain text for `grep` -* `docs/reference/`, `docs/samples/`, `docs/memory management/` ? original - Russian docs and code samples from Peters Plus - -## Repository layout - -``` -bin/sprinter-cc one-line compiler driver (bash) -toolchain/mkexe/ host-side tool: .ihx -> .exe SprintEXE -toolchain/check_banks.py post-link bank size enforcer -runtime/ crt0 variants (default, minimal, small, banked) - bank trampolines, heap, heap_top -libc/include/ headers -libc/io|stdio|mem|gfx/ C and asm sources for libsprinter.lib -lib/ Makefile that archives libsprinter.lib via sdar -examples/ 27 example programs -mame/v306/ MAME binary + Sprinter ROM/HDD images + floppy script -third_party/sdcc/ vendored SDCC 4.5 (fetched via `make sdcc`) -third_party/solid-c/ reference: original Sprinter native C (for compat target) -docs/ documentation -``` - -## License - -This repository contains: -* Original code in `bin/`, `toolchain/`, `runtime/`, `libc/`, `lib/`, `examples/` ? - MIT-licensed. -* `third_party/sdcc/` ? SDCC 4.5 under GPLv2 with linking exception - (see `third_party/sdcc/COPYING.txt`) -* `third_party/solid-c/` ? original Sprinter Solid C, used only as a reference - -## Credits - -* **Sprinter / Peters Plus** ? , ਩ ਭ and the original team -* **SDCC** ? for the underlying Z80 compiler -* **MAME** ? for the Sprinter Sp2000 emulation - ---- - -For questions / patches: see CONTRIBUTING.md (TBD) or open an issue. - ---- - -# TODO / Roadmap - - 浪 뢠 ਮ.  ॠ ணࠬ ? ਮ ᬥ. - -## ⠯ 5 ? malloc / free + banking-aware page allocator ? - -- [x] SDCC's `malloc`/`free` + `runtime/heap.s` ( library heap.rel, 14000- heap 2) -- [x] `libc/mem/mem_alloc.c` ? page allocator: `mem_alloc_pages`/`mem_free_block`/`mem_get_page`/`mem_info` १ ESTEX `$3C/$3D/$3E` + BIOS `$C4` -- [x] `libc/mem/bank_io.c` ? HOME-१ `bank_read`/`bank_write`/`bank_load_byte`/`bank_store_byte` ᢮ W3 -- [x] `examples/malloc_test/` ? ஢ઠ SDCC's malloc (~210 64- allocations १ heap) -- [x] `examples/mem_test/` ? ஢ઠ page allocator: 3 ࠭, ࠧ १ bank_write, 䨪 १ bank_read - -## ⠯ 6 ? argv parsing + sprinter-cc wrapper ? - -- [x] crt0 ESTEX command-line IX-prefix (inline asm `runtime/crt0.s`) -- [x] Strip leading CP/M-style space (DSS quirk) -- [x] । `argc`/`argv` main() १ HL/DE (SDCC __sdcccall(1) ABI) -- [x] argv[0] = basename .EXE १ ESTEX APPINFO ($47 subfn 2) -- [x] `runtime/crt0_minimal.s` ? opt-out 祭 쪨 ணࠬ -- [x] `runtime/crt0_banked.s` ? ⥯ ⮦ argv (parse_argv + get_progname ᪮஢ crt0.s; 㤥 factored argv.s libsprinter.lib) -- [x] Bash-⪠ `bin/sprinter-cc`: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権: `--memory`, `--memory-manual`, `--stack-size`, `--crt0=`, `--bank N=FILE.c`, `--debug`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` - -## ⠯ 8 ? 䨪 (320?256?256 + 640?256?16 + accel + bitmap font) ? - -- [x] **8a** Graphics core: `gfx_init`/`gfx_done`/`gfx_clear`/`gfx_putpixel`/`gfx_pal_load`/`gfx_pal_set` (libc/gfx/gfx_core.c). १ BIOS PIC_SET_PAL ($A4). Verified 320?256?256. -- [x] **8b** /אַ㣮쭨/fill १ accelerator (libc/gfx/gfx_lines.c): `gfx_hline`/`gfx_vline` १ accel Fill (LD C,C / LD E,E + SMC block-size), `gfx_rect`/`gfx_fill_rect` heuristic 롮஬ ਥ樨 (h/v bursts count), `gfx_line` Bresenham . `gfx_clear` ⮦ ९ᠭ column-major accel (~4? ॥). -- [x] **8c** 640?256?16 mode (libc/gfx/gfx_16.c): `gfx_*16` API, HIGH nibble = LEFT pixel (㬥 misleading), per-row RMW vline ( = 2 ਧ⠫ ᥫ). -- [x] **8d** Bitmap font + gfx_text (libc/gfx/gfx_text.c): १ BIOS WIN_GET_ZG ($B8), interleaved layout `font[row*256+char]`, `gfx_text`/`gfx_putchar` 320 mode, `gfx_text16`/`gfx_putchar16` 640 mode pair-table lookup. - -. memory/sprinter_graphics.md, sprinter_accelerator.md, sprinter_graphics_16.md, sprinter_font_format.md. - - ( ): -- [ ] -quad 640: per-cell (mode 0x82 ࠧ蠥 1 4 per 16?8 cell) ? १ אַ area-ᠭ ࠭ 0x0300..0x039F - -## Auto-banking (. `memory/banking_roadmap.md` ⠫) - -Phase 1 ? file-level bin-packing ? ॠ뢠 ஥ ~30 KB . - -- [ ] `toolchain/auto_bank.py`: - - ࠧ `.rel`-䠩 ( .map ᫥ dry-run link') - - First-fit-decreasing bin-packing - - `#pragma codeseg BANKn` manual override - - ५뢠 묨 `-Wl-b_BANKn=...` ࠬࠬ - - ⠥ । - -Phase 2-5: incremental rebalance, declarative `banks.toml`, function-level, call-graph-aware. 쪮 ᫨/ . - -## Bank-local static data (mutable data ⮬ ) ? ? - -- [x] ਬ `examples/bank_local_data/` ? 㭪 BANK1 ᢮ writable BSS array + const table + malloc- -- [x] `mkexe -p 0` 㫥 padding (BSS-storage 㧪) -- [x] ᪨ 楯: `--codeseg BANK1 --constseg BANK1 --dataseg BANK1` bank1.c + `-Wl-b_BANK1=0x1C000` . **`--dataseg BANK1` ** ? ࠭ ⭮ - trampoline bug ᪨஢ १. -- [x] ** 䨪 trampoline'a runtime/bank.s** ? `pop af; out (n), a` ਫ A ? banked-㭪樨 騥 uint8_t 頫 . `pop bc; out (c), b` ࠭ A. -- [x] **malloc banked-㭪樨 ࠡ⠥ ஧筮** ? heap W2 (HOME), W2 ᢠ trampoline', pointer  ⥪. . memory/bank_local_data_pattern.md. -- [x] 㬥 memory: `memory/bank_local_data_pattern.md` ( 楯 + malloc + nuances), `memory/sdcc_banking.md` (trampoline fix) -- [ ] 樮쭮 ? `check_banks.py` ⮡ 뢠 ࠧ size = code + const + bss per bank (cosmetic) - -祬: 㫥 訬 private state (level loader, audio engine, scene data). W2 heap , ⨪ . - -## ᪠ solid-c (⨢ Sprinter C ? `third_party/solid-c/`) - -᫥ solid-c'᪮ libc (. `memory/solid_c_findings.md`)  ⮢ ᫥ 㭪権. ਮ **᮪** : - -### High-priority gaps ( ஢, 짠) -- [x] **`errno` + `strerror`/`perror`** ? ⠡窠 32 訡 (libc/io/errno.c) -- [x] **७ `open()`** O_CREAT/O_TRUNC/O_APPEND/O_EXCL state machine -- [x] **`atexit`** ? 8-callback LIFO + `exit()` + `_exit()` (libc/io/atexit.c) -- [x] **`setjmp`/`longjmp`** ? 6- jmp_buf={sp,ix,pc} (libc/io/setjmp.c) -- [x] **`sleep(seconds)`** ? 50Hz halt-loop (libc/io/sleep.c) -- [x] **ESTEX ENV API** ($46, getenv/putenv) ? libc/io/env.c. 竨 doc-bug: ॠ쭮 A=0 NOT FOUND - -### Medium-priority (㦭 shell-like ⨫) -- [ ] **Mouse driver** ? `rst $30h`, 17 㭪権. **砫 ࠡ⠥ MAME**. -- [x] **`ffirst`/`fnext` + ffblk_t struct** directory listing ? ॠ, demo: ls.exe -- [x] **`getdatetime`/`setdatetime`** १ ESTEX $21/$22 ? libc/io/time.c, demo: time_dir_test -- [x] **`chdir`/`getcwd`/`mkdir`/`rmdir`** ? wrappers ESTEX $1B-$1E ? libc/io/fsdir.c -- [x] **conio: `kbhit`/`getch`/`getche`/`cputs`/`clrscr`/`gotoxy`** ? ॠ -- [x] **conio extras**: `wherex`/`wherey` ($53), `wrchar`/`rdchar` ($58/$57), `textmode_get/set` ($50/$51), `clrscr_attr` ($56) + COLOR macros - -### Low-priority ? ? FILE* stack - -- [x] ** unbuffered FILE\*** ? `libc/stdio/file.c` + `libc/include/stdio.h`. fopen/fclose/fputs/fgets/fread/fwrite/fseek/ftell/rewind/feof/ferror/clearerr/fflush + stdin/stdout/stderr pseudo-streams. . `memory/file_star_design.md` `examples/filetest`. -- [ ] fprintf / fscanf ? 㦭 printf-१-callback machinery. 짮⥫ `sprintf(buf, ...) + fputs(buf, fp)`. -- [ ] 樮 buffered mode (setvbuf, line/block buffering) ? ᫨ - . - -### POSIX time API ? ? -- [x] `libc/io/posix_time.c` ? time/localtime/gmtime/mktime/asctime/ctime getdatetime. SDCC's time.rel ( override _RtcRead). . `examples/ptime`. - -### sys/stat ? ? -- [x] `libc/io/stat.c` ? POSIX stat/fstat. ਤ open+fstat 䠩, ffirst+iter ( "."/".."). . `examples/stattest` `memory/estex_ffirst_dotdot.md`. - -### assert ? ? (ᯮ㥬 SDCC's __assert १ fallback include path) - -## libc/stdlib ? ? 㦭 (. memory/sdcc_stdlib_works.md) - -஢७ १ `examples/stdlib_test/`: SDCC z80.lib ᮤন ࠡ騥 ॠ樨: -- `atoi/atol/atof, strtol/strtoul, rand/srand, qsort/bsearch, abs/labs, div/ldiv` -- `` (memchr/cmp/set/cpy, strcat/cmp/cpy/len/chr/spn/etc.) -- `` (toupper/tolower) -- `` (sinf/cosf/sqrtf/etc.) - - ⮬᪨ ﭥ z80.lib 㦭. ** ९뢠**. - - Sprinter-specific 易⥫ 㫨 : atexit, env, errno, setjmp, putchar/puts/getchar, conio, fsdir, time, mouse, open/read/lseek/close. - -## Build-system: libsprinter.lib + sprinter-cc ? ? - -- [x] `lib/Makefile` ? ᮡࠥ libc/*.c `.rel`, 娢 १ sdar `lib/sprinter.lib` -- [x] 砥 runtime/bank.s runtime/heap.s (auto-pulled __banked/malloc) -- [x] `bin/sprinter-cc` ? bash-wrapper: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権 `--crt0=default|minimal|banked`, `--bank N=FILE.c`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` -- [x] `examples/hello_sccc/` ? : `hello.c` ᮡࠥ shell-맮, ࠧ ᮢ Makefile (925 ) -- [x] Split `putchar.c` ? `putchar.c` + `puts.c` per-function granularity (puts override SDCC's z80.lib version) -- [x] `make all` (ᨬ `lib` । `examples`) - - 襭 (樮쭮): -- [ ] ஢ ⠫ examples sprinter-cc Makefile (ᬥ⨪) -- [ ] 쭥 libc/*.c per-function ( ⥪ granularity 㦥 㦭 ࠧ ? .rel 楫, 設⢠ 䠩 㭪) - -## ⠯ 9 ? memory modes sprinter-cc - -DSS 뤥 ࠭ ࠧ ਫ: < 16 KB ? ࠭, ⠫ 砥 ?࠭ #FF? (read=0xFF, write ). - ⮣ CODE--W1 + DATA--W2 쪮 ணࠬ . . [memory/sprinter_memory_modes.md](../../.claude/projects/-Volumes-SAM8-Projects-DIY-Z80-Sprinter-C-Compiler/memory/sprinter_memory_modes.md). - -- [x] **`tiny`**: (CODE+DATA+⥪) W2. Default. Verified hello/argv/conio/malloc/file/etc. -- [x] **`--memory MODE` 䫠 sprinter-cc**: parser + per-mode 䮫 CODE_LOC/DATA_LOC, override १  `--code-loc`/`--data-loc`. tiny ࠡ⠥; small/big/huge warning' (runtime ⮢). 2026-05-30. -- [x] **`--memory-manual SPEC`**: `CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3`. 2026-05-30. -- [x] **`small` runtime**: `runtime/crt0_small.s` ᯮ ESTEX `$3D GETMEM` + `$3A SETWIN2` ⮡ 뤥 W2-࠭ gsinit. **** BIOS `$C4` ? ⥪ ⮬ ⠯ W1 (boot_stack HOME), BIOS ॡ ⥪ W2. ᫥ SP ४砥 0xBFFE, ⠭ flow. 2026-05-30, verified hello.exe. -- [x] **`small` auto-detect >16 KB ணࠬ**: `crt0_small.s` ⠥ `0xC2` (⥪ ࠭ W2 ? `0xA2`! W1). ᫨ 0xFF ? 뤥 page; DSS 㦥 ᤥ (ணࠬ ᠬ 뫥 W2). crt0 뢠 0..30 KB. mkexe ⠪ ࠧ蠥 HOME span W1+W2 (0x4000..0xBFFF). Verified hello: small (5 KB 䠩, SETWIN2 path) + 32 KB 䠩 (auto-skip). 2026-05-30. -- [x] **`big` runtime** (tiny + banked code W1): ࠬਧ `crt0_banked.s` + `bank.s` १ `.ifdef BANK_W1` ? 㣮 banking port (0xA2 vs 0xE2), 㣮 load-addr (0x4000 vs 0xC000). sprinter-cc prepend' `BANK_W1 = 1` `--memory big`, । `mkexe -B 0x4000`. ਬ `examples/banked_big/`. 2026-05-30. -- [x] **`huge` runtime** (small + banked code W3): merge W2-detect `crt0_small.s` `crt0_banked.s`. 騩 ਬ `examples/banked/` ⥯ ᯮ MEMORY=huge. 2026-05-30. -- [x] **`--debug` 䫠**: prepend `DEBUG_RT = 1` crt0 + `-DDEBUG_RT` sdcc. 뢠 symbol `_w2_self_allocated` (uint8_t) ? runtime diagnostic ஢ W2. 2026-05-30. - -### -襭 libc crt0 - -** `sprinter.lib`** ࠡ⠥ memory mode ? `.rel`-童 relocatable, SDLD dead-code elimination per-member ( 䨪  `gfx_core.rel` ..). Verified hello vs malloc_test १ map-䠩. - -**`gfx.lib` ⤥쭮 ? 㦥**: dead-code elimination 㦥 ࠡ⠥. - -**`libc_banked` (libc bank HOME)** ? ⮬, HOME (16 KB) user- + libc `huge` mode. १ `--codeseg BANK0` 樨 libc; trade-off: trampoline ~30 横 libc-맮. ਣ: ॠ쭠 ணࠬ HOME budget. - -**HW-ᨬ 㫨 ? `sprinter_home.lib` ⤥쭮.** libc 䨧᪨ W3, ⮬ W3: -- `gfx_*` ? `0xC000+` ᫥ swap W3 video page -- `bank_io` (mem_alloc_pages/bank_read/bank_write) ? swap' W3 १ `OUT (0xE2)` -- 騥 ISR ? 뢠 ਩ W3 㣮 - - huge mode 㫨 HOME (W1). 㤥 `libc_banked`, **६** 뤥塞 `sprinter_home.lib` (HOME-only) `sprinter.lib` (bankable). 쭠 奬: -``` -sprinter_home.lib HOME-only: gfx, bank_io, ISR shims -sprinter.lib bankable: printf, malloc, string, conio, stdio, env, ... -sprinter_banked.lib sprinter.lib --codeseg BANK0 ( huge) -``` -ਣ: ॠ `--memory huge` runtime. - -**crt0 ? mode:** -- `crt0.s` ? ⥪騩, **tiny/big**: SP=0xBFFE, argv (W2- 㦥 뤥 DSS). -- `crt0_minimal.s` ? ⥪騩, tiny argv. -- `crt0_small.s` ? **, step 3**: **small/huge**, W2 १ `mem_alloc_pages` gsinit, `0xA2`, ⮬ ⠭ flow. -- `crt0_banked.s` ? ⥪騩, **big**: trampoline-⠡ W3 , CODE W2. -- `crt0_banked_small.s` ? ****: huge = small (W2-alloc) + banked (W3 trampolines). - -sprinter-cc ࠥ crt0 `--memory` mode (ᥩ `--crt0=` override). -- [x] **ࠨ ࠧ ⥪**: 䫠 `sprinter-cc --stack-size BYTES`. Wrapper `heap_top.s` `___sdcc_heap_end = stack_top + 1 - stack_size`, ⤥ .rel per-program. Default ?1278 (heap_top=0xBB00) `runtime/heap_top.s`. 2026-05-30. - -䥩: `sprinter-cc --memory [tiny|small|big|huge|manual] [--memory-manual SPEC] [--stack-size N] foo.c`. `--memory-manual` ⮫쪮 `--memory manual`. - -## Known issues / quirks - -- **ESTEX $46 ENV API**: ? ࠡ⠥. - `DiskSyscalls.txt v1.6` 訡筮 뢠 return-status ? A=0 NOT FOUND, FOUND. 䨪஢ `memory/sprinter_platform.md`. - -## V2 (᫥ ५ v1) - -### Turbo-C-style graphics API (BGI-like) ? **MUST v2** - - `gfx_*` API ஢ **Turbo-C ``** (BGI) MS-DOS. -ணࠬ ਢ訥 Turbo-C ७ ᪨ 1--1. - -** ** ( ᭮ Borland BGI): - -Setup/teardown: -- `initgraph()` / `closegraph()` ? ᥩ `gfx_init`/`gfx_done`, alias -- `getmaxx()` / `getmaxy()` ? GFX_WIDTH-1 / GFX_HEIGHT-1 -- `cleardevice()` ? alias to gfx_clear -- `getgraphmode()` / `setgraphmode()` ? get_videomode/set_videomode - -Color/palette: -- `setcolor(c)`, `getcolor()` ? current draw color -- `setbkcolor(c)`, `getbkcolor()` ? background color -- `setpalette(idx, c)` ? entry -- `getpalette(&info)` ? read all palette - -Primitives ( 㦥 ? BGI- aliases): -- `putpixel(x, y, c)` ? gfx_putpixel -- `getpixel(x, y)` ? 㦭 ॠ (RMW ⭮ ? IN) -- `moveto(x, y)`, `lineto(x, y)`, `linerel(dx, dy)` ? current point + line drawing -- `line(x1, y1, x2, y2)` ? gfx_line -- `rectangle(x1, y1, x2, y2)` ? gfx_rect ( 㣮 API: x1,y1,x2,y2 vs x,y,w,h!) -- `bar(x1, y1, x2, y2)` ? gfx_fill_rect -- `bar3d(x1, y1, x2, y2, depth, topflag)` ? : rect + 3d edges -- `circle(x, y, r)`, `arc(...)`, `ellipse(...)`, `pieslice(...)` ? primitives -- `fillpoly()`, `drawpoly()` ? -- `floodfill(x, y, border_color)` ? - -Text on graphics screen: -- `outtext(s)` / `outtextxy(x, y, s)` ? gfx_text (alias) -- `settextstyle(font, dir, size)` ? multiple bitmap fonts -- `gettextsettings(&info)` -- `textwidth(s)` / `textheight(s)` ? measure - -Image manipulation: -- `imagesize(x1, y1, x2, y2)` ? bytes needed for getimage -- `getimage(x1, y1, x2, y2, buf)` ? save rect to buffer -- `putimage(x, y, buf, op)` ? paste back with COPY_PUT/XOR_PUT/AND_PUT/OR_PUT/NOT_PUT - -Clipping/viewport: -- `setviewport(x1, y1, x2, y2, clip)` ? drawing clip rect -- `getviewsettings(&info)` -- `clearviewport()` -- `setactivepage(p)` / `setvisualpage(p)` ? ਧ (Sprinter 2 screen) - -Line style: -- `setlinestyle(style, pattern, thickness)` ? SOLID_LINE / DOTTED_LINE / etc. -- `getlinesettings(&info)` - -**Acceptance:** ७ ⨯筮 Turbo-C BGI ணࠬ (饩 ᯮ짮 -moveto/lineto/circle/bar/setcolor) ࠡ ⢥ ࠢ. - -**Notes:** -- BGI fonts (TRIPLEX/SANS_SERIF/GOTHIC) ? BIOS font, ⠫ 㦭 - ( bitmap data lib) -- imagesize/getimage/putimage ? ᠬ ॡ game/animation -- Active/visual page ( ਧ) ? Sprinter ন 2 graphics pages, - 㦥 API switching - -. ⠪ `examples/` Turbo C 2.x BGIDEMO reference 㦭. - -### IM2 Interrupt Handlers ? **MUST v2** - -User- ISR १ Z80 IM 2 mode. 㦭 : -- Timer ticks (50 Hz frame counter, ) -- Music playback (AY, COVOX) -- Real-time games (input + game logic + render interrupt-driven) -- Async keyboard / mouse handling - -**Status:** v2. research + design `docs/im2_isr_design.md`. - -**襭 ⥪:** ॠ ⤥ memory mode `--memory im2` -( ⮣ ⮡ 騥 crt0). Detail' design-doc. - -** research'** ( ⥪ `docs/im2_isr_design.md`): -- Vector 0xFF ? frame + keyboard + CBL. Disambiguation ⠬ 0x19 / 0xFE -- Mouse hardware-IRQ 室 ( ⥪饩 ) -- Vector table / ISR / stack W2 (0x8000..0xBFFF) -- DSS ᢮ IM 2 handler ? 㦭 chain' ( / SYSTIME ) - -### 稥 㯭 㭪 v2 - -- [ ] **FILE API rewrite ? buffered streams** ? ⥪ ॠ - `libc/stdio/file.c` provisional unbuffered shim ( fputc/fgetc - = read/write syscall). 㦭 業 buffered ᥬ⨪ - Solid-C: - - ```c - typedef struct { - uint flags; // +0..1 file status flags - int level; // +2..3 empty/fill level of buffer - char *curp; // +4..5 current active pointer - int fd; // +6..7 underlying low-level fd - char *buffer; // +8..9 data transfer buffer - char hold; // +10 ungetc byte if no buffer - short token; // +11..12 reserved - char dummy; // +13 reserved - } FILE; - ``` - - stdin/stdout/stderr ? fd-થ `0 / -1 / -2`. ⥫ - stdout/stderr ࠭ ७: ESTEX OPEN positive - small fd (1, 2, ?) 筮 䠩 ? ᫨ stdout=1, ॠ - fd=1 ⠫ 䨪஬. fd=0 stdin ᭮ - (ESTEX 0 頥). fd । syscall' ? - ᯥਧ 䫠 `_F_CONIN/_F_CONOUT`. - - ਭ-⮪ (stdaux/stdprn) ॠ㥬 ? Sprinter ਭ୮ - API . - - ୠ⨢ ? ॠ third_party/solid-c (sources - `SRC/CLIB/`); ⠬ ⮢ buffered FILE + fopen/fread/fwrite/ - fseek/setvbuf .. ஢ 訬 open/read/write/lseek. - - rewrite deferred issues stdio-review: - - `fwrite` short-write ⠢ `_F_ERROR` - - `fgets(buf, 1, fp)` ? ⠭ "empty string", 㫨 NULL - - `mode_to_flags` ? break-out '+' (cosmetic) - -- [ ] **Audio API** ? AY-3-8910 + COVOX १ 뢠 (ॡ IM2) -- [ ] **ISA-8 slot support** ? ZX-Bus (sound, network, etc.) ? ॡ IM2 + ⥭ port - -## 稥 (v1 backlog, 騥) - -- [x] **#9: text I/O split (Turbo-C style)** ? stdio (puts/printf/putchar) ⥯ fast no-attr १ PCHARS/PUTCHAR. conio (cputs/cprintf/putch) ਬ attr १ textcolor/textbackground/textattr. KEEP_EXIST_ATTR ? conio fallback fast path. Verified hello.exe. . `memory/text_output_api_split.md`. 2026-05-31. -- [x] **Mouse API ** (१ driver, RST 30h) ? 14 㭪権 (init/show/hide/refresh/read/goto/bounds/text_cursor/load_cursor/get_cursor/get/set_sensitivity/video_mode_changed). . `memory/mouse_api.md`. Verified MAME 2026-05-31. Sensitivity = divider ( = ॥). -- [ ] Interrupt handlers ? IM 2 vector table HOME user ISR' -- [ ] প `restore SP on EXIT` ( z88dk +pps) ? ஢ 㦭 -- [ ] CI: ⮬᪨ ᪠ MAME `-aviwrite` screenshot-ࠢ, ⮡ ਬ஢ 室 祫 - -## ⮬ - -- প `` ( SDCC stdlib ? 㦭 ஢ crt0 ᮢ⨬) -- `` १ ESTEX SYSTIME (`$21`) CMOS BIOS-㭪樨 -- ZX Spectrum-ᮢ⨬ ० ⤥ target ( ஢ ᯥ㬮᪨ ணࠬ) -- প ZX-Bus (sound, network, etc.) ? 㦭 ࠩ -- Profile-guided optimization tools (hot/cold detection) 㯭 ணࠬ - -## Linker duplicate-symbol warnings (, 䨫஢) - - ᮧ⥫쭮 overrides' SDCC z80.lib 㭪樨 ᮡ⢥ ᨥ `sprinter.lib`, `sdldz80` `?ASlink-Warning-Definition of public symbol '...' found more than once`. ࢮ । (), ⮬ ४⭮ ? warning ⮫쪮 noise. - -騥 overrides: -- `_puts` ? १ PCHARS+\r\n vs SDCC posix puts -- `___sdcc_heap` ? heap W2 vs SDCC's ⠭ -- `_asctime`, `_localtime` ( 㣨 time) ? `posix_time.c` १ ESTEX SYSTIME vs SDCC's `time.rel` `_RtcRead` - -**饥 襭:** `bin/sprinter-cc` 䨫஢뢠 warning- (warning + 2 follow-up `Library:` ப) 뢮 `sdcc`. १ `-v` (verbose) 뢠. १ awk-pipe. - -** 襭:** -- ३ explicit `--nostdlib` + 筮 ᯨ᮪ 㦭 㫥 z80.lib (string, math, stdlib override') ? warning', ᪨஢ -- : २ `_puts` ? `_puts_sprinter` + alias १ linker flag ( 㢥७ SDCC ন) -- : ⠢ ᥩ (ࠡ祥 benign) ? ਮ - -## TODO: ஢ ॠ쭮 - -- [ ] **Port_Y banking trick** (`docs/part2/SprinterGraphics programming.txt`): - ⢥ত ᫥ `OUT (0x89), Y` 0xC000+0x400*N W3 - ப Y..Y+15 ( ணࠬ஢ ? 16 ப). - Empirical 2026-06-01 MAME 0.283 trick ** ࠡ⠥** ? ᥫ - ᠬ 0xC000+row_width 室 . ᪨ - `docs/samples/plasma2.asm` ⮦ ᯮ banking, ⠭ - Port_Y per row. - : - 1. ॠ쭮 Sprinter - 2. dual-write (`_gfx_putpixel_raw` + ன write `0xD000+x`) - 3. ᫨ ? MAME, issue - ९ - 4. ᫨ ⮦ ? 㬥 , 㤠 㯮 - ⠢ ⥪ ॠ (Port_Y per pixel) - 5. ᫨ banking ࠡ⠥ ? ஢ Port_Y - `_gfx_putpixel_raw` (sentinel out-of-range, . memory/gfx_port_y_banking.md) - - 易 먣 Bresenham (60-pixel ) ? 8? - OUT (0x89) 権, `gfx_fill_rect 320x256` ? 16? . - release v1. - -## GFX: ७ `docs/part2/accelerator_doc.txt` - -᫥ ⥭ ⠫쭮 accelerator doc  ࠢ. - ᯮ ⮫쪮 ਧ⠫/⨪ Fill mode. - -### Quick wins ⥪ primitives - -- [ ] ** SMC `LD A, (var)` block-size**. 㬥  - ࠧ蠥 `LD A, (HL)`, `LD A, (BC)`, `LD A, (DE)` ( `LD A, r`). - 㡥 SMC complexity `gfx_lines.c:hfill_chunk/vfill_chunk` - `gfx_16.c:g16_hfill_chunk`. 饭 ⮫쪮 register-to-register. -- [ ] **஢ block-size**. 㬥 뢠 accel - block size bursts (. `Horizontal_Line_Fill`: ⠭ - size + `LD B,B` ⪫祭, ⮬ Fill mode ᯮ - ࠭ size). `gfx_fill_rect` 100 묨 - ப ? ⠭ size 1 ࠧ, 100. - -### Bank-prefix modes (port 0xE2 bits) - -㬥 뢠 ਠ ࠭ ⠭⭮ 0x50: - -| Bank byte | Effect | -|---|---| -| 0x50 | Normal write ? shadow + | -| 0x54 | "no copy in main shadow RAM" | -| 0x58 | **"FF is transparent"** ? 0xFF write ⠢ background | -| 0x5C | both | - -Bank 0x58 祬 mouse cursor 0xFF-஧筮. - **sprite-blending १ accel block copy**: - -- [ ] **`gfx_set_bank_transparent(on)`** 䫠 `gfx_set_bank` - 롮 0x50/0x58 ᮢ sprite' -- [ ] ᯮ짮 `gfx_blit()` ⮡ 䠪 - transparent sprites १ accel- - -### Block copy mode (sprite blit') - -`LD L,L` (horizontal) `LD A,A` (vertical) ? ० ஢ १ -256- accel memory. blit'. - -- [ ] **`gfx_blit(src_data, x, y, w, h)`** ? ஢ sprite' - (ந ࠧ, १ accel) -- [ ] **`gfx_blit_transparent(src, x, y, w, h)`** ? ᯮ짮 bank 0x58 - -. `Draw_Restangle_Data` accelerator_doc.txt ७. - -### AND / OR / XOR operations १ accel - -㬥 뢠 accel ন ᪨ 樨 -. ਬ: -- XOR ? (뤥 selection UI) -- OR / AND ? masking, alpha-style blending -- . ਬ accelerator_doc.txt: "256 bytes block coding via XOR" - -- [ ] **`gfx_xor_rect`** / **`gfx_or_rect`** / **`gfx_and_rect`** ? - ਬ⨢ ᪨ 権 אַ㣮쭨 -- [ ] **`gfx_invert_rect(x, y, w, h)`** ? alias xor 0xFF - -### Bitmap fonts ࠧ ࠧ஢ - - `gfx_text` / `gfx_putchar` प ࠡ 8?8 ⮬ -(BIOS WIN_GET_ZG 頥 256?8 ). UI / 쭨 -㦭: -- [ ] **`gfx_set_font_size(w, h)`** ? ४ ਭ/ glyph' -- [ ] **`gfx_set_font_data(ptr, w, h, advance)`** ? 㪠⥫ - 짮⥫᪨ + ࠧ -- [ ] প **proportional** (advance != w) ⮢ ? - array advance[256] ਭ glyph' -- [ ] **Big-font ०**: 8?16, 16?16, 16?8 ( 쭨) -- [ ] ⤥ API `gfx_text_ex(x, y, str, font_id)` - font_id 롨ࠥ 㦥 ⮢ -- [ ] **Anti-alias 2-bit ** ( 䮭 / ࠭ / 2- alpha?) - ? far future, smooth UI - -## ⠯ ⨬権 ( ᥩ) - -- **`gfx_line` १ accel ** ? Bresenham |dy| << |dx| ( ) 뤠 runs Y ( X): ᥫ, ᥫ, ᥫ, 蠣 Y, ᥫ, ᥫ... ⠪ run ? ⮢ 㬥 `gfx_hline` ( `vline`). - ᫥: runs 㭪 ; run length, ஬ 룮 accel hline 祬 N?putpixel (overhead accel ~20?s, putpixel ~5?s ? accel 룮 run ? 4-5 px); (dx ? dy) ⠢ Bresenham, ? run-length-based fill. - `gfx_line` orthogonal cases 㦥 १ accel ? ⨬஢ ⮫쪮 . - -- **`gfx_fill_rect` W3-swap ** ? ᥩ ७ `gfx_hline`/`gfx_vline` ᢮ DI/save-W3/restore-W3/EI. ᤥ internal `_fill_rect_inner` ন W3 DI 横; ~20?s ? ⢮ ப/⮫殢 . ਬ ᥬ ਬ⨢. -# MDView ? ᬮ騪 Markdown Sprinter - -**MDView** ? ணࠬ ᬮ 㬥⮢ ଠ *Markdown* -Sprinter ( Z80). 㬥 ࠭ ⤥쭮 W3 -᭮ RAM ணࠬ. - -## - -- 㬥 **128 ** (8 ࠭ EMM 16 ) -- **16 384** ࠭ ப -- ⮬᪨ ७ ᫮ ਭ ࠭ (80 ⮫殢) -- ਧ⠫ ᤢ ப ப ( , ⠡) -- -: 䠩, ப, 業 ப⪨ -- ப ﭨ ६ 㧪 樨 -- প ?? ᪫ ப - -## - -``` -mdview [_䠩.md] -``` - -᫨ 䠩 , 㦠 `README.MD`. - -## ࠢ - -``` - ⢨ - -Up Down ப⪠ ப / -PgUp PgDn ப⪠ ࠭ (30 ப) -Home 砫 㬥 -End 㬥 -Left Right ਧ⠫ ᤢ (⮫쪮 nowrap-ப) -F1 ࠢ -F8 ஢: CP866 ? CP1251 ? KOI8-R ? UTF-8 ? ... -F10 / Esc 室 ணࠬ -``` - -஢ । ⮬᪨ ⨨ (BOM + ⨪ - 4 ); `F8` ४砥 , ᫨ ⥪ 訡. -8- ஢ (CP866/CP1251/KOI8-R) ४ (६ -ᮢ). ன / (ਬ UTF-8) ந ? -ࢮ ४祭 (⪠ 㧠 ᯨ஬), . - -## ⠪ Markdown - -### - -ন ஢ H1?H4. ஢ H5 H6 ⮡ࠦ H4. - - # ࢮ ஢ - ## ண ஢ - ### 쥣 ஢ - #### ⮣ ஢ - -### ⮢ ଠ஢ - -** ⥪** 뤥 묨 񧤮窠: `**⥪**` - -*ᨢ* 뤥 묨 񧤮窠 `*⥪*` ન `_⥪_` - -`஥ ` 砥 묨 窠 - -~~ભ ⥪~~ ? ⨫: `~~⥪~~` - -### 㬥஢ ᯨ᮪ - -થ `-`, `*` `+`: - -- 㭪 ᯨ᪠ -- ன 㭪 ᯨ᪠ -- ⨩ 㭪 筮 ⥪⮬, 室 - 㤥 ७ ᫥ ப ࠭ 㯠 - -### 㬥஢ ᯨ᮪ - -1. -2. ன -3. ⨩ - -### - -> 稭 ᨬ `>`. ᪮쪮 ᫥⥫ -> ப ᪫ ⮬᪨ -> ७ᮬ ᫮. - -### (verbatim) - - 砥 ன 窨. -⥪ ⮡ࠦ ? ? ࠧ Markdown: - -``` -#include -#include - -int main(void) { - puts("Hello, Sprinter!"); - return 0; -} -``` - -### ਧ⠫쭠 - - ᨬ `---`, `***` `___` ⤥쭮 ப: - ---- - -## 孨᪨ ࠪ⨪ - -- **ଠ:** Sprinter, Z80 @ 21 -- **஢:** CP866 / CP1251 / KOI8-R / UTF-8 (⮮।, `F8`) -- **ᨬ ࠧ 䠩:** 128 -- **ᨬ쭮 ᫮ ப :** 16 384 -- ** :** small - - ணࠬ, c⥪, , ? a W1-W2 (32 , 0x4000?0xBFFF). - - 䠩 ? ࠭ EMM, ⮡ࠦ W3 (0xC000?0xFFFF) - -## TODO - -1. **祭 ࠧ 㬥⮢.** 128 : 筮 - ࠧ ࠡ 訬 - ࠭ , ஢ - ࠡ 8- ࠭栬 16. - -: ଠ஢ ⠡ ࠬ; প ஢ -CP866 / CP1251 / KOI8-R / UTF-8 ⮮। ४祭 `F8` -(ன ந , ࢮ ४祭). - -4. **᪮७ ७ਭ.** ப ࠭. ⨬ 横 뢮 - ᨬ १ BIOS WRCHAR ( 뢮, DMA). - ---- - -*MDView v0.2 (c) 2026 ஢ ..* - - ---- - -# Sprinter C Compiler ? v1.0 - -C toolchain for **Sprinter** ? the Z80-based home computer by Peters Plus, running -ESTEX DSS. Host: macOS / Linux. Target: `.EXE` files in SprintEXE format. - -Built on top of **SDCC 4.5** (vendored in `third_party/sdcc/`). This repository adds -everything Sprinter-specific: crt0, linker integration, libc wrappers over ESTEX, -banked-call trampolines, graphics & accelerator API, mouse driver wrappers, and the -`mkexe` utility for producing SprintEXE images. - -## What you get - -* **`bin/sprinter-cc`** ? one-line driver: `sprinter-cc -o foo.exe foo.c` -* **Memory modes**: `tiny`, `small`, `big`, `huge`, `manual` ? see below. -* **stdio + conio**: printf, puts, putchar, getchar, fopen/fread/..., cprintf, cputs, putch, textcolor/textbackground/textattr, gotoxy, kbhit/getch. -* **Graphics**: 320?256?256 and 640?256?16 modes, accelerator-backed primitives (hline / vline / rect / fill_rect / line via Bresenham, plus clear), bitmap-font text in both modes via BIOS character generator. -* **File I/O**: POSIX (`open`/`read`/`write`/`close`/`lseek`/`unlink`/`creat`), FILE\* streams (`fopen`/`fgets`/`fwrite`/...), directory listing (`ffirst`/`fnext`), `chdir`/`getcwd`/`mkdir`/`rmdir`, `stat`/`fstat`. -* **Memory**: 32 KB heap (W2-resident), banking-aware page allocator (`mem_alloc_pages`/`bank_read`/`bank_write`), explicit memory modes for sub-16 KB programs. -* **Mouse**: full Sprinter driver wrapper (14 functions including custom cursor bitmaps). -* **Environment**: `getenv`/`putenv`/`sysenv` over ESTEX `$46`. -* **Time**: `getdatetime`/`setdatetime` + POSIX `time`/`localtime`/`mktime`/`asctime`/`ctime`. -* **Misc**: `errno`/`strerror`/`perror`, `atexit`, `setjmp`/`longjmp`, `sleep`, full argv parsing in crt0. - -## Quick start - -```sh -git clone sprinter-c -cd sprinter-c -make sdcc # one-time: fetch SDCC 4.5 binary (~25 MB) -make all # build mkexe + libsprinter.lib + 27 examples -make floppy # pack everything into mame/v306/IMG/mc.img -cd mame/v306 && ./run_mame.sh # boot Sprinter in MAME -``` - -Compile a single program: - -```sh -cat > hello.c < -int main(void) { puts("Hello, Sprinter!"); return 0; } -EOF - -bin/sprinter-cc -o hello.exe hello.c -``` - -That's it ? `hello.exe` is now a valid SprintEXE you can `RUN HELLO` from the ESTEX shell. - -## Memory modes - -Sprinter's address space is four 16 KB windows (W0 / W1 / W2 / W3). DSS allocates -pages by program size ? small programs get only one page. Pick a memory mode based -on what your program needs: - -| Mode | Code lives in | Banking | Use when | Note | -|---|---|---|---|---| -| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | | -| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | | -| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | | -| `huge` | W1-W2 + W3 banking | yes (W3) | small + extra code modules | | -| `manual` | user-specified | optional | special layouts | Not implemented | - -```sh -sprinter-cc --memory small -o big.exe bigprog.c -sprinter-cc --memory huge -o app.exe main.c --bank 1=engine.c --bank 2=ai.c -``` - -Banked functions are declared with `__banked`: - -```c -void engine_tick(int dt) __banked; // lives in BANK1, automatically swapped -``` - -## Examples (27 total) - -| Example | What it demonstrates | -|---|---| -| `hello` | Hello world with stdio + conio Turbo-C-style colors | -| `argv` | argv parsing in crt0 | -| `cat` | File I/O ? read & print TEST.TXT | -| `seek` | 32-bit lseek over a 100 KB file | -| `ls` | Directory listing via ffirst/fnext | -| `filetest` | FILE\* streams (fopen/fread/...) | -| `stattest` | `stat`/`fstat` on files and directories | -| `errno` | errno / strerror / perror | -| `mem_test` | Page allocator + bank\_read/bank\_write | -| `malloc` | Heap stress test (200+ allocations) | -| `banked` | Banked code in W3 (huge mode) | -| `bankedbg` | Banked code in W1 (big mode) | -| `banklocl` | Bank-local static data and BSS | -| `mouse` | Mouse driver in text mode | -| `gfx_mous` | Mouse with custom bitmap cursor in graphics mode | -| `gfx_demo` | 320?256?256 lines / rects / accelerator | -| `gfx_d16` | 640?256?16 same primitives | -| `gfx_text` | Bitmap-font text on graphics screen | -| `timedir` | Date/time + directory listing | -| `ptime` | POSIX time API | -| `openenv` | open() flags + environment vars | -| `conio` | conio API smoke test | -| `attrprob` | Probe Sprinter text attribute byte layout | -| `strtest` | string.h test (from SDCC's z80.lib) | -| `stdlib` | stdlib.h test (qsort / rand / strtol / etc.) | -| `assrtest` | assert() | -| `rt_test` | Runtime helpers (sleep, setjmp, atexit) | - -## Headers - -Standard: -* `` ? puts / printf / FILE\* + Sprinter-specific dec/hex helpers -* `` ? atoi / atof / malloc / qsort / ... (from SDCC z80.lib) -* `` / `` / `` ? from SDCC z80.lib -* `` ? read / write / close / lseek / unlink -* `` ? open / creat + O\_RDONLY / O\_CREAT / ... -* `` ? errno + error names + strerror -* `` ? stat / fstat -* `` / `` ? from SDCC - -Sprinter-specific: -* `` ? putch / cputs / cprintf, textcolor / textbackground / textattr, kbhit / getch, clrscr, gotoxy, wherex/y -* `` ? gfx_init/done, palette, putpixel, hline/vline/rect/fill_rect/line, text ? for both 320 and 640 modes (gfx_\*16 variants) -* `` ? full 14-function driver wrapper + mouse_cursor_t with bitmap support -* `` ? chdir / getcwd / mkdir / rmdir / ffirst / fnext / ffblk -* `` ? getdatetime / setdatetime + POSIX time / localtime / etc. -* `` ? raw ports, ESTEX/BIOS function numbers, env API -* `` ? exit / \_exit / atexit -* `` ? mem\_alloc\_pages / mem\_free\_block / bank\_read / bank\_write -* `` ? Solid-C compatibility layer (aliases + BOOL/WORD/uint types) - -## Toolchain commands - -```sh -make all # build mkexe + lib + every example -make floppy # repack mame/v306/IMG/mc.img with all .exe files -make check # 17 mkexe unit-tests -make clean # remove all build artefacts -make sdcc # one-time: fetch SDCC 4.5 binary -``` - -### sprinter-cc options - -``` -sprinter-cc -o foo.exe foo.c [more.c ...] [options] - - --memory MODE tiny | small | big | huge | manual (default: tiny) - --memory-manual SPEC explicit placement (CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3) - --stack-size N bytes reserved for the stack (default ~1278) - --crt0=TYPE default | minimal | banked | small - --bank N=FILE.c compile FILE.c into bank N (repeatable, max 15) - --debug enable runtime diagnostics (defines DEBUG_RT) - -I PATH extra include path - -L 0xADDR / -E / -S override load / entry / stack addresses - -Wl FLAG pass FLAG to sdldz80 - --mkexe FLAG pass FLAG to mkexe (e.g. --mkexe -p --mkexe 0 for bank padding) - -v verbose -``` - -## Status - -What works in v1.0: -* Compile / link / pack to SprintEXE ? verified on all 27 examples -* Four memory modes (tiny / small / big / huge) -* Graphics (both modes) with accelerator -* Mouse (text + graphics cursor) -* File I/O, directories, environment, time -* All headers listed above - -Deferred to v2.0 (see `docs/TODO.md`): -* **Turbo-C-style BGI graphics API** ? `initgraph` / `setcolor` / `circle` / - `getimage` / `putimage` / etc. on top of our `gfx_*` primitives -* Remaining Solid-C compatibility gaps (Phase 2/3) ? see `docs/solid_c_compatibility.md` -* Manual memory mode -* Rewrite FILE\* stream API (current implementation is very primitive and doesn't use buffers) - -Deferred to v3.0: -* **IM2 interrupt handlers** ? research complete (`docs/im2_isr_design.md`), - implementation scheduled for v3 -* **Audio API** (AY-3-8910 + COVOX) ? requires IM2 -* **ISA-8 slot drivers** ? requires IM2 (???) - -## Documentation - -* `docs/TODO.md` ? roadmap and open work items -* `docs/solid_c_compatibility.md` ? gap analysis vs Solid-C 2004 -* `docs/im2_isr_design.md` ? interrupt handler design (v2) -* `docs/converted/` ? source documentation (ESTEX, BIOS, architecture) - converted to plain text for `grep` -* `docs/reference/`, `docs/samples/`, `docs/memory management/` ? original - Russian docs and code samples from Peters Plus - -## Repository layout - -``` -bin/sprinter-cc one-line compiler driver (bash) -toolchain/mkexe/ host-side tool: .ihx -> .exe SprintEXE -toolchain/check_banks.py post-link bank size enforcer -runtime/ crt0 variants (default, minimal, small, banked) - bank trampolines, heap, heap_top -libc/include/ headers -libc/io|stdio|mem|gfx/ C and asm sources for libsprinter.lib -lib/ Makefile that archives libsprinter.lib via sdar -examples/ 27 example programs -mame/v306/ MAME binary + Sprinter ROM/HDD images + floppy script -third_party/sdcc/ vendored SDCC 4.5 (fetched via `make sdcc`) -third_party/solid-c/ reference: original Sprinter native C (for compat target) -docs/ documentation -``` - -## License - -This repository contains: -* Original code in `bin/`, `toolchain/`, `runtime/`, `libc/`, `lib/`, `examples/` ? - MIT-licensed. -* `third_party/sdcc/` ? SDCC 4.5 under GPLv2 with linking exception - (see `third_party/sdcc/COPYING.txt`) -* `third_party/solid-c/` ? original Sprinter Solid C, used only as a reference - -## Credits - -* **Sprinter / Peters Plus** ? , ਩ ਭ and the original team -* **SDCC** ? for the underlying Z80 compiler -* **MAME** ? for the Sprinter Sp2000 emulation - ---- - -For questions / patches: see CONTRIBUTING.md (TBD) or open an issue. - ---- - -# TODO / Roadmap - - 浪 뢠 ਮ.  ॠ ணࠬ ? ਮ ᬥ. - -## ⠯ 5 ? malloc / free + banking-aware page allocator ? - -- [x] SDCC's `malloc`/`free` + `runtime/heap.s` ( library heap.rel, 14000- heap 2) -- [x] `libc/mem/mem_alloc.c` ? page allocator: `mem_alloc_pages`/`mem_free_block`/`mem_get_page`/`mem_info` १ ESTEX `$3C/$3D/$3E` + BIOS `$C4` -- [x] `libc/mem/bank_io.c` ? HOME-१ `bank_read`/`bank_write`/`bank_load_byte`/`bank_store_byte` ᢮ W3 -- [x] `examples/malloc_test/` ? ஢ઠ SDCC's malloc (~210 64- allocations १ heap) -- [x] `examples/mem_test/` ? ஢ઠ page allocator: 3 ࠭, ࠧ १ bank_write, 䨪 १ bank_read - -## ⠯ 6 ? argv parsing + sprinter-cc wrapper ? - -- [x] crt0 ESTEX command-line IX-prefix (inline asm `runtime/crt0.s`) -- [x] Strip leading CP/M-style space (DSS quirk) -- [x] । `argc`/`argv` main() १ HL/DE (SDCC __sdcccall(1) ABI) -- [x] argv[0] = basename .EXE १ ESTEX APPINFO ($47 subfn 2) -- [x] `runtime/crt0_minimal.s` ? opt-out 祭 쪨 ணࠬ -- [x] `runtime/crt0_banked.s` ? ⥯ ⮦ argv (parse_argv + get_progname ᪮஢ crt0.s; 㤥 factored argv.s libsprinter.lib) -- [x] Bash-⪠ `bin/sprinter-cc`: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権: `--memory`, `--memory-manual`, `--stack-size`, `--crt0=`, `--bank N=FILE.c`, `--debug`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` - -## ⠯ 8 ? 䨪 (320?256?256 + 640?256?16 + accel + bitmap font) ? - -- [x] **8a** Graphics core: `gfx_init`/`gfx_done`/`gfx_clear`/`gfx_putpixel`/`gfx_pal_load`/`gfx_pal_set` (libc/gfx/gfx_core.c). १ BIOS PIC_SET_PAL ($A4). Verified 320?256?256. -- [x] **8b** /אַ㣮쭨/fill १ accelerator (libc/gfx/gfx_lines.c): `gfx_hline`/`gfx_vline` १ accel Fill (LD C,C / LD E,E + SMC block-size), `gfx_rect`/`gfx_fill_rect` heuristic 롮஬ ਥ樨 (h/v bursts count), `gfx_line` Bresenham . `gfx_clear` ⮦ ९ᠭ column-major accel (~4? ॥). -- [x] **8c** 640?256?16 mode (libc/gfx/gfx_16.c): `gfx_*16` API, HIGH nibble = LEFT pixel (㬥 misleading), per-row RMW vline ( = 2 ਧ⠫ ᥫ). -- [x] **8d** Bitmap font + gfx_text (libc/gfx/gfx_text.c): १ BIOS WIN_GET_ZG ($B8), interleaved layout `font[row*256+char]`, `gfx_text`/`gfx_putchar` 320 mode, `gfx_text16`/`gfx_putchar16` 640 mode pair-table lookup. - -. memory/sprinter_graphics.md, sprinter_accelerator.md, sprinter_graphics_16.md, sprinter_font_format.md. - - ( ): -- [ ] -quad 640: per-cell (mode 0x82 ࠧ蠥 1 4 per 16?8 cell) ? १ אַ area-ᠭ ࠭ 0x0300..0x039F - -## Auto-banking (. `memory/banking_roadmap.md` ⠫) - -Phase 1 ? file-level bin-packing ? ॠ뢠 ஥ ~30 KB . - -- [ ] `toolchain/auto_bank.py`: - - ࠧ `.rel`-䠩 ( .map ᫥ dry-run link') - - First-fit-decreasing bin-packing - - `#pragma codeseg BANKn` manual override - - ५뢠 묨 `-Wl-b_BANKn=...` ࠬࠬ - - ⠥ । - -Phase 2-5: incremental rebalance, declarative `banks.toml`, function-level, call-graph-aware. 쪮 ᫨/ . - -## Bank-local static data (mutable data ⮬ ) ? ? - -- [x] ਬ `examples/bank_local_data/` ? 㭪 BANK1 ᢮ writable BSS array + const table + malloc- -- [x] `mkexe -p 0` 㫥 padding (BSS-storage 㧪) -- [x] ᪨ 楯: `--codeseg BANK1 --constseg BANK1 --dataseg BANK1` bank1.c + `-Wl-b_BANK1=0x1C000` . **`--dataseg BANK1` ** ? ࠭ ⭮ - trampoline bug ᪨஢ १. -- [x] ** 䨪 trampoline'a runtime/bank.s** ? `pop af; out (n), a` ਫ A ? banked-㭪樨 騥 uint8_t 頫 . `pop bc; out (c), b` ࠭ A. -- [x] **malloc banked-㭪樨 ࠡ⠥ ஧筮** ? heap W2 (HOME), W2 ᢠ trampoline', pointer  ⥪. . memory/bank_local_data_pattern.md. -- [x] 㬥 memory: `memory/bank_local_data_pattern.md` ( 楯 + malloc + nuances), `memory/sdcc_banking.md` (trampoline fix) -- [ ] 樮쭮 ? `check_banks.py` ⮡ 뢠 ࠧ size = code + const + bss per bank (cosmetic) - -祬: 㫥 訬 private state (level loader, audio engine, scene data). W2 heap , ⨪ . - -## ᪠ solid-c (⨢ Sprinter C ? `third_party/solid-c/`) - -᫥ solid-c'᪮ libc (. `memory/solid_c_findings.md`)  ⮢ ᫥ 㭪権. ਮ **᮪** : - -### High-priority gaps ( ஢, 짠) -- [x] **`errno` + `strerror`/`perror`** ? ⠡窠 32 訡 (libc/io/errno.c) -- [x] **७ `open()`** O_CREAT/O_TRUNC/O_APPEND/O_EXCL state machine -- [x] **`atexit`** ? 8-callback LIFO + `exit()` + `_exit()` (libc/io/atexit.c) -- [x] **`setjmp`/`longjmp`** ? 6- jmp_buf={sp,ix,pc} (libc/io/setjmp.c) -- [x] **`sleep(seconds)`** ? 50Hz halt-loop (libc/io/sleep.c) -- [x] **ESTEX ENV API** ($46, getenv/putenv) ? libc/io/env.c. 竨 doc-bug: ॠ쭮 A=0 NOT FOUND - -### Medium-priority (㦭 shell-like ⨫) -- [ ] **Mouse driver** ? `rst $30h`, 17 㭪権. **砫 ࠡ⠥ MAME**. -- [x] **`ffirst`/`fnext` + ffblk_t struct** directory listing ? ॠ, demo: ls.exe -- [x] **`getdatetime`/`setdatetime`** १ ESTEX $21/$22 ? libc/io/time.c, demo: time_dir_test -- [x] **`chdir`/`getcwd`/`mkdir`/`rmdir`** ? wrappers ESTEX $1B-$1E ? libc/io/fsdir.c -- [x] **conio: `kbhit`/`getch`/`getche`/`cputs`/`clrscr`/`gotoxy`** ? ॠ -- [x] **conio extras**: `wherex`/`wherey` ($53), `wrchar`/`rdchar` ($58/$57), `textmode_get/set` ($50/$51), `clrscr_attr` ($56) + COLOR macros - -### Low-priority ? ? FILE* stack - -- [x] ** unbuffered FILE\*** ? `libc/stdio/file.c` + `libc/include/stdio.h`. fopen/fclose/fputs/fgets/fread/fwrite/fseek/ftell/rewind/feof/ferror/clearerr/fflush + stdin/stdout/stderr pseudo-streams. . `memory/file_star_design.md` `examples/filetest`. -- [ ] fprintf / fscanf ? 㦭 printf-१-callback machinery. 짮⥫ `sprintf(buf, ...) + fputs(buf, fp)`. -- [ ] 樮 buffered mode (setvbuf, line/block buffering) ? ᫨ - . - -### POSIX time API ? ? -- [x] `libc/io/posix_time.c` ? time/localtime/gmtime/mktime/asctime/ctime getdatetime. SDCC's time.rel ( override _RtcRead). . `examples/ptime`. - -### sys/stat ? ? -- [x] `libc/io/stat.c` ? POSIX stat/fstat. ਤ open+fstat 䠩, ffirst+iter ( "."/".."). . `examples/stattest` `memory/estex_ffirst_dotdot.md`. - -### assert ? ? (ᯮ㥬 SDCC's __assert १ fallback include path) - -## libc/stdlib ? ? 㦭 (. memory/sdcc_stdlib_works.md) - -஢७ १ `examples/stdlib_test/`: SDCC z80.lib ᮤন ࠡ騥 ॠ樨: -- `atoi/atol/atof, strtol/strtoul, rand/srand, qsort/bsearch, abs/labs, div/ldiv` -- `` (memchr/cmp/set/cpy, strcat/cmp/cpy/len/chr/spn/etc.) -- `` (toupper/tolower) -- `` (sinf/cosf/sqrtf/etc.) - - ⮬᪨ ﭥ z80.lib 㦭. ** ९뢠**. - - Sprinter-specific 易⥫ 㫨 : atexit, env, errno, setjmp, putchar/puts/getchar, conio, fsdir, time, mouse, open/read/lseek/close. - -## Build-system: libsprinter.lib + sprinter-cc ? ? - -- [x] `lib/Makefile` ? ᮡࠥ libc/*.c `.rel`, 娢 १ sdar `lib/sprinter.lib` -- [x] 砥 runtime/bank.s runtime/heap.s (auto-pulled __banked/malloc) -- [x] `bin/sprinter-cc` ? bash-wrapper: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権 `--crt0=default|minimal|banked`, `--bank N=FILE.c`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` -- [x] `examples/hello_sccc/` ? : `hello.c` ᮡࠥ shell-맮, ࠧ ᮢ Makefile (925 ) -- [x] Split `putchar.c` ? `putchar.c` + `puts.c` per-function granularity (puts override SDCC's z80.lib version) -- [x] `make all` (ᨬ `lib` । `examples`) - - 襭 (樮쭮): -- [ ] ஢ ⠫ examples sprinter-cc Makefile (ᬥ⨪) -- [ ] 쭥 libc/*.c per-function ( ⥪ granularity 㦥 㦭 ࠧ ? .rel 楫, 設⢠ 䠩 㭪) - -## ⠯ 9 ? memory modes sprinter-cc - -DSS 뤥 ࠭ ࠧ ਫ: < 16 KB ? ࠭, ⠫ 砥 ?࠭ #FF? (read=0xFF, write ). - ⮣ CODE--W1 + DATA--W2 쪮 ணࠬ . . [memory/sprinter_memory_modes.md](../../.claude/projects/-Volumes-SAM8-Projects-DIY-Z80-Sprinter-C-Compiler/memory/sprinter_memory_modes.md). - -- [x] **`tiny`**: (CODE+DATA+⥪) W2. Default. Verified hello/argv/conio/malloc/file/etc. -- [x] **`--memory MODE` 䫠 sprinter-cc**: parser + per-mode 䮫 CODE_LOC/DATA_LOC, override १  `--code-loc`/`--data-loc`. tiny ࠡ⠥; small/big/huge warning' (runtime ⮢). 2026-05-30. -- [x] **`--memory-manual SPEC`**: `CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3`. 2026-05-30. -- [x] **`small` runtime**: `runtime/crt0_small.s` ᯮ ESTEX `$3D GETMEM` + `$3A SETWIN2` ⮡ 뤥 W2-࠭ gsinit. **** BIOS `$C4` ? ⥪ ⮬ ⠯ W1 (boot_stack HOME), BIOS ॡ ⥪ W2. ᫥ SP ४砥 0xBFFE, ⠭ flow. 2026-05-30, verified hello.exe. -- [x] **`small` auto-detect >16 KB ணࠬ**: `crt0_small.s` ⠥ `0xC2` (⥪ ࠭ W2 ? `0xA2`! W1). ᫨ 0xFF ? 뤥 page; DSS 㦥 ᤥ (ணࠬ ᠬ 뫥 W2). crt0 뢠 0..30 KB. mkexe ⠪ ࠧ蠥 HOME span W1+W2 (0x4000..0xBFFF). Verified hello: small (5 KB 䠩, SETWIN2 path) + 32 KB 䠩 (auto-skip). 2026-05-30. -- [x] **`big` runtime** (tiny + banked code W1): ࠬਧ `crt0_banked.s` + `bank.s` १ `.ifdef BANK_W1` ? 㣮 banking port (0xA2 vs 0xE2), 㣮 load-addr (0x4000 vs 0xC000). sprinter-cc prepend' `BANK_W1 = 1` `--memory big`, । `mkexe -B 0x4000`. ਬ `examples/banked_big/`. 2026-05-30. -- [x] **`huge` runtime** (small + banked code W3): merge W2-detect `crt0_small.s` `crt0_banked.s`. 騩 ਬ `examples/banked/` ⥯ ᯮ MEMORY=huge. 2026-05-30. -- [x] **`--debug` 䫠**: prepend `DEBUG_RT = 1` crt0 + `-DDEBUG_RT` sdcc. 뢠 symbol `_w2_self_allocated` (uint8_t) ? runtime diagnostic ஢ W2. 2026-05-30. - -### -襭 libc crt0 - -** `sprinter.lib`** ࠡ⠥ memory mode ? `.rel`-童 relocatable, SDLD dead-code elimination per-member ( 䨪  `gfx_core.rel` ..). Verified hello vs malloc_test १ map-䠩. - -**`gfx.lib` ⤥쭮 ? 㦥**: dead-code elimination 㦥 ࠡ⠥. - -**`libc_banked` (libc bank HOME)** ? ⮬, HOME (16 KB) user- + libc `huge` mode. १ `--codeseg BANK0` 樨 libc; trade-off: trampoline ~30 横 libc-맮. ਣ: ॠ쭠 ணࠬ HOME budget. - -**HW-ᨬ 㫨 ? `sprinter_home.lib` ⤥쭮.** libc 䨧᪨ W3, ⮬ W3: -- `gfx_*` ? `0xC000+` ᫥ swap W3 video page -- `bank_io` (mem_alloc_pages/bank_read/bank_write) ? swap' W3 १ `OUT (0xE2)` -- 騥 ISR ? 뢠 ਩ W3 㣮 - - huge mode 㫨 HOME (W1). 㤥 `libc_banked`, **६** 뤥塞 `sprinter_home.lib` (HOME-only) `sprinter.lib` (bankable). 쭠 奬: -``` -sprinter_home.lib HOME-only: gfx, bank_io, ISR shims -sprinter.lib bankable: printf, malloc, string, conio, stdio, env, ... -sprinter_banked.lib sprinter.lib --codeseg BANK0 ( huge) -``` -ਣ: ॠ `--memory huge` runtime. - -**crt0 ? mode:** -- `crt0.s` ? ⥪騩, **tiny/big**: SP=0xBFFE, argv (W2- 㦥 뤥 DSS). -- `crt0_minimal.s` ? ⥪騩, tiny argv. -- `crt0_small.s` ? **, step 3**: **small/huge**, W2 १ `mem_alloc_pages` gsinit, `0xA2`, ⮬ ⠭ flow. -- `crt0_banked.s` ? ⥪騩, **big**: trampoline-⠡ W3 , CODE W2. -- `crt0_banked_small.s` ? ****: huge = small (W2-alloc) + banked (W3 trampolines). - -sprinter-cc ࠥ crt0 `--memory` mode (ᥩ `--crt0=` override). -- [x] **ࠨ ࠧ ⥪**: 䫠 `sprinter-cc --stack-size BYTES`. Wrapper `heap_top.s` `___sdcc_heap_end = stack_top + 1 - stack_size`, ⤥ .rel per-program. Default ?1278 (heap_top=0xBB00) `runtime/heap_top.s`. 2026-05-30. - -䥩: `sprinter-cc --memory [tiny|small|big|huge|manual] [--memory-manual SPEC] [--stack-size N] foo.c`. `--memory-manual` ⮫쪮 `--memory manual`. - -## Known issues / quirks - -- **ESTEX $46 ENV API**: ? ࠡ⠥. - `DiskSyscalls.txt v1.6` 訡筮 뢠 return-status ? A=0 NOT FOUND, FOUND. 䨪஢ `memory/sprinter_platform.md`. - -## V2 (᫥ ५ v1) - -### Turbo-C-style graphics API (BGI-like) ? **MUST v2** - - `gfx_*` API ஢ **Turbo-C ``** (BGI) MS-DOS. -ணࠬ ਢ訥 Turbo-C ७ ᪨ 1--1. - -** ** ( ᭮ Borland BGI): - -Setup/teardown: -- `initgraph()` / `closegraph()` ? ᥩ `gfx_init`/`gfx_done`, alias -- `getmaxx()` / `getmaxy()` ? GFX_WIDTH-1 / GFX_HEIGHT-1 -- `cleardevice()` ? alias to gfx_clear -- `getgraphmode()` / `setgraphmode()` ? get_videomode/set_videomode - -Color/palette: -- `setcolor(c)`, `getcolor()` ? current draw color -- `setbkcolor(c)`, `getbkcolor()` ? background color -- `setpalette(idx, c)` ? entry -- `getpalette(&info)` ? read all palette - -Primitives ( 㦥 ? BGI- aliases): -- `putpixel(x, y, c)` ? gfx_putpixel -- `getpixel(x, y)` ? 㦭 ॠ (RMW ⭮ ? IN) -- `moveto(x, y)`, `lineto(x, y)`, `linerel(dx, dy)` ? current point + line drawing -- `line(x1, y1, x2, y2)` ? gfx_line -- `rectangle(x1, y1, x2, y2)` ? gfx_rect ( 㣮 API: x1,y1,x2,y2 vs x,y,w,h!) -- `bar(x1, y1, x2, y2)` ? gfx_fill_rect -- `bar3d(x1, y1, x2, y2, depth, topflag)` ? : rect + 3d edges -- `circle(x, y, r)`, `arc(...)`, `ellipse(...)`, `pieslice(...)` ? primitives -- `fillpoly()`, `drawpoly()` ? -- `floodfill(x, y, border_color)` ? - -Text on graphics screen: -- `outtext(s)` / `outtextxy(x, y, s)` ? gfx_text (alias) -- `settextstyle(font, dir, size)` ? multiple bitmap fonts -- `gettextsettings(&info)` -- `textwidth(s)` / `textheight(s)` ? measure - -Image manipulation: -- `imagesize(x1, y1, x2, y2)` ? bytes needed for getimage -- `getimage(x1, y1, x2, y2, buf)` ? save rect to buffer -- `putimage(x, y, buf, op)` ? paste back with COPY_PUT/XOR_PUT/AND_PUT/OR_PUT/NOT_PUT - -Clipping/viewport: -- `setviewport(x1, y1, x2, y2, clip)` ? drawing clip rect -- `getviewsettings(&info)` -- `clearviewport()` -- `setactivepage(p)` / `setvisualpage(p)` ? ਧ (Sprinter 2 screen) - -Line style: -- `setlinestyle(style, pattern, thickness)` ? SOLID_LINE / DOTTED_LINE / etc. -- `getlinesettings(&info)` - -**Acceptance:** ७ ⨯筮 Turbo-C BGI ணࠬ (饩 ᯮ짮 -moveto/lineto/circle/bar/setcolor) ࠡ ⢥ ࠢ. - -**Notes:** -- BGI fonts (TRIPLEX/SANS_SERIF/GOTHIC) ? BIOS font, ⠫ 㦭 - ( bitmap data lib) -- imagesize/getimage/putimage ? ᠬ ॡ game/animation -- Active/visual page ( ਧ) ? Sprinter ন 2 graphics pages, - 㦥 API switching - -. ⠪ `examples/` Turbo C 2.x BGIDEMO reference 㦭. - -### IM2 Interrupt Handlers ? **MUST v2** - -User- ISR १ Z80 IM 2 mode. 㦭 : -- Timer ticks (50 Hz frame counter, ) -- Music playback (AY, COVOX) -- Real-time games (input + game logic + render interrupt-driven) -- Async keyboard / mouse handling - -**Status:** v2. research + design `docs/im2_isr_design.md`. - -**襭 ⥪:** ॠ ⤥ memory mode `--memory im2` -( ⮣ ⮡ 騥 crt0). Detail' design-doc. - -** research'** ( ⥪ `docs/im2_isr_design.md`): -- Vector 0xFF ? frame + keyboard + CBL. Disambiguation ⠬ 0x19 / 0xFE -- Mouse hardware-IRQ 室 ( ⥪饩 ) -- Vector table / ISR / stack W2 (0x8000..0xBFFF) -- DSS ᢮ IM 2 handler ? 㦭 chain' ( / SYSTIME ) - -### 稥 㯭 㭪 v2 - -- [ ] **FILE API rewrite ? buffered streams** ? ⥪ ॠ - `libc/stdio/file.c` provisional unbuffered shim ( fputc/fgetc - = read/write syscall). 㦭 業 buffered ᥬ⨪ - Solid-C: - - ```c - typedef struct { - uint flags; // +0..1 file status flags - int level; // +2..3 empty/fill level of buffer - char *curp; // +4..5 current active pointer - int fd; // +6..7 underlying low-level fd - char *buffer; // +8..9 data transfer buffer - char hold; // +10 ungetc byte if no buffer - short token; // +11..12 reserved - char dummy; // +13 reserved - } FILE; - ``` - - stdin/stdout/stderr ? fd-થ `0 / -1 / -2`. ⥫ - stdout/stderr ࠭ ७: ESTEX OPEN positive - small fd (1, 2, ?) 筮 䠩 ? ᫨ stdout=1, ॠ - fd=1 ⠫ 䨪஬. fd=0 stdin ᭮ - (ESTEX 0 頥). fd । syscall' ? - ᯥਧ 䫠 `_F_CONIN/_F_CONOUT`. - - ਭ-⮪ (stdaux/stdprn) ॠ㥬 ? Sprinter ਭ୮ - API . - - ୠ⨢ ? ॠ third_party/solid-c (sources - `SRC/CLIB/`); ⠬ ⮢ buffered FILE + fopen/fread/fwrite/ - fseek/setvbuf .. ஢ 訬 open/read/write/lseek. - - rewrite deferred issues stdio-review: - - `fwrite` short-write ⠢ `_F_ERROR` - - `fgets(buf, 1, fp)` ? ⠭ "empty string", 㫨 NULL - - `mode_to_flags` ? break-out '+' (cosmetic) - -- [ ] **Audio API** ? AY-3-8910 + COVOX १ 뢠 (ॡ IM2) -- [ ] **ISA-8 slot support** ? ZX-Bus (sound, network, etc.) ? ॡ IM2 + ⥭ port - -## 稥 (v1 backlog, 騥) - -- [x] **#9: text I/O split (Turbo-C style)** ? stdio (puts/printf/putchar) ⥯ fast no-attr १ PCHARS/PUTCHAR. conio (cputs/cprintf/putch) ਬ attr १ textcolor/textbackground/textattr. KEEP_EXIST_ATTR ? conio fallback fast path. Verified hello.exe. . `memory/text_output_api_split.md`. 2026-05-31. -- [x] **Mouse API ** (१ driver, RST 30h) ? 14 㭪権 (init/show/hide/refresh/read/goto/bounds/text_cursor/load_cursor/get_cursor/get/set_sensitivity/video_mode_changed). . `memory/mouse_api.md`. Verified MAME 2026-05-31. Sensitivity = divider ( = ॥). -- [ ] Interrupt handlers ? IM 2 vector table HOME user ISR' -- [ ] প `restore SP on EXIT` ( z88dk +pps) ? ஢ 㦭 -- [ ] CI: ⮬᪨ ᪠ MAME `-aviwrite` screenshot-ࠢ, ⮡ ਬ஢ 室 祫 - -## ⮬ - -- প `` ( SDCC stdlib ? 㦭 ஢ crt0 ᮢ⨬) -- `` १ ESTEX SYSTIME (`$21`) CMOS BIOS-㭪樨 -- ZX Spectrum-ᮢ⨬ ० ⤥ target ( ஢ ᯥ㬮᪨ ணࠬ) -- প ZX-Bus (sound, network, etc.) ? 㦭 ࠩ -- Profile-guided optimization tools (hot/cold detection) 㯭 ணࠬ - -## Linker duplicate-symbol warnings (, 䨫஢) - - ᮧ⥫쭮 overrides' SDCC z80.lib 㭪樨 ᮡ⢥ ᨥ `sprinter.lib`, `sdldz80` `?ASlink-Warning-Definition of public symbol '...' found more than once`. ࢮ । (), ⮬ ४⭮ ? warning ⮫쪮 noise. - -騥 overrides: -- `_puts` ? १ PCHARS+\r\n vs SDCC posix puts -- `___sdcc_heap` ? heap W2 vs SDCC's ⠭ -- `_asctime`, `_localtime` ( 㣨 time) ? `posix_time.c` १ ESTEX SYSTIME vs SDCC's `time.rel` `_RtcRead` - -**饥 襭:** `bin/sprinter-cc` 䨫஢뢠 warning- (warning + 2 follow-up `Library:` ப) 뢮 `sdcc`. १ `-v` (verbose) 뢠. १ awk-pipe. - -** 襭:** -- ३ explicit `--nostdlib` + 筮 ᯨ᮪ 㦭 㫥 z80.lib (string, math, stdlib override') ? warning', ᪨஢ -- : २ `_puts` ? `_puts_sprinter` + alias १ linker flag ( 㢥७ SDCC ন) -- : ⠢ ᥩ (ࠡ祥 benign) ? ਮ - -## TODO: ஢ ॠ쭮 - -- [ ] **Port_Y banking trick** (`docs/part2/SprinterGraphics programming.txt`): - ⢥ত ᫥ `OUT (0x89), Y` 0xC000+0x400*N W3 - ப Y..Y+15 ( ணࠬ஢ ? 16 ப). - Empirical 2026-06-01 MAME 0.283 trick ** ࠡ⠥** ? ᥫ - ᠬ 0xC000+row_width 室 . ᪨ - `docs/samples/plasma2.asm` ⮦ ᯮ banking, ⠭ - Port_Y per row. - : - 1. ॠ쭮 Sprinter - 2. dual-write (`_gfx_putpixel_raw` + ன write `0xD000+x`) - 3. ᫨ ? MAME, issue - ९ - 4. ᫨ ⮦ ? 㬥 , 㤠 㯮 - ⠢ ⥪ ॠ (Port_Y per pixel) - 5. ᫨ banking ࠡ⠥ ? ஢ Port_Y - `_gfx_putpixel_raw` (sentinel out-of-range, . memory/gfx_port_y_banking.md) - - 易 먣 Bresenham (60-pixel ) ? 8? - OUT (0x89) 権, `gfx_fill_rect 320x256` ? 16? . - release v1. - -## GFX: ७ `docs/part2/accelerator_doc.txt` - -᫥ ⥭ ⠫쭮 accelerator doc  ࠢ. - ᯮ ⮫쪮 ਧ⠫/⨪ Fill mode. - -### Quick wins ⥪ primitives - -- [ ] ** SMC `LD A, (var)` block-size**. 㬥  - ࠧ蠥 `LD A, (HL)`, `LD A, (BC)`, `LD A, (DE)` ( `LD A, r`). - 㡥 SMC complexity `gfx_lines.c:hfill_chunk/vfill_chunk` - `gfx_16.c:g16_hfill_chunk`. 饭 ⮫쪮 register-to-register. -- [ ] **஢ block-size**. 㬥 뢠 accel - block size bursts (. `Horizontal_Line_Fill`: ⠭ - size + `LD B,B` ⪫祭, ⮬ Fill mode ᯮ - ࠭ size). `gfx_fill_rect` 100 묨 - ப ? ⠭ size 1 ࠧ, 100. - -### Bank-prefix modes (port 0xE2 bits) - -㬥 뢠 ਠ ࠭ ⠭⭮ 0x50: - -| Bank byte | Effect | -|---|---| -| 0x50 | Normal write ? shadow + | -| 0x54 | "no copy in main shadow RAM" | -| 0x58 | **"FF is transparent"** ? 0xFF write ⠢ background | -| 0x5C | both | - -Bank 0x58 祬 mouse cursor 0xFF-஧筮. - **sprite-blending १ accel block copy**: - -- [ ] **`gfx_set_bank_transparent(on)`** 䫠 `gfx_set_bank` - 롮 0x50/0x58 ᮢ sprite' -- [ ] ᯮ짮 `gfx_blit()` ⮡ 䠪 - transparent sprites १ accel- - -### Block copy mode (sprite blit') - -`LD L,L` (horizontal) `LD A,A` (vertical) ? ० ஢ १ -256- accel memory. blit'. - -- [ ] **`gfx_blit(src_data, x, y, w, h)`** ? ஢ sprite' - (ந ࠧ, १ accel) -- [ ] **`gfx_blit_transparent(src, x, y, w, h)`** ? ᯮ짮 bank 0x58 - -. `Draw_Restangle_Data` accelerator_doc.txt ७. - -### AND / OR / XOR operations १ accel - -㬥 뢠 accel ন ᪨ 樨 -. ਬ: -- XOR ? (뤥 selection UI) -- OR / AND ? masking, alpha-style blending -- . ਬ accelerator_doc.txt: "256 bytes block coding via XOR" - -- [ ] **`gfx_xor_rect`** / **`gfx_or_rect`** / **`gfx_and_rect`** ? - ਬ⨢ ᪨ 権 אַ㣮쭨 -- [ ] **`gfx_invert_rect(x, y, w, h)`** ? alias xor 0xFF - -### Bitmap fonts ࠧ ࠧ஢ - - `gfx_text` / `gfx_putchar` प ࠡ 8?8 ⮬ -(BIOS WIN_GET_ZG 頥 256?8 ). UI / 쭨 -㦭: -- [ ] **`gfx_set_font_size(w, h)`** ? ४ ਭ/ glyph' -- [ ] **`gfx_set_font_data(ptr, w, h, advance)`** ? 㪠⥫ - 짮⥫᪨ + ࠧ -- [ ] প **proportional** (advance != w) ⮢ ? - array advance[256] ਭ glyph' -- [ ] **Big-font ०**: 8?16, 16?16, 16?8 ( 쭨) -- [ ] ⤥ API `gfx_text_ex(x, y, str, font_id)` - font_id 롨ࠥ 㦥 ⮢ -- [ ] **Anti-alias 2-bit ** ( 䮭 / ࠭ / 2- alpha?) - ? far future, smooth UI - -## ⠯ ⨬権 ( ᥩ) - -- **`gfx_line` १ accel ** ? Bresenham |dy| << |dx| ( ) 뤠 runs Y ( X): ᥫ, ᥫ, ᥫ, 蠣 Y, ᥫ, ᥫ... ⠪ run ? ⮢ 㬥 `gfx_hline` ( `vline`). - ᫥: runs 㭪 ; run length, ஬ 룮 accel hline 祬 N?putpixel (overhead accel ~20?s, putpixel ~5?s ? accel 룮 run ? 4-5 px); (dx ? dy) ⠢ Bresenham, ? run-length-based fill. - `gfx_line` orthogonal cases 㦥 १ accel ? ⨬஢ ⮫쪮 . - -- **`gfx_fill_rect` W3-swap ** ? ᥩ ७ `gfx_hline`/`gfx_vline` ᢮ DI/save-W3/restore-W3/EI. ᤥ internal `_fill_rect_inner` ন W3 DI 横; ~20?s ? ⢮ ப/⮫殢 . ਬ ᥬ ਬ⨢. -# MDView ? ᬮ騪 Markdown Sprinter - -**MDView** ? ணࠬ ᬮ 㬥⮢ ଠ *Markdown* -Sprinter ( Z80). 㬥 ࠭ ⤥쭮 W3 -᭮ RAM ணࠬ. - -## - -- 㬥 **128 ** (8 ࠭ EMM 16 ) -- **16 384** ࠭ ப -- ⮬᪨ ७ ᫮ ਭ ࠭ (80 ⮫殢) -- ਧ⠫ ᤢ ப ப ( , ⠡) -- -: 䠩, ப, 業 ப⪨ -- ப ﭨ ६ 㧪 樨 -- প ?? ᪫ ப - -## - -``` -mdview [_䠩.md] -``` - -᫨ 䠩 , 㦠 `README.MD`. - -## ࠢ - -``` - ⢨ - -Up Down ப⪠ ப / -PgUp PgDn ப⪠ ࠭ (30 ப) -Home 砫 㬥 -End 㬥 -Left Right ਧ⠫ ᤢ (⮫쪮 nowrap-ப) -F1 ࠢ -F8 ஢: CP866 ? CP1251 ? KOI8-R ? UTF-8 ? ... -F10 / Esc 室 ணࠬ -``` - -஢ । ⮬᪨ ⨨ (BOM + ⨪ - 4 ); `F8` ४砥 , ᫨ ⥪ 訡. -8- ஢ (CP866/CP1251/KOI8-R) ४ (६ -ᮢ). ன / (ਬ UTF-8) ந ? -ࢮ ४祭 (⪠ 㧠 ᯨ஬), . - -## ⠪ Markdown - -### - -ন ஢ H1?H4. ஢ H5 H6 ⮡ࠦ H4. - - # ࢮ ஢ - ## ண ஢ - ### 쥣 ஢ - #### ⮣ ஢ - -### ⮢ ଠ஢ - -** ⥪** 뤥 묨 񧤮窠: `**⥪**` - -*ᨢ* 뤥 묨 񧤮窠 `*⥪*` ન `_⥪_` - -`஥ ` 砥 묨 窠 - -~~ભ ⥪~~ ? ⨫: `~~⥪~~` - -### 㬥஢ ᯨ᮪ - -થ `-`, `*` `+`: - -- 㭪 ᯨ᪠ -- ன 㭪 ᯨ᪠ -- ⨩ 㭪 筮 ⥪⮬, 室 - 㤥 ७ ᫥ ப ࠭ 㯠 - -### 㬥஢ ᯨ᮪ - -1. -2. ன -3. ⨩ - -### - -> 稭 ᨬ `>`. ᪮쪮 ᫥⥫ -> ப ᪫ ⮬᪨ -> ७ᮬ ᫮. - -### (verbatim) - - 砥 ன 窨. -⥪ ⮡ࠦ ? ? ࠧ Markdown: - -``` -#include -#include - -int main(void) { - puts("Hello, Sprinter!"); - return 0; -} -``` - -### ਧ⠫쭠 - - ᨬ `---`, `***` `___` ⤥쭮 ப: - ---- - -## 孨᪨ ࠪ⨪ - -- **ଠ:** Sprinter, Z80 @ 21 -- **஢:** CP866 / CP1251 / KOI8-R / UTF-8 (⮮।, `F8`) -- **ᨬ ࠧ 䠩:** 128 -- **ᨬ쭮 ᫮ ப :** 16 384 -- **ᨬ쭠 ப ७-:** 255 祥 (. ࠭祭 ) -- ** :** small - - ணࠬ, c⥪, , ? a W1-W2 (32 , 0x4000?0xBFFF). - - 䠩 ? ࠭ EMM, ⮡ࠦ W3 (0xC000?0xFFFF) - -## ࠭祭 - -### ப MD-० ? 255 祥 - -- ࠭ ப ** 255 (ᨬ, ਡ)** -? ⠭ `MAX_CACHE_LINE_LEN`. ⨯: `g_ncells` -`cache_rec_t.len` ? `uint8_t`. ᠥ ப, ⭥ ᥣ -**ਧ⠫쭮 ஫㥬** ப ( ப ⠡, 䫠 -`IF_HSCROLL`), MD-० ࠢ. - -** ந室 ப:** 樨 `gc_put()` -뢠 祩 ᫥ 255- (`if (g_ncells < MAX_CACHE_LINE_LEN)`). - ⮫쪮 255 祥, ⮪ **** ? -஫ ** થ १** 255- 樨 (થ `>` 砥 -? । ?). ९ ? `gc_put` ஢ ࠭. - -: ?255 祥? ? **७७ ਭ**, 室. - ࠧ稢 ஡ ( `TAB_STOP`), 祩 ⠡ -஡ ਭ + ࠬ `` ? ⮬ ⨣ ࠭, 祬 -255 ?? ᨬ. - -> **RAW-० (`F2`) ⮣ ** ? אַ 䠩 -> ⮢, ப 楫 (१ wrap `F3` ਧ. ஫). - -** ** (業, ॠ) ? `uint16_t`: -- `cache_rec_t.len` `uint8_t?uint16_t` ? **஢ 8 ** - (len ꥤ pad-), `idx<<3` . ᯫ⭮. -- `g_ncells` / `g_ncells_at_space` ? `uint16_t` ? 業 **/᪮**: - 16-⭠ 䬥⨪ Z80 祬 `gc_put` (맮 祩) - scan-横. ਥ: **+0.2?0.4 ** + 樨. -- `g_cells[MAX_CACHE_LINE_LEN*2]` near-RAM (W2) ? 業 - ****: `2 ? ` . 510 ; 512 ? +0.5 , 1024 ? +1.5 . - EMM- ⥭ (1 ) ப ﭥ ? 㧪 near. -- `viewport_x` ( `docset_t`) + ⮫ `max_vx` (ᥩ 248) ? `uint16_t`, - ࠭ >255 , ஫ . `widths[]`/`ccx` ⠡/. - - ஬ ? 512?1023: 墠 ᥬ ⨭/⠡栬, - 業 ~+0.3 +0.5?1.5 near-RAM. ** 㬥 `uint16_t`** ? - ⠢ થ १ 255- 樨, ⮡ 뫠 . - -## TODO - -1. **祭 ࠧ 㬥⮢.** 128 : 筮 - ࠧ ࠡ 訬 - ࠭ , ஢ - ࠡ 8- ࠭栬 16. - -: ଠ஢ ⠡ ࠬ; প ஢ -CP866 / CP1251 / KOI8-R / UTF-8 ⮮। ४祭 `F8` -(ன ந , ࢮ ४祭). - -4. **᪮७ ७ਭ.** ப ࠭. ⨬ 横 뢮 - ᨬ १ BIOS WRCHAR ( 뢮, DMA). - ---- - -*MDView v0.2 (c) 2026 ஢ ..* - - ---- - -# Sprinter C Compiler ? v1.0 - -C toolchain for **Sprinter** ? the Z80-based home computer by Peters Plus, running -ESTEX DSS. Host: macOS / Linux. Target: `.EXE` files in SprintEXE format. - -Built on top of **SDCC 4.5** (vendored in `third_party/sdcc/`). This repository adds -everything Sprinter-specific: crt0, linker integration, libc wrappers over ESTEX, -banked-call trampolines, graphics & accelerator API, mouse driver wrappers, and the -`mkexe` utility for producing SprintEXE images. - -## What you get - -* **`bin/sprinter-cc`** ? one-line driver: `sprinter-cc -o foo.exe foo.c` -* **Memory modes**: `tiny`, `small`, `big`, `huge`, `manual` ? see below. -* **stdio + conio**: printf, puts, putchar, getchar, fopen/fread/..., cprintf, cputs, putch, textcolor/textbackground/textattr, gotoxy, kbhit/getch. -* **Graphics**: 320?256?256 and 640?256?16 modes, accelerator-backed primitives (hline / vline / rect / fill_rect / line via Bresenham, plus clear), bitmap-font text in both modes via BIOS character generator. -* **File I/O**: POSIX (`open`/`read`/`write`/`close`/`lseek`/`unlink`/`creat`), FILE\* streams (`fopen`/`fgets`/`fwrite`/...), directory listing (`ffirst`/`fnext`), `chdir`/`getcwd`/`mkdir`/`rmdir`, `stat`/`fstat`. -* **Memory**: 32 KB heap (W2-resident), banking-aware page allocator (`mem_alloc_pages`/`bank_read`/`bank_write`), explicit memory modes for sub-16 KB programs. -* **Mouse**: full Sprinter driver wrapper (14 functions including custom cursor bitmaps). -* **Environment**: `getenv`/`putenv`/`sysenv` over ESTEX `$46`. -* **Time**: `getdatetime`/`setdatetime` + POSIX `time`/`localtime`/`mktime`/`asctime`/`ctime`. -* **Misc**: `errno`/`strerror`/`perror`, `atexit`, `setjmp`/`longjmp`, `sleep`, full argv parsing in crt0. - -## Quick start - -```sh -git clone sprinter-c -cd sprinter-c -make sdcc # one-time: fetch SDCC 4.5 binary (~25 MB) -make all # build mkexe + libsprinter.lib + 27 examples -make floppy # pack everything into mame/v306/IMG/mc.img -cd mame/v306 && ./run_mame.sh # boot Sprinter in MAME -``` - -Compile a single program: - -```sh -cat > hello.c < -int main(void) { puts("Hello, Sprinter!"); return 0; } -EOF - -bin/sprinter-cc -o hello.exe hello.c -``` - -That's it ? `hello.exe` is now a valid SprintEXE you can `RUN HELLO` from the ESTEX shell. - -## Memory modes - -Sprinter's address space is four 16 KB windows (W0 / W1 / W2 / W3). DSS allocates -pages by program size ? small programs get only one page. Pick a memory mode based -on what your program needs: - -| Mode | Code lives in | Banking | Use when | Note | -|---|---|---|---|---| -| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | | -| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | | -| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | | -| `huge` | W1-W2 + W3 banking | yes (W3) | small + extra code modules | | -| `manual` | user-specified | optional | special layouts | Not implemented | - -```sh -sprinter-cc --memory small -o big.exe bigprog.c -sprinter-cc --memory huge -o app.exe main.c --bank 1=engine.c --bank 2=ai.c -``` - -Banked functions are declared with `__banked`: - -```c -void engine_tick(int dt) __banked; // lives in BANK1, automatically swapped -``` - -## Examples (27 total) - -| Example | What it demonstrates | -|---|---| -| `hello` | Hello world with stdio + conio Turbo-C-style colors | -| `argv` | argv parsing in crt0 | -| `cat` | File I/O ? read & print TEST.TXT | -| `seek` | 32-bit lseek over a 100 KB file | -| `ls` | Directory listing via ffirst/fnext | -| `filetest` | FILE\* streams (fopen/fread/...) | -| `stattest` | `stat`/`fstat` on files and directories | -| `errno` | errno / strerror / perror | -| `mem_test` | Page allocator + bank\_read/bank\_write | -| `malloc` | Heap stress test (200+ allocations) | -| `banked` | Banked code in W3 (huge mode) | -| `bankedbg` | Banked code in W1 (big mode) | -| `banklocl` | Bank-local static data and BSS | -| `mouse` | Mouse driver in text mode | -| `gfx_mous` | Mouse with custom bitmap cursor in graphics mode | -| `gfx_demo` | 320?256?256 lines / rects / accelerator | -| `gfx_d16` | 640?256?16 same primitives | -| `gfx_text` | Bitmap-font text on graphics screen | -| `timedir` | Date/time + directory listing | -| `ptime` | POSIX time API | -| `openenv` | open() flags + environment vars | -| `conio` | conio API smoke test | -| `attrprob` | Probe Sprinter text attribute byte layout | -| `strtest` | string.h test (from SDCC's z80.lib) | -| `stdlib` | stdlib.h test (qsort / rand / strtol / etc.) | -| `assrtest` | assert() | -| `rt_test` | Runtime helpers (sleep, setjmp, atexit) | - -## Headers - -Standard: -* `` ? puts / printf / FILE\* + Sprinter-specific dec/hex helpers -* `` ? atoi / atof / malloc / qsort / ... (from SDCC z80.lib) -* `` / `` / `` ? from SDCC z80.lib -* `` ? read / write / close / lseek / unlink -* `` ? open / creat + O\_RDONLY / O\_CREAT / ... -* `` ? errno + error names + strerror -* `` ? stat / fstat -* `` / `` ? from SDCC - -Sprinter-specific: -* `` ? putch / cputs / cprintf, textcolor / textbackground / textattr, kbhit / getch, clrscr, gotoxy, wherex/y -* `` ? gfx_init/done, palette, putpixel, hline/vline/rect/fill_rect/line, text ? for both 320 and 640 modes (gfx_\*16 variants) -* `` ? full 14-function driver wrapper + mouse_cursor_t with bitmap support -* `` ? chdir / getcwd / mkdir / rmdir / ffirst / fnext / ffblk -* `` ? getdatetime / setdatetime + POSIX time / localtime / etc. -* `` ? raw ports, ESTEX/BIOS function numbers, env API -* `` ? exit / \_exit / atexit -* `` ? mem\_alloc\_pages / mem\_free\_block / bank\_read / bank\_write -* `` ? Solid-C compatibility layer (aliases + BOOL/WORD/uint types) - -## Toolchain commands - -```sh -make all # build mkexe + lib + every example -make floppy # repack mame/v306/IMG/mc.img with all .exe files -make check # 17 mkexe unit-tests -make clean # remove all build artefacts -make sdcc # one-time: fetch SDCC 4.5 binary -``` - -### sprinter-cc options - -``` -sprinter-cc -o foo.exe foo.c [more.c ...] [options] - - --memory MODE tiny | small | big | huge | manual (default: tiny) - --memory-manual SPEC explicit placement (CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3) - --stack-size N bytes reserved for the stack (default ~1278) - --crt0=TYPE default | minimal | banked | small - --bank N=FILE.c compile FILE.c into bank N (repeatable, max 15) - --debug enable runtime diagnostics (defines DEBUG_RT) - -I PATH extra include path - -L 0xADDR / -E / -S override load / entry / stack addresses - -Wl FLAG pass FLAG to sdldz80 - --mkexe FLAG pass FLAG to mkexe (e.g. --mkexe -p --mkexe 0 for bank padding) - -v verbose -``` - -## Status - -What works in v1.0: -* Compile / link / pack to SprintEXE ? verified on all 27 examples -* Four memory modes (tiny / small / big / huge) -* Graphics (both modes) with accelerator -* Mouse (text + graphics cursor) -* File I/O, directories, environment, time -* All headers listed above - -Deferred to v2.0 (see `docs/TODO.md`): -* **Turbo-C-style BGI graphics API** ? `initgraph` / `setcolor` / `circle` / - `getimage` / `putimage` / etc. on top of our `gfx_*` primitives -* Remaining Solid-C compatibility gaps (Phase 2/3) ? see `docs/solid_c_compatibility.md` -* Manual memory mode -* Rewrite FILE\* stream API (current implementation is very primitive and doesn't use buffers) - -Deferred to v3.0: -* **IM2 interrupt handlers** ? research complete (`docs/im2_isr_design.md`), - implementation scheduled for v3 -* **Audio API** (AY-3-8910 + COVOX) ? requires IM2 -* **ISA-8 slot drivers** ? requires IM2 (???) - -## Documentation - -* `docs/TODO.md` ? roadmap and open work items -* `docs/solid_c_compatibility.md` ? gap analysis vs Solid-C 2004 -* `docs/im2_isr_design.md` ? interrupt handler design (v2) -* `docs/converted/` ? source documentation (ESTEX, BIOS, architecture) - converted to plain text for `grep` -* `docs/reference/`, `docs/samples/`, `docs/memory management/` ? original - Russian docs and code samples from Peters Plus - -## Repository layout - -``` -bin/sprinter-cc one-line compiler driver (bash) -toolchain/mkexe/ host-side tool: .ihx -> .exe SprintEXE -toolchain/check_banks.py post-link bank size enforcer -runtime/ crt0 variants (default, minimal, small, banked) - bank trampolines, heap, heap_top -libc/include/ headers -libc/io|stdio|mem|gfx/ C and asm sources for libsprinter.lib -lib/ Makefile that archives libsprinter.lib via sdar -examples/ 27 example programs -mame/v306/ MAME binary + Sprinter ROM/HDD images + floppy script -third_party/sdcc/ vendored SDCC 4.5 (fetched via `make sdcc`) -third_party/solid-c/ reference: original Sprinter native C (for compat target) -docs/ documentation -``` - -## License - -This repository contains: -* Original code in `bin/`, `toolchain/`, `runtime/`, `libc/`, `lib/`, `examples/` ? - MIT-licensed. -* `third_party/sdcc/` ? SDCC 4.5 under GPLv2 with linking exception - (see `third_party/sdcc/COPYING.txt`) -* `third_party/solid-c/` ? original Sprinter Solid C, used only as a reference - -## Credits - -* **Sprinter / Peters Plus** ? , ਩ ਭ and the original team -* **SDCC** ? for the underlying Z80 compiler -* **MAME** ? for the Sprinter Sp2000 emulation - ---- - -For questions / patches: see CONTRIBUTING.md (TBD) or open an issue. - ---- - -# TODO / Roadmap - - 浪 뢠 ਮ.  ॠ ணࠬ ? ਮ ᬥ. - -## ⠯ 5 ? malloc / free + banking-aware page allocator ? - -- [x] SDCC's `malloc`/`free` + `runtime/heap.s` ( library heap.rel, 14000- heap 2) -- [x] `libc/mem/mem_alloc.c` ? page allocator: `mem_alloc_pages`/`mem_free_block`/`mem_get_page`/`mem_info` १ ESTEX `$3C/$3D/$3E` + BIOS `$C4` -- [x] `libc/mem/bank_io.c` ? HOME-१ `bank_read`/`bank_write`/`bank_load_byte`/`bank_store_byte` ᢮ W3 -- [x] `examples/malloc_test/` ? ஢ઠ SDCC's malloc (~210 64- allocations १ heap) -- [x] `examples/mem_test/` ? ஢ઠ page allocator: 3 ࠭, ࠧ १ bank_write, 䨪 १ bank_read - -## ⠯ 6 ? argv parsing + sprinter-cc wrapper ? - -- [x] crt0 ESTEX command-line IX-prefix (inline asm `runtime/crt0.s`) -- [x] Strip leading CP/M-style space (DSS quirk) -- [x] । `argc`/`argv` main() १ HL/DE (SDCC __sdcccall(1) ABI) -- [x] argv[0] = basename .EXE १ ESTEX APPINFO ($47 subfn 2) -- [x] `runtime/crt0_minimal.s` ? opt-out 祭 쪨 ணࠬ -- [x] `runtime/crt0_banked.s` ? ⥯ ⮦ argv (parse_argv + get_progname ᪮஢ crt0.s; 㤥 factored argv.s libsprinter.lib) -- [x] Bash-⪠ `bin/sprinter-cc`: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権: `--memory`, `--memory-manual`, `--stack-size`, `--crt0=`, `--bank N=FILE.c`, `--debug`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` - -## ⠯ 8 ? 䨪 (320?256?256 + 640?256?16 + accel + bitmap font) ? - -- [x] **8a** Graphics core: `gfx_init`/`gfx_done`/`gfx_clear`/`gfx_putpixel`/`gfx_pal_load`/`gfx_pal_set` (libc/gfx/gfx_core.c). १ BIOS PIC_SET_PAL ($A4). Verified 320?256?256. -- [x] **8b** /אַ㣮쭨/fill १ accelerator (libc/gfx/gfx_lines.c): `gfx_hline`/`gfx_vline` १ accel Fill (LD C,C / LD E,E + SMC block-size), `gfx_rect`/`gfx_fill_rect` heuristic 롮஬ ਥ樨 (h/v bursts count), `gfx_line` Bresenham . `gfx_clear` ⮦ ९ᠭ column-major accel (~4? ॥). -- [x] **8c** 640?256?16 mode (libc/gfx/gfx_16.c): `gfx_*16` API, HIGH nibble = LEFT pixel (㬥 misleading), per-row RMW vline ( = 2 ਧ⠫ ᥫ). -- [x] **8d** Bitmap font + gfx_text (libc/gfx/gfx_text.c): १ BIOS WIN_GET_ZG ($B8), interleaved layout `font[row*256+char]`, `gfx_text`/`gfx_putchar` 320 mode, `gfx_text16`/`gfx_putchar16` 640 mode pair-table lookup. - -. memory/sprinter_graphics.md, sprinter_accelerator.md, sprinter_graphics_16.md, sprinter_font_format.md. - - ( ): -- [ ] -quad 640: per-cell (mode 0x82 ࠧ蠥 1 4 per 16?8 cell) ? १ אַ area-ᠭ ࠭ 0x0300..0x039F - -## Auto-banking (. `memory/banking_roadmap.md` ⠫) - -Phase 1 ? file-level bin-packing ? ॠ뢠 ஥ ~30 KB . - -- [ ] `toolchain/auto_bank.py`: - - ࠧ `.rel`-䠩 ( .map ᫥ dry-run link') - - First-fit-decreasing bin-packing - - `#pragma codeseg BANKn` manual override - - ५뢠 묨 `-Wl-b_BANKn=...` ࠬࠬ - - ⠥ । - -Phase 2-5: incremental rebalance, declarative `banks.toml`, function-level, call-graph-aware. 쪮 ᫨/ . - -## Bank-local static data (mutable data ⮬ ) ? ? - -- [x] ਬ `examples/bank_local_data/` ? 㭪 BANK1 ᢮ writable BSS array + const table + malloc- -- [x] `mkexe -p 0` 㫥 padding (BSS-storage 㧪) -- [x] ᪨ 楯: `--codeseg BANK1 --constseg BANK1 --dataseg BANK1` bank1.c + `-Wl-b_BANK1=0x1C000` . **`--dataseg BANK1` ** ? ࠭ ⭮ - trampoline bug ᪨஢ १. -- [x] ** 䨪 trampoline'a runtime/bank.s** ? `pop af; out (n), a` ਫ A ? banked-㭪樨 騥 uint8_t 頫 . `pop bc; out (c), b` ࠭ A. -- [x] **malloc banked-㭪樨 ࠡ⠥ ஧筮** ? heap W2 (HOME), W2 ᢠ trampoline', pointer  ⥪. . memory/bank_local_data_pattern.md. -- [x] 㬥 memory: `memory/bank_local_data_pattern.md` ( 楯 + malloc + nuances), `memory/sdcc_banking.md` (trampoline fix) -- [ ] 樮쭮 ? `check_banks.py` ⮡ 뢠 ࠧ size = code + const + bss per bank (cosmetic) - -祬: 㫥 訬 private state (level loader, audio engine, scene data). W2 heap , ⨪ . - -## ᪠ solid-c (⨢ Sprinter C ? `third_party/solid-c/`) - -᫥ solid-c'᪮ libc (. `memory/solid_c_findings.md`)  ⮢ ᫥ 㭪権. ਮ **᮪** : - -### High-priority gaps ( ஢, 짠) -- [x] **`errno` + `strerror`/`perror`** ? ⠡窠 32 訡 (libc/io/errno.c) -- [x] **७ `open()`** O_CREAT/O_TRUNC/O_APPEND/O_EXCL state machine -- [x] **`atexit`** ? 8-callback LIFO + `exit()` + `_exit()` (libc/io/atexit.c) -- [x] **`setjmp`/`longjmp`** ? 6- jmp_buf={sp,ix,pc} (libc/io/setjmp.c) -- [x] **`sleep(seconds)`** ? 50Hz halt-loop (libc/io/sleep.c) -- [x] **ESTEX ENV API** ($46, getenv/putenv) ? libc/io/env.c. 竨 doc-bug: ॠ쭮 A=0 NOT FOUND - -### Medium-priority (㦭 shell-like ⨫) -- [ ] **Mouse driver** ? `rst $30h`, 17 㭪権. **砫 ࠡ⠥ MAME**. -- [x] **`ffirst`/`fnext` + ffblk_t struct** directory listing ? ॠ, demo: ls.exe -- [x] **`getdatetime`/`setdatetime`** १ ESTEX $21/$22 ? libc/io/time.c, demo: time_dir_test -- [x] **`chdir`/`getcwd`/`mkdir`/`rmdir`** ? wrappers ESTEX $1B-$1E ? libc/io/fsdir.c -- [x] **conio: `kbhit`/`getch`/`getche`/`cputs`/`clrscr`/`gotoxy`** ? ॠ -- [x] **conio extras**: `wherex`/`wherey` ($53), `wrchar`/`rdchar` ($58/$57), `textmode_get/set` ($50/$51), `clrscr_attr` ($56) + COLOR macros - -### Low-priority ? ? FILE* stack - -- [x] ** unbuffered FILE\*** ? `libc/stdio/file.c` + `libc/include/stdio.h`. fopen/fclose/fputs/fgets/fread/fwrite/fseek/ftell/rewind/feof/ferror/clearerr/fflush + stdin/stdout/stderr pseudo-streams. . `memory/file_star_design.md` `examples/filetest`. -- [ ] fprintf / fscanf ? 㦭 printf-१-callback machinery. 짮⥫ `sprintf(buf, ...) + fputs(buf, fp)`. -- [ ] 樮 buffered mode (setvbuf, line/block buffering) ? ᫨ - . - -### POSIX time API ? ? -- [x] `libc/io/posix_time.c` ? time/localtime/gmtime/mktime/asctime/ctime getdatetime. SDCC's time.rel ( override _RtcRead). . `examples/ptime`. - -### sys/stat ? ? -- [x] `libc/io/stat.c` ? POSIX stat/fstat. ਤ open+fstat 䠩, ffirst+iter ( "."/".."). . `examples/stattest` `memory/estex_ffirst_dotdot.md`. - -### assert ? ? (ᯮ㥬 SDCC's __assert १ fallback include path) - -## libc/stdlib ? ? 㦭 (. memory/sdcc_stdlib_works.md) - -஢७ १ `examples/stdlib_test/`: SDCC z80.lib ᮤন ࠡ騥 ॠ樨: -- `atoi/atol/atof, strtol/strtoul, rand/srand, qsort/bsearch, abs/labs, div/ldiv` -- `` (memchr/cmp/set/cpy, strcat/cmp/cpy/len/chr/spn/etc.) -- `` (toupper/tolower) -- `` (sinf/cosf/sqrtf/etc.) - - ⮬᪨ ﭥ z80.lib 㦭. ** ९뢠**. - - Sprinter-specific 易⥫ 㫨 : atexit, env, errno, setjmp, putchar/puts/getchar, conio, fsdir, time, mouse, open/read/lseek/close. - -## Build-system: libsprinter.lib + sprinter-cc ? ? - -- [x] `lib/Makefile` ? ᮡࠥ libc/*.c `.rel`, 娢 १ sdar `lib/sprinter.lib` -- [x] 砥 runtime/bank.s runtime/heap.s (auto-pulled __banked/malloc) -- [x] `bin/sprinter-cc` ? bash-wrapper: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権 `--crt0=default|minimal|banked`, `--bank N=FILE.c`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` -- [x] `examples/hello_sccc/` ? : `hello.c` ᮡࠥ shell-맮, ࠧ ᮢ Makefile (925 ) -- [x] Split `putchar.c` ? `putchar.c` + `puts.c` per-function granularity (puts override SDCC's z80.lib version) -- [x] `make all` (ᨬ `lib` । `examples`) - - 襭 (樮쭮): -- [ ] ஢ ⠫ examples sprinter-cc Makefile (ᬥ⨪) -- [ ] 쭥 libc/*.c per-function ( ⥪ granularity 㦥 㦭 ࠧ ? .rel 楫, 設⢠ 䠩 㭪) - -## ⠯ 9 ? memory modes sprinter-cc - -DSS 뤥 ࠭ ࠧ ਫ: < 16 KB ? ࠭, ⠫ 砥 ?࠭ #FF? (read=0xFF, write ). - ⮣ CODE--W1 + DATA--W2 쪮 ணࠬ . . [memory/sprinter_memory_modes.md](../../.claude/projects/-Volumes-SAM8-Projects-DIY-Z80-Sprinter-C-Compiler/memory/sprinter_memory_modes.md). - -- [x] **`tiny`**: (CODE+DATA+⥪) W2. Default. Verified hello/argv/conio/malloc/file/etc. -- [x] **`--memory MODE` 䫠 sprinter-cc**: parser + per-mode 䮫 CODE_LOC/DATA_LOC, override १  `--code-loc`/`--data-loc`. tiny ࠡ⠥; small/big/huge warning' (runtime ⮢). 2026-05-30. -- [x] **`--memory-manual SPEC`**: `CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3`. 2026-05-30. -- [x] **`small` runtime**: `runtime/crt0_small.s` ᯮ ESTEX `$3D GETMEM` + `$3A SETWIN2` ⮡ 뤥 W2-࠭ gsinit. **** BIOS `$C4` ? ⥪ ⮬ ⠯ W1 (boot_stack HOME), BIOS ॡ ⥪ W2. ᫥ SP ४砥 0xBFFE, ⠭ flow. 2026-05-30, verified hello.exe. -- [x] **`small` auto-detect >16 KB ணࠬ**: `crt0_small.s` ⠥ `0xC2` (⥪ ࠭ W2 ? `0xA2`! W1). ᫨ 0xFF ? 뤥 page; DSS 㦥 ᤥ (ணࠬ ᠬ 뫥 W2). crt0 뢠 0..30 KB. mkexe ⠪ ࠧ蠥 HOME span W1+W2 (0x4000..0xBFFF). Verified hello: small (5 KB 䠩, SETWIN2 path) + 32 KB 䠩 (auto-skip). 2026-05-30. -- [x] **`big` runtime** (tiny + banked code W1): ࠬਧ `crt0_banked.s` + `bank.s` १ `.ifdef BANK_W1` ? 㣮 banking port (0xA2 vs 0xE2), 㣮 load-addr (0x4000 vs 0xC000). sprinter-cc prepend' `BANK_W1 = 1` `--memory big`, । `mkexe -B 0x4000`. ਬ `examples/banked_big/`. 2026-05-30. -- [x] **`huge` runtime** (small + banked code W3): merge W2-detect `crt0_small.s` `crt0_banked.s`. 騩 ਬ `examples/banked/` ⥯ ᯮ MEMORY=huge. 2026-05-30. -- [x] **`--debug` 䫠**: prepend `DEBUG_RT = 1` crt0 + `-DDEBUG_RT` sdcc. 뢠 symbol `_w2_self_allocated` (uint8_t) ? runtime diagnostic ஢ W2. 2026-05-30. - -### -襭 libc crt0 - -** `sprinter.lib`** ࠡ⠥ memory mode ? `.rel`-童 relocatable, SDLD dead-code elimination per-member ( 䨪  `gfx_core.rel` ..). Verified hello vs malloc_test १ map-䠩. - -**`gfx.lib` ⤥쭮 ? 㦥**: dead-code elimination 㦥 ࠡ⠥. - -**`libc_banked` (libc bank HOME)** ? ⮬, HOME (16 KB) user- + libc `huge` mode. १ `--codeseg BANK0` 樨 libc; trade-off: trampoline ~30 横 libc-맮. ਣ: ॠ쭠 ணࠬ HOME budget. - -**HW-ᨬ 㫨 ? `sprinter_home.lib` ⤥쭮.** libc 䨧᪨ W3, ⮬ W3: -- `gfx_*` ? `0xC000+` ᫥ swap W3 video page -- `bank_io` (mem_alloc_pages/bank_read/bank_write) ? swap' W3 १ `OUT (0xE2)` -- 騥 ISR ? 뢠 ਩ W3 㣮 - - huge mode 㫨 HOME (W1). 㤥 `libc_banked`, **६** 뤥塞 `sprinter_home.lib` (HOME-only) `sprinter.lib` (bankable). 쭠 奬: -``` -sprinter_home.lib HOME-only: gfx, bank_io, ISR shims -sprinter.lib bankable: printf, malloc, string, conio, stdio, env, ... -sprinter_banked.lib sprinter.lib --codeseg BANK0 ( huge) -``` -ਣ: ॠ `--memory huge` runtime. - -**crt0 ? mode:** -- `crt0.s` ? ⥪騩, **tiny/big**: SP=0xBFFE, argv (W2- 㦥 뤥 DSS). -- `crt0_minimal.s` ? ⥪騩, tiny argv. -- `crt0_small.s` ? **, step 3**: **small/huge**, W2 १ `mem_alloc_pages` gsinit, `0xA2`, ⮬ ⠭ flow. -- `crt0_banked.s` ? ⥪騩, **big**: trampoline-⠡ W3 , CODE W2. -- `crt0_banked_small.s` ? ****: huge = small (W2-alloc) + banked (W3 trampolines). - -sprinter-cc ࠥ crt0 `--memory` mode (ᥩ `--crt0=` override). -- [x] **ࠨ ࠧ ⥪**: 䫠 `sprinter-cc --stack-size BYTES`. Wrapper `heap_top.s` `___sdcc_heap_end = stack_top + 1 - stack_size`, ⤥ .rel per-program. Default ?1278 (heap_top=0xBB00) `runtime/heap_top.s`. 2026-05-30. - -䥩: `sprinter-cc --memory [tiny|small|big|huge|manual] [--memory-manual SPEC] [--stack-size N] foo.c`. `--memory-manual` ⮫쪮 `--memory manual`. - -## Known issues / quirks - -- **ESTEX $46 ENV API**: ? ࠡ⠥. - `DiskSyscalls.txt v1.6` 訡筮 뢠 return-status ? A=0 NOT FOUND, FOUND. 䨪஢ `memory/sprinter_platform.md`. - -## V2 (᫥ ५ v1) - -### Turbo-C-style graphics API (BGI-like) ? **MUST v2** - - `gfx_*` API ஢ **Turbo-C ``** (BGI) MS-DOS. -ணࠬ ਢ訥 Turbo-C ७ ᪨ 1--1. - -** ** ( ᭮ Borland BGI): - -Setup/teardown: -- `initgraph()` / `closegraph()` ? ᥩ `gfx_init`/`gfx_done`, alias -- `getmaxx()` / `getmaxy()` ? GFX_WIDTH-1 / GFX_HEIGHT-1 -- `cleardevice()` ? alias to gfx_clear -- `getgraphmode()` / `setgraphmode()` ? get_videomode/set_videomode - -Color/palette: -- `setcolor(c)`, `getcolor()` ? current draw color -- `setbkcolor(c)`, `getbkcolor()` ? background color -- `setpalette(idx, c)` ? entry -- `getpalette(&info)` ? read all palette - -Primitives ( 㦥 ? BGI- aliases): -- `putpixel(x, y, c)` ? gfx_putpixel -- `getpixel(x, y)` ? 㦭 ॠ (RMW ⭮ ? IN) -- `moveto(x, y)`, `lineto(x, y)`, `linerel(dx, dy)` ? current point + line drawing -- `line(x1, y1, x2, y2)` ? gfx_line -- `rectangle(x1, y1, x2, y2)` ? gfx_rect ( 㣮 API: x1,y1,x2,y2 vs x,y,w,h!) -- `bar(x1, y1, x2, y2)` ? gfx_fill_rect -- `bar3d(x1, y1, x2, y2, depth, topflag)` ? : rect + 3d edges -- `circle(x, y, r)`, `arc(...)`, `ellipse(...)`, `pieslice(...)` ? primitives -- `fillpoly()`, `drawpoly()` ? -- `floodfill(x, y, border_color)` ? - -Text on graphics screen: -- `outtext(s)` / `outtextxy(x, y, s)` ? gfx_text (alias) -- `settextstyle(font, dir, size)` ? multiple bitmap fonts -- `gettextsettings(&info)` -- `textwidth(s)` / `textheight(s)` ? measure - -Image manipulation: -- `imagesize(x1, y1, x2, y2)` ? bytes needed for getimage -- `getimage(x1, y1, x2, y2, buf)` ? save rect to buffer -- `putimage(x, y, buf, op)` ? paste back with COPY_PUT/XOR_PUT/AND_PUT/OR_PUT/NOT_PUT - -Clipping/viewport: -- `setviewport(x1, y1, x2, y2, clip)` ? drawing clip rect -- `getviewsettings(&info)` -- `clearviewport()` -- `setactivepage(p)` / `setvisualpage(p)` ? ਧ (Sprinter 2 screen) - -Line style: -- `setlinestyle(style, pattern, thickness)` ? SOLID_LINE / DOTTED_LINE / etc. -- `getlinesettings(&info)` - -**Acceptance:** ७ ⨯筮 Turbo-C BGI ணࠬ (饩 ᯮ짮 -moveto/lineto/circle/bar/setcolor) ࠡ ⢥ ࠢ. - -**Notes:** -- BGI fonts (TRIPLEX/SANS_SERIF/GOTHIC) ? BIOS font, ⠫ 㦭 - ( bitmap data lib) -- imagesize/getimage/putimage ? ᠬ ॡ game/animation -- Active/visual page ( ਧ) ? Sprinter ন 2 graphics pages, - 㦥 API switching - -. ⠪ `examples/` Turbo C 2.x BGIDEMO reference 㦭. - -### IM2 Interrupt Handlers ? **MUST v2** - -User- ISR १ Z80 IM 2 mode. 㦭 : -- Timer ticks (50 Hz frame counter, ) -- Music playback (AY, COVOX) -- Real-time games (input + game logic + render interrupt-driven) -- Async keyboard / mouse handling - -**Status:** v2. research + design `docs/im2_isr_design.md`. - -**襭 ⥪:** ॠ ⤥ memory mode `--memory im2` -( ⮣ ⮡ 騥 crt0). Detail' design-doc. - -** research'** ( ⥪ `docs/im2_isr_design.md`): -- Vector 0xFF ? frame + keyboard + CBL. Disambiguation ⠬ 0x19 / 0xFE -- Mouse hardware-IRQ 室 ( ⥪饩 ) -- Vector table / ISR / stack W2 (0x8000..0xBFFF) -- DSS ᢮ IM 2 handler ? 㦭 chain' ( / SYSTIME ) - -### 稥 㯭 㭪 v2 - -- [ ] **FILE API rewrite ? buffered streams** ? ⥪ ॠ - `libc/stdio/file.c` provisional unbuffered shim ( fputc/fgetc - = read/write syscall). 㦭 業 buffered ᥬ⨪ - Solid-C: - - ```c - typedef struct { - uint flags; // +0..1 file status flags - int level; // +2..3 empty/fill level of buffer - char *curp; // +4..5 current active pointer - int fd; // +6..7 underlying low-level fd - char *buffer; // +8..9 data transfer buffer - char hold; // +10 ungetc byte if no buffer - short token; // +11..12 reserved - char dummy; // +13 reserved - } FILE; - ``` - - stdin/stdout/stderr ? fd-થ `0 / -1 / -2`. ⥫ - stdout/stderr ࠭ ७: ESTEX OPEN positive - small fd (1, 2, ?) 筮 䠩 ? ᫨ stdout=1, ॠ - fd=1 ⠫ 䨪஬. fd=0 stdin ᭮ - (ESTEX 0 頥). fd । syscall' ? - ᯥਧ 䫠 `_F_CONIN/_F_CONOUT`. - - ਭ-⮪ (stdaux/stdprn) ॠ㥬 ? Sprinter ਭ୮ - API . - - ୠ⨢ ? ॠ third_party/solid-c (sources - `SRC/CLIB/`); ⠬ ⮢ buffered FILE + fopen/fread/fwrite/ - fseek/setvbuf .. ஢ 訬 open/read/write/lseek. - - rewrite deferred issues stdio-review: - - `fwrite` short-write ⠢ `_F_ERROR` - - `fgets(buf, 1, fp)` ? ⠭ "empty string", 㫨 NULL - - `mode_to_flags` ? break-out '+' (cosmetic) - -- [ ] **Audio API** ? AY-3-8910 + COVOX १ 뢠 (ॡ IM2) -- [ ] **ISA-8 slot support** ? ZX-Bus (sound, network, etc.) ? ॡ IM2 + ⥭ port - -## 稥 (v1 backlog, 騥) - -- [x] **#9: text I/O split (Turbo-C style)** ? stdio (puts/printf/putchar) ⥯ fast no-attr १ PCHARS/PUTCHAR. conio (cputs/cprintf/putch) ਬ attr १ textcolor/textbackground/textattr. KEEP_EXIST_ATTR ? conio fallback fast path. Verified hello.exe. . `memory/text_output_api_split.md`. 2026-05-31. -- [x] **Mouse API ** (१ driver, RST 30h) ? 14 㭪権 (init/show/hide/refresh/read/goto/bounds/text_cursor/load_cursor/get_cursor/get/set_sensitivity/video_mode_changed). . `memory/mouse_api.md`. Verified MAME 2026-05-31. Sensitivity = divider ( = ॥). -- [ ] Interrupt handlers ? IM 2 vector table HOME user ISR' -- [ ] প `restore SP on EXIT` ( z88dk +pps) ? ஢ 㦭 -- [ ] CI: ⮬᪨ ᪠ MAME `-aviwrite` screenshot-ࠢ, ⮡ ਬ஢ 室 祫 - -## ⮬ - -- প `` ( SDCC stdlib ? 㦭 ஢ crt0 ᮢ⨬) -- `` १ ESTEX SYSTIME (`$21`) CMOS BIOS-㭪樨 -- ZX Spectrum-ᮢ⨬ ० ⤥ target ( ஢ ᯥ㬮᪨ ணࠬ) -- প ZX-Bus (sound, network, etc.) ? 㦭 ࠩ -- Profile-guided optimization tools (hot/cold detection) 㯭 ணࠬ - -## Linker duplicate-symbol warnings (, 䨫஢) - - ᮧ⥫쭮 overrides' SDCC z80.lib 㭪樨 ᮡ⢥ ᨥ `sprinter.lib`, `sdldz80` `?ASlink-Warning-Definition of public symbol '...' found more than once`. ࢮ । (), ⮬ ४⭮ ? warning ⮫쪮 noise. - -騥 overrides: -- `_puts` ? १ PCHARS+\r\n vs SDCC posix puts -- `___sdcc_heap` ? heap W2 vs SDCC's ⠭ -- `_asctime`, `_localtime` ( 㣨 time) ? `posix_time.c` १ ESTEX SYSTIME vs SDCC's `time.rel` `_RtcRead` - -**饥 襭:** `bin/sprinter-cc` 䨫஢뢠 warning- (warning + 2 follow-up `Library:` ப) 뢮 `sdcc`. १ `-v` (verbose) 뢠. १ awk-pipe. - -** 襭:** -- ३ explicit `--nostdlib` + 筮 ᯨ᮪ 㦭 㫥 z80.lib (string, math, stdlib override') ? warning', ᪨஢ -- : २ `_puts` ? `_puts_sprinter` + alias १ linker flag ( 㢥७ SDCC ন) -- : ⠢ ᥩ (ࠡ祥 benign) ? ਮ - -## TODO: ஢ ॠ쭮 - -- [ ] **Port_Y banking trick** (`docs/part2/SprinterGraphics programming.txt`): - ⢥ত ᫥ `OUT (0x89), Y` 0xC000+0x400*N W3 - ப Y..Y+15 ( ணࠬ஢ ? 16 ப). - Empirical 2026-06-01 MAME 0.283 trick ** ࠡ⠥** ? ᥫ - ᠬ 0xC000+row_width 室 . ᪨ - `docs/samples/plasma2.asm` ⮦ ᯮ banking, ⠭ - Port_Y per row. - : - 1. ॠ쭮 Sprinter - 2. dual-write (`_gfx_putpixel_raw` + ன write `0xD000+x`) - 3. ᫨ ? MAME, issue - ९ - 4. ᫨ ⮦ ? 㬥 , 㤠 㯮 - ⠢ ⥪ ॠ (Port_Y per pixel) - 5. ᫨ banking ࠡ⠥ ? ஢ Port_Y - `_gfx_putpixel_raw` (sentinel out-of-range, . memory/gfx_port_y_banking.md) - - 易 먣 Bresenham (60-pixel ) ? 8? - OUT (0x89) 権, `gfx_fill_rect 320x256` ? 16? . - release v1. - -## GFX: ७ `docs/part2/accelerator_doc.txt` - -᫥ ⥭ ⠫쭮 accelerator doc  ࠢ. - ᯮ ⮫쪮 ਧ⠫/⨪ Fill mode. - -### Quick wins ⥪ primitives - -- [ ] ** SMC `LD A, (var)` block-size**. 㬥  - ࠧ蠥 `LD A, (HL)`, `LD A, (BC)`, `LD A, (DE)` ( `LD A, r`). - 㡥 SMC complexity `gfx_lines.c:hfill_chunk/vfill_chunk` - `gfx_16.c:g16_hfill_chunk`. 饭 ⮫쪮 register-to-register. -- [ ] **஢ block-size**. 㬥 뢠 accel - block size bursts (. `Horizontal_Line_Fill`: ⠭ - size + `LD B,B` ⪫祭, ⮬ Fill mode ᯮ - ࠭ size). `gfx_fill_rect` 100 묨 - ப ? ⠭ size 1 ࠧ, 100. - -### Bank-prefix modes (port 0xE2 bits) - -㬥 뢠 ਠ ࠭ ⠭⭮ 0x50: - -| Bank byte | Effect | -|---|---| -| 0x50 | Normal write ? shadow + | -| 0x54 | "no copy in main shadow RAM" | -| 0x58 | **"FF is transparent"** ? 0xFF write ⠢ background | -| 0x5C | both | - -Bank 0x58 祬 mouse cursor 0xFF-஧筮. - **sprite-blending १ accel block copy**: - -- [ ] **`gfx_set_bank_transparent(on)`** 䫠 `gfx_set_bank` - 롮 0x50/0x58 ᮢ sprite' -- [ ] ᯮ짮 `gfx_blit()` ⮡ 䠪 - transparent sprites १ accel- - -### Block copy mode (sprite blit') - -`LD L,L` (horizontal) `LD A,A` (vertical) ? ० ஢ १ -256- accel memory. blit'. - -- [ ] **`gfx_blit(src_data, x, y, w, h)`** ? ஢ sprite' - (ந ࠧ, १ accel) -- [ ] **`gfx_blit_transparent(src, x, y, w, h)`** ? ᯮ짮 bank 0x58 - -. `Draw_Restangle_Data` accelerator_doc.txt ७. - -### AND / OR / XOR operations १ accel - -㬥 뢠 accel ন ᪨ 樨 -. ਬ: -- XOR ? (뤥 selection UI) -- OR / AND ? masking, alpha-style blending -- . ਬ accelerator_doc.txt: "256 bytes block coding via XOR" - -- [ ] **`gfx_xor_rect`** / **`gfx_or_rect`** / **`gfx_and_rect`** ? - ਬ⨢ ᪨ 権 אַ㣮쭨 -- [ ] **`gfx_invert_rect(x, y, w, h)`** ? alias xor 0xFF - -### Bitmap fonts ࠧ ࠧ஢ - - `gfx_text` / `gfx_putchar` प ࠡ 8?8 ⮬ -(BIOS WIN_GET_ZG 頥 256?8 ). UI / 쭨 -㦭: -- [ ] **`gfx_set_font_size(w, h)`** ? ४ ਭ/ glyph' -- [ ] **`gfx_set_font_data(ptr, w, h, advance)`** ? 㪠⥫ - 짮⥫᪨ + ࠧ -- [ ] প **proportional** (advance != w) ⮢ ? - array advance[256] ਭ glyph' -- [ ] **Big-font ०**: 8?16, 16?16, 16?8 ( 쭨) -- [ ] ⤥ API `gfx_text_ex(x, y, str, font_id)` - font_id 롨ࠥ 㦥 ⮢ -- [ ] **Anti-alias 2-bit ** ( 䮭 / ࠭ / 2- alpha?) - ? far future, smooth UI - -## ⠯ ⨬権 ( ᥩ) - -- **`gfx_line` १ accel ** ? Bresenham |dy| << |dx| ( ) 뤠 runs Y ( X): ᥫ, ᥫ, ᥫ, 蠣 Y, ᥫ, ᥫ... ⠪ run ? ⮢ 㬥 `gfx_hline` ( `vline`). - ᫥: runs 㭪 ; run length, ஬ 룮 accel hline 祬 N?putpixel (overhead accel ~20?s, putpixel ~5?s ? accel 룮 run ? 4-5 px); (dx ? dy) ⠢ Bresenham, ? run-length-based fill. - `gfx_line` orthogonal cases 㦥 १ accel ? ⨬஢ ⮫쪮 . - -- **`gfx_fill_rect` W3-swap ** ? ᥩ ७ `gfx_hline`/`gfx_vline` ᢮ DI/save-W3/restore-W3/EI. ᤥ internal `_fill_rect_inner` ন W3 DI 横; ~20?s ? ⢮ ப/⮫殢 . ਬ ᥬ ਬ⨢. -# MDView ? ᬮ騪 Markdown Sprinter - -**MDView** ? ணࠬ ᬮ 㬥⮢ ଠ *Markdown* -Sprinter ( Z80). 㬥 ࠭ ⤥쭮 W3 -᭮ RAM ணࠬ. - -## - -- 㬥 **128 ** (8 ࠭ EMM 16 ) -- **16 384** ࠭ ப -- ⮬᪨ ७ ᫮ ਭ ࠭ (80 ⮫殢) -- ਧ⠫ ᤢ ப ப ( , ⠡) -- -: 䠩, ப, 業 ப⪨ -- ப ﭨ ६ 㧪 樨 -- প ?? ᪫ ப - -## - -``` -mdview [_䠩.md] -``` - -᫨ 䠩 , 㦠 `README.MD`. - -## ࠢ - -``` - ⢨ - -Up Down ப⪠ ப / -PgUp PgDn ப⪠ ࠭ (30 ப) -Home 砫 㬥 -End 㬥 -Left Right ਧ⠫ ᤢ (⮫쪮 nowrap-ப) -F1 ࠢ -F8 ஢: CP866 ? CP1251 ? KOI8-R ? UTF-8 ? ... -F10 / Esc 室 ணࠬ -``` - -஢ । ⮬᪨ ⨨ (BOM + ⨪ - 4 ); `F8` ४砥 , ᫨ ⥪ 訡. -8- ஢ (CP866/CP1251/KOI8-R) ४ (६ -ᮢ). ன / (ਬ UTF-8) ந ? -ࢮ ४祭 (⪠ 㧠 ᯨ஬), . - -## ⠪ Markdown - -### - -ন ஢ H1?H4. ஢ H5 H6 ⮡ࠦ H4. - - # ࢮ ஢ - ## ண ஢ - ### 쥣 ஢ - #### ⮣ ஢ - -### ⮢ ଠ஢ - -** ⥪** 뤥 묨 񧤮窠: `**⥪**` - -*ᨢ* 뤥 묨 񧤮窠 `*⥪*` ન `_⥪_` - -`஥ ` 砥 묨 窠 - -~~ભ ⥪~~ ? ⨫: `~~⥪~~` - -### 㬥஢ ᯨ᮪ - -થ `-`, `*` `+`: - -- 㭪 ᯨ᪠ -- ன 㭪 ᯨ᪠ -- ⨩ 㭪 筮 ⥪⮬, 室 - 㤥 ७ ᫥ ப ࠭ 㯠 - -### 㬥஢ ᯨ᮪ - -1. -2. ன -3. ⨩ - -### - -> 稭 ᨬ `>`. ᪮쪮 ᫥⥫ -> ப ᪫ ⮬᪨ -> ७ᮬ ᫮. - -### (verbatim) - - 砥 ன 窨. -⥪ ⮡ࠦ ? ? ࠧ Markdown: - -``` -#include -#include - -int main(void) { - puts("Hello, Sprinter!"); - return 0; -} -``` - -### ਧ⠫쭠 - - ᨬ `---`, `***` `___` ⤥쭮 ப: - ---- - -## 孨᪨ ࠪ⨪ - -- **ଠ:** Sprinter, Z80 @ 21 -- **஢:** CP866 / CP1251 / KOI8-R / UTF-8 (⮮।, `F8`) -- **ᨬ ࠧ 䠩:** 128 -- **ᨬ쭮 ᫮ ப :** 16 384 -- ** :** small - - ணࠬ, c⥪, , ? a W1-W2 (32 , 0x4000?0xBFFF). - - 䠩 ? ࠭ EMM, ⮡ࠦ W3 (0xC000?0xFFFF) - -## TODO - -1. **祭 ࠧ 㬥⮢.** 128 : 筮 - ࠧ ࠡ 訬 - ࠭ , ஢ - ࠡ 8- ࠭栬 16. - -: ଠ஢ ⠡ ࠬ; প ஢ -CP866 / CP1251 / KOI8-R / UTF-8 ⮮। ४祭 `F8` -(ன ந , ࢮ ४祭). - -4. **᪮७ ७ਭ.** ப ࠭. ⨬ 横 뢮 - ᨬ १ BIOS WRCHAR ( 뢮, DMA). - ---- - -*MDView v0.2 (c) 2026 ஢ ..* - - ---- - -# Sprinter C Compiler ? v1.0 - -C toolchain for **Sprinter** ? the Z80-based home computer by Peters Plus, running -ESTEX DSS. Host: macOS / Linux. Target: `.EXE` files in SprintEXE format. - -Built on top of **SDCC 4.5** (vendored in `third_party/sdcc/`). This repository adds -everything Sprinter-specific: crt0, linker integration, libc wrappers over ESTEX, -banked-call trampolines, graphics & accelerator API, mouse driver wrappers, and the -`mkexe` utility for producing SprintEXE images. - -## What you get - -* **`bin/sprinter-cc`** ? one-line driver: `sprinter-cc -o foo.exe foo.c` -* **Memory modes**: `tiny`, `small`, `big`, `huge`, `manual` ? see below. -* **stdio + conio**: printf, puts, putchar, getchar, fopen/fread/..., cprintf, cputs, putch, textcolor/textbackground/textattr, gotoxy, kbhit/getch. -* **Graphics**: 320?256?256 and 640?256?16 modes, accelerator-backed primitives (hline / vline / rect / fill_rect / line via Bresenham, plus clear), bitmap-font text in both modes via BIOS character generator. -* **File I/O**: POSIX (`open`/`read`/`write`/`close`/`lseek`/`unlink`/`creat`), FILE\* streams (`fopen`/`fgets`/`fwrite`/...), directory listing (`ffirst`/`fnext`), `chdir`/`getcwd`/`mkdir`/`rmdir`, `stat`/`fstat`. -* **Memory**: 32 KB heap (W2-resident), banking-aware page allocator (`mem_alloc_pages`/`bank_read`/`bank_write`), explicit memory modes for sub-16 KB programs. -* **Mouse**: full Sprinter driver wrapper (14 functions including custom cursor bitmaps). -* **Environment**: `getenv`/`putenv`/`sysenv` over ESTEX `$46`. -* **Time**: `getdatetime`/`setdatetime` + POSIX `time`/`localtime`/`mktime`/`asctime`/`ctime`. -* **Misc**: `errno`/`strerror`/`perror`, `atexit`, `setjmp`/`longjmp`, `sleep`, full argv parsing in crt0. - -## Quick start - -```sh -git clone sprinter-c -cd sprinter-c -make sdcc # one-time: fetch SDCC 4.5 binary (~25 MB) -make all # build mkexe + libsprinter.lib + 27 examples -make floppy # pack everything into mame/v306/IMG/mc.img -cd mame/v306 && ./run_mame.sh # boot Sprinter in MAME -``` - -Compile a single program: - -```sh -cat > hello.c < -int main(void) { puts("Hello, Sprinter!"); return 0; } -EOF - -bin/sprinter-cc -o hello.exe hello.c -``` - -That's it ? `hello.exe` is now a valid SprintEXE you can `RUN HELLO` from the ESTEX shell. - -## Memory modes - -Sprinter's address space is four 16 KB windows (W0 / W1 / W2 / W3). DSS allocates -pages by program size ? small programs get only one page. Pick a memory mode based -on what your program needs: - -| Mode | Code lives in | Banking | Use when | Note | -|---|---|---|---|---| -| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | | -| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | | -| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | | -| `huge` | W1-W2 + W3 banking | yes (W3) | small + extra code modules | | -| `manual` | user-specified | optional | special layouts | Not implemented | - -```sh -sprinter-cc --memory small -o big.exe bigprog.c -sprinter-cc --memory huge -o app.exe main.c --bank 1=engine.c --bank 2=ai.c -``` - -Banked functions are declared with `__banked`: - -```c -void engine_tick(int dt) __banked; // lives in BANK1, automatically swapped -``` - -## Examples (27 total) - -| Example | What it demonstrates | -|---|---| -| `hello` | Hello world with stdio + conio Turbo-C-style colors | -| `argv` | argv parsing in crt0 | -| `cat` | File I/O ? read & print TEST.TXT | -| `seek` | 32-bit lseek over a 100 KB file | -| `ls` | Directory listing via ffirst/fnext | -| `filetest` | FILE\* streams (fopen/fread/...) | -| `stattest` | `stat`/`fstat` on files and directories | -| `errno` | errno / strerror / perror | -| `mem_test` | Page allocator + bank\_read/bank\_write | -| `malloc` | Heap stress test (200+ allocations) | -| `banked` | Banked code in W3 (huge mode) | -| `bankedbg` | Banked code in W1 (big mode) | -| `banklocl` | Bank-local static data and BSS | -| `mouse` | Mouse driver in text mode | -| `gfx_mous` | Mouse with custom bitmap cursor in graphics mode | -| `gfx_demo` | 320?256?256 lines / rects / accelerator | -| `gfx_d16` | 640?256?16 same primitives | -| `gfx_text` | Bitmap-font text on graphics screen | -| `timedir` | Date/time + directory listing | -| `ptime` | POSIX time API | -| `openenv` | open() flags + environment vars | -| `conio` | conio API smoke test | -| `attrprob` | Probe Sprinter text attribute byte layout | -| `strtest` | string.h test (from SDCC's z80.lib) | -| `stdlib` | stdlib.h test (qsort / rand / strtol / etc.) | -| `assrtest` | assert() | -| `rt_test` | Runtime helpers (sleep, setjmp, atexit) | - -## Headers - -Standard: -* `` ? puts / printf / FILE\* + Sprinter-specific dec/hex helpers -* `` ? atoi / atof / malloc / qsort / ... (from SDCC z80.lib) -* `` / `` / `` ? from SDCC z80.lib -* `` ? read / write / close / lseek / unlink -* `` ? open / creat + O\_RDONLY / O\_CREAT / ... -* `` ? errno + error names + strerror -* `` ? stat / fstat -* `` / `` ? from SDCC - -Sprinter-specific: -* `` ? putch / cputs / cprintf, textcolor / textbackground / textattr, kbhit / getch, clrscr, gotoxy, wherex/y -* `` ? gfx_init/done, palette, putpixel, hline/vline/rect/fill_rect/line, text ? for both 320 and 640 modes (gfx_\*16 variants) -* `` ? full 14-function driver wrapper + mouse_cursor_t with bitmap support -* `` ? chdir / getcwd / mkdir / rmdir / ffirst / fnext / ffblk -* `` ? getdatetime / setdatetime + POSIX time / localtime / etc. -* `` ? raw ports, ESTEX/BIOS function numbers, env API -* `` ? exit / \_exit / atexit -* `` ? mem\_alloc\_pages / mem\_free\_block / bank\_read / bank\_write -* `` ? Solid-C compatibility layer (aliases + BOOL/WORD/uint types) - -## Toolchain commands - -```sh -make all # build mkexe + lib + every example -make floppy # repack mame/v306/IMG/mc.img with all .exe files -make check # 17 mkexe unit-tests -make clean # remove all build artefacts -make sdcc # one-time: fetch SDCC 4.5 binary -``` - -### sprinter-cc options - -``` -sprinter-cc -o foo.exe foo.c [more.c ...] [options] - - --memory MODE tiny | small | big | huge | manual (default: tiny) - --memory-manual SPEC explicit placement (CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3) - --stack-size N bytes reserved for the stack (default ~1278) - --crt0=TYPE default | minimal | banked | small - --bank N=FILE.c compile FILE.c into bank N (repeatable, max 15) - --debug enable runtime diagnostics (defines DEBUG_RT) - -I PATH extra include path - -L 0xADDR / -E / -S override load / entry / stack addresses - -Wl FLAG pass FLAG to sdldz80 - --mkexe FLAG pass FLAG to mkexe (e.g. --mkexe -p --mkexe 0 for bank padding) - -v verbose -``` - -## Status - -What works in v1.0: -* Compile / link / pack to SprintEXE ? verified on all 27 examples -* Four memory modes (tiny / small / big / huge) -* Graphics (both modes) with accelerator -* Mouse (text + graphics cursor) -* File I/O, directories, environment, time -* All headers listed above - -Deferred to v2.0 (see `docs/TODO.md`): -* **Turbo-C-style BGI graphics API** ? `initgraph` / `setcolor` / `circle` / - `getimage` / `putimage` / etc. on top of our `gfx_*` primitives -* Remaining Solid-C compatibility gaps (Phase 2/3) ? see `docs/solid_c_compatibility.md` -* Manual memory mode -* Rewrite FILE\* stream API (current implementation is very primitive and doesn't use buffers) - -Deferred to v3.0: -* **IM2 interrupt handlers** ? research complete (`docs/im2_isr_design.md`), - implementation scheduled for v3 -* **Audio API** (AY-3-8910 + COVOX) ? requires IM2 -* **ISA-8 slot drivers** ? requires IM2 (???) - -## Documentation - -* `docs/TODO.md` ? roadmap and open work items -* `docs/solid_c_compatibility.md` ? gap analysis vs Solid-C 2004 -* `docs/im2_isr_design.md` ? interrupt handler design (v2) -* `docs/converted/` ? source documentation (ESTEX, BIOS, architecture) - converted to plain text for `grep` -* `docs/reference/`, `docs/samples/`, `docs/memory management/` ? original - Russian docs and code samples from Peters Plus - -## Repository layout - -``` -bin/sprinter-cc one-line compiler driver (bash) -toolchain/mkexe/ host-side tool: .ihx -> .exe SprintEXE -toolchain/check_banks.py post-link bank size enforcer -runtime/ crt0 variants (default, minimal, small, banked) - bank trampolines, heap, heap_top -libc/include/ headers -libc/io|stdio|mem|gfx/ C and asm sources for libsprinter.lib -lib/ Makefile that archives libsprinter.lib via sdar -examples/ 27 example programs -mame/v306/ MAME binary + Sprinter ROM/HDD images + floppy script -third_party/sdcc/ vendored SDCC 4.5 (fetched via `make sdcc`) -third_party/solid-c/ reference: original Sprinter native C (for compat target) -docs/ documentation -``` - -## License - -This repository contains: -* Original code in `bin/`, `toolchain/`, `runtime/`, `libc/`, `lib/`, `examples/` ? - MIT-licensed. -* `third_party/sdcc/` ? SDCC 4.5 under GPLv2 with linking exception - (see `third_party/sdcc/COPYING.txt`) -* `third_party/solid-c/` ? original Sprinter Solid C, used only as a reference - -## Credits - -* **Sprinter / Peters Plus** ? , ਩ ਭ and the original team -* **SDCC** ? for the underlying Z80 compiler -* **MAME** ? for the Sprinter Sp2000 emulation - ---- - -For questions / patches: see CONTRIBUTING.md (TBD) or open an issue. - ---- - -# TODO / Roadmap - - 浪 뢠 ਮ.  ॠ ணࠬ ? ਮ ᬥ. - -## ⠯ 5 ? malloc / free + banking-aware page allocator ? - -- [x] SDCC's `malloc`/`free` + `runtime/heap.s` ( library heap.rel, 14000- heap 2) -- [x] `libc/mem/mem_alloc.c` ? page allocator: `mem_alloc_pages`/`mem_free_block`/`mem_get_page`/`mem_info` १ ESTEX `$3C/$3D/$3E` + BIOS `$C4` -- [x] `libc/mem/bank_io.c` ? HOME-१ `bank_read`/`bank_write`/`bank_load_byte`/`bank_store_byte` ᢮ W3 -- [x] `examples/malloc_test/` ? ஢ઠ SDCC's malloc (~210 64- allocations १ heap) -- [x] `examples/mem_test/` ? ஢ઠ page allocator: 3 ࠭, ࠧ १ bank_write, 䨪 १ bank_read - -## ⠯ 6 ? argv parsing + sprinter-cc wrapper ? - -- [x] crt0 ESTEX command-line IX-prefix (inline asm `runtime/crt0.s`) -- [x] Strip leading CP/M-style space (DSS quirk) -- [x] । `argc`/`argv` main() १ HL/DE (SDCC __sdcccall(1) ABI) -- [x] argv[0] = basename .EXE १ ESTEX APPINFO ($47 subfn 2) -- [x] `runtime/crt0_minimal.s` ? opt-out 祭 쪨 ணࠬ -- [x] `runtime/crt0_banked.s` ? ⥯ ⮦ argv (parse_argv + get_progname ᪮஢ crt0.s; 㤥 factored argv.s libsprinter.lib) -- [x] Bash-⪠ `bin/sprinter-cc`: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権: `--memory`, `--memory-manual`, `--stack-size`, `--crt0=`, `--bank N=FILE.c`, `--debug`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` - -## ⠯ 8 ? 䨪 (320?256?256 + 640?256?16 + accel + bitmap font) ? - -- [x] **8a** Graphics core: `gfx_init`/`gfx_done`/`gfx_clear`/`gfx_putpixel`/`gfx_pal_load`/`gfx_pal_set` (libc/gfx/gfx_core.c). १ BIOS PIC_SET_PAL ($A4). Verified 320?256?256. -- [x] **8b** /אַ㣮쭨/fill १ accelerator (libc/gfx/gfx_lines.c): `gfx_hline`/`gfx_vline` १ accel Fill (LD C,C / LD E,E + SMC block-size), `gfx_rect`/`gfx_fill_rect` heuristic 롮஬ ਥ樨 (h/v bursts count), `gfx_line` Bresenham . `gfx_clear` ⮦ ९ᠭ column-major accel (~4? ॥). -- [x] **8c** 640?256?16 mode (libc/gfx/gfx_16.c): `gfx_*16` API, HIGH nibble = LEFT pixel (㬥 misleading), per-row RMW vline ( = 2 ਧ⠫ ᥫ). -- [x] **8d** Bitmap font + gfx_text (libc/gfx/gfx_text.c): १ BIOS WIN_GET_ZG ($B8), interleaved layout `font[row*256+char]`, `gfx_text`/`gfx_putchar` 320 mode, `gfx_text16`/`gfx_putchar16` 640 mode pair-table lookup. - -. memory/sprinter_graphics.md, sprinter_accelerator.md, sprinter_graphics_16.md, sprinter_font_format.md. - - ( ): -- [ ] -quad 640: per-cell (mode 0x82 ࠧ蠥 1 4 per 16?8 cell) ? १ אַ area-ᠭ ࠭ 0x0300..0x039F - -## Auto-banking (. `memory/banking_roadmap.md` ⠫) - -Phase 1 ? file-level bin-packing ? ॠ뢠 ஥ ~30 KB . - -- [ ] `toolchain/auto_bank.py`: - - ࠧ `.rel`-䠩 ( .map ᫥ dry-run link') - - First-fit-decreasing bin-packing - - `#pragma codeseg BANKn` manual override - - ५뢠 묨 `-Wl-b_BANKn=...` ࠬࠬ - - ⠥ । - -Phase 2-5: incremental rebalance, declarative `banks.toml`, function-level, call-graph-aware. 쪮 ᫨/ . - -## Bank-local static data (mutable data ⮬ ) ? ? - -- [x] ਬ `examples/bank_local_data/` ? 㭪 BANK1 ᢮ writable BSS array + const table + malloc- -- [x] `mkexe -p 0` 㫥 padding (BSS-storage 㧪) -- [x] ᪨ 楯: `--codeseg BANK1 --constseg BANK1 --dataseg BANK1` bank1.c + `-Wl-b_BANK1=0x1C000` . **`--dataseg BANK1` ** ? ࠭ ⭮ - trampoline bug ᪨஢ १. -- [x] ** 䨪 trampoline'a runtime/bank.s** ? `pop af; out (n), a` ਫ A ? banked-㭪樨 騥 uint8_t 頫 . `pop bc; out (c), b` ࠭ A. -- [x] **malloc banked-㭪樨 ࠡ⠥ ஧筮** ? heap W2 (HOME), W2 ᢠ trampoline', pointer  ⥪. . memory/bank_local_data_pattern.md. -- [x] 㬥 memory: `memory/bank_local_data_pattern.md` ( 楯 + malloc + nuances), `memory/sdcc_banking.md` (trampoline fix) -- [ ] 樮쭮 ? `check_banks.py` ⮡ 뢠 ࠧ size = code + const + bss per bank (cosmetic) - -祬: 㫥 訬 private state (level loader, audio engine, scene data). W2 heap , ⨪ . - -## ᪠ solid-c (⨢ Sprinter C ? `third_party/solid-c/`) - -᫥ solid-c'᪮ libc (. `memory/solid_c_findings.md`)  ⮢ ᫥ 㭪権. ਮ **᮪** : - -### High-priority gaps ( ஢, 짠) -- [x] **`errno` + `strerror`/`perror`** ? ⠡窠 32 訡 (libc/io/errno.c) -- [x] **७ `open()`** O_CREAT/O_TRUNC/O_APPEND/O_EXCL state machine -- [x] **`atexit`** ? 8-callback LIFO + `exit()` + `_exit()` (libc/io/atexit.c) -- [x] **`setjmp`/`longjmp`** ? 6- jmp_buf={sp,ix,pc} (libc/io/setjmp.c) -- [x] **`sleep(seconds)`** ? 50Hz halt-loop (libc/io/sleep.c) -- [x] **ESTEX ENV API** ($46, getenv/putenv) ? libc/io/env.c. 竨 doc-bug: ॠ쭮 A=0 NOT FOUND - -### Medium-priority (㦭 shell-like ⨫) -- [ ] **Mouse driver** ? `rst $30h`, 17 㭪権. **砫 ࠡ⠥ MAME**. -- [x] **`ffirst`/`fnext` + ffblk_t struct** directory listing ? ॠ, demo: ls.exe -- [x] **`getdatetime`/`setdatetime`** १ ESTEX $21/$22 ? libc/io/time.c, demo: time_dir_test -- [x] **`chdir`/`getcwd`/`mkdir`/`rmdir`** ? wrappers ESTEX $1B-$1E ? libc/io/fsdir.c -- [x] **conio: `kbhit`/`getch`/`getche`/`cputs`/`clrscr`/`gotoxy`** ? ॠ -- [x] **conio extras**: `wherex`/`wherey` ($53), `wrchar`/`rdchar` ($58/$57), `textmode_get/set` ($50/$51), `clrscr_attr` ($56) + COLOR macros - -### Low-priority ? ? FILE* stack - -- [x] ** unbuffered FILE\*** ? `libc/stdio/file.c` + `libc/include/stdio.h`. fopen/fclose/fputs/fgets/fread/fwrite/fseek/ftell/rewind/feof/ferror/clearerr/fflush + stdin/stdout/stderr pseudo-streams. . `memory/file_star_design.md` `examples/filetest`. -- [ ] fprintf / fscanf ? 㦭 printf-१-callback machinery. 짮⥫ `sprintf(buf, ...) + fputs(buf, fp)`. -- [ ] 樮 buffered mode (setvbuf, line/block buffering) ? ᫨ - . - -### POSIX time API ? ? -- [x] `libc/io/posix_time.c` ? time/localtime/gmtime/mktime/asctime/ctime getdatetime. SDCC's time.rel ( override _RtcRead). . `examples/ptime`. - -### sys/stat ? ? -- [x] `libc/io/stat.c` ? POSIX stat/fstat. ਤ open+fstat 䠩, ffirst+iter ( "."/".."). . `examples/stattest` `memory/estex_ffirst_dotdot.md`. - -### assert ? ? (ᯮ㥬 SDCC's __assert १ fallback include path) - -## libc/stdlib ? ? 㦭 (. memory/sdcc_stdlib_works.md) - -஢७ १ `examples/stdlib_test/`: SDCC z80.lib ᮤন ࠡ騥 ॠ樨: -- `atoi/atol/atof, strtol/strtoul, rand/srand, qsort/bsearch, abs/labs, div/ldiv` -- `` (memchr/cmp/set/cpy, strcat/cmp/cpy/len/chr/spn/etc.) -- `` (toupper/tolower) -- `` (sinf/cosf/sqrtf/etc.) - - ⮬᪨ ﭥ z80.lib 㦭. ** ९뢠**. - - Sprinter-specific 易⥫ 㫨 : atexit, env, errno, setjmp, putchar/puts/getchar, conio, fsdir, time, mouse, open/read/lseek/close. - -## Build-system: libsprinter.lib + sprinter-cc ? ? - -- [x] `lib/Makefile` ? ᮡࠥ libc/*.c `.rel`, 娢 १ sdar `lib/sprinter.lib` -- [x] 砥 runtime/bank.s runtime/heap.s (auto-pulled __banked/malloc) -- [x] `bin/sprinter-cc` ? bash-wrapper: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権 `--crt0=default|minimal|banked`, `--bank N=FILE.c`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` -- [x] `examples/hello_sccc/` ? : `hello.c` ᮡࠥ shell-맮, ࠧ ᮢ Makefile (925 ) -- [x] Split `putchar.c` ? `putchar.c` + `puts.c` per-function granularity (puts override SDCC's z80.lib version) -- [x] `make all` (ᨬ `lib` । `examples`) - - 襭 (樮쭮): -- [ ] ஢ ⠫ examples sprinter-cc Makefile (ᬥ⨪) -- [ ] 쭥 libc/*.c per-function ( ⥪ granularity 㦥 㦭 ࠧ ? .rel 楫, 設⢠ 䠩 㭪) - -## ⠯ 9 ? memory modes sprinter-cc - -DSS 뤥 ࠭ ࠧ ਫ: < 16 KB ? ࠭, ⠫ 砥 ?࠭ #FF? (read=0xFF, write ). - ⮣ CODE--W1 + DATA--W2 쪮 ணࠬ . . [memory/sprinter_memory_modes.md](../../.claude/projects/-Volumes-SAM8-Projects-DIY-Z80-Sprinter-C-Compiler/memory/sprinter_memory_modes.md). - -- [x] **`tiny`**: (CODE+DATA+⥪) W2. Default. Verified hello/argv/conio/malloc/file/etc. -- [x] **`--memory MODE` 䫠 sprinter-cc**: parser + per-mode 䮫 CODE_LOC/DATA_LOC, override १  `--code-loc`/`--data-loc`. tiny ࠡ⠥; small/big/huge warning' (runtime ⮢). 2026-05-30. -- [x] **`--memory-manual SPEC`**: `CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3`. 2026-05-30. -- [x] **`small` runtime**: `runtime/crt0_small.s` ᯮ ESTEX `$3D GETMEM` + `$3A SETWIN2` ⮡ 뤥 W2-࠭ gsinit. **** BIOS `$C4` ? ⥪ ⮬ ⠯ W1 (boot_stack HOME), BIOS ॡ ⥪ W2. ᫥ SP ४砥 0xBFFE, ⠭ flow. 2026-05-30, verified hello.exe. -- [x] **`small` auto-detect >16 KB ணࠬ**: `crt0_small.s` ⠥ `0xC2` (⥪ ࠭ W2 ? `0xA2`! W1). ᫨ 0xFF ? 뤥 page; DSS 㦥 ᤥ (ணࠬ ᠬ 뫥 W2). crt0 뢠 0..30 KB. mkexe ⠪ ࠧ蠥 HOME span W1+W2 (0x4000..0xBFFF). Verified hello: small (5 KB 䠩, SETWIN2 path) + 32 KB 䠩 (auto-skip). 2026-05-30. -- [x] **`big` runtime** (tiny + banked code W1): ࠬਧ `crt0_banked.s` + `bank.s` १ `.ifdef BANK_W1` ? 㣮 banking port (0xA2 vs 0xE2), 㣮 load-addr (0x4000 vs 0xC000). sprinter-cc prepend' `BANK_W1 = 1` `--memory big`, । `mkexe -B 0x4000`. ਬ `examples/banked_big/`. 2026-05-30. -- [x] **`huge` runtime** (small + banked code W3): merge W2-detect `crt0_small.s` `crt0_banked.s`. 騩 ਬ `examples/banked/` ⥯ ᯮ MEMORY=huge. 2026-05-30. -- [x] **`--debug` 䫠**: prepend `DEBUG_RT = 1` crt0 + `-DDEBUG_RT` sdcc. 뢠 symbol `_w2_self_allocated` (uint8_t) ? runtime diagnostic ஢ W2. 2026-05-30. - -### -襭 libc crt0 - -** `sprinter.lib`** ࠡ⠥ memory mode ? `.rel`-童 relocatable, SDLD dead-code elimination per-member ( 䨪  `gfx_core.rel` ..). Verified hello vs malloc_test १ map-䠩. - -**`gfx.lib` ⤥쭮 ? 㦥**: dead-code elimination 㦥 ࠡ⠥. - -**`libc_banked` (libc bank HOME)** ? ⮬, HOME (16 KB) user- + libc `huge` mode. १ `--codeseg BANK0` 樨 libc; trade-off: trampoline ~30 横 libc-맮. ਣ: ॠ쭠 ணࠬ HOME budget. - -**HW-ᨬ 㫨 ? `sprinter_home.lib` ⤥쭮.** libc 䨧᪨ W3, ⮬ W3: -- `gfx_*` ? `0xC000+` ᫥ swap W3 video page -- `bank_io` (mem_alloc_pages/bank_read/bank_write) ? swap' W3 १ `OUT (0xE2)` -- 騥 ISR ? 뢠 ਩ W3 㣮 - - huge mode 㫨 HOME (W1). 㤥 `libc_banked`, **६** 뤥塞 `sprinter_home.lib` (HOME-only) `sprinter.lib` (bankable). 쭠 奬: -``` -sprinter_home.lib HOME-only: gfx, bank_io, ISR shims -sprinter.lib bankable: printf, malloc, string, conio, stdio, env, ... -sprinter_banked.lib sprinter.lib --codeseg BANK0 ( huge) -``` -ਣ: ॠ `--memory huge` runtime. - -**crt0 ? mode:** -- `crt0.s` ? ⥪騩, **tiny/big**: SP=0xBFFE, argv (W2- 㦥 뤥 DSS). -- `crt0_minimal.s` ? ⥪騩, tiny argv. -- `crt0_small.s` ? **, step 3**: **small/huge**, W2 १ `mem_alloc_pages` gsinit, `0xA2`, ⮬ ⠭ flow. -- `crt0_banked.s` ? ⥪騩, **big**: trampoline-⠡ W3 , CODE W2. -- `crt0_banked_small.s` ? ****: huge = small (W2-alloc) + banked (W3 trampolines). - -sprinter-cc ࠥ crt0 `--memory` mode (ᥩ `--crt0=` override). -- [x] **ࠨ ࠧ ⥪**: 䫠 `sprinter-cc --stack-size BYTES`. Wrapper `heap_top.s` `___sdcc_heap_end = stack_top + 1 - stack_size`, ⤥ .rel per-program. Default ?1278 (heap_top=0xBB00) `runtime/heap_top.s`. 2026-05-30. - -䥩: `sprinter-cc --memory [tiny|small|big|huge|manual] [--memory-manual SPEC] [--stack-size N] foo.c`. `--memory-manual` ⮫쪮 `--memory manual`. - -## Known issues / quirks - -- **ESTEX $46 ENV API**: ? ࠡ⠥. - `DiskSyscalls.txt v1.6` 訡筮 뢠 return-status ? A=0 NOT FOUND, FOUND. 䨪஢ `memory/sprinter_platform.md`. - -## V2 (᫥ ५ v1) - -### Turbo-C-style graphics API (BGI-like) ? **MUST v2** - - `gfx_*` API ஢ **Turbo-C ``** (BGI) MS-DOS. -ணࠬ ਢ訥 Turbo-C ७ ᪨ 1--1. - -** ** ( ᭮ Borland BGI): - -Setup/teardown: -- `initgraph()` / `closegraph()` ? ᥩ `gfx_init`/`gfx_done`, alias -- `getmaxx()` / `getmaxy()` ? GFX_WIDTH-1 / GFX_HEIGHT-1 -- `cleardevice()` ? alias to gfx_clear -- `getgraphmode()` / `setgraphmode()` ? get_videomode/set_videomode - -Color/palette: -- `setcolor(c)`, `getcolor()` ? current draw color -- `setbkcolor(c)`, `getbkcolor()` ? background color -- `setpalette(idx, c)` ? entry -- `getpalette(&info)` ? read all palette - -Primitives ( 㦥 ? BGI- aliases): -- `putpixel(x, y, c)` ? gfx_putpixel -- `getpixel(x, y)` ? 㦭 ॠ (RMW ⭮ ? IN) -- `moveto(x, y)`, `lineto(x, y)`, `linerel(dx, dy)` ? current point + line drawing -- `line(x1, y1, x2, y2)` ? gfx_line -- `rectangle(x1, y1, x2, y2)` ? gfx_rect ( 㣮 API: x1,y1,x2,y2 vs x,y,w,h!) -- `bar(x1, y1, x2, y2)` ? gfx_fill_rect -- `bar3d(x1, y1, x2, y2, depth, topflag)` ? : rect + 3d edges -- `circle(x, y, r)`, `arc(...)`, `ellipse(...)`, `pieslice(...)` ? primitives -- `fillpoly()`, `drawpoly()` ? -- `floodfill(x, y, border_color)` ? - -Text on graphics screen: -- `outtext(s)` / `outtextxy(x, y, s)` ? gfx_text (alias) -- `settextstyle(font, dir, size)` ? multiple bitmap fonts -- `gettextsettings(&info)` -- `textwidth(s)` / `textheight(s)` ? measure - -Image manipulation: -- `imagesize(x1, y1, x2, y2)` ? bytes needed for getimage -- `getimage(x1, y1, x2, y2, buf)` ? save rect to buffer -- `putimage(x, y, buf, op)` ? paste back with COPY_PUT/XOR_PUT/AND_PUT/OR_PUT/NOT_PUT - -Clipping/viewport: -- `setviewport(x1, y1, x2, y2, clip)` ? drawing clip rect -- `getviewsettings(&info)` -- `clearviewport()` -- `setactivepage(p)` / `setvisualpage(p)` ? ਧ (Sprinter 2 screen) - -Line style: -- `setlinestyle(style, pattern, thickness)` ? SOLID_LINE / DOTTED_LINE / etc. -- `getlinesettings(&info)` - -**Acceptance:** ७ ⨯筮 Turbo-C BGI ணࠬ (饩 ᯮ짮 -moveto/lineto/circle/bar/setcolor) ࠡ ⢥ ࠢ. - -**Notes:** -- BGI fonts (TRIPLEX/SANS_SERIF/GOTHIC) ? BIOS font, ⠫ 㦭 - ( bitmap data lib) -- imagesize/getimage/putimage ? ᠬ ॡ game/animation -- Active/visual page ( ਧ) ? Sprinter ন 2 graphics pages, - 㦥 API switching - -. ⠪ `examples/` Turbo C 2.x BGIDEMO reference 㦭. - -### IM2 Interrupt Handlers ? **MUST v2** - -User- ISR १ Z80 IM 2 mode. 㦭 : -- Timer ticks (50 Hz frame counter, ) -- Music playback (AY, COVOX) -- Real-time games (input + game logic + render interrupt-driven) -- Async keyboard / mouse handling - -**Status:** v2. research + design `docs/im2_isr_design.md`. - -**襭 ⥪:** ॠ ⤥ memory mode `--memory im2` -( ⮣ ⮡ 騥 crt0). Detail' design-doc. - -** research'** ( ⥪ `docs/im2_isr_design.md`): -- Vector 0xFF ? frame + keyboard + CBL. Disambiguation ⠬ 0x19 / 0xFE -- Mouse hardware-IRQ 室 ( ⥪饩 ) -- Vector table / ISR / stack W2 (0x8000..0xBFFF) -- DSS ᢮ IM 2 handler ? 㦭 chain' ( / SYSTIME ) - -### 稥 㯭 㭪 v2 - -- [ ] **FILE API rewrite ? buffered streams** ? ⥪ ॠ - `libc/stdio/file.c` provisional unbuffered shim ( fputc/fgetc - = read/write syscall). 㦭 業 buffered ᥬ⨪ - Solid-C: - - ```c - typedef struct { - uint flags; // +0..1 file status flags - int level; // +2..3 empty/fill level of buffer - char *curp; // +4..5 current active pointer - int fd; // +6..7 underlying low-level fd - char *buffer; // +8..9 data transfer buffer - char hold; // +10 ungetc byte if no buffer - short token; // +11..12 reserved - char dummy; // +13 reserved - } FILE; - ``` - - stdin/stdout/stderr ? fd-થ `0 / -1 / -2`. ⥫ - stdout/stderr ࠭ ७: ESTEX OPEN positive - small fd (1, 2, ?) 筮 䠩 ? ᫨ stdout=1, ॠ - fd=1 ⠫ 䨪஬. fd=0 stdin ᭮ - (ESTEX 0 頥). fd । syscall' ? - ᯥਧ 䫠 `_F_CONIN/_F_CONOUT`. - - ਭ-⮪ (stdaux/stdprn) ॠ㥬 ? Sprinter ਭ୮ - API . - - ୠ⨢ ? ॠ third_party/solid-c (sources - `SRC/CLIB/`); ⠬ ⮢ buffered FILE + fopen/fread/fwrite/ - fseek/setvbuf .. ஢ 訬 open/read/write/lseek. - - rewrite deferred issues stdio-review: - - `fwrite` short-write ⠢ `_F_ERROR` - - `fgets(buf, 1, fp)` ? ⠭ "empty string", 㫨 NULL - - `mode_to_flags` ? break-out '+' (cosmetic) - -- [ ] **Audio API** ? AY-3-8910 + COVOX १ 뢠 (ॡ IM2) -- [ ] **ISA-8 slot support** ? ZX-Bus (sound, network, etc.) ? ॡ IM2 + ⥭ port - -## 稥 (v1 backlog, 騥) - -- [x] **#9: text I/O split (Turbo-C style)** ? stdio (puts/printf/putchar) ⥯ fast no-attr १ PCHARS/PUTCHAR. conio (cputs/cprintf/putch) ਬ attr १ textcolor/textbackground/textattr. KEEP_EXIST_ATTR ? conio fallback fast path. Verified hello.exe. . `memory/text_output_api_split.md`. 2026-05-31. -- [x] **Mouse API ** (१ driver, RST 30h) ? 14 㭪権 (init/show/hide/refresh/read/goto/bounds/text_cursor/load_cursor/get_cursor/get/set_sensitivity/video_mode_changed). . `memory/mouse_api.md`. Verified MAME 2026-05-31. Sensitivity = divider ( = ॥). -- [ ] Interrupt handlers ? IM 2 vector table HOME user ISR' -- [ ] প `restore SP on EXIT` ( z88dk +pps) ? ஢ 㦭 -- [ ] CI: ⮬᪨ ᪠ MAME `-aviwrite` screenshot-ࠢ, ⮡ ਬ஢ 室 祫 - -## ⮬ - -- প `` ( SDCC stdlib ? 㦭 ஢ crt0 ᮢ⨬) -- `` १ ESTEX SYSTIME (`$21`) CMOS BIOS-㭪樨 -- ZX Spectrum-ᮢ⨬ ० ⤥ target ( ஢ ᯥ㬮᪨ ணࠬ) -- প ZX-Bus (sound, network, etc.) ? 㦭 ࠩ -- Profile-guided optimization tools (hot/cold detection) 㯭 ணࠬ - -## Linker duplicate-symbol warnings (, 䨫஢) - - ᮧ⥫쭮 overrides' SDCC z80.lib 㭪樨 ᮡ⢥ ᨥ `sprinter.lib`, `sdldz80` `?ASlink-Warning-Definition of public symbol '...' found more than once`. ࢮ । (), ⮬ ४⭮ ? warning ⮫쪮 noise. - -騥 overrides: -- `_puts` ? १ PCHARS+\r\n vs SDCC posix puts -- `___sdcc_heap` ? heap W2 vs SDCC's ⠭ -- `_asctime`, `_localtime` ( 㣨 time) ? `posix_time.c` १ ESTEX SYSTIME vs SDCC's `time.rel` `_RtcRead` - -**饥 襭:** `bin/sprinter-cc` 䨫஢뢠 warning- (warning + 2 follow-up `Library:` ப) 뢮 `sdcc`. १ `-v` (verbose) 뢠. १ awk-pipe. - -** 襭:** -- ३ explicit `--nostdlib` + 筮 ᯨ᮪ 㦭 㫥 z80.lib (string, math, stdlib override') ? warning', ᪨஢ -- : २ `_puts` ? `_puts_sprinter` + alias १ linker flag ( 㢥७ SDCC ন) -- : ⠢ ᥩ (ࠡ祥 benign) ? ਮ - -## TODO: ஢ ॠ쭮 - -- [ ] **Port_Y banking trick** (`docs/part2/SprinterGraphics programming.txt`): - ⢥ত ᫥ `OUT (0x89), Y` 0xC000+0x400*N W3 - ப Y..Y+15 ( ணࠬ஢ ? 16 ப). - Empirical 2026-06-01 MAME 0.283 trick ** ࠡ⠥** ? ᥫ - ᠬ 0xC000+row_width 室 . ᪨ - `docs/samples/plasma2.asm` ⮦ ᯮ banking, ⠭ - Port_Y per row. - : - 1. ॠ쭮 Sprinter - 2. dual-write (`_gfx_putpixel_raw` + ன write `0xD000+x`) - 3. ᫨ ? MAME, issue - ९ - 4. ᫨ ⮦ ? 㬥 , 㤠 㯮 - ⠢ ⥪ ॠ (Port_Y per pixel) - 5. ᫨ banking ࠡ⠥ ? ஢ Port_Y - `_gfx_putpixel_raw` (sentinel out-of-range, . memory/gfx_port_y_banking.md) - - 易 먣 Bresenham (60-pixel ) ? 8? - OUT (0x89) 権, `gfx_fill_rect 320x256` ? 16? . - release v1. - -## GFX: ७ `docs/part2/accelerator_doc.txt` - -᫥ ⥭ ⠫쭮 accelerator doc  ࠢ. - ᯮ ⮫쪮 ਧ⠫/⨪ Fill mode. - -### Quick wins ⥪ primitives - -- [ ] ** SMC `LD A, (var)` block-size**. 㬥  - ࠧ蠥 `LD A, (HL)`, `LD A, (BC)`, `LD A, (DE)` ( `LD A, r`). - 㡥 SMC complexity `gfx_lines.c:hfill_chunk/vfill_chunk` - `gfx_16.c:g16_hfill_chunk`. 饭 ⮫쪮 register-to-register. -- [ ] **஢ block-size**. 㬥 뢠 accel - block size bursts (. `Horizontal_Line_Fill`: ⠭ - size + `LD B,B` ⪫祭, ⮬ Fill mode ᯮ - ࠭ size). `gfx_fill_rect` 100 묨 - ப ? ⠭ size 1 ࠧ, 100. - -### Bank-prefix modes (port 0xE2 bits) - -㬥 뢠 ਠ ࠭ ⠭⭮ 0x50: - -| Bank byte | Effect | -|---|---| -| 0x50 | Normal write ? shadow + | -| 0x54 | "no copy in main shadow RAM" | -| 0x58 | **"FF is transparent"** ? 0xFF write ⠢ background | -| 0x5C | both | - -Bank 0x58 祬 mouse cursor 0xFF-஧筮. - **sprite-blending १ accel block copy**: - -- [ ] **`gfx_set_bank_transparent(on)`** 䫠 `gfx_set_bank` - 롮 0x50/0x58 ᮢ sprite' -- [ ] ᯮ짮 `gfx_blit()` ⮡ 䠪 - transparent sprites १ accel- - -### Block copy mode (sprite blit') - -`LD L,L` (horizontal) `LD A,A` (vertical) ? ० ஢ १ -256- accel memory. blit'. - -- [ ] **`gfx_blit(src_data, x, y, w, h)`** ? ஢ sprite' - (ந ࠧ, १ accel) -- [ ] **`gfx_blit_transparent(src, x, y, w, h)`** ? ᯮ짮 bank 0x58 - -. `Draw_Restangle_Data` accelerator_doc.txt ७. - -### AND / OR / XOR operations १ accel - -㬥 뢠 accel ন ᪨ 樨 -. ਬ: -- XOR ? (뤥 selection UI) -- OR / AND ? masking, alpha-style blending -- . ਬ accelerator_doc.txt: "256 bytes block coding via XOR" - -- [ ] **`gfx_xor_rect`** / **`gfx_or_rect`** / **`gfx_and_rect`** ? - ਬ⨢ ᪨ 権 אַ㣮쭨 -- [ ] **`gfx_invert_rect(x, y, w, h)`** ? alias xor 0xFF - -### Bitmap fonts ࠧ ࠧ஢ - - `gfx_text` / `gfx_putchar` प ࠡ 8?8 ⮬ -(BIOS WIN_GET_ZG 頥 256?8 ). UI / 쭨 -㦭: -- [ ] **`gfx_set_font_size(w, h)`** ? ४ ਭ/ glyph' -- [ ] **`gfx_set_font_data(ptr, w, h, advance)`** ? 㪠⥫ - 짮⥫᪨ + ࠧ -- [ ] প **proportional** (advance != w) ⮢ ? - array advance[256] ਭ glyph' -- [ ] **Big-font ०**: 8?16, 16?16, 16?8 ( 쭨) -- [ ] ⤥ API `gfx_text_ex(x, y, str, font_id)` - font_id 롨ࠥ 㦥 ⮢ -- [ ] **Anti-alias 2-bit ** ( 䮭 / ࠭ / 2- alpha?) - ? far future, smooth UI - -## ⠯ ⨬権 ( ᥩ) - -- **`gfx_line` १ accel ** ? Bresenham |dy| << |dx| ( ) 뤠 runs Y ( X): ᥫ, ᥫ, ᥫ, 蠣 Y, ᥫ, ᥫ... ⠪ run ? ⮢ 㬥 `gfx_hline` ( `vline`). - ᫥: runs 㭪 ; run length, ஬ 룮 accel hline 祬 N?putpixel (overhead accel ~20?s, putpixel ~5?s ? accel 룮 run ? 4-5 px); (dx ? dy) ⠢ Bresenham, ? run-length-based fill. - `gfx_line` orthogonal cases 㦥 १ accel ? ⨬஢ ⮫쪮 . - -- **`gfx_fill_rect` W3-swap ** ? ᥩ ७ `gfx_hline`/`gfx_vline` ᢮ DI/save-W3/restore-W3/EI. ᤥ internal `_fill_rect_inner` ন W3 DI 横; ~20?s ? ⢮ ப/⮫殢 . ਬ ᥬ ਬ⨢. -# MDView ? ᬮ騪 Markdown Sprinter - -**MDView** ? ணࠬ ᬮ 㬥⮢ ଠ *Markdown* -Sprinter ( Z80). 㬥 ࠭ ⤥쭮 W3 -᭮ RAM ணࠬ. - -## - -- 㬥 **128 ** (8 ࠭ EMM 16 ) -- **16 384** ࠭ ப -- ⮬᪨ ७ ᫮ ਭ ࠭ (80 ⮫殢) -- ਧ⠫ ᤢ ப ப ( , ⠡) -- -: 䠩, ப, 業 ப⪨ -- ப ﭨ ६ 㧪 樨 -- প ?? ᪫ ப - -## - -``` -mdview [_䠩.md] -``` - -᫨ 䠩 , 㦠 `README.MD`. - -## ࠢ - -``` - ⢨ - -Up Down ப⪠ ப / -PgUp PgDn ப⪠ ࠭ (30 ப) -Home 砫 㬥 -End 㬥 -Left Right ਧ⠫ ᤢ (⮫쪮 nowrap-ப) -F1 ࠢ -F8 ஢: CP866 ? CP1251 ? KOI8-R ? UTF-8 ? ... -F10 / Esc 室 ணࠬ -``` - -஢ । ⮬᪨ ⨨ (BOM + ⨪ - 4 ); `F8` ४砥 , ᫨ ⥪ 訡. -8- ஢ (CP866/CP1251/KOI8-R) ४ (६ -ᮢ). ன / (ਬ UTF-8) ந ? -ࢮ ४祭 (⪠ 㧠 ᯨ஬), . - -## ⠪ Markdown - -### - -ন ஢ H1?H4. ஢ H5 H6 ⮡ࠦ H4. - - # ࢮ ஢ - ## ண ஢ - ### 쥣 ஢ - #### ⮣ ஢ - -### ⮢ ଠ஢ - -** ⥪** 뤥 묨 񧤮窠: `**⥪**` - -*ᨢ* 뤥 묨 񧤮窠 `*⥪*` ન `_⥪_` - -`஥ ` 砥 묨 窠 - -~~ભ ⥪~~ ? ⨫: `~~⥪~~` - -### 㬥஢ ᯨ᮪ - -થ `-`, `*` `+`: - -- 㭪 ᯨ᪠ -- ன 㭪 ᯨ᪠ -- ⨩ 㭪 筮 ⥪⮬, 室 - 㤥 ७ ᫥ ப ࠭ 㯠 - -### 㬥஢ ᯨ᮪ - -1. -2. ன -3. ⨩ - -### - -> 稭 ᨬ `>`. ᪮쪮 ᫥⥫ -> ப ᪫ ⮬᪨ -> ७ᮬ ᫮. - -### (verbatim) - - 砥 ன 窨. -⥪ ⮡ࠦ ? ? ࠧ Markdown: - -``` -#include -#include - -int main(void) { - puts("Hello, Sprinter!"); - return 0; -} -``` - -### ਧ⠫쭠 - - ᨬ `---`, `***` `___` ⤥쭮 ப: - ---- - -## 孨᪨ ࠪ⨪ - -- **ଠ:** Sprinter, Z80 @ 21 -- **஢:** CP866 / CP1251 / KOI8-R / UTF-8 (⮮।, `F8`) -- **ᨬ ࠧ 䠩:** 128 -- **ᨬ쭮 ᫮ ப :** 16 384 -- **ᨬ쭠 ப ७-:** 255 祥 (. ࠭祭 ) -- ** :** small - - ணࠬ, c⥪, , ? a W1-W2 (32 , 0x4000?0xBFFF). - - 䠩 ? ࠭ EMM, ⮡ࠦ W3 (0xC000?0xFFFF) - -## ࠭祭 - -### ப MD-० ? 255 祥 - -- ࠭ ப ** 255 (ᨬ, ਡ)** -? ⠭ `MAX_CACHE_LINE_LEN`. ⨯: `g_ncells` -`cache_rec_t.len` ? `uint8_t`. ᠥ ப, ⭥ ᥣ -**ਧ⠫쭮 ஫㥬** ப ( ப ⠡, 䫠 -`IF_HSCROLL`), MD-० ࠢ. - -** ந室 ப:** 樨 `gc_put()` -뢠 祩 ᫥ 255- (`if (g_ncells < MAX_CACHE_LINE_LEN)`). - ⮫쪮 255 祥, ⮪ **** ? -஫ ** થ १** 255- 樨 (થ `>` 砥 -? । ?). ९ ? `gc_put` ஢ ࠭. - -: ?255 祥? ? **७७ ਭ**, 室. - ࠧ稢 ஡ ( `TAB_STOP`), 祩 ⠡ -஡ ਭ + ࠬ `` ? ⮬ ⨣ ࠭, 祬 -255 ?? ᨬ. - -> **RAW-० (`F2`) ⮣ ** ? אַ 䠩 -> ⮢, ப 楫 (१ wrap `F3` ਧ. ஫). - -** ** (業, ॠ) ? `uint16_t`: -- `cache_rec_t.len` `uint8_t?uint16_t` ? **஢ 8 ** - (len ꥤ pad-), `idx<<3` . ᯫ⭮. -- `g_ncells` / `g_ncells_at_space` ? `uint16_t` ? 業 **/᪮**: - 16-⭠ 䬥⨪ Z80 祬 `gc_put` (맮 祩) - scan-横. ਥ: **+0.2?0.4 ** + 樨. -- `g_cells[MAX_CACHE_LINE_LEN*2]` near-RAM (W2) ? 業 - ****: `2 ? ` . 510 ; 512 ? +0.5 , 1024 ? +1.5 . - EMM- ⥭ (1 ) ப ﭥ ? 㧪 near. -- `viewport_x` ( `docset_t`) + ⮫ `max_vx` (ᥩ 248) ? `uint16_t`, - ࠭ >255 , ஫ . `widths[]`/`ccx` ⠡/. - - ஬ ? 512?1023: 墠 ᥬ ⨭/⠡栬, - 業 ~+0.3 +0.5?1.5 near-RAM. ** 㬥 `uint16_t`** ? - ⠢ થ १ 255- 樨, ⮡ 뫠 . - -## TODO - -1. **祭 ࠧ 㬥⮢.** 128 : 筮 - ࠧ ࠡ 訬 - ࠭ , ஢ - ࠡ 8- ࠭栬 16. - -: ଠ஢ ⠡ ࠬ; প ஢ -CP866 / CP1251 / KOI8-R / UTF-8 ⮮। ४祭 `F8` -(ன ந , ࢮ ४祭). - -4. **᪮७ ७ਭ.** ப ࠭. ⨬ 横 뢮 - ᨬ १ BIOS WRCHAR ( 뢮, DMA). - ---- - -*MDView v0.2 (c) 2026 ஢ ..* - - ---- - -# Sprinter C Compiler ? v1.0 - -C toolchain for **Sprinter** ? the Z80-based home computer by Peters Plus, running -ESTEX DSS. Host: macOS / Linux. Target: `.EXE` files in SprintEXE format. - -Built on top of **SDCC 4.5** (vendored in `third_party/sdcc/`). This repository adds -everything Sprinter-specific: crt0, linker integration, libc wrappers over ESTEX, -banked-call trampolines, graphics & accelerator API, mouse driver wrappers, and the -`mkexe` utility for producing SprintEXE images. - -## What you get - -* **`bin/sprinter-cc`** ? one-line driver: `sprinter-cc -o foo.exe foo.c` -* **Memory modes**: `tiny`, `small`, `big`, `huge`, `manual` ? see below. -* **stdio + conio**: printf, puts, putchar, getchar, fopen/fread/..., cprintf, cputs, putch, textcolor/textbackground/textattr, gotoxy, kbhit/getch. -* **Graphics**: 320?256?256 and 640?256?16 modes, accelerator-backed primitives (hline / vline / rect / fill_rect / line via Bresenham, plus clear), bitmap-font text in both modes via BIOS character generator. -* **File I/O**: POSIX (`open`/`read`/`write`/`close`/`lseek`/`unlink`/`creat`), FILE\* streams (`fopen`/`fgets`/`fwrite`/...), directory listing (`ffirst`/`fnext`), `chdir`/`getcwd`/`mkdir`/`rmdir`, `stat`/`fstat`. -* **Memory**: 32 KB heap (W2-resident), banking-aware page allocator (`mem_alloc_pages`/`bank_read`/`bank_write`), explicit memory modes for sub-16 KB programs. -* **Mouse**: full Sprinter driver wrapper (14 functions including custom cursor bitmaps). -* **Environment**: `getenv`/`putenv`/`sysenv` over ESTEX `$46`. -* **Time**: `getdatetime`/`setdatetime` + POSIX `time`/`localtime`/`mktime`/`asctime`/`ctime`. -* **Misc**: `errno`/`strerror`/`perror`, `atexit`, `setjmp`/`longjmp`, `sleep`, full argv parsing in crt0. - -## Quick start - -```sh -git clone sprinter-c -cd sprinter-c -make sdcc # one-time: fetch SDCC 4.5 binary (~25 MB) -make all # build mkexe + libsprinter.lib + 27 examples -make floppy # pack everything into mame/v306/IMG/mc.img -cd mame/v306 && ./run_mame.sh # boot Sprinter in MAME -``` - -Compile a single program: - -```sh -cat > hello.c < -int main(void) { puts("Hello, Sprinter!"); return 0; } -EOF - -bin/sprinter-cc -o hello.exe hello.c -``` - -That's it ? `hello.exe` is now a valid SprintEXE you can `RUN HELLO` from the ESTEX shell. - -## Memory modes - -Sprinter's address space is four 16 KB windows (W0 / W1 / W2 / W3). DSS allocates -pages by program size ? small programs get only one page. Pick a memory mode based -on what your program needs: - -| Mode | Code lives in | Banking | Use when | Note | -|---|---|---|---|---| -| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | | -| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | | -| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | | -| `huge` | W1-W2 + W3 banking | yes (W3) | small + extra code modules | | -| `manual` | user-specified | optional | special layouts | Not implemented | - -```sh -sprinter-cc --memory small -o big.exe bigprog.c -sprinter-cc --memory huge -o app.exe main.c --bank 1=engine.c --bank 2=ai.c -``` - -Banked functions are declared with `__banked`: - -```c -void engine_tick(int dt) __banked; // lives in BANK1, automatically swapped -``` - -## Examples (27 total) - -| Example | What it demonstrates | -|---|---| -| `hello` | Hello world with stdio + conio Turbo-C-style colors | -| `argv` | argv parsing in crt0 | -| `cat` | File I/O ? read & print TEST.TXT | -| `seek` | 32-bit lseek over a 100 KB file | -| `ls` | Directory listing via ffirst/fnext | -| `filetest` | FILE\* streams (fopen/fread/...) | -| `stattest` | `stat`/`fstat` on files and directories | -| `errno` | errno / strerror / perror | -| `mem_test` | Page allocator + bank\_read/bank\_write | -| `malloc` | Heap stress test (200+ allocations) | -| `banked` | Banked code in W3 (huge mode) | -| `bankedbg` | Banked code in W1 (big mode) | -| `banklocl` | Bank-local static data and BSS | -| `mouse` | Mouse driver in text mode | -| `gfx_mous` | Mouse with custom bitmap cursor in graphics mode | -| `gfx_demo` | 320?256?256 lines / rects / accelerator | -| `gfx_d16` | 640?256?16 same primitives | -| `gfx_text` | Bitmap-font text on graphics screen | -| `timedir` | Date/time + directory listing | -| `ptime` | POSIX time API | -| `openenv` | open() flags + environment vars | -| `conio` | conio API smoke test | -| `attrprob` | Probe Sprinter text attribute byte layout | -| `strtest` | string.h test (from SDCC's z80.lib) | -| `stdlib` | stdlib.h test (qsort / rand / strtol / etc.) | -| `assrtest` | assert() | -| `rt_test` | Runtime helpers (sleep, setjmp, atexit) | - -## Headers - -Standard: -* `` ? puts / printf / FILE\* + Sprinter-specific dec/hex helpers -* `` ? atoi / atof / malloc / qsort / ... (from SDCC z80.lib) -* `` / `` / `` ? from SDCC z80.lib -* `` ? read / write / close / lseek / unlink -* `` ? open / creat + O\_RDONLY / O\_CREAT / ... -* `` ? errno + error names + strerror -* `` ? stat / fstat -* `` / `` ? from SDCC - -Sprinter-specific: -* `` ? putch / cputs / cprintf, textcolor / textbackground / textattr, kbhit / getch, clrscr, gotoxy, wherex/y -* `` ? gfx_init/done, palette, putpixel, hline/vline/rect/fill_rect/line, text ? for both 320 and 640 modes (gfx_\*16 variants) -* `` ? full 14-function driver wrapper + mouse_cursor_t with bitmap support -* `` ? chdir / getcwd / mkdir / rmdir / ffirst / fnext / ffblk -* `` ? getdatetime / setdatetime + POSIX time / localtime / etc. -* `` ? raw ports, ESTEX/BIOS function numbers, env API -* `` ? exit / \_exit / atexit -* `` ? mem\_alloc\_pages / mem\_free\_block / bank\_read / bank\_write -* `` ? Solid-C compatibility layer (aliases + BOOL/WORD/uint types) - -## Toolchain commands - -```sh -make all # build mkexe + lib + every example -make floppy # repack mame/v306/IMG/mc.img with all .exe files -make check # 17 mkexe unit-tests -make clean # remove all build artefacts -make sdcc # one-time: fetch SDCC 4.5 binary -``` - -### sprinter-cc options - -``` -sprinter-cc -o foo.exe foo.c [more.c ...] [options] - - --memory MODE tiny | small | big | huge | manual (default: tiny) - --memory-manual SPEC explicit placement (CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3) - --stack-size N bytes reserved for the stack (default ~1278) - --crt0=TYPE default | minimal | banked | small - --bank N=FILE.c compile FILE.c into bank N (repeatable, max 15) - --debug enable runtime diagnostics (defines DEBUG_RT) - -I PATH extra include path - -L 0xADDR / -E / -S override load / entry / stack addresses - -Wl FLAG pass FLAG to sdldz80 - --mkexe FLAG pass FLAG to mkexe (e.g. --mkexe -p --mkexe 0 for bank padding) - -v verbose -``` - -## Status - -What works in v1.0: -* Compile / link / pack to SprintEXE ? verified on all 27 examples -* Four memory modes (tiny / small / big / huge) -* Graphics (both modes) with accelerator -* Mouse (text + graphics cursor) -* File I/O, directories, environment, time -* All headers listed above - -Deferred to v2.0 (see `docs/TODO.md`): -* **Turbo-C-style BGI graphics API** ? `initgraph` / `setcolor` / `circle` / - `getimage` / `putimage` / etc. on top of our `gfx_*` primitives -* Remaining Solid-C compatibility gaps (Phase 2/3) ? see `docs/solid_c_compatibility.md` -* Manual memory mode -* Rewrite FILE\* stream API (current implementation is very primitive and doesn't use buffers) - -Deferred to v3.0: -* **IM2 interrupt handlers** ? research complete (`docs/im2_isr_design.md`), - implementation scheduled for v3 -* **Audio API** (AY-3-8910 + COVOX) ? requires IM2 -* **ISA-8 slot drivers** ? requires IM2 (???) - -## Documentation - -* `docs/TODO.md` ? roadmap and open work items -* `docs/solid_c_compatibility.md` ? gap analysis vs Solid-C 2004 -* `docs/im2_isr_design.md` ? interrupt handler design (v2) -* `docs/converted/` ? source documentation (ESTEX, BIOS, architecture) - converted to plain text for `grep` -* `docs/reference/`, `docs/samples/`, `docs/memory management/` ? original - Russian docs and code samples from Peters Plus - -## Repository layout - -``` -bin/sprinter-cc one-line compiler driver (bash) -toolchain/mkexe/ host-side tool: .ihx -> .exe SprintEXE -toolchain/check_banks.py post-link bank size enforcer -runtime/ crt0 variants (default, minimal, small, banked) - bank trampolines, heap, heap_top -libc/include/ headers -libc/io|stdio|mem|gfx/ C and asm sources for libsprinter.lib -lib/ Makefile that archives libsprinter.lib via sdar -examples/ 27 example programs -mame/v306/ MAME binary + Sprinter ROM/HDD images + floppy script -third_party/sdcc/ vendored SDCC 4.5 (fetched via `make sdcc`) -third_party/solid-c/ reference: original Sprinter native C (for compat target) -docs/ documentation -``` - -## License - -This repository contains: -* Original code in `bin/`, `toolchain/`, `runtime/`, `libc/`, `lib/`, `examples/` ? - MIT-licensed. -* `third_party/sdcc/` ? SDCC 4.5 under GPLv2 with linking exception - (see `third_party/sdcc/COPYING.txt`) -* `third_party/solid-c/` ? original Sprinter Solid C, used only as a reference - -## Credits - -* **Sprinter / Peters Plus** ? , ਩ ਭ and the original team -* **SDCC** ? for the underlying Z80 compiler -* **MAME** ? for the Sprinter Sp2000 emulation - ---- - -For questions / patches: see CONTRIBUTING.md (TBD) or open an issue. - ---- - -# TODO / Roadmap - - 浪 뢠 ਮ.  ॠ ணࠬ ? ਮ ᬥ. - -## ⠯ 5 ? malloc / free + banking-aware page allocator ? - -- [x] SDCC's `malloc`/`free` + `runtime/heap.s` ( library heap.rel, 14000- heap 2) -- [x] `libc/mem/mem_alloc.c` ? page allocator: `mem_alloc_pages`/`mem_free_block`/`mem_get_page`/`mem_info` १ ESTEX `$3C/$3D/$3E` + BIOS `$C4` -- [x] `libc/mem/bank_io.c` ? HOME-१ `bank_read`/`bank_write`/`bank_load_byte`/`bank_store_byte` ᢮ W3 -- [x] `examples/malloc_test/` ? ஢ઠ SDCC's malloc (~210 64- allocations १ heap) -- [x] `examples/mem_test/` ? ஢ઠ page allocator: 3 ࠭, ࠧ १ bank_write, 䨪 १ bank_read - -## ⠯ 6 ? argv parsing + sprinter-cc wrapper ? - -- [x] crt0 ESTEX command-line IX-prefix (inline asm `runtime/crt0.s`) -- [x] Strip leading CP/M-style space (DSS quirk) -- [x] । `argc`/`argv` main() १ HL/DE (SDCC __sdcccall(1) ABI) -- [x] argv[0] = basename .EXE १ ESTEX APPINFO ($47 subfn 2) -- [x] `runtime/crt0_minimal.s` ? opt-out 祭 쪨 ணࠬ -- [x] `runtime/crt0_banked.s` ? ⥯ ⮦ argv (parse_argv + get_progname ᪮஢ crt0.s; 㤥 factored argv.s libsprinter.lib) -- [x] Bash-⪠ `bin/sprinter-cc`: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権: `--memory`, `--memory-manual`, `--stack-size`, `--crt0=`, `--bank N=FILE.c`, `--debug`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` - -## ⠯ 8 ? 䨪 (320?256?256 + 640?256?16 + accel + bitmap font) ? - -- [x] **8a** Graphics core: `gfx_init`/`gfx_done`/`gfx_clear`/`gfx_putpixel`/`gfx_pal_load`/`gfx_pal_set` (libc/gfx/gfx_core.c). १ BIOS PIC_SET_PAL ($A4). Verified 320?256?256. -- [x] **8b** /אַ㣮쭨/fill १ accelerator (libc/gfx/gfx_lines.c): `gfx_hline`/`gfx_vline` १ accel Fill (LD C,C / LD E,E + SMC block-size), `gfx_rect`/`gfx_fill_rect` heuristic 롮஬ ਥ樨 (h/v bursts count), `gfx_line` Bresenham . `gfx_clear` ⮦ ९ᠭ column-major accel (~4? ॥). -- [x] **8c** 640?256?16 mode (libc/gfx/gfx_16.c): `gfx_*16` API, HIGH nibble = LEFT pixel (㬥 misleading), per-row RMW vline ( = 2 ਧ⠫ ᥫ). -- [x] **8d** Bitmap font + gfx_text (libc/gfx/gfx_text.c): १ BIOS WIN_GET_ZG ($B8), interleaved layout `font[row*256+char]`, `gfx_text`/`gfx_putchar` 320 mode, `gfx_text16`/`gfx_putchar16` 640 mode pair-table lookup. - -. memory/sprinter_graphics.md, sprinter_accelerator.md, sprinter_graphics_16.md, sprinter_font_format.md. - - ( ): -- [ ] -quad 640: per-cell (mode 0x82 ࠧ蠥 1 4 per 16?8 cell) ? १ אַ area-ᠭ ࠭ 0x0300..0x039F - -## Auto-banking (. `memory/banking_roadmap.md` ⠫) - -Phase 1 ? file-level bin-packing ? ॠ뢠 ஥ ~30 KB . - -- [ ] `toolchain/auto_bank.py`: - - ࠧ `.rel`-䠩 ( .map ᫥ dry-run link') - - First-fit-decreasing bin-packing - - `#pragma codeseg BANKn` manual override - - ५뢠 묨 `-Wl-b_BANKn=...` ࠬࠬ - - ⠥ । - -Phase 2-5: incremental rebalance, declarative `banks.toml`, function-level, call-graph-aware. 쪮 ᫨/ . - -## Bank-local static data (mutable data ⮬ ) ? ? - -- [x] ਬ `examples/bank_local_data/` ? 㭪 BANK1 ᢮ writable BSS array + const table + malloc- -- [x] `mkexe -p 0` 㫥 padding (BSS-storage 㧪) -- [x] ᪨ 楯: `--codeseg BANK1 --constseg BANK1 --dataseg BANK1` bank1.c + `-Wl-b_BANK1=0x1C000` . **`--dataseg BANK1` ** ? ࠭ ⭮ - trampoline bug ᪨஢ १. -- [x] ** 䨪 trampoline'a runtime/bank.s** ? `pop af; out (n), a` ਫ A ? banked-㭪樨 騥 uint8_t 頫 . `pop bc; out (c), b` ࠭ A. -- [x] **malloc banked-㭪樨 ࠡ⠥ ஧筮** ? heap W2 (HOME), W2 ᢠ trampoline', pointer  ⥪. . memory/bank_local_data_pattern.md. -- [x] 㬥 memory: `memory/bank_local_data_pattern.md` ( 楯 + malloc + nuances), `memory/sdcc_banking.md` (trampoline fix) -- [ ] 樮쭮 ? `check_banks.py` ⮡ 뢠 ࠧ size = code + const + bss per bank (cosmetic) - -祬: 㫥 訬 private state (level loader, audio engine, scene data). W2 heap , ⨪ . - -## ᪠ solid-c (⨢ Sprinter C ? `third_party/solid-c/`) - -᫥ solid-c'᪮ libc (. `memory/solid_c_findings.md`)  ⮢ ᫥ 㭪権. ਮ **᮪** : - -### High-priority gaps ( ஢, 짠) -- [x] **`errno` + `strerror`/`perror`** ? ⠡窠 32 訡 (libc/io/errno.c) -- [x] **७ `open()`** O_CREAT/O_TRUNC/O_APPEND/O_EXCL state machine -- [x] **`atexit`** ? 8-callback LIFO + `exit()` + `_exit()` (libc/io/atexit.c) -- [x] **`setjmp`/`longjmp`** ? 6- jmp_buf={sp,ix,pc} (libc/io/setjmp.c) -- [x] **`sleep(seconds)`** ? 50Hz halt-loop (libc/io/sleep.c) -- [x] **ESTEX ENV API** ($46, getenv/putenv) ? libc/io/env.c. 竨 doc-bug: ॠ쭮 A=0 NOT FOUND - -### Medium-priority (㦭 shell-like ⨫) -- [ ] **Mouse driver** ? `rst $30h`, 17 㭪権. **砫 ࠡ⠥ MAME**. -- [x] **`ffirst`/`fnext` + ffblk_t struct** directory listing ? ॠ, demo: ls.exe -- [x] **`getdatetime`/`setdatetime`** १ ESTEX $21/$22 ? libc/io/time.c, demo: time_dir_test -- [x] **`chdir`/`getcwd`/`mkdir`/`rmdir`** ? wrappers ESTEX $1B-$1E ? libc/io/fsdir.c -- [x] **conio: `kbhit`/`getch`/`getche`/`cputs`/`clrscr`/`gotoxy`** ? ॠ -- [x] **conio extras**: `wherex`/`wherey` ($53), `wrchar`/`rdchar` ($58/$57), `textmode_get/set` ($50/$51), `clrscr_attr` ($56) + COLOR macros - -### Low-priority ? ? FILE* stack - -- [x] ** unbuffered FILE\*** ? `libc/stdio/file.c` + `libc/include/stdio.h`. fopen/fclose/fputs/fgets/fread/fwrite/fseek/ftell/rewind/feof/ferror/clearerr/fflush + stdin/stdout/stderr pseudo-streams. . `memory/file_star_design.md` `examples/filetest`. -- [ ] fprintf / fscanf ? 㦭 printf-१-callback machinery. 짮⥫ `sprintf(buf, ...) + fputs(buf, fp)`. -- [ ] 樮 buffered mode (setvbuf, line/block buffering) ? ᫨ - . - -### POSIX time API ? ? -- [x] `libc/io/posix_time.c` ? time/localtime/gmtime/mktime/asctime/ctime getdatetime. SDCC's time.rel ( override _RtcRead). . `examples/ptime`. - -### sys/stat ? ? -- [x] `libc/io/stat.c` ? POSIX stat/fstat. ਤ open+fstat 䠩, ffirst+iter ( "."/".."). . `examples/stattest` `memory/estex_ffirst_dotdot.md`. - -### assert ? ? (ᯮ㥬 SDCC's __assert १ fallback include path) - -## libc/stdlib ? ? 㦭 (. memory/sdcc_stdlib_works.md) - -஢७ १ `examples/stdlib_test/`: SDCC z80.lib ᮤন ࠡ騥 ॠ樨: -- `atoi/atol/atof, strtol/strtoul, rand/srand, qsort/bsearch, abs/labs, div/ldiv` -- `` (memchr/cmp/set/cpy, strcat/cmp/cpy/len/chr/spn/etc.) -- `` (toupper/tolower) -- `` (sinf/cosf/sqrtf/etc.) - - ⮬᪨ ﭥ z80.lib 㦭. ** ९뢠**. - - Sprinter-specific 易⥫ 㫨 : atexit, env, errno, setjmp, putchar/puts/getchar, conio, fsdir, time, mouse, open/read/lseek/close. - -## Build-system: libsprinter.lib + sprinter-cc ? ? - -- [x] `lib/Makefile` ? ᮡࠥ libc/*.c `.rel`, 娢 १ sdar `lib/sprinter.lib` -- [x] 砥 runtime/bank.s runtime/heap.s (auto-pulled __banked/malloc) -- [x] `bin/sprinter-cc` ? bash-wrapper: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権 `--crt0=default|minimal|banked`, `--bank N=FILE.c`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` -- [x] `examples/hello_sccc/` ? : `hello.c` ᮡࠥ shell-맮, ࠧ ᮢ Makefile (925 ) -- [x] Split `putchar.c` ? `putchar.c` + `puts.c` per-function granularity (puts override SDCC's z80.lib version) -- [x] `make all` (ᨬ `lib` । `examples`) - - 襭 (樮쭮): -- [ ] ஢ ⠫ examples sprinter-cc Makefile (ᬥ⨪) -- [ ] 쭥 libc/*.c per-function ( ⥪ granularity 㦥 㦭 ࠧ ? .rel 楫, 設⢠ 䠩 㭪) - -## ⠯ 9 ? memory modes sprinter-cc - -DSS 뤥 ࠭ ࠧ ਫ: < 16 KB ? ࠭, ⠫ 砥 ?࠭ #FF? (read=0xFF, write ). - ⮣ CODE--W1 + DATA--W2 쪮 ணࠬ . . [memory/sprinter_memory_modes.md](../../.claude/projects/-Volumes-SAM8-Projects-DIY-Z80-Sprinter-C-Compiler/memory/sprinter_memory_modes.md). - -- [x] **`tiny`**: (CODE+DATA+⥪) W2. Default. Verified hello/argv/conio/malloc/file/etc. -- [x] **`--memory MODE` 䫠 sprinter-cc**: parser + per-mode 䮫 CODE_LOC/DATA_LOC, override १  `--code-loc`/`--data-loc`. tiny ࠡ⠥; small/big/huge warning' (runtime ⮢). 2026-05-30. -- [x] **`--memory-manual SPEC`**: `CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3`. 2026-05-30. -- [x] **`small` runtime**: `runtime/crt0_small.s` ᯮ ESTEX `$3D GETMEM` + `$3A SETWIN2` ⮡ 뤥 W2-࠭ gsinit. **** BIOS `$C4` ? ⥪ ⮬ ⠯ W1 (boot_stack HOME), BIOS ॡ ⥪ W2. ᫥ SP ४砥 0xBFFE, ⠭ flow. 2026-05-30, verified hello.exe. -- [x] **`small` auto-detect >16 KB ணࠬ**: `crt0_small.s` ⠥ `0xC2` (⥪ ࠭ W2 ? `0xA2`! W1). ᫨ 0xFF ? 뤥 page; DSS 㦥 ᤥ (ணࠬ ᠬ 뫥 W2). crt0 뢠 0..30 KB. mkexe ⠪ ࠧ蠥 HOME span W1+W2 (0x4000..0xBFFF). Verified hello: small (5 KB 䠩, SETWIN2 path) + 32 KB 䠩 (auto-skip). 2026-05-30. -- [x] **`big` runtime** (tiny + banked code W1): ࠬਧ `crt0_banked.s` + `bank.s` १ `.ifdef BANK_W1` ? 㣮 banking port (0xA2 vs 0xE2), 㣮 load-addr (0x4000 vs 0xC000). sprinter-cc prepend' `BANK_W1 = 1` `--memory big`, । `mkexe -B 0x4000`. ਬ `examples/banked_big/`. 2026-05-30. -- [x] **`huge` runtime** (small + banked code W3): merge W2-detect `crt0_small.s` `crt0_banked.s`. 騩 ਬ `examples/banked/` ⥯ ᯮ MEMORY=huge. 2026-05-30. -- [x] **`--debug` 䫠**: prepend `DEBUG_RT = 1` crt0 + `-DDEBUG_RT` sdcc. 뢠 symbol `_w2_self_allocated` (uint8_t) ? runtime diagnostic ஢ W2. 2026-05-30. - -### -襭 libc crt0 - -** `sprinter.lib`** ࠡ⠥ memory mode ? `.rel`-童 relocatable, SDLD dead-code elimination per-member ( 䨪  `gfx_core.rel` ..). Verified hello vs malloc_test १ map-䠩. - -**`gfx.lib` ⤥쭮 ? 㦥**: dead-code elimination 㦥 ࠡ⠥. - -**`libc_banked` (libc bank HOME)** ? ⮬, HOME (16 KB) user- + libc `huge` mode. १ `--codeseg BANK0` 樨 libc; trade-off: trampoline ~30 横 libc-맮. ਣ: ॠ쭠 ணࠬ HOME budget. - -**HW-ᨬ 㫨 ? `sprinter_home.lib` ⤥쭮.** libc 䨧᪨ W3, ⮬ W3: -- `gfx_*` ? `0xC000+` ᫥ swap W3 video page -- `bank_io` (mem_alloc_pages/bank_read/bank_write) ? swap' W3 १ `OUT (0xE2)` -- 騥 ISR ? 뢠 ਩ W3 㣮 - - huge mode 㫨 HOME (W1). 㤥 `libc_banked`, **६** 뤥塞 `sprinter_home.lib` (HOME-only) `sprinter.lib` (bankable). 쭠 奬: -``` -sprinter_home.lib HOME-only: gfx, bank_io, ISR shims -sprinter.lib bankable: printf, malloc, string, conio, stdio, env, ... -sprinter_banked.lib sprinter.lib --codeseg BANK0 ( huge) -``` -ਣ: ॠ `--memory huge` runtime. - -**crt0 ? mode:** -- `crt0.s` ? ⥪騩, **tiny/big**: SP=0xBFFE, argv (W2- 㦥 뤥 DSS). -- `crt0_minimal.s` ? ⥪騩, tiny argv. -- `crt0_small.s` ? **, step 3**: **small/huge**, W2 १ `mem_alloc_pages` gsinit, `0xA2`, ⮬ ⠭ flow. -- `crt0_banked.s` ? ⥪騩, **big**: trampoline-⠡ W3 , CODE W2. -- `crt0_banked_small.s` ? ****: huge = small (W2-alloc) + banked (W3 trampolines). - -sprinter-cc ࠥ crt0 `--memory` mode (ᥩ `--crt0=` override). -- [x] **ࠨ ࠧ ⥪**: 䫠 `sprinter-cc --stack-size BYTES`. Wrapper `heap_top.s` `___sdcc_heap_end = stack_top + 1 - stack_size`, ⤥ .rel per-program. Default ?1278 (heap_top=0xBB00) `runtime/heap_top.s`. 2026-05-30. - -䥩: `sprinter-cc --memory [tiny|small|big|huge|manual] [--memory-manual SPEC] [--stack-size N] foo.c`. `--memory-manual` ⮫쪮 `--memory manual`. - -## Known issues / quirks - -- **ESTEX $46 ENV API**: ? ࠡ⠥. - `DiskSyscalls.txt v1.6` 訡筮 뢠 return-status ? A=0 NOT FOUND, FOUND. 䨪஢ `memory/sprinter_platform.md`. - -## V2 (᫥ ५ v1) - -### Turbo-C-style graphics API (BGI-like) ? **MUST v2** - - `gfx_*` API ஢ **Turbo-C ``** (BGI) MS-DOS. -ணࠬ ਢ訥 Turbo-C ७ ᪨ 1--1. - -** ** ( ᭮ Borland BGI): - -Setup/teardown: -- `initgraph()` / `closegraph()` ? ᥩ `gfx_init`/`gfx_done`, alias -- `getmaxx()` / `getmaxy()` ? GFX_WIDTH-1 / GFX_HEIGHT-1 -- `cleardevice()` ? alias to gfx_clear -- `getgraphmode()` / `setgraphmode()` ? get_videomode/set_videomode - -Color/palette: -- `setcolor(c)`, `getcolor()` ? current draw color -- `setbkcolor(c)`, `getbkcolor()` ? background color -- `setpalette(idx, c)` ? entry -- `getpalette(&info)` ? read all palette - -Primitives ( 㦥 ? BGI- aliases): -- `putpixel(x, y, c)` ? gfx_putpixel -- `getpixel(x, y)` ? 㦭 ॠ (RMW ⭮ ? IN) -- `moveto(x, y)`, `lineto(x, y)`, `linerel(dx, dy)` ? current point + line drawing -- `line(x1, y1, x2, y2)` ? gfx_line -- `rectangle(x1, y1, x2, y2)` ? gfx_rect ( 㣮 API: x1,y1,x2,y2 vs x,y,w,h!) -- `bar(x1, y1, x2, y2)` ? gfx_fill_rect -- `bar3d(x1, y1, x2, y2, depth, topflag)` ? : rect + 3d edges -- `circle(x, y, r)`, `arc(...)`, `ellipse(...)`, `pieslice(...)` ? primitives -- `fillpoly()`, `drawpoly()` ? -- `floodfill(x, y, border_color)` ? - -Text on graphics screen: -- `outtext(s)` / `outtextxy(x, y, s)` ? gfx_text (alias) -- `settextstyle(font, dir, size)` ? multiple bitmap fonts -- `gettextsettings(&info)` -- `textwidth(s)` / `textheight(s)` ? measure - -Image manipulation: -- `imagesize(x1, y1, x2, y2)` ? bytes needed for getimage -- `getimage(x1, y1, x2, y2, buf)` ? save rect to buffer -- `putimage(x, y, buf, op)` ? paste back with COPY_PUT/XOR_PUT/AND_PUT/OR_PUT/NOT_PUT - -Clipping/viewport: -- `setviewport(x1, y1, x2, y2, clip)` ? drawing clip rect -- `getviewsettings(&info)` -- `clearviewport()` -- `setactivepage(p)` / `setvisualpage(p)` ? ਧ (Sprinter 2 screen) - -Line style: -- `setlinestyle(style, pattern, thickness)` ? SOLID_LINE / DOTTED_LINE / etc. -- `getlinesettings(&info)` - -**Acceptance:** ७ ⨯筮 Turbo-C BGI ணࠬ (饩 ᯮ짮 -moveto/lineto/circle/bar/setcolor) ࠡ ⢥ ࠢ. - -**Notes:** -- BGI fonts (TRIPLEX/SANS_SERIF/GOTHIC) ? BIOS font, ⠫ 㦭 - ( bitmap data lib) -- imagesize/getimage/putimage ? ᠬ ॡ game/animation -- Active/visual page ( ਧ) ? Sprinter ন 2 graphics pages, - 㦥 API switching - -. ⠪ `examples/` Turbo C 2.x BGIDEMO reference 㦭. - -### IM2 Interrupt Handlers ? **MUST v2** - -User- ISR १ Z80 IM 2 mode. 㦭 : -- Timer ticks (50 Hz frame counter, ) -- Music playback (AY, COVOX) -- Real-time games (input + game logic + render interrupt-driven) -- Async keyboard / mouse handling - -**Status:** v2. research + design `docs/im2_isr_design.md`. - -**襭 ⥪:** ॠ ⤥ memory mode `--memory im2` -( ⮣ ⮡ 騥 crt0). Detail' design-doc. - -** research'** ( ⥪ `docs/im2_isr_design.md`): -- Vector 0xFF ? frame + keyboard + CBL. Disambiguation ⠬ 0x19 / 0xFE -- Mouse hardware-IRQ 室 ( ⥪饩 ) -- Vector table / ISR / stack W2 (0x8000..0xBFFF) -- DSS ᢮ IM 2 handler ? 㦭 chain' ( / SYSTIME ) - -### 稥 㯭 㭪 v2 - -- [ ] **FILE API rewrite ? buffered streams** ? ⥪ ॠ - `libc/stdio/file.c` provisional unbuffered shim ( fputc/fgetc - = read/write syscall). 㦭 業 buffered ᥬ⨪ - Solid-C: - - ```c - typedef struct { - uint flags; // +0..1 file status flags - int level; // +2..3 empty/fill level of buffer - char *curp; // +4..5 current active pointer - int fd; // +6..7 underlying low-level fd - char *buffer; // +8..9 data transfer buffer - char hold; // +10 ungetc byte if no buffer - short token; // +11..12 reserved - char dummy; // +13 reserved - } FILE; - ``` - - stdin/stdout/stderr ? fd-થ `0 / -1 / -2`. ⥫ - stdout/stderr ࠭ ७: ESTEX OPEN positive - small fd (1, 2, ?) 筮 䠩 ? ᫨ stdout=1, ॠ - fd=1 ⠫ 䨪஬. fd=0 stdin ᭮ - (ESTEX 0 頥). fd । syscall' ? - ᯥਧ 䫠 `_F_CONIN/_F_CONOUT`. - - ਭ-⮪ (stdaux/stdprn) ॠ㥬 ? Sprinter ਭ୮ - API . - - ୠ⨢ ? ॠ third_party/solid-c (sources - `SRC/CLIB/`); ⠬ ⮢ buffered FILE + fopen/fread/fwrite/ - fseek/setvbuf .. ஢ 訬 open/read/write/lseek. - - rewrite deferred issues stdio-review: - - `fwrite` short-write ⠢ `_F_ERROR` - - `fgets(buf, 1, fp)` ? ⠭ "empty string", 㫨 NULL - - `mode_to_flags` ? break-out '+' (cosmetic) - -- [ ] **Audio API** ? AY-3-8910 + COVOX १ 뢠 (ॡ IM2) -- [ ] **ISA-8 slot support** ? ZX-Bus (sound, network, etc.) ? ॡ IM2 + ⥭ port - -## 稥 (v1 backlog, 騥) - -- [x] **#9: text I/O split (Turbo-C style)** ? stdio (puts/printf/putchar) ⥯ fast no-attr १ PCHARS/PUTCHAR. conio (cputs/cprintf/putch) ਬ attr १ textcolor/textbackground/textattr. KEEP_EXIST_ATTR ? conio fallback fast path. Verified hello.exe. . `memory/text_output_api_split.md`. 2026-05-31. -- [x] **Mouse API ** (१ driver, RST 30h) ? 14 㭪権 (init/show/hide/refresh/read/goto/bounds/text_cursor/load_cursor/get_cursor/get/set_sensitivity/video_mode_changed). . `memory/mouse_api.md`. Verified MAME 2026-05-31. Sensitivity = divider ( = ॥). -- [ ] Interrupt handlers ? IM 2 vector table HOME user ISR' -- [ ] প `restore SP on EXIT` ( z88dk +pps) ? ஢ 㦭 -- [ ] CI: ⮬᪨ ᪠ MAME `-aviwrite` screenshot-ࠢ, ⮡ ਬ஢ 室 祫 - -## ⮬ - -- প `` ( SDCC stdlib ? 㦭 ஢ crt0 ᮢ⨬) -- `` १ ESTEX SYSTIME (`$21`) CMOS BIOS-㭪樨 -- ZX Spectrum-ᮢ⨬ ० ⤥ target ( ஢ ᯥ㬮᪨ ணࠬ) -- প ZX-Bus (sound, network, etc.) ? 㦭 ࠩ -- Profile-guided optimization tools (hot/cold detection) 㯭 ணࠬ - -## Linker duplicate-symbol warnings (, 䨫஢) - - ᮧ⥫쭮 overrides' SDCC z80.lib 㭪樨 ᮡ⢥ ᨥ `sprinter.lib`, `sdldz80` `?ASlink-Warning-Definition of public symbol '...' found more than once`. ࢮ । (), ⮬ ४⭮ ? warning ⮫쪮 noise. - -騥 overrides: -- `_puts` ? १ PCHARS+\r\n vs SDCC posix puts -- `___sdcc_heap` ? heap W2 vs SDCC's ⠭ -- `_asctime`, `_localtime` ( 㣨 time) ? `posix_time.c` १ ESTEX SYSTIME vs SDCC's `time.rel` `_RtcRead` - -**饥 襭:** `bin/sprinter-cc` 䨫஢뢠 warning- (warning + 2 follow-up `Library:` ப) 뢮 `sdcc`. १ `-v` (verbose) 뢠. १ awk-pipe. - -** 襭:** -- ३ explicit `--nostdlib` + 筮 ᯨ᮪ 㦭 㫥 z80.lib (string, math, stdlib override') ? warning', ᪨஢ -- : २ `_puts` ? `_puts_sprinter` + alias १ linker flag ( 㢥७ SDCC ন) -- : ⠢ ᥩ (ࠡ祥 benign) ? ਮ - -## TODO: ஢ ॠ쭮 - -- [ ] **Port_Y banking trick** (`docs/part2/SprinterGraphics programming.txt`): - ⢥ত ᫥ `OUT (0x89), Y` 0xC000+0x400*N W3 - ப Y..Y+15 ( ணࠬ஢ ? 16 ப). - Empirical 2026-06-01 MAME 0.283 trick ** ࠡ⠥** ? ᥫ - ᠬ 0xC000+row_width 室 . ᪨ - `docs/samples/plasma2.asm` ⮦ ᯮ banking, ⠭ - Port_Y per row. - : - 1. ॠ쭮 Sprinter - 2. dual-write (`_gfx_putpixel_raw` + ன write `0xD000+x`) - 3. ᫨ ? MAME, issue - ९ - 4. ᫨ ⮦ ? 㬥 , 㤠 㯮 - ⠢ ⥪ ॠ (Port_Y per pixel) - 5. ᫨ banking ࠡ⠥ ? ஢ Port_Y - `_gfx_putpixel_raw` (sentinel out-of-range, . memory/gfx_port_y_banking.md) - - 易 먣 Bresenham (60-pixel ) ? 8? - OUT (0x89) 権, `gfx_fill_rect 320x256` ? 16? . - release v1. - -## GFX: ७ `docs/part2/accelerator_doc.txt` - -᫥ ⥭ ⠫쭮 accelerator doc  ࠢ. - ᯮ ⮫쪮 ਧ⠫/⨪ Fill mode. - -### Quick wins ⥪ primitives - -- [ ] ** SMC `LD A, (var)` block-size**. 㬥  - ࠧ蠥 `LD A, (HL)`, `LD A, (BC)`, `LD A, (DE)` ( `LD A, r`). - 㡥 SMC complexity `gfx_lines.c:hfill_chunk/vfill_chunk` - `gfx_16.c:g16_hfill_chunk`. 饭 ⮫쪮 register-to-register. -- [ ] **஢ block-size**. 㬥 뢠 accel - block size bursts (. `Horizontal_Line_Fill`: ⠭ - size + `LD B,B` ⪫祭, ⮬ Fill mode ᯮ - ࠭ size). `gfx_fill_rect` 100 묨 - ப ? ⠭ size 1 ࠧ, 100. - -### Bank-prefix modes (port 0xE2 bits) - -㬥 뢠 ਠ ࠭ ⠭⭮ 0x50: - -| Bank byte | Effect | -|---|---| -| 0x50 | Normal write ? shadow + | -| 0x54 | "no copy in main shadow RAM" | -| 0x58 | **"FF is transparent"** ? 0xFF write ⠢ background | -| 0x5C | both | - -Bank 0x58 祬 mouse cursor 0xFF-஧筮. - **sprite-blending १ accel block copy**: - -- [ ] **`gfx_set_bank_transparent(on)`** 䫠 `gfx_set_bank` - 롮 0x50/0x58 ᮢ sprite' -- [ ] ᯮ짮 `gfx_blit()` ⮡ 䠪 - transparent sprites १ accel- - -### Block copy mode (sprite blit') - -`LD L,L` (horizontal) `LD A,A` (vertical) ? ० ஢ १ -256- accel memory. blit'. - -- [ ] **`gfx_blit(src_data, x, y, w, h)`** ? ஢ sprite' - (ந ࠧ, १ accel) -- [ ] **`gfx_blit_transparent(src, x, y, w, h)`** ? ᯮ짮 bank 0x58 - -. `Draw_Restangle_Data` accelerator_doc.txt ७. - -### AND / OR / XOR operations १ accel - -㬥 뢠 accel ন ᪨ 樨 -. ਬ: -- XOR ? (뤥 selection UI) -- OR / AND ? masking, alpha-style blending -- . ਬ accelerator_doc.txt: "256 bytes block coding via XOR" - -- [ ] **`gfx_xor_rect`** / **`gfx_or_rect`** / **`gfx_and_rect`** ? - ਬ⨢ ᪨ 権 אַ㣮쭨 -- [ ] **`gfx_invert_rect(x, y, w, h)`** ? alias xor 0xFF - -### Bitmap fonts ࠧ ࠧ஢ - - `gfx_text` / `gfx_putchar` प ࠡ 8?8 ⮬ -(BIOS WIN_GET_ZG 頥 256?8 ). UI / 쭨 -㦭: -- [ ] **`gfx_set_font_size(w, h)`** ? ४ ਭ/ glyph' -- [ ] **`gfx_set_font_data(ptr, w, h, advance)`** ? 㪠⥫ - 짮⥫᪨ + ࠧ -- [ ] প **proportional** (advance != w) ⮢ ? - array advance[256] ਭ glyph' -- [ ] **Big-font ०**: 8?16, 16?16, 16?8 ( 쭨) -- [ ] ⤥ API `gfx_text_ex(x, y, str, font_id)` - font_id 롨ࠥ 㦥 ⮢ -- [ ] **Anti-alias 2-bit ** ( 䮭 / ࠭ / 2- alpha?) - ? far future, smooth UI - -## ⠯ ⨬権 ( ᥩ) - -- **`gfx_line` १ accel ** ? Bresenham |dy| << |dx| ( ) 뤠 runs Y ( X): ᥫ, ᥫ, ᥫ, 蠣 Y, ᥫ, ᥫ... ⠪ run ? ⮢ 㬥 `gfx_hline` ( `vline`). - ᫥: runs 㭪 ; run length, ஬ 룮 accel hline 祬 N?putpixel (overhead accel ~20?s, putpixel ~5?s ? accel 룮 run ? 4-5 px); (dx ? dy) ⠢ Bresenham, ? run-length-based fill. - `gfx_line` orthogonal cases 㦥 १ accel ? ⨬஢ ⮫쪮 . - -- **`gfx_fill_rect` W3-swap ** ? ᥩ ७ `gfx_hline`/`gfx_vline` ᢮ DI/save-W3/restore-W3/EI. ᤥ internal `_fill_rect_inner` ন W3 DI 横; ~20?s ? ⢮ ப/⮫殢 . ਬ ᥬ ਬ⨢. -# MDView ? ᬮ騪 Markdown Sprinter - -**MDView** ? ணࠬ ᬮ 㬥⮢ ଠ *Markdown* -Sprinter ( Z80). 㬥 ࠭ ⤥쭮 W3 -᭮ RAM ணࠬ. - -## - -- 㬥 **128 ** (8 ࠭ EMM 16 ) -- **16 384** ࠭ ப -- ⮬᪨ ७ ᫮ ਭ ࠭ (80 ⮫殢) -- ਧ⠫ ᤢ ப ப ( , ⠡) -- -: 䠩, ப, 業 ப⪨ -- ப ﭨ ६ 㧪 樨 -- প ?? ᪫ ப - -## - -``` -mdview [_䠩.md] -``` - -᫨ 䠩 , 㦠 `README.MD`. - -## ࠢ - -``` - ⢨ - -Up Down ப⪠ ப / -PgUp PgDn ப⪠ ࠭ (30 ப) -Home 砫 㬥 -End 㬥 -Left Right ਧ⠫ ᤢ (⮫쪮 nowrap-ப) -F1 ࠢ -F8 ஢: CP866 ? CP1251 ? KOI8-R ? UTF-8 ? ... -F10 / Esc 室 ணࠬ -``` - -஢ । ⮬᪨ ⨨ (BOM + ⨪ - 4 ); `F8` ४砥 , ᫨ ⥪ 訡. -8- ஢ (CP866/CP1251/KOI8-R) ४ (६ -ᮢ). ன / (ਬ UTF-8) ந ? -ࢮ ४祭 (⪠ 㧠 ᯨ஬), . - -## ⠪ Markdown - -### - -ন ஢ H1?H4. ஢ H5 H6 ⮡ࠦ H4. - - # ࢮ ஢ - ## ண ஢ - ### 쥣 ஢ - #### ⮣ ஢ - -### ⮢ ଠ஢ - -** ⥪** 뤥 묨 񧤮窠: `**⥪**` - -*ᨢ* 뤥 묨 񧤮窠 `*⥪*` ન `_⥪_` - -`஥ ` 砥 묨 窠 - -~~ભ ⥪~~ ? ⨫: `~~⥪~~` - -### 㬥஢ ᯨ᮪ - -થ `-`, `*` `+`: - -- 㭪 ᯨ᪠ -- ன 㭪 ᯨ᪠ -- ⨩ 㭪 筮 ⥪⮬, 室 - 㤥 ७ ᫥ ப ࠭ 㯠 - -### 㬥஢ ᯨ᮪ - -1. -2. ன -3. ⨩ - -### - -> 稭 ᨬ `>`. ᪮쪮 ᫥⥫ -> ப ᪫ ⮬᪨ -> ७ᮬ ᫮. - -### (verbatim) - - 砥 ன 窨. -⥪ ⮡ࠦ ? ? ࠧ Markdown: - -``` -#include -#include - -int main(void) { - puts("Hello, Sprinter!"); - return 0; -} -``` - -### ਧ⠫쭠 - - ᨬ `---`, `***` `___` ⤥쭮 ப: - ---- - -## 孨᪨ ࠪ⨪ - -- **ଠ:** Sprinter, Z80 @ 21 -- **஢:** CP866 / CP1251 / KOI8-R / UTF-8 (⮮।, `F8`) -- **ᨬ ࠧ 䠩:** 128 -- **ᨬ쭮 ᫮ ப :** 16 384 -- ** :** small - - ணࠬ, c⥪, , ? a W1-W2 (32 , 0x4000?0xBFFF). - - 䠩 ? ࠭ EMM, ⮡ࠦ W3 (0xC000?0xFFFF) - -## TODO - -1. **祭 ࠧ 㬥⮢.** 128 : 筮 - ࠧ ࠡ 訬 - ࠭ , ஢ - ࠡ 8- ࠭栬 16. - -: ଠ஢ ⠡ ࠬ; প ஢ -CP866 / CP1251 / KOI8-R / UTF-8 ⮮। ४祭 `F8` -(ன ந , ࢮ ४祭). - -4. **᪮७ ७ਭ.** ப ࠭. ⨬ 横 뢮 - ᨬ १ BIOS WRCHAR ( 뢮, DMA). - ---- - -*MDView v0.2 (c) 2026 ஢ ..* - - ---- - -# Sprinter C Compiler ? v1.0 - -C toolchain for **Sprinter** ? the Z80-based home computer by Peters Plus, running -ESTEX DSS. Host: macOS / Linux. Target: `.EXE` files in SprintEXE format. - -Built on top of **SDCC 4.5** (vendored in `third_party/sdcc/`). This repository adds -everything Sprinter-specific: crt0, linker integration, libc wrappers over ESTEX, -banked-call trampolines, graphics & accelerator API, mouse driver wrappers, and the -`mkexe` utility for producing SprintEXE images. - -## What you get - -* **`bin/sprinter-cc`** ? one-line driver: `sprinter-cc -o foo.exe foo.c` -* **Memory modes**: `tiny`, `small`, `big`, `huge`, `manual` ? see below. -* **stdio + conio**: printf, puts, putchar, getchar, fopen/fread/..., cprintf, cputs, putch, textcolor/textbackground/textattr, gotoxy, kbhit/getch. -* **Graphics**: 320?256?256 and 640?256?16 modes, accelerator-backed primitives (hline / vline / rect / fill_rect / line via Bresenham, plus clear), bitmap-font text in both modes via BIOS character generator. -* **File I/O**: POSIX (`open`/`read`/`write`/`close`/`lseek`/`unlink`/`creat`), FILE\* streams (`fopen`/`fgets`/`fwrite`/...), directory listing (`ffirst`/`fnext`), `chdir`/`getcwd`/`mkdir`/`rmdir`, `stat`/`fstat`. -* **Memory**: 32 KB heap (W2-resident), banking-aware page allocator (`mem_alloc_pages`/`bank_read`/`bank_write`), explicit memory modes for sub-16 KB programs. -* **Mouse**: full Sprinter driver wrapper (14 functions including custom cursor bitmaps). -* **Environment**: `getenv`/`putenv`/`sysenv` over ESTEX `$46`. -* **Time**: `getdatetime`/`setdatetime` + POSIX `time`/`localtime`/`mktime`/`asctime`/`ctime`. -* **Misc**: `errno`/`strerror`/`perror`, `atexit`, `setjmp`/`longjmp`, `sleep`, full argv parsing in crt0. - -## Quick start - -```sh -git clone sprinter-c -cd sprinter-c -make sdcc # one-time: fetch SDCC 4.5 binary (~25 MB) -make all # build mkexe + libsprinter.lib + 27 examples -make floppy # pack everything into mame/v306/IMG/mc.img -cd mame/v306 && ./run_mame.sh # boot Sprinter in MAME -``` - -Compile a single program: - -```sh -cat > hello.c < -int main(void) { puts("Hello, Sprinter!"); return 0; } -EOF - -bin/sprinter-cc -o hello.exe hello.c -``` - -That's it ? `hello.exe` is now a valid SprintEXE you can `RUN HELLO` from the ESTEX shell. - -## Memory modes - -Sprinter's address space is four 16 KB windows (W0 / W1 / W2 / W3). DSS allocates -pages by program size ? small programs get only one page. Pick a memory mode based -on what your program needs: - -| Mode | Code lives in | Banking | Use when | Note | -|---|---|---|---|---| -| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | | -| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | | -| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | | -| `huge` | W1-W2 + W3 banking | yes (W3) | small + extra code modules | | -| `manual` | user-specified | optional | special layouts | Not implemented | - -```sh -sprinter-cc --memory small -o big.exe bigprog.c -sprinter-cc --memory huge -o app.exe main.c --bank 1=engine.c --bank 2=ai.c -``` - -Banked functions are declared with `__banked`: - -```c -void engine_tick(int dt) __banked; // lives in BANK1, automatically swapped -``` - -## Examples (27 total) - -| Example | What it demonstrates | -|---|---| -| `hello` | Hello world with stdio + conio Turbo-C-style colors | -| `argv` | argv parsing in crt0 | -| `cat` | File I/O ? read & print TEST.TXT | -| `seek` | 32-bit lseek over a 100 KB file | -| `ls` | Directory listing via ffirst/fnext | -| `filetest` | FILE\* streams (fopen/fread/...) | -| `stattest` | `stat`/`fstat` on files and directories | -| `errno` | errno / strerror / perror | -| `mem_test` | Page allocator + bank\_read/bank\_write | -| `malloc` | Heap stress test (200+ allocations) | -| `banked` | Banked code in W3 (huge mode) | -| `bankedbg` | Banked code in W1 (big mode) | -| `banklocl` | Bank-local static data and BSS | -| `mouse` | Mouse driver in text mode | -| `gfx_mous` | Mouse with custom bitmap cursor in graphics mode | -| `gfx_demo` | 320?256?256 lines / rects / accelerator | -| `gfx_d16` | 640?256?16 same primitives | -| `gfx_text` | Bitmap-font text on graphics screen | -| `timedir` | Date/time + directory listing | -| `ptime` | POSIX time API | -| `openenv` | open() flags + environment vars | -| `conio` | conio API smoke test | -| `attrprob` | Probe Sprinter text attribute byte layout | -| `strtest` | string.h test (from SDCC's z80.lib) | -| `stdlib` | stdlib.h test (qsort / rand / strtol / etc.) | -| `assrtest` | assert() | -| `rt_test` | Runtime helpers (sleep, setjmp, atexit) | - -## Headers - -Standard: -* `` ? puts / printf / FILE\* + Sprinter-specific dec/hex helpers -* `` ? atoi / atof / malloc / qsort / ... (from SDCC z80.lib) -* `` / `` / `` ? from SDCC z80.lib -* `` ? read / write / close / lseek / unlink -* `` ? open / creat + O\_RDONLY / O\_CREAT / ... -* `` ? errno + error names + strerror -* `` ? stat / fstat -* `` / `` ? from SDCC - -Sprinter-specific: -* `` ? putch / cputs / cprintf, textcolor / textbackground / textattr, kbhit / getch, clrscr, gotoxy, wherex/y -* `` ? gfx_init/done, palette, putpixel, hline/vline/rect/fill_rect/line, text ? for both 320 and 640 modes (gfx_\*16 variants) -* `` ? full 14-function driver wrapper + mouse_cursor_t with bitmap support -* `` ? chdir / getcwd / mkdir / rmdir / ffirst / fnext / ffblk -* `` ? getdatetime / setdatetime + POSIX time / localtime / etc. -* `` ? raw ports, ESTEX/BIOS function numbers, env API -* `` ? exit / \_exit / atexit -* `` ? mem\_alloc\_pages / mem\_free\_block / bank\_read / bank\_write -* `` ? Solid-C compatibility layer (aliases + BOOL/WORD/uint types) - -## Toolchain commands - -```sh -make all # build mkexe + lib + every example -make floppy # repack mame/v306/IMG/mc.img with all .exe files -make check # 17 mkexe unit-tests -make clean # remove all build artefacts -make sdcc # one-time: fetch SDCC 4.5 binary -``` - -### sprinter-cc options - -``` -sprinter-cc -o foo.exe foo.c [more.c ...] [options] - - --memory MODE tiny | small | big | huge | manual (default: tiny) - --memory-manual SPEC explicit placement (CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3) - --stack-size N bytes reserved for the stack (default ~1278) - --crt0=TYPE default | minimal | banked | small - --bank N=FILE.c compile FILE.c into bank N (repeatable, max 15) - --debug enable runtime diagnostics (defines DEBUG_RT) - -I PATH extra include path - -L 0xADDR / -E / -S override load / entry / stack addresses - -Wl FLAG pass FLAG to sdldz80 - --mkexe FLAG pass FLAG to mkexe (e.g. --mkexe -p --mkexe 0 for bank padding) - -v verbose -``` - -## Status - -What works in v1.0: -* Compile / link / pack to SprintEXE ? verified on all 27 examples -* Four memory modes (tiny / small / big / huge) -* Graphics (both modes) with accelerator -* Mouse (text + graphics cursor) -* File I/O, directories, environment, time -* All headers listed above - -Deferred to v2.0 (see `docs/TODO.md`): -* **Turbo-C-style BGI graphics API** ? `initgraph` / `setcolor` / `circle` / - `getimage` / `putimage` / etc. on top of our `gfx_*` primitives -* Remaining Solid-C compatibility gaps (Phase 2/3) ? see `docs/solid_c_compatibility.md` -* Manual memory mode -* Rewrite FILE\* stream API (current implementation is very primitive and doesn't use buffers) - -Deferred to v3.0: -* **IM2 interrupt handlers** ? research complete (`docs/im2_isr_design.md`), - implementation scheduled for v3 -* **Audio API** (AY-3-8910 + COVOX) ? requires IM2 -* **ISA-8 slot drivers** ? requires IM2 (???) - -## Documentation - -* `docs/TODO.md` ? roadmap and open work items -* `docs/solid_c_compatibility.md` ? gap analysis vs Solid-C 2004 -* `docs/im2_isr_design.md` ? interrupt handler design (v2) -* `docs/converted/` ? source documentation (ESTEX, BIOS, architecture) - converted to plain text for `grep` -* `docs/reference/`, `docs/samples/`, `docs/memory management/` ? original - Russian docs and code samples from Peters Plus - -## Repository layout - -``` -bin/sprinter-cc one-line compiler driver (bash) -toolchain/mkexe/ host-side tool: .ihx -> .exe SprintEXE -toolchain/check_banks.py post-link bank size enforcer -runtime/ crt0 variants (default, minimal, small, banked) - bank trampolines, heap, heap_top -libc/include/ headers -libc/io|stdio|mem|gfx/ C and asm sources for libsprinter.lib -lib/ Makefile that archives libsprinter.lib via sdar -examples/ 27 example programs -mame/v306/ MAME binary + Sprinter ROM/HDD images + floppy script -third_party/sdcc/ vendored SDCC 4.5 (fetched via `make sdcc`) -third_party/solid-c/ reference: original Sprinter native C (for compat target) -docs/ documentation -``` - -## License - -This repository contains: -* Original code in `bin/`, `toolchain/`, `runtime/`, `libc/`, `lib/`, `examples/` ? - MIT-licensed. -* `third_party/sdcc/` ? SDCC 4.5 under GPLv2 with linking exception - (see `third_party/sdcc/COPYING.txt`) -* `third_party/solid-c/` ? original Sprinter Solid C, used only as a reference - -## Credits - -* **Sprinter / Peters Plus** ? , ਩ ਭ and the original team -* **SDCC** ? for the underlying Z80 compiler -* **MAME** ? for the Sprinter Sp2000 emulation - ---- - -For questions / patches: see CONTRIBUTING.md (TBD) or open an issue. - ---- - -# TODO / Roadmap - - 浪 뢠 ਮ.  ॠ ணࠬ ? ਮ ᬥ. - -## ⠯ 5 ? malloc / free + banking-aware page allocator ? - -- [x] SDCC's `malloc`/`free` + `runtime/heap.s` ( library heap.rel, 14000- heap 2) -- [x] `libc/mem/mem_alloc.c` ? page allocator: `mem_alloc_pages`/`mem_free_block`/`mem_get_page`/`mem_info` १ ESTEX `$3C/$3D/$3E` + BIOS `$C4` -- [x] `libc/mem/bank_io.c` ? HOME-१ `bank_read`/`bank_write`/`bank_load_byte`/`bank_store_byte` ᢮ W3 -- [x] `examples/malloc_test/` ? ஢ઠ SDCC's malloc (~210 64- allocations १ heap) -- [x] `examples/mem_test/` ? ஢ઠ page allocator: 3 ࠭, ࠧ १ bank_write, 䨪 १ bank_read - -## ⠯ 6 ? argv parsing + sprinter-cc wrapper ? - -- [x] crt0 ESTEX command-line IX-prefix (inline asm `runtime/crt0.s`) -- [x] Strip leading CP/M-style space (DSS quirk) -- [x] । `argc`/`argv` main() १ HL/DE (SDCC __sdcccall(1) ABI) -- [x] argv[0] = basename .EXE १ ESTEX APPINFO ($47 subfn 2) -- [x] `runtime/crt0_minimal.s` ? opt-out 祭 쪨 ணࠬ -- [x] `runtime/crt0_banked.s` ? ⥯ ⮦ argv (parse_argv + get_progname ᪮஢ crt0.s; 㤥 factored argv.s libsprinter.lib) -- [x] Bash-⪠ `bin/sprinter-cc`: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権: `--memory`, `--memory-manual`, `--stack-size`, `--crt0=`, `--bank N=FILE.c`, `--debug`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` - -## ⠯ 8 ? 䨪 (320?256?256 + 640?256?16 + accel + bitmap font) ? - -- [x] **8a** Graphics core: `gfx_init`/`gfx_done`/`gfx_clear`/`gfx_putpixel`/`gfx_pal_load`/`gfx_pal_set` (libc/gfx/gfx_core.c). १ BIOS PIC_SET_PAL ($A4). Verified 320?256?256. -- [x] **8b** /אַ㣮쭨/fill १ accelerator (libc/gfx/gfx_lines.c): `gfx_hline`/`gfx_vline` १ accel Fill (LD C,C / LD E,E + SMC block-size), `gfx_rect`/`gfx_fill_rect` heuristic 롮஬ ਥ樨 (h/v bursts count), `gfx_line` Bresenham . `gfx_clear` ⮦ ९ᠭ column-major accel (~4? ॥). -- [x] **8c** 640?256?16 mode (libc/gfx/gfx_16.c): `gfx_*16` API, HIGH nibble = LEFT pixel (㬥 misleading), per-row RMW vline ( = 2 ਧ⠫ ᥫ). -- [x] **8d** Bitmap font + gfx_text (libc/gfx/gfx_text.c): १ BIOS WIN_GET_ZG ($B8), interleaved layout `font[row*256+char]`, `gfx_text`/`gfx_putchar` 320 mode, `gfx_text16`/`gfx_putchar16` 640 mode pair-table lookup. - -. memory/sprinter_graphics.md, sprinter_accelerator.md, sprinter_graphics_16.md, sprinter_font_format.md. - - ( ): -- [ ] -quad 640: per-cell (mode 0x82 ࠧ蠥 1 4 per 16?8 cell) ? १ אַ area-ᠭ ࠭ 0x0300..0x039F - -## Auto-banking (. `memory/banking_roadmap.md` ⠫) - -Phase 1 ? file-level bin-packing ? ॠ뢠 ஥ ~30 KB . - -- [ ] `toolchain/auto_bank.py`: - - ࠧ `.rel`-䠩 ( .map ᫥ dry-run link') - - First-fit-decreasing bin-packing - - `#pragma codeseg BANKn` manual override - - ५뢠 묨 `-Wl-b_BANKn=...` ࠬࠬ - - ⠥ । - -Phase 2-5: incremental rebalance, declarative `banks.toml`, function-level, call-graph-aware. 쪮 ᫨/ . - -## Bank-local static data (mutable data ⮬ ) ? ? - -- [x] ਬ `examples/bank_local_data/` ? 㭪 BANK1 ᢮ writable BSS array + const table + malloc- -- [x] `mkexe -p 0` 㫥 padding (BSS-storage 㧪) -- [x] ᪨ 楯: `--codeseg BANK1 --constseg BANK1 --dataseg BANK1` bank1.c + `-Wl-b_BANK1=0x1C000` . **`--dataseg BANK1` ** ? ࠭ ⭮ - trampoline bug ᪨஢ १. -- [x] ** 䨪 trampoline'a runtime/bank.s** ? `pop af; out (n), a` ਫ A ? banked-㭪樨 騥 uint8_t 頫 . `pop bc; out (c), b` ࠭ A. -- [x] **malloc banked-㭪樨 ࠡ⠥ ஧筮** ? heap W2 (HOME), W2 ᢠ trampoline', pointer  ⥪. . memory/bank_local_data_pattern.md. -- [x] 㬥 memory: `memory/bank_local_data_pattern.md` ( 楯 + malloc + nuances), `memory/sdcc_banking.md` (trampoline fix) -- [ ] 樮쭮 ? `check_banks.py` ⮡ 뢠 ࠧ size = code + const + bss per bank (cosmetic) - -祬: 㫥 訬 private state (level loader, audio engine, scene data). W2 heap , ⨪ . - -## ᪠ solid-c (⨢ Sprinter C ? `third_party/solid-c/`) - -᫥ solid-c'᪮ libc (. `memory/solid_c_findings.md`)  ⮢ ᫥ 㭪権. ਮ **᮪** : - -### High-priority gaps ( ஢, 짠) -- [x] **`errno` + `strerror`/`perror`** ? ⠡窠 32 訡 (libc/io/errno.c) -- [x] **७ `open()`** O_CREAT/O_TRUNC/O_APPEND/O_EXCL state machine -- [x] **`atexit`** ? 8-callback LIFO + `exit()` + `_exit()` (libc/io/atexit.c) -- [x] **`setjmp`/`longjmp`** ? 6- jmp_buf={sp,ix,pc} (libc/io/setjmp.c) -- [x] **`sleep(seconds)`** ? 50Hz halt-loop (libc/io/sleep.c) -- [x] **ESTEX ENV API** ($46, getenv/putenv) ? libc/io/env.c. 竨 doc-bug: ॠ쭮 A=0 NOT FOUND - -### Medium-priority (㦭 shell-like ⨫) -- [ ] **Mouse driver** ? `rst $30h`, 17 㭪権. **砫 ࠡ⠥ MAME**. -- [x] **`ffirst`/`fnext` + ffblk_t struct** directory listing ? ॠ, demo: ls.exe -- [x] **`getdatetime`/`setdatetime`** १ ESTEX $21/$22 ? libc/io/time.c, demo: time_dir_test -- [x] **`chdir`/`getcwd`/`mkdir`/`rmdir`** ? wrappers ESTEX $1B-$1E ? libc/io/fsdir.c -- [x] **conio: `kbhit`/`getch`/`getche`/`cputs`/`clrscr`/`gotoxy`** ? ॠ -- [x] **conio extras**: `wherex`/`wherey` ($53), `wrchar`/`rdchar` ($58/$57), `textmode_get/set` ($50/$51), `clrscr_attr` ($56) + COLOR macros - -### Low-priority ? ? FILE* stack - -- [x] ** unbuffered FILE\*** ? `libc/stdio/file.c` + `libc/include/stdio.h`. fopen/fclose/fputs/fgets/fread/fwrite/fseek/ftell/rewind/feof/ferror/clearerr/fflush + stdin/stdout/stderr pseudo-streams. . `memory/file_star_design.md` `examples/filetest`. -- [ ] fprintf / fscanf ? 㦭 printf-१-callback machinery. 짮⥫ `sprintf(buf, ...) + fputs(buf, fp)`. -- [ ] 樮 buffered mode (setvbuf, line/block buffering) ? ᫨ - . - -### POSIX time API ? ? -- [x] `libc/io/posix_time.c` ? time/localtime/gmtime/mktime/asctime/ctime getdatetime. SDCC's time.rel ( override _RtcRead). . `examples/ptime`. - -### sys/stat ? ? -- [x] `libc/io/stat.c` ? POSIX stat/fstat. ਤ open+fstat 䠩, ffirst+iter ( "."/".."). . `examples/stattest` `memory/estex_ffirst_dotdot.md`. - -### assert ? ? (ᯮ㥬 SDCC's __assert १ fallback include path) - -## libc/stdlib ? ? 㦭 (. memory/sdcc_stdlib_works.md) - -஢७ १ `examples/stdlib_test/`: SDCC z80.lib ᮤন ࠡ騥 ॠ樨: -- `atoi/atol/atof, strtol/strtoul, rand/srand, qsort/bsearch, abs/labs, div/ldiv` -- `` (memchr/cmp/set/cpy, strcat/cmp/cpy/len/chr/spn/etc.) -- `` (toupper/tolower) -- `` (sinf/cosf/sqrtf/etc.) - - ⮬᪨ ﭥ z80.lib 㦭. ** ९뢠**. - - Sprinter-specific 易⥫ 㫨 : atexit, env, errno, setjmp, putchar/puts/getchar, conio, fsdir, time, mouse, open/read/lseek/close. - -## Build-system: libsprinter.lib + sprinter-cc ? ? - -- [x] `lib/Makefile` ? ᮡࠥ libc/*.c `.rel`, 娢 १ sdar `lib/sprinter.lib` -- [x] 砥 runtime/bank.s runtime/heap.s (auto-pulled __banked/malloc) -- [x] `bin/sprinter-cc` ? bash-wrapper: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権 `--crt0=default|minimal|banked`, `--bank N=FILE.c`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` -- [x] `examples/hello_sccc/` ? : `hello.c` ᮡࠥ shell-맮, ࠧ ᮢ Makefile (925 ) -- [x] Split `putchar.c` ? `putchar.c` + `puts.c` per-function granularity (puts override SDCC's z80.lib version) -- [x] `make all` (ᨬ `lib` । `examples`) - - 襭 (樮쭮): -- [ ] ஢ ⠫ examples sprinter-cc Makefile (ᬥ⨪) -- [ ] 쭥 libc/*.c per-function ( ⥪ granularity 㦥 㦭 ࠧ ? .rel 楫, 設⢠ 䠩 㭪) - -## ⠯ 9 ? memory modes sprinter-cc - -DSS 뤥 ࠭ ࠧ ਫ: < 16 KB ? ࠭, ⠫ 砥 ?࠭ #FF? (read=0xFF, write ). - ⮣ CODE--W1 + DATA--W2 쪮 ணࠬ . . [memory/sprinter_memory_modes.md](../../.claude/projects/-Volumes-SAM8-Projects-DIY-Z80-Sprinter-C-Compiler/memory/sprinter_memory_modes.md). - -- [x] **`tiny`**: (CODE+DATA+⥪) W2. Default. Verified hello/argv/conio/malloc/file/etc. -- [x] **`--memory MODE` 䫠 sprinter-cc**: parser + per-mode 䮫 CODE_LOC/DATA_LOC, override १  `--code-loc`/`--data-loc`. tiny ࠡ⠥; small/big/huge warning' (runtime ⮢). 2026-05-30. -- [x] **`--memory-manual SPEC`**: `CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3`. 2026-05-30. -- [x] **`small` runtime**: `runtime/crt0_small.s` ᯮ ESTEX `$3D GETMEM` + `$3A SETWIN2` ⮡ 뤥 W2-࠭ gsinit. **** BIOS `$C4` ? ⥪ ⮬ ⠯ W1 (boot_stack HOME), BIOS ॡ ⥪ W2. ᫥ SP ४砥 0xBFFE, ⠭ flow. 2026-05-30, verified hello.exe. -- [x] **`small` auto-detect >16 KB ணࠬ**: `crt0_small.s` ⠥ `0xC2` (⥪ ࠭ W2 ? `0xA2`! W1). ᫨ 0xFF ? 뤥 page; DSS 㦥 ᤥ (ணࠬ ᠬ 뫥 W2). crt0 뢠 0..30 KB. mkexe ⠪ ࠧ蠥 HOME span W1+W2 (0x4000..0xBFFF). Verified hello: small (5 KB 䠩, SETWIN2 path) + 32 KB 䠩 (auto-skip). 2026-05-30. -- [x] **`big` runtime** (tiny + banked code W1): ࠬਧ `crt0_banked.s` + `bank.s` १ `.ifdef BANK_W1` ? 㣮 banking port (0xA2 vs 0xE2), 㣮 load-addr (0x4000 vs 0xC000). sprinter-cc prepend' `BANK_W1 = 1` `--memory big`, । `mkexe -B 0x4000`. ਬ `examples/banked_big/`. 2026-05-30. -- [x] **`huge` runtime** (small + banked code W3): merge W2-detect `crt0_small.s` `crt0_banked.s`. 騩 ਬ `examples/banked/` ⥯ ᯮ MEMORY=huge. 2026-05-30. -- [x] **`--debug` 䫠**: prepend `DEBUG_RT = 1` crt0 + `-DDEBUG_RT` sdcc. 뢠 symbol `_w2_self_allocated` (uint8_t) ? runtime diagnostic ஢ W2. 2026-05-30. - -### -襭 libc crt0 - -** `sprinter.lib`** ࠡ⠥ memory mode ? `.rel`-童 relocatable, SDLD dead-code elimination per-member ( 䨪  `gfx_core.rel` ..). Verified hello vs malloc_test १ map-䠩. - -**`gfx.lib` ⤥쭮 ? 㦥**: dead-code elimination 㦥 ࠡ⠥. - -**`libc_banked` (libc bank HOME)** ? ⮬, HOME (16 KB) user- + libc `huge` mode. १ `--codeseg BANK0` 樨 libc; trade-off: trampoline ~30 横 libc-맮. ਣ: ॠ쭠 ணࠬ HOME budget. - -**HW-ᨬ 㫨 ? `sprinter_home.lib` ⤥쭮.** libc 䨧᪨ W3, ⮬ W3: -- `gfx_*` ? `0xC000+` ᫥ swap W3 video page -- `bank_io` (mem_alloc_pages/bank_read/bank_write) ? swap' W3 १ `OUT (0xE2)` -- 騥 ISR ? 뢠 ਩ W3 㣮 - - huge mode 㫨 HOME (W1). 㤥 `libc_banked`, **६** 뤥塞 `sprinter_home.lib` (HOME-only) `sprinter.lib` (bankable). 쭠 奬: -``` -sprinter_home.lib HOME-only: gfx, bank_io, ISR shims -sprinter.lib bankable: printf, malloc, string, conio, stdio, env, ... -sprinter_banked.lib sprinter.lib --codeseg BANK0 ( huge) -``` -ਣ: ॠ `--memory huge` runtime. - -**crt0 ? mode:** -- `crt0.s` ? ⥪騩, **tiny/big**: SP=0xBFFE, argv (W2- 㦥 뤥 DSS). -- `crt0_minimal.s` ? ⥪騩, tiny argv. -- `crt0_small.s` ? **, step 3**: **small/huge**, W2 १ `mem_alloc_pages` gsinit, `0xA2`, ⮬ ⠭ flow. -- `crt0_banked.s` ? ⥪騩, **big**: trampoline-⠡ W3 , CODE W2. -- `crt0_banked_small.s` ? ****: huge = small (W2-alloc) + banked (W3 trampolines). - -sprinter-cc ࠥ crt0 `--memory` mode (ᥩ `--crt0=` override). -- [x] **ࠨ ࠧ ⥪**: 䫠 `sprinter-cc --stack-size BYTES`. Wrapper `heap_top.s` `___sdcc_heap_end = stack_top + 1 - stack_size`, ⤥ .rel per-program. Default ?1278 (heap_top=0xBB00) `runtime/heap_top.s`. 2026-05-30. - -䥩: `sprinter-cc --memory [tiny|small|big|huge|manual] [--memory-manual SPEC] [--stack-size N] foo.c`. `--memory-manual` ⮫쪮 `--memory manual`. - -## Known issues / quirks - -- **ESTEX $46 ENV API**: ? ࠡ⠥. - `DiskSyscalls.txt v1.6` 訡筮 뢠 return-status ? A=0 NOT FOUND, FOUND. 䨪஢ `memory/sprinter_platform.md`. - -## V2 (᫥ ५ v1) - -### Turbo-C-style graphics API (BGI-like) ? **MUST v2** - - `gfx_*` API ஢ **Turbo-C ``** (BGI) MS-DOS. -ணࠬ ਢ訥 Turbo-C ७ ᪨ 1--1. - -** ** ( ᭮ Borland BGI): - -Setup/teardown: -- `initgraph()` / `closegraph()` ? ᥩ `gfx_init`/`gfx_done`, alias -- `getmaxx()` / `getmaxy()` ? GFX_WIDTH-1 / GFX_HEIGHT-1 -- `cleardevice()` ? alias to gfx_clear -- `getgraphmode()` / `setgraphmode()` ? get_videomode/set_videomode - -Color/palette: -- `setcolor(c)`, `getcolor()` ? current draw color -- `setbkcolor(c)`, `getbkcolor()` ? background color -- `setpalette(idx, c)` ? entry -- `getpalette(&info)` ? read all palette - -Primitives ( 㦥 ? BGI- aliases): -- `putpixel(x, y, c)` ? gfx_putpixel -- `getpixel(x, y)` ? 㦭 ॠ (RMW ⭮ ? IN) -- `moveto(x, y)`, `lineto(x, y)`, `linerel(dx, dy)` ? current point + line drawing -- `line(x1, y1, x2, y2)` ? gfx_line -- `rectangle(x1, y1, x2, y2)` ? gfx_rect ( 㣮 API: x1,y1,x2,y2 vs x,y,w,h!) -- `bar(x1, y1, x2, y2)` ? gfx_fill_rect -- `bar3d(x1, y1, x2, y2, depth, topflag)` ? : rect + 3d edges -- `circle(x, y, r)`, `arc(...)`, `ellipse(...)`, `pieslice(...)` ? primitives -- `fillpoly()`, `drawpoly()` ? -- `floodfill(x, y, border_color)` ? - -Text on graphics screen: -- `outtext(s)` / `outtextxy(x, y, s)` ? gfx_text (alias) -- `settextstyle(font, dir, size)` ? multiple bitmap fonts -- `gettextsettings(&info)` -- `textwidth(s)` / `textheight(s)` ? measure - -Image manipulation: -- `imagesize(x1, y1, x2, y2)` ? bytes needed for getimage -- `getimage(x1, y1, x2, y2, buf)` ? save rect to buffer -- `putimage(x, y, buf, op)` ? paste back with COPY_PUT/XOR_PUT/AND_PUT/OR_PUT/NOT_PUT - -Clipping/viewport: -- `setviewport(x1, y1, x2, y2, clip)` ? drawing clip rect -- `getviewsettings(&info)` -- `clearviewport()` -- `setactivepage(p)` / `setvisualpage(p)` ? ਧ (Sprinter 2 screen) - -Line style: -- `setlinestyle(style, pattern, thickness)` ? SOLID_LINE / DOTTED_LINE / etc. -- `getlinesettings(&info)` - -**Acceptance:** ७ ⨯筮 Turbo-C BGI ணࠬ (饩 ᯮ짮 -moveto/lineto/circle/bar/setcolor) ࠡ ⢥ ࠢ. - -**Notes:** -- BGI fonts (TRIPLEX/SANS_SERIF/GOTHIC) ? BIOS font, ⠫ 㦭 - ( bitmap data lib) -- imagesize/getimage/putimage ? ᠬ ॡ game/animation -- Active/visual page ( ਧ) ? Sprinter ন 2 graphics pages, - 㦥 API switching - -. ⠪ `examples/` Turbo C 2.x BGIDEMO reference 㦭. - -### IM2 Interrupt Handlers ? **MUST v2** - -User- ISR १ Z80 IM 2 mode. 㦭 : -- Timer ticks (50 Hz frame counter, ) -- Music playback (AY, COVOX) -- Real-time games (input + game logic + render interrupt-driven) -- Async keyboard / mouse handling - -**Status:** v2. research + design `docs/im2_isr_design.md`. - -**襭 ⥪:** ॠ ⤥ memory mode `--memory im2` -( ⮣ ⮡ 騥 crt0). Detail' design-doc. - -** research'** ( ⥪ `docs/im2_isr_design.md`): -- Vector 0xFF ? frame + keyboard + CBL. Disambiguation ⠬ 0x19 / 0xFE -- Mouse hardware-IRQ 室 ( ⥪饩 ) -- Vector table / ISR / stack W2 (0x8000..0xBFFF) -- DSS ᢮ IM 2 handler ? 㦭 chain' ( / SYSTIME ) - -### 稥 㯭 㭪 v2 - -- [ ] **FILE API rewrite ? buffered streams** ? ⥪ ॠ - `libc/stdio/file.c` provisional unbuffered shim ( fputc/fgetc - = read/write syscall). 㦭 業 buffered ᥬ⨪ - Solid-C: - - ```c - typedef struct { - uint flags; // +0..1 file status flags - int level; // +2..3 empty/fill level of buffer - char *curp; // +4..5 current active pointer - int fd; // +6..7 underlying low-level fd - char *buffer; // +8..9 data transfer buffer - char hold; // +10 ungetc byte if no buffer - short token; // +11..12 reserved - char dummy; // +13 reserved - } FILE; - ``` - - stdin/stdout/stderr ? fd-થ `0 / -1 / -2`. ⥫ - stdout/stderr ࠭ ७: ESTEX OPEN positive - small fd (1, 2, ?) 筮 䠩 ? ᫨ stdout=1, ॠ - fd=1 ⠫ 䨪஬. fd=0 stdin ᭮ - (ESTEX 0 頥). fd । syscall' ? - ᯥਧ 䫠 `_F_CONIN/_F_CONOUT`. - - ਭ-⮪ (stdaux/stdprn) ॠ㥬 ? Sprinter ਭ୮ - API . - - ୠ⨢ ? ॠ third_party/solid-c (sources - `SRC/CLIB/`); ⠬ ⮢ buffered FILE + fopen/fread/fwrite/ - fseek/setvbuf .. ஢ 訬 open/read/write/lseek. - - rewrite deferred issues stdio-review: - - `fwrite` short-write ⠢ `_F_ERROR` - - `fgets(buf, 1, fp)` ? ⠭ "empty string", 㫨 NULL - - `mode_to_flags` ? break-out '+' (cosmetic) - -- [ ] **Audio API** ? AY-3-8910 + COVOX १ 뢠 (ॡ IM2) -- [ ] **ISA-8 slot support** ? ZX-Bus (sound, network, etc.) ? ॡ IM2 + ⥭ port - -## 稥 (v1 backlog, 騥) - -- [x] **#9: text I/O split (Turbo-C style)** ? stdio (puts/printf/putchar) ⥯ fast no-attr १ PCHARS/PUTCHAR. conio (cputs/cprintf/putch) ਬ attr १ textcolor/textbackground/textattr. KEEP_EXIST_ATTR ? conio fallback fast path. Verified hello.exe. . `memory/text_output_api_split.md`. 2026-05-31. -- [x] **Mouse API ** (१ driver, RST 30h) ? 14 㭪権 (init/show/hide/refresh/read/goto/bounds/text_cursor/load_cursor/get_cursor/get/set_sensitivity/video_mode_changed). . `memory/mouse_api.md`. Verified MAME 2026-05-31. Sensitivity = divider ( = ॥). -- [ ] Interrupt handlers ? IM 2 vector table HOME user ISR' -- [ ] প `restore SP on EXIT` ( z88dk +pps) ? ஢ 㦭 -- [ ] CI: ⮬᪨ ᪠ MAME `-aviwrite` screenshot-ࠢ, ⮡ ਬ஢ 室 祫 - -## ⮬ - -- প `` ( SDCC stdlib ? 㦭 ஢ crt0 ᮢ⨬) -- `` १ ESTEX SYSTIME (`$21`) CMOS BIOS-㭪樨 -- ZX Spectrum-ᮢ⨬ ० ⤥ target ( ஢ ᯥ㬮᪨ ணࠬ) -- প ZX-Bus (sound, network, etc.) ? 㦭 ࠩ -- Profile-guided optimization tools (hot/cold detection) 㯭 ணࠬ - -## Linker duplicate-symbol warnings (, 䨫஢) - - ᮧ⥫쭮 overrides' SDCC z80.lib 㭪樨 ᮡ⢥ ᨥ `sprinter.lib`, `sdldz80` `?ASlink-Warning-Definition of public symbol '...' found more than once`. ࢮ । (), ⮬ ४⭮ ? warning ⮫쪮 noise. - -騥 overrides: -- `_puts` ? १ PCHARS+\r\n vs SDCC posix puts -- `___sdcc_heap` ? heap W2 vs SDCC's ⠭ -- `_asctime`, `_localtime` ( 㣨 time) ? `posix_time.c` १ ESTEX SYSTIME vs SDCC's `time.rel` `_RtcRead` - -**饥 襭:** `bin/sprinter-cc` 䨫஢뢠 warning- (warning + 2 follow-up `Library:` ப) 뢮 `sdcc`. १ `-v` (verbose) 뢠. १ awk-pipe. - -** 襭:** -- ३ explicit `--nostdlib` + 筮 ᯨ᮪ 㦭 㫥 z80.lib (string, math, stdlib override') ? warning', ᪨஢ -- : २ `_puts` ? `_puts_sprinter` + alias १ linker flag ( 㢥७ SDCC ন) -- : ⠢ ᥩ (ࠡ祥 benign) ? ਮ - -## TODO: ஢ ॠ쭮 - -- [ ] **Port_Y banking trick** (`docs/part2/SprinterGraphics programming.txt`): - ⢥ত ᫥ `OUT (0x89), Y` 0xC000+0x400*N W3 - ப Y..Y+15 ( ணࠬ஢ ? 16 ப). - Empirical 2026-06-01 MAME 0.283 trick ** ࠡ⠥** ? ᥫ - ᠬ 0xC000+row_width 室 . ᪨ - `docs/samples/plasma2.asm` ⮦ ᯮ banking, ⠭ - Port_Y per row. - : - 1. ॠ쭮 Sprinter - 2. dual-write (`_gfx_putpixel_raw` + ன write `0xD000+x`) - 3. ᫨ ? MAME, issue - ९ - 4. ᫨ ⮦ ? 㬥 , 㤠 㯮 - ⠢ ⥪ ॠ (Port_Y per pixel) - 5. ᫨ banking ࠡ⠥ ? ஢ Port_Y - `_gfx_putpixel_raw` (sentinel out-of-range, . memory/gfx_port_y_banking.md) - - 易 먣 Bresenham (60-pixel ) ? 8? - OUT (0x89) 権, `gfx_fill_rect 320x256` ? 16? . - release v1. - -## GFX: ७ `docs/part2/accelerator_doc.txt` - -᫥ ⥭ ⠫쭮 accelerator doc  ࠢ. - ᯮ ⮫쪮 ਧ⠫/⨪ Fill mode. - -### Quick wins ⥪ primitives - -- [ ] ** SMC `LD A, (var)` block-size**. 㬥  - ࠧ蠥 `LD A, (HL)`, `LD A, (BC)`, `LD A, (DE)` ( `LD A, r`). - 㡥 SMC complexity `gfx_lines.c:hfill_chunk/vfill_chunk` - `gfx_16.c:g16_hfill_chunk`. 饭 ⮫쪮 register-to-register. -- [ ] **஢ block-size**. 㬥 뢠 accel - block size bursts (. `Horizontal_Line_Fill`: ⠭ - size + `LD B,B` ⪫祭, ⮬ Fill mode ᯮ - ࠭ size). `gfx_fill_rect` 100 묨 - ப ? ⠭ size 1 ࠧ, 100. - -### Bank-prefix modes (port 0xE2 bits) - -㬥 뢠 ਠ ࠭ ⠭⭮ 0x50: - -| Bank byte | Effect | -|---|---| -| 0x50 | Normal write ? shadow + | -| 0x54 | "no copy in main shadow RAM" | -| 0x58 | **"FF is transparent"** ? 0xFF write ⠢ background | -| 0x5C | both | - -Bank 0x58 祬 mouse cursor 0xFF-஧筮. - **sprite-blending १ accel block copy**: - -- [ ] **`gfx_set_bank_transparent(on)`** 䫠 `gfx_set_bank` - 롮 0x50/0x58 ᮢ sprite' -- [ ] ᯮ짮 `gfx_blit()` ⮡ 䠪 - transparent sprites १ accel- - -### Block copy mode (sprite blit') - -`LD L,L` (horizontal) `LD A,A` (vertical) ? ० ஢ १ -256- accel memory. blit'. - -- [ ] **`gfx_blit(src_data, x, y, w, h)`** ? ஢ sprite' - (ந ࠧ, १ accel) -- [ ] **`gfx_blit_transparent(src, x, y, w, h)`** ? ᯮ짮 bank 0x58 - -. `Draw_Restangle_Data` accelerator_doc.txt ७. - -### AND / OR / XOR operations १ accel - -㬥 뢠 accel ন ᪨ 樨 -. ਬ: -- XOR ? (뤥 selection UI) -- OR / AND ? masking, alpha-style blending -- . ਬ accelerator_doc.txt: "256 bytes block coding via XOR" - -- [ ] **`gfx_xor_rect`** / **`gfx_or_rect`** / **`gfx_and_rect`** ? - ਬ⨢ ᪨ 権 אַ㣮쭨 -- [ ] **`gfx_invert_rect(x, y, w, h)`** ? alias xor 0xFF - -### Bitmap fonts ࠧ ࠧ஢ - - `gfx_text` / `gfx_putchar` प ࠡ 8?8 ⮬ -(BIOS WIN_GET_ZG 頥 256?8 ). UI / 쭨 -㦭: -- [ ] **`gfx_set_font_size(w, h)`** ? ४ ਭ/ glyph' -- [ ] **`gfx_set_font_data(ptr, w, h, advance)`** ? 㪠⥫ - 짮⥫᪨ + ࠧ -- [ ] প **proportional** (advance != w) ⮢ ? - array advance[256] ਭ glyph' -- [ ] **Big-font ०**: 8?16, 16?16, 16?8 ( 쭨) -- [ ] ⤥ API `gfx_text_ex(x, y, str, font_id)` - font_id 롨ࠥ 㦥 ⮢ -- [ ] **Anti-alias 2-bit ** ( 䮭 / ࠭ / 2- alpha?) - ? far future, smooth UI - -## ⠯ ⨬権 ( ᥩ) - -- **`gfx_line` १ accel ** ? Bresenham |dy| << |dx| ( ) 뤠 runs Y ( X): ᥫ, ᥫ, ᥫ, 蠣 Y, ᥫ, ᥫ... ⠪ run ? ⮢ 㬥 `gfx_hline` ( `vline`). - ᫥: runs 㭪 ; run length, ஬ 룮 accel hline 祬 N?putpixel (overhead accel ~20?s, putpixel ~5?s ? accel 룮 run ? 4-5 px); (dx ? dy) ⠢ Bresenham, ? run-length-based fill. - `gfx_line` orthogonal cases 㦥 १ accel ? ⨬஢ ⮫쪮 . - -- **`gfx_fill_rect` W3-swap ** ? ᥩ ७ `gfx_hline`/`gfx_vline` ᢮ DI/save-W3/restore-W3/EI. ᤥ internal `_fill_rect_inner` ন W3 DI 横; ~20?s ? ⢮ ப/⮫殢 . ਬ ᥬ ਬ⨢. -# MDView ? ᬮ騪 Markdown Sprinter - -**MDView** ? ணࠬ ᬮ 㬥⮢ ଠ *Markdown* -Sprinter ( Z80). 㬥 ࠭ ⤥쭮 W3 -᭮ RAM ணࠬ. - -## - -- 㬥 **128 ** (8 ࠭ EMM 16 ) -- **16 384** ࠭ ப -- ⮬᪨ ७ ᫮ ਭ ࠭ (80 ⮫殢) -- ਧ⠫ ᤢ ப ப ( , ⠡) -- -: 䠩, ப, 業 ப⪨ -- ப ﭨ ६ 㧪 樨 -- প ?? ᪫ ப - -## - -``` -mdview [_䠩.md] -``` - -᫨ 䠩 , 㦠 `README.MD`. - -## ࠢ - -``` - ⢨ - -Up Down ப⪠ ப / -PgUp PgDn ப⪠ ࠭ (30 ப) -Home 砫 㬥 -End 㬥 -Left Right ਧ⠫ ᤢ (⮫쪮 nowrap-ப) -F1 ࠢ -F8 ஢: CP866 ? CP1251 ? KOI8-R ? UTF-8 ? ... -F10 / Esc 室 ணࠬ -``` - -஢ । ⮬᪨ ⨨ (BOM + ⨪ - 4 ); `F8` ४砥 , ᫨ ⥪ 訡. -8- ஢ (CP866/CP1251/KOI8-R) ४ (६ -ᮢ). ன / (ਬ UTF-8) ந ? -ࢮ ४祭 (⪠ 㧠 ᯨ஬), . - -## ⠪ Markdown - -### - -ন ஢ H1?H4. ஢ H5 H6 ⮡ࠦ H4. - - # ࢮ ஢ - ## ண ஢ - ### 쥣 ஢ - #### ⮣ ஢ - -### ⮢ ଠ஢ - -** ⥪** 뤥 묨 񧤮窠: `**⥪**` - -*ᨢ* 뤥 묨 񧤮窠 `*⥪*` ન `_⥪_` - -`஥ ` 砥 묨 窠 - -~~ભ ⥪~~ ? ⨫: `~~⥪~~` - -### 㬥஢ ᯨ᮪ - -થ `-`, `*` `+`: - -- 㭪 ᯨ᪠ -- ன 㭪 ᯨ᪠ -- ⨩ 㭪 筮 ⥪⮬, 室 - 㤥 ७ ᫥ ப ࠭ 㯠 - -### 㬥஢ ᯨ᮪ - -1. -2. ன -3. ⨩ - -### - -> 稭 ᨬ `>`. ᪮쪮 ᫥⥫ -> ப ᪫ ⮬᪨ -> ७ᮬ ᫮. - -### (verbatim) - - 砥 ன 窨. -⥪ ⮡ࠦ ? ? ࠧ Markdown: - -``` -#include -#include - -int main(void) { - puts("Hello, Sprinter!"); - return 0; -} -``` - -### ਧ⠫쭠 - - ᨬ `---`, `***` `___` ⤥쭮 ப: - ---- - -## 孨᪨ ࠪ⨪ - -- **ଠ:** Sprinter, Z80 @ 21 -- **஢:** CP866 / CP1251 / KOI8-R / UTF-8 (⮮।, `F8`) -- **ᨬ ࠧ 䠩:** 128 -- **ᨬ쭮 ᫮ ப :** 16 384 -- **ᨬ쭠 ப ७-:** 255 祥 (. ࠭祭 ) -- ** :** small - - ணࠬ, c⥪, , ? a W1-W2 (32 , 0x4000?0xBFFF). - - 䠩 ? ࠭ EMM, ⮡ࠦ W3 (0xC000?0xFFFF) - -## ࠭祭 - -### ப MD-० ? 255 祥 - -- ࠭ ப ** 255 (ᨬ, ਡ)** -? ⠭ `MAX_CACHE_LINE_LEN`. ⨯: `g_ncells` -`cache_rec_t.len` ? `uint8_t`. ᠥ ப, ⭥ ᥣ -**ਧ⠫쭮 ஫㥬** ப ( ப ⠡, 䫠 -`IF_HSCROLL`), MD-० ࠢ. - -** ந室 ப:** 樨 `gc_put()` -뢠 祩 ᫥ 255- (`if (g_ncells < MAX_CACHE_LINE_LEN)`). - ⮫쪮 255 祥, ⮪ **** ? -஫ ** થ १** 255- 樨 (થ `>` 砥 -? । ?). ९ ? `gc_put` ஢ ࠭. - -: ?255 祥? ? **७७ ਭ**, 室. - ࠧ稢 ஡ ( `TAB_STOP`), 祩 ⠡ -஡ ਭ + ࠬ `` ? ⮬ ⨣ ࠭, 祬 -255 ?? ᨬ. - -> **RAW-० (`F2`) ⮣ ** ? אַ 䠩 -> ⮢, ப 楫 (१ wrap `F3` ਧ. ஫). - -** ** (業, ॠ) ? `uint16_t`: -- `cache_rec_t.len` `uint8_t?uint16_t` ? **஢ 8 ** - (len ꥤ pad-), `idx<<3` . ᯫ⭮. -- `g_ncells` / `g_ncells_at_space` ? `uint16_t` ? 業 **/᪮**: - 16-⭠ 䬥⨪ Z80 祬 `gc_put` (맮 祩) - scan-横. ਥ: **+0.2?0.4 ** + 樨. -- `g_cells[MAX_CACHE_LINE_LEN*2]` near-RAM (W2) ? 業 - ****: `2 ? ` . 510 ; 512 ? +0.5 , 1024 ? +1.5 . - EMM- ⥭ (1 ) ப ﭥ ? 㧪 near. -- `viewport_x` ( `docset_t`) + ⮫ `max_vx` (ᥩ 248) ? `uint16_t`, - ࠭ >255 , ஫ . `widths[]`/`ccx` ⠡/. - - ஬ ? 512?1023: 墠 ᥬ ⨭/⠡栬, - 業 ~+0.3 +0.5?1.5 near-RAM. ** 㬥 `uint16_t`** ? - ⠢ થ १ 255- 樨, ⮡ 뫠 . - -## TODO - -1. **祭 ࠧ 㬥⮢.** 128 : 筮 - ࠧ ࠡ 訬 - ࠭ , ஢ - ࠡ 8- ࠭栬 16. - -: ଠ஢ ⠡ ࠬ; প ஢ -CP866 / CP1251 / KOI8-R / UTF-8 ⮮। ४祭 `F8` -(ன ந , ࢮ ४祭). - -4. **᪮७ ७ਭ.** ப ࠭. ⨬ 横 뢮 - ᨬ १ BIOS WRCHAR ( 뢮, DMA). - ---- - -*MDView v0.2 (c) 2026 ஢ ..* - - ---- - -# Sprinter C Compiler ? v1.0 - -C toolchain for **Sprinter** ? the Z80-based home computer by Peters Plus, running -ESTEX DSS. Host: macOS / Linux. Target: `.EXE` files in SprintEXE format. - -Built on top of **SDCC 4.5** (vendored in `third_party/sdcc/`). This repository adds -everything Sprinter-specific: crt0, linker integration, libc wrappers over ESTEX, -banked-call trampolines, graphics & accelerator API, mouse driver wrappers, and the -`mkexe` utility for producing SprintEXE images. - -## What you get - -* **`bin/sprinter-cc`** ? one-line driver: `sprinter-cc -o foo.exe foo.c` -* **Memory modes**: `tiny`, `small`, `big`, `huge`, `manual` ? see below. -* **stdio + conio**: printf, puts, putchar, getchar, fopen/fread/..., cprintf, cputs, putch, textcolor/textbackground/textattr, gotoxy, kbhit/getch. -* **Graphics**: 320?256?256 and 640?256?16 modes, accelerator-backed primitives (hline / vline / rect / fill_rect / line via Bresenham, plus clear), bitmap-font text in both modes via BIOS character generator. -* **File I/O**: POSIX (`open`/`read`/`write`/`close`/`lseek`/`unlink`/`creat`), FILE\* streams (`fopen`/`fgets`/`fwrite`/...), directory listing (`ffirst`/`fnext`), `chdir`/`getcwd`/`mkdir`/`rmdir`, `stat`/`fstat`. -* **Memory**: 32 KB heap (W2-resident), banking-aware page allocator (`mem_alloc_pages`/`bank_read`/`bank_write`), explicit memory modes for sub-16 KB programs. -* **Mouse**: full Sprinter driver wrapper (14 functions including custom cursor bitmaps). -* **Environment**: `getenv`/`putenv`/`sysenv` over ESTEX `$46`. -* **Time**: `getdatetime`/`setdatetime` + POSIX `time`/`localtime`/`mktime`/`asctime`/`ctime`. -* **Misc**: `errno`/`strerror`/`perror`, `atexit`, `setjmp`/`longjmp`, `sleep`, full argv parsing in crt0. - -## Quick start - -```sh -git clone sprinter-c -cd sprinter-c -make sdcc # one-time: fetch SDCC 4.5 binary (~25 MB) -make all # build mkexe + libsprinter.lib + 27 examples -make floppy # pack everything into mame/v306/IMG/mc.img -cd mame/v306 && ./run_mame.sh # boot Sprinter in MAME -``` - -Compile a single program: - -```sh -cat > hello.c < -int main(void) { puts("Hello, Sprinter!"); return 0; } -EOF - -bin/sprinter-cc -o hello.exe hello.c -``` - -That's it ? `hello.exe` is now a valid SprintEXE you can `RUN HELLO` from the ESTEX shell. - -## Memory modes - -Sprinter's address space is four 16 KB windows (W0 / W1 / W2 / W3). DSS allocates -pages by program size ? small programs get only one page. Pick a memory mode based -on what your program needs: - -| Mode | Code lives in | Banking | Use when | Note | -|---|---|---|---|---| -| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | | -| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | | -| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | | -| `huge` | W1-W2 + W3 banking | yes (W3) | small + extra code modules | | -| `manual` | user-specified | optional | special layouts | Not implemented | - -```sh -sprinter-cc --memory small -o big.exe bigprog.c -sprinter-cc --memory huge -o app.exe main.c --bank 1=engine.c --bank 2=ai.c -``` - -Banked functions are declared with `__banked`: - -```c -void engine_tick(int dt) __banked; // lives in BANK1, automatically swapped -``` - -## Examples (27 total) - -| Example | What it demonstrates | -|---|---| -| `hello` | Hello world with stdio + conio Turbo-C-style colors | -| `argv` | argv parsing in crt0 | -| `cat` | File I/O ? read & print TEST.TXT | -| `seek` | 32-bit lseek over a 100 KB file | -| `ls` | Directory listing via ffirst/fnext | -| `filetest` | FILE\* streams (fopen/fread/...) | -| `stattest` | `stat`/`fstat` on files and directories | -| `errno` | errno / strerror / perror | -| `mem_test` | Page allocator + bank\_read/bank\_write | -| `malloc` | Heap stress test (200+ allocations) | -| `banked` | Banked code in W3 (huge mode) | -| `bankedbg` | Banked code in W1 (big mode) | -| `banklocl` | Bank-local static data and BSS | -| `mouse` | Mouse driver in text mode | -| `gfx_mous` | Mouse with custom bitmap cursor in graphics mode | -| `gfx_demo` | 320?256?256 lines / rects / accelerator | -| `gfx_d16` | 640?256?16 same primitives | -| `gfx_text` | Bitmap-font text on graphics screen | -| `timedir` | Date/time + directory listing | -| `ptime` | POSIX time API | -| `openenv` | open() flags + environment vars | -| `conio` | conio API smoke test | -| `attrprob` | Probe Sprinter text attribute byte layout | -| `strtest` | string.h test (from SDCC's z80.lib) | -| `stdlib` | stdlib.h test (qsort / rand / strtol / etc.) | -| `assrtest` | assert() | -| `rt_test` | Runtime helpers (sleep, setjmp, atexit) | - -## Headers - -Standard: -* `` ? puts / printf / FILE\* + Sprinter-specific dec/hex helpers -* `` ? atoi / atof / malloc / qsort / ... (from SDCC z80.lib) -* `` / `` / `` ? from SDCC z80.lib -* `` ? read / write / close / lseek / unlink -* `` ? open / creat + O\_RDONLY / O\_CREAT / ... -* `` ? errno + error names + strerror -* `` ? stat / fstat -* `` / `` ? from SDCC - -Sprinter-specific: -* `` ? putch / cputs / cprintf, textcolor / textbackground / textattr, kbhit / getch, clrscr, gotoxy, wherex/y -* `` ? gfx_init/done, palette, putpixel, hline/vline/rect/fill_rect/line, text ? for both 320 and 640 modes (gfx_\*16 variants) -* `` ? full 14-function driver wrapper + mouse_cursor_t with bitmap support -* `` ? chdir / getcwd / mkdir / rmdir / ffirst / fnext / ffblk -* `` ? getdatetime / setdatetime + POSIX time / localtime / etc. -* `` ? raw ports, ESTEX/BIOS function numbers, env API -* `` ? exit / \_exit / atexit -* `` ? mem\_alloc\_pages / mem\_free\_block / bank\_read / bank\_write -* `` ? Solid-C compatibility layer (aliases + BOOL/WORD/uint types) - -## Toolchain commands - -```sh -make all # build mkexe + lib + every example -make floppy # repack mame/v306/IMG/mc.img with all .exe files -make check # 17 mkexe unit-tests -make clean # remove all build artefacts -make sdcc # one-time: fetch SDCC 4.5 binary -``` - -### sprinter-cc options - -``` -sprinter-cc -o foo.exe foo.c [more.c ...] [options] - - --memory MODE tiny | small | big | huge | manual (default: tiny) - --memory-manual SPEC explicit placement (CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3) - --stack-size N bytes reserved for the stack (default ~1278) - --crt0=TYPE default | minimal | banked | small - --bank N=FILE.c compile FILE.c into bank N (repeatable, max 15) - --debug enable runtime diagnostics (defines DEBUG_RT) - -I PATH extra include path - -L 0xADDR / -E / -S override load / entry / stack addresses - -Wl FLAG pass FLAG to sdldz80 - --mkexe FLAG pass FLAG to mkexe (e.g. --mkexe -p --mkexe 0 for bank padding) - -v verbose -``` - -## Status - -What works in v1.0: -* Compile / link / pack to SprintEXE ? verified on all 27 examples -* Four memory modes (tiny / small / big / huge) -* Graphics (both modes) with accelerator -* Mouse (text + graphics cursor) -* File I/O, directories, environment, time -* All headers listed above - -Deferred to v2.0 (see `docs/TODO.md`): -* **Turbo-C-style BGI graphics API** ? `initgraph` / `setcolor` / `circle` / - `getimage` / `putimage` / etc. on top of our `gfx_*` primitives -* Remaining Solid-C compatibility gaps (Phase 2/3) ? see `docs/solid_c_compatibility.md` -* Manual memory mode -* Rewrite FILE\* stream API (current implementation is very primitive and doesn't use buffers) - -Deferred to v3.0: -* **IM2 interrupt handlers** ? research complete (`docs/im2_isr_design.md`), - implementation scheduled for v3 -* **Audio API** (AY-3-8910 + COVOX) ? requires IM2 -* **ISA-8 slot drivers** ? requires IM2 (???) - -## Documentation - -* `docs/TODO.md` ? roadmap and open work items -* `docs/solid_c_compatibility.md` ? gap analysis vs Solid-C 2004 -* `docs/im2_isr_design.md` ? interrupt handler design (v2) -* `docs/converted/` ? source documentation (ESTEX, BIOS, architecture) - converted to plain text for `grep` -* `docs/reference/`, `docs/samples/`, `docs/memory management/` ? original - Russian docs and code samples from Peters Plus - -## Repository layout - -``` -bin/sprinter-cc one-line compiler driver (bash) -toolchain/mkexe/ host-side tool: .ihx -> .exe SprintEXE -toolchain/check_banks.py post-link bank size enforcer -runtime/ crt0 variants (default, minimal, small, banked) - bank trampolines, heap, heap_top -libc/include/ headers -libc/io|stdio|mem|gfx/ C and asm sources for libsprinter.lib -lib/ Makefile that archives libsprinter.lib via sdar -examples/ 27 example programs -mame/v306/ MAME binary + Sprinter ROM/HDD images + floppy script -third_party/sdcc/ vendored SDCC 4.5 (fetched via `make sdcc`) -third_party/solid-c/ reference: original Sprinter native C (for compat target) -docs/ documentation -``` - -## License - -This repository contains: -* Original code in `bin/`, `toolchain/`, `runtime/`, `libc/`, `lib/`, `examples/` ? - MIT-licensed. -* `third_party/sdcc/` ? SDCC 4.5 under GPLv2 with linking exception - (see `third_party/sdcc/COPYING.txt`) -* `third_party/solid-c/` ? original Sprinter Solid C, used only as a reference - -## Credits - -* **Sprinter / Peters Plus** ? , ਩ ਭ and the original team -* **SDCC** ? for the underlying Z80 compiler -* **MAME** ? for the Sprinter Sp2000 emulation - ---- - -For questions / patches: see CONTRIBUTING.md (TBD) or open an issue. - ---- - -# TODO / Roadmap - - 浪 뢠 ਮ.  ॠ ணࠬ ? ਮ ᬥ. - -## ⠯ 5 ? malloc / free + banking-aware page allocator ? - -- [x] SDCC's `malloc`/`free` + `runtime/heap.s` ( library heap.rel, 14000- heap 2) -- [x] `libc/mem/mem_alloc.c` ? page allocator: `mem_alloc_pages`/`mem_free_block`/`mem_get_page`/`mem_info` १ ESTEX `$3C/$3D/$3E` + BIOS `$C4` -- [x] `libc/mem/bank_io.c` ? HOME-१ `bank_read`/`bank_write`/`bank_load_byte`/`bank_store_byte` ᢮ W3 -- [x] `examples/malloc_test/` ? ஢ઠ SDCC's malloc (~210 64- allocations १ heap) -- [x] `examples/mem_test/` ? ஢ઠ page allocator: 3 ࠭, ࠧ १ bank_write, 䨪 १ bank_read - -## ⠯ 6 ? argv parsing + sprinter-cc wrapper ? - -- [x] crt0 ESTEX command-line IX-prefix (inline asm `runtime/crt0.s`) -- [x] Strip leading CP/M-style space (DSS quirk) -- [x] । `argc`/`argv` main() १ HL/DE (SDCC __sdcccall(1) ABI) -- [x] argv[0] = basename .EXE १ ESTEX APPINFO ($47 subfn 2) -- [x] `runtime/crt0_minimal.s` ? opt-out 祭 쪨 ணࠬ -- [x] `runtime/crt0_banked.s` ? ⥯ ⮦ argv (parse_argv + get_progname ᪮஢ crt0.s; 㤥 factored argv.s libsprinter.lib) -- [x] Bash-⪠ `bin/sprinter-cc`: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権: `--memory`, `--memory-manual`, `--stack-size`, `--crt0=`, `--bank N=FILE.c`, `--debug`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` - -## ⠯ 8 ? 䨪 (320?256?256 + 640?256?16 + accel + bitmap font) ? - -- [x] **8a** Graphics core: `gfx_init`/`gfx_done`/`gfx_clear`/`gfx_putpixel`/`gfx_pal_load`/`gfx_pal_set` (libc/gfx/gfx_core.c). १ BIOS PIC_SET_PAL ($A4). Verified 320?256?256. -- [x] **8b** /אַ㣮쭨/fill १ accelerator (libc/gfx/gfx_lines.c): `gfx_hline`/`gfx_vline` १ accel Fill (LD C,C / LD E,E + SMC block-size), `gfx_rect`/`gfx_fill_rect` heuristic 롮஬ ਥ樨 (h/v bursts count), `gfx_line` Bresenham . `gfx_clear` ⮦ ९ᠭ column-major accel (~4? ॥). -- [x] **8c** 640?256?16 mode (libc/gfx/gfx_16.c): `gfx_*16` API, HIGH nibble = LEFT pixel (㬥 misleading), per-row RMW vline ( = 2 ਧ⠫ ᥫ). -- [x] **8d** Bitmap font + gfx_text (libc/gfx/gfx_text.c): १ BIOS WIN_GET_ZG ($B8), interleaved layout `font[row*256+char]`, `gfx_text`/`gfx_putchar` 320 mode, `gfx_text16`/`gfx_putchar16` 640 mode pair-table lookup. - -. memory/sprinter_graphics.md, sprinter_accelerator.md, sprinter_graphics_16.md, sprinter_font_format.md. - - ( ): -- [ ] -quad 640: per-cell (mode 0x82 ࠧ蠥 1 4 per 16?8 cell) ? १ אַ area-ᠭ ࠭ 0x0300..0x039F - -## Auto-banking (. `memory/banking_roadmap.md` ⠫) - -Phase 1 ? file-level bin-packing ? ॠ뢠 ஥ ~30 KB . - -- [ ] `toolchain/auto_bank.py`: - - ࠧ `.rel`-䠩 ( .map ᫥ dry-run link') - - First-fit-decreasing bin-packing - - `#pragma codeseg BANKn` manual override - - ५뢠 묨 `-Wl-b_BANKn=...` ࠬࠬ - - ⠥ । - -Phase 2-5: incremental rebalance, declarative `banks.toml`, function-level, call-graph-aware. 쪮 ᫨/ . - -## Bank-local static data (mutable data ⮬ ) ? ? - -- [x] ਬ `examples/bank_local_data/` ? 㭪 BANK1 ᢮ writable BSS array + const table + malloc- -- [x] `mkexe -p 0` 㫥 padding (BSS-storage 㧪) -- [x] ᪨ 楯: `--codeseg BANK1 --constseg BANK1 --dataseg BANK1` bank1.c + `-Wl-b_BANK1=0x1C000` . **`--dataseg BANK1` ** ? ࠭ ⭮ - trampoline bug ᪨஢ १. -- [x] ** 䨪 trampoline'a runtime/bank.s** ? `pop af; out (n), a` ਫ A ? banked-㭪樨 騥 uint8_t 頫 . `pop bc; out (c), b` ࠭ A. -- [x] **malloc banked-㭪樨 ࠡ⠥ ஧筮** ? heap W2 (HOME), W2 ᢠ trampoline', pointer  ⥪. . memory/bank_local_data_pattern.md. -- [x] 㬥 memory: `memory/bank_local_data_pattern.md` ( 楯 + malloc + nuances), `memory/sdcc_banking.md` (trampoline fix) -- [ ] 樮쭮 ? `check_banks.py` ⮡ 뢠 ࠧ size = code + const + bss per bank (cosmetic) - -祬: 㫥 訬 private state (level loader, audio engine, scene data). W2 heap , ⨪ . - -## ᪠ solid-c (⨢ Sprinter C ? `third_party/solid-c/`) - -᫥ solid-c'᪮ libc (. `memory/solid_c_findings.md`)  ⮢ ᫥ 㭪権. ਮ **᮪** : - -### High-priority gaps ( ஢, 짠) -- [x] **`errno` + `strerror`/`perror`** ? ⠡窠 32 訡 (libc/io/errno.c) -- [x] **७ `open()`** O_CREAT/O_TRUNC/O_APPEND/O_EXCL state machine -- [x] **`atexit`** ? 8-callback LIFO + `exit()` + `_exit()` (libc/io/atexit.c) -- [x] **`setjmp`/`longjmp`** ? 6- jmp_buf={sp,ix,pc} (libc/io/setjmp.c) -- [x] **`sleep(seconds)`** ? 50Hz halt-loop (libc/io/sleep.c) -- [x] **ESTEX ENV API** ($46, getenv/putenv) ? libc/io/env.c. 竨 doc-bug: ॠ쭮 A=0 NOT FOUND - -### Medium-priority (㦭 shell-like ⨫) -- [ ] **Mouse driver** ? `rst $30h`, 17 㭪権. **砫 ࠡ⠥ MAME**. -- [x] **`ffirst`/`fnext` + ffblk_t struct** directory listing ? ॠ, demo: ls.exe -- [x] **`getdatetime`/`setdatetime`** १ ESTEX $21/$22 ? libc/io/time.c, demo: time_dir_test -- [x] **`chdir`/`getcwd`/`mkdir`/`rmdir`** ? wrappers ESTEX $1B-$1E ? libc/io/fsdir.c -- [x] **conio: `kbhit`/`getch`/`getche`/`cputs`/`clrscr`/`gotoxy`** ? ॠ -- [x] **conio extras**: `wherex`/`wherey` ($53), `wrchar`/`rdchar` ($58/$57), `textmode_get/set` ($50/$51), `clrscr_attr` ($56) + COLOR macros - -### Low-priority ? ? FILE* stack - -- [x] ** unbuffered FILE\*** ? `libc/stdio/file.c` + `libc/include/stdio.h`. fopen/fclose/fputs/fgets/fread/fwrite/fseek/ftell/rewind/feof/ferror/clearerr/fflush + stdin/stdout/stderr pseudo-streams. . `memory/file_star_design.md` `examples/filetest`. -- [ ] fprintf / fscanf ? 㦭 printf-१-callback machinery. 짮⥫ `sprintf(buf, ...) + fputs(buf, fp)`. -- [ ] 樮 buffered mode (setvbuf, line/block buffering) ? ᫨ - . - -### POSIX time API ? ? -- [x] `libc/io/posix_time.c` ? time/localtime/gmtime/mktime/asctime/ctime getdatetime. SDCC's time.rel ( override _RtcRead). . `examples/ptime`. - -### sys/stat ? ? -- [x] `libc/io/stat.c` ? POSIX stat/fstat. ਤ open+fstat 䠩, ffirst+iter ( "."/".."). . `examples/stattest` `memory/estex_ffirst_dotdot.md`. - -### assert ? ? (ᯮ㥬 SDCC's __assert १ fallback include path) - -## libc/stdlib ? ? 㦭 (. memory/sdcc_stdlib_works.md) - -஢७ १ `examples/stdlib_test/`: SDCC z80.lib ᮤন ࠡ騥 ॠ樨: -- `atoi/atol/atof, strtol/strtoul, rand/srand, qsort/bsearch, abs/labs, div/ldiv` -- `` (memchr/cmp/set/cpy, strcat/cmp/cpy/len/chr/spn/etc.) -- `` (toupper/tolower) -- `` (sinf/cosf/sqrtf/etc.) - - ⮬᪨ ﭥ z80.lib 㦭. ** ९뢠**. - - Sprinter-specific 易⥫ 㫨 : atexit, env, errno, setjmp, putchar/puts/getchar, conio, fsdir, time, mouse, open/read/lseek/close. - -## Build-system: libsprinter.lib + sprinter-cc ? ? - -- [x] `lib/Makefile` ? ᮡࠥ libc/*.c `.rel`, 娢 १ sdar `lib/sprinter.lib` -- [x] 砥 runtime/bank.s runtime/heap.s (auto-pulled __banked/malloc) -- [x] `bin/sprinter-cc` ? bash-wrapper: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権 `--crt0=default|minimal|banked`, `--bank N=FILE.c`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` -- [x] `examples/hello_sccc/` ? : `hello.c` ᮡࠥ shell-맮, ࠧ ᮢ Makefile (925 ) -- [x] Split `putchar.c` ? `putchar.c` + `puts.c` per-function granularity (puts override SDCC's z80.lib version) -- [x] `make all` (ᨬ `lib` । `examples`) - - 襭 (樮쭮): -- [ ] ஢ ⠫ examples sprinter-cc Makefile (ᬥ⨪) -- [ ] 쭥 libc/*.c per-function ( ⥪ granularity 㦥 㦭 ࠧ ? .rel 楫, 設⢠ 䠩 㭪) - -## ⠯ 9 ? memory modes sprinter-cc - -DSS 뤥 ࠭ ࠧ ਫ: < 16 KB ? ࠭, ⠫ 砥 ?࠭ #FF? (read=0xFF, write ). - ⮣ CODE--W1 + DATA--W2 쪮 ணࠬ . . [memory/sprinter_memory_modes.md](../../.claude/projects/-Volumes-SAM8-Projects-DIY-Z80-Sprinter-C-Compiler/memory/sprinter_memory_modes.md). - -- [x] **`tiny`**: (CODE+DATA+⥪) W2. Default. Verified hello/argv/conio/malloc/file/etc. -- [x] **`--memory MODE` 䫠 sprinter-cc**: parser + per-mode 䮫 CODE_LOC/DATA_LOC, override १  `--code-loc`/`--data-loc`. tiny ࠡ⠥; small/big/huge warning' (runtime ⮢). 2026-05-30. -- [x] **`--memory-manual SPEC`**: `CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3`. 2026-05-30. -- [x] **`small` runtime**: `runtime/crt0_small.s` ᯮ ESTEX `$3D GETMEM` + `$3A SETWIN2` ⮡ 뤥 W2-࠭ gsinit. **** BIOS `$C4` ? ⥪ ⮬ ⠯ W1 (boot_stack HOME), BIOS ॡ ⥪ W2. ᫥ SP ४砥 0xBFFE, ⠭ flow. 2026-05-30, verified hello.exe. -- [x] **`small` auto-detect >16 KB ணࠬ**: `crt0_small.s` ⠥ `0xC2` (⥪ ࠭ W2 ? `0xA2`! W1). ᫨ 0xFF ? 뤥 page; DSS 㦥 ᤥ (ணࠬ ᠬ 뫥 W2). crt0 뢠 0..30 KB. mkexe ⠪ ࠧ蠥 HOME span W1+W2 (0x4000..0xBFFF). Verified hello: small (5 KB 䠩, SETWIN2 path) + 32 KB 䠩 (auto-skip). 2026-05-30. -- [x] **`big` runtime** (tiny + banked code W1): ࠬਧ `crt0_banked.s` + `bank.s` १ `.ifdef BANK_W1` ? 㣮 banking port (0xA2 vs 0xE2), 㣮 load-addr (0x4000 vs 0xC000). sprinter-cc prepend' `BANK_W1 = 1` `--memory big`, । `mkexe -B 0x4000`. ਬ `examples/banked_big/`. 2026-05-30. -- [x] **`huge` runtime** (small + banked code W3): merge W2-detect `crt0_small.s` `crt0_banked.s`. 騩 ਬ `examples/banked/` ⥯ ᯮ MEMORY=huge. 2026-05-30. -- [x] **`--debug` 䫠**: prepend `DEBUG_RT = 1` crt0 + `-DDEBUG_RT` sdcc. 뢠 symbol `_w2_self_allocated` (uint8_t) ? runtime diagnostic ஢ W2. 2026-05-30. - -### -襭 libc crt0 - -** `sprinter.lib`** ࠡ⠥ memory mode ? `.rel`-童 relocatable, SDLD dead-code elimination per-member ( 䨪  `gfx_core.rel` ..). Verified hello vs malloc_test १ map-䠩. - -**`gfx.lib` ⤥쭮 ? 㦥**: dead-code elimination 㦥 ࠡ⠥. - -**`libc_banked` (libc bank HOME)** ? ⮬, HOME (16 KB) user- + libc `huge` mode. १ `--codeseg BANK0` 樨 libc; trade-off: trampoline ~30 横 libc-맮. ਣ: ॠ쭠 ணࠬ HOME budget. - -**HW-ᨬ 㫨 ? `sprinter_home.lib` ⤥쭮.** libc 䨧᪨ W3, ⮬ W3: -- `gfx_*` ? `0xC000+` ᫥ swap W3 video page -- `bank_io` (mem_alloc_pages/bank_read/bank_write) ? swap' W3 १ `OUT (0xE2)` -- 騥 ISR ? 뢠 ਩ W3 㣮 - - huge mode 㫨 HOME (W1). 㤥 `libc_banked`, **६** 뤥塞 `sprinter_home.lib` (HOME-only) `sprinter.lib` (bankable). 쭠 奬: -``` -sprinter_home.lib HOME-only: gfx, bank_io, ISR shims -sprinter.lib bankable: printf, malloc, string, conio, stdio, env, ... -sprinter_banked.lib sprinter.lib --codeseg BANK0 ( huge) -``` -ਣ: ॠ `--memory huge` runtime. - -**crt0 ? mode:** -- `crt0.s` ? ⥪騩, **tiny/big**: SP=0xBFFE, argv (W2- 㦥 뤥 DSS). -- `crt0_minimal.s` ? ⥪騩, tiny argv. -- `crt0_small.s` ? **, step 3**: **small/huge**, W2 १ `mem_alloc_pages` gsinit, `0xA2`, ⮬ ⠭ flow. -- `crt0_banked.s` ? ⥪騩, **big**: trampoline-⠡ W3 , CODE W2. -- `crt0_banked_small.s` ? ****: huge = small (W2-alloc) + banked (W3 trampolines). - -sprinter-cc ࠥ crt0 `--memory` mode (ᥩ `--crt0=` override). -- [x] **ࠨ ࠧ ⥪**: 䫠 `sprinter-cc --stack-size BYTES`. Wrapper `heap_top.s` `___sdcc_heap_end = stack_top + 1 - stack_size`, ⤥ .rel per-program. Default ?1278 (heap_top=0xBB00) `runtime/heap_top.s`. 2026-05-30. - -䥩: `sprinter-cc --memory [tiny|small|big|huge|manual] [--memory-manual SPEC] [--stack-size N] foo.c`. `--memory-manual` ⮫쪮 `--memory manual`. - -## Known issues / quirks - -- **ESTEX $46 ENV API**: ? ࠡ⠥. - `DiskSyscalls.txt v1.6` 訡筮 뢠 return-status ? A=0 NOT FOUND, FOUND. 䨪஢ `memory/sprinter_platform.md`. - -## V2 (᫥ ५ v1) - -### Turbo-C-style graphics API (BGI-like) ? **MUST v2** - - `gfx_*` API ஢ **Turbo-C ``** (BGI) MS-DOS. -ணࠬ ਢ訥 Turbo-C ७ ᪨ 1--1. - -** ** ( ᭮ Borland BGI): - -Setup/teardown: -- `initgraph()` / `closegraph()` ? ᥩ `gfx_init`/`gfx_done`, alias -- `getmaxx()` / `getmaxy()` ? GFX_WIDTH-1 / GFX_HEIGHT-1 -- `cleardevice()` ? alias to gfx_clear -- `getgraphmode()` / `setgraphmode()` ? get_videomode/set_videomode - -Color/palette: -- `setcolor(c)`, `getcolor()` ? current draw color -- `setbkcolor(c)`, `getbkcolor()` ? background color -- `setpalette(idx, c)` ? entry -- `getpalette(&info)` ? read all palette - -Primitives ( 㦥 ? BGI- aliases): -- `putpixel(x, y, c)` ? gfx_putpixel -- `getpixel(x, y)` ? 㦭 ॠ (RMW ⭮ ? IN) -- `moveto(x, y)`, `lineto(x, y)`, `linerel(dx, dy)` ? current point + line drawing -- `line(x1, y1, x2, y2)` ? gfx_line -- `rectangle(x1, y1, x2, y2)` ? gfx_rect ( 㣮 API: x1,y1,x2,y2 vs x,y,w,h!) -- `bar(x1, y1, x2, y2)` ? gfx_fill_rect -- `bar3d(x1, y1, x2, y2, depth, topflag)` ? : rect + 3d edges -- `circle(x, y, r)`, `arc(...)`, `ellipse(...)`, `pieslice(...)` ? primitives -- `fillpoly()`, `drawpoly()` ? -- `floodfill(x, y, border_color)` ? - -Text on graphics screen: -- `outtext(s)` / `outtextxy(x, y, s)` ? gfx_text (alias) -- `settextstyle(font, dir, size)` ? multiple bitmap fonts -- `gettextsettings(&info)` -- `textwidth(s)` / `textheight(s)` ? measure - -Image manipulation: -- `imagesize(x1, y1, x2, y2)` ? bytes needed for getimage -- `getimage(x1, y1, x2, y2, buf)` ? save rect to buffer -- `putimage(x, y, buf, op)` ? paste back with COPY_PUT/XOR_PUT/AND_PUT/OR_PUT/NOT_PUT - -Clipping/viewport: -- `setviewport(x1, y1, x2, y2, clip)` ? drawing clip rect -- `getviewsettings(&info)` -- `clearviewport()` -- `setactivepage(p)` / `setvisualpage(p)` ? ਧ (Sprinter 2 screen) - -Line style: -- `setlinestyle(style, pattern, thickness)` ? SOLID_LINE / DOTTED_LINE / etc. -- `getlinesettings(&info)` - -**Acceptance:** ७ ⨯筮 Turbo-C BGI ணࠬ (饩 ᯮ짮 -moveto/lineto/circle/bar/setcolor) ࠡ ⢥ ࠢ. - -**Notes:** -- BGI fonts (TRIPLEX/SANS_SERIF/GOTHIC) ? BIOS font, ⠫ 㦭 - ( bitmap data lib) -- imagesize/getimage/putimage ? ᠬ ॡ game/animation -- Active/visual page ( ਧ) ? Sprinter ন 2 graphics pages, - 㦥 API switching - -. ⠪ `examples/` Turbo C 2.x BGIDEMO reference 㦭. - -### IM2 Interrupt Handlers ? **MUST v2** - -User- ISR १ Z80 IM 2 mode. 㦭 : -- Timer ticks (50 Hz frame counter, ) -- Music playback (AY, COVOX) -- Real-time games (input + game logic + render interrupt-driven) -- Async keyboard / mouse handling - -**Status:** v2. research + design `docs/im2_isr_design.md`. - -**襭 ⥪:** ॠ ⤥ memory mode `--memory im2` -( ⮣ ⮡ 騥 crt0). Detail' design-doc. - -** research'** ( ⥪ `docs/im2_isr_design.md`): -- Vector 0xFF ? frame + keyboard + CBL. Disambiguation ⠬ 0x19 / 0xFE -- Mouse hardware-IRQ 室 ( ⥪饩 ) -- Vector table / ISR / stack W2 (0x8000..0xBFFF) -- DSS ᢮ IM 2 handler ? 㦭 chain' ( / SYSTIME ) - -### 稥 㯭 㭪 v2 - -- [ ] **FILE API rewrite ? buffered streams** ? ⥪ ॠ - `libc/stdio/file.c` provisional unbuffered shim ( fputc/fgetc - = read/write syscall). 㦭 業 buffered ᥬ⨪ - Solid-C: - - ```c - typedef struct { - uint flags; // +0..1 file status flags - int level; // +2..3 empty/fill level of buffer - char *curp; // +4..5 current active pointer - int fd; // +6..7 underlying low-level fd - char *buffer; // +8..9 data transfer buffer - char hold; // +10 ungetc byte if no buffer - short token; // +11..12 reserved - char dummy; // +13 reserved - } FILE; - ``` - - stdin/stdout/stderr ? fd-થ `0 / -1 / -2`. ⥫ - stdout/stderr ࠭ ७: ESTEX OPEN positive - small fd (1, 2, ?) 筮 䠩 ? ᫨ stdout=1, ॠ - fd=1 ⠫ 䨪஬. fd=0 stdin ᭮ - (ESTEX 0 頥). fd । syscall' ? - ᯥਧ 䫠 `_F_CONIN/_F_CONOUT`. - - ਭ-⮪ (stdaux/stdprn) ॠ㥬 ? Sprinter ਭ୮ - API . - - ୠ⨢ ? ॠ third_party/solid-c (sources - `SRC/CLIB/`); ⠬ ⮢ buffered FILE + fopen/fread/fwrite/ - fseek/setvbuf .. ஢ 訬 open/read/write/lseek. - - rewrite deferred issues stdio-review: - - `fwrite` short-write ⠢ `_F_ERROR` - - `fgets(buf, 1, fp)` ? ⠭ "empty string", 㫨 NULL - - `mode_to_flags` ? break-out '+' (cosmetic) - -- [ ] **Audio API** ? AY-3-8910 + COVOX १ 뢠 (ॡ IM2) -- [ ] **ISA-8 slot support** ? ZX-Bus (sound, network, etc.) ? ॡ IM2 + ⥭ port - -## 稥 (v1 backlog, 騥) - -- [x] **#9: text I/O split (Turbo-C style)** ? stdio (puts/printf/putchar) ⥯ fast no-attr १ PCHARS/PUTCHAR. conio (cputs/cprintf/putch) ਬ attr १ textcolor/textbackground/textattr. KEEP_EXIST_ATTR ? conio fallback fast path. Verified hello.exe. . `memory/text_output_api_split.md`. 2026-05-31. -- [x] **Mouse API ** (१ driver, RST 30h) ? 14 㭪権 (init/show/hide/refresh/read/goto/bounds/text_cursor/load_cursor/get_cursor/get/set_sensitivity/video_mode_changed). . `memory/mouse_api.md`. Verified MAME 2026-05-31. Sensitivity = divider ( = ॥). -- [ ] Interrupt handlers ? IM 2 vector table HOME user ISR' -- [ ] প `restore SP on EXIT` ( z88dk +pps) ? ஢ 㦭 -- [ ] CI: ⮬᪨ ᪠ MAME `-aviwrite` screenshot-ࠢ, ⮡ ਬ஢ 室 祫 - -## ⮬ - -- প `` ( SDCC stdlib ? 㦭 ஢ crt0 ᮢ⨬) -- `` १ ESTEX SYSTIME (`$21`) CMOS BIOS-㭪樨 -- ZX Spectrum-ᮢ⨬ ० ⤥ target ( ஢ ᯥ㬮᪨ ணࠬ) -- প ZX-Bus (sound, network, etc.) ? 㦭 ࠩ -- Profile-guided optimization tools (hot/cold detection) 㯭 ணࠬ - -## Linker duplicate-symbol warnings (, 䨫஢) - - ᮧ⥫쭮 overrides' SDCC z80.lib 㭪樨 ᮡ⢥ ᨥ `sprinter.lib`, `sdldz80` `?ASlink-Warning-Definition of public symbol '...' found more than once`. ࢮ । (), ⮬ ४⭮ ? warning ⮫쪮 noise. - -騥 overrides: -- `_puts` ? १ PCHARS+\r\n vs SDCC posix puts -- `___sdcc_heap` ? heap W2 vs SDCC's ⠭ -- `_asctime`, `_localtime` ( 㣨 time) ? `posix_time.c` १ ESTEX SYSTIME vs SDCC's `time.rel` `_RtcRead` - -**饥 襭:** `bin/sprinter-cc` 䨫஢뢠 warning- (warning + 2 follow-up `Library:` ப) 뢮 `sdcc`. १ `-v` (verbose) 뢠. १ awk-pipe. - -** 襭:** -- ३ explicit `--nostdlib` + 筮 ᯨ᮪ 㦭 㫥 z80.lib (string, math, stdlib override') ? warning', ᪨஢ -- : २ `_puts` ? `_puts_sprinter` + alias १ linker flag ( 㢥७ SDCC ন) -- : ⠢ ᥩ (ࠡ祥 benign) ? ਮ - -## TODO: ஢ ॠ쭮 - -- [ ] **Port_Y banking trick** (`docs/part2/SprinterGraphics programming.txt`): - ⢥ত ᫥ `OUT (0x89), Y` 0xC000+0x400*N W3 - ப Y..Y+15 ( ணࠬ஢ ? 16 ப). - Empirical 2026-06-01 MAME 0.283 trick ** ࠡ⠥** ? ᥫ - ᠬ 0xC000+row_width 室 . ᪨ - `docs/samples/plasma2.asm` ⮦ ᯮ banking, ⠭ - Port_Y per row. - : - 1. ॠ쭮 Sprinter - 2. dual-write (`_gfx_putpixel_raw` + ன write `0xD000+x`) - 3. ᫨ ? MAME, issue - ९ - 4. ᫨ ⮦ ? 㬥 , 㤠 㯮 - ⠢ ⥪ ॠ (Port_Y per pixel) - 5. ᫨ banking ࠡ⠥ ? ஢ Port_Y - `_gfx_putpixel_raw` (sentinel out-of-range, . memory/gfx_port_y_banking.md) - - 易 먣 Bresenham (60-pixel ) ? 8? - OUT (0x89) 権, `gfx_fill_rect 320x256` ? 16? . - release v1. - -## GFX: ७ `docs/part2/accelerator_doc.txt` - -᫥ ⥭ ⠫쭮 accelerator doc  ࠢ. - ᯮ ⮫쪮 ਧ⠫/⨪ Fill mode. - -### Quick wins ⥪ primitives - -- [ ] ** SMC `LD A, (var)` block-size**. 㬥  - ࠧ蠥 `LD A, (HL)`, `LD A, (BC)`, `LD A, (DE)` ( `LD A, r`). - 㡥 SMC complexity `gfx_lines.c:hfill_chunk/vfill_chunk` - `gfx_16.c:g16_hfill_chunk`. 饭 ⮫쪮 register-to-register. -- [ ] **஢ block-size**. 㬥 뢠 accel - block size bursts (. `Horizontal_Line_Fill`: ⠭ - size + `LD B,B` ⪫祭, ⮬ Fill mode ᯮ - ࠭ size). `gfx_fill_rect` 100 묨 - ப ? ⠭ size 1 ࠧ, 100. - -### Bank-prefix modes (port 0xE2 bits) - -㬥 뢠 ਠ ࠭ ⠭⭮ 0x50: - -| Bank byte | Effect | -|---|---| -| 0x50 | Normal write ? shadow + | -| 0x54 | "no copy in main shadow RAM" | -| 0x58 | **"FF is transparent"** ? 0xFF write ⠢ background | -| 0x5C | both | - -Bank 0x58 祬 mouse cursor 0xFF-஧筮. - **sprite-blending १ accel block copy**: - -- [ ] **`gfx_set_bank_transparent(on)`** 䫠 `gfx_set_bank` - 롮 0x50/0x58 ᮢ sprite' -- [ ] ᯮ짮 `gfx_blit()` ⮡ 䠪 - transparent sprites १ accel- - -### Block copy mode (sprite blit') - -`LD L,L` (horizontal) `LD A,A` (vertical) ? ० ஢ १ -256- accel memory. blit'. - -- [ ] **`gfx_blit(src_data, x, y, w, h)`** ? ஢ sprite' - (ந ࠧ, १ accel) -- [ ] **`gfx_blit_transparent(src, x, y, w, h)`** ? ᯮ짮 bank 0x58 - -. `Draw_Restangle_Data` accelerator_doc.txt ७. - -### AND / OR / XOR operations १ accel - -㬥 뢠 accel ন ᪨ 樨 -. ਬ: -- XOR ? (뤥 selection UI) -- OR / AND ? masking, alpha-style blending -- . ਬ accelerator_doc.txt: "256 bytes block coding via XOR" - -- [ ] **`gfx_xor_rect`** / **`gfx_or_rect`** / **`gfx_and_rect`** ? - ਬ⨢ ᪨ 権 אַ㣮쭨 -- [ ] **`gfx_invert_rect(x, y, w, h)`** ? alias xor 0xFF - -### Bitmap fonts ࠧ ࠧ஢ - - `gfx_text` / `gfx_putchar` प ࠡ 8?8 ⮬ -(BIOS WIN_GET_ZG 頥 256?8 ). UI / 쭨 -㦭: -- [ ] **`gfx_set_font_size(w, h)`** ? ४ ਭ/ glyph' -- [ ] **`gfx_set_font_data(ptr, w, h, advance)`** ? 㪠⥫ - 짮⥫᪨ + ࠧ -- [ ] প **proportional** (advance != w) ⮢ ? - array advance[256] ਭ glyph' -- [ ] **Big-font ०**: 8?16, 16?16, 16?8 ( 쭨) -- [ ] ⤥ API `gfx_text_ex(x, y, str, font_id)` - font_id 롨ࠥ 㦥 ⮢ -- [ ] **Anti-alias 2-bit ** ( 䮭 / ࠭ / 2- alpha?) - ? far future, smooth UI - -## ⠯ ⨬権 ( ᥩ) - -- **`gfx_line` १ accel ** ? Bresenham |dy| << |dx| ( ) 뤠 runs Y ( X): ᥫ, ᥫ, ᥫ, 蠣 Y, ᥫ, ᥫ... ⠪ run ? ⮢ 㬥 `gfx_hline` ( `vline`). - ᫥: runs 㭪 ; run length, ஬ 룮 accel hline 祬 N?putpixel (overhead accel ~20?s, putpixel ~5?s ? accel 룮 run ? 4-5 px); (dx ? dy) ⠢ Bresenham, ? run-length-based fill. - `gfx_line` orthogonal cases 㦥 १ accel ? ⨬஢ ⮫쪮 . - -- **`gfx_fill_rect` W3-swap ** ? ᥩ ७ `gfx_hline`/`gfx_vline` ᢮ DI/save-W3/restore-W3/EI. ᤥ internal `_fill_rect_inner` ন W3 DI 横; ~20?s ? ⢮ ப/⮫殢 . ਬ ᥬ ਬ⨢. -# MDView ? ᬮ騪 Markdown Sprinter - -**MDView** ? ணࠬ ᬮ 㬥⮢ ଠ *Markdown* -Sprinter ( Z80). 㬥 ࠭ ⤥쭮 W3 -᭮ RAM ணࠬ. - -## - -- 㬥 **128 ** (8 ࠭ EMM 16 ) -- **16 384** ࠭ ப -- ⮬᪨ ७ ᫮ ਭ ࠭ (80 ⮫殢) -- ਧ⠫ ᤢ ப ப ( , ⠡) -- -: 䠩, ப, 業 ப⪨ -- ப ﭨ ६ 㧪 樨 -- প ?? ᪫ ப - -## - -``` -mdview [_䠩.md] -``` - -᫨ 䠩 , 㦠 `README.MD`. - -## ࠢ - -``` - ⢨ - -Up Down ப⪠ ப / -PgUp PgDn ப⪠ ࠭ (30 ப) -Home 砫 㬥 -End 㬥 -Left Right ਧ⠫ ᤢ (⮫쪮 nowrap-ப) -F1 ࠢ -F8 ஢: CP866 ? CP1251 ? KOI8-R ? UTF-8 ? ... -F10 / Esc 室 ணࠬ -``` - -஢ । ⮬᪨ ⨨ (BOM + ⨪ - 4 ); `F8` ४砥 , ᫨ ⥪ 訡. -8- ஢ (CP866/CP1251/KOI8-R) ४ (६ -ᮢ). ன / (ਬ UTF-8) ந ? -ࢮ ४祭 (⪠ 㧠 ᯨ஬), . - -## ⠪ Markdown - -### - -ন ஢ H1?H4. ஢ H5 H6 ⮡ࠦ H4. - - # ࢮ ஢ - ## ண ஢ - ### 쥣 ஢ - #### ⮣ ஢ - -### ⮢ ଠ஢ - -** ⥪** 뤥 묨 񧤮窠: `**⥪**` - -*ᨢ* 뤥 묨 񧤮窠 `*⥪*` ન `_⥪_` - -`஥ ` 砥 묨 窠 - -~~ભ ⥪~~ ? ⨫: `~~⥪~~` - -### 㬥஢ ᯨ᮪ - -થ `-`, `*` `+`: - -- 㭪 ᯨ᪠ -- ன 㭪 ᯨ᪠ -- ⨩ 㭪 筮 ⥪⮬, 室 - 㤥 ७ ᫥ ப ࠭ 㯠 - -### 㬥஢ ᯨ᮪ - -1. -2. ன -3. ⨩ - -### - -> 稭 ᨬ `>`. ᪮쪮 ᫥⥫ -> ப ᪫ ⮬᪨ -> ७ᮬ ᫮. - -### (verbatim) - - 砥 ன 窨. -⥪ ⮡ࠦ ? ? ࠧ Markdown: - -``` -#include -#include - -int main(void) { - puts("Hello, Sprinter!"); - return 0; -} -``` - -### ਧ⠫쭠 - - ᨬ `---`, `***` `___` ⤥쭮 ப: - ---- - -## 孨᪨ ࠪ⨪ - -- **ଠ:** Sprinter, Z80 @ 21 -- **஢:** CP866 / CP1251 / KOI8-R / UTF-8 (⮮।, `F8`) -- **ᨬ ࠧ 䠩:** 128 -- **ᨬ쭮 ᫮ ப :** 16 384 -- ** :** small - - ணࠬ, c⥪, , ? a W1-W2 (32 , 0x4000?0xBFFF). - - 䠩 ? ࠭ EMM, ⮡ࠦ W3 (0xC000?0xFFFF) - -## TODO - -1. **祭 ࠧ 㬥⮢.** 128 : 筮 - ࠧ ࠡ 訬 - ࠭ , ஢ - ࠡ 8- ࠭栬 16. - -: ଠ஢ ⠡ ࠬ; প ஢ -CP866 / CP1251 / KOI8-R / UTF-8 ⮮। ४祭 `F8` -(ன ந , ࢮ ४祭). - -4. **᪮७ ७ਭ.** ப ࠭. ⨬ 横 뢮 - ᨬ १ BIOS WRCHAR ( 뢮, DMA). - ---- - -*MDView v0.2 (c) 2026 ஢ ..* - - ---- - -# Sprinter C Compiler ? v1.0 - -C toolchain for **Sprinter** ? the Z80-based home computer by Peters Plus, running -ESTEX DSS. Host: macOS / Linux. Target: `.EXE` files in SprintEXE format. - -Built on top of **SDCC 4.5** (vendored in `third_party/sdcc/`). This repository adds -everything Sprinter-specific: crt0, linker integration, libc wrappers over ESTEX, -banked-call trampolines, graphics & accelerator API, mouse driver wrappers, and the -`mkexe` utility for producing SprintEXE images. - -## What you get - -* **`bin/sprinter-cc`** ? one-line driver: `sprinter-cc -o foo.exe foo.c` -* **Memory modes**: `tiny`, `small`, `big`, `huge`, `manual` ? see below. -* **stdio + conio**: printf, puts, putchar, getchar, fopen/fread/..., cprintf, cputs, putch, textcolor/textbackground/textattr, gotoxy, kbhit/getch. -* **Graphics**: 320?256?256 and 640?256?16 modes, accelerator-backed primitives (hline / vline / rect / fill_rect / line via Bresenham, plus clear), bitmap-font text in both modes via BIOS character generator. -* **File I/O**: POSIX (`open`/`read`/`write`/`close`/`lseek`/`unlink`/`creat`), FILE\* streams (`fopen`/`fgets`/`fwrite`/...), directory listing (`ffirst`/`fnext`), `chdir`/`getcwd`/`mkdir`/`rmdir`, `stat`/`fstat`. -* **Memory**: 32 KB heap (W2-resident), banking-aware page allocator (`mem_alloc_pages`/`bank_read`/`bank_write`), explicit memory modes for sub-16 KB programs. -* **Mouse**: full Sprinter driver wrapper (14 functions including custom cursor bitmaps). -* **Environment**: `getenv`/`putenv`/`sysenv` over ESTEX `$46`. -* **Time**: `getdatetime`/`setdatetime` + POSIX `time`/`localtime`/`mktime`/`asctime`/`ctime`. -* **Misc**: `errno`/`strerror`/`perror`, `atexit`, `setjmp`/`longjmp`, `sleep`, full argv parsing in crt0. - -## Quick start - -```sh -git clone sprinter-c -cd sprinter-c -make sdcc # one-time: fetch SDCC 4.5 binary (~25 MB) -make all # build mkexe + libsprinter.lib + 27 examples -make floppy # pack everything into mame/v306/IMG/mc.img -cd mame/v306 && ./run_mame.sh # boot Sprinter in MAME -``` - -Compile a single program: - -```sh -cat > hello.c < -int main(void) { puts("Hello, Sprinter!"); return 0; } -EOF - -bin/sprinter-cc -o hello.exe hello.c -``` - -That's it ? `hello.exe` is now a valid SprintEXE you can `RUN HELLO` from the ESTEX shell. - -## Memory modes - -Sprinter's address space is four 16 KB windows (W0 / W1 / W2 / W3). DSS allocates -pages by program size ? small programs get only one page. Pick a memory mode based -on what your program needs: - -| Mode | Code lives in | Banking | Use when | Note | -|---|---|---|---|---| -| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | | -| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | | -| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | | -| `huge` | W1-W2 + W3 banking | yes (W3) | small + extra code modules | | -| `manual` | user-specified | optional | special layouts | Not implemented | - -```sh -sprinter-cc --memory small -o big.exe bigprog.c -sprinter-cc --memory huge -o app.exe main.c --bank 1=engine.c --bank 2=ai.c -``` - -Banked functions are declared with `__banked`: - -```c -void engine_tick(int dt) __banked; // lives in BANK1, automatically swapped -``` - -## Examples (27 total) - -| Example | What it demonstrates | -|---|---| -| `hello` | Hello world with stdio + conio Turbo-C-style colors | -| `argv` | argv parsing in crt0 | -| `cat` | File I/O ? read & print TEST.TXT | -| `seek` | 32-bit lseek over a 100 KB file | -| `ls` | Directory listing via ffirst/fnext | -| `filetest` | FILE\* streams (fopen/fread/...) | -| `stattest` | `stat`/`fstat` on files and directories | -| `errno` | errno / strerror / perror | -| `mem_test` | Page allocator + bank\_read/bank\_write | -| `malloc` | Heap stress test (200+ allocations) | -| `banked` | Banked code in W3 (huge mode) | -| `bankedbg` | Banked code in W1 (big mode) | -| `banklocl` | Bank-local static data and BSS | -| `mouse` | Mouse driver in text mode | -| `gfx_mous` | Mouse with custom bitmap cursor in graphics mode | -| `gfx_demo` | 320?256?256 lines / rects / accelerator | -| `gfx_d16` | 640?256?16 same primitives | -| `gfx_text` | Bitmap-font text on graphics screen | -| `timedir` | Date/time + directory listing | -| `ptime` | POSIX time API | -| `openenv` | open() flags + environment vars | -| `conio` | conio API smoke test | -| `attrprob` | Probe Sprinter text attribute byte layout | -| `strtest` | string.h test (from SDCC's z80.lib) | -| `stdlib` | stdlib.h test (qsort / rand / strtol / etc.) | -| `assrtest` | assert() | -| `rt_test` | Runtime helpers (sleep, setjmp, atexit) | - -## Headers - -Standard: -* `` ? puts / printf / FILE\* + Sprinter-specific dec/hex helpers -* `` ? atoi / atof / malloc / qsort / ... (from SDCC z80.lib) -* `` / `` / `` ? from SDCC z80.lib -* `` ? read / write / close / lseek / unlink -* `` ? open / creat + O\_RDONLY / O\_CREAT / ... -* `` ? errno + error names + strerror -* `` ? stat / fstat -* `` / `` ? from SDCC - -Sprinter-specific: -* `` ? putch / cputs / cprintf, textcolor / textbackground / textattr, kbhit / getch, clrscr, gotoxy, wherex/y -* `` ? gfx_init/done, palette, putpixel, hline/vline/rect/fill_rect/line, text ? for both 320 and 640 modes (gfx_\*16 variants) -* `` ? full 14-function driver wrapper + mouse_cursor_t with bitmap support -* `` ? chdir / getcwd / mkdir / rmdir / ffirst / fnext / ffblk -* `` ? getdatetime / setdatetime + POSIX time / localtime / etc. -* `` ? raw ports, ESTEX/BIOS function numbers, env API -* `` ? exit / \_exit / atexit -* `` ? mem\_alloc\_pages / mem\_free\_block / bank\_read / bank\_write -* `` ? Solid-C compatibility layer (aliases + BOOL/WORD/uint types) - -## Toolchain commands - -```sh -make all # build mkexe + lib + every example -make floppy # repack mame/v306/IMG/mc.img with all .exe files -make check # 17 mkexe unit-tests -make clean # remove all build artefacts -make sdcc # one-time: fetch SDCC 4.5 binary -``` - -### sprinter-cc options - -``` -sprinter-cc -o foo.exe foo.c [more.c ...] [options] - - --memory MODE tiny | small | big | huge | manual (default: tiny) - --memory-manual SPEC explicit placement (CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3) - --stack-size N bytes reserved for the stack (default ~1278) - --crt0=TYPE default | minimal | banked | small - --bank N=FILE.c compile FILE.c into bank N (repeatable, max 15) - --debug enable runtime diagnostics (defines DEBUG_RT) - -I PATH extra include path - -L 0xADDR / -E / -S override load / entry / stack addresses - -Wl FLAG pass FLAG to sdldz80 - --mkexe FLAG pass FLAG to mkexe (e.g. --mkexe -p --mkexe 0 for bank padding) - -v verbose -``` - -## Status - -What works in v1.0: -* Compile / link / pack to SprintEXE ? verified on all 27 examples -* Four memory modes (tiny / small / big / huge) -* Graphics (both modes) with accelerator -* Mouse (text + graphics cursor) -* File I/O, directories, environment, time -* All headers listed above - -Deferred to v2.0 (see `docs/TODO.md`): -* **Turbo-C-style BGI graphics API** ? `initgraph` / `setcolor` / `circle` / - `getimage` / `putimage` / etc. on top of our `gfx_*` primitives -* Remaining Solid-C compatibility gaps (Phase 2/3) ? see `docs/solid_c_compatibility.md` -* Manual memory mode -* Rewrite FILE\* stream API (current implementation is very primitive and doesn't use buffers) - -Deferred to v3.0: -* **IM2 interrupt handlers** ? research complete (`docs/im2_isr_design.md`), - implementation scheduled for v3 -* **Audio API** (AY-3-8910 + COVOX) ? requires IM2 -* **ISA-8 slot drivers** ? requires IM2 (???) - -## Documentation - -* `docs/TODO.md` ? roadmap and open work items -* `docs/solid_c_compatibility.md` ? gap analysis vs Solid-C 2004 -* `docs/im2_isr_design.md` ? interrupt handler design (v2) -* `docs/converted/` ? source documentation (ESTEX, BIOS, architecture) - converted to plain text for `grep` -* `docs/reference/`, `docs/samples/`, `docs/memory management/` ? original - Russian docs and code samples from Peters Plus - -## Repository layout - -``` -bin/sprinter-cc one-line compiler driver (bash) -toolchain/mkexe/ host-side tool: .ihx -> .exe SprintEXE -toolchain/check_banks.py post-link bank size enforcer -runtime/ crt0 variants (default, minimal, small, banked) - bank trampolines, heap, heap_top -libc/include/ headers -libc/io|stdio|mem|gfx/ C and asm sources for libsprinter.lib -lib/ Makefile that archives libsprinter.lib via sdar -examples/ 27 example programs -mame/v306/ MAME binary + Sprinter ROM/HDD images + floppy script -third_party/sdcc/ vendored SDCC 4.5 (fetched via `make sdcc`) -third_party/solid-c/ reference: original Sprinter native C (for compat target) -docs/ documentation -``` - -## License - -This repository contains: -* Original code in `bin/`, `toolchain/`, `runtime/`, `libc/`, `lib/`, `examples/` ? - MIT-licensed. -* `third_party/sdcc/` ? SDCC 4.5 under GPLv2 with linking exception - (see `third_party/sdcc/COPYING.txt`) -* `third_party/solid-c/` ? original Sprinter Solid C, used only as a reference - -## Credits - -* **Sprinter / Peters Plus** ? , ਩ ਭ and the original team -* **SDCC** ? for the underlying Z80 compiler -* **MAME** ? for the Sprinter Sp2000 emulation - ---- - -For questions / patches: see CONTRIBUTING.md (TBD) or open an issue. - ---- - -# TODO / Roadmap - - 浪 뢠 ਮ.  ॠ ணࠬ ? ਮ ᬥ. - -## ⠯ 5 ? malloc / free + banking-aware page allocator ? - -- [x] SDCC's `malloc`/`free` + `runtime/heap.s` ( library heap.rel, 14000- heap 2) -- [x] `libc/mem/mem_alloc.c` ? page allocator: `mem_alloc_pages`/`mem_free_block`/`mem_get_page`/`mem_info` १ ESTEX `$3C/$3D/$3E` + BIOS `$C4` -- [x] `libc/mem/bank_io.c` ? HOME-१ `bank_read`/`bank_write`/`bank_load_byte`/`bank_store_byte` ᢮ W3 -- [x] `examples/malloc_test/` ? ஢ઠ SDCC's malloc (~210 64- allocations १ heap) -- [x] `examples/mem_test/` ? ஢ઠ page allocator: 3 ࠭, ࠧ १ bank_write, 䨪 १ bank_read - -## ⠯ 6 ? argv parsing + sprinter-cc wrapper ? - -- [x] crt0 ESTEX command-line IX-prefix (inline asm `runtime/crt0.s`) -- [x] Strip leading CP/M-style space (DSS quirk) -- [x] । `argc`/`argv` main() १ HL/DE (SDCC __sdcccall(1) ABI) -- [x] argv[0] = basename .EXE १ ESTEX APPINFO ($47 subfn 2) -- [x] `runtime/crt0_minimal.s` ? opt-out 祭 쪨 ணࠬ -- [x] `runtime/crt0_banked.s` ? ⥯ ⮦ argv (parse_argv + get_progname ᪮஢ crt0.s; 㤥 factored argv.s libsprinter.lib) -- [x] Bash-⪠ `bin/sprinter-cc`: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権: `--memory`, `--memory-manual`, `--stack-size`, `--crt0=`, `--bank N=FILE.c`, `--debug`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` - -## ⠯ 8 ? 䨪 (320?256?256 + 640?256?16 + accel + bitmap font) ? - -- [x] **8a** Graphics core: `gfx_init`/`gfx_done`/`gfx_clear`/`gfx_putpixel`/`gfx_pal_load`/`gfx_pal_set` (libc/gfx/gfx_core.c). १ BIOS PIC_SET_PAL ($A4). Verified 320?256?256. -- [x] **8b** /אַ㣮쭨/fill १ accelerator (libc/gfx/gfx_lines.c): `gfx_hline`/`gfx_vline` १ accel Fill (LD C,C / LD E,E + SMC block-size), `gfx_rect`/`gfx_fill_rect` heuristic 롮஬ ਥ樨 (h/v bursts count), `gfx_line` Bresenham . `gfx_clear` ⮦ ९ᠭ column-major accel (~4? ॥). -- [x] **8c** 640?256?16 mode (libc/gfx/gfx_16.c): `gfx_*16` API, HIGH nibble = LEFT pixel (㬥 misleading), per-row RMW vline ( = 2 ਧ⠫ ᥫ). -- [x] **8d** Bitmap font + gfx_text (libc/gfx/gfx_text.c): १ BIOS WIN_GET_ZG ($B8), interleaved layout `font[row*256+char]`, `gfx_text`/`gfx_putchar` 320 mode, `gfx_text16`/`gfx_putchar16` 640 mode pair-table lookup. - -. memory/sprinter_graphics.md, sprinter_accelerator.md, sprinter_graphics_16.md, sprinter_font_format.md. - - ( ): -- [ ] -quad 640: per-cell (mode 0x82 ࠧ蠥 1 4 per 16?8 cell) ? १ אַ area-ᠭ ࠭ 0x0300..0x039F - -## Auto-banking (. `memory/banking_roadmap.md` ⠫) - -Phase 1 ? file-level bin-packing ? ॠ뢠 ஥ ~30 KB . - -- [ ] `toolchain/auto_bank.py`: - - ࠧ `.rel`-䠩 ( .map ᫥ dry-run link') - - First-fit-decreasing bin-packing - - `#pragma codeseg BANKn` manual override - - ५뢠 묨 `-Wl-b_BANKn=...` ࠬࠬ - - ⠥ । - -Phase 2-5: incremental rebalance, declarative `banks.toml`, function-level, call-graph-aware. 쪮 ᫨/ . - -## Bank-local static data (mutable data ⮬ ) ? ? - -- [x] ਬ `examples/bank_local_data/` ? 㭪 BANK1 ᢮ writable BSS array + const table + malloc- -- [x] `mkexe -p 0` 㫥 padding (BSS-storage 㧪) -- [x] ᪨ 楯: `--codeseg BANK1 --constseg BANK1 --dataseg BANK1` bank1.c + `-Wl-b_BANK1=0x1C000` . **`--dataseg BANK1` ** ? ࠭ ⭮ - trampoline bug ᪨஢ १. -- [x] ** 䨪 trampoline'a runtime/bank.s** ? `pop af; out (n), a` ਫ A ? banked-㭪樨 騥 uint8_t 頫 . `pop bc; out (c), b` ࠭ A. -- [x] **malloc banked-㭪樨 ࠡ⠥ ஧筮** ? heap W2 (HOME), W2 ᢠ trampoline', pointer  ⥪. . memory/bank_local_data_pattern.md. -- [x] 㬥 memory: `memory/bank_local_data_pattern.md` ( 楯 + malloc + nuances), `memory/sdcc_banking.md` (trampoline fix) -- [ ] 樮쭮 ? `check_banks.py` ⮡ 뢠 ࠧ size = code + const + bss per bank (cosmetic) - -祬: 㫥 訬 private state (level loader, audio engine, scene data). W2 heap , ⨪ . - -## ᪠ solid-c (⨢ Sprinter C ? `third_party/solid-c/`) - -᫥ solid-c'᪮ libc (. `memory/solid_c_findings.md`)  ⮢ ᫥ 㭪権. ਮ **᮪** : - -### High-priority gaps ( ஢, 짠) -- [x] **`errno` + `strerror`/`perror`** ? ⠡窠 32 訡 (libc/io/errno.c) -- [x] **७ `open()`** O_CREAT/O_TRUNC/O_APPEND/O_EXCL state machine -- [x] **`atexit`** ? 8-callback LIFO + `exit()` + `_exit()` (libc/io/atexit.c) -- [x] **`setjmp`/`longjmp`** ? 6- jmp_buf={sp,ix,pc} (libc/io/setjmp.c) -- [x] **`sleep(seconds)`** ? 50Hz halt-loop (libc/io/sleep.c) -- [x] **ESTEX ENV API** ($46, getenv/putenv) ? libc/io/env.c. 竨 doc-bug: ॠ쭮 A=0 NOT FOUND - -### Medium-priority (㦭 shell-like ⨫) -- [ ] **Mouse driver** ? `rst $30h`, 17 㭪権. **砫 ࠡ⠥ MAME**. -- [x] **`ffirst`/`fnext` + ffblk_t struct** directory listing ? ॠ, demo: ls.exe -- [x] **`getdatetime`/`setdatetime`** १ ESTEX $21/$22 ? libc/io/time.c, demo: time_dir_test -- [x] **`chdir`/`getcwd`/`mkdir`/`rmdir`** ? wrappers ESTEX $1B-$1E ? libc/io/fsdir.c -- [x] **conio: `kbhit`/`getch`/`getche`/`cputs`/`clrscr`/`gotoxy`** ? ॠ -- [x] **conio extras**: `wherex`/`wherey` ($53), `wrchar`/`rdchar` ($58/$57), `textmode_get/set` ($50/$51), `clrscr_attr` ($56) + COLOR macros - -### Low-priority ? ? FILE* stack - -- [x] ** unbuffered FILE\*** ? `libc/stdio/file.c` + `libc/include/stdio.h`. fopen/fclose/fputs/fgets/fread/fwrite/fseek/ftell/rewind/feof/ferror/clearerr/fflush + stdin/stdout/stderr pseudo-streams. . `memory/file_star_design.md` `examples/filetest`. -- [ ] fprintf / fscanf ? 㦭 printf-१-callback machinery. 짮⥫ `sprintf(buf, ...) + fputs(buf, fp)`. -- [ ] 樮 buffered mode (setvbuf, line/block buffering) ? ᫨ - . - -### POSIX time API ? ? -- [x] `libc/io/posix_time.c` ? time/localtime/gmtime/mktime/asctime/ctime getdatetime. SDCC's time.rel ( override _RtcRead). . `examples/ptime`. - -### sys/stat ? ? -- [x] `libc/io/stat.c` ? POSIX stat/fstat. ਤ open+fstat 䠩, ffirst+iter ( "."/".."). . `examples/stattest` `memory/estex_ffirst_dotdot.md`. - -### assert ? ? (ᯮ㥬 SDCC's __assert १ fallback include path) - -## libc/stdlib ? ? 㦭 (. memory/sdcc_stdlib_works.md) - -஢७ १ `examples/stdlib_test/`: SDCC z80.lib ᮤন ࠡ騥 ॠ樨: -- `atoi/atol/atof, strtol/strtoul, rand/srand, qsort/bsearch, abs/labs, div/ldiv` -- `` (memchr/cmp/set/cpy, strcat/cmp/cpy/len/chr/spn/etc.) -- `` (toupper/tolower) -- `` (sinf/cosf/sqrtf/etc.) - - ⮬᪨ ﭥ z80.lib 㦭. ** ९뢠**. - - Sprinter-specific 易⥫ 㫨 : atexit, env, errno, setjmp, putchar/puts/getchar, conio, fsdir, time, mouse, open/read/lseek/close. - -## Build-system: libsprinter.lib + sprinter-cc ? ? - -- [x] `lib/Makefile` ? ᮡࠥ libc/*.c `.rel`, 娢 १ sdar `lib/sprinter.lib` -- [x] 砥 runtime/bank.s runtime/heap.s (auto-pulled __banked/malloc) -- [x] `bin/sprinter-cc` ? bash-wrapper: `sprinter-cc -o foo.exe foo.c` ப -- [x] প 権 `--crt0=default|minimal|banked`, `--bank N=FILE.c`, `-I`, `-L`/`-E`/`-S`, `-Wl`, `--mkexe` -- [x] `examples/hello_sccc/` ? : `hello.c` ᮡࠥ shell-맮, ࠧ ᮢ Makefile (925 ) -- [x] Split `putchar.c` ? `putchar.c` + `puts.c` per-function granularity (puts override SDCC's z80.lib version) -- [x] `make all` (ᨬ `lib` । `examples`) - - 襭 (樮쭮): -- [ ] ஢ ⠫ examples sprinter-cc Makefile (ᬥ⨪) -- [ ] 쭥 libc/*.c per-function ( ⥪ granularity 㦥 㦭 ࠧ ? .rel 楫, 設⢠ 䠩 㭪) - -## ⠯ 9 ? memory modes sprinter-cc - -DSS 뤥 ࠭ ࠧ ਫ: < 16 KB ? ࠭, ⠫ 砥 ?࠭ #FF? (read=0xFF, write ). - ⮣ CODE--W1 + DATA--W2 쪮 ணࠬ . . [memory/sprinter_memory_modes.md](../../.claude/projects/-Volumes-SAM8-Projects-DIY-Z80-Sprinter-C-Compiler/memory/sprinter_memory_modes.md). - -- [x] **`tiny`**: (CODE+DATA+⥪) W2. Default. Verified hello/argv/conio/malloc/file/etc. -- [x] **`--memory MODE` 䫠 sprinter-cc**: parser + per-mode 䮫 CODE_LOC/DATA_LOC, override १  `--code-loc`/`--data-loc`. tiny ࠡ⠥; small/big/huge warning' (runtime ⮢). 2026-05-30. -- [x] **`--memory-manual SPEC`**: `CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3`. 2026-05-30. -- [x] **`small` runtime**: `runtime/crt0_small.s` ᯮ ESTEX `$3D GETMEM` + `$3A SETWIN2` ⮡ 뤥 W2-࠭ gsinit. **** BIOS `$C4` ? ⥪ ⮬ ⠯ W1 (boot_stack HOME), BIOS ॡ ⥪ W2. ᫥ SP ४砥 0xBFFE, ⠭ flow. 2026-05-30, verified hello.exe. -- [x] **`small` auto-detect >16 KB ணࠬ**: `crt0_small.s` ⠥ `0xC2` (⥪ ࠭ W2 ? `0xA2`! W1). ᫨ 0xFF ? 뤥 page; DSS 㦥 ᤥ (ணࠬ ᠬ 뫥 W2). crt0 뢠 0..30 KB. mkexe ⠪ ࠧ蠥 HOME span W1+W2 (0x4000..0xBFFF). Verified hello: small (5 KB 䠩, SETWIN2 path) + 32 KB 䠩 (auto-skip). 2026-05-30. -- [x] **`big` runtime** (tiny + banked code W1): ࠬਧ `crt0_banked.s` + `bank.s` १ `.ifdef BANK_W1` ? 㣮 banking port (0xA2 vs 0xE2), 㣮 load-addr (0x4000 vs 0xC000). sprinter-cc prepend' `BANK_W1 = 1` `--memory big`, । `mkexe -B 0x4000`. ਬ `examples/banked_big/`. 2026-05-30. -- [x] **`huge` runtime** (small + banked code W3): merge W2-detect `crt0_small.s` `crt0_banked.s`. 騩 ਬ `examples/banked/` ⥯ ᯮ MEMORY=huge. 2026-05-30. -- [x] **`--debug` 䫠**: prepend `DEBUG_RT = 1` crt0 + `-DDEBUG_RT` sdcc. 뢠 symbol `_w2_self_allocated` (uint8_t) ? runtime diagnostic ஢ W2. 2026-05-30. - -### -襭 libc crt0 - -** `sprinter.lib`** ࠡ⠥ memory mode ? `.rel`-童 relocatable, SDLD dead-code elimination per-member ( 䨪  `gfx_core.rel` ..). Verified hello vs malloc_test १ map-䠩. - -**`gfx.lib` ⤥쭮 ? 㦥**: dead-code elimination 㦥 ࠡ⠥. - -**`libc_banked` (libc bank HOME)** ? ⮬, HOME (16 KB) user- + libc `huge` mode. १ `--codeseg BANK0` 樨 libc; trade-off: trampoline ~30 横 libc-맮. ਣ: ॠ쭠 ணࠬ HOME budget. - -**HW-ᨬ 㫨 ? `sprinter_home.lib` ⤥쭮.** libc 䨧᪨ W3, ⮬ W3: -- `gfx_*` ? `0xC000+` ᫥ swap W3 video page -- `bank_io` (mem_alloc_pages/bank_read/bank_write) ? swap' W3 १ `OUT (0xE2)` -- 騥 ISR ? 뢠 ਩ W3 㣮 - - huge mode 㫨 HOME (W1). 㤥 `libc_banked`, **६** 뤥塞 `sprinter_home.lib` (HOME-only) `sprinter.lib` (bankable). 쭠 奬: -``` -sprinter_home.lib HOME-only: gfx, bank_io, ISR shims -sprinter.lib bankable: printf, malloc, string, conio, stdio, env, ... -sprinter_banked.lib sprinter.lib --codeseg BANK0 ( huge) -``` -ਣ: ॠ `--memory huge` runtime. - -**crt0 ? mode:** -- `crt0.s` ? ⥪騩, **tiny/big**: SP=0xBFFE, argv (W2- 㦥 뤥 DSS). -- `crt0_minimal.s` ? ⥪騩, tiny argv. -- `crt0_small.s` ? **, step 3**: **small/huge**, W2 १ `mem_alloc_pages` gsinit, `0xA2`, ⮬ ⠭ flow. -- `crt0_banked.s` ? ⥪騩, **big**: trampoline-⠡ W3 , CODE W2. -- `crt0_banked_small.s` ? ****: huge = small (W2-alloc) + banked (W3 trampolines). - -sprinter-cc ࠥ crt0 `--memory` mode (ᥩ `--crt0=` override). -- [x] **ࠨ ࠧ ⥪**: 䫠 `sprinter-cc --stack-size BYTES`. Wrapper `heap_top.s` `___sdcc_heap_end = stack_top + 1 - stack_size`, ⤥ .rel per-program. Default ?1278 (heap_top=0xBB00) `runtime/heap_top.s`. 2026-05-30. - -䥩: `sprinter-cc --memory [tiny|small|big|huge|manual] [--memory-manual SPEC] [--stack-size N] foo.c`. `--memory-manual` ⮫쪮 `--memory manual`. - -## Known issues / quirks - -- **ESTEX $46 ENV API**: ? ࠡ⠥. - `DiskSyscalls.txt v1.6` 訡筮 뢠 return-status ? A=0 NOT FOUND, FOUND. 䨪஢ `memory/sprinter_platform.md`. - -## V2 (᫥ ५ v1) - -### Turbo-C-style graphics API (BGI-like) ? **MUST v2** - - `gfx_*` API ஢ **Turbo-C ``** (BGI) MS-DOS. -ணࠬ ਢ訥 Turbo-C ७ ᪨ 1--1. - -** ** ( ᭮ Borland BGI): - -Setup/teardown: -- `initgraph()` / `closegraph()` ? ᥩ `gfx_init`/`gfx_done`, alias -- `getmaxx()` / `getmaxy()` ? GFX_WIDTH-1 / GFX_HEIGHT-1 -- `cleardevice()` ? alias to gfx_clear -- `getgraphmode()` / `setgraphmode()` ? get_videomode/set_videomode - -Color/palette: -- `setcolor(c)`, `getcolor()` ? current draw color -- `setbkcolor(c)`, `getbkcolor()` ? background color -- `setpalette(idx, c)` ? entry -- `getpalette(&info)` ? read all palette - -Primitives ( 㦥 ? BGI- aliases): -- `putpixel(x, y, c)` ? gfx_putpixel -- `getpixel(x, y)` ? 㦭 ॠ (RMW ⭮ ? IN) -- `moveto(x, y)`, `lineto(x, y)`, `linerel(dx, dy)` ? current point + line drawing -- `line(x1, y1, x2, y2)` ? gfx_line -- `rectangle(x1, y1, x2, y2)` ? gfx_rect ( 㣮 API: x1,y1,x2,y2 vs x,y,w,h!) -- `bar(x1, y1, x2, y2)` ? gfx_fill_rect -- `bar3d(x1, y1, x2, y2, depth, topflag)` ? : rect + 3d edges -- `circle(x, y, r)`, `arc(...)`, `ellipse(...)`, `pieslice(...)` ? primitives -- `fillpoly()`, `drawpoly()` ? -- `floodfill(x, y, border_color)` ? - -Text on graphics screen: -- `outtext(s)` / `outtextxy(x, y, s)` ? gfx_text (alias) -- `settextstyle(font, dir, size)` ? multiple bitmap fonts -- `gettextsettings(&info)` -- `textwidth(s)` / `textheight(s)` ? measure - -Image manipulation: -- `imagesize(x1, y1, x2, y2)` ? bytes needed for getimage -- `getimage(x1, y1, x2, y2, buf)` ? save rect to buffer -- `putimage(x, y, buf, op)` ? paste back with COPY_PUT/XOR_PUT/AND_PUT/OR_PUT/NOT_PUT - -Clipping/viewport: -- `setviewport(x1, y1, x2, y2, clip)` ? drawing clip rect -- `getviewsettings(&info)` -- `clearviewport()` -- `setactivepage(p)` / `setvisualpage(p)` ? ਧ (Sprinter 2 screen) - -Line style: -- `setlinestyle(style, pattern, thickness)` ? SOLID_LINE / DOTTED_LINE / etc. -- `getlinesettings(&info)` - -**Acceptance:** ७ ⨯筮 Turbo-C BGI ணࠬ (饩 ᯮ짮 -moveto/lineto/circle/bar/setcolor) ࠡ ⢥ ࠢ. - -**Notes:** -- BGI fonts (TRIPLEX/SANS_SERIF/GOTHIC) ? BIOS font, ⠫ 㦭 - ( bitmap data lib) -- imagesize/getimage/putimage ? ᠬ ॡ game/animation -- Active/visual page ( ਧ) ? Sprinter ন 2 graphics pages, - 㦥 API switching - -. ⠪ `examples/` Turbo C 2.x BGIDEMO reference 㦭. - -### IM2 Interrupt Handlers ? **MUST v2** - -User- ISR १ Z80 IM 2 mode. 㦭 : -- Timer ticks (50 Hz frame counter, ) -- Music playback (AY, COVOX) -- Real-time games (input + game logic + render interrupt-driven) -- Async keyboard / mouse handling - -**Status:** v2. research + design `docs/im2_isr_design.md`. - -**襭 ⥪:** ॠ ⤥ memory mode `--memory im2` -( ⮣ ⮡ 騥 crt0). Detail' design-doc. - -** research'** ( ⥪ `docs/im2_isr_design.md`): -- Vector 0xFF ? frame + keyboard + CBL. Disambiguation ⠬ 0x19 / 0xFE -- Mouse hardware-IRQ 室 ( ⥪饩 ) -- Vector table / ISR / stack W2 (0x8000..0xBFFF) -- DSS ᢮ IM 2 handler ? 㦭 chain' ( / SYSTIME ) - -### 稥 㯭 㭪 v2 - -- [ ] **FILE API rewrite ? buffered streams** ? ⥪ ॠ - `libc/stdio/file.c` provisional unbuffered shim ( fputc/fgetc - = read/write syscall). 㦭 業 buffered ᥬ⨪ - Solid-C: - - ```c - typedef struct { - uint flags; // +0..1 file status flags - int level; // +2..3 empty/fill level of buffer - char *curp; // +4..5 current active pointer - int fd; // +6..7 underlying low-level fd - char *buffer; // +8..9 data transfer buffer - char hold; // +10 ungetc byte if no buffer - short token; // +11..12 reserved - char dummy; // +13 reserved - } FILE; - ``` - - stdin/stdout/stderr ? fd-થ `0 / -1 / -2`. ⥫ - stdout/stderr ࠭ ७: ESTEX OPEN positive - small fd (1, 2, ?) 筮 䠩 ? ᫨ stdout=1, ॠ - fd=1 ⠫ 䨪஬. fd=0 stdin ᭮ - (ESTEX 0 頥). fd । syscall' ? - ᯥਧ 䫠 `_F_CONIN/_F_CONOUT`. - - ਭ-⮪ (stdaux/stdprn) ॠ㥬 ? Sprinter ਭ୮ - API . - - ୠ⨢ ? ॠ third_party/solid-c (sources - `SRC/CLIB/`); ⠬ ⮢ buffered FILE + fopen/fread/fwrite/ - fseek/setvbuf .. ஢ 訬 open/read/write/lseek. - - rewrite deferred issues stdio-review: - - `fwrite` short-write ⠢ `_F_ERROR` - - `fgets(buf, 1, fp)` ? ⠭ "empty string", 㫨 NULL - - `mode_to_flags` ? break-out '+' (cosmetic) - -- [ ] **Audio API** ? AY-3-8910 + COVOX १ 뢠 (ॡ IM2) -- [ ] **ISA-8 slot support** ? ZX-Bus (sound, network, etc.) ? ॡ IM2 + ⥭ port - -## 稥 (v1 backlog, 騥) - -- [x] **#9: text I/O split (Turbo-C style)** ? stdio (puts/printf/putchar) ⥯ fast no-attr १ PCHARS/PUTCHAR. conio (cputs/cprintf/putch) ਬ attr १ textcolor/textbackground/textattr. KEEP_EXIST_ATTR ? conio fallback fast path. Verified hello.exe. . `memory/text_output_api_split.md`. 2026-05-31. -- [x] **Mouse API ** (१ driver, RST 30h) ? 14 㭪権 (init/show/hide/refresh/read/goto/bounds/text_cursor/load_cursor/get_cursor/get/set_sensitivity/video_mode_changed). . `memory/mouse_api.md`. Verified MAME 2026-05-31. Sensitivity = divider ( = ॥). -- [ ] Interrupt handlers ? IM 2 vector table HOME user ISR' -- [ ] প `restore SP on EXIT` ( z88dk +pps) ? ஢ 㦭 -- [ ] CI: ⮬᪨ ᪠ MAME `-aviwrite` screenshot-ࠢ, ⮡ ਬ஢ 室 祫 - -## ⮬ - -- প `` ( SDCC stdlib ? 㦭 ஢ crt0 ᮢ⨬) -- `` १ ESTEX SYSTIME (`$21`) CMOS BIOS-㭪樨 -- ZX Spectrum-ᮢ⨬ ० ⤥ target ( ஢ ᯥ㬮᪨ ணࠬ) -- প ZX-Bus (sound, network, etc.) ? 㦭 ࠩ -- Profile-guided optimization tools (hot/cold detection) 㯭 ணࠬ - -## Linker duplicate-symbol warnings (, 䨫஢) - - ᮧ⥫쭮 overrides' SDCC z80.lib 㭪樨 ᮡ⢥ ᨥ `sprinter.lib`, `sdldz80` `?ASlink-Warning-Definition of public symbol '...' found more than once`. ࢮ । (), ⮬ ४⭮ ? warning ⮫쪮 noise. - -騥 overrides: -- `_puts` ? १ PCHARS+\r\n vs SDCC posix puts -- `___sdcc_heap` ? heap W2 vs SDCC's ⠭ -- `_asctime`, `_localtime` ( 㣨 time) ? `posix_time.c` १ ESTEX SYSTIME vs SDCC's `time.rel` `_RtcRead` - -**饥 襭:** `bin/sprinter-cc` 䨫஢뢠 warning- (warning + 2 follow-up `Library:` ப) 뢮 `sdcc`. १ `-v` (verbose) 뢠. १ awk-pipe. - -** 襭:** -- ३ explicit `--nostdlib` + 筮 ᯨ᮪ 㦭 㫥 z80.lib (string, math, stdlib override') ? warning', ᪨஢ -- : २ `_puts` ? `_puts_sprinter` + alias १ linker flag ( 㢥७ SDCC ন) -- : ⠢ ᥩ (ࠡ祥 benign) ? ਮ - -## TODO: ஢ ॠ쭮 - -- [ ] **Port_Y banking trick** (`docs/part2/SprinterGraphics programming.txt`): - ⢥ত ᫥ `OUT (0x89), Y` 0xC000+0x400*N W3 - ப Y..Y+15 ( ணࠬ஢ ? 16 ப). - Empirical 2026-06-01 MAME 0.283 trick ** ࠡ⠥** ? ᥫ - ᠬ 0xC000+row_width 室 . ᪨ - `docs/samples/plasma2.asm` ⮦ ᯮ banking, ⠭ - Port_Y per row. - : - 1. ॠ쭮 Sprinter - 2. dual-write (`_gfx_putpixel_raw` + ன write `0xD000+x`) - 3. ᫨ ? MAME, issue - ९ - 4. ᫨ ⮦ ? 㬥 , 㤠 㯮 - ⠢ ⥪ ॠ (Port_Y per pixel) - 5. ᫨ banking ࠡ⠥ ? ஢ Port_Y - `_gfx_putpixel_raw` (sentinel out-of-range, . memory/gfx_port_y_banking.md) - - 易 먣 Bresenham (60-pixel ) ? 8? - OUT (0x89) 権, `gfx_fill_rect 320x256` ? 16? . - release v1. - -## GFX: ७ `docs/part2/accelerator_doc.txt` - -᫥ ⥭ ⠫쭮 accelerator doc  ࠢ. - ᯮ ⮫쪮 ਧ⠫/⨪ Fill mode. - -### Quick wins ⥪ primitives - -- [ ] ** SMC `LD A, (var)` block-size**. 㬥  - ࠧ蠥 `LD A, (HL)`, `LD A, (BC)`, `LD A, (DE)` ( `LD A, r`). - 㡥 SMC complexity `gfx_lines.c:hfill_chunk/vfill_chunk` - `gfx_16.c:g16_hfill_chunk`. 饭 ⮫쪮 register-to-register. -- [ ] **஢ block-size**. 㬥 뢠 accel - block size bursts (. `Horizontal_Line_Fill`: ⠭ - size + `LD B,B` ⪫祭, ⮬ Fill mode ᯮ - ࠭ size). `gfx_fill_rect` 100 묨 - ப ? ⠭ size 1 ࠧ, 100. - -### Bank-prefix modes (port 0xE2 bits) - -㬥 뢠 ਠ ࠭ ⠭⭮ 0x50: - -| Bank byte | Effect | -|---|---| -| 0x50 | Normal write ? shadow + | -| 0x54 | "no copy in main shadow RAM" | -| 0x58 | **"FF is transparent"** ? 0xFF write ⠢ background | -| 0x5C | both | - -Bank 0x58 祬 mouse cursor 0xFF-஧筮. - **sprite-blending १ accel block copy**: - -- [ ] **`gfx_set_bank_transparent(on)`** 䫠 `gfx_set_bank` - 롮 0x50/0x58 ᮢ sprite' -- [ ] ᯮ짮 `gfx_blit()` ⮡ 䠪 - transparent sprites १ accel- - -### Block copy mode (sprite blit') - -`LD L,L` (horizontal) `LD A,A` (vertical) ? ० ஢ १ -256- accel memory. blit'. - -- [ ] **`gfx_blit(src_data, x, y, w, h)`** ? ஢ sprite' - (ந ࠧ, १ accel) -- [ ] **`gfx_blit_transparent(src, x, y, w, h)`** ? ᯮ짮 bank 0x58 - -. `Draw_Restangle_Data` accelerator_doc.txt ७. - -### AND / OR / XOR operations १ accel - -㬥 뢠 accel ন ᪨ 樨 -. ਬ: -- XOR ? (뤥 selection UI) -- OR / AND ? masking, alpha-style blending -- . ਬ accelerator_doc.txt: "256 bytes block coding via XOR" - -- [ ] **`gfx_xor_rect`** / **`gfx_or_rect`** / **`gfx_and_rect`** ? - ਬ⨢ ᪨ 権 אַ㣮쭨 -- [ ] **`gfx_invert_rect(x, y, w, h)`** ? alias xor 0xFF - -### Bitmap fonts ࠧ ࠧ஢ - - `gfx_text` / `gfx_putchar` प ࠡ 8?8 ⮬ -(BIOS WIN_GET_ZG 頥 256?8 ). UI / 쭨 -㦭: -- [ ] **`gfx_set_font_size(w, h)`** ? ४ ਭ/ glyph' -- [ ] **`gfx_set_font_data(ptr, w, h, advance)`** ? 㪠⥫ - 짮⥫᪨ + ࠧ -- [ ] প **proportional** (advance != w) ⮢ ? - array advance[256] ਭ glyph' -- [ ] **Big-font ०**: 8?16, 16?16, 16?8 ( 쭨) -- [ ] ⤥ API `gfx_text_ex(x, y, str, font_id)` - font_id 롨ࠥ 㦥 ⮢ -- [ ] **Anti-alias 2-bit ** ( 䮭 / ࠭ / 2- alpha?) - ? far future, smooth UI - -## ⠯ ⨬権 ( ᥩ) - -- **`gfx_line` १ accel ** ? Bresenham |dy| << |dx| ( ) 뤠 runs Y ( X): ᥫ, ᥫ, ᥫ, 蠣 Y, ᥫ, ᥫ... ⠪ run ? ⮢ 㬥 `gfx_hline` ( `vline`). - ᫥: runs 㭪 ; run length, ஬ 룮 accel hline 祬 N?putpixel (overhead accel ~20?s, putpixel ~5?s ? accel 룮 run ? 4-5 px); (dx ? dy) ⠢ Bresenham, ? run-length-based fill. - `gfx_line` orthogonal cases 㦥 १ accel ? ⨬஢ ⮫쪮 . - -- **`gfx_fill_rect` W3-swap ** ? ᥩ ७ `gfx_hline`/`gfx_vline` ᢮ DI/save-W3/restore-W3/EI. ᤥ internal `_fill_rect_inner` ন W3 DI 横; ~20?s ? ⢮ ப/⮫殢 . ਬ ᥬ ਬ⨢. diff --git a/examples/mdview2/testfiles/LINES.MD b/examples/mdview2/testfiles/LINES.MD deleted file mode 100644 index 80273ed..0000000 --- a/examples/mdview2/testfiles/LINES.MD +++ /dev/null @@ -1,25004 +0,0 @@ -# Short lines test - line limit trigger - -``` -row 00001 -row 00002 -row 00003 -row 00004 -row 00005 -row 00006 -row 00007 -row 00008 -row 00009 -row 00010 -row 00011 -row 00012 -row 00013 -row 00014 -row 00015 -row 00016 -row 00017 -row 00018 -row 00019 -row 00020 -row 00021 -row 00022 -row 00023 -row 00024 -row 00025 -row 00026 -row 00027 -row 00028 -row 00029 -row 00030 -row 00031 -row 00032 -row 00033 -row 00034 -row 00035 -row 00036 -row 00037 -row 00038 -row 00039 -row 00040 -row 00041 -row 00042 -row 00043 -row 00044 -row 00045 -row 00046 -row 00047 -row 00048 -row 00049 -row 00050 -row 00051 -row 00052 -row 00053 -row 00054 -row 00055 -row 00056 -row 00057 -row 00058 -row 00059 -row 00060 -row 00061 -row 00062 -row 00063 -row 00064 -row 00065 -row 00066 -row 00067 -row 00068 -row 00069 -row 00070 -row 00071 -row 00072 -row 00073 -row 00074 -row 00075 -row 00076 -row 00077 -row 00078 -row 00079 -row 00080 -row 00081 -row 00082 -row 00083 -row 00084 -row 00085 -row 00086 -row 00087 -row 00088 -row 00089 -row 00090 -row 00091 -row 00092 -row 00093 -row 00094 -row 00095 -row 00096 -row 00097 -row 00098 -row 00099 -row 00100 -row 00101 -row 00102 -row 00103 -row 00104 -row 00105 -row 00106 -row 00107 -row 00108 -row 00109 -row 00110 -row 00111 -row 00112 -row 00113 -row 00114 -row 00115 -row 00116 -row 00117 -row 00118 -row 00119 -row 00120 -row 00121 -row 00122 -row 00123 -row 00124 -row 00125 -row 00126 -row 00127 -row 00128 -row 00129 -row 00130 -row 00131 -row 00132 -row 00133 -row 00134 -row 00135 -row 00136 -row 00137 -row 00138 -row 00139 -row 00140 -row 00141 -row 00142 -row 00143 -row 00144 -row 00145 -row 00146 -row 00147 -row 00148 -row 00149 -row 00150 -row 00151 -row 00152 -row 00153 -row 00154 -row 00155 -row 00156 -row 00157 -row 00158 -row 00159 -row 00160 -row 00161 -row 00162 -row 00163 -row 00164 -row 00165 -row 00166 -row 00167 -row 00168 -row 00169 -row 00170 -row 00171 -row 00172 -row 00173 -row 00174 -row 00175 -row 00176 -row 00177 -row 00178 -row 00179 -row 00180 -row 00181 -row 00182 -row 00183 -row 00184 -row 00185 -row 00186 -row 00187 -row 00188 -row 00189 -row 00190 -row 00191 -row 00192 -row 00193 -row 00194 -row 00195 -row 00196 -row 00197 -row 00198 -row 00199 -row 00200 -row 00201 -row 00202 -row 00203 -row 00204 -row 00205 -row 00206 -row 00207 -row 00208 -row 00209 -row 00210 -row 00211 -row 00212 -row 00213 -row 00214 -row 00215 -row 00216 -row 00217 -row 00218 -row 00219 -row 00220 -row 00221 -row 00222 -row 00223 -row 00224 -row 00225 -row 00226 -row 00227 -row 00228 -row 00229 -row 00230 -row 00231 -row 00232 -row 00233 -row 00234 -row 00235 -row 00236 -row 00237 -row 00238 -row 00239 -row 00240 -row 00241 -row 00242 -row 00243 -row 00244 -row 00245 -row 00246 -row 00247 -row 00248 -row 00249 -row 00250 -row 00251 -row 00252 -row 00253 -row 00254 -row 00255 -row 00256 -row 00257 -row 00258 -row 00259 -row 00260 -row 00261 -row 00262 -row 00263 -row 00264 -row 00265 -row 00266 -row 00267 -row 00268 -row 00269 -row 00270 -row 00271 -row 00272 -row 00273 -row 00274 -row 00275 -row 00276 -row 00277 -row 00278 -row 00279 -row 00280 -row 00281 -row 00282 -row 00283 -row 00284 -row 00285 -row 00286 -row 00287 -row 00288 -row 00289 -row 00290 -row 00291 -row 00292 -row 00293 -row 00294 -row 00295 -row 00296 -row 00297 -row 00298 -row 00299 -row 00300 -row 00301 -row 00302 -row 00303 -row 00304 -row 00305 -row 00306 -row 00307 -row 00308 -row 00309 -row 00310 -row 00311 -row 00312 -row 00313 -row 00314 -row 00315 -row 00316 -row 00317 -row 00318 -row 00319 -row 00320 -row 00321 -row 00322 -row 00323 -row 00324 -row 00325 -row 00326 -row 00327 -row 00328 -row 00329 -row 00330 -row 00331 -row 00332 -row 00333 -row 00334 -row 00335 -row 00336 -row 00337 -row 00338 -row 00339 -row 00340 -row 00341 -row 00342 -row 00343 -row 00344 -row 00345 -row 00346 -row 00347 -row 00348 -row 00349 -row 00350 -row 00351 -row 00352 -row 00353 -row 00354 -row 00355 -row 00356 -row 00357 -row 00358 -row 00359 -row 00360 -row 00361 -row 00362 -row 00363 -row 00364 -row 00365 -row 00366 -row 00367 -row 00368 -row 00369 -row 00370 -row 00371 -row 00372 -row 00373 -row 00374 -row 00375 -row 00376 -row 00377 -row 00378 -row 00379 -row 00380 -row 00381 -row 00382 -row 00383 -row 00384 -row 00385 -row 00386 -row 00387 -row 00388 -row 00389 -row 00390 -row 00391 -row 00392 -row 00393 -row 00394 -row 00395 -row 00396 -row 00397 -row 00398 -row 00399 -row 00400 -row 00401 -row 00402 -row 00403 -row 00404 -row 00405 -row 00406 -row 00407 -row 00408 -row 00409 -row 00410 -row 00411 -row 00412 -row 00413 -row 00414 -row 00415 -row 00416 -row 00417 -row 00418 -row 00419 -row 00420 -row 00421 -row 00422 -row 00423 -row 00424 -row 00425 -row 00426 -row 00427 -row 00428 -row 00429 -row 00430 -row 00431 -row 00432 -row 00433 -row 00434 -row 00435 -row 00436 -row 00437 -row 00438 -row 00439 -row 00440 -row 00441 -row 00442 -row 00443 -row 00444 -row 00445 -row 00446 -row 00447 -row 00448 -row 00449 -row 00450 -row 00451 -row 00452 -row 00453 -row 00454 -row 00455 -row 00456 -row 00457 -row 00458 -row 00459 -row 00460 -row 00461 -row 00462 -row 00463 -row 00464 -row 00465 -row 00466 -row 00467 -row 00468 -row 00469 -row 00470 -row 00471 -row 00472 -row 00473 -row 00474 -row 00475 -row 00476 -row 00477 -row 00478 -row 00479 -row 00480 -row 00481 -row 00482 -row 00483 -row 00484 -row 00485 -row 00486 -row 00487 -row 00488 -row 00489 -row 00490 -row 00491 -row 00492 -row 00493 -row 00494 -row 00495 -row 00496 -row 00497 -row 00498 -row 00499 -row 00500 -row 00501 -row 00502 -row 00503 -row 00504 -row 00505 -row 00506 -row 00507 -row 00508 -row 00509 -row 00510 -row 00511 -row 00512 -row 00513 -row 00514 -row 00515 -row 00516 -row 00517 -row 00518 -row 00519 -row 00520 -row 00521 -row 00522 -row 00523 -row 00524 -row 00525 -row 00526 -row 00527 -row 00528 -row 00529 -row 00530 -row 00531 -row 00532 -row 00533 -row 00534 -row 00535 -row 00536 -row 00537 -row 00538 -row 00539 -row 00540 -row 00541 -row 00542 -row 00543 -row 00544 -row 00545 -row 00546 -row 00547 -row 00548 -row 00549 -row 00550 -row 00551 -row 00552 -row 00553 -row 00554 -row 00555 -row 00556 -row 00557 -row 00558 -row 00559 -row 00560 -row 00561 -row 00562 -row 00563 -row 00564 -row 00565 -row 00566 -row 00567 -row 00568 -row 00569 -row 00570 -row 00571 -row 00572 -row 00573 -row 00574 -row 00575 -row 00576 -row 00577 -row 00578 -row 00579 -row 00580 -row 00581 -row 00582 -row 00583 -row 00584 -row 00585 -row 00586 -row 00587 -row 00588 -row 00589 -row 00590 -row 00591 -row 00592 -row 00593 -row 00594 -row 00595 -row 00596 -row 00597 -row 00598 -row 00599 -row 00600 -row 00601 -row 00602 -row 00603 -row 00604 -row 00605 -row 00606 -row 00607 -row 00608 -row 00609 -row 00610 -row 00611 -row 00612 -row 00613 -row 00614 -row 00615 -row 00616 -row 00617 -row 00618 -row 00619 -row 00620 -row 00621 -row 00622 -row 00623 -row 00624 -row 00625 -row 00626 -row 00627 -row 00628 -row 00629 -row 00630 -row 00631 -row 00632 -row 00633 -row 00634 -row 00635 -row 00636 -row 00637 -row 00638 -row 00639 -row 00640 -row 00641 -row 00642 -row 00643 -row 00644 -row 00645 -row 00646 -row 00647 -row 00648 -row 00649 -row 00650 -row 00651 -row 00652 -row 00653 -row 00654 -row 00655 -row 00656 -row 00657 -row 00658 -row 00659 -row 00660 -row 00661 -row 00662 -row 00663 -row 00664 -row 00665 -row 00666 -row 00667 -row 00668 -row 00669 -row 00670 -row 00671 -row 00672 -row 00673 -row 00674 -row 00675 -row 00676 -row 00677 -row 00678 -row 00679 -row 00680 -row 00681 -row 00682 -row 00683 -row 00684 -row 00685 -row 00686 -row 00687 -row 00688 -row 00689 -row 00690 -row 00691 -row 00692 -row 00693 -row 00694 -row 00695 -row 00696 -row 00697 -row 00698 -row 00699 -row 00700 -row 00701 -row 00702 -row 00703 -row 00704 -row 00705 -row 00706 -row 00707 -row 00708 -row 00709 -row 00710 -row 00711 -row 00712 -row 00713 -row 00714 -row 00715 -row 00716 -row 00717 -row 00718 -row 00719 -row 00720 -row 00721 -row 00722 -row 00723 -row 00724 -row 00725 -row 00726 -row 00727 -row 00728 -row 00729 -row 00730 -row 00731 -row 00732 -row 00733 -row 00734 -row 00735 -row 00736 -row 00737 -row 00738 -row 00739 -row 00740 -row 00741 -row 00742 -row 00743 -row 00744 -row 00745 -row 00746 -row 00747 -row 00748 -row 00749 -row 00750 -row 00751 -row 00752 -row 00753 -row 00754 -row 00755 -row 00756 -row 00757 -row 00758 -row 00759 -row 00760 -row 00761 -row 00762 -row 00763 -row 00764 -row 00765 -row 00766 -row 00767 -row 00768 -row 00769 -row 00770 -row 00771 -row 00772 -row 00773 -row 00774 -row 00775 -row 00776 -row 00777 -row 00778 -row 00779 -row 00780 -row 00781 -row 00782 -row 00783 -row 00784 -row 00785 -row 00786 -row 00787 -row 00788 -row 00789 -row 00790 -row 00791 -row 00792 -row 00793 -row 00794 -row 00795 -row 00796 -row 00797 -row 00798 -row 00799 -row 00800 -row 00801 -row 00802 -row 00803 -row 00804 -row 00805 -row 00806 -row 00807 -row 00808 -row 00809 -row 00810 -row 00811 -row 00812 -row 00813 -row 00814 -row 00815 -row 00816 -row 00817 -row 00818 -row 00819 -row 00820 -row 00821 -row 00822 -row 00823 -row 00824 -row 00825 -row 00826 -row 00827 -row 00828 -row 00829 -row 00830 -row 00831 -row 00832 -row 00833 -row 00834 -row 00835 -row 00836 -row 00837 -row 00838 -row 00839 -row 00840 -row 00841 -row 00842 -row 00843 -row 00844 -row 00845 -row 00846 -row 00847 -row 00848 -row 00849 -row 00850 -row 00851 -row 00852 -row 00853 -row 00854 -row 00855 -row 00856 -row 00857 -row 00858 -row 00859 -row 00860 -row 00861 -row 00862 -row 00863 -row 00864 -row 00865 -row 00866 -row 00867 -row 00868 -row 00869 -row 00870 -row 00871 -row 00872 -row 00873 -row 00874 -row 00875 -row 00876 -row 00877 -row 00878 -row 00879 -row 00880 -row 00881 -row 00882 -row 00883 -row 00884 -row 00885 -row 00886 -row 00887 -row 00888 -row 00889 -row 00890 -row 00891 -row 00892 -row 00893 -row 00894 -row 00895 -row 00896 -row 00897 -row 00898 -row 00899 -row 00900 -row 00901 -row 00902 -row 00903 -row 00904 -row 00905 -row 00906 -row 00907 -row 00908 -row 00909 -row 00910 -row 00911 -row 00912 -row 00913 -row 00914 -row 00915 -row 00916 -row 00917 -row 00918 -row 00919 -row 00920 -row 00921 -row 00922 -row 00923 -row 00924 -row 00925 -row 00926 -row 00927 -row 00928 -row 00929 -row 00930 -row 00931 -row 00932 -row 00933 -row 00934 -row 00935 -row 00936 -row 00937 -row 00938 -row 00939 -row 00940 -row 00941 -row 00942 -row 00943 -row 00944 -row 00945 -row 00946 -row 00947 -row 00948 -row 00949 -row 00950 -row 00951 -row 00952 -row 00953 -row 00954 -row 00955 -row 00956 -row 00957 -row 00958 -row 00959 -row 00960 -row 00961 -row 00962 -row 00963 -row 00964 -row 00965 -row 00966 -row 00967 -row 00968 -row 00969 -row 00970 -row 00971 -row 00972 -row 00973 -row 00974 -row 00975 -row 00976 -row 00977 -row 00978 -row 00979 -row 00980 -row 00981 -row 00982 -row 00983 -row 00984 -row 00985 -row 00986 -row 00987 -row 00988 -row 00989 -row 00990 -row 00991 -row 00992 -row 00993 -row 00994 -row 00995 -row 00996 -row 00997 -row 00998 -row 00999 -row 01000 -row 01001 -row 01002 -row 01003 -row 01004 -row 01005 -row 01006 -row 01007 -row 01008 -row 01009 -row 01010 -row 01011 -row 01012 -row 01013 -row 01014 -row 01015 -row 01016 -row 01017 -row 01018 -row 01019 -row 01020 -row 01021 -row 01022 -row 01023 -row 01024 -row 01025 -row 01026 -row 01027 -row 01028 -row 01029 -row 01030 -row 01031 -row 01032 -row 01033 -row 01034 -row 01035 -row 01036 -row 01037 -row 01038 -row 01039 -row 01040 -row 01041 -row 01042 -row 01043 -row 01044 -row 01045 -row 01046 -row 01047 -row 01048 -row 01049 -row 01050 -row 01051 -row 01052 -row 01053 -row 01054 -row 01055 -row 01056 -row 01057 -row 01058 -row 01059 -row 01060 -row 01061 -row 01062 -row 01063 -row 01064 -row 01065 -row 01066 -row 01067 -row 01068 -row 01069 -row 01070 -row 01071 -row 01072 -row 01073 -row 01074 -row 01075 -row 01076 -row 01077 -row 01078 -row 01079 -row 01080 -row 01081 -row 01082 -row 01083 -row 01084 -row 01085 -row 01086 -row 01087 -row 01088 -row 01089 -row 01090 -row 01091 -row 01092 -row 01093 -row 01094 -row 01095 -row 01096 -row 01097 -row 01098 -row 01099 -row 01100 -row 01101 -row 01102 -row 01103 -row 01104 -row 01105 -row 01106 -row 01107 -row 01108 -row 01109 -row 01110 -row 01111 -row 01112 -row 01113 -row 01114 -row 01115 -row 01116 -row 01117 -row 01118 -row 01119 -row 01120 -row 01121 -row 01122 -row 01123 -row 01124 -row 01125 -row 01126 -row 01127 -row 01128 -row 01129 -row 01130 -row 01131 -row 01132 -row 01133 -row 01134 -row 01135 -row 01136 -row 01137 -row 01138 -row 01139 -row 01140 -row 01141 -row 01142 -row 01143 -row 01144 -row 01145 -row 01146 -row 01147 -row 01148 -row 01149 -row 01150 -row 01151 -row 01152 -row 01153 -row 01154 -row 01155 -row 01156 -row 01157 -row 01158 -row 01159 -row 01160 -row 01161 -row 01162 -row 01163 -row 01164 -row 01165 -row 01166 -row 01167 -row 01168 -row 01169 -row 01170 -row 01171 -row 01172 -row 01173 -row 01174 -row 01175 -row 01176 -row 01177 -row 01178 -row 01179 -row 01180 -row 01181 -row 01182 -row 01183 -row 01184 -row 01185 -row 01186 -row 01187 -row 01188 -row 01189 -row 01190 -row 01191 -row 01192 -row 01193 -row 01194 -row 01195 -row 01196 -row 01197 -row 01198 -row 01199 -row 01200 -row 01201 -row 01202 -row 01203 -row 01204 -row 01205 -row 01206 -row 01207 -row 01208 -row 01209 -row 01210 -row 01211 -row 01212 -row 01213 -row 01214 -row 01215 -row 01216 -row 01217 -row 01218 -row 01219 -row 01220 -row 01221 -row 01222 -row 01223 -row 01224 -row 01225 -row 01226 -row 01227 -row 01228 -row 01229 -row 01230 -row 01231 -row 01232 -row 01233 -row 01234 -row 01235 -row 01236 -row 01237 -row 01238 -row 01239 -row 01240 -row 01241 -row 01242 -row 01243 -row 01244 -row 01245 -row 01246 -row 01247 -row 01248 -row 01249 -row 01250 -row 01251 -row 01252 -row 01253 -row 01254 -row 01255 -row 01256 -row 01257 -row 01258 -row 01259 -row 01260 -row 01261 -row 01262 -row 01263 -row 01264 -row 01265 -row 01266 -row 01267 -row 01268 -row 01269 -row 01270 -row 01271 -row 01272 -row 01273 -row 01274 -row 01275 -row 01276 -row 01277 -row 01278 -row 01279 -row 01280 -row 01281 -row 01282 -row 01283 -row 01284 -row 01285 -row 01286 -row 01287 -row 01288 -row 01289 -row 01290 -row 01291 -row 01292 -row 01293 -row 01294 -row 01295 -row 01296 -row 01297 -row 01298 -row 01299 -row 01300 -row 01301 -row 01302 -row 01303 -row 01304 -row 01305 -row 01306 -row 01307 -row 01308 -row 01309 -row 01310 -row 01311 -row 01312 -row 01313 -row 01314 -row 01315 -row 01316 -row 01317 -row 01318 -row 01319 -row 01320 -row 01321 -row 01322 -row 01323 -row 01324 -row 01325 -row 01326 -row 01327 -row 01328 -row 01329 -row 01330 -row 01331 -row 01332 -row 01333 -row 01334 -row 01335 -row 01336 -row 01337 -row 01338 -row 01339 -row 01340 -row 01341 -row 01342 -row 01343 -row 01344 -row 01345 -row 01346 -row 01347 -row 01348 -row 01349 -row 01350 -row 01351 -row 01352 -row 01353 -row 01354 -row 01355 -row 01356 -row 01357 -row 01358 -row 01359 -row 01360 -row 01361 -row 01362 -row 01363 -row 01364 -row 01365 -row 01366 -row 01367 -row 01368 -row 01369 -row 01370 -row 01371 -row 01372 -row 01373 -row 01374 -row 01375 -row 01376 -row 01377 -row 01378 -row 01379 -row 01380 -row 01381 -row 01382 -row 01383 -row 01384 -row 01385 -row 01386 -row 01387 -row 01388 -row 01389 -row 01390 -row 01391 -row 01392 -row 01393 -row 01394 -row 01395 -row 01396 -row 01397 -row 01398 -row 01399 -row 01400 -row 01401 -row 01402 -row 01403 -row 01404 -row 01405 -row 01406 -row 01407 -row 01408 -row 01409 -row 01410 -row 01411 -row 01412 -row 01413 -row 01414 -row 01415 -row 01416 -row 01417 -row 01418 -row 01419 -row 01420 -row 01421 -row 01422 -row 01423 -row 01424 -row 01425 -row 01426 -row 01427 -row 01428 -row 01429 -row 01430 -row 01431 -row 01432 -row 01433 -row 01434 -row 01435 -row 01436 -row 01437 -row 01438 -row 01439 -row 01440 -row 01441 -row 01442 -row 01443 -row 01444 -row 01445 -row 01446 -row 01447 -row 01448 -row 01449 -row 01450 -row 01451 -row 01452 -row 01453 -row 01454 -row 01455 -row 01456 -row 01457 -row 01458 -row 01459 -row 01460 -row 01461 -row 01462 -row 01463 -row 01464 -row 01465 -row 01466 -row 01467 -row 01468 -row 01469 -row 01470 -row 01471 -row 01472 -row 01473 -row 01474 -row 01475 -row 01476 -row 01477 -row 01478 -row 01479 -row 01480 -row 01481 -row 01482 -row 01483 -row 01484 -row 01485 -row 01486 -row 01487 -row 01488 -row 01489 -row 01490 -row 01491 -row 01492 -row 01493 -row 01494 -row 01495 -row 01496 -row 01497 -row 01498 -row 01499 -row 01500 -row 01501 -row 01502 -row 01503 -row 01504 -row 01505 -row 01506 -row 01507 -row 01508 -row 01509 -row 01510 -row 01511 -row 01512 -row 01513 -row 01514 -row 01515 -row 01516 -row 01517 -row 01518 -row 01519 -row 01520 -row 01521 -row 01522 -row 01523 -row 01524 -row 01525 -row 01526 -row 01527 -row 01528 -row 01529 -row 01530 -row 01531 -row 01532 -row 01533 -row 01534 -row 01535 -row 01536 -row 01537 -row 01538 -row 01539 -row 01540 -row 01541 -row 01542 -row 01543 -row 01544 -row 01545 -row 01546 -row 01547 -row 01548 -row 01549 -row 01550 -row 01551 -row 01552 -row 01553 -row 01554 -row 01555 -row 01556 -row 01557 -row 01558 -row 01559 -row 01560 -row 01561 -row 01562 -row 01563 -row 01564 -row 01565 -row 01566 -row 01567 -row 01568 -row 01569 -row 01570 -row 01571 -row 01572 -row 01573 -row 01574 -row 01575 -row 01576 -row 01577 -row 01578 -row 01579 -row 01580 -row 01581 -row 01582 -row 01583 -row 01584 -row 01585 -row 01586 -row 01587 -row 01588 -row 01589 -row 01590 -row 01591 -row 01592 -row 01593 -row 01594 -row 01595 -row 01596 -row 01597 -row 01598 -row 01599 -row 01600 -row 01601 -row 01602 -row 01603 -row 01604 -row 01605 -row 01606 -row 01607 -row 01608 -row 01609 -row 01610 -row 01611 -row 01612 -row 01613 -row 01614 -row 01615 -row 01616 -row 01617 -row 01618 -row 01619 -row 01620 -row 01621 -row 01622 -row 01623 -row 01624 -row 01625 -row 01626 -row 01627 -row 01628 -row 01629 -row 01630 -row 01631 -row 01632 -row 01633 -row 01634 -row 01635 -row 01636 -row 01637 -row 01638 -row 01639 -row 01640 -row 01641 -row 01642 -row 01643 -row 01644 -row 01645 -row 01646 -row 01647 -row 01648 -row 01649 -row 01650 -row 01651 -row 01652 -row 01653 -row 01654 -row 01655 -row 01656 -row 01657 -row 01658 -row 01659 -row 01660 -row 01661 -row 01662 -row 01663 -row 01664 -row 01665 -row 01666 -row 01667 -row 01668 -row 01669 -row 01670 -row 01671 -row 01672 -row 01673 -row 01674 -row 01675 -row 01676 -row 01677 -row 01678 -row 01679 -row 01680 -row 01681 -row 01682 -row 01683 -row 01684 -row 01685 -row 01686 -row 01687 -row 01688 -row 01689 -row 01690 -row 01691 -row 01692 -row 01693 -row 01694 -row 01695 -row 01696 -row 01697 -row 01698 -row 01699 -row 01700 -row 01701 -row 01702 -row 01703 -row 01704 -row 01705 -row 01706 -row 01707 -row 01708 -row 01709 -row 01710 -row 01711 -row 01712 -row 01713 -row 01714 -row 01715 -row 01716 -row 01717 -row 01718 -row 01719 -row 01720 -row 01721 -row 01722 -row 01723 -row 01724 -row 01725 -row 01726 -row 01727 -row 01728 -row 01729 -row 01730 -row 01731 -row 01732 -row 01733 -row 01734 -row 01735 -row 01736 -row 01737 -row 01738 -row 01739 -row 01740 -row 01741 -row 01742 -row 01743 -row 01744 -row 01745 -row 01746 -row 01747 -row 01748 -row 01749 -row 01750 -row 01751 -row 01752 -row 01753 -row 01754 -row 01755 -row 01756 -row 01757 -row 01758 -row 01759 -row 01760 -row 01761 -row 01762 -row 01763 -row 01764 -row 01765 -row 01766 -row 01767 -row 01768 -row 01769 -row 01770 -row 01771 -row 01772 -row 01773 -row 01774 -row 01775 -row 01776 -row 01777 -row 01778 -row 01779 -row 01780 -row 01781 -row 01782 -row 01783 -row 01784 -row 01785 -row 01786 -row 01787 -row 01788 -row 01789 -row 01790 -row 01791 -row 01792 -row 01793 -row 01794 -row 01795 -row 01796 -row 01797 -row 01798 -row 01799 -row 01800 -row 01801 -row 01802 -row 01803 -row 01804 -row 01805 -row 01806 -row 01807 -row 01808 -row 01809 -row 01810 -row 01811 -row 01812 -row 01813 -row 01814 -row 01815 -row 01816 -row 01817 -row 01818 -row 01819 -row 01820 -row 01821 -row 01822 -row 01823 -row 01824 -row 01825 -row 01826 -row 01827 -row 01828 -row 01829 -row 01830 -row 01831 -row 01832 -row 01833 -row 01834 -row 01835 -row 01836 -row 01837 -row 01838 -row 01839 -row 01840 -row 01841 -row 01842 -row 01843 -row 01844 -row 01845 -row 01846 -row 01847 -row 01848 -row 01849 -row 01850 -row 01851 -row 01852 -row 01853 -row 01854 -row 01855 -row 01856 -row 01857 -row 01858 -row 01859 -row 01860 -row 01861 -row 01862 -row 01863 -row 01864 -row 01865 -row 01866 -row 01867 -row 01868 -row 01869 -row 01870 -row 01871 -row 01872 -row 01873 -row 01874 -row 01875 -row 01876 -row 01877 -row 01878 -row 01879 -row 01880 -row 01881 -row 01882 -row 01883 -row 01884 -row 01885 -row 01886 -row 01887 -row 01888 -row 01889 -row 01890 -row 01891 -row 01892 -row 01893 -row 01894 -row 01895 -row 01896 -row 01897 -row 01898 -row 01899 -row 01900 -row 01901 -row 01902 -row 01903 -row 01904 -row 01905 -row 01906 -row 01907 -row 01908 -row 01909 -row 01910 -row 01911 -row 01912 -row 01913 -row 01914 -row 01915 -row 01916 -row 01917 -row 01918 -row 01919 -row 01920 -row 01921 -row 01922 -row 01923 -row 01924 -row 01925 -row 01926 -row 01927 -row 01928 -row 01929 -row 01930 -row 01931 -row 01932 -row 01933 -row 01934 -row 01935 -row 01936 -row 01937 -row 01938 -row 01939 -row 01940 -row 01941 -row 01942 -row 01943 -row 01944 -row 01945 -row 01946 -row 01947 -row 01948 -row 01949 -row 01950 -row 01951 -row 01952 -row 01953 -row 01954 -row 01955 -row 01956 -row 01957 -row 01958 -row 01959 -row 01960 -row 01961 -row 01962 -row 01963 -row 01964 -row 01965 -row 01966 -row 01967 -row 01968 -row 01969 -row 01970 -row 01971 -row 01972 -row 01973 -row 01974 -row 01975 -row 01976 -row 01977 -row 01978 -row 01979 -row 01980 -row 01981 -row 01982 -row 01983 -row 01984 -row 01985 -row 01986 -row 01987 -row 01988 -row 01989 -row 01990 -row 01991 -row 01992 -row 01993 -row 01994 -row 01995 -row 01996 -row 01997 -row 01998 -row 01999 -row 02000 -row 02001 -row 02002 -row 02003 -row 02004 -row 02005 -row 02006 -row 02007 -row 02008 -row 02009 -row 02010 -row 02011 -row 02012 -row 02013 -row 02014 -row 02015 -row 02016 -row 02017 -row 02018 -row 02019 -row 02020 -row 02021 -row 02022 -row 02023 -row 02024 -row 02025 -row 02026 -row 02027 -row 02028 -row 02029 -row 02030 -row 02031 -row 02032 -row 02033 -row 02034 -row 02035 -row 02036 -row 02037 -row 02038 -row 02039 -row 02040 -row 02041 -row 02042 -row 02043 -row 02044 -row 02045 -row 02046 -row 02047 -row 02048 -row 02049 -row 02050 -row 02051 -row 02052 -row 02053 -row 02054 -row 02055 -row 02056 -row 02057 -row 02058 -row 02059 -row 02060 -row 02061 -row 02062 -row 02063 -row 02064 -row 02065 -row 02066 -row 02067 -row 02068 -row 02069 -row 02070 -row 02071 -row 02072 -row 02073 -row 02074 -row 02075 -row 02076 -row 02077 -row 02078 -row 02079 -row 02080 -row 02081 -row 02082 -row 02083 -row 02084 -row 02085 -row 02086 -row 02087 -row 02088 -row 02089 -row 02090 -row 02091 -row 02092 -row 02093 -row 02094 -row 02095 -row 02096 -row 02097 -row 02098 -row 02099 -row 02100 -row 02101 -row 02102 -row 02103 -row 02104 -row 02105 -row 02106 -row 02107 -row 02108 -row 02109 -row 02110 -row 02111 -row 02112 -row 02113 -row 02114 -row 02115 -row 02116 -row 02117 -row 02118 -row 02119 -row 02120 -row 02121 -row 02122 -row 02123 -row 02124 -row 02125 -row 02126 -row 02127 -row 02128 -row 02129 -row 02130 -row 02131 -row 02132 -row 02133 -row 02134 -row 02135 -row 02136 -row 02137 -row 02138 -row 02139 -row 02140 -row 02141 -row 02142 -row 02143 -row 02144 -row 02145 -row 02146 -row 02147 -row 02148 -row 02149 -row 02150 -row 02151 -row 02152 -row 02153 -row 02154 -row 02155 -row 02156 -row 02157 -row 02158 -row 02159 -row 02160 -row 02161 -row 02162 -row 02163 -row 02164 -row 02165 -row 02166 -row 02167 -row 02168 -row 02169 -row 02170 -row 02171 -row 02172 -row 02173 -row 02174 -row 02175 -row 02176 -row 02177 -row 02178 -row 02179 -row 02180 -row 02181 -row 02182 -row 02183 -row 02184 -row 02185 -row 02186 -row 02187 -row 02188 -row 02189 -row 02190 -row 02191 -row 02192 -row 02193 -row 02194 -row 02195 -row 02196 -row 02197 -row 02198 -row 02199 -row 02200 -row 02201 -row 02202 -row 02203 -row 02204 -row 02205 -row 02206 -row 02207 -row 02208 -row 02209 -row 02210 -row 02211 -row 02212 -row 02213 -row 02214 -row 02215 -row 02216 -row 02217 -row 02218 -row 02219 -row 02220 -row 02221 -row 02222 -row 02223 -row 02224 -row 02225 -row 02226 -row 02227 -row 02228 -row 02229 -row 02230 -row 02231 -row 02232 -row 02233 -row 02234 -row 02235 -row 02236 -row 02237 -row 02238 -row 02239 -row 02240 -row 02241 -row 02242 -row 02243 -row 02244 -row 02245 -row 02246 -row 02247 -row 02248 -row 02249 -row 02250 -row 02251 -row 02252 -row 02253 -row 02254 -row 02255 -row 02256 -row 02257 -row 02258 -row 02259 -row 02260 -row 02261 -row 02262 -row 02263 -row 02264 -row 02265 -row 02266 -row 02267 -row 02268 -row 02269 -row 02270 -row 02271 -row 02272 -row 02273 -row 02274 -row 02275 -row 02276 -row 02277 -row 02278 -row 02279 -row 02280 -row 02281 -row 02282 -row 02283 -row 02284 -row 02285 -row 02286 -row 02287 -row 02288 -row 02289 -row 02290 -row 02291 -row 02292 -row 02293 -row 02294 -row 02295 -row 02296 -row 02297 -row 02298 -row 02299 -row 02300 -row 02301 -row 02302 -row 02303 -row 02304 -row 02305 -row 02306 -row 02307 -row 02308 -row 02309 -row 02310 -row 02311 -row 02312 -row 02313 -row 02314 -row 02315 -row 02316 -row 02317 -row 02318 -row 02319 -row 02320 -row 02321 -row 02322 -row 02323 -row 02324 -row 02325 -row 02326 -row 02327 -row 02328 -row 02329 -row 02330 -row 02331 -row 02332 -row 02333 -row 02334 -row 02335 -row 02336 -row 02337 -row 02338 -row 02339 -row 02340 -row 02341 -row 02342 -row 02343 -row 02344 -row 02345 -row 02346 -row 02347 -row 02348 -row 02349 -row 02350 -row 02351 -row 02352 -row 02353 -row 02354 -row 02355 -row 02356 -row 02357 -row 02358 -row 02359 -row 02360 -row 02361 -row 02362 -row 02363 -row 02364 -row 02365 -row 02366 -row 02367 -row 02368 -row 02369 -row 02370 -row 02371 -row 02372 -row 02373 -row 02374 -row 02375 -row 02376 -row 02377 -row 02378 -row 02379 -row 02380 -row 02381 -row 02382 -row 02383 -row 02384 -row 02385 -row 02386 -row 02387 -row 02388 -row 02389 -row 02390 -row 02391 -row 02392 -row 02393 -row 02394 -row 02395 -row 02396 -row 02397 -row 02398 -row 02399 -row 02400 -row 02401 -row 02402 -row 02403 -row 02404 -row 02405 -row 02406 -row 02407 -row 02408 -row 02409 -row 02410 -row 02411 -row 02412 -row 02413 -row 02414 -row 02415 -row 02416 -row 02417 -row 02418 -row 02419 -row 02420 -row 02421 -row 02422 -row 02423 -row 02424 -row 02425 -row 02426 -row 02427 -row 02428 -row 02429 -row 02430 -row 02431 -row 02432 -row 02433 -row 02434 -row 02435 -row 02436 -row 02437 -row 02438 -row 02439 -row 02440 -row 02441 -row 02442 -row 02443 -row 02444 -row 02445 -row 02446 -row 02447 -row 02448 -row 02449 -row 02450 -row 02451 -row 02452 -row 02453 -row 02454 -row 02455 -row 02456 -row 02457 -row 02458 -row 02459 -row 02460 -row 02461 -row 02462 -row 02463 -row 02464 -row 02465 -row 02466 -row 02467 -row 02468 -row 02469 -row 02470 -row 02471 -row 02472 -row 02473 -row 02474 -row 02475 -row 02476 -row 02477 -row 02478 -row 02479 -row 02480 -row 02481 -row 02482 -row 02483 -row 02484 -row 02485 -row 02486 -row 02487 -row 02488 -row 02489 -row 02490 -row 02491 -row 02492 -row 02493 -row 02494 -row 02495 -row 02496 -row 02497 -row 02498 -row 02499 -row 02500 -row 02501 -row 02502 -row 02503 -row 02504 -row 02505 -row 02506 -row 02507 -row 02508 -row 02509 -row 02510 -row 02511 -row 02512 -row 02513 -row 02514 -row 02515 -row 02516 -row 02517 -row 02518 -row 02519 -row 02520 -row 02521 -row 02522 -row 02523 -row 02524 -row 02525 -row 02526 -row 02527 -row 02528 -row 02529 -row 02530 -row 02531 -row 02532 -row 02533 -row 02534 -row 02535 -row 02536 -row 02537 -row 02538 -row 02539 -row 02540 -row 02541 -row 02542 -row 02543 -row 02544 -row 02545 -row 02546 -row 02547 -row 02548 -row 02549 -row 02550 -row 02551 -row 02552 -row 02553 -row 02554 -row 02555 -row 02556 -row 02557 -row 02558 -row 02559 -row 02560 -row 02561 -row 02562 -row 02563 -row 02564 -row 02565 -row 02566 -row 02567 -row 02568 -row 02569 -row 02570 -row 02571 -row 02572 -row 02573 -row 02574 -row 02575 -row 02576 -row 02577 -row 02578 -row 02579 -row 02580 -row 02581 -row 02582 -row 02583 -row 02584 -row 02585 -row 02586 -row 02587 -row 02588 -row 02589 -row 02590 -row 02591 -row 02592 -row 02593 -row 02594 -row 02595 -row 02596 -row 02597 -row 02598 -row 02599 -row 02600 -row 02601 -row 02602 -row 02603 -row 02604 -row 02605 -row 02606 -row 02607 -row 02608 -row 02609 -row 02610 -row 02611 -row 02612 -row 02613 -row 02614 -row 02615 -row 02616 -row 02617 -row 02618 -row 02619 -row 02620 -row 02621 -row 02622 -row 02623 -row 02624 -row 02625 -row 02626 -row 02627 -row 02628 -row 02629 -row 02630 -row 02631 -row 02632 -row 02633 -row 02634 -row 02635 -row 02636 -row 02637 -row 02638 -row 02639 -row 02640 -row 02641 -row 02642 -row 02643 -row 02644 -row 02645 -row 02646 -row 02647 -row 02648 -row 02649 -row 02650 -row 02651 -row 02652 -row 02653 -row 02654 -row 02655 -row 02656 -row 02657 -row 02658 -row 02659 -row 02660 -row 02661 -row 02662 -row 02663 -row 02664 -row 02665 -row 02666 -row 02667 -row 02668 -row 02669 -row 02670 -row 02671 -row 02672 -row 02673 -row 02674 -row 02675 -row 02676 -row 02677 -row 02678 -row 02679 -row 02680 -row 02681 -row 02682 -row 02683 -row 02684 -row 02685 -row 02686 -row 02687 -row 02688 -row 02689 -row 02690 -row 02691 -row 02692 -row 02693 -row 02694 -row 02695 -row 02696 -row 02697 -row 02698 -row 02699 -row 02700 -row 02701 -row 02702 -row 02703 -row 02704 -row 02705 -row 02706 -row 02707 -row 02708 -row 02709 -row 02710 -row 02711 -row 02712 -row 02713 -row 02714 -row 02715 -row 02716 -row 02717 -row 02718 -row 02719 -row 02720 -row 02721 -row 02722 -row 02723 -row 02724 -row 02725 -row 02726 -row 02727 -row 02728 -row 02729 -row 02730 -row 02731 -row 02732 -row 02733 -row 02734 -row 02735 -row 02736 -row 02737 -row 02738 -row 02739 -row 02740 -row 02741 -row 02742 -row 02743 -row 02744 -row 02745 -row 02746 -row 02747 -row 02748 -row 02749 -row 02750 -row 02751 -row 02752 -row 02753 -row 02754 -row 02755 -row 02756 -row 02757 -row 02758 -row 02759 -row 02760 -row 02761 -row 02762 -row 02763 -row 02764 -row 02765 -row 02766 -row 02767 -row 02768 -row 02769 -row 02770 -row 02771 -row 02772 -row 02773 -row 02774 -row 02775 -row 02776 -row 02777 -row 02778 -row 02779 -row 02780 -row 02781 -row 02782 -row 02783 -row 02784 -row 02785 -row 02786 -row 02787 -row 02788 -row 02789 -row 02790 -row 02791 -row 02792 -row 02793 -row 02794 -row 02795 -row 02796 -row 02797 -row 02798 -row 02799 -row 02800 -row 02801 -row 02802 -row 02803 -row 02804 -row 02805 -row 02806 -row 02807 -row 02808 -row 02809 -row 02810 -row 02811 -row 02812 -row 02813 -row 02814 -row 02815 -row 02816 -row 02817 -row 02818 -row 02819 -row 02820 -row 02821 -row 02822 -row 02823 -row 02824 -row 02825 -row 02826 -row 02827 -row 02828 -row 02829 -row 02830 -row 02831 -row 02832 -row 02833 -row 02834 -row 02835 -row 02836 -row 02837 -row 02838 -row 02839 -row 02840 -row 02841 -row 02842 -row 02843 -row 02844 -row 02845 -row 02846 -row 02847 -row 02848 -row 02849 -row 02850 -row 02851 -row 02852 -row 02853 -row 02854 -row 02855 -row 02856 -row 02857 -row 02858 -row 02859 -row 02860 -row 02861 -row 02862 -row 02863 -row 02864 -row 02865 -row 02866 -row 02867 -row 02868 -row 02869 -row 02870 -row 02871 -row 02872 -row 02873 -row 02874 -row 02875 -row 02876 -row 02877 -row 02878 -row 02879 -row 02880 -row 02881 -row 02882 -row 02883 -row 02884 -row 02885 -row 02886 -row 02887 -row 02888 -row 02889 -row 02890 -row 02891 -row 02892 -row 02893 -row 02894 -row 02895 -row 02896 -row 02897 -row 02898 -row 02899 -row 02900 -row 02901 -row 02902 -row 02903 -row 02904 -row 02905 -row 02906 -row 02907 -row 02908 -row 02909 -row 02910 -row 02911 -row 02912 -row 02913 -row 02914 -row 02915 -row 02916 -row 02917 -row 02918 -row 02919 -row 02920 -row 02921 -row 02922 -row 02923 -row 02924 -row 02925 -row 02926 -row 02927 -row 02928 -row 02929 -row 02930 -row 02931 -row 02932 -row 02933 -row 02934 -row 02935 -row 02936 -row 02937 -row 02938 -row 02939 -row 02940 -row 02941 -row 02942 -row 02943 -row 02944 -row 02945 -row 02946 -row 02947 -row 02948 -row 02949 -row 02950 -row 02951 -row 02952 -row 02953 -row 02954 -row 02955 -row 02956 -row 02957 -row 02958 -row 02959 -row 02960 -row 02961 -row 02962 -row 02963 -row 02964 -row 02965 -row 02966 -row 02967 -row 02968 -row 02969 -row 02970 -row 02971 -row 02972 -row 02973 -row 02974 -row 02975 -row 02976 -row 02977 -row 02978 -row 02979 -row 02980 -row 02981 -row 02982 -row 02983 -row 02984 -row 02985 -row 02986 -row 02987 -row 02988 -row 02989 -row 02990 -row 02991 -row 02992 -row 02993 -row 02994 -row 02995 -row 02996 -row 02997 -row 02998 -row 02999 -row 03000 -row 03001 -row 03002 -row 03003 -row 03004 -row 03005 -row 03006 -row 03007 -row 03008 -row 03009 -row 03010 -row 03011 -row 03012 -row 03013 -row 03014 -row 03015 -row 03016 -row 03017 -row 03018 -row 03019 -row 03020 -row 03021 -row 03022 -row 03023 -row 03024 -row 03025 -row 03026 -row 03027 -row 03028 -row 03029 -row 03030 -row 03031 -row 03032 -row 03033 -row 03034 -row 03035 -row 03036 -row 03037 -row 03038 -row 03039 -row 03040 -row 03041 -row 03042 -row 03043 -row 03044 -row 03045 -row 03046 -row 03047 -row 03048 -row 03049 -row 03050 -row 03051 -row 03052 -row 03053 -row 03054 -row 03055 -row 03056 -row 03057 -row 03058 -row 03059 -row 03060 -row 03061 -row 03062 -row 03063 -row 03064 -row 03065 -row 03066 -row 03067 -row 03068 -row 03069 -row 03070 -row 03071 -row 03072 -row 03073 -row 03074 -row 03075 -row 03076 -row 03077 -row 03078 -row 03079 -row 03080 -row 03081 -row 03082 -row 03083 -row 03084 -row 03085 -row 03086 -row 03087 -row 03088 -row 03089 -row 03090 -row 03091 -row 03092 -row 03093 -row 03094 -row 03095 -row 03096 -row 03097 -row 03098 -row 03099 -row 03100 -row 03101 -row 03102 -row 03103 -row 03104 -row 03105 -row 03106 -row 03107 -row 03108 -row 03109 -row 03110 -row 03111 -row 03112 -row 03113 -row 03114 -row 03115 -row 03116 -row 03117 -row 03118 -row 03119 -row 03120 -row 03121 -row 03122 -row 03123 -row 03124 -row 03125 -row 03126 -row 03127 -row 03128 -row 03129 -row 03130 -row 03131 -row 03132 -row 03133 -row 03134 -row 03135 -row 03136 -row 03137 -row 03138 -row 03139 -row 03140 -row 03141 -row 03142 -row 03143 -row 03144 -row 03145 -row 03146 -row 03147 -row 03148 -row 03149 -row 03150 -row 03151 -row 03152 -row 03153 -row 03154 -row 03155 -row 03156 -row 03157 -row 03158 -row 03159 -row 03160 -row 03161 -row 03162 -row 03163 -row 03164 -row 03165 -row 03166 -row 03167 -row 03168 -row 03169 -row 03170 -row 03171 -row 03172 -row 03173 -row 03174 -row 03175 -row 03176 -row 03177 -row 03178 -row 03179 -row 03180 -row 03181 -row 03182 -row 03183 -row 03184 -row 03185 -row 03186 -row 03187 -row 03188 -row 03189 -row 03190 -row 03191 -row 03192 -row 03193 -row 03194 -row 03195 -row 03196 -row 03197 -row 03198 -row 03199 -row 03200 -row 03201 -row 03202 -row 03203 -row 03204 -row 03205 -row 03206 -row 03207 -row 03208 -row 03209 -row 03210 -row 03211 -row 03212 -row 03213 -row 03214 -row 03215 -row 03216 -row 03217 -row 03218 -row 03219 -row 03220 -row 03221 -row 03222 -row 03223 -row 03224 -row 03225 -row 03226 -row 03227 -row 03228 -row 03229 -row 03230 -row 03231 -row 03232 -row 03233 -row 03234 -row 03235 -row 03236 -row 03237 -row 03238 -row 03239 -row 03240 -row 03241 -row 03242 -row 03243 -row 03244 -row 03245 -row 03246 -row 03247 -row 03248 -row 03249 -row 03250 -row 03251 -row 03252 -row 03253 -row 03254 -row 03255 -row 03256 -row 03257 -row 03258 -row 03259 -row 03260 -row 03261 -row 03262 -row 03263 -row 03264 -row 03265 -row 03266 -row 03267 -row 03268 -row 03269 -row 03270 -row 03271 -row 03272 -row 03273 -row 03274 -row 03275 -row 03276 -row 03277 -row 03278 -row 03279 -row 03280 -row 03281 -row 03282 -row 03283 -row 03284 -row 03285 -row 03286 -row 03287 -row 03288 -row 03289 -row 03290 -row 03291 -row 03292 -row 03293 -row 03294 -row 03295 -row 03296 -row 03297 -row 03298 -row 03299 -row 03300 -row 03301 -row 03302 -row 03303 -row 03304 -row 03305 -row 03306 -row 03307 -row 03308 -row 03309 -row 03310 -row 03311 -row 03312 -row 03313 -row 03314 -row 03315 -row 03316 -row 03317 -row 03318 -row 03319 -row 03320 -row 03321 -row 03322 -row 03323 -row 03324 -row 03325 -row 03326 -row 03327 -row 03328 -row 03329 -row 03330 -row 03331 -row 03332 -row 03333 -row 03334 -row 03335 -row 03336 -row 03337 -row 03338 -row 03339 -row 03340 -row 03341 -row 03342 -row 03343 -row 03344 -row 03345 -row 03346 -row 03347 -row 03348 -row 03349 -row 03350 -row 03351 -row 03352 -row 03353 -row 03354 -row 03355 -row 03356 -row 03357 -row 03358 -row 03359 -row 03360 -row 03361 -row 03362 -row 03363 -row 03364 -row 03365 -row 03366 -row 03367 -row 03368 -row 03369 -row 03370 -row 03371 -row 03372 -row 03373 -row 03374 -row 03375 -row 03376 -row 03377 -row 03378 -row 03379 -row 03380 -row 03381 -row 03382 -row 03383 -row 03384 -row 03385 -row 03386 -row 03387 -row 03388 -row 03389 -row 03390 -row 03391 -row 03392 -row 03393 -row 03394 -row 03395 -row 03396 -row 03397 -row 03398 -row 03399 -row 03400 -row 03401 -row 03402 -row 03403 -row 03404 -row 03405 -row 03406 -row 03407 -row 03408 -row 03409 -row 03410 -row 03411 -row 03412 -row 03413 -row 03414 -row 03415 -row 03416 -row 03417 -row 03418 -row 03419 -row 03420 -row 03421 -row 03422 -row 03423 -row 03424 -row 03425 -row 03426 -row 03427 -row 03428 -row 03429 -row 03430 -row 03431 -row 03432 -row 03433 -row 03434 -row 03435 -row 03436 -row 03437 -row 03438 -row 03439 -row 03440 -row 03441 -row 03442 -row 03443 -row 03444 -row 03445 -row 03446 -row 03447 -row 03448 -row 03449 -row 03450 -row 03451 -row 03452 -row 03453 -row 03454 -row 03455 -row 03456 -row 03457 -row 03458 -row 03459 -row 03460 -row 03461 -row 03462 -row 03463 -row 03464 -row 03465 -row 03466 -row 03467 -row 03468 -row 03469 -row 03470 -row 03471 -row 03472 -row 03473 -row 03474 -row 03475 -row 03476 -row 03477 -row 03478 -row 03479 -row 03480 -row 03481 -row 03482 -row 03483 -row 03484 -row 03485 -row 03486 -row 03487 -row 03488 -row 03489 -row 03490 -row 03491 -row 03492 -row 03493 -row 03494 -row 03495 -row 03496 -row 03497 -row 03498 -row 03499 -row 03500 -row 03501 -row 03502 -row 03503 -row 03504 -row 03505 -row 03506 -row 03507 -row 03508 -row 03509 -row 03510 -row 03511 -row 03512 -row 03513 -row 03514 -row 03515 -row 03516 -row 03517 -row 03518 -row 03519 -row 03520 -row 03521 -row 03522 -row 03523 -row 03524 -row 03525 -row 03526 -row 03527 -row 03528 -row 03529 -row 03530 -row 03531 -row 03532 -row 03533 -row 03534 -row 03535 -row 03536 -row 03537 -row 03538 -row 03539 -row 03540 -row 03541 -row 03542 -row 03543 -row 03544 -row 03545 -row 03546 -row 03547 -row 03548 -row 03549 -row 03550 -row 03551 -row 03552 -row 03553 -row 03554 -row 03555 -row 03556 -row 03557 -row 03558 -row 03559 -row 03560 -row 03561 -row 03562 -row 03563 -row 03564 -row 03565 -row 03566 -row 03567 -row 03568 -row 03569 -row 03570 -row 03571 -row 03572 -row 03573 -row 03574 -row 03575 -row 03576 -row 03577 -row 03578 -row 03579 -row 03580 -row 03581 -row 03582 -row 03583 -row 03584 -row 03585 -row 03586 -row 03587 -row 03588 -row 03589 -row 03590 -row 03591 -row 03592 -row 03593 -row 03594 -row 03595 -row 03596 -row 03597 -row 03598 -row 03599 -row 03600 -row 03601 -row 03602 -row 03603 -row 03604 -row 03605 -row 03606 -row 03607 -row 03608 -row 03609 -row 03610 -row 03611 -row 03612 -row 03613 -row 03614 -row 03615 -row 03616 -row 03617 -row 03618 -row 03619 -row 03620 -row 03621 -row 03622 -row 03623 -row 03624 -row 03625 -row 03626 -row 03627 -row 03628 -row 03629 -row 03630 -row 03631 -row 03632 -row 03633 -row 03634 -row 03635 -row 03636 -row 03637 -row 03638 -row 03639 -row 03640 -row 03641 -row 03642 -row 03643 -row 03644 -row 03645 -row 03646 -row 03647 -row 03648 -row 03649 -row 03650 -row 03651 -row 03652 -row 03653 -row 03654 -row 03655 -row 03656 -row 03657 -row 03658 -row 03659 -row 03660 -row 03661 -row 03662 -row 03663 -row 03664 -row 03665 -row 03666 -row 03667 -row 03668 -row 03669 -row 03670 -row 03671 -row 03672 -row 03673 -row 03674 -row 03675 -row 03676 -row 03677 -row 03678 -row 03679 -row 03680 -row 03681 -row 03682 -row 03683 -row 03684 -row 03685 -row 03686 -row 03687 -row 03688 -row 03689 -row 03690 -row 03691 -row 03692 -row 03693 -row 03694 -row 03695 -row 03696 -row 03697 -row 03698 -row 03699 -row 03700 -row 03701 -row 03702 -row 03703 -row 03704 -row 03705 -row 03706 -row 03707 -row 03708 -row 03709 -row 03710 -row 03711 -row 03712 -row 03713 -row 03714 -row 03715 -row 03716 -row 03717 -row 03718 -row 03719 -row 03720 -row 03721 -row 03722 -row 03723 -row 03724 -row 03725 -row 03726 -row 03727 -row 03728 -row 03729 -row 03730 -row 03731 -row 03732 -row 03733 -row 03734 -row 03735 -row 03736 -row 03737 -row 03738 -row 03739 -row 03740 -row 03741 -row 03742 -row 03743 -row 03744 -row 03745 -row 03746 -row 03747 -row 03748 -row 03749 -row 03750 -row 03751 -row 03752 -row 03753 -row 03754 -row 03755 -row 03756 -row 03757 -row 03758 -row 03759 -row 03760 -row 03761 -row 03762 -row 03763 -row 03764 -row 03765 -row 03766 -row 03767 -row 03768 -row 03769 -row 03770 -row 03771 -row 03772 -row 03773 -row 03774 -row 03775 -row 03776 -row 03777 -row 03778 -row 03779 -row 03780 -row 03781 -row 03782 -row 03783 -row 03784 -row 03785 -row 03786 -row 03787 -row 03788 -row 03789 -row 03790 -row 03791 -row 03792 -row 03793 -row 03794 -row 03795 -row 03796 -row 03797 -row 03798 -row 03799 -row 03800 -row 03801 -row 03802 -row 03803 -row 03804 -row 03805 -row 03806 -row 03807 -row 03808 -row 03809 -row 03810 -row 03811 -row 03812 -row 03813 -row 03814 -row 03815 -row 03816 -row 03817 -row 03818 -row 03819 -row 03820 -row 03821 -row 03822 -row 03823 -row 03824 -row 03825 -row 03826 -row 03827 -row 03828 -row 03829 -row 03830 -row 03831 -row 03832 -row 03833 -row 03834 -row 03835 -row 03836 -row 03837 -row 03838 -row 03839 -row 03840 -row 03841 -row 03842 -row 03843 -row 03844 -row 03845 -row 03846 -row 03847 -row 03848 -row 03849 -row 03850 -row 03851 -row 03852 -row 03853 -row 03854 -row 03855 -row 03856 -row 03857 -row 03858 -row 03859 -row 03860 -row 03861 -row 03862 -row 03863 -row 03864 -row 03865 -row 03866 -row 03867 -row 03868 -row 03869 -row 03870 -row 03871 -row 03872 -row 03873 -row 03874 -row 03875 -row 03876 -row 03877 -row 03878 -row 03879 -row 03880 -row 03881 -row 03882 -row 03883 -row 03884 -row 03885 -row 03886 -row 03887 -row 03888 -row 03889 -row 03890 -row 03891 -row 03892 -row 03893 -row 03894 -row 03895 -row 03896 -row 03897 -row 03898 -row 03899 -row 03900 -row 03901 -row 03902 -row 03903 -row 03904 -row 03905 -row 03906 -row 03907 -row 03908 -row 03909 -row 03910 -row 03911 -row 03912 -row 03913 -row 03914 -row 03915 -row 03916 -row 03917 -row 03918 -row 03919 -row 03920 -row 03921 -row 03922 -row 03923 -row 03924 -row 03925 -row 03926 -row 03927 -row 03928 -row 03929 -row 03930 -row 03931 -row 03932 -row 03933 -row 03934 -row 03935 -row 03936 -row 03937 -row 03938 -row 03939 -row 03940 -row 03941 -row 03942 -row 03943 -row 03944 -row 03945 -row 03946 -row 03947 -row 03948 -row 03949 -row 03950 -row 03951 -row 03952 -row 03953 -row 03954 -row 03955 -row 03956 -row 03957 -row 03958 -row 03959 -row 03960 -row 03961 -row 03962 -row 03963 -row 03964 -row 03965 -row 03966 -row 03967 -row 03968 -row 03969 -row 03970 -row 03971 -row 03972 -row 03973 -row 03974 -row 03975 -row 03976 -row 03977 -row 03978 -row 03979 -row 03980 -row 03981 -row 03982 -row 03983 -row 03984 -row 03985 -row 03986 -row 03987 -row 03988 -row 03989 -row 03990 -row 03991 -row 03992 -row 03993 -row 03994 -row 03995 -row 03996 -row 03997 -row 03998 -row 03999 -row 04000 -row 04001 -row 04002 -row 04003 -row 04004 -row 04005 -row 04006 -row 04007 -row 04008 -row 04009 -row 04010 -row 04011 -row 04012 -row 04013 -row 04014 -row 04015 -row 04016 -row 04017 -row 04018 -row 04019 -row 04020 -row 04021 -row 04022 -row 04023 -row 04024 -row 04025 -row 04026 -row 04027 -row 04028 -row 04029 -row 04030 -row 04031 -row 04032 -row 04033 -row 04034 -row 04035 -row 04036 -row 04037 -row 04038 -row 04039 -row 04040 -row 04041 -row 04042 -row 04043 -row 04044 -row 04045 -row 04046 -row 04047 -row 04048 -row 04049 -row 04050 -row 04051 -row 04052 -row 04053 -row 04054 -row 04055 -row 04056 -row 04057 -row 04058 -row 04059 -row 04060 -row 04061 -row 04062 -row 04063 -row 04064 -row 04065 -row 04066 -row 04067 -row 04068 -row 04069 -row 04070 -row 04071 -row 04072 -row 04073 -row 04074 -row 04075 -row 04076 -row 04077 -row 04078 -row 04079 -row 04080 -row 04081 -row 04082 -row 04083 -row 04084 -row 04085 -row 04086 -row 04087 -row 04088 -row 04089 -row 04090 -row 04091 -row 04092 -row 04093 -row 04094 -row 04095 -row 04096 -row 04097 -row 04098 -row 04099 -row 04100 -row 04101 -row 04102 -row 04103 -row 04104 -row 04105 -row 04106 -row 04107 -row 04108 -row 04109 -row 04110 -row 04111 -row 04112 -row 04113 -row 04114 -row 04115 -row 04116 -row 04117 -row 04118 -row 04119 -row 04120 -row 04121 -row 04122 -row 04123 -row 04124 -row 04125 -row 04126 -row 04127 -row 04128 -row 04129 -row 04130 -row 04131 -row 04132 -row 04133 -row 04134 -row 04135 -row 04136 -row 04137 -row 04138 -row 04139 -row 04140 -row 04141 -row 04142 -row 04143 -row 04144 -row 04145 -row 04146 -row 04147 -row 04148 -row 04149 -row 04150 -row 04151 -row 04152 -row 04153 -row 04154 -row 04155 -row 04156 -row 04157 -row 04158 -row 04159 -row 04160 -row 04161 -row 04162 -row 04163 -row 04164 -row 04165 -row 04166 -row 04167 -row 04168 -row 04169 -row 04170 -row 04171 -row 04172 -row 04173 -row 04174 -row 04175 -row 04176 -row 04177 -row 04178 -row 04179 -row 04180 -row 04181 -row 04182 -row 04183 -row 04184 -row 04185 -row 04186 -row 04187 -row 04188 -row 04189 -row 04190 -row 04191 -row 04192 -row 04193 -row 04194 -row 04195 -row 04196 -row 04197 -row 04198 -row 04199 -row 04200 -row 04201 -row 04202 -row 04203 -row 04204 -row 04205 -row 04206 -row 04207 -row 04208 -row 04209 -row 04210 -row 04211 -row 04212 -row 04213 -row 04214 -row 04215 -row 04216 -row 04217 -row 04218 -row 04219 -row 04220 -row 04221 -row 04222 -row 04223 -row 04224 -row 04225 -row 04226 -row 04227 -row 04228 -row 04229 -row 04230 -row 04231 -row 04232 -row 04233 -row 04234 -row 04235 -row 04236 -row 04237 -row 04238 -row 04239 -row 04240 -row 04241 -row 04242 -row 04243 -row 04244 -row 04245 -row 04246 -row 04247 -row 04248 -row 04249 -row 04250 -row 04251 -row 04252 -row 04253 -row 04254 -row 04255 -row 04256 -row 04257 -row 04258 -row 04259 -row 04260 -row 04261 -row 04262 -row 04263 -row 04264 -row 04265 -row 04266 -row 04267 -row 04268 -row 04269 -row 04270 -row 04271 -row 04272 -row 04273 -row 04274 -row 04275 -row 04276 -row 04277 -row 04278 -row 04279 -row 04280 -row 04281 -row 04282 -row 04283 -row 04284 -row 04285 -row 04286 -row 04287 -row 04288 -row 04289 -row 04290 -row 04291 -row 04292 -row 04293 -row 04294 -row 04295 -row 04296 -row 04297 -row 04298 -row 04299 -row 04300 -row 04301 -row 04302 -row 04303 -row 04304 -row 04305 -row 04306 -row 04307 -row 04308 -row 04309 -row 04310 -row 04311 -row 04312 -row 04313 -row 04314 -row 04315 -row 04316 -row 04317 -row 04318 -row 04319 -row 04320 -row 04321 -row 04322 -row 04323 -row 04324 -row 04325 -row 04326 -row 04327 -row 04328 -row 04329 -row 04330 -row 04331 -row 04332 -row 04333 -row 04334 -row 04335 -row 04336 -row 04337 -row 04338 -row 04339 -row 04340 -row 04341 -row 04342 -row 04343 -row 04344 -row 04345 -row 04346 -row 04347 -row 04348 -row 04349 -row 04350 -row 04351 -row 04352 -row 04353 -row 04354 -row 04355 -row 04356 -row 04357 -row 04358 -row 04359 -row 04360 -row 04361 -row 04362 -row 04363 -row 04364 -row 04365 -row 04366 -row 04367 -row 04368 -row 04369 -row 04370 -row 04371 -row 04372 -row 04373 -row 04374 -row 04375 -row 04376 -row 04377 -row 04378 -row 04379 -row 04380 -row 04381 -row 04382 -row 04383 -row 04384 -row 04385 -row 04386 -row 04387 -row 04388 -row 04389 -row 04390 -row 04391 -row 04392 -row 04393 -row 04394 -row 04395 -row 04396 -row 04397 -row 04398 -row 04399 -row 04400 -row 04401 -row 04402 -row 04403 -row 04404 -row 04405 -row 04406 -row 04407 -row 04408 -row 04409 -row 04410 -row 04411 -row 04412 -row 04413 -row 04414 -row 04415 -row 04416 -row 04417 -row 04418 -row 04419 -row 04420 -row 04421 -row 04422 -row 04423 -row 04424 -row 04425 -row 04426 -row 04427 -row 04428 -row 04429 -row 04430 -row 04431 -row 04432 -row 04433 -row 04434 -row 04435 -row 04436 -row 04437 -row 04438 -row 04439 -row 04440 -row 04441 -row 04442 -row 04443 -row 04444 -row 04445 -row 04446 -row 04447 -row 04448 -row 04449 -row 04450 -row 04451 -row 04452 -row 04453 -row 04454 -row 04455 -row 04456 -row 04457 -row 04458 -row 04459 -row 04460 -row 04461 -row 04462 -row 04463 -row 04464 -row 04465 -row 04466 -row 04467 -row 04468 -row 04469 -row 04470 -row 04471 -row 04472 -row 04473 -row 04474 -row 04475 -row 04476 -row 04477 -row 04478 -row 04479 -row 04480 -row 04481 -row 04482 -row 04483 -row 04484 -row 04485 -row 04486 -row 04487 -row 04488 -row 04489 -row 04490 -row 04491 -row 04492 -row 04493 -row 04494 -row 04495 -row 04496 -row 04497 -row 04498 -row 04499 -row 04500 -row 04501 -row 04502 -row 04503 -row 04504 -row 04505 -row 04506 -row 04507 -row 04508 -row 04509 -row 04510 -row 04511 -row 04512 -row 04513 -row 04514 -row 04515 -row 04516 -row 04517 -row 04518 -row 04519 -row 04520 -row 04521 -row 04522 -row 04523 -row 04524 -row 04525 -row 04526 -row 04527 -row 04528 -row 04529 -row 04530 -row 04531 -row 04532 -row 04533 -row 04534 -row 04535 -row 04536 -row 04537 -row 04538 -row 04539 -row 04540 -row 04541 -row 04542 -row 04543 -row 04544 -row 04545 -row 04546 -row 04547 -row 04548 -row 04549 -row 04550 -row 04551 -row 04552 -row 04553 -row 04554 -row 04555 -row 04556 -row 04557 -row 04558 -row 04559 -row 04560 -row 04561 -row 04562 -row 04563 -row 04564 -row 04565 -row 04566 -row 04567 -row 04568 -row 04569 -row 04570 -row 04571 -row 04572 -row 04573 -row 04574 -row 04575 -row 04576 -row 04577 -row 04578 -row 04579 -row 04580 -row 04581 -row 04582 -row 04583 -row 04584 -row 04585 -row 04586 -row 04587 -row 04588 -row 04589 -row 04590 -row 04591 -row 04592 -row 04593 -row 04594 -row 04595 -row 04596 -row 04597 -row 04598 -row 04599 -row 04600 -row 04601 -row 04602 -row 04603 -row 04604 -row 04605 -row 04606 -row 04607 -row 04608 -row 04609 -row 04610 -row 04611 -row 04612 -row 04613 -row 04614 -row 04615 -row 04616 -row 04617 -row 04618 -row 04619 -row 04620 -row 04621 -row 04622 -row 04623 -row 04624 -row 04625 -row 04626 -row 04627 -row 04628 -row 04629 -row 04630 -row 04631 -row 04632 -row 04633 -row 04634 -row 04635 -row 04636 -row 04637 -row 04638 -row 04639 -row 04640 -row 04641 -row 04642 -row 04643 -row 04644 -row 04645 -row 04646 -row 04647 -row 04648 -row 04649 -row 04650 -row 04651 -row 04652 -row 04653 -row 04654 -row 04655 -row 04656 -row 04657 -row 04658 -row 04659 -row 04660 -row 04661 -row 04662 -row 04663 -row 04664 -row 04665 -row 04666 -row 04667 -row 04668 -row 04669 -row 04670 -row 04671 -row 04672 -row 04673 -row 04674 -row 04675 -row 04676 -row 04677 -row 04678 -row 04679 -row 04680 -row 04681 -row 04682 -row 04683 -row 04684 -row 04685 -row 04686 -row 04687 -row 04688 -row 04689 -row 04690 -row 04691 -row 04692 -row 04693 -row 04694 -row 04695 -row 04696 -row 04697 -row 04698 -row 04699 -row 04700 -row 04701 -row 04702 -row 04703 -row 04704 -row 04705 -row 04706 -row 04707 -row 04708 -row 04709 -row 04710 -row 04711 -row 04712 -row 04713 -row 04714 -row 04715 -row 04716 -row 04717 -row 04718 -row 04719 -row 04720 -row 04721 -row 04722 -row 04723 -row 04724 -row 04725 -row 04726 -row 04727 -row 04728 -row 04729 -row 04730 -row 04731 -row 04732 -row 04733 -row 04734 -row 04735 -row 04736 -row 04737 -row 04738 -row 04739 -row 04740 -row 04741 -row 04742 -row 04743 -row 04744 -row 04745 -row 04746 -row 04747 -row 04748 -row 04749 -row 04750 -row 04751 -row 04752 -row 04753 -row 04754 -row 04755 -row 04756 -row 04757 -row 04758 -row 04759 -row 04760 -row 04761 -row 04762 -row 04763 -row 04764 -row 04765 -row 04766 -row 04767 -row 04768 -row 04769 -row 04770 -row 04771 -row 04772 -row 04773 -row 04774 -row 04775 -row 04776 -row 04777 -row 04778 -row 04779 -row 04780 -row 04781 -row 04782 -row 04783 -row 04784 -row 04785 -row 04786 -row 04787 -row 04788 -row 04789 -row 04790 -row 04791 -row 04792 -row 04793 -row 04794 -row 04795 -row 04796 -row 04797 -row 04798 -row 04799 -row 04800 -row 04801 -row 04802 -row 04803 -row 04804 -row 04805 -row 04806 -row 04807 -row 04808 -row 04809 -row 04810 -row 04811 -row 04812 -row 04813 -row 04814 -row 04815 -row 04816 -row 04817 -row 04818 -row 04819 -row 04820 -row 04821 -row 04822 -row 04823 -row 04824 -row 04825 -row 04826 -row 04827 -row 04828 -row 04829 -row 04830 -row 04831 -row 04832 -row 04833 -row 04834 -row 04835 -row 04836 -row 04837 -row 04838 -row 04839 -row 04840 -row 04841 -row 04842 -row 04843 -row 04844 -row 04845 -row 04846 -row 04847 -row 04848 -row 04849 -row 04850 -row 04851 -row 04852 -row 04853 -row 04854 -row 04855 -row 04856 -row 04857 -row 04858 -row 04859 -row 04860 -row 04861 -row 04862 -row 04863 -row 04864 -row 04865 -row 04866 -row 04867 -row 04868 -row 04869 -row 04870 -row 04871 -row 04872 -row 04873 -row 04874 -row 04875 -row 04876 -row 04877 -row 04878 -row 04879 -row 04880 -row 04881 -row 04882 -row 04883 -row 04884 -row 04885 -row 04886 -row 04887 -row 04888 -row 04889 -row 04890 -row 04891 -row 04892 -row 04893 -row 04894 -row 04895 -row 04896 -row 04897 -row 04898 -row 04899 -row 04900 -row 04901 -row 04902 -row 04903 -row 04904 -row 04905 -row 04906 -row 04907 -row 04908 -row 04909 -row 04910 -row 04911 -row 04912 -row 04913 -row 04914 -row 04915 -row 04916 -row 04917 -row 04918 -row 04919 -row 04920 -row 04921 -row 04922 -row 04923 -row 04924 -row 04925 -row 04926 -row 04927 -row 04928 -row 04929 -row 04930 -row 04931 -row 04932 -row 04933 -row 04934 -row 04935 -row 04936 -row 04937 -row 04938 -row 04939 -row 04940 -row 04941 -row 04942 -row 04943 -row 04944 -row 04945 -row 04946 -row 04947 -row 04948 -row 04949 -row 04950 -row 04951 -row 04952 -row 04953 -row 04954 -row 04955 -row 04956 -row 04957 -row 04958 -row 04959 -row 04960 -row 04961 -row 04962 -row 04963 -row 04964 -row 04965 -row 04966 -row 04967 -row 04968 -row 04969 -row 04970 -row 04971 -row 04972 -row 04973 -row 04974 -row 04975 -row 04976 -row 04977 -row 04978 -row 04979 -row 04980 -row 04981 -row 04982 -row 04983 -row 04984 -row 04985 -row 04986 -row 04987 -row 04988 -row 04989 -row 04990 -row 04991 -row 04992 -row 04993 -row 04994 -row 04995 -row 04996 -row 04997 -row 04998 -row 04999 -row 05000 -row 05001 -row 05002 -row 05003 -row 05004 -row 05005 -row 05006 -row 05007 -row 05008 -row 05009 -row 05010 -row 05011 -row 05012 -row 05013 -row 05014 -row 05015 -row 05016 -row 05017 -row 05018 -row 05019 -row 05020 -row 05021 -row 05022 -row 05023 -row 05024 -row 05025 -row 05026 -row 05027 -row 05028 -row 05029 -row 05030 -row 05031 -row 05032 -row 05033 -row 05034 -row 05035 -row 05036 -row 05037 -row 05038 -row 05039 -row 05040 -row 05041 -row 05042 -row 05043 -row 05044 -row 05045 -row 05046 -row 05047 -row 05048 -row 05049 -row 05050 -row 05051 -row 05052 -row 05053 -row 05054 -row 05055 -row 05056 -row 05057 -row 05058 -row 05059 -row 05060 -row 05061 -row 05062 -row 05063 -row 05064 -row 05065 -row 05066 -row 05067 -row 05068 -row 05069 -row 05070 -row 05071 -row 05072 -row 05073 -row 05074 -row 05075 -row 05076 -row 05077 -row 05078 -row 05079 -row 05080 -row 05081 -row 05082 -row 05083 -row 05084 -row 05085 -row 05086 -row 05087 -row 05088 -row 05089 -row 05090 -row 05091 -row 05092 -row 05093 -row 05094 -row 05095 -row 05096 -row 05097 -row 05098 -row 05099 -row 05100 -row 05101 -row 05102 -row 05103 -row 05104 -row 05105 -row 05106 -row 05107 -row 05108 -row 05109 -row 05110 -row 05111 -row 05112 -row 05113 -row 05114 -row 05115 -row 05116 -row 05117 -row 05118 -row 05119 -row 05120 -row 05121 -row 05122 -row 05123 -row 05124 -row 05125 -row 05126 -row 05127 -row 05128 -row 05129 -row 05130 -row 05131 -row 05132 -row 05133 -row 05134 -row 05135 -row 05136 -row 05137 -row 05138 -row 05139 -row 05140 -row 05141 -row 05142 -row 05143 -row 05144 -row 05145 -row 05146 -row 05147 -row 05148 -row 05149 -row 05150 -row 05151 -row 05152 -row 05153 -row 05154 -row 05155 -row 05156 -row 05157 -row 05158 -row 05159 -row 05160 -row 05161 -row 05162 -row 05163 -row 05164 -row 05165 -row 05166 -row 05167 -row 05168 -row 05169 -row 05170 -row 05171 -row 05172 -row 05173 -row 05174 -row 05175 -row 05176 -row 05177 -row 05178 -row 05179 -row 05180 -row 05181 -row 05182 -row 05183 -row 05184 -row 05185 -row 05186 -row 05187 -row 05188 -row 05189 -row 05190 -row 05191 -row 05192 -row 05193 -row 05194 -row 05195 -row 05196 -row 05197 -row 05198 -row 05199 -row 05200 -row 05201 -row 05202 -row 05203 -row 05204 -row 05205 -row 05206 -row 05207 -row 05208 -row 05209 -row 05210 -row 05211 -row 05212 -row 05213 -row 05214 -row 05215 -row 05216 -row 05217 -row 05218 -row 05219 -row 05220 -row 05221 -row 05222 -row 05223 -row 05224 -row 05225 -row 05226 -row 05227 -row 05228 -row 05229 -row 05230 -row 05231 -row 05232 -row 05233 -row 05234 -row 05235 -row 05236 -row 05237 -row 05238 -row 05239 -row 05240 -row 05241 -row 05242 -row 05243 -row 05244 -row 05245 -row 05246 -row 05247 -row 05248 -row 05249 -row 05250 -row 05251 -row 05252 -row 05253 -row 05254 -row 05255 -row 05256 -row 05257 -row 05258 -row 05259 -row 05260 -row 05261 -row 05262 -row 05263 -row 05264 -row 05265 -row 05266 -row 05267 -row 05268 -row 05269 -row 05270 -row 05271 -row 05272 -row 05273 -row 05274 -row 05275 -row 05276 -row 05277 -row 05278 -row 05279 -row 05280 -row 05281 -row 05282 -row 05283 -row 05284 -row 05285 -row 05286 -row 05287 -row 05288 -row 05289 -row 05290 -row 05291 -row 05292 -row 05293 -row 05294 -row 05295 -row 05296 -row 05297 -row 05298 -row 05299 -row 05300 -row 05301 -row 05302 -row 05303 -row 05304 -row 05305 -row 05306 -row 05307 -row 05308 -row 05309 -row 05310 -row 05311 -row 05312 -row 05313 -row 05314 -row 05315 -row 05316 -row 05317 -row 05318 -row 05319 -row 05320 -row 05321 -row 05322 -row 05323 -row 05324 -row 05325 -row 05326 -row 05327 -row 05328 -row 05329 -row 05330 -row 05331 -row 05332 -row 05333 -row 05334 -row 05335 -row 05336 -row 05337 -row 05338 -row 05339 -row 05340 -row 05341 -row 05342 -row 05343 -row 05344 -row 05345 -row 05346 -row 05347 -row 05348 -row 05349 -row 05350 -row 05351 -row 05352 -row 05353 -row 05354 -row 05355 -row 05356 -row 05357 -row 05358 -row 05359 -row 05360 -row 05361 -row 05362 -row 05363 -row 05364 -row 05365 -row 05366 -row 05367 -row 05368 -row 05369 -row 05370 -row 05371 -row 05372 -row 05373 -row 05374 -row 05375 -row 05376 -row 05377 -row 05378 -row 05379 -row 05380 -row 05381 -row 05382 -row 05383 -row 05384 -row 05385 -row 05386 -row 05387 -row 05388 -row 05389 -row 05390 -row 05391 -row 05392 -row 05393 -row 05394 -row 05395 -row 05396 -row 05397 -row 05398 -row 05399 -row 05400 -row 05401 -row 05402 -row 05403 -row 05404 -row 05405 -row 05406 -row 05407 -row 05408 -row 05409 -row 05410 -row 05411 -row 05412 -row 05413 -row 05414 -row 05415 -row 05416 -row 05417 -row 05418 -row 05419 -row 05420 -row 05421 -row 05422 -row 05423 -row 05424 -row 05425 -row 05426 -row 05427 -row 05428 -row 05429 -row 05430 -row 05431 -row 05432 -row 05433 -row 05434 -row 05435 -row 05436 -row 05437 -row 05438 -row 05439 -row 05440 -row 05441 -row 05442 -row 05443 -row 05444 -row 05445 -row 05446 -row 05447 -row 05448 -row 05449 -row 05450 -row 05451 -row 05452 -row 05453 -row 05454 -row 05455 -row 05456 -row 05457 -row 05458 -row 05459 -row 05460 -row 05461 -row 05462 -row 05463 -row 05464 -row 05465 -row 05466 -row 05467 -row 05468 -row 05469 -row 05470 -row 05471 -row 05472 -row 05473 -row 05474 -row 05475 -row 05476 -row 05477 -row 05478 -row 05479 -row 05480 -row 05481 -row 05482 -row 05483 -row 05484 -row 05485 -row 05486 -row 05487 -row 05488 -row 05489 -row 05490 -row 05491 -row 05492 -row 05493 -row 05494 -row 05495 -row 05496 -row 05497 -row 05498 -row 05499 -row 05500 -row 05501 -row 05502 -row 05503 -row 05504 -row 05505 -row 05506 -row 05507 -row 05508 -row 05509 -row 05510 -row 05511 -row 05512 -row 05513 -row 05514 -row 05515 -row 05516 -row 05517 -row 05518 -row 05519 -row 05520 -row 05521 -row 05522 -row 05523 -row 05524 -row 05525 -row 05526 -row 05527 -row 05528 -row 05529 -row 05530 -row 05531 -row 05532 -row 05533 -row 05534 -row 05535 -row 05536 -row 05537 -row 05538 -row 05539 -row 05540 -row 05541 -row 05542 -row 05543 -row 05544 -row 05545 -row 05546 -row 05547 -row 05548 -row 05549 -row 05550 -row 05551 -row 05552 -row 05553 -row 05554 -row 05555 -row 05556 -row 05557 -row 05558 -row 05559 -row 05560 -row 05561 -row 05562 -row 05563 -row 05564 -row 05565 -row 05566 -row 05567 -row 05568 -row 05569 -row 05570 -row 05571 -row 05572 -row 05573 -row 05574 -row 05575 -row 05576 -row 05577 -row 05578 -row 05579 -row 05580 -row 05581 -row 05582 -row 05583 -row 05584 -row 05585 -row 05586 -row 05587 -row 05588 -row 05589 -row 05590 -row 05591 -row 05592 -row 05593 -row 05594 -row 05595 -row 05596 -row 05597 -row 05598 -row 05599 -row 05600 -row 05601 -row 05602 -row 05603 -row 05604 -row 05605 -row 05606 -row 05607 -row 05608 -row 05609 -row 05610 -row 05611 -row 05612 -row 05613 -row 05614 -row 05615 -row 05616 -row 05617 -row 05618 -row 05619 -row 05620 -row 05621 -row 05622 -row 05623 -row 05624 -row 05625 -row 05626 -row 05627 -row 05628 -row 05629 -row 05630 -row 05631 -row 05632 -row 05633 -row 05634 -row 05635 -row 05636 -row 05637 -row 05638 -row 05639 -row 05640 -row 05641 -row 05642 -row 05643 -row 05644 -row 05645 -row 05646 -row 05647 -row 05648 -row 05649 -row 05650 -row 05651 -row 05652 -row 05653 -row 05654 -row 05655 -row 05656 -row 05657 -row 05658 -row 05659 -row 05660 -row 05661 -row 05662 -row 05663 -row 05664 -row 05665 -row 05666 -row 05667 -row 05668 -row 05669 -row 05670 -row 05671 -row 05672 -row 05673 -row 05674 -row 05675 -row 05676 -row 05677 -row 05678 -row 05679 -row 05680 -row 05681 -row 05682 -row 05683 -row 05684 -row 05685 -row 05686 -row 05687 -row 05688 -row 05689 -row 05690 -row 05691 -row 05692 -row 05693 -row 05694 -row 05695 -row 05696 -row 05697 -row 05698 -row 05699 -row 05700 -row 05701 -row 05702 -row 05703 -row 05704 -row 05705 -row 05706 -row 05707 -row 05708 -row 05709 -row 05710 -row 05711 -row 05712 -row 05713 -row 05714 -row 05715 -row 05716 -row 05717 -row 05718 -row 05719 -row 05720 -row 05721 -row 05722 -row 05723 -row 05724 -row 05725 -row 05726 -row 05727 -row 05728 -row 05729 -row 05730 -row 05731 -row 05732 -row 05733 -row 05734 -row 05735 -row 05736 -row 05737 -row 05738 -row 05739 -row 05740 -row 05741 -row 05742 -row 05743 -row 05744 -row 05745 -row 05746 -row 05747 -row 05748 -row 05749 -row 05750 -row 05751 -row 05752 -row 05753 -row 05754 -row 05755 -row 05756 -row 05757 -row 05758 -row 05759 -row 05760 -row 05761 -row 05762 -row 05763 -row 05764 -row 05765 -row 05766 -row 05767 -row 05768 -row 05769 -row 05770 -row 05771 -row 05772 -row 05773 -row 05774 -row 05775 -row 05776 -row 05777 -row 05778 -row 05779 -row 05780 -row 05781 -row 05782 -row 05783 -row 05784 -row 05785 -row 05786 -row 05787 -row 05788 -row 05789 -row 05790 -row 05791 -row 05792 -row 05793 -row 05794 -row 05795 -row 05796 -row 05797 -row 05798 -row 05799 -row 05800 -row 05801 -row 05802 -row 05803 -row 05804 -row 05805 -row 05806 -row 05807 -row 05808 -row 05809 -row 05810 -row 05811 -row 05812 -row 05813 -row 05814 -row 05815 -row 05816 -row 05817 -row 05818 -row 05819 -row 05820 -row 05821 -row 05822 -row 05823 -row 05824 -row 05825 -row 05826 -row 05827 -row 05828 -row 05829 -row 05830 -row 05831 -row 05832 -row 05833 -row 05834 -row 05835 -row 05836 -row 05837 -row 05838 -row 05839 -row 05840 -row 05841 -row 05842 -row 05843 -row 05844 -row 05845 -row 05846 -row 05847 -row 05848 -row 05849 -row 05850 -row 05851 -row 05852 -row 05853 -row 05854 -row 05855 -row 05856 -row 05857 -row 05858 -row 05859 -row 05860 -row 05861 -row 05862 -row 05863 -row 05864 -row 05865 -row 05866 -row 05867 -row 05868 -row 05869 -row 05870 -row 05871 -row 05872 -row 05873 -row 05874 -row 05875 -row 05876 -row 05877 -row 05878 -row 05879 -row 05880 -row 05881 -row 05882 -row 05883 -row 05884 -row 05885 -row 05886 -row 05887 -row 05888 -row 05889 -row 05890 -row 05891 -row 05892 -row 05893 -row 05894 -row 05895 -row 05896 -row 05897 -row 05898 -row 05899 -row 05900 -row 05901 -row 05902 -row 05903 -row 05904 -row 05905 -row 05906 -row 05907 -row 05908 -row 05909 -row 05910 -row 05911 -row 05912 -row 05913 -row 05914 -row 05915 -row 05916 -row 05917 -row 05918 -row 05919 -row 05920 -row 05921 -row 05922 -row 05923 -row 05924 -row 05925 -row 05926 -row 05927 -row 05928 -row 05929 -row 05930 -row 05931 -row 05932 -row 05933 -row 05934 -row 05935 -row 05936 -row 05937 -row 05938 -row 05939 -row 05940 -row 05941 -row 05942 -row 05943 -row 05944 -row 05945 -row 05946 -row 05947 -row 05948 -row 05949 -row 05950 -row 05951 -row 05952 -row 05953 -row 05954 -row 05955 -row 05956 -row 05957 -row 05958 -row 05959 -row 05960 -row 05961 -row 05962 -row 05963 -row 05964 -row 05965 -row 05966 -row 05967 -row 05968 -row 05969 -row 05970 -row 05971 -row 05972 -row 05973 -row 05974 -row 05975 -row 05976 -row 05977 -row 05978 -row 05979 -row 05980 -row 05981 -row 05982 -row 05983 -row 05984 -row 05985 -row 05986 -row 05987 -row 05988 -row 05989 -row 05990 -row 05991 -row 05992 -row 05993 -row 05994 -row 05995 -row 05996 -row 05997 -row 05998 -row 05999 -row 06000 -row 06001 -row 06002 -row 06003 -row 06004 -row 06005 -row 06006 -row 06007 -row 06008 -row 06009 -row 06010 -row 06011 -row 06012 -row 06013 -row 06014 -row 06015 -row 06016 -row 06017 -row 06018 -row 06019 -row 06020 -row 06021 -row 06022 -row 06023 -row 06024 -row 06025 -row 06026 -row 06027 -row 06028 -row 06029 -row 06030 -row 06031 -row 06032 -row 06033 -row 06034 -row 06035 -row 06036 -row 06037 -row 06038 -row 06039 -row 06040 -row 06041 -row 06042 -row 06043 -row 06044 -row 06045 -row 06046 -row 06047 -row 06048 -row 06049 -row 06050 -row 06051 -row 06052 -row 06053 -row 06054 -row 06055 -row 06056 -row 06057 -row 06058 -row 06059 -row 06060 -row 06061 -row 06062 -row 06063 -row 06064 -row 06065 -row 06066 -row 06067 -row 06068 -row 06069 -row 06070 -row 06071 -row 06072 -row 06073 -row 06074 -row 06075 -row 06076 -row 06077 -row 06078 -row 06079 -row 06080 -row 06081 -row 06082 -row 06083 -row 06084 -row 06085 -row 06086 -row 06087 -row 06088 -row 06089 -row 06090 -row 06091 -row 06092 -row 06093 -row 06094 -row 06095 -row 06096 -row 06097 -row 06098 -row 06099 -row 06100 -row 06101 -row 06102 -row 06103 -row 06104 -row 06105 -row 06106 -row 06107 -row 06108 -row 06109 -row 06110 -row 06111 -row 06112 -row 06113 -row 06114 -row 06115 -row 06116 -row 06117 -row 06118 -row 06119 -row 06120 -row 06121 -row 06122 -row 06123 -row 06124 -row 06125 -row 06126 -row 06127 -row 06128 -row 06129 -row 06130 -row 06131 -row 06132 -row 06133 -row 06134 -row 06135 -row 06136 -row 06137 -row 06138 -row 06139 -row 06140 -row 06141 -row 06142 -row 06143 -row 06144 -row 06145 -row 06146 -row 06147 -row 06148 -row 06149 -row 06150 -row 06151 -row 06152 -row 06153 -row 06154 -row 06155 -row 06156 -row 06157 -row 06158 -row 06159 -row 06160 -row 06161 -row 06162 -row 06163 -row 06164 -row 06165 -row 06166 -row 06167 -row 06168 -row 06169 -row 06170 -row 06171 -row 06172 -row 06173 -row 06174 -row 06175 -row 06176 -row 06177 -row 06178 -row 06179 -row 06180 -row 06181 -row 06182 -row 06183 -row 06184 -row 06185 -row 06186 -row 06187 -row 06188 -row 06189 -row 06190 -row 06191 -row 06192 -row 06193 -row 06194 -row 06195 -row 06196 -row 06197 -row 06198 -row 06199 -row 06200 -row 06201 -row 06202 -row 06203 -row 06204 -row 06205 -row 06206 -row 06207 -row 06208 -row 06209 -row 06210 -row 06211 -row 06212 -row 06213 -row 06214 -row 06215 -row 06216 -row 06217 -row 06218 -row 06219 -row 06220 -row 06221 -row 06222 -row 06223 -row 06224 -row 06225 -row 06226 -row 06227 -row 06228 -row 06229 -row 06230 -row 06231 -row 06232 -row 06233 -row 06234 -row 06235 -row 06236 -row 06237 -row 06238 -row 06239 -row 06240 -row 06241 -row 06242 -row 06243 -row 06244 -row 06245 -row 06246 -row 06247 -row 06248 -row 06249 -row 06250 -row 06251 -row 06252 -row 06253 -row 06254 -row 06255 -row 06256 -row 06257 -row 06258 -row 06259 -row 06260 -row 06261 -row 06262 -row 06263 -row 06264 -row 06265 -row 06266 -row 06267 -row 06268 -row 06269 -row 06270 -row 06271 -row 06272 -row 06273 -row 06274 -row 06275 -row 06276 -row 06277 -row 06278 -row 06279 -row 06280 -row 06281 -row 06282 -row 06283 -row 06284 -row 06285 -row 06286 -row 06287 -row 06288 -row 06289 -row 06290 -row 06291 -row 06292 -row 06293 -row 06294 -row 06295 -row 06296 -row 06297 -row 06298 -row 06299 -row 06300 -row 06301 -row 06302 -row 06303 -row 06304 -row 06305 -row 06306 -row 06307 -row 06308 -row 06309 -row 06310 -row 06311 -row 06312 -row 06313 -row 06314 -row 06315 -row 06316 -row 06317 -row 06318 -row 06319 -row 06320 -row 06321 -row 06322 -row 06323 -row 06324 -row 06325 -row 06326 -row 06327 -row 06328 -row 06329 -row 06330 -row 06331 -row 06332 -row 06333 -row 06334 -row 06335 -row 06336 -row 06337 -row 06338 -row 06339 -row 06340 -row 06341 -row 06342 -row 06343 -row 06344 -row 06345 -row 06346 -row 06347 -row 06348 -row 06349 -row 06350 -row 06351 -row 06352 -row 06353 -row 06354 -row 06355 -row 06356 -row 06357 -row 06358 -row 06359 -row 06360 -row 06361 -row 06362 -row 06363 -row 06364 -row 06365 -row 06366 -row 06367 -row 06368 -row 06369 -row 06370 -row 06371 -row 06372 -row 06373 -row 06374 -row 06375 -row 06376 -row 06377 -row 06378 -row 06379 -row 06380 -row 06381 -row 06382 -row 06383 -row 06384 -row 06385 -row 06386 -row 06387 -row 06388 -row 06389 -row 06390 -row 06391 -row 06392 -row 06393 -row 06394 -row 06395 -row 06396 -row 06397 -row 06398 -row 06399 -row 06400 -row 06401 -row 06402 -row 06403 -row 06404 -row 06405 -row 06406 -row 06407 -row 06408 -row 06409 -row 06410 -row 06411 -row 06412 -row 06413 -row 06414 -row 06415 -row 06416 -row 06417 -row 06418 -row 06419 -row 06420 -row 06421 -row 06422 -row 06423 -row 06424 -row 06425 -row 06426 -row 06427 -row 06428 -row 06429 -row 06430 -row 06431 -row 06432 -row 06433 -row 06434 -row 06435 -row 06436 -row 06437 -row 06438 -row 06439 -row 06440 -row 06441 -row 06442 -row 06443 -row 06444 -row 06445 -row 06446 -row 06447 -row 06448 -row 06449 -row 06450 -row 06451 -row 06452 -row 06453 -row 06454 -row 06455 -row 06456 -row 06457 -row 06458 -row 06459 -row 06460 -row 06461 -row 06462 -row 06463 -row 06464 -row 06465 -row 06466 -row 06467 -row 06468 -row 06469 -row 06470 -row 06471 -row 06472 -row 06473 -row 06474 -row 06475 -row 06476 -row 06477 -row 06478 -row 06479 -row 06480 -row 06481 -row 06482 -row 06483 -row 06484 -row 06485 -row 06486 -row 06487 -row 06488 -row 06489 -row 06490 -row 06491 -row 06492 -row 06493 -row 06494 -row 06495 -row 06496 -row 06497 -row 06498 -row 06499 -row 06500 -row 06501 -row 06502 -row 06503 -row 06504 -row 06505 -row 06506 -row 06507 -row 06508 -row 06509 -row 06510 -row 06511 -row 06512 -row 06513 -row 06514 -row 06515 -row 06516 -row 06517 -row 06518 -row 06519 -row 06520 -row 06521 -row 06522 -row 06523 -row 06524 -row 06525 -row 06526 -row 06527 -row 06528 -row 06529 -row 06530 -row 06531 -row 06532 -row 06533 -row 06534 -row 06535 -row 06536 -row 06537 -row 06538 -row 06539 -row 06540 -row 06541 -row 06542 -row 06543 -row 06544 -row 06545 -row 06546 -row 06547 -row 06548 -row 06549 -row 06550 -row 06551 -row 06552 -row 06553 -row 06554 -row 06555 -row 06556 -row 06557 -row 06558 -row 06559 -row 06560 -row 06561 -row 06562 -row 06563 -row 06564 -row 06565 -row 06566 -row 06567 -row 06568 -row 06569 -row 06570 -row 06571 -row 06572 -row 06573 -row 06574 -row 06575 -row 06576 -row 06577 -row 06578 -row 06579 -row 06580 -row 06581 -row 06582 -row 06583 -row 06584 -row 06585 -row 06586 -row 06587 -row 06588 -row 06589 -row 06590 -row 06591 -row 06592 -row 06593 -row 06594 -row 06595 -row 06596 -row 06597 -row 06598 -row 06599 -row 06600 -row 06601 -row 06602 -row 06603 -row 06604 -row 06605 -row 06606 -row 06607 -row 06608 -row 06609 -row 06610 -row 06611 -row 06612 -row 06613 -row 06614 -row 06615 -row 06616 -row 06617 -row 06618 -row 06619 -row 06620 -row 06621 -row 06622 -row 06623 -row 06624 -row 06625 -row 06626 -row 06627 -row 06628 -row 06629 -row 06630 -row 06631 -row 06632 -row 06633 -row 06634 -row 06635 -row 06636 -row 06637 -row 06638 -row 06639 -row 06640 -row 06641 -row 06642 -row 06643 -row 06644 -row 06645 -row 06646 -row 06647 -row 06648 -row 06649 -row 06650 -row 06651 -row 06652 -row 06653 -row 06654 -row 06655 -row 06656 -row 06657 -row 06658 -row 06659 -row 06660 -row 06661 -row 06662 -row 06663 -row 06664 -row 06665 -row 06666 -row 06667 -row 06668 -row 06669 -row 06670 -row 06671 -row 06672 -row 06673 -row 06674 -row 06675 -row 06676 -row 06677 -row 06678 -row 06679 -row 06680 -row 06681 -row 06682 -row 06683 -row 06684 -row 06685 -row 06686 -row 06687 -row 06688 -row 06689 -row 06690 -row 06691 -row 06692 -row 06693 -row 06694 -row 06695 -row 06696 -row 06697 -row 06698 -row 06699 -row 06700 -row 06701 -row 06702 -row 06703 -row 06704 -row 06705 -row 06706 -row 06707 -row 06708 -row 06709 -row 06710 -row 06711 -row 06712 -row 06713 -row 06714 -row 06715 -row 06716 -row 06717 -row 06718 -row 06719 -row 06720 -row 06721 -row 06722 -row 06723 -row 06724 -row 06725 -row 06726 -row 06727 -row 06728 -row 06729 -row 06730 -row 06731 -row 06732 -row 06733 -row 06734 -row 06735 -row 06736 -row 06737 -row 06738 -row 06739 -row 06740 -row 06741 -row 06742 -row 06743 -row 06744 -row 06745 -row 06746 -row 06747 -row 06748 -row 06749 -row 06750 -row 06751 -row 06752 -row 06753 -row 06754 -row 06755 -row 06756 -row 06757 -row 06758 -row 06759 -row 06760 -row 06761 -row 06762 -row 06763 -row 06764 -row 06765 -row 06766 -row 06767 -row 06768 -row 06769 -row 06770 -row 06771 -row 06772 -row 06773 -row 06774 -row 06775 -row 06776 -row 06777 -row 06778 -row 06779 -row 06780 -row 06781 -row 06782 -row 06783 -row 06784 -row 06785 -row 06786 -row 06787 -row 06788 -row 06789 -row 06790 -row 06791 -row 06792 -row 06793 -row 06794 -row 06795 -row 06796 -row 06797 -row 06798 -row 06799 -row 06800 -row 06801 -row 06802 -row 06803 -row 06804 -row 06805 -row 06806 -row 06807 -row 06808 -row 06809 -row 06810 -row 06811 -row 06812 -row 06813 -row 06814 -row 06815 -row 06816 -row 06817 -row 06818 -row 06819 -row 06820 -row 06821 -row 06822 -row 06823 -row 06824 -row 06825 -row 06826 -row 06827 -row 06828 -row 06829 -row 06830 -row 06831 -row 06832 -row 06833 -row 06834 -row 06835 -row 06836 -row 06837 -row 06838 -row 06839 -row 06840 -row 06841 -row 06842 -row 06843 -row 06844 -row 06845 -row 06846 -row 06847 -row 06848 -row 06849 -row 06850 -row 06851 -row 06852 -row 06853 -row 06854 -row 06855 -row 06856 -row 06857 -row 06858 -row 06859 -row 06860 -row 06861 -row 06862 -row 06863 -row 06864 -row 06865 -row 06866 -row 06867 -row 06868 -row 06869 -row 06870 -row 06871 -row 06872 -row 06873 -row 06874 -row 06875 -row 06876 -row 06877 -row 06878 -row 06879 -row 06880 -row 06881 -row 06882 -row 06883 -row 06884 -row 06885 -row 06886 -row 06887 -row 06888 -row 06889 -row 06890 -row 06891 -row 06892 -row 06893 -row 06894 -row 06895 -row 06896 -row 06897 -row 06898 -row 06899 -row 06900 -row 06901 -row 06902 -row 06903 -row 06904 -row 06905 -row 06906 -row 06907 -row 06908 -row 06909 -row 06910 -row 06911 -row 06912 -row 06913 -row 06914 -row 06915 -row 06916 -row 06917 -row 06918 -row 06919 -row 06920 -row 06921 -row 06922 -row 06923 -row 06924 -row 06925 -row 06926 -row 06927 -row 06928 -row 06929 -row 06930 -row 06931 -row 06932 -row 06933 -row 06934 -row 06935 -row 06936 -row 06937 -row 06938 -row 06939 -row 06940 -row 06941 -row 06942 -row 06943 -row 06944 -row 06945 -row 06946 -row 06947 -row 06948 -row 06949 -row 06950 -row 06951 -row 06952 -row 06953 -row 06954 -row 06955 -row 06956 -row 06957 -row 06958 -row 06959 -row 06960 -row 06961 -row 06962 -row 06963 -row 06964 -row 06965 -row 06966 -row 06967 -row 06968 -row 06969 -row 06970 -row 06971 -row 06972 -row 06973 -row 06974 -row 06975 -row 06976 -row 06977 -row 06978 -row 06979 -row 06980 -row 06981 -row 06982 -row 06983 -row 06984 -row 06985 -row 06986 -row 06987 -row 06988 -row 06989 -row 06990 -row 06991 -row 06992 -row 06993 -row 06994 -row 06995 -row 06996 -row 06997 -row 06998 -row 06999 -row 07000 -row 07001 -row 07002 -row 07003 -row 07004 -row 07005 -row 07006 -row 07007 -row 07008 -row 07009 -row 07010 -row 07011 -row 07012 -row 07013 -row 07014 -row 07015 -row 07016 -row 07017 -row 07018 -row 07019 -row 07020 -row 07021 -row 07022 -row 07023 -row 07024 -row 07025 -row 07026 -row 07027 -row 07028 -row 07029 -row 07030 -row 07031 -row 07032 -row 07033 -row 07034 -row 07035 -row 07036 -row 07037 -row 07038 -row 07039 -row 07040 -row 07041 -row 07042 -row 07043 -row 07044 -row 07045 -row 07046 -row 07047 -row 07048 -row 07049 -row 07050 -row 07051 -row 07052 -row 07053 -row 07054 -row 07055 -row 07056 -row 07057 -row 07058 -row 07059 -row 07060 -row 07061 -row 07062 -row 07063 -row 07064 -row 07065 -row 07066 -row 07067 -row 07068 -row 07069 -row 07070 -row 07071 -row 07072 -row 07073 -row 07074 -row 07075 -row 07076 -row 07077 -row 07078 -row 07079 -row 07080 -row 07081 -row 07082 -row 07083 -row 07084 -row 07085 -row 07086 -row 07087 -row 07088 -row 07089 -row 07090 -row 07091 -row 07092 -row 07093 -row 07094 -row 07095 -row 07096 -row 07097 -row 07098 -row 07099 -row 07100 -row 07101 -row 07102 -row 07103 -row 07104 -row 07105 -row 07106 -row 07107 -row 07108 -row 07109 -row 07110 -row 07111 -row 07112 -row 07113 -row 07114 -row 07115 -row 07116 -row 07117 -row 07118 -row 07119 -row 07120 -row 07121 -row 07122 -row 07123 -row 07124 -row 07125 -row 07126 -row 07127 -row 07128 -row 07129 -row 07130 -row 07131 -row 07132 -row 07133 -row 07134 -row 07135 -row 07136 -row 07137 -row 07138 -row 07139 -row 07140 -row 07141 -row 07142 -row 07143 -row 07144 -row 07145 -row 07146 -row 07147 -row 07148 -row 07149 -row 07150 -row 07151 -row 07152 -row 07153 -row 07154 -row 07155 -row 07156 -row 07157 -row 07158 -row 07159 -row 07160 -row 07161 -row 07162 -row 07163 -row 07164 -row 07165 -row 07166 -row 07167 -row 07168 -row 07169 -row 07170 -row 07171 -row 07172 -row 07173 -row 07174 -row 07175 -row 07176 -row 07177 -row 07178 -row 07179 -row 07180 -row 07181 -row 07182 -row 07183 -row 07184 -row 07185 -row 07186 -row 07187 -row 07188 -row 07189 -row 07190 -row 07191 -row 07192 -row 07193 -row 07194 -row 07195 -row 07196 -row 07197 -row 07198 -row 07199 -row 07200 -row 07201 -row 07202 -row 07203 -row 07204 -row 07205 -row 07206 -row 07207 -row 07208 -row 07209 -row 07210 -row 07211 -row 07212 -row 07213 -row 07214 -row 07215 -row 07216 -row 07217 -row 07218 -row 07219 -row 07220 -row 07221 -row 07222 -row 07223 -row 07224 -row 07225 -row 07226 -row 07227 -row 07228 -row 07229 -row 07230 -row 07231 -row 07232 -row 07233 -row 07234 -row 07235 -row 07236 -row 07237 -row 07238 -row 07239 -row 07240 -row 07241 -row 07242 -row 07243 -row 07244 -row 07245 -row 07246 -row 07247 -row 07248 -row 07249 -row 07250 -row 07251 -row 07252 -row 07253 -row 07254 -row 07255 -row 07256 -row 07257 -row 07258 -row 07259 -row 07260 -row 07261 -row 07262 -row 07263 -row 07264 -row 07265 -row 07266 -row 07267 -row 07268 -row 07269 -row 07270 -row 07271 -row 07272 -row 07273 -row 07274 -row 07275 -row 07276 -row 07277 -row 07278 -row 07279 -row 07280 -row 07281 -row 07282 -row 07283 -row 07284 -row 07285 -row 07286 -row 07287 -row 07288 -row 07289 -row 07290 -row 07291 -row 07292 -row 07293 -row 07294 -row 07295 -row 07296 -row 07297 -row 07298 -row 07299 -row 07300 -row 07301 -row 07302 -row 07303 -row 07304 -row 07305 -row 07306 -row 07307 -row 07308 -row 07309 -row 07310 -row 07311 -row 07312 -row 07313 -row 07314 -row 07315 -row 07316 -row 07317 -row 07318 -row 07319 -row 07320 -row 07321 -row 07322 -row 07323 -row 07324 -row 07325 -row 07326 -row 07327 -row 07328 -row 07329 -row 07330 -row 07331 -row 07332 -row 07333 -row 07334 -row 07335 -row 07336 -row 07337 -row 07338 -row 07339 -row 07340 -row 07341 -row 07342 -row 07343 -row 07344 -row 07345 -row 07346 -row 07347 -row 07348 -row 07349 -row 07350 -row 07351 -row 07352 -row 07353 -row 07354 -row 07355 -row 07356 -row 07357 -row 07358 -row 07359 -row 07360 -row 07361 -row 07362 -row 07363 -row 07364 -row 07365 -row 07366 -row 07367 -row 07368 -row 07369 -row 07370 -row 07371 -row 07372 -row 07373 -row 07374 -row 07375 -row 07376 -row 07377 -row 07378 -row 07379 -row 07380 -row 07381 -row 07382 -row 07383 -row 07384 -row 07385 -row 07386 -row 07387 -row 07388 -row 07389 -row 07390 -row 07391 -row 07392 -row 07393 -row 07394 -row 07395 -row 07396 -row 07397 -row 07398 -row 07399 -row 07400 -row 07401 -row 07402 -row 07403 -row 07404 -row 07405 -row 07406 -row 07407 -row 07408 -row 07409 -row 07410 -row 07411 -row 07412 -row 07413 -row 07414 -row 07415 -row 07416 -row 07417 -row 07418 -row 07419 -row 07420 -row 07421 -row 07422 -row 07423 -row 07424 -row 07425 -row 07426 -row 07427 -row 07428 -row 07429 -row 07430 -row 07431 -row 07432 -row 07433 -row 07434 -row 07435 -row 07436 -row 07437 -row 07438 -row 07439 -row 07440 -row 07441 -row 07442 -row 07443 -row 07444 -row 07445 -row 07446 -row 07447 -row 07448 -row 07449 -row 07450 -row 07451 -row 07452 -row 07453 -row 07454 -row 07455 -row 07456 -row 07457 -row 07458 -row 07459 -row 07460 -row 07461 -row 07462 -row 07463 -row 07464 -row 07465 -row 07466 -row 07467 -row 07468 -row 07469 -row 07470 -row 07471 -row 07472 -row 07473 -row 07474 -row 07475 -row 07476 -row 07477 -row 07478 -row 07479 -row 07480 -row 07481 -row 07482 -row 07483 -row 07484 -row 07485 -row 07486 -row 07487 -row 07488 -row 07489 -row 07490 -row 07491 -row 07492 -row 07493 -row 07494 -row 07495 -row 07496 -row 07497 -row 07498 -row 07499 -row 07500 -row 07501 -row 07502 -row 07503 -row 07504 -row 07505 -row 07506 -row 07507 -row 07508 -row 07509 -row 07510 -row 07511 -row 07512 -row 07513 -row 07514 -row 07515 -row 07516 -row 07517 -row 07518 -row 07519 -row 07520 -row 07521 -row 07522 -row 07523 -row 07524 -row 07525 -row 07526 -row 07527 -row 07528 -row 07529 -row 07530 -row 07531 -row 07532 -row 07533 -row 07534 -row 07535 -row 07536 -row 07537 -row 07538 -row 07539 -row 07540 -row 07541 -row 07542 -row 07543 -row 07544 -row 07545 -row 07546 -row 07547 -row 07548 -row 07549 -row 07550 -row 07551 -row 07552 -row 07553 -row 07554 -row 07555 -row 07556 -row 07557 -row 07558 -row 07559 -row 07560 -row 07561 -row 07562 -row 07563 -row 07564 -row 07565 -row 07566 -row 07567 -row 07568 -row 07569 -row 07570 -row 07571 -row 07572 -row 07573 -row 07574 -row 07575 -row 07576 -row 07577 -row 07578 -row 07579 -row 07580 -row 07581 -row 07582 -row 07583 -row 07584 -row 07585 -row 07586 -row 07587 -row 07588 -row 07589 -row 07590 -row 07591 -row 07592 -row 07593 -row 07594 -row 07595 -row 07596 -row 07597 -row 07598 -row 07599 -row 07600 -row 07601 -row 07602 -row 07603 -row 07604 -row 07605 -row 07606 -row 07607 -row 07608 -row 07609 -row 07610 -row 07611 -row 07612 -row 07613 -row 07614 -row 07615 -row 07616 -row 07617 -row 07618 -row 07619 -row 07620 -row 07621 -row 07622 -row 07623 -row 07624 -row 07625 -row 07626 -row 07627 -row 07628 -row 07629 -row 07630 -row 07631 -row 07632 -row 07633 -row 07634 -row 07635 -row 07636 -row 07637 -row 07638 -row 07639 -row 07640 -row 07641 -row 07642 -row 07643 -row 07644 -row 07645 -row 07646 -row 07647 -row 07648 -row 07649 -row 07650 -row 07651 -row 07652 -row 07653 -row 07654 -row 07655 -row 07656 -row 07657 -row 07658 -row 07659 -row 07660 -row 07661 -row 07662 -row 07663 -row 07664 -row 07665 -row 07666 -row 07667 -row 07668 -row 07669 -row 07670 -row 07671 -row 07672 -row 07673 -row 07674 -row 07675 -row 07676 -row 07677 -row 07678 -row 07679 -row 07680 -row 07681 -row 07682 -row 07683 -row 07684 -row 07685 -row 07686 -row 07687 -row 07688 -row 07689 -row 07690 -row 07691 -row 07692 -row 07693 -row 07694 -row 07695 -row 07696 -row 07697 -row 07698 -row 07699 -row 07700 -row 07701 -row 07702 -row 07703 -row 07704 -row 07705 -row 07706 -row 07707 -row 07708 -row 07709 -row 07710 -row 07711 -row 07712 -row 07713 -row 07714 -row 07715 -row 07716 -row 07717 -row 07718 -row 07719 -row 07720 -row 07721 -row 07722 -row 07723 -row 07724 -row 07725 -row 07726 -row 07727 -row 07728 -row 07729 -row 07730 -row 07731 -row 07732 -row 07733 -row 07734 -row 07735 -row 07736 -row 07737 -row 07738 -row 07739 -row 07740 -row 07741 -row 07742 -row 07743 -row 07744 -row 07745 -row 07746 -row 07747 -row 07748 -row 07749 -row 07750 -row 07751 -row 07752 -row 07753 -row 07754 -row 07755 -row 07756 -row 07757 -row 07758 -row 07759 -row 07760 -row 07761 -row 07762 -row 07763 -row 07764 -row 07765 -row 07766 -row 07767 -row 07768 -row 07769 -row 07770 -row 07771 -row 07772 -row 07773 -row 07774 -row 07775 -row 07776 -row 07777 -row 07778 -row 07779 -row 07780 -row 07781 -row 07782 -row 07783 -row 07784 -row 07785 -row 07786 -row 07787 -row 07788 -row 07789 -row 07790 -row 07791 -row 07792 -row 07793 -row 07794 -row 07795 -row 07796 -row 07797 -row 07798 -row 07799 -row 07800 -row 07801 -row 07802 -row 07803 -row 07804 -row 07805 -row 07806 -row 07807 -row 07808 -row 07809 -row 07810 -row 07811 -row 07812 -row 07813 -row 07814 -row 07815 -row 07816 -row 07817 -row 07818 -row 07819 -row 07820 -row 07821 -row 07822 -row 07823 -row 07824 -row 07825 -row 07826 -row 07827 -row 07828 -row 07829 -row 07830 -row 07831 -row 07832 -row 07833 -row 07834 -row 07835 -row 07836 -row 07837 -row 07838 -row 07839 -row 07840 -row 07841 -row 07842 -row 07843 -row 07844 -row 07845 -row 07846 -row 07847 -row 07848 -row 07849 -row 07850 -row 07851 -row 07852 -row 07853 -row 07854 -row 07855 -row 07856 -row 07857 -row 07858 -row 07859 -row 07860 -row 07861 -row 07862 -row 07863 -row 07864 -row 07865 -row 07866 -row 07867 -row 07868 -row 07869 -row 07870 -row 07871 -row 07872 -row 07873 -row 07874 -row 07875 -row 07876 -row 07877 -row 07878 -row 07879 -row 07880 -row 07881 -row 07882 -row 07883 -row 07884 -row 07885 -row 07886 -row 07887 -row 07888 -row 07889 -row 07890 -row 07891 -row 07892 -row 07893 -row 07894 -row 07895 -row 07896 -row 07897 -row 07898 -row 07899 -row 07900 -row 07901 -row 07902 -row 07903 -row 07904 -row 07905 -row 07906 -row 07907 -row 07908 -row 07909 -row 07910 -row 07911 -row 07912 -row 07913 -row 07914 -row 07915 -row 07916 -row 07917 -row 07918 -row 07919 -row 07920 -row 07921 -row 07922 -row 07923 -row 07924 -row 07925 -row 07926 -row 07927 -row 07928 -row 07929 -row 07930 -row 07931 -row 07932 -row 07933 -row 07934 -row 07935 -row 07936 -row 07937 -row 07938 -row 07939 -row 07940 -row 07941 -row 07942 -row 07943 -row 07944 -row 07945 -row 07946 -row 07947 -row 07948 -row 07949 -row 07950 -row 07951 -row 07952 -row 07953 -row 07954 -row 07955 -row 07956 -row 07957 -row 07958 -row 07959 -row 07960 -row 07961 -row 07962 -row 07963 -row 07964 -row 07965 -row 07966 -row 07967 -row 07968 -row 07969 -row 07970 -row 07971 -row 07972 -row 07973 -row 07974 -row 07975 -row 07976 -row 07977 -row 07978 -row 07979 -row 07980 -row 07981 -row 07982 -row 07983 -row 07984 -row 07985 -row 07986 -row 07987 -row 07988 -row 07989 -row 07990 -row 07991 -row 07992 -row 07993 -row 07994 -row 07995 -row 07996 -row 07997 -row 07998 -row 07999 -row 08000 -row 08001 -row 08002 -row 08003 -row 08004 -row 08005 -row 08006 -row 08007 -row 08008 -row 08009 -row 08010 -row 08011 -row 08012 -row 08013 -row 08014 -row 08015 -row 08016 -row 08017 -row 08018 -row 08019 -row 08020 -row 08021 -row 08022 -row 08023 -row 08024 -row 08025 -row 08026 -row 08027 -row 08028 -row 08029 -row 08030 -row 08031 -row 08032 -row 08033 -row 08034 -row 08035 -row 08036 -row 08037 -row 08038 -row 08039 -row 08040 -row 08041 -row 08042 -row 08043 -row 08044 -row 08045 -row 08046 -row 08047 -row 08048 -row 08049 -row 08050 -row 08051 -row 08052 -row 08053 -row 08054 -row 08055 -row 08056 -row 08057 -row 08058 -row 08059 -row 08060 -row 08061 -row 08062 -row 08063 -row 08064 -row 08065 -row 08066 -row 08067 -row 08068 -row 08069 -row 08070 -row 08071 -row 08072 -row 08073 -row 08074 -row 08075 -row 08076 -row 08077 -row 08078 -row 08079 -row 08080 -row 08081 -row 08082 -row 08083 -row 08084 -row 08085 -row 08086 -row 08087 -row 08088 -row 08089 -row 08090 -row 08091 -row 08092 -row 08093 -row 08094 -row 08095 -row 08096 -row 08097 -row 08098 -row 08099 -row 08100 -row 08101 -row 08102 -row 08103 -row 08104 -row 08105 -row 08106 -row 08107 -row 08108 -row 08109 -row 08110 -row 08111 -row 08112 -row 08113 -row 08114 -row 08115 -row 08116 -row 08117 -row 08118 -row 08119 -row 08120 -row 08121 -row 08122 -row 08123 -row 08124 -row 08125 -row 08126 -row 08127 -row 08128 -row 08129 -row 08130 -row 08131 -row 08132 -row 08133 -row 08134 -row 08135 -row 08136 -row 08137 -row 08138 -row 08139 -row 08140 -row 08141 -row 08142 -row 08143 -row 08144 -row 08145 -row 08146 -row 08147 -row 08148 -row 08149 -row 08150 -row 08151 -row 08152 -row 08153 -row 08154 -row 08155 -row 08156 -row 08157 -row 08158 -row 08159 -row 08160 -row 08161 -row 08162 -row 08163 -row 08164 -row 08165 -row 08166 -row 08167 -row 08168 -row 08169 -row 08170 -row 08171 -row 08172 -row 08173 -row 08174 -row 08175 -row 08176 -row 08177 -row 08178 -row 08179 -row 08180 -row 08181 -row 08182 -row 08183 -row 08184 -row 08185 -row 08186 -row 08187 -row 08188 -row 08189 -row 08190 -row 08191 -row 08192 -row 08193 -row 08194 -row 08195 -row 08196 -row 08197 -row 08198 -row 08199 -row 08200 -row 08201 -row 08202 -row 08203 -row 08204 -row 08205 -row 08206 -row 08207 -row 08208 -row 08209 -row 08210 -row 08211 -row 08212 -row 08213 -row 08214 -row 08215 -row 08216 -row 08217 -row 08218 -row 08219 -row 08220 -row 08221 -row 08222 -row 08223 -row 08224 -row 08225 -row 08226 -row 08227 -row 08228 -row 08229 -row 08230 -row 08231 -row 08232 -row 08233 -row 08234 -row 08235 -row 08236 -row 08237 -row 08238 -row 08239 -row 08240 -row 08241 -row 08242 -row 08243 -row 08244 -row 08245 -row 08246 -row 08247 -row 08248 -row 08249 -row 08250 -row 08251 -row 08252 -row 08253 -row 08254 -row 08255 -row 08256 -row 08257 -row 08258 -row 08259 -row 08260 -row 08261 -row 08262 -row 08263 -row 08264 -row 08265 -row 08266 -row 08267 -row 08268 -row 08269 -row 08270 -row 08271 -row 08272 -row 08273 -row 08274 -row 08275 -row 08276 -row 08277 -row 08278 -row 08279 -row 08280 -row 08281 -row 08282 -row 08283 -row 08284 -row 08285 -row 08286 -row 08287 -row 08288 -row 08289 -row 08290 -row 08291 -row 08292 -row 08293 -row 08294 -row 08295 -row 08296 -row 08297 -row 08298 -row 08299 -row 08300 -row 08301 -row 08302 -row 08303 -row 08304 -row 08305 -row 08306 -row 08307 -row 08308 -row 08309 -row 08310 -row 08311 -row 08312 -row 08313 -row 08314 -row 08315 -row 08316 -row 08317 -row 08318 -row 08319 -row 08320 -row 08321 -row 08322 -row 08323 -row 08324 -row 08325 -row 08326 -row 08327 -row 08328 -row 08329 -row 08330 -row 08331 -row 08332 -row 08333 -row 08334 -row 08335 -row 08336 -row 08337 -row 08338 -row 08339 -row 08340 -row 08341 -row 08342 -row 08343 -row 08344 -row 08345 -row 08346 -row 08347 -row 08348 -row 08349 -row 08350 -row 08351 -row 08352 -row 08353 -row 08354 -row 08355 -row 08356 -row 08357 -row 08358 -row 08359 -row 08360 -row 08361 -row 08362 -row 08363 -row 08364 -row 08365 -row 08366 -row 08367 -row 08368 -row 08369 -row 08370 -row 08371 -row 08372 -row 08373 -row 08374 -row 08375 -row 08376 -row 08377 -row 08378 -row 08379 -row 08380 -row 08381 -row 08382 -row 08383 -row 08384 -row 08385 -row 08386 -row 08387 -row 08388 -row 08389 -row 08390 -row 08391 -row 08392 -row 08393 -row 08394 -row 08395 -row 08396 -row 08397 -row 08398 -row 08399 -row 08400 -row 08401 -row 08402 -row 08403 -row 08404 -row 08405 -row 08406 -row 08407 -row 08408 -row 08409 -row 08410 -row 08411 -row 08412 -row 08413 -row 08414 -row 08415 -row 08416 -row 08417 -row 08418 -row 08419 -row 08420 -row 08421 -row 08422 -row 08423 -row 08424 -row 08425 -row 08426 -row 08427 -row 08428 -row 08429 -row 08430 -row 08431 -row 08432 -row 08433 -row 08434 -row 08435 -row 08436 -row 08437 -row 08438 -row 08439 -row 08440 -row 08441 -row 08442 -row 08443 -row 08444 -row 08445 -row 08446 -row 08447 -row 08448 -row 08449 -row 08450 -row 08451 -row 08452 -row 08453 -row 08454 -row 08455 -row 08456 -row 08457 -row 08458 -row 08459 -row 08460 -row 08461 -row 08462 -row 08463 -row 08464 -row 08465 -row 08466 -row 08467 -row 08468 -row 08469 -row 08470 -row 08471 -row 08472 -row 08473 -row 08474 -row 08475 -row 08476 -row 08477 -row 08478 -row 08479 -row 08480 -row 08481 -row 08482 -row 08483 -row 08484 -row 08485 -row 08486 -row 08487 -row 08488 -row 08489 -row 08490 -row 08491 -row 08492 -row 08493 -row 08494 -row 08495 -row 08496 -row 08497 -row 08498 -row 08499 -row 08500 -row 08501 -row 08502 -row 08503 -row 08504 -row 08505 -row 08506 -row 08507 -row 08508 -row 08509 -row 08510 -row 08511 -row 08512 -row 08513 -row 08514 -row 08515 -row 08516 -row 08517 -row 08518 -row 08519 -row 08520 -row 08521 -row 08522 -row 08523 -row 08524 -row 08525 -row 08526 -row 08527 -row 08528 -row 08529 -row 08530 -row 08531 -row 08532 -row 08533 -row 08534 -row 08535 -row 08536 -row 08537 -row 08538 -row 08539 -row 08540 -row 08541 -row 08542 -row 08543 -row 08544 -row 08545 -row 08546 -row 08547 -row 08548 -row 08549 -row 08550 -row 08551 -row 08552 -row 08553 -row 08554 -row 08555 -row 08556 -row 08557 -row 08558 -row 08559 -row 08560 -row 08561 -row 08562 -row 08563 -row 08564 -row 08565 -row 08566 -row 08567 -row 08568 -row 08569 -row 08570 -row 08571 -row 08572 -row 08573 -row 08574 -row 08575 -row 08576 -row 08577 -row 08578 -row 08579 -row 08580 -row 08581 -row 08582 -row 08583 -row 08584 -row 08585 -row 08586 -row 08587 -row 08588 -row 08589 -row 08590 -row 08591 -row 08592 -row 08593 -row 08594 -row 08595 -row 08596 -row 08597 -row 08598 -row 08599 -row 08600 -row 08601 -row 08602 -row 08603 -row 08604 -row 08605 -row 08606 -row 08607 -row 08608 -row 08609 -row 08610 -row 08611 -row 08612 -row 08613 -row 08614 -row 08615 -row 08616 -row 08617 -row 08618 -row 08619 -row 08620 -row 08621 -row 08622 -row 08623 -row 08624 -row 08625 -row 08626 -row 08627 -row 08628 -row 08629 -row 08630 -row 08631 -row 08632 -row 08633 -row 08634 -row 08635 -row 08636 -row 08637 -row 08638 -row 08639 -row 08640 -row 08641 -row 08642 -row 08643 -row 08644 -row 08645 -row 08646 -row 08647 -row 08648 -row 08649 -row 08650 -row 08651 -row 08652 -row 08653 -row 08654 -row 08655 -row 08656 -row 08657 -row 08658 -row 08659 -row 08660 -row 08661 -row 08662 -row 08663 -row 08664 -row 08665 -row 08666 -row 08667 -row 08668 -row 08669 -row 08670 -row 08671 -row 08672 -row 08673 -row 08674 -row 08675 -row 08676 -row 08677 -row 08678 -row 08679 -row 08680 -row 08681 -row 08682 -row 08683 -row 08684 -row 08685 -row 08686 -row 08687 -row 08688 -row 08689 -row 08690 -row 08691 -row 08692 -row 08693 -row 08694 -row 08695 -row 08696 -row 08697 -row 08698 -row 08699 -row 08700 -row 08701 -row 08702 -row 08703 -row 08704 -row 08705 -row 08706 -row 08707 -row 08708 -row 08709 -row 08710 -row 08711 -row 08712 -row 08713 -row 08714 -row 08715 -row 08716 -row 08717 -row 08718 -row 08719 -row 08720 -row 08721 -row 08722 -row 08723 -row 08724 -row 08725 -row 08726 -row 08727 -row 08728 -row 08729 -row 08730 -row 08731 -row 08732 -row 08733 -row 08734 -row 08735 -row 08736 -row 08737 -row 08738 -row 08739 -row 08740 -row 08741 -row 08742 -row 08743 -row 08744 -row 08745 -row 08746 -row 08747 -row 08748 -row 08749 -row 08750 -row 08751 -row 08752 -row 08753 -row 08754 -row 08755 -row 08756 -row 08757 -row 08758 -row 08759 -row 08760 -row 08761 -row 08762 -row 08763 -row 08764 -row 08765 -row 08766 -row 08767 -row 08768 -row 08769 -row 08770 -row 08771 -row 08772 -row 08773 -row 08774 -row 08775 -row 08776 -row 08777 -row 08778 -row 08779 -row 08780 -row 08781 -row 08782 -row 08783 -row 08784 -row 08785 -row 08786 -row 08787 -row 08788 -row 08789 -row 08790 -row 08791 -row 08792 -row 08793 -row 08794 -row 08795 -row 08796 -row 08797 -row 08798 -row 08799 -row 08800 -row 08801 -row 08802 -row 08803 -row 08804 -row 08805 -row 08806 -row 08807 -row 08808 -row 08809 -row 08810 -row 08811 -row 08812 -row 08813 -row 08814 -row 08815 -row 08816 -row 08817 -row 08818 -row 08819 -row 08820 -row 08821 -row 08822 -row 08823 -row 08824 -row 08825 -row 08826 -row 08827 -row 08828 -row 08829 -row 08830 -row 08831 -row 08832 -row 08833 -row 08834 -row 08835 -row 08836 -row 08837 -row 08838 -row 08839 -row 08840 -row 08841 -row 08842 -row 08843 -row 08844 -row 08845 -row 08846 -row 08847 -row 08848 -row 08849 -row 08850 -row 08851 -row 08852 -row 08853 -row 08854 -row 08855 -row 08856 -row 08857 -row 08858 -row 08859 -row 08860 -row 08861 -row 08862 -row 08863 -row 08864 -row 08865 -row 08866 -row 08867 -row 08868 -row 08869 -row 08870 -row 08871 -row 08872 -row 08873 -row 08874 -row 08875 -row 08876 -row 08877 -row 08878 -row 08879 -row 08880 -row 08881 -row 08882 -row 08883 -row 08884 -row 08885 -row 08886 -row 08887 -row 08888 -row 08889 -row 08890 -row 08891 -row 08892 -row 08893 -row 08894 -row 08895 -row 08896 -row 08897 -row 08898 -row 08899 -row 08900 -row 08901 -row 08902 -row 08903 -row 08904 -row 08905 -row 08906 -row 08907 -row 08908 -row 08909 -row 08910 -row 08911 -row 08912 -row 08913 -row 08914 -row 08915 -row 08916 -row 08917 -row 08918 -row 08919 -row 08920 -row 08921 -row 08922 -row 08923 -row 08924 -row 08925 -row 08926 -row 08927 -row 08928 -row 08929 -row 08930 -row 08931 -row 08932 -row 08933 -row 08934 -row 08935 -row 08936 -row 08937 -row 08938 -row 08939 -row 08940 -row 08941 -row 08942 -row 08943 -row 08944 -row 08945 -row 08946 -row 08947 -row 08948 -row 08949 -row 08950 -row 08951 -row 08952 -row 08953 -row 08954 -row 08955 -row 08956 -row 08957 -row 08958 -row 08959 -row 08960 -row 08961 -row 08962 -row 08963 -row 08964 -row 08965 -row 08966 -row 08967 -row 08968 -row 08969 -row 08970 -row 08971 -row 08972 -row 08973 -row 08974 -row 08975 -row 08976 -row 08977 -row 08978 -row 08979 -row 08980 -row 08981 -row 08982 -row 08983 -row 08984 -row 08985 -row 08986 -row 08987 -row 08988 -row 08989 -row 08990 -row 08991 -row 08992 -row 08993 -row 08994 -row 08995 -row 08996 -row 08997 -row 08998 -row 08999 -row 09000 -row 09001 -row 09002 -row 09003 -row 09004 -row 09005 -row 09006 -row 09007 -row 09008 -row 09009 -row 09010 -row 09011 -row 09012 -row 09013 -row 09014 -row 09015 -row 09016 -row 09017 -row 09018 -row 09019 -row 09020 -row 09021 -row 09022 -row 09023 -row 09024 -row 09025 -row 09026 -row 09027 -row 09028 -row 09029 -row 09030 -row 09031 -row 09032 -row 09033 -row 09034 -row 09035 -row 09036 -row 09037 -row 09038 -row 09039 -row 09040 -row 09041 -row 09042 -row 09043 -row 09044 -row 09045 -row 09046 -row 09047 -row 09048 -row 09049 -row 09050 -row 09051 -row 09052 -row 09053 -row 09054 -row 09055 -row 09056 -row 09057 -row 09058 -row 09059 -row 09060 -row 09061 -row 09062 -row 09063 -row 09064 -row 09065 -row 09066 -row 09067 -row 09068 -row 09069 -row 09070 -row 09071 -row 09072 -row 09073 -row 09074 -row 09075 -row 09076 -row 09077 -row 09078 -row 09079 -row 09080 -row 09081 -row 09082 -row 09083 -row 09084 -row 09085 -row 09086 -row 09087 -row 09088 -row 09089 -row 09090 -row 09091 -row 09092 -row 09093 -row 09094 -row 09095 -row 09096 -row 09097 -row 09098 -row 09099 -row 09100 -row 09101 -row 09102 -row 09103 -row 09104 -row 09105 -row 09106 -row 09107 -row 09108 -row 09109 -row 09110 -row 09111 -row 09112 -row 09113 -row 09114 -row 09115 -row 09116 -row 09117 -row 09118 -row 09119 -row 09120 -row 09121 -row 09122 -row 09123 -row 09124 -row 09125 -row 09126 -row 09127 -row 09128 -row 09129 -row 09130 -row 09131 -row 09132 -row 09133 -row 09134 -row 09135 -row 09136 -row 09137 -row 09138 -row 09139 -row 09140 -row 09141 -row 09142 -row 09143 -row 09144 -row 09145 -row 09146 -row 09147 -row 09148 -row 09149 -row 09150 -row 09151 -row 09152 -row 09153 -row 09154 -row 09155 -row 09156 -row 09157 -row 09158 -row 09159 -row 09160 -row 09161 -row 09162 -row 09163 -row 09164 -row 09165 -row 09166 -row 09167 -row 09168 -row 09169 -row 09170 -row 09171 -row 09172 -row 09173 -row 09174 -row 09175 -row 09176 -row 09177 -row 09178 -row 09179 -row 09180 -row 09181 -row 09182 -row 09183 -row 09184 -row 09185 -row 09186 -row 09187 -row 09188 -row 09189 -row 09190 -row 09191 -row 09192 -row 09193 -row 09194 -row 09195 -row 09196 -row 09197 -row 09198 -row 09199 -row 09200 -row 09201 -row 09202 -row 09203 -row 09204 -row 09205 -row 09206 -row 09207 -row 09208 -row 09209 -row 09210 -row 09211 -row 09212 -row 09213 -row 09214 -row 09215 -row 09216 -row 09217 -row 09218 -row 09219 -row 09220 -row 09221 -row 09222 -row 09223 -row 09224 -row 09225 -row 09226 -row 09227 -row 09228 -row 09229 -row 09230 -row 09231 -row 09232 -row 09233 -row 09234 -row 09235 -row 09236 -row 09237 -row 09238 -row 09239 -row 09240 -row 09241 -row 09242 -row 09243 -row 09244 -row 09245 -row 09246 -row 09247 -row 09248 -row 09249 -row 09250 -row 09251 -row 09252 -row 09253 -row 09254 -row 09255 -row 09256 -row 09257 -row 09258 -row 09259 -row 09260 -row 09261 -row 09262 -row 09263 -row 09264 -row 09265 -row 09266 -row 09267 -row 09268 -row 09269 -row 09270 -row 09271 -row 09272 -row 09273 -row 09274 -row 09275 -row 09276 -row 09277 -row 09278 -row 09279 -row 09280 -row 09281 -row 09282 -row 09283 -row 09284 -row 09285 -row 09286 -row 09287 -row 09288 -row 09289 -row 09290 -row 09291 -row 09292 -row 09293 -row 09294 -row 09295 -row 09296 -row 09297 -row 09298 -row 09299 -row 09300 -row 09301 -row 09302 -row 09303 -row 09304 -row 09305 -row 09306 -row 09307 -row 09308 -row 09309 -row 09310 -row 09311 -row 09312 -row 09313 -row 09314 -row 09315 -row 09316 -row 09317 -row 09318 -row 09319 -row 09320 -row 09321 -row 09322 -row 09323 -row 09324 -row 09325 -row 09326 -row 09327 -row 09328 -row 09329 -row 09330 -row 09331 -row 09332 -row 09333 -row 09334 -row 09335 -row 09336 -row 09337 -row 09338 -row 09339 -row 09340 -row 09341 -row 09342 -row 09343 -row 09344 -row 09345 -row 09346 -row 09347 -row 09348 -row 09349 -row 09350 -row 09351 -row 09352 -row 09353 -row 09354 -row 09355 -row 09356 -row 09357 -row 09358 -row 09359 -row 09360 -row 09361 -row 09362 -row 09363 -row 09364 -row 09365 -row 09366 -row 09367 -row 09368 -row 09369 -row 09370 -row 09371 -row 09372 -row 09373 -row 09374 -row 09375 -row 09376 -row 09377 -row 09378 -row 09379 -row 09380 -row 09381 -row 09382 -row 09383 -row 09384 -row 09385 -row 09386 -row 09387 -row 09388 -row 09389 -row 09390 -row 09391 -row 09392 -row 09393 -row 09394 -row 09395 -row 09396 -row 09397 -row 09398 -row 09399 -row 09400 -row 09401 -row 09402 -row 09403 -row 09404 -row 09405 -row 09406 -row 09407 -row 09408 -row 09409 -row 09410 -row 09411 -row 09412 -row 09413 -row 09414 -row 09415 -row 09416 -row 09417 -row 09418 -row 09419 -row 09420 -row 09421 -row 09422 -row 09423 -row 09424 -row 09425 -row 09426 -row 09427 -row 09428 -row 09429 -row 09430 -row 09431 -row 09432 -row 09433 -row 09434 -row 09435 -row 09436 -row 09437 -row 09438 -row 09439 -row 09440 -row 09441 -row 09442 -row 09443 -row 09444 -row 09445 -row 09446 -row 09447 -row 09448 -row 09449 -row 09450 -row 09451 -row 09452 -row 09453 -row 09454 -row 09455 -row 09456 -row 09457 -row 09458 -row 09459 -row 09460 -row 09461 -row 09462 -row 09463 -row 09464 -row 09465 -row 09466 -row 09467 -row 09468 -row 09469 -row 09470 -row 09471 -row 09472 -row 09473 -row 09474 -row 09475 -row 09476 -row 09477 -row 09478 -row 09479 -row 09480 -row 09481 -row 09482 -row 09483 -row 09484 -row 09485 -row 09486 -row 09487 -row 09488 -row 09489 -row 09490 -row 09491 -row 09492 -row 09493 -row 09494 -row 09495 -row 09496 -row 09497 -row 09498 -row 09499 -row 09500 -row 09501 -row 09502 -row 09503 -row 09504 -row 09505 -row 09506 -row 09507 -row 09508 -row 09509 -row 09510 -row 09511 -row 09512 -row 09513 -row 09514 -row 09515 -row 09516 -row 09517 -row 09518 -row 09519 -row 09520 -row 09521 -row 09522 -row 09523 -row 09524 -row 09525 -row 09526 -row 09527 -row 09528 -row 09529 -row 09530 -row 09531 -row 09532 -row 09533 -row 09534 -row 09535 -row 09536 -row 09537 -row 09538 -row 09539 -row 09540 -row 09541 -row 09542 -row 09543 -row 09544 -row 09545 -row 09546 -row 09547 -row 09548 -row 09549 -row 09550 -row 09551 -row 09552 -row 09553 -row 09554 -row 09555 -row 09556 -row 09557 -row 09558 -row 09559 -row 09560 -row 09561 -row 09562 -row 09563 -row 09564 -row 09565 -row 09566 -row 09567 -row 09568 -row 09569 -row 09570 -row 09571 -row 09572 -row 09573 -row 09574 -row 09575 -row 09576 -row 09577 -row 09578 -row 09579 -row 09580 -row 09581 -row 09582 -row 09583 -row 09584 -row 09585 -row 09586 -row 09587 -row 09588 -row 09589 -row 09590 -row 09591 -row 09592 -row 09593 -row 09594 -row 09595 -row 09596 -row 09597 -row 09598 -row 09599 -row 09600 -row 09601 -row 09602 -row 09603 -row 09604 -row 09605 -row 09606 -row 09607 -row 09608 -row 09609 -row 09610 -row 09611 -row 09612 -row 09613 -row 09614 -row 09615 -row 09616 -row 09617 -row 09618 -row 09619 -row 09620 -row 09621 -row 09622 -row 09623 -row 09624 -row 09625 -row 09626 -row 09627 -row 09628 -row 09629 -row 09630 -row 09631 -row 09632 -row 09633 -row 09634 -row 09635 -row 09636 -row 09637 -row 09638 -row 09639 -row 09640 -row 09641 -row 09642 -row 09643 -row 09644 -row 09645 -row 09646 -row 09647 -row 09648 -row 09649 -row 09650 -row 09651 -row 09652 -row 09653 -row 09654 -row 09655 -row 09656 -row 09657 -row 09658 -row 09659 -row 09660 -row 09661 -row 09662 -row 09663 -row 09664 -row 09665 -row 09666 -row 09667 -row 09668 -row 09669 -row 09670 -row 09671 -row 09672 -row 09673 -row 09674 -row 09675 -row 09676 -row 09677 -row 09678 -row 09679 -row 09680 -row 09681 -row 09682 -row 09683 -row 09684 -row 09685 -row 09686 -row 09687 -row 09688 -row 09689 -row 09690 -row 09691 -row 09692 -row 09693 -row 09694 -row 09695 -row 09696 -row 09697 -row 09698 -row 09699 -row 09700 -row 09701 -row 09702 -row 09703 -row 09704 -row 09705 -row 09706 -row 09707 -row 09708 -row 09709 -row 09710 -row 09711 -row 09712 -row 09713 -row 09714 -row 09715 -row 09716 -row 09717 -row 09718 -row 09719 -row 09720 -row 09721 -row 09722 -row 09723 -row 09724 -row 09725 -row 09726 -row 09727 -row 09728 -row 09729 -row 09730 -row 09731 -row 09732 -row 09733 -row 09734 -row 09735 -row 09736 -row 09737 -row 09738 -row 09739 -row 09740 -row 09741 -row 09742 -row 09743 -row 09744 -row 09745 -row 09746 -row 09747 -row 09748 -row 09749 -row 09750 -row 09751 -row 09752 -row 09753 -row 09754 -row 09755 -row 09756 -row 09757 -row 09758 -row 09759 -row 09760 -row 09761 -row 09762 -row 09763 -row 09764 -row 09765 -row 09766 -row 09767 -row 09768 -row 09769 -row 09770 -row 09771 -row 09772 -row 09773 -row 09774 -row 09775 -row 09776 -row 09777 -row 09778 -row 09779 -row 09780 -row 09781 -row 09782 -row 09783 -row 09784 -row 09785 -row 09786 -row 09787 -row 09788 -row 09789 -row 09790 -row 09791 -row 09792 -row 09793 -row 09794 -row 09795 -row 09796 -row 09797 -row 09798 -row 09799 -row 09800 -row 09801 -row 09802 -row 09803 -row 09804 -row 09805 -row 09806 -row 09807 -row 09808 -row 09809 -row 09810 -row 09811 -row 09812 -row 09813 -row 09814 -row 09815 -row 09816 -row 09817 -row 09818 -row 09819 -row 09820 -row 09821 -row 09822 -row 09823 -row 09824 -row 09825 -row 09826 -row 09827 -row 09828 -row 09829 -row 09830 -row 09831 -row 09832 -row 09833 -row 09834 -row 09835 -row 09836 -row 09837 -row 09838 -row 09839 -row 09840 -row 09841 -row 09842 -row 09843 -row 09844 -row 09845 -row 09846 -row 09847 -row 09848 -row 09849 -row 09850 -row 09851 -row 09852 -row 09853 -row 09854 -row 09855 -row 09856 -row 09857 -row 09858 -row 09859 -row 09860 -row 09861 -row 09862 -row 09863 -row 09864 -row 09865 -row 09866 -row 09867 -row 09868 -row 09869 -row 09870 -row 09871 -row 09872 -row 09873 -row 09874 -row 09875 -row 09876 -row 09877 -row 09878 -row 09879 -row 09880 -row 09881 -row 09882 -row 09883 -row 09884 -row 09885 -row 09886 -row 09887 -row 09888 -row 09889 -row 09890 -row 09891 -row 09892 -row 09893 -row 09894 -row 09895 -row 09896 -row 09897 -row 09898 -row 09899 -row 09900 -row 09901 -row 09902 -row 09903 -row 09904 -row 09905 -row 09906 -row 09907 -row 09908 -row 09909 -row 09910 -row 09911 -row 09912 -row 09913 -row 09914 -row 09915 -row 09916 -row 09917 -row 09918 -row 09919 -row 09920 -row 09921 -row 09922 -row 09923 -row 09924 -row 09925 -row 09926 -row 09927 -row 09928 -row 09929 -row 09930 -row 09931 -row 09932 -row 09933 -row 09934 -row 09935 -row 09936 -row 09937 -row 09938 -row 09939 -row 09940 -row 09941 -row 09942 -row 09943 -row 09944 -row 09945 -row 09946 -row 09947 -row 09948 -row 09949 -row 09950 -row 09951 -row 09952 -row 09953 -row 09954 -row 09955 -row 09956 -row 09957 -row 09958 -row 09959 -row 09960 -row 09961 -row 09962 -row 09963 -row 09964 -row 09965 -row 09966 -row 09967 -row 09968 -row 09969 -row 09970 -row 09971 -row 09972 -row 09973 -row 09974 -row 09975 -row 09976 -row 09977 -row 09978 -row 09979 -row 09980 -row 09981 -row 09982 -row 09983 -row 09984 -row 09985 -row 09986 -row 09987 -row 09988 -row 09989 -row 09990 -row 09991 -row 09992 -row 09993 -row 09994 -row 09995 -row 09996 -row 09997 -row 09998 -row 09999 -row 10000 -row 10001 -row 10002 -row 10003 -row 10004 -row 10005 -row 10006 -row 10007 -row 10008 -row 10009 -row 10010 -row 10011 -row 10012 -row 10013 -row 10014 -row 10015 -row 10016 -row 10017 -row 10018 -row 10019 -row 10020 -row 10021 -row 10022 -row 10023 -row 10024 -row 10025 -row 10026 -row 10027 -row 10028 -row 10029 -row 10030 -row 10031 -row 10032 -row 10033 -row 10034 -row 10035 -row 10036 -row 10037 -row 10038 -row 10039 -row 10040 -row 10041 -row 10042 -row 10043 -row 10044 -row 10045 -row 10046 -row 10047 -row 10048 -row 10049 -row 10050 -row 10051 -row 10052 -row 10053 -row 10054 -row 10055 -row 10056 -row 10057 -row 10058 -row 10059 -row 10060 -row 10061 -row 10062 -row 10063 -row 10064 -row 10065 -row 10066 -row 10067 -row 10068 -row 10069 -row 10070 -row 10071 -row 10072 -row 10073 -row 10074 -row 10075 -row 10076 -row 10077 -row 10078 -row 10079 -row 10080 -row 10081 -row 10082 -row 10083 -row 10084 -row 10085 -row 10086 -row 10087 -row 10088 -row 10089 -row 10090 -row 10091 -row 10092 -row 10093 -row 10094 -row 10095 -row 10096 -row 10097 -row 10098 -row 10099 -row 10100 -row 10101 -row 10102 -row 10103 -row 10104 -row 10105 -row 10106 -row 10107 -row 10108 -row 10109 -row 10110 -row 10111 -row 10112 -row 10113 -row 10114 -row 10115 -row 10116 -row 10117 -row 10118 -row 10119 -row 10120 -row 10121 -row 10122 -row 10123 -row 10124 -row 10125 -row 10126 -row 10127 -row 10128 -row 10129 -row 10130 -row 10131 -row 10132 -row 10133 -row 10134 -row 10135 -row 10136 -row 10137 -row 10138 -row 10139 -row 10140 -row 10141 -row 10142 -row 10143 -row 10144 -row 10145 -row 10146 -row 10147 -row 10148 -row 10149 -row 10150 -row 10151 -row 10152 -row 10153 -row 10154 -row 10155 -row 10156 -row 10157 -row 10158 -row 10159 -row 10160 -row 10161 -row 10162 -row 10163 -row 10164 -row 10165 -row 10166 -row 10167 -row 10168 -row 10169 -row 10170 -row 10171 -row 10172 -row 10173 -row 10174 -row 10175 -row 10176 -row 10177 -row 10178 -row 10179 -row 10180 -row 10181 -row 10182 -row 10183 -row 10184 -row 10185 -row 10186 -row 10187 -row 10188 -row 10189 -row 10190 -row 10191 -row 10192 -row 10193 -row 10194 -row 10195 -row 10196 -row 10197 -row 10198 -row 10199 -row 10200 -row 10201 -row 10202 -row 10203 -row 10204 -row 10205 -row 10206 -row 10207 -row 10208 -row 10209 -row 10210 -row 10211 -row 10212 -row 10213 -row 10214 -row 10215 -row 10216 -row 10217 -row 10218 -row 10219 -row 10220 -row 10221 -row 10222 -row 10223 -row 10224 -row 10225 -row 10226 -row 10227 -row 10228 -row 10229 -row 10230 -row 10231 -row 10232 -row 10233 -row 10234 -row 10235 -row 10236 -row 10237 -row 10238 -row 10239 -row 10240 -row 10241 -row 10242 -row 10243 -row 10244 -row 10245 -row 10246 -row 10247 -row 10248 -row 10249 -row 10250 -row 10251 -row 10252 -row 10253 -row 10254 -row 10255 -row 10256 -row 10257 -row 10258 -row 10259 -row 10260 -row 10261 -row 10262 -row 10263 -row 10264 -row 10265 -row 10266 -row 10267 -row 10268 -row 10269 -row 10270 -row 10271 -row 10272 -row 10273 -row 10274 -row 10275 -row 10276 -row 10277 -row 10278 -row 10279 -row 10280 -row 10281 -row 10282 -row 10283 -row 10284 -row 10285 -row 10286 -row 10287 -row 10288 -row 10289 -row 10290 -row 10291 -row 10292 -row 10293 -row 10294 -row 10295 -row 10296 -row 10297 -row 10298 -row 10299 -row 10300 -row 10301 -row 10302 -row 10303 -row 10304 -row 10305 -row 10306 -row 10307 -row 10308 -row 10309 -row 10310 -row 10311 -row 10312 -row 10313 -row 10314 -row 10315 -row 10316 -row 10317 -row 10318 -row 10319 -row 10320 -row 10321 -row 10322 -row 10323 -row 10324 -row 10325 -row 10326 -row 10327 -row 10328 -row 10329 -row 10330 -row 10331 -row 10332 -row 10333 -row 10334 -row 10335 -row 10336 -row 10337 -row 10338 -row 10339 -row 10340 -row 10341 -row 10342 -row 10343 -row 10344 -row 10345 -row 10346 -row 10347 -row 10348 -row 10349 -row 10350 -row 10351 -row 10352 -row 10353 -row 10354 -row 10355 -row 10356 -row 10357 -row 10358 -row 10359 -row 10360 -row 10361 -row 10362 -row 10363 -row 10364 -row 10365 -row 10366 -row 10367 -row 10368 -row 10369 -row 10370 -row 10371 -row 10372 -row 10373 -row 10374 -row 10375 -row 10376 -row 10377 -row 10378 -row 10379 -row 10380 -row 10381 -row 10382 -row 10383 -row 10384 -row 10385 -row 10386 -row 10387 -row 10388 -row 10389 -row 10390 -row 10391 -row 10392 -row 10393 -row 10394 -row 10395 -row 10396 -row 10397 -row 10398 -row 10399 -row 10400 -row 10401 -row 10402 -row 10403 -row 10404 -row 10405 -row 10406 -row 10407 -row 10408 -row 10409 -row 10410 -row 10411 -row 10412 -row 10413 -row 10414 -row 10415 -row 10416 -row 10417 -row 10418 -row 10419 -row 10420 -row 10421 -row 10422 -row 10423 -row 10424 -row 10425 -row 10426 -row 10427 -row 10428 -row 10429 -row 10430 -row 10431 -row 10432 -row 10433 -row 10434 -row 10435 -row 10436 -row 10437 -row 10438 -row 10439 -row 10440 -row 10441 -row 10442 -row 10443 -row 10444 -row 10445 -row 10446 -row 10447 -row 10448 -row 10449 -row 10450 -row 10451 -row 10452 -row 10453 -row 10454 -row 10455 -row 10456 -row 10457 -row 10458 -row 10459 -row 10460 -row 10461 -row 10462 -row 10463 -row 10464 -row 10465 -row 10466 -row 10467 -row 10468 -row 10469 -row 10470 -row 10471 -row 10472 -row 10473 -row 10474 -row 10475 -row 10476 -row 10477 -row 10478 -row 10479 -row 10480 -row 10481 -row 10482 -row 10483 -row 10484 -row 10485 -row 10486 -row 10487 -row 10488 -row 10489 -row 10490 -row 10491 -row 10492 -row 10493 -row 10494 -row 10495 -row 10496 -row 10497 -row 10498 -row 10499 -row 10500 -row 10501 -row 10502 -row 10503 -row 10504 -row 10505 -row 10506 -row 10507 -row 10508 -row 10509 -row 10510 -row 10511 -row 10512 -row 10513 -row 10514 -row 10515 -row 10516 -row 10517 -row 10518 -row 10519 -row 10520 -row 10521 -row 10522 -row 10523 -row 10524 -row 10525 -row 10526 -row 10527 -row 10528 -row 10529 -row 10530 -row 10531 -row 10532 -row 10533 -row 10534 -row 10535 -row 10536 -row 10537 -row 10538 -row 10539 -row 10540 -row 10541 -row 10542 -row 10543 -row 10544 -row 10545 -row 10546 -row 10547 -row 10548 -row 10549 -row 10550 -row 10551 -row 10552 -row 10553 -row 10554 -row 10555 -row 10556 -row 10557 -row 10558 -row 10559 -row 10560 -row 10561 -row 10562 -row 10563 -row 10564 -row 10565 -row 10566 -row 10567 -row 10568 -row 10569 -row 10570 -row 10571 -row 10572 -row 10573 -row 10574 -row 10575 -row 10576 -row 10577 -row 10578 -row 10579 -row 10580 -row 10581 -row 10582 -row 10583 -row 10584 -row 10585 -row 10586 -row 10587 -row 10588 -row 10589 -row 10590 -row 10591 -row 10592 -row 10593 -row 10594 -row 10595 -row 10596 -row 10597 -row 10598 -row 10599 -row 10600 -row 10601 -row 10602 -row 10603 -row 10604 -row 10605 -row 10606 -row 10607 -row 10608 -row 10609 -row 10610 -row 10611 -row 10612 -row 10613 -row 10614 -row 10615 -row 10616 -row 10617 -row 10618 -row 10619 -row 10620 -row 10621 -row 10622 -row 10623 -row 10624 -row 10625 -row 10626 -row 10627 -row 10628 -row 10629 -row 10630 -row 10631 -row 10632 -row 10633 -row 10634 -row 10635 -row 10636 -row 10637 -row 10638 -row 10639 -row 10640 -row 10641 -row 10642 -row 10643 -row 10644 -row 10645 -row 10646 -row 10647 -row 10648 -row 10649 -row 10650 -row 10651 -row 10652 -row 10653 -row 10654 -row 10655 -row 10656 -row 10657 -row 10658 -row 10659 -row 10660 -row 10661 -row 10662 -row 10663 -row 10664 -row 10665 -row 10666 -row 10667 -row 10668 -row 10669 -row 10670 -row 10671 -row 10672 -row 10673 -row 10674 -row 10675 -row 10676 -row 10677 -row 10678 -row 10679 -row 10680 -row 10681 -row 10682 -row 10683 -row 10684 -row 10685 -row 10686 -row 10687 -row 10688 -row 10689 -row 10690 -row 10691 -row 10692 -row 10693 -row 10694 -row 10695 -row 10696 -row 10697 -row 10698 -row 10699 -row 10700 -row 10701 -row 10702 -row 10703 -row 10704 -row 10705 -row 10706 -row 10707 -row 10708 -row 10709 -row 10710 -row 10711 -row 10712 -row 10713 -row 10714 -row 10715 -row 10716 -row 10717 -row 10718 -row 10719 -row 10720 -row 10721 -row 10722 -row 10723 -row 10724 -row 10725 -row 10726 -row 10727 -row 10728 -row 10729 -row 10730 -row 10731 -row 10732 -row 10733 -row 10734 -row 10735 -row 10736 -row 10737 -row 10738 -row 10739 -row 10740 -row 10741 -row 10742 -row 10743 -row 10744 -row 10745 -row 10746 -row 10747 -row 10748 -row 10749 -row 10750 -row 10751 -row 10752 -row 10753 -row 10754 -row 10755 -row 10756 -row 10757 -row 10758 -row 10759 -row 10760 -row 10761 -row 10762 -row 10763 -row 10764 -row 10765 -row 10766 -row 10767 -row 10768 -row 10769 -row 10770 -row 10771 -row 10772 -row 10773 -row 10774 -row 10775 -row 10776 -row 10777 -row 10778 -row 10779 -row 10780 -row 10781 -row 10782 -row 10783 -row 10784 -row 10785 -row 10786 -row 10787 -row 10788 -row 10789 -row 10790 -row 10791 -row 10792 -row 10793 -row 10794 -row 10795 -row 10796 -row 10797 -row 10798 -row 10799 -row 10800 -row 10801 -row 10802 -row 10803 -row 10804 -row 10805 -row 10806 -row 10807 -row 10808 -row 10809 -row 10810 -row 10811 -row 10812 -row 10813 -row 10814 -row 10815 -row 10816 -row 10817 -row 10818 -row 10819 -row 10820 -row 10821 -row 10822 -row 10823 -row 10824 -row 10825 -row 10826 -row 10827 -row 10828 -row 10829 -row 10830 -row 10831 -row 10832 -row 10833 -row 10834 -row 10835 -row 10836 -row 10837 -row 10838 -row 10839 -row 10840 -row 10841 -row 10842 -row 10843 -row 10844 -row 10845 -row 10846 -row 10847 -row 10848 -row 10849 -row 10850 -row 10851 -row 10852 -row 10853 -row 10854 -row 10855 -row 10856 -row 10857 -row 10858 -row 10859 -row 10860 -row 10861 -row 10862 -row 10863 -row 10864 -row 10865 -row 10866 -row 10867 -row 10868 -row 10869 -row 10870 -row 10871 -row 10872 -row 10873 -row 10874 -row 10875 -row 10876 -row 10877 -row 10878 -row 10879 -row 10880 -row 10881 -row 10882 -row 10883 -row 10884 -row 10885 -row 10886 -row 10887 -row 10888 -row 10889 -row 10890 -row 10891 -row 10892 -row 10893 -row 10894 -row 10895 -row 10896 -row 10897 -row 10898 -row 10899 -row 10900 -row 10901 -row 10902 -row 10903 -row 10904 -row 10905 -row 10906 -row 10907 -row 10908 -row 10909 -row 10910 -row 10911 -row 10912 -row 10913 -row 10914 -row 10915 -row 10916 -row 10917 -row 10918 -row 10919 -row 10920 -row 10921 -row 10922 -row 10923 -row 10924 -row 10925 -row 10926 -row 10927 -row 10928 -row 10929 -row 10930 -row 10931 -row 10932 -row 10933 -row 10934 -row 10935 -row 10936 -row 10937 -row 10938 -row 10939 -row 10940 -row 10941 -row 10942 -row 10943 -row 10944 -row 10945 -row 10946 -row 10947 -row 10948 -row 10949 -row 10950 -row 10951 -row 10952 -row 10953 -row 10954 -row 10955 -row 10956 -row 10957 -row 10958 -row 10959 -row 10960 -row 10961 -row 10962 -row 10963 -row 10964 -row 10965 -row 10966 -row 10967 -row 10968 -row 10969 -row 10970 -row 10971 -row 10972 -row 10973 -row 10974 -row 10975 -row 10976 -row 10977 -row 10978 -row 10979 -row 10980 -row 10981 -row 10982 -row 10983 -row 10984 -row 10985 -row 10986 -row 10987 -row 10988 -row 10989 -row 10990 -row 10991 -row 10992 -row 10993 -row 10994 -row 10995 -row 10996 -row 10997 -row 10998 -row 10999 -row 11000 -row 11001 -row 11002 -row 11003 -row 11004 -row 11005 -row 11006 -row 11007 -row 11008 -row 11009 -row 11010 -row 11011 -row 11012 -row 11013 -row 11014 -row 11015 -row 11016 -row 11017 -row 11018 -row 11019 -row 11020 -row 11021 -row 11022 -row 11023 -row 11024 -row 11025 -row 11026 -row 11027 -row 11028 -row 11029 -row 11030 -row 11031 -row 11032 -row 11033 -row 11034 -row 11035 -row 11036 -row 11037 -row 11038 -row 11039 -row 11040 -row 11041 -row 11042 -row 11043 -row 11044 -row 11045 -row 11046 -row 11047 -row 11048 -row 11049 -row 11050 -row 11051 -row 11052 -row 11053 -row 11054 -row 11055 -row 11056 -row 11057 -row 11058 -row 11059 -row 11060 -row 11061 -row 11062 -row 11063 -row 11064 -row 11065 -row 11066 -row 11067 -row 11068 -row 11069 -row 11070 -row 11071 -row 11072 -row 11073 -row 11074 -row 11075 -row 11076 -row 11077 -row 11078 -row 11079 -row 11080 -row 11081 -row 11082 -row 11083 -row 11084 -row 11085 -row 11086 -row 11087 -row 11088 -row 11089 -row 11090 -row 11091 -row 11092 -row 11093 -row 11094 -row 11095 -row 11096 -row 11097 -row 11098 -row 11099 -row 11100 -row 11101 -row 11102 -row 11103 -row 11104 -row 11105 -row 11106 -row 11107 -row 11108 -row 11109 -row 11110 -row 11111 -row 11112 -row 11113 -row 11114 -row 11115 -row 11116 -row 11117 -row 11118 -row 11119 -row 11120 -row 11121 -row 11122 -row 11123 -row 11124 -row 11125 -row 11126 -row 11127 -row 11128 -row 11129 -row 11130 -row 11131 -row 11132 -row 11133 -row 11134 -row 11135 -row 11136 -row 11137 -row 11138 -row 11139 -row 11140 -row 11141 -row 11142 -row 11143 -row 11144 -row 11145 -row 11146 -row 11147 -row 11148 -row 11149 -row 11150 -row 11151 -row 11152 -row 11153 -row 11154 -row 11155 -row 11156 -row 11157 -row 11158 -row 11159 -row 11160 -row 11161 -row 11162 -row 11163 -row 11164 -row 11165 -row 11166 -row 11167 -row 11168 -row 11169 -row 11170 -row 11171 -row 11172 -row 11173 -row 11174 -row 11175 -row 11176 -row 11177 -row 11178 -row 11179 -row 11180 -row 11181 -row 11182 -row 11183 -row 11184 -row 11185 -row 11186 -row 11187 -row 11188 -row 11189 -row 11190 -row 11191 -row 11192 -row 11193 -row 11194 -row 11195 -row 11196 -row 11197 -row 11198 -row 11199 -row 11200 -row 11201 -row 11202 -row 11203 -row 11204 -row 11205 -row 11206 -row 11207 -row 11208 -row 11209 -row 11210 -row 11211 -row 11212 -row 11213 -row 11214 -row 11215 -row 11216 -row 11217 -row 11218 -row 11219 -row 11220 -row 11221 -row 11222 -row 11223 -row 11224 -row 11225 -row 11226 -row 11227 -row 11228 -row 11229 -row 11230 -row 11231 -row 11232 -row 11233 -row 11234 -row 11235 -row 11236 -row 11237 -row 11238 -row 11239 -row 11240 -row 11241 -row 11242 -row 11243 -row 11244 -row 11245 -row 11246 -row 11247 -row 11248 -row 11249 -row 11250 -row 11251 -row 11252 -row 11253 -row 11254 -row 11255 -row 11256 -row 11257 -row 11258 -row 11259 -row 11260 -row 11261 -row 11262 -row 11263 -row 11264 -row 11265 -row 11266 -row 11267 -row 11268 -row 11269 -row 11270 -row 11271 -row 11272 -row 11273 -row 11274 -row 11275 -row 11276 -row 11277 -row 11278 -row 11279 -row 11280 -row 11281 -row 11282 -row 11283 -row 11284 -row 11285 -row 11286 -row 11287 -row 11288 -row 11289 -row 11290 -row 11291 -row 11292 -row 11293 -row 11294 -row 11295 -row 11296 -row 11297 -row 11298 -row 11299 -row 11300 -row 11301 -row 11302 -row 11303 -row 11304 -row 11305 -row 11306 -row 11307 -row 11308 -row 11309 -row 11310 -row 11311 -row 11312 -row 11313 -row 11314 -row 11315 -row 11316 -row 11317 -row 11318 -row 11319 -row 11320 -row 11321 -row 11322 -row 11323 -row 11324 -row 11325 -row 11326 -row 11327 -row 11328 -row 11329 -row 11330 -row 11331 -row 11332 -row 11333 -row 11334 -row 11335 -row 11336 -row 11337 -row 11338 -row 11339 -row 11340 -row 11341 -row 11342 -row 11343 -row 11344 -row 11345 -row 11346 -row 11347 -row 11348 -row 11349 -row 11350 -row 11351 -row 11352 -row 11353 -row 11354 -row 11355 -row 11356 -row 11357 -row 11358 -row 11359 -row 11360 -row 11361 -row 11362 -row 11363 -row 11364 -row 11365 -row 11366 -row 11367 -row 11368 -row 11369 -row 11370 -row 11371 -row 11372 -row 11373 -row 11374 -row 11375 -row 11376 -row 11377 -row 11378 -row 11379 -row 11380 -row 11381 -row 11382 -row 11383 -row 11384 -row 11385 -row 11386 -row 11387 -row 11388 -row 11389 -row 11390 -row 11391 -row 11392 -row 11393 -row 11394 -row 11395 -row 11396 -row 11397 -row 11398 -row 11399 -row 11400 -row 11401 -row 11402 -row 11403 -row 11404 -row 11405 -row 11406 -row 11407 -row 11408 -row 11409 -row 11410 -row 11411 -row 11412 -row 11413 -row 11414 -row 11415 -row 11416 -row 11417 -row 11418 -row 11419 -row 11420 -row 11421 -row 11422 -row 11423 -row 11424 -row 11425 -row 11426 -row 11427 -row 11428 -row 11429 -row 11430 -row 11431 -row 11432 -row 11433 -row 11434 -row 11435 -row 11436 -row 11437 -row 11438 -row 11439 -row 11440 -row 11441 -row 11442 -row 11443 -row 11444 -row 11445 -row 11446 -row 11447 -row 11448 -row 11449 -row 11450 -row 11451 -row 11452 -row 11453 -row 11454 -row 11455 -row 11456 -row 11457 -row 11458 -row 11459 -row 11460 -row 11461 -row 11462 -row 11463 -row 11464 -row 11465 -row 11466 -row 11467 -row 11468 -row 11469 -row 11470 -row 11471 -row 11472 -row 11473 -row 11474 -row 11475 -row 11476 -row 11477 -row 11478 -row 11479 -row 11480 -row 11481 -row 11482 -row 11483 -row 11484 -row 11485 -row 11486 -row 11487 -row 11488 -row 11489 -row 11490 -row 11491 -row 11492 -row 11493 -row 11494 -row 11495 -row 11496 -row 11497 -row 11498 -row 11499 -row 11500 -row 11501 -row 11502 -row 11503 -row 11504 -row 11505 -row 11506 -row 11507 -row 11508 -row 11509 -row 11510 -row 11511 -row 11512 -row 11513 -row 11514 -row 11515 -row 11516 -row 11517 -row 11518 -row 11519 -row 11520 -row 11521 -row 11522 -row 11523 -row 11524 -row 11525 -row 11526 -row 11527 -row 11528 -row 11529 -row 11530 -row 11531 -row 11532 -row 11533 -row 11534 -row 11535 -row 11536 -row 11537 -row 11538 -row 11539 -row 11540 -row 11541 -row 11542 -row 11543 -row 11544 -row 11545 -row 11546 -row 11547 -row 11548 -row 11549 -row 11550 -row 11551 -row 11552 -row 11553 -row 11554 -row 11555 -row 11556 -row 11557 -row 11558 -row 11559 -row 11560 -row 11561 -row 11562 -row 11563 -row 11564 -row 11565 -row 11566 -row 11567 -row 11568 -row 11569 -row 11570 -row 11571 -row 11572 -row 11573 -row 11574 -row 11575 -row 11576 -row 11577 -row 11578 -row 11579 -row 11580 -row 11581 -row 11582 -row 11583 -row 11584 -row 11585 -row 11586 -row 11587 -row 11588 -row 11589 -row 11590 -row 11591 -row 11592 -row 11593 -row 11594 -row 11595 -row 11596 -row 11597 -row 11598 -row 11599 -row 11600 -row 11601 -row 11602 -row 11603 -row 11604 -row 11605 -row 11606 -row 11607 -row 11608 -row 11609 -row 11610 -row 11611 -row 11612 -row 11613 -row 11614 -row 11615 -row 11616 -row 11617 -row 11618 -row 11619 -row 11620 -row 11621 -row 11622 -row 11623 -row 11624 -row 11625 -row 11626 -row 11627 -row 11628 -row 11629 -row 11630 -row 11631 -row 11632 -row 11633 -row 11634 -row 11635 -row 11636 -row 11637 -row 11638 -row 11639 -row 11640 -row 11641 -row 11642 -row 11643 -row 11644 -row 11645 -row 11646 -row 11647 -row 11648 -row 11649 -row 11650 -row 11651 -row 11652 -row 11653 -row 11654 -row 11655 -row 11656 -row 11657 -row 11658 -row 11659 -row 11660 -row 11661 -row 11662 -row 11663 -row 11664 -row 11665 -row 11666 -row 11667 -row 11668 -row 11669 -row 11670 -row 11671 -row 11672 -row 11673 -row 11674 -row 11675 -row 11676 -row 11677 -row 11678 -row 11679 -row 11680 -row 11681 -row 11682 -row 11683 -row 11684 -row 11685 -row 11686 -row 11687 -row 11688 -row 11689 -row 11690 -row 11691 -row 11692 -row 11693 -row 11694 -row 11695 -row 11696 -row 11697 -row 11698 -row 11699 -row 11700 -row 11701 -row 11702 -row 11703 -row 11704 -row 11705 -row 11706 -row 11707 -row 11708 -row 11709 -row 11710 -row 11711 -row 11712 -row 11713 -row 11714 -row 11715 -row 11716 -row 11717 -row 11718 -row 11719 -row 11720 -row 11721 -row 11722 -row 11723 -row 11724 -row 11725 -row 11726 -row 11727 -row 11728 -row 11729 -row 11730 -row 11731 -row 11732 -row 11733 -row 11734 -row 11735 -row 11736 -row 11737 -row 11738 -row 11739 -row 11740 -row 11741 -row 11742 -row 11743 -row 11744 -row 11745 -row 11746 -row 11747 -row 11748 -row 11749 -row 11750 -row 11751 -row 11752 -row 11753 -row 11754 -row 11755 -row 11756 -row 11757 -row 11758 -row 11759 -row 11760 -row 11761 -row 11762 -row 11763 -row 11764 -row 11765 -row 11766 -row 11767 -row 11768 -row 11769 -row 11770 -row 11771 -row 11772 -row 11773 -row 11774 -row 11775 -row 11776 -row 11777 -row 11778 -row 11779 -row 11780 -row 11781 -row 11782 -row 11783 -row 11784 -row 11785 -row 11786 -row 11787 -row 11788 -row 11789 -row 11790 -row 11791 -row 11792 -row 11793 -row 11794 -row 11795 -row 11796 -row 11797 -row 11798 -row 11799 -row 11800 -row 11801 -row 11802 -row 11803 -row 11804 -row 11805 -row 11806 -row 11807 -row 11808 -row 11809 -row 11810 -row 11811 -row 11812 -row 11813 -row 11814 -row 11815 -row 11816 -row 11817 -row 11818 -row 11819 -row 11820 -row 11821 -row 11822 -row 11823 -row 11824 -row 11825 -row 11826 -row 11827 -row 11828 -row 11829 -row 11830 -row 11831 -row 11832 -row 11833 -row 11834 -row 11835 -row 11836 -row 11837 -row 11838 -row 11839 -row 11840 -row 11841 -row 11842 -row 11843 -row 11844 -row 11845 -row 11846 -row 11847 -row 11848 -row 11849 -row 11850 -row 11851 -row 11852 -row 11853 -row 11854 -row 11855 -row 11856 -row 11857 -row 11858 -row 11859 -row 11860 -row 11861 -row 11862 -row 11863 -row 11864 -row 11865 -row 11866 -row 11867 -row 11868 -row 11869 -row 11870 -row 11871 -row 11872 -row 11873 -row 11874 -row 11875 -row 11876 -row 11877 -row 11878 -row 11879 -row 11880 -row 11881 -row 11882 -row 11883 -row 11884 -row 11885 -row 11886 -row 11887 -row 11888 -row 11889 -row 11890 -row 11891 -row 11892 -row 11893 -row 11894 -row 11895 -row 11896 -row 11897 -row 11898 -row 11899 -row 11900 -row 11901 -row 11902 -row 11903 -row 11904 -row 11905 -row 11906 -row 11907 -row 11908 -row 11909 -row 11910 -row 11911 -row 11912 -row 11913 -row 11914 -row 11915 -row 11916 -row 11917 -row 11918 -row 11919 -row 11920 -row 11921 -row 11922 -row 11923 -row 11924 -row 11925 -row 11926 -row 11927 -row 11928 -row 11929 -row 11930 -row 11931 -row 11932 -row 11933 -row 11934 -row 11935 -row 11936 -row 11937 -row 11938 -row 11939 -row 11940 -row 11941 -row 11942 -row 11943 -row 11944 -row 11945 -row 11946 -row 11947 -row 11948 -row 11949 -row 11950 -row 11951 -row 11952 -row 11953 -row 11954 -row 11955 -row 11956 -row 11957 -row 11958 -row 11959 -row 11960 -row 11961 -row 11962 -row 11963 -row 11964 -row 11965 -row 11966 -row 11967 -row 11968 -row 11969 -row 11970 -row 11971 -row 11972 -row 11973 -row 11974 -row 11975 -row 11976 -row 11977 -row 11978 -row 11979 -row 11980 -row 11981 -row 11982 -row 11983 -row 11984 -row 11985 -row 11986 -row 11987 -row 11988 -row 11989 -row 11990 -row 11991 -row 11992 -row 11993 -row 11994 -row 11995 -row 11996 -row 11997 -row 11998 -row 11999 -row 12000 -row 12001 -row 12002 -row 12003 -row 12004 -row 12005 -row 12006 -row 12007 -row 12008 -row 12009 -row 12010 -row 12011 -row 12012 -row 12013 -row 12014 -row 12015 -row 12016 -row 12017 -row 12018 -row 12019 -row 12020 -row 12021 -row 12022 -row 12023 -row 12024 -row 12025 -row 12026 -row 12027 -row 12028 -row 12029 -row 12030 -row 12031 -row 12032 -row 12033 -row 12034 -row 12035 -row 12036 -row 12037 -row 12038 -row 12039 -row 12040 -row 12041 -row 12042 -row 12043 -row 12044 -row 12045 -row 12046 -row 12047 -row 12048 -row 12049 -row 12050 -row 12051 -row 12052 -row 12053 -row 12054 -row 12055 -row 12056 -row 12057 -row 12058 -row 12059 -row 12060 -row 12061 -row 12062 -row 12063 -row 12064 -row 12065 -row 12066 -row 12067 -row 12068 -row 12069 -row 12070 -row 12071 -row 12072 -row 12073 -row 12074 -row 12075 -row 12076 -row 12077 -row 12078 -row 12079 -row 12080 -row 12081 -row 12082 -row 12083 -row 12084 -row 12085 -row 12086 -row 12087 -row 12088 -row 12089 -row 12090 -row 12091 -row 12092 -row 12093 -row 12094 -row 12095 -row 12096 -row 12097 -row 12098 -row 12099 -row 12100 -row 12101 -row 12102 -row 12103 -row 12104 -row 12105 -row 12106 -row 12107 -row 12108 -row 12109 -row 12110 -row 12111 -row 12112 -row 12113 -row 12114 -row 12115 -row 12116 -row 12117 -row 12118 -row 12119 -row 12120 -row 12121 -row 12122 -row 12123 -row 12124 -row 12125 -row 12126 -row 12127 -row 12128 -row 12129 -row 12130 -row 12131 -row 12132 -row 12133 -row 12134 -row 12135 -row 12136 -row 12137 -row 12138 -row 12139 -row 12140 -row 12141 -row 12142 -row 12143 -row 12144 -row 12145 -row 12146 -row 12147 -row 12148 -row 12149 -row 12150 -row 12151 -row 12152 -row 12153 -row 12154 -row 12155 -row 12156 -row 12157 -row 12158 -row 12159 -row 12160 -row 12161 -row 12162 -row 12163 -row 12164 -row 12165 -row 12166 -row 12167 -row 12168 -row 12169 -row 12170 -row 12171 -row 12172 -row 12173 -row 12174 -row 12175 -row 12176 -row 12177 -row 12178 -row 12179 -row 12180 -row 12181 -row 12182 -row 12183 -row 12184 -row 12185 -row 12186 -row 12187 -row 12188 -row 12189 -row 12190 -row 12191 -row 12192 -row 12193 -row 12194 -row 12195 -row 12196 -row 12197 -row 12198 -row 12199 -row 12200 -row 12201 -row 12202 -row 12203 -row 12204 -row 12205 -row 12206 -row 12207 -row 12208 -row 12209 -row 12210 -row 12211 -row 12212 -row 12213 -row 12214 -row 12215 -row 12216 -row 12217 -row 12218 -row 12219 -row 12220 -row 12221 -row 12222 -row 12223 -row 12224 -row 12225 -row 12226 -row 12227 -row 12228 -row 12229 -row 12230 -row 12231 -row 12232 -row 12233 -row 12234 -row 12235 -row 12236 -row 12237 -row 12238 -row 12239 -row 12240 -row 12241 -row 12242 -row 12243 -row 12244 -row 12245 -row 12246 -row 12247 -row 12248 -row 12249 -row 12250 -row 12251 -row 12252 -row 12253 -row 12254 -row 12255 -row 12256 -row 12257 -row 12258 -row 12259 -row 12260 -row 12261 -row 12262 -row 12263 -row 12264 -row 12265 -row 12266 -row 12267 -row 12268 -row 12269 -row 12270 -row 12271 -row 12272 -row 12273 -row 12274 -row 12275 -row 12276 -row 12277 -row 12278 -row 12279 -row 12280 -row 12281 -row 12282 -row 12283 -row 12284 -row 12285 -row 12286 -row 12287 -row 12288 -row 12289 -row 12290 -row 12291 -row 12292 -row 12293 -row 12294 -row 12295 -row 12296 -row 12297 -row 12298 -row 12299 -row 12300 -row 12301 -row 12302 -row 12303 -row 12304 -row 12305 -row 12306 -row 12307 -row 12308 -row 12309 -row 12310 -row 12311 -row 12312 -row 12313 -row 12314 -row 12315 -row 12316 -row 12317 -row 12318 -row 12319 -row 12320 -row 12321 -row 12322 -row 12323 -row 12324 -row 12325 -row 12326 -row 12327 -row 12328 -row 12329 -row 12330 -row 12331 -row 12332 -row 12333 -row 12334 -row 12335 -row 12336 -row 12337 -row 12338 -row 12339 -row 12340 -row 12341 -row 12342 -row 12343 -row 12344 -row 12345 -row 12346 -row 12347 -row 12348 -row 12349 -row 12350 -row 12351 -row 12352 -row 12353 -row 12354 -row 12355 -row 12356 -row 12357 -row 12358 -row 12359 -row 12360 -row 12361 -row 12362 -row 12363 -row 12364 -row 12365 -row 12366 -row 12367 -row 12368 -row 12369 -row 12370 -row 12371 -row 12372 -row 12373 -row 12374 -row 12375 -row 12376 -row 12377 -row 12378 -row 12379 -row 12380 -row 12381 -row 12382 -row 12383 -row 12384 -row 12385 -row 12386 -row 12387 -row 12388 -row 12389 -row 12390 -row 12391 -row 12392 -row 12393 -row 12394 -row 12395 -row 12396 -row 12397 -row 12398 -row 12399 -row 12400 -row 12401 -row 12402 -row 12403 -row 12404 -row 12405 -row 12406 -row 12407 -row 12408 -row 12409 -row 12410 -row 12411 -row 12412 -row 12413 -row 12414 -row 12415 -row 12416 -row 12417 -row 12418 -row 12419 -row 12420 -row 12421 -row 12422 -row 12423 -row 12424 -row 12425 -row 12426 -row 12427 -row 12428 -row 12429 -row 12430 -row 12431 -row 12432 -row 12433 -row 12434 -row 12435 -row 12436 -row 12437 -row 12438 -row 12439 -row 12440 -row 12441 -row 12442 -row 12443 -row 12444 -row 12445 -row 12446 -row 12447 -row 12448 -row 12449 -row 12450 -row 12451 -row 12452 -row 12453 -row 12454 -row 12455 -row 12456 -row 12457 -row 12458 -row 12459 -row 12460 -row 12461 -row 12462 -row 12463 -row 12464 -row 12465 -row 12466 -row 12467 -row 12468 -row 12469 -row 12470 -row 12471 -row 12472 -row 12473 -row 12474 -row 12475 -row 12476 -row 12477 -row 12478 -row 12479 -row 12480 -row 12481 -row 12482 -row 12483 -row 12484 -row 12485 -row 12486 -row 12487 -row 12488 -row 12489 -row 12490 -row 12491 -row 12492 -row 12493 -row 12494 -row 12495 -row 12496 -row 12497 -row 12498 -row 12499 -row 12500 -row 12501 -row 12502 -row 12503 -row 12504 -row 12505 -row 12506 -row 12507 -row 12508 -row 12509 -row 12510 -row 12511 -row 12512 -row 12513 -row 12514 -row 12515 -row 12516 -row 12517 -row 12518 -row 12519 -row 12520 -row 12521 -row 12522 -row 12523 -row 12524 -row 12525 -row 12526 -row 12527 -row 12528 -row 12529 -row 12530 -row 12531 -row 12532 -row 12533 -row 12534 -row 12535 -row 12536 -row 12537 -row 12538 -row 12539 -row 12540 -row 12541 -row 12542 -row 12543 -row 12544 -row 12545 -row 12546 -row 12547 -row 12548 -row 12549 -row 12550 -row 12551 -row 12552 -row 12553 -row 12554 -row 12555 -row 12556 -row 12557 -row 12558 -row 12559 -row 12560 -row 12561 -row 12562 -row 12563 -row 12564 -row 12565 -row 12566 -row 12567 -row 12568 -row 12569 -row 12570 -row 12571 -row 12572 -row 12573 -row 12574 -row 12575 -row 12576 -row 12577 -row 12578 -row 12579 -row 12580 -row 12581 -row 12582 -row 12583 -row 12584 -row 12585 -row 12586 -row 12587 -row 12588 -row 12589 -row 12590 -row 12591 -row 12592 -row 12593 -row 12594 -row 12595 -row 12596 -row 12597 -row 12598 -row 12599 -row 12600 -row 12601 -row 12602 -row 12603 -row 12604 -row 12605 -row 12606 -row 12607 -row 12608 -row 12609 -row 12610 -row 12611 -row 12612 -row 12613 -row 12614 -row 12615 -row 12616 -row 12617 -row 12618 -row 12619 -row 12620 -row 12621 -row 12622 -row 12623 -row 12624 -row 12625 -row 12626 -row 12627 -row 12628 -row 12629 -row 12630 -row 12631 -row 12632 -row 12633 -row 12634 -row 12635 -row 12636 -row 12637 -row 12638 -row 12639 -row 12640 -row 12641 -row 12642 -row 12643 -row 12644 -row 12645 -row 12646 -row 12647 -row 12648 -row 12649 -row 12650 -row 12651 -row 12652 -row 12653 -row 12654 -row 12655 -row 12656 -row 12657 -row 12658 -row 12659 -row 12660 -row 12661 -row 12662 -row 12663 -row 12664 -row 12665 -row 12666 -row 12667 -row 12668 -row 12669 -row 12670 -row 12671 -row 12672 -row 12673 -row 12674 -row 12675 -row 12676 -row 12677 -row 12678 -row 12679 -row 12680 -row 12681 -row 12682 -row 12683 -row 12684 -row 12685 -row 12686 -row 12687 -row 12688 -row 12689 -row 12690 -row 12691 -row 12692 -row 12693 -row 12694 -row 12695 -row 12696 -row 12697 -row 12698 -row 12699 -row 12700 -row 12701 -row 12702 -row 12703 -row 12704 -row 12705 -row 12706 -row 12707 -row 12708 -row 12709 -row 12710 -row 12711 -row 12712 -row 12713 -row 12714 -row 12715 -row 12716 -row 12717 -row 12718 -row 12719 -row 12720 -row 12721 -row 12722 -row 12723 -row 12724 -row 12725 -row 12726 -row 12727 -row 12728 -row 12729 -row 12730 -row 12731 -row 12732 -row 12733 -row 12734 -row 12735 -row 12736 -row 12737 -row 12738 -row 12739 -row 12740 -row 12741 -row 12742 -row 12743 -row 12744 -row 12745 -row 12746 -row 12747 -row 12748 -row 12749 -row 12750 -row 12751 -row 12752 -row 12753 -row 12754 -row 12755 -row 12756 -row 12757 -row 12758 -row 12759 -row 12760 -row 12761 -row 12762 -row 12763 -row 12764 -row 12765 -row 12766 -row 12767 -row 12768 -row 12769 -row 12770 -row 12771 -row 12772 -row 12773 -row 12774 -row 12775 -row 12776 -row 12777 -row 12778 -row 12779 -row 12780 -row 12781 -row 12782 -row 12783 -row 12784 -row 12785 -row 12786 -row 12787 -row 12788 -row 12789 -row 12790 -row 12791 -row 12792 -row 12793 -row 12794 -row 12795 -row 12796 -row 12797 -row 12798 -row 12799 -row 12800 -row 12801 -row 12802 -row 12803 -row 12804 -row 12805 -row 12806 -row 12807 -row 12808 -row 12809 -row 12810 -row 12811 -row 12812 -row 12813 -row 12814 -row 12815 -row 12816 -row 12817 -row 12818 -row 12819 -row 12820 -row 12821 -row 12822 -row 12823 -row 12824 -row 12825 -row 12826 -row 12827 -row 12828 -row 12829 -row 12830 -row 12831 -row 12832 -row 12833 -row 12834 -row 12835 -row 12836 -row 12837 -row 12838 -row 12839 -row 12840 -row 12841 -row 12842 -row 12843 -row 12844 -row 12845 -row 12846 -row 12847 -row 12848 -row 12849 -row 12850 -row 12851 -row 12852 -row 12853 -row 12854 -row 12855 -row 12856 -row 12857 -row 12858 -row 12859 -row 12860 -row 12861 -row 12862 -row 12863 -row 12864 -row 12865 -row 12866 -row 12867 -row 12868 -row 12869 -row 12870 -row 12871 -row 12872 -row 12873 -row 12874 -row 12875 -row 12876 -row 12877 -row 12878 -row 12879 -row 12880 -row 12881 -row 12882 -row 12883 -row 12884 -row 12885 -row 12886 -row 12887 -row 12888 -row 12889 -row 12890 -row 12891 -row 12892 -row 12893 -row 12894 -row 12895 -row 12896 -row 12897 -row 12898 -row 12899 -row 12900 -row 12901 -row 12902 -row 12903 -row 12904 -row 12905 -row 12906 -row 12907 -row 12908 -row 12909 -row 12910 -row 12911 -row 12912 -row 12913 -row 12914 -row 12915 -row 12916 -row 12917 -row 12918 -row 12919 -row 12920 -row 12921 -row 12922 -row 12923 -row 12924 -row 12925 -row 12926 -row 12927 -row 12928 -row 12929 -row 12930 -row 12931 -row 12932 -row 12933 -row 12934 -row 12935 -row 12936 -row 12937 -row 12938 -row 12939 -row 12940 -row 12941 -row 12942 -row 12943 -row 12944 -row 12945 -row 12946 -row 12947 -row 12948 -row 12949 -row 12950 -row 12951 -row 12952 -row 12953 -row 12954 -row 12955 -row 12956 -row 12957 -row 12958 -row 12959 -row 12960 -row 12961 -row 12962 -row 12963 -row 12964 -row 12965 -row 12966 -row 12967 -row 12968 -row 12969 -row 12970 -row 12971 -row 12972 -row 12973 -row 12974 -row 12975 -row 12976 -row 12977 -row 12978 -row 12979 -row 12980 -row 12981 -row 12982 -row 12983 -row 12984 -row 12985 -row 12986 -row 12987 -row 12988 -row 12989 -row 12990 -row 12991 -row 12992 -row 12993 -row 12994 -row 12995 -row 12996 -row 12997 -row 12998 -row 12999 -row 13000 -row 13001 -row 13002 -row 13003 -row 13004 -row 13005 -row 13006 -row 13007 -row 13008 -row 13009 -row 13010 -row 13011 -row 13012 -row 13013 -row 13014 -row 13015 -row 13016 -row 13017 -row 13018 -row 13019 -row 13020 -row 13021 -row 13022 -row 13023 -row 13024 -row 13025 -row 13026 -row 13027 -row 13028 -row 13029 -row 13030 -row 13031 -row 13032 -row 13033 -row 13034 -row 13035 -row 13036 -row 13037 -row 13038 -row 13039 -row 13040 -row 13041 -row 13042 -row 13043 -row 13044 -row 13045 -row 13046 -row 13047 -row 13048 -row 13049 -row 13050 -row 13051 -row 13052 -row 13053 -row 13054 -row 13055 -row 13056 -row 13057 -row 13058 -row 13059 -row 13060 -row 13061 -row 13062 -row 13063 -row 13064 -row 13065 -row 13066 -row 13067 -row 13068 -row 13069 -row 13070 -row 13071 -row 13072 -row 13073 -row 13074 -row 13075 -row 13076 -row 13077 -row 13078 -row 13079 -row 13080 -row 13081 -row 13082 -row 13083 -row 13084 -row 13085 -row 13086 -row 13087 -row 13088 -row 13089 -row 13090 -row 13091 -row 13092 -row 13093 -row 13094 -row 13095 -row 13096 -row 13097 -row 13098 -row 13099 -row 13100 -row 13101 -row 13102 -row 13103 -row 13104 -row 13105 -row 13106 -row 13107 -row 13108 -row 13109 -row 13110 -row 13111 -row 13112 -row 13113 -row 13114 -row 13115 -row 13116 -row 13117 -row 13118 -row 13119 -row 13120 -row 13121 -row 13122 -row 13123 -row 13124 -row 13125 -row 13126 -row 13127 -row 13128 -row 13129 -row 13130 -row 13131 -row 13132 -row 13133 -row 13134 -row 13135 -row 13136 -row 13137 -row 13138 -row 13139 -row 13140 -row 13141 -row 13142 -row 13143 -row 13144 -row 13145 -row 13146 -row 13147 -row 13148 -row 13149 -row 13150 -row 13151 -row 13152 -row 13153 -row 13154 -row 13155 -row 13156 -row 13157 -row 13158 -row 13159 -row 13160 -row 13161 -row 13162 -row 13163 -row 13164 -row 13165 -row 13166 -row 13167 -row 13168 -row 13169 -row 13170 -row 13171 -row 13172 -row 13173 -row 13174 -row 13175 -row 13176 -row 13177 -row 13178 -row 13179 -row 13180 -row 13181 -row 13182 -row 13183 -row 13184 -row 13185 -row 13186 -row 13187 -row 13188 -row 13189 -row 13190 -row 13191 -row 13192 -row 13193 -row 13194 -row 13195 -row 13196 -row 13197 -row 13198 -row 13199 -row 13200 -row 13201 -row 13202 -row 13203 -row 13204 -row 13205 -row 13206 -row 13207 -row 13208 -row 13209 -row 13210 -row 13211 -row 13212 -row 13213 -row 13214 -row 13215 -row 13216 -row 13217 -row 13218 -row 13219 -row 13220 -row 13221 -row 13222 -row 13223 -row 13224 -row 13225 -row 13226 -row 13227 -row 13228 -row 13229 -row 13230 -row 13231 -row 13232 -row 13233 -row 13234 -row 13235 -row 13236 -row 13237 -row 13238 -row 13239 -row 13240 -row 13241 -row 13242 -row 13243 -row 13244 -row 13245 -row 13246 -row 13247 -row 13248 -row 13249 -row 13250 -row 13251 -row 13252 -row 13253 -row 13254 -row 13255 -row 13256 -row 13257 -row 13258 -row 13259 -row 13260 -row 13261 -row 13262 -row 13263 -row 13264 -row 13265 -row 13266 -row 13267 -row 13268 -row 13269 -row 13270 -row 13271 -row 13272 -row 13273 -row 13274 -row 13275 -row 13276 -row 13277 -row 13278 -row 13279 -row 13280 -row 13281 -row 13282 -row 13283 -row 13284 -row 13285 -row 13286 -row 13287 -row 13288 -row 13289 -row 13290 -row 13291 -row 13292 -row 13293 -row 13294 -row 13295 -row 13296 -row 13297 -row 13298 -row 13299 -row 13300 -row 13301 -row 13302 -row 13303 -row 13304 -row 13305 -row 13306 -row 13307 -row 13308 -row 13309 -row 13310 -row 13311 -row 13312 -row 13313 -row 13314 -row 13315 -row 13316 -row 13317 -row 13318 -row 13319 -row 13320 -row 13321 -row 13322 -row 13323 -row 13324 -row 13325 -row 13326 -row 13327 -row 13328 -row 13329 -row 13330 -row 13331 -row 13332 -row 13333 -row 13334 -row 13335 -row 13336 -row 13337 -row 13338 -row 13339 -row 13340 -row 13341 -row 13342 -row 13343 -row 13344 -row 13345 -row 13346 -row 13347 -row 13348 -row 13349 -row 13350 -row 13351 -row 13352 -row 13353 -row 13354 -row 13355 -row 13356 -row 13357 -row 13358 -row 13359 -row 13360 -row 13361 -row 13362 -row 13363 -row 13364 -row 13365 -row 13366 -row 13367 -row 13368 -row 13369 -row 13370 -row 13371 -row 13372 -row 13373 -row 13374 -row 13375 -row 13376 -row 13377 -row 13378 -row 13379 -row 13380 -row 13381 -row 13382 -row 13383 -row 13384 -row 13385 -row 13386 -row 13387 -row 13388 -row 13389 -row 13390 -row 13391 -row 13392 -row 13393 -row 13394 -row 13395 -row 13396 -row 13397 -row 13398 -row 13399 -row 13400 -row 13401 -row 13402 -row 13403 -row 13404 -row 13405 -row 13406 -row 13407 -row 13408 -row 13409 -row 13410 -row 13411 -row 13412 -row 13413 -row 13414 -row 13415 -row 13416 -row 13417 -row 13418 -row 13419 -row 13420 -row 13421 -row 13422 -row 13423 -row 13424 -row 13425 -row 13426 -row 13427 -row 13428 -row 13429 -row 13430 -row 13431 -row 13432 -row 13433 -row 13434 -row 13435 -row 13436 -row 13437 -row 13438 -row 13439 -row 13440 -row 13441 -row 13442 -row 13443 -row 13444 -row 13445 -row 13446 -row 13447 -row 13448 -row 13449 -row 13450 -row 13451 -row 13452 -row 13453 -row 13454 -row 13455 -row 13456 -row 13457 -row 13458 -row 13459 -row 13460 -row 13461 -row 13462 -row 13463 -row 13464 -row 13465 -row 13466 -row 13467 -row 13468 -row 13469 -row 13470 -row 13471 -row 13472 -row 13473 -row 13474 -row 13475 -row 13476 -row 13477 -row 13478 -row 13479 -row 13480 -row 13481 -row 13482 -row 13483 -row 13484 -row 13485 -row 13486 -row 13487 -row 13488 -row 13489 -row 13490 -row 13491 -row 13492 -row 13493 -row 13494 -row 13495 -row 13496 -row 13497 -row 13498 -row 13499 -row 13500 -row 13501 -row 13502 -row 13503 -row 13504 -row 13505 -row 13506 -row 13507 -row 13508 -row 13509 -row 13510 -row 13511 -row 13512 -row 13513 -row 13514 -row 13515 -row 13516 -row 13517 -row 13518 -row 13519 -row 13520 -row 13521 -row 13522 -row 13523 -row 13524 -row 13525 -row 13526 -row 13527 -row 13528 -row 13529 -row 13530 -row 13531 -row 13532 -row 13533 -row 13534 -row 13535 -row 13536 -row 13537 -row 13538 -row 13539 -row 13540 -row 13541 -row 13542 -row 13543 -row 13544 -row 13545 -row 13546 -row 13547 -row 13548 -row 13549 -row 13550 -row 13551 -row 13552 -row 13553 -row 13554 -row 13555 -row 13556 -row 13557 -row 13558 -row 13559 -row 13560 -row 13561 -row 13562 -row 13563 -row 13564 -row 13565 -row 13566 -row 13567 -row 13568 -row 13569 -row 13570 -row 13571 -row 13572 -row 13573 -row 13574 -row 13575 -row 13576 -row 13577 -row 13578 -row 13579 -row 13580 -row 13581 -row 13582 -row 13583 -row 13584 -row 13585 -row 13586 -row 13587 -row 13588 -row 13589 -row 13590 -row 13591 -row 13592 -row 13593 -row 13594 -row 13595 -row 13596 -row 13597 -row 13598 -row 13599 -row 13600 -row 13601 -row 13602 -row 13603 -row 13604 -row 13605 -row 13606 -row 13607 -row 13608 -row 13609 -row 13610 -row 13611 -row 13612 -row 13613 -row 13614 -row 13615 -row 13616 -row 13617 -row 13618 -row 13619 -row 13620 -row 13621 -row 13622 -row 13623 -row 13624 -row 13625 -row 13626 -row 13627 -row 13628 -row 13629 -row 13630 -row 13631 -row 13632 -row 13633 -row 13634 -row 13635 -row 13636 -row 13637 -row 13638 -row 13639 -row 13640 -row 13641 -row 13642 -row 13643 -row 13644 -row 13645 -row 13646 -row 13647 -row 13648 -row 13649 -row 13650 -row 13651 -row 13652 -row 13653 -row 13654 -row 13655 -row 13656 -row 13657 -row 13658 -row 13659 -row 13660 -row 13661 -row 13662 -row 13663 -row 13664 -row 13665 -row 13666 -row 13667 -row 13668 -row 13669 -row 13670 -row 13671 -row 13672 -row 13673 -row 13674 -row 13675 -row 13676 -row 13677 -row 13678 -row 13679 -row 13680 -row 13681 -row 13682 -row 13683 -row 13684 -row 13685 -row 13686 -row 13687 -row 13688 -row 13689 -row 13690 -row 13691 -row 13692 -row 13693 -row 13694 -row 13695 -row 13696 -row 13697 -row 13698 -row 13699 -row 13700 -row 13701 -row 13702 -row 13703 -row 13704 -row 13705 -row 13706 -row 13707 -row 13708 -row 13709 -row 13710 -row 13711 -row 13712 -row 13713 -row 13714 -row 13715 -row 13716 -row 13717 -row 13718 -row 13719 -row 13720 -row 13721 -row 13722 -row 13723 -row 13724 -row 13725 -row 13726 -row 13727 -row 13728 -row 13729 -row 13730 -row 13731 -row 13732 -row 13733 -row 13734 -row 13735 -row 13736 -row 13737 -row 13738 -row 13739 -row 13740 -row 13741 -row 13742 -row 13743 -row 13744 -row 13745 -row 13746 -row 13747 -row 13748 -row 13749 -row 13750 -row 13751 -row 13752 -row 13753 -row 13754 -row 13755 -row 13756 -row 13757 -row 13758 -row 13759 -row 13760 -row 13761 -row 13762 -row 13763 -row 13764 -row 13765 -row 13766 -row 13767 -row 13768 -row 13769 -row 13770 -row 13771 -row 13772 -row 13773 -row 13774 -row 13775 -row 13776 -row 13777 -row 13778 -row 13779 -row 13780 -row 13781 -row 13782 -row 13783 -row 13784 -row 13785 -row 13786 -row 13787 -row 13788 -row 13789 -row 13790 -row 13791 -row 13792 -row 13793 -row 13794 -row 13795 -row 13796 -row 13797 -row 13798 -row 13799 -row 13800 -row 13801 -row 13802 -row 13803 -row 13804 -row 13805 -row 13806 -row 13807 -row 13808 -row 13809 -row 13810 -row 13811 -row 13812 -row 13813 -row 13814 -row 13815 -row 13816 -row 13817 -row 13818 -row 13819 -row 13820 -row 13821 -row 13822 -row 13823 -row 13824 -row 13825 -row 13826 -row 13827 -row 13828 -row 13829 -row 13830 -row 13831 -row 13832 -row 13833 -row 13834 -row 13835 -row 13836 -row 13837 -row 13838 -row 13839 -row 13840 -row 13841 -row 13842 -row 13843 -row 13844 -row 13845 -row 13846 -row 13847 -row 13848 -row 13849 -row 13850 -row 13851 -row 13852 -row 13853 -row 13854 -row 13855 -row 13856 -row 13857 -row 13858 -row 13859 -row 13860 -row 13861 -row 13862 -row 13863 -row 13864 -row 13865 -row 13866 -row 13867 -row 13868 -row 13869 -row 13870 -row 13871 -row 13872 -row 13873 -row 13874 -row 13875 -row 13876 -row 13877 -row 13878 -row 13879 -row 13880 -row 13881 -row 13882 -row 13883 -row 13884 -row 13885 -row 13886 -row 13887 -row 13888 -row 13889 -row 13890 -row 13891 -row 13892 -row 13893 -row 13894 -row 13895 -row 13896 -row 13897 -row 13898 -row 13899 -row 13900 -row 13901 -row 13902 -row 13903 -row 13904 -row 13905 -row 13906 -row 13907 -row 13908 -row 13909 -row 13910 -row 13911 -row 13912 -row 13913 -row 13914 -row 13915 -row 13916 -row 13917 -row 13918 -row 13919 -row 13920 -row 13921 -row 13922 -row 13923 -row 13924 -row 13925 -row 13926 -row 13927 -row 13928 -row 13929 -row 13930 -row 13931 -row 13932 -row 13933 -row 13934 -row 13935 -row 13936 -row 13937 -row 13938 -row 13939 -row 13940 -row 13941 -row 13942 -row 13943 -row 13944 -row 13945 -row 13946 -row 13947 -row 13948 -row 13949 -row 13950 -row 13951 -row 13952 -row 13953 -row 13954 -row 13955 -row 13956 -row 13957 -row 13958 -row 13959 -row 13960 -row 13961 -row 13962 -row 13963 -row 13964 -row 13965 -row 13966 -row 13967 -row 13968 -row 13969 -row 13970 -row 13971 -row 13972 -row 13973 -row 13974 -row 13975 -row 13976 -row 13977 -row 13978 -row 13979 -row 13980 -row 13981 -row 13982 -row 13983 -row 13984 -row 13985 -row 13986 -row 13987 -row 13988 -row 13989 -row 13990 -row 13991 -row 13992 -row 13993 -row 13994 -row 13995 -row 13996 -row 13997 -row 13998 -row 13999 -row 14000 -row 14001 -row 14002 -row 14003 -row 14004 -row 14005 -row 14006 -row 14007 -row 14008 -row 14009 -row 14010 -row 14011 -row 14012 -row 14013 -row 14014 -row 14015 -row 14016 -row 14017 -row 14018 -row 14019 -row 14020 -row 14021 -row 14022 -row 14023 -row 14024 -row 14025 -row 14026 -row 14027 -row 14028 -row 14029 -row 14030 -row 14031 -row 14032 -row 14033 -row 14034 -row 14035 -row 14036 -row 14037 -row 14038 -row 14039 -row 14040 -row 14041 -row 14042 -row 14043 -row 14044 -row 14045 -row 14046 -row 14047 -row 14048 -row 14049 -row 14050 -row 14051 -row 14052 -row 14053 -row 14054 -row 14055 -row 14056 -row 14057 -row 14058 -row 14059 -row 14060 -row 14061 -row 14062 -row 14063 -row 14064 -row 14065 -row 14066 -row 14067 -row 14068 -row 14069 -row 14070 -row 14071 -row 14072 -row 14073 -row 14074 -row 14075 -row 14076 -row 14077 -row 14078 -row 14079 -row 14080 -row 14081 -row 14082 -row 14083 -row 14084 -row 14085 -row 14086 -row 14087 -row 14088 -row 14089 -row 14090 -row 14091 -row 14092 -row 14093 -row 14094 -row 14095 -row 14096 -row 14097 -row 14098 -row 14099 -row 14100 -row 14101 -row 14102 -row 14103 -row 14104 -row 14105 -row 14106 -row 14107 -row 14108 -row 14109 -row 14110 -row 14111 -row 14112 -row 14113 -row 14114 -row 14115 -row 14116 -row 14117 -row 14118 -row 14119 -row 14120 -row 14121 -row 14122 -row 14123 -row 14124 -row 14125 -row 14126 -row 14127 -row 14128 -row 14129 -row 14130 -row 14131 -row 14132 -row 14133 -row 14134 -row 14135 -row 14136 -row 14137 -row 14138 -row 14139 -row 14140 -row 14141 -row 14142 -row 14143 -row 14144 -row 14145 -row 14146 -row 14147 -row 14148 -row 14149 -row 14150 -row 14151 -row 14152 -row 14153 -row 14154 -row 14155 -row 14156 -row 14157 -row 14158 -row 14159 -row 14160 -row 14161 -row 14162 -row 14163 -row 14164 -row 14165 -row 14166 -row 14167 -row 14168 -row 14169 -row 14170 -row 14171 -row 14172 -row 14173 -row 14174 -row 14175 -row 14176 -row 14177 -row 14178 -row 14179 -row 14180 -row 14181 -row 14182 -row 14183 -row 14184 -row 14185 -row 14186 -row 14187 -row 14188 -row 14189 -row 14190 -row 14191 -row 14192 -row 14193 -row 14194 -row 14195 -row 14196 -row 14197 -row 14198 -row 14199 -row 14200 -row 14201 -row 14202 -row 14203 -row 14204 -row 14205 -row 14206 -row 14207 -row 14208 -row 14209 -row 14210 -row 14211 -row 14212 -row 14213 -row 14214 -row 14215 -row 14216 -row 14217 -row 14218 -row 14219 -row 14220 -row 14221 -row 14222 -row 14223 -row 14224 -row 14225 -row 14226 -row 14227 -row 14228 -row 14229 -row 14230 -row 14231 -row 14232 -row 14233 -row 14234 -row 14235 -row 14236 -row 14237 -row 14238 -row 14239 -row 14240 -row 14241 -row 14242 -row 14243 -row 14244 -row 14245 -row 14246 -row 14247 -row 14248 -row 14249 -row 14250 -row 14251 -row 14252 -row 14253 -row 14254 -row 14255 -row 14256 -row 14257 -row 14258 -row 14259 -row 14260 -row 14261 -row 14262 -row 14263 -row 14264 -row 14265 -row 14266 -row 14267 -row 14268 -row 14269 -row 14270 -row 14271 -row 14272 -row 14273 -row 14274 -row 14275 -row 14276 -row 14277 -row 14278 -row 14279 -row 14280 -row 14281 -row 14282 -row 14283 -row 14284 -row 14285 -row 14286 -row 14287 -row 14288 -row 14289 -row 14290 -row 14291 -row 14292 -row 14293 -row 14294 -row 14295 -row 14296 -row 14297 -row 14298 -row 14299 -row 14300 -row 14301 -row 14302 -row 14303 -row 14304 -row 14305 -row 14306 -row 14307 -row 14308 -row 14309 -row 14310 -row 14311 -row 14312 -row 14313 -row 14314 -row 14315 -row 14316 -row 14317 -row 14318 -row 14319 -row 14320 -row 14321 -row 14322 -row 14323 -row 14324 -row 14325 -row 14326 -row 14327 -row 14328 -row 14329 -row 14330 -row 14331 -row 14332 -row 14333 -row 14334 -row 14335 -row 14336 -row 14337 -row 14338 -row 14339 -row 14340 -row 14341 -row 14342 -row 14343 -row 14344 -row 14345 -row 14346 -row 14347 -row 14348 -row 14349 -row 14350 -row 14351 -row 14352 -row 14353 -row 14354 -row 14355 -row 14356 -row 14357 -row 14358 -row 14359 -row 14360 -row 14361 -row 14362 -row 14363 -row 14364 -row 14365 -row 14366 -row 14367 -row 14368 -row 14369 -row 14370 -row 14371 -row 14372 -row 14373 -row 14374 -row 14375 -row 14376 -row 14377 -row 14378 -row 14379 -row 14380 -row 14381 -row 14382 -row 14383 -row 14384 -row 14385 -row 14386 -row 14387 -row 14388 -row 14389 -row 14390 -row 14391 -row 14392 -row 14393 -row 14394 -row 14395 -row 14396 -row 14397 -row 14398 -row 14399 -row 14400 -row 14401 -row 14402 -row 14403 -row 14404 -row 14405 -row 14406 -row 14407 -row 14408 -row 14409 -row 14410 -row 14411 -row 14412 -row 14413 -row 14414 -row 14415 -row 14416 -row 14417 -row 14418 -row 14419 -row 14420 -row 14421 -row 14422 -row 14423 -row 14424 -row 14425 -row 14426 -row 14427 -row 14428 -row 14429 -row 14430 -row 14431 -row 14432 -row 14433 -row 14434 -row 14435 -row 14436 -row 14437 -row 14438 -row 14439 -row 14440 -row 14441 -row 14442 -row 14443 -row 14444 -row 14445 -row 14446 -row 14447 -row 14448 -row 14449 -row 14450 -row 14451 -row 14452 -row 14453 -row 14454 -row 14455 -row 14456 -row 14457 -row 14458 -row 14459 -row 14460 -row 14461 -row 14462 -row 14463 -row 14464 -row 14465 -row 14466 -row 14467 -row 14468 -row 14469 -row 14470 -row 14471 -row 14472 -row 14473 -row 14474 -row 14475 -row 14476 -row 14477 -row 14478 -row 14479 -row 14480 -row 14481 -row 14482 -row 14483 -row 14484 -row 14485 -row 14486 -row 14487 -row 14488 -row 14489 -row 14490 -row 14491 -row 14492 -row 14493 -row 14494 -row 14495 -row 14496 -row 14497 -row 14498 -row 14499 -row 14500 -row 14501 -row 14502 -row 14503 -row 14504 -row 14505 -row 14506 -row 14507 -row 14508 -row 14509 -row 14510 -row 14511 -row 14512 -row 14513 -row 14514 -row 14515 -row 14516 -row 14517 -row 14518 -row 14519 -row 14520 -row 14521 -row 14522 -row 14523 -row 14524 -row 14525 -row 14526 -row 14527 -row 14528 -row 14529 -row 14530 -row 14531 -row 14532 -row 14533 -row 14534 -row 14535 -row 14536 -row 14537 -row 14538 -row 14539 -row 14540 -row 14541 -row 14542 -row 14543 -row 14544 -row 14545 -row 14546 -row 14547 -row 14548 -row 14549 -row 14550 -row 14551 -row 14552 -row 14553 -row 14554 -row 14555 -row 14556 -row 14557 -row 14558 -row 14559 -row 14560 -row 14561 -row 14562 -row 14563 -row 14564 -row 14565 -row 14566 -row 14567 -row 14568 -row 14569 -row 14570 -row 14571 -row 14572 -row 14573 -row 14574 -row 14575 -row 14576 -row 14577 -row 14578 -row 14579 -row 14580 -row 14581 -row 14582 -row 14583 -row 14584 -row 14585 -row 14586 -row 14587 -row 14588 -row 14589 -row 14590 -row 14591 -row 14592 -row 14593 -row 14594 -row 14595 -row 14596 -row 14597 -row 14598 -row 14599 -row 14600 -row 14601 -row 14602 -row 14603 -row 14604 -row 14605 -row 14606 -row 14607 -row 14608 -row 14609 -row 14610 -row 14611 -row 14612 -row 14613 -row 14614 -row 14615 -row 14616 -row 14617 -row 14618 -row 14619 -row 14620 -row 14621 -row 14622 -row 14623 -row 14624 -row 14625 -row 14626 -row 14627 -row 14628 -row 14629 -row 14630 -row 14631 -row 14632 -row 14633 -row 14634 -row 14635 -row 14636 -row 14637 -row 14638 -row 14639 -row 14640 -row 14641 -row 14642 -row 14643 -row 14644 -row 14645 -row 14646 -row 14647 -row 14648 -row 14649 -row 14650 -row 14651 -row 14652 -row 14653 -row 14654 -row 14655 -row 14656 -row 14657 -row 14658 -row 14659 -row 14660 -row 14661 -row 14662 -row 14663 -row 14664 -row 14665 -row 14666 -row 14667 -row 14668 -row 14669 -row 14670 -row 14671 -row 14672 -row 14673 -row 14674 -row 14675 -row 14676 -row 14677 -row 14678 -row 14679 -row 14680 -row 14681 -row 14682 -row 14683 -row 14684 -row 14685 -row 14686 -row 14687 -row 14688 -row 14689 -row 14690 -row 14691 -row 14692 -row 14693 -row 14694 -row 14695 -row 14696 -row 14697 -row 14698 -row 14699 -row 14700 -row 14701 -row 14702 -row 14703 -row 14704 -row 14705 -row 14706 -row 14707 -row 14708 -row 14709 -row 14710 -row 14711 -row 14712 -row 14713 -row 14714 -row 14715 -row 14716 -row 14717 -row 14718 -row 14719 -row 14720 -row 14721 -row 14722 -row 14723 -row 14724 -row 14725 -row 14726 -row 14727 -row 14728 -row 14729 -row 14730 -row 14731 -row 14732 -row 14733 -row 14734 -row 14735 -row 14736 -row 14737 -row 14738 -row 14739 -row 14740 -row 14741 -row 14742 -row 14743 -row 14744 -row 14745 -row 14746 -row 14747 -row 14748 -row 14749 -row 14750 -row 14751 -row 14752 -row 14753 -row 14754 -row 14755 -row 14756 -row 14757 -row 14758 -row 14759 -row 14760 -row 14761 -row 14762 -row 14763 -row 14764 -row 14765 -row 14766 -row 14767 -row 14768 -row 14769 -row 14770 -row 14771 -row 14772 -row 14773 -row 14774 -row 14775 -row 14776 -row 14777 -row 14778 -row 14779 -row 14780 -row 14781 -row 14782 -row 14783 -row 14784 -row 14785 -row 14786 -row 14787 -row 14788 -row 14789 -row 14790 -row 14791 -row 14792 -row 14793 -row 14794 -row 14795 -row 14796 -row 14797 -row 14798 -row 14799 -row 14800 -row 14801 -row 14802 -row 14803 -row 14804 -row 14805 -row 14806 -row 14807 -row 14808 -row 14809 -row 14810 -row 14811 -row 14812 -row 14813 -row 14814 -row 14815 -row 14816 -row 14817 -row 14818 -row 14819 -row 14820 -row 14821 -row 14822 -row 14823 -row 14824 -row 14825 -row 14826 -row 14827 -row 14828 -row 14829 -row 14830 -row 14831 -row 14832 -row 14833 -row 14834 -row 14835 -row 14836 -row 14837 -row 14838 -row 14839 -row 14840 -row 14841 -row 14842 -row 14843 -row 14844 -row 14845 -row 14846 -row 14847 -row 14848 -row 14849 -row 14850 -row 14851 -row 14852 -row 14853 -row 14854 -row 14855 -row 14856 -row 14857 -row 14858 -row 14859 -row 14860 -row 14861 -row 14862 -row 14863 -row 14864 -row 14865 -row 14866 -row 14867 -row 14868 -row 14869 -row 14870 -row 14871 -row 14872 -row 14873 -row 14874 -row 14875 -row 14876 -row 14877 -row 14878 -row 14879 -row 14880 -row 14881 -row 14882 -row 14883 -row 14884 -row 14885 -row 14886 -row 14887 -row 14888 -row 14889 -row 14890 -row 14891 -row 14892 -row 14893 -row 14894 -row 14895 -row 14896 -row 14897 -row 14898 -row 14899 -row 14900 -row 14901 -row 14902 -row 14903 -row 14904 -row 14905 -row 14906 -row 14907 -row 14908 -row 14909 -row 14910 -row 14911 -row 14912 -row 14913 -row 14914 -row 14915 -row 14916 -row 14917 -row 14918 -row 14919 -row 14920 -row 14921 -row 14922 -row 14923 -row 14924 -row 14925 -row 14926 -row 14927 -row 14928 -row 14929 -row 14930 -row 14931 -row 14932 -row 14933 -row 14934 -row 14935 -row 14936 -row 14937 -row 14938 -row 14939 -row 14940 -row 14941 -row 14942 -row 14943 -row 14944 -row 14945 -row 14946 -row 14947 -row 14948 -row 14949 -row 14950 -row 14951 -row 14952 -row 14953 -row 14954 -row 14955 -row 14956 -row 14957 -row 14958 -row 14959 -row 14960 -row 14961 -row 14962 -row 14963 -row 14964 -row 14965 -row 14966 -row 14967 -row 14968 -row 14969 -row 14970 -row 14971 -row 14972 -row 14973 -row 14974 -row 14975 -row 14976 -row 14977 -row 14978 -row 14979 -row 14980 -row 14981 -row 14982 -row 14983 -row 14984 -row 14985 -row 14986 -row 14987 -row 14988 -row 14989 -row 14990 -row 14991 -row 14992 -row 14993 -row 14994 -row 14995 -row 14996 -row 14997 -row 14998 -row 14999 -row 15000 -row 15001 -row 15002 -row 15003 -row 15004 -row 15005 -row 15006 -row 15007 -row 15008 -row 15009 -row 15010 -row 15011 -row 15012 -row 15013 -row 15014 -row 15015 -row 15016 -row 15017 -row 15018 -row 15019 -row 15020 -row 15021 -row 15022 -row 15023 -row 15024 -row 15025 -row 15026 -row 15027 -row 15028 -row 15029 -row 15030 -row 15031 -row 15032 -row 15033 -row 15034 -row 15035 -row 15036 -row 15037 -row 15038 -row 15039 -row 15040 -row 15041 -row 15042 -row 15043 -row 15044 -row 15045 -row 15046 -row 15047 -row 15048 -row 15049 -row 15050 -row 15051 -row 15052 -row 15053 -row 15054 -row 15055 -row 15056 -row 15057 -row 15058 -row 15059 -row 15060 -row 15061 -row 15062 -row 15063 -row 15064 -row 15065 -row 15066 -row 15067 -row 15068 -row 15069 -row 15070 -row 15071 -row 15072 -row 15073 -row 15074 -row 15075 -row 15076 -row 15077 -row 15078 -row 15079 -row 15080 -row 15081 -row 15082 -row 15083 -row 15084 -row 15085 -row 15086 -row 15087 -row 15088 -row 15089 -row 15090 -row 15091 -row 15092 -row 15093 -row 15094 -row 15095 -row 15096 -row 15097 -row 15098 -row 15099 -row 15100 -row 15101 -row 15102 -row 15103 -row 15104 -row 15105 -row 15106 -row 15107 -row 15108 -row 15109 -row 15110 -row 15111 -row 15112 -row 15113 -row 15114 -row 15115 -row 15116 -row 15117 -row 15118 -row 15119 -row 15120 -row 15121 -row 15122 -row 15123 -row 15124 -row 15125 -row 15126 -row 15127 -row 15128 -row 15129 -row 15130 -row 15131 -row 15132 -row 15133 -row 15134 -row 15135 -row 15136 -row 15137 -row 15138 -row 15139 -row 15140 -row 15141 -row 15142 -row 15143 -row 15144 -row 15145 -row 15146 -row 15147 -row 15148 -row 15149 -row 15150 -row 15151 -row 15152 -row 15153 -row 15154 -row 15155 -row 15156 -row 15157 -row 15158 -row 15159 -row 15160 -row 15161 -row 15162 -row 15163 -row 15164 -row 15165 -row 15166 -row 15167 -row 15168 -row 15169 -row 15170 -row 15171 -row 15172 -row 15173 -row 15174 -row 15175 -row 15176 -row 15177 -row 15178 -row 15179 -row 15180 -row 15181 -row 15182 -row 15183 -row 15184 -row 15185 -row 15186 -row 15187 -row 15188 -row 15189 -row 15190 -row 15191 -row 15192 -row 15193 -row 15194 -row 15195 -row 15196 -row 15197 -row 15198 -row 15199 -row 15200 -row 15201 -row 15202 -row 15203 -row 15204 -row 15205 -row 15206 -row 15207 -row 15208 -row 15209 -row 15210 -row 15211 -row 15212 -row 15213 -row 15214 -row 15215 -row 15216 -row 15217 -row 15218 -row 15219 -row 15220 -row 15221 -row 15222 -row 15223 -row 15224 -row 15225 -row 15226 -row 15227 -row 15228 -row 15229 -row 15230 -row 15231 -row 15232 -row 15233 -row 15234 -row 15235 -row 15236 -row 15237 -row 15238 -row 15239 -row 15240 -row 15241 -row 15242 -row 15243 -row 15244 -row 15245 -row 15246 -row 15247 -row 15248 -row 15249 -row 15250 -row 15251 -row 15252 -row 15253 -row 15254 -row 15255 -row 15256 -row 15257 -row 15258 -row 15259 -row 15260 -row 15261 -row 15262 -row 15263 -row 15264 -row 15265 -row 15266 -row 15267 -row 15268 -row 15269 -row 15270 -row 15271 -row 15272 -row 15273 -row 15274 -row 15275 -row 15276 -row 15277 -row 15278 -row 15279 -row 15280 -row 15281 -row 15282 -row 15283 -row 15284 -row 15285 -row 15286 -row 15287 -row 15288 -row 15289 -row 15290 -row 15291 -row 15292 -row 15293 -row 15294 -row 15295 -row 15296 -row 15297 -row 15298 -row 15299 -row 15300 -row 15301 -row 15302 -row 15303 -row 15304 -row 15305 -row 15306 -row 15307 -row 15308 -row 15309 -row 15310 -row 15311 -row 15312 -row 15313 -row 15314 -row 15315 -row 15316 -row 15317 -row 15318 -row 15319 -row 15320 -row 15321 -row 15322 -row 15323 -row 15324 -row 15325 -row 15326 -row 15327 -row 15328 -row 15329 -row 15330 -row 15331 -row 15332 -row 15333 -row 15334 -row 15335 -row 15336 -row 15337 -row 15338 -row 15339 -row 15340 -row 15341 -row 15342 -row 15343 -row 15344 -row 15345 -row 15346 -row 15347 -row 15348 -row 15349 -row 15350 -row 15351 -row 15352 -row 15353 -row 15354 -row 15355 -row 15356 -row 15357 -row 15358 -row 15359 -row 15360 -row 15361 -row 15362 -row 15363 -row 15364 -row 15365 -row 15366 -row 15367 -row 15368 -row 15369 -row 15370 -row 15371 -row 15372 -row 15373 -row 15374 -row 15375 -row 15376 -row 15377 -row 15378 -row 15379 -row 15380 -row 15381 -row 15382 -row 15383 -row 15384 -row 15385 -row 15386 -row 15387 -row 15388 -row 15389 -row 15390 -row 15391 -row 15392 -row 15393 -row 15394 -row 15395 -row 15396 -row 15397 -row 15398 -row 15399 -row 15400 -row 15401 -row 15402 -row 15403 -row 15404 -row 15405 -row 15406 -row 15407 -row 15408 -row 15409 -row 15410 -row 15411 -row 15412 -row 15413 -row 15414 -row 15415 -row 15416 -row 15417 -row 15418 -row 15419 -row 15420 -row 15421 -row 15422 -row 15423 -row 15424 -row 15425 -row 15426 -row 15427 -row 15428 -row 15429 -row 15430 -row 15431 -row 15432 -row 15433 -row 15434 -row 15435 -row 15436 -row 15437 -row 15438 -row 15439 -row 15440 -row 15441 -row 15442 -row 15443 -row 15444 -row 15445 -row 15446 -row 15447 -row 15448 -row 15449 -row 15450 -row 15451 -row 15452 -row 15453 -row 15454 -row 15455 -row 15456 -row 15457 -row 15458 -row 15459 -row 15460 -row 15461 -row 15462 -row 15463 -row 15464 -row 15465 -row 15466 -row 15467 -row 15468 -row 15469 -row 15470 -row 15471 -row 15472 -row 15473 -row 15474 -row 15475 -row 15476 -row 15477 -row 15478 -row 15479 -row 15480 -row 15481 -row 15482 -row 15483 -row 15484 -row 15485 -row 15486 -row 15487 -row 15488 -row 15489 -row 15490 -row 15491 -row 15492 -row 15493 -row 15494 -row 15495 -row 15496 -row 15497 -row 15498 -row 15499 -row 15500 -row 15501 -row 15502 -row 15503 -row 15504 -row 15505 -row 15506 -row 15507 -row 15508 -row 15509 -row 15510 -row 15511 -row 15512 -row 15513 -row 15514 -row 15515 -row 15516 -row 15517 -row 15518 -row 15519 -row 15520 -row 15521 -row 15522 -row 15523 -row 15524 -row 15525 -row 15526 -row 15527 -row 15528 -row 15529 -row 15530 -row 15531 -row 15532 -row 15533 -row 15534 -row 15535 -row 15536 -row 15537 -row 15538 -row 15539 -row 15540 -row 15541 -row 15542 -row 15543 -row 15544 -row 15545 -row 15546 -row 15547 -row 15548 -row 15549 -row 15550 -row 15551 -row 15552 -row 15553 -row 15554 -row 15555 -row 15556 -row 15557 -row 15558 -row 15559 -row 15560 -row 15561 -row 15562 -row 15563 -row 15564 -row 15565 -row 15566 -row 15567 -row 15568 -row 15569 -row 15570 -row 15571 -row 15572 -row 15573 -row 15574 -row 15575 -row 15576 -row 15577 -row 15578 -row 15579 -row 15580 -row 15581 -row 15582 -row 15583 -row 15584 -row 15585 -row 15586 -row 15587 -row 15588 -row 15589 -row 15590 -row 15591 -row 15592 -row 15593 -row 15594 -row 15595 -row 15596 -row 15597 -row 15598 -row 15599 -row 15600 -row 15601 -row 15602 -row 15603 -row 15604 -row 15605 -row 15606 -row 15607 -row 15608 -row 15609 -row 15610 -row 15611 -row 15612 -row 15613 -row 15614 -row 15615 -row 15616 -row 15617 -row 15618 -row 15619 -row 15620 -row 15621 -row 15622 -row 15623 -row 15624 -row 15625 -row 15626 -row 15627 -row 15628 -row 15629 -row 15630 -row 15631 -row 15632 -row 15633 -row 15634 -row 15635 -row 15636 -row 15637 -row 15638 -row 15639 -row 15640 -row 15641 -row 15642 -row 15643 -row 15644 -row 15645 -row 15646 -row 15647 -row 15648 -row 15649 -row 15650 -row 15651 -row 15652 -row 15653 -row 15654 -row 15655 -row 15656 -row 15657 -row 15658 -row 15659 -row 15660 -row 15661 -row 15662 -row 15663 -row 15664 -row 15665 -row 15666 -row 15667 -row 15668 -row 15669 -row 15670 -row 15671 -row 15672 -row 15673 -row 15674 -row 15675 -row 15676 -row 15677 -row 15678 -row 15679 -row 15680 -row 15681 -row 15682 -row 15683 -row 15684 -row 15685 -row 15686 -row 15687 -row 15688 -row 15689 -row 15690 -row 15691 -row 15692 -row 15693 -row 15694 -row 15695 -row 15696 -row 15697 -row 15698 -row 15699 -row 15700 -row 15701 -row 15702 -row 15703 -row 15704 -row 15705 -row 15706 -row 15707 -row 15708 -row 15709 -row 15710 -row 15711 -row 15712 -row 15713 -row 15714 -row 15715 -row 15716 -row 15717 -row 15718 -row 15719 -row 15720 -row 15721 -row 15722 -row 15723 -row 15724 -row 15725 -row 15726 -row 15727 -row 15728 -row 15729 -row 15730 -row 15731 -row 15732 -row 15733 -row 15734 -row 15735 -row 15736 -row 15737 -row 15738 -row 15739 -row 15740 -row 15741 -row 15742 -row 15743 -row 15744 -row 15745 -row 15746 -row 15747 -row 15748 -row 15749 -row 15750 -row 15751 -row 15752 -row 15753 -row 15754 -row 15755 -row 15756 -row 15757 -row 15758 -row 15759 -row 15760 -row 15761 -row 15762 -row 15763 -row 15764 -row 15765 -row 15766 -row 15767 -row 15768 -row 15769 -row 15770 -row 15771 -row 15772 -row 15773 -row 15774 -row 15775 -row 15776 -row 15777 -row 15778 -row 15779 -row 15780 -row 15781 -row 15782 -row 15783 -row 15784 -row 15785 -row 15786 -row 15787 -row 15788 -row 15789 -row 15790 -row 15791 -row 15792 -row 15793 -row 15794 -row 15795 -row 15796 -row 15797 -row 15798 -row 15799 -row 15800 -row 15801 -row 15802 -row 15803 -row 15804 -row 15805 -row 15806 -row 15807 -row 15808 -row 15809 -row 15810 -row 15811 -row 15812 -row 15813 -row 15814 -row 15815 -row 15816 -row 15817 -row 15818 -row 15819 -row 15820 -row 15821 -row 15822 -row 15823 -row 15824 -row 15825 -row 15826 -row 15827 -row 15828 -row 15829 -row 15830 -row 15831 -row 15832 -row 15833 -row 15834 -row 15835 -row 15836 -row 15837 -row 15838 -row 15839 -row 15840 -row 15841 -row 15842 -row 15843 -row 15844 -row 15845 -row 15846 -row 15847 -row 15848 -row 15849 -row 15850 -row 15851 -row 15852 -row 15853 -row 15854 -row 15855 -row 15856 -row 15857 -row 15858 -row 15859 -row 15860 -row 15861 -row 15862 -row 15863 -row 15864 -row 15865 -row 15866 -row 15867 -row 15868 -row 15869 -row 15870 -row 15871 -row 15872 -row 15873 -row 15874 -row 15875 -row 15876 -row 15877 -row 15878 -row 15879 -row 15880 -row 15881 -row 15882 -row 15883 -row 15884 -row 15885 -row 15886 -row 15887 -row 15888 -row 15889 -row 15890 -row 15891 -row 15892 -row 15893 -row 15894 -row 15895 -row 15896 -row 15897 -row 15898 -row 15899 -row 15900 -row 15901 -row 15902 -row 15903 -row 15904 -row 15905 -row 15906 -row 15907 -row 15908 -row 15909 -row 15910 -row 15911 -row 15912 -row 15913 -row 15914 -row 15915 -row 15916 -row 15917 -row 15918 -row 15919 -row 15920 -row 15921 -row 15922 -row 15923 -row 15924 -row 15925 -row 15926 -row 15927 -row 15928 -row 15929 -row 15930 -row 15931 -row 15932 -row 15933 -row 15934 -row 15935 -row 15936 -row 15937 -row 15938 -row 15939 -row 15940 -row 15941 -row 15942 -row 15943 -row 15944 -row 15945 -row 15946 -row 15947 -row 15948 -row 15949 -row 15950 -row 15951 -row 15952 -row 15953 -row 15954 -row 15955 -row 15956 -row 15957 -row 15958 -row 15959 -row 15960 -row 15961 -row 15962 -row 15963 -row 15964 -row 15965 -row 15966 -row 15967 -row 15968 -row 15969 -row 15970 -row 15971 -row 15972 -row 15973 -row 15974 -row 15975 -row 15976 -row 15977 -row 15978 -row 15979 -row 15980 -row 15981 -row 15982 -row 15983 -row 15984 -row 15985 -row 15986 -row 15987 -row 15988 -row 15989 -row 15990 -row 15991 -row 15992 -row 15993 -row 15994 -row 15995 -row 15996 -row 15997 -row 15998 -row 15999 -row 16000 -row 16001 -row 16002 -row 16003 -row 16004 -row 16005 -row 16006 -row 16007 -row 16008 -row 16009 -row 16010 -row 16011 -row 16012 -row 16013 -row 16014 -row 16015 -row 16016 -row 16017 -row 16018 -row 16019 -row 16020 -row 16021 -row 16022 -row 16023 -row 16024 -row 16025 -row 16026 -row 16027 -row 16028 -row 16029 -row 16030 -row 16031 -row 16032 -row 16033 -row 16034 -row 16035 -row 16036 -row 16037 -row 16038 -row 16039 -row 16040 -row 16041 -row 16042 -row 16043 -row 16044 -row 16045 -row 16046 -row 16047 -row 16048 -row 16049 -row 16050 -row 16051 -row 16052 -row 16053 -row 16054 -row 16055 -row 16056 -row 16057 -row 16058 -row 16059 -row 16060 -row 16061 -row 16062 -row 16063 -row 16064 -row 16065 -row 16066 -row 16067 -row 16068 -row 16069 -row 16070 -row 16071 -row 16072 -row 16073 -row 16074 -row 16075 -row 16076 -row 16077 -row 16078 -row 16079 -row 16080 -row 16081 -row 16082 -row 16083 -row 16084 -row 16085 -row 16086 -row 16087 -row 16088 -row 16089 -row 16090 -row 16091 -row 16092 -row 16093 -row 16094 -row 16095 -row 16096 -row 16097 -row 16098 -row 16099 -row 16100 -row 16101 -row 16102 -row 16103 -row 16104 -row 16105 -row 16106 -row 16107 -row 16108 -row 16109 -row 16110 -row 16111 -row 16112 -row 16113 -row 16114 -row 16115 -row 16116 -row 16117 -row 16118 -row 16119 -row 16120 -row 16121 -row 16122 -row 16123 -row 16124 -row 16125 -row 16126 -row 16127 -row 16128 -row 16129 -row 16130 -row 16131 -row 16132 -row 16133 -row 16134 -row 16135 -row 16136 -row 16137 -row 16138 -row 16139 -row 16140 -row 16141 -row 16142 -row 16143 -row 16144 -row 16145 -row 16146 -row 16147 -row 16148 -row 16149 -row 16150 -row 16151 -row 16152 -row 16153 -row 16154 -row 16155 -row 16156 -row 16157 -row 16158 -row 16159 -row 16160 -row 16161 -row 16162 -row 16163 -row 16164 -row 16165 -row 16166 -row 16167 -row 16168 -row 16169 -row 16170 -row 16171 -row 16172 -row 16173 -row 16174 -row 16175 -row 16176 -row 16177 -row 16178 -row 16179 -row 16180 -row 16181 -row 16182 -row 16183 -row 16184 -row 16185 -row 16186 -row 16187 -row 16188 -row 16189 -row 16190 -row 16191 -row 16192 -row 16193 -row 16194 -row 16195 -row 16196 -row 16197 -row 16198 -row 16199 -row 16200 -row 16201 -row 16202 -row 16203 -row 16204 -row 16205 -row 16206 -row 16207 -row 16208 -row 16209 -row 16210 -row 16211 -row 16212 -row 16213 -row 16214 -row 16215 -row 16216 -row 16217 -row 16218 -row 16219 -row 16220 -row 16221 -row 16222 -row 16223 -row 16224 -row 16225 -row 16226 -row 16227 -row 16228 -row 16229 -row 16230 -row 16231 -row 16232 -row 16233 -row 16234 -row 16235 -row 16236 -row 16237 -row 16238 -row 16239 -row 16240 -row 16241 -row 16242 -row 16243 -row 16244 -row 16245 -row 16246 -row 16247 -row 16248 -row 16249 -row 16250 -row 16251 -row 16252 -row 16253 -row 16254 -row 16255 -row 16256 -row 16257 -row 16258 -row 16259 -row 16260 -row 16261 -row 16262 -row 16263 -row 16264 -row 16265 -row 16266 -row 16267 -row 16268 -row 16269 -row 16270 -row 16271 -row 16272 -row 16273 -row 16274 -row 16275 -row 16276 -row 16277 -row 16278 -row 16279 -row 16280 -row 16281 -row 16282 -row 16283 -row 16284 -row 16285 -row 16286 -row 16287 -row 16288 -row 16289 -row 16290 -row 16291 -row 16292 -row 16293 -row 16294 -row 16295 -row 16296 -row 16297 -row 16298 -row 16299 -row 16300 -row 16301 -row 16302 -row 16303 -row 16304 -row 16305 -row 16306 -row 16307 -row 16308 -row 16309 -row 16310 -row 16311 -row 16312 -row 16313 -row 16314 -row 16315 -row 16316 -row 16317 -row 16318 -row 16319 -row 16320 -row 16321 -row 16322 -row 16323 -row 16324 -row 16325 -row 16326 -row 16327 -row 16328 -row 16329 -row 16330 -row 16331 -row 16332 -row 16333 -row 16334 -row 16335 -row 16336 -row 16337 -row 16338 -row 16339 -row 16340 -row 16341 -row 16342 -row 16343 -row 16344 -row 16345 -row 16346 -row 16347 -row 16348 -row 16349 -row 16350 -row 16351 -row 16352 -row 16353 -row 16354 -row 16355 -row 16356 -row 16357 -row 16358 -row 16359 -row 16360 -row 16361 -row 16362 -row 16363 -row 16364 -row 16365 -row 16366 -row 16367 -row 16368 -row 16369 -row 16370 -row 16371 -row 16372 -row 16373 -row 16374 -row 16375 -row 16376 -row 16377 -row 16378 -row 16379 -row 16380 -row 16381 -row 16382 -row 16383 -row 16384 -row 16385 -row 16386 -row 16387 -row 16388 -row 16389 -row 16390 -row 16391 -row 16392 -row 16393 -row 16394 -row 16395 -row 16396 -row 16397 -row 16398 -row 16399 -row 16400 -row 16401 -row 16402 -row 16403 -row 16404 -row 16405 -row 16406 -row 16407 -row 16408 -row 16409 -row 16410 -row 16411 -row 16412 -row 16413 -row 16414 -row 16415 -row 16416 -row 16417 -row 16418 -row 16419 -row 16420 -row 16421 -row 16422 -row 16423 -row 16424 -row 16425 -row 16426 -row 16427 -row 16428 -row 16429 -row 16430 -row 16431 -row 16432 -row 16433 -row 16434 -row 16435 -row 16436 -row 16437 -row 16438 -row 16439 -row 16440 -row 16441 -row 16442 -row 16443 -row 16444 -row 16445 -row 16446 -row 16447 -row 16448 -row 16449 -row 16450 -row 16451 -row 16452 -row 16453 -row 16454 -row 16455 -row 16456 -row 16457 -row 16458 -row 16459 -row 16460 -row 16461 -row 16462 -row 16463 -row 16464 -row 16465 -row 16466 -row 16467 -row 16468 -row 16469 -row 16470 -row 16471 -row 16472 -row 16473 -row 16474 -row 16475 -row 16476 -row 16477 -row 16478 -row 16479 -row 16480 -row 16481 -row 16482 -row 16483 -row 16484 -row 16485 -row 16486 -row 16487 -row 16488 -row 16489 -row 16490 -row 16491 -row 16492 -row 16493 -row 16494 -row 16495 -row 16496 -row 16497 -row 16498 -row 16499 -row 16500 -row 16501 -row 16502 -row 16503 -row 16504 -row 16505 -row 16506 -row 16507 -row 16508 -row 16509 -row 16510 -row 16511 -row 16512 -row 16513 -row 16514 -row 16515 -row 16516 -row 16517 -row 16518 -row 16519 -row 16520 -row 16521 -row 16522 -row 16523 -row 16524 -row 16525 -row 16526 -row 16527 -row 16528 -row 16529 -row 16530 -row 16531 -row 16532 -row 16533 -row 16534 -row 16535 -row 16536 -row 16537 -row 16538 -row 16539 -row 16540 -row 16541 -row 16542 -row 16543 -row 16544 -row 16545 -row 16546 -row 16547 -row 16548 -row 16549 -row 16550 -row 16551 -row 16552 -row 16553 -row 16554 -row 16555 -row 16556 -row 16557 -row 16558 -row 16559 -row 16560 -row 16561 -row 16562 -row 16563 -row 16564 -row 16565 -row 16566 -row 16567 -row 16568 -row 16569 -row 16570 -row 16571 -row 16572 -row 16573 -row 16574 -row 16575 -row 16576 -row 16577 -row 16578 -row 16579 -row 16580 -row 16581 -row 16582 -row 16583 -row 16584 -row 16585 -row 16586 -row 16587 -row 16588 -row 16589 -row 16590 -row 16591 -row 16592 -row 16593 -row 16594 -row 16595 -row 16596 -row 16597 -row 16598 -row 16599 -row 16600 -row 16601 -row 16602 -row 16603 -row 16604 -row 16605 -row 16606 -row 16607 -row 16608 -row 16609 -row 16610 -row 16611 -row 16612 -row 16613 -row 16614 -row 16615 -row 16616 -row 16617 -row 16618 -row 16619 -row 16620 -row 16621 -row 16622 -row 16623 -row 16624 -row 16625 -row 16626 -row 16627 -row 16628 -row 16629 -row 16630 -row 16631 -row 16632 -row 16633 -row 16634 -row 16635 -row 16636 -row 16637 -row 16638 -row 16639 -row 16640 -row 16641 -row 16642 -row 16643 -row 16644 -row 16645 -row 16646 -row 16647 -row 16648 -row 16649 -row 16650 -row 16651 -row 16652 -row 16653 -row 16654 -row 16655 -row 16656 -row 16657 -row 16658 -row 16659 -row 16660 -row 16661 -row 16662 -row 16663 -row 16664 -row 16665 -row 16666 -row 16667 -row 16668 -row 16669 -row 16670 -row 16671 -row 16672 -row 16673 -row 16674 -row 16675 -row 16676 -row 16677 -row 16678 -row 16679 -row 16680 -row 16681 -row 16682 -row 16683 -row 16684 -row 16685 -row 16686 -row 16687 -row 16688 -row 16689 -row 16690 -row 16691 -row 16692 -row 16693 -row 16694 -row 16695 -row 16696 -row 16697 -row 16698 -row 16699 -row 16700 -row 16701 -row 16702 -row 16703 -row 16704 -row 16705 -row 16706 -row 16707 -row 16708 -row 16709 -row 16710 -row 16711 -row 16712 -row 16713 -row 16714 -row 16715 -row 16716 -row 16717 -row 16718 -row 16719 -row 16720 -row 16721 -row 16722 -row 16723 -row 16724 -row 16725 -row 16726 -row 16727 -row 16728 -row 16729 -row 16730 -row 16731 -row 16732 -row 16733 -row 16734 -row 16735 -row 16736 -row 16737 -row 16738 -row 16739 -row 16740 -row 16741 -row 16742 -row 16743 -row 16744 -row 16745 -row 16746 -row 16747 -row 16748 -row 16749 -row 16750 -row 16751 -row 16752 -row 16753 -row 16754 -row 16755 -row 16756 -row 16757 -row 16758 -row 16759 -row 16760 -row 16761 -row 16762 -row 16763 -row 16764 -row 16765 -row 16766 -row 16767 -row 16768 -row 16769 -row 16770 -row 16771 -row 16772 -row 16773 -row 16774 -row 16775 -row 16776 -row 16777 -row 16778 -row 16779 -row 16780 -row 16781 -row 16782 -row 16783 -row 16784 -row 16785 -row 16786 -row 16787 -row 16788 -row 16789 -row 16790 -row 16791 -row 16792 -row 16793 -row 16794 -row 16795 -row 16796 -row 16797 -row 16798 -row 16799 -row 16800 -row 16801 -row 16802 -row 16803 -row 16804 -row 16805 -row 16806 -row 16807 -row 16808 -row 16809 -row 16810 -row 16811 -row 16812 -row 16813 -row 16814 -row 16815 -row 16816 -row 16817 -row 16818 -row 16819 -row 16820 -row 16821 -row 16822 -row 16823 -row 16824 -row 16825 -row 16826 -row 16827 -row 16828 -row 16829 -row 16830 -row 16831 -row 16832 -row 16833 -row 16834 -row 16835 -row 16836 -row 16837 -row 16838 -row 16839 -row 16840 -row 16841 -row 16842 -row 16843 -row 16844 -row 16845 -row 16846 -row 16847 -row 16848 -row 16849 -row 16850 -row 16851 -row 16852 -row 16853 -row 16854 -row 16855 -row 16856 -row 16857 -row 16858 -row 16859 -row 16860 -row 16861 -row 16862 -row 16863 -row 16864 -row 16865 -row 16866 -row 16867 -row 16868 -row 16869 -row 16870 -row 16871 -row 16872 -row 16873 -row 16874 -row 16875 -row 16876 -row 16877 -row 16878 -row 16879 -row 16880 -row 16881 -row 16882 -row 16883 -row 16884 -row 16885 -row 16886 -row 16887 -row 16888 -row 16889 -row 16890 -row 16891 -row 16892 -row 16893 -row 16894 -row 16895 -row 16896 -row 16897 -row 16898 -row 16899 -row 16900 -row 16901 -row 16902 -row 16903 -row 16904 -row 16905 -row 16906 -row 16907 -row 16908 -row 16909 -row 16910 -row 16911 -row 16912 -row 16913 -row 16914 -row 16915 -row 16916 -row 16917 -row 16918 -row 16919 -row 16920 -row 16921 -row 16922 -row 16923 -row 16924 -row 16925 -row 16926 -row 16927 -row 16928 -row 16929 -row 16930 -row 16931 -row 16932 -row 16933 -row 16934 -row 16935 -row 16936 -row 16937 -row 16938 -row 16939 -row 16940 -row 16941 -row 16942 -row 16943 -row 16944 -row 16945 -row 16946 -row 16947 -row 16948 -row 16949 -row 16950 -row 16951 -row 16952 -row 16953 -row 16954 -row 16955 -row 16956 -row 16957 -row 16958 -row 16959 -row 16960 -row 16961 -row 16962 -row 16963 -row 16964 -row 16965 -row 16966 -row 16967 -row 16968 -row 16969 -row 16970 -row 16971 -row 16972 -row 16973 -row 16974 -row 16975 -row 16976 -row 16977 -row 16978 -row 16979 -row 16980 -row 16981 -row 16982 -row 16983 -row 16984 -row 16985 -row 16986 -row 16987 -row 16988 -row 16989 -row 16990 -row 16991 -row 16992 -row 16993 -row 16994 -row 16995 -row 16996 -row 16997 -row 16998 -row 16999 -row 17000 -row 17001 -row 17002 -row 17003 -row 17004 -row 17005 -row 17006 -row 17007 -row 17008 -row 17009 -row 17010 -row 17011 -row 17012 -row 17013 -row 17014 -row 17015 -row 17016 -row 17017 -row 17018 -row 17019 -row 17020 -row 17021 -row 17022 -row 17023 -row 17024 -row 17025 -row 17026 -row 17027 -row 17028 -row 17029 -row 17030 -row 17031 -row 17032 -row 17033 -row 17034 -row 17035 -row 17036 -row 17037 -row 17038 -row 17039 -row 17040 -row 17041 -row 17042 -row 17043 -row 17044 -row 17045 -row 17046 -row 17047 -row 17048 -row 17049 -row 17050 -row 17051 -row 17052 -row 17053 -row 17054 -row 17055 -row 17056 -row 17057 -row 17058 -row 17059 -row 17060 -row 17061 -row 17062 -row 17063 -row 17064 -row 17065 -row 17066 -row 17067 -row 17068 -row 17069 -row 17070 -row 17071 -row 17072 -row 17073 -row 17074 -row 17075 -row 17076 -row 17077 -row 17078 -row 17079 -row 17080 -row 17081 -row 17082 -row 17083 -row 17084 -row 17085 -row 17086 -row 17087 -row 17088 -row 17089 -row 17090 -row 17091 -row 17092 -row 17093 -row 17094 -row 17095 -row 17096 -row 17097 -row 17098 -row 17099 -row 17100 -row 17101 -row 17102 -row 17103 -row 17104 -row 17105 -row 17106 -row 17107 -row 17108 -row 17109 -row 17110 -row 17111 -row 17112 -row 17113 -row 17114 -row 17115 -row 17116 -row 17117 -row 17118 -row 17119 -row 17120 -row 17121 -row 17122 -row 17123 -row 17124 -row 17125 -row 17126 -row 17127 -row 17128 -row 17129 -row 17130 -row 17131 -row 17132 -row 17133 -row 17134 -row 17135 -row 17136 -row 17137 -row 17138 -row 17139 -row 17140 -row 17141 -row 17142 -row 17143 -row 17144 -row 17145 -row 17146 -row 17147 -row 17148 -row 17149 -row 17150 -row 17151 -row 17152 -row 17153 -row 17154 -row 17155 -row 17156 -row 17157 -row 17158 -row 17159 -row 17160 -row 17161 -row 17162 -row 17163 -row 17164 -row 17165 -row 17166 -row 17167 -row 17168 -row 17169 -row 17170 -row 17171 -row 17172 -row 17173 -row 17174 -row 17175 -row 17176 -row 17177 -row 17178 -row 17179 -row 17180 -row 17181 -row 17182 -row 17183 -row 17184 -row 17185 -row 17186 -row 17187 -row 17188 -row 17189 -row 17190 -row 17191 -row 17192 -row 17193 -row 17194 -row 17195 -row 17196 -row 17197 -row 17198 -row 17199 -row 17200 -row 17201 -row 17202 -row 17203 -row 17204 -row 17205 -row 17206 -row 17207 -row 17208 -row 17209 -row 17210 -row 17211 -row 17212 -row 17213 -row 17214 -row 17215 -row 17216 -row 17217 -row 17218 -row 17219 -row 17220 -row 17221 -row 17222 -row 17223 -row 17224 -row 17225 -row 17226 -row 17227 -row 17228 -row 17229 -row 17230 -row 17231 -row 17232 -row 17233 -row 17234 -row 17235 -row 17236 -row 17237 -row 17238 -row 17239 -row 17240 -row 17241 -row 17242 -row 17243 -row 17244 -row 17245 -row 17246 -row 17247 -row 17248 -row 17249 -row 17250 -row 17251 -row 17252 -row 17253 -row 17254 -row 17255 -row 17256 -row 17257 -row 17258 -row 17259 -row 17260 -row 17261 -row 17262 -row 17263 -row 17264 -row 17265 -row 17266 -row 17267 -row 17268 -row 17269 -row 17270 -row 17271 -row 17272 -row 17273 -row 17274 -row 17275 -row 17276 -row 17277 -row 17278 -row 17279 -row 17280 -row 17281 -row 17282 -row 17283 -row 17284 -row 17285 -row 17286 -row 17287 -row 17288 -row 17289 -row 17290 -row 17291 -row 17292 -row 17293 -row 17294 -row 17295 -row 17296 -row 17297 -row 17298 -row 17299 -row 17300 -row 17301 -row 17302 -row 17303 -row 17304 -row 17305 -row 17306 -row 17307 -row 17308 -row 17309 -row 17310 -row 17311 -row 17312 -row 17313 -row 17314 -row 17315 -row 17316 -row 17317 -row 17318 -row 17319 -row 17320 -row 17321 -row 17322 -row 17323 -row 17324 -row 17325 -row 17326 -row 17327 -row 17328 -row 17329 -row 17330 -row 17331 -row 17332 -row 17333 -row 17334 -row 17335 -row 17336 -row 17337 -row 17338 -row 17339 -row 17340 -row 17341 -row 17342 -row 17343 -row 17344 -row 17345 -row 17346 -row 17347 -row 17348 -row 17349 -row 17350 -row 17351 -row 17352 -row 17353 -row 17354 -row 17355 -row 17356 -row 17357 -row 17358 -row 17359 -row 17360 -row 17361 -row 17362 -row 17363 -row 17364 -row 17365 -row 17366 -row 17367 -row 17368 -row 17369 -row 17370 -row 17371 -row 17372 -row 17373 -row 17374 -row 17375 -row 17376 -row 17377 -row 17378 -row 17379 -row 17380 -row 17381 -row 17382 -row 17383 -row 17384 -row 17385 -row 17386 -row 17387 -row 17388 -row 17389 -row 17390 -row 17391 -row 17392 -row 17393 -row 17394 -row 17395 -row 17396 -row 17397 -row 17398 -row 17399 -row 17400 -row 17401 -row 17402 -row 17403 -row 17404 -row 17405 -row 17406 -row 17407 -row 17408 -row 17409 -row 17410 -row 17411 -row 17412 -row 17413 -row 17414 -row 17415 -row 17416 -row 17417 -row 17418 -row 17419 -row 17420 -row 17421 -row 17422 -row 17423 -row 17424 -row 17425 -row 17426 -row 17427 -row 17428 -row 17429 -row 17430 -row 17431 -row 17432 -row 17433 -row 17434 -row 17435 -row 17436 -row 17437 -row 17438 -row 17439 -row 17440 -row 17441 -row 17442 -row 17443 -row 17444 -row 17445 -row 17446 -row 17447 -row 17448 -row 17449 -row 17450 -row 17451 -row 17452 -row 17453 -row 17454 -row 17455 -row 17456 -row 17457 -row 17458 -row 17459 -row 17460 -row 17461 -row 17462 -row 17463 -row 17464 -row 17465 -row 17466 -row 17467 -row 17468 -row 17469 -row 17470 -row 17471 -row 17472 -row 17473 -row 17474 -row 17475 -row 17476 -row 17477 -row 17478 -row 17479 -row 17480 -row 17481 -row 17482 -row 17483 -row 17484 -row 17485 -row 17486 -row 17487 -row 17488 -row 17489 -row 17490 -row 17491 -row 17492 -row 17493 -row 17494 -row 17495 -row 17496 -row 17497 -row 17498 -row 17499 -row 17500 -row 17501 -row 17502 -row 17503 -row 17504 -row 17505 -row 17506 -row 17507 -row 17508 -row 17509 -row 17510 -row 17511 -row 17512 -row 17513 -row 17514 -row 17515 -row 17516 -row 17517 -row 17518 -row 17519 -row 17520 -row 17521 -row 17522 -row 17523 -row 17524 -row 17525 -row 17526 -row 17527 -row 17528 -row 17529 -row 17530 -row 17531 -row 17532 -row 17533 -row 17534 -row 17535 -row 17536 -row 17537 -row 17538 -row 17539 -row 17540 -row 17541 -row 17542 -row 17543 -row 17544 -row 17545 -row 17546 -row 17547 -row 17548 -row 17549 -row 17550 -row 17551 -row 17552 -row 17553 -row 17554 -row 17555 -row 17556 -row 17557 -row 17558 -row 17559 -row 17560 -row 17561 -row 17562 -row 17563 -row 17564 -row 17565 -row 17566 -row 17567 -row 17568 -row 17569 -row 17570 -row 17571 -row 17572 -row 17573 -row 17574 -row 17575 -row 17576 -row 17577 -row 17578 -row 17579 -row 17580 -row 17581 -row 17582 -row 17583 -row 17584 -row 17585 -row 17586 -row 17587 -row 17588 -row 17589 -row 17590 -row 17591 -row 17592 -row 17593 -row 17594 -row 17595 -row 17596 -row 17597 -row 17598 -row 17599 -row 17600 -row 17601 -row 17602 -row 17603 -row 17604 -row 17605 -row 17606 -row 17607 -row 17608 -row 17609 -row 17610 -row 17611 -row 17612 -row 17613 -row 17614 -row 17615 -row 17616 -row 17617 -row 17618 -row 17619 -row 17620 -row 17621 -row 17622 -row 17623 -row 17624 -row 17625 -row 17626 -row 17627 -row 17628 -row 17629 -row 17630 -row 17631 -row 17632 -row 17633 -row 17634 -row 17635 -row 17636 -row 17637 -row 17638 -row 17639 -row 17640 -row 17641 -row 17642 -row 17643 -row 17644 -row 17645 -row 17646 -row 17647 -row 17648 -row 17649 -row 17650 -row 17651 -row 17652 -row 17653 -row 17654 -row 17655 -row 17656 -row 17657 -row 17658 -row 17659 -row 17660 -row 17661 -row 17662 -row 17663 -row 17664 -row 17665 -row 17666 -row 17667 -row 17668 -row 17669 -row 17670 -row 17671 -row 17672 -row 17673 -row 17674 -row 17675 -row 17676 -row 17677 -row 17678 -row 17679 -row 17680 -row 17681 -row 17682 -row 17683 -row 17684 -row 17685 -row 17686 -row 17687 -row 17688 -row 17689 -row 17690 -row 17691 -row 17692 -row 17693 -row 17694 -row 17695 -row 17696 -row 17697 -row 17698 -row 17699 -row 17700 -row 17701 -row 17702 -row 17703 -row 17704 -row 17705 -row 17706 -row 17707 -row 17708 -row 17709 -row 17710 -row 17711 -row 17712 -row 17713 -row 17714 -row 17715 -row 17716 -row 17717 -row 17718 -row 17719 -row 17720 -row 17721 -row 17722 -row 17723 -row 17724 -row 17725 -row 17726 -row 17727 -row 17728 -row 17729 -row 17730 -row 17731 -row 17732 -row 17733 -row 17734 -row 17735 -row 17736 -row 17737 -row 17738 -row 17739 -row 17740 -row 17741 -row 17742 -row 17743 -row 17744 -row 17745 -row 17746 -row 17747 -row 17748 -row 17749 -row 17750 -row 17751 -row 17752 -row 17753 -row 17754 -row 17755 -row 17756 -row 17757 -row 17758 -row 17759 -row 17760 -row 17761 -row 17762 -row 17763 -row 17764 -row 17765 -row 17766 -row 17767 -row 17768 -row 17769 -row 17770 -row 17771 -row 17772 -row 17773 -row 17774 -row 17775 -row 17776 -row 17777 -row 17778 -row 17779 -row 17780 -row 17781 -row 17782 -row 17783 -row 17784 -row 17785 -row 17786 -row 17787 -row 17788 -row 17789 -row 17790 -row 17791 -row 17792 -row 17793 -row 17794 -row 17795 -row 17796 -row 17797 -row 17798 -row 17799 -row 17800 -row 17801 -row 17802 -row 17803 -row 17804 -row 17805 -row 17806 -row 17807 -row 17808 -row 17809 -row 17810 -row 17811 -row 17812 -row 17813 -row 17814 -row 17815 -row 17816 -row 17817 -row 17818 -row 17819 -row 17820 -row 17821 -row 17822 -row 17823 -row 17824 -row 17825 -row 17826 -row 17827 -row 17828 -row 17829 -row 17830 -row 17831 -row 17832 -row 17833 -row 17834 -row 17835 -row 17836 -row 17837 -row 17838 -row 17839 -row 17840 -row 17841 -row 17842 -row 17843 -row 17844 -row 17845 -row 17846 -row 17847 -row 17848 -row 17849 -row 17850 -row 17851 -row 17852 -row 17853 -row 17854 -row 17855 -row 17856 -row 17857 -row 17858 -row 17859 -row 17860 -row 17861 -row 17862 -row 17863 -row 17864 -row 17865 -row 17866 -row 17867 -row 17868 -row 17869 -row 17870 -row 17871 -row 17872 -row 17873 -row 17874 -row 17875 -row 17876 -row 17877 -row 17878 -row 17879 -row 17880 -row 17881 -row 17882 -row 17883 -row 17884 -row 17885 -row 17886 -row 17887 -row 17888 -row 17889 -row 17890 -row 17891 -row 17892 -row 17893 -row 17894 -row 17895 -row 17896 -row 17897 -row 17898 -row 17899 -row 17900 -row 17901 -row 17902 -row 17903 -row 17904 -row 17905 -row 17906 -row 17907 -row 17908 -row 17909 -row 17910 -row 17911 -row 17912 -row 17913 -row 17914 -row 17915 -row 17916 -row 17917 -row 17918 -row 17919 -row 17920 -row 17921 -row 17922 -row 17923 -row 17924 -row 17925 -row 17926 -row 17927 -row 17928 -row 17929 -row 17930 -row 17931 -row 17932 -row 17933 -row 17934 -row 17935 -row 17936 -row 17937 -row 17938 -row 17939 -row 17940 -row 17941 -row 17942 -row 17943 -row 17944 -row 17945 -row 17946 -row 17947 -row 17948 -row 17949 -row 17950 -row 17951 -row 17952 -row 17953 -row 17954 -row 17955 -row 17956 -row 17957 -row 17958 -row 17959 -row 17960 -row 17961 -row 17962 -row 17963 -row 17964 -row 17965 -row 17966 -row 17967 -row 17968 -row 17969 -row 17970 -row 17971 -row 17972 -row 17973 -row 17974 -row 17975 -row 17976 -row 17977 -row 17978 -row 17979 -row 17980 -row 17981 -row 17982 -row 17983 -row 17984 -row 17985 -row 17986 -row 17987 -row 17988 -row 17989 -row 17990 -row 17991 -row 17992 -row 17993 -row 17994 -row 17995 -row 17996 -row 17997 -row 17998 -row 17999 -row 18000 -row 18001 -row 18002 -row 18003 -row 18004 -row 18005 -row 18006 -row 18007 -row 18008 -row 18009 -row 18010 -row 18011 -row 18012 -row 18013 -row 18014 -row 18015 -row 18016 -row 18017 -row 18018 -row 18019 -row 18020 -row 18021 -row 18022 -row 18023 -row 18024 -row 18025 -row 18026 -row 18027 -row 18028 -row 18029 -row 18030 -row 18031 -row 18032 -row 18033 -row 18034 -row 18035 -row 18036 -row 18037 -row 18038 -row 18039 -row 18040 -row 18041 -row 18042 -row 18043 -row 18044 -row 18045 -row 18046 -row 18047 -row 18048 -row 18049 -row 18050 -row 18051 -row 18052 -row 18053 -row 18054 -row 18055 -row 18056 -row 18057 -row 18058 -row 18059 -row 18060 -row 18061 -row 18062 -row 18063 -row 18064 -row 18065 -row 18066 -row 18067 -row 18068 -row 18069 -row 18070 -row 18071 -row 18072 -row 18073 -row 18074 -row 18075 -row 18076 -row 18077 -row 18078 -row 18079 -row 18080 -row 18081 -row 18082 -row 18083 -row 18084 -row 18085 -row 18086 -row 18087 -row 18088 -row 18089 -row 18090 -row 18091 -row 18092 -row 18093 -row 18094 -row 18095 -row 18096 -row 18097 -row 18098 -row 18099 -row 18100 -row 18101 -row 18102 -row 18103 -row 18104 -row 18105 -row 18106 -row 18107 -row 18108 -row 18109 -row 18110 -row 18111 -row 18112 -row 18113 -row 18114 -row 18115 -row 18116 -row 18117 -row 18118 -row 18119 -row 18120 -row 18121 -row 18122 -row 18123 -row 18124 -row 18125 -row 18126 -row 18127 -row 18128 -row 18129 -row 18130 -row 18131 -row 18132 -row 18133 -row 18134 -row 18135 -row 18136 -row 18137 -row 18138 -row 18139 -row 18140 -row 18141 -row 18142 -row 18143 -row 18144 -row 18145 -row 18146 -row 18147 -row 18148 -row 18149 -row 18150 -row 18151 -row 18152 -row 18153 -row 18154 -row 18155 -row 18156 -row 18157 -row 18158 -row 18159 -row 18160 -row 18161 -row 18162 -row 18163 -row 18164 -row 18165 -row 18166 -row 18167 -row 18168 -row 18169 -row 18170 -row 18171 -row 18172 -row 18173 -row 18174 -row 18175 -row 18176 -row 18177 -row 18178 -row 18179 -row 18180 -row 18181 -row 18182 -row 18183 -row 18184 -row 18185 -row 18186 -row 18187 -row 18188 -row 18189 -row 18190 -row 18191 -row 18192 -row 18193 -row 18194 -row 18195 -row 18196 -row 18197 -row 18198 -row 18199 -row 18200 -row 18201 -row 18202 -row 18203 -row 18204 -row 18205 -row 18206 -row 18207 -row 18208 -row 18209 -row 18210 -row 18211 -row 18212 -row 18213 -row 18214 -row 18215 -row 18216 -row 18217 -row 18218 -row 18219 -row 18220 -row 18221 -row 18222 -row 18223 -row 18224 -row 18225 -row 18226 -row 18227 -row 18228 -row 18229 -row 18230 -row 18231 -row 18232 -row 18233 -row 18234 -row 18235 -row 18236 -row 18237 -row 18238 -row 18239 -row 18240 -row 18241 -row 18242 -row 18243 -row 18244 -row 18245 -row 18246 -row 18247 -row 18248 -row 18249 -row 18250 -row 18251 -row 18252 -row 18253 -row 18254 -row 18255 -row 18256 -row 18257 -row 18258 -row 18259 -row 18260 -row 18261 -row 18262 -row 18263 -row 18264 -row 18265 -row 18266 -row 18267 -row 18268 -row 18269 -row 18270 -row 18271 -row 18272 -row 18273 -row 18274 -row 18275 -row 18276 -row 18277 -row 18278 -row 18279 -row 18280 -row 18281 -row 18282 -row 18283 -row 18284 -row 18285 -row 18286 -row 18287 -row 18288 -row 18289 -row 18290 -row 18291 -row 18292 -row 18293 -row 18294 -row 18295 -row 18296 -row 18297 -row 18298 -row 18299 -row 18300 -row 18301 -row 18302 -row 18303 -row 18304 -row 18305 -row 18306 -row 18307 -row 18308 -row 18309 -row 18310 -row 18311 -row 18312 -row 18313 -row 18314 -row 18315 -row 18316 -row 18317 -row 18318 -row 18319 -row 18320 -row 18321 -row 18322 -row 18323 -row 18324 -row 18325 -row 18326 -row 18327 -row 18328 -row 18329 -row 18330 -row 18331 -row 18332 -row 18333 -row 18334 -row 18335 -row 18336 -row 18337 -row 18338 -row 18339 -row 18340 -row 18341 -row 18342 -row 18343 -row 18344 -row 18345 -row 18346 -row 18347 -row 18348 -row 18349 -row 18350 -row 18351 -row 18352 -row 18353 -row 18354 -row 18355 -row 18356 -row 18357 -row 18358 -row 18359 -row 18360 -row 18361 -row 18362 -row 18363 -row 18364 -row 18365 -row 18366 -row 18367 -row 18368 -row 18369 -row 18370 -row 18371 -row 18372 -row 18373 -row 18374 -row 18375 -row 18376 -row 18377 -row 18378 -row 18379 -row 18380 -row 18381 -row 18382 -row 18383 -row 18384 -row 18385 -row 18386 -row 18387 -row 18388 -row 18389 -row 18390 -row 18391 -row 18392 -row 18393 -row 18394 -row 18395 -row 18396 -row 18397 -row 18398 -row 18399 -row 18400 -row 18401 -row 18402 -row 18403 -row 18404 -row 18405 -row 18406 -row 18407 -row 18408 -row 18409 -row 18410 -row 18411 -row 18412 -row 18413 -row 18414 -row 18415 -row 18416 -row 18417 -row 18418 -row 18419 -row 18420 -row 18421 -row 18422 -row 18423 -row 18424 -row 18425 -row 18426 -row 18427 -row 18428 -row 18429 -row 18430 -row 18431 -row 18432 -row 18433 -row 18434 -row 18435 -row 18436 -row 18437 -row 18438 -row 18439 -row 18440 -row 18441 -row 18442 -row 18443 -row 18444 -row 18445 -row 18446 -row 18447 -row 18448 -row 18449 -row 18450 -row 18451 -row 18452 -row 18453 -row 18454 -row 18455 -row 18456 -row 18457 -row 18458 -row 18459 -row 18460 -row 18461 -row 18462 -row 18463 -row 18464 -row 18465 -row 18466 -row 18467 -row 18468 -row 18469 -row 18470 -row 18471 -row 18472 -row 18473 -row 18474 -row 18475 -row 18476 -row 18477 -row 18478 -row 18479 -row 18480 -row 18481 -row 18482 -row 18483 -row 18484 -row 18485 -row 18486 -row 18487 -row 18488 -row 18489 -row 18490 -row 18491 -row 18492 -row 18493 -row 18494 -row 18495 -row 18496 -row 18497 -row 18498 -row 18499 -row 18500 -row 18501 -row 18502 -row 18503 -row 18504 -row 18505 -row 18506 -row 18507 -row 18508 -row 18509 -row 18510 -row 18511 -row 18512 -row 18513 -row 18514 -row 18515 -row 18516 -row 18517 -row 18518 -row 18519 -row 18520 -row 18521 -row 18522 -row 18523 -row 18524 -row 18525 -row 18526 -row 18527 -row 18528 -row 18529 -row 18530 -row 18531 -row 18532 -row 18533 -row 18534 -row 18535 -row 18536 -row 18537 -row 18538 -row 18539 -row 18540 -row 18541 -row 18542 -row 18543 -row 18544 -row 18545 -row 18546 -row 18547 -row 18548 -row 18549 -row 18550 -row 18551 -row 18552 -row 18553 -row 18554 -row 18555 -row 18556 -row 18557 -row 18558 -row 18559 -row 18560 -row 18561 -row 18562 -row 18563 -row 18564 -row 18565 -row 18566 -row 18567 -row 18568 -row 18569 -row 18570 -row 18571 -row 18572 -row 18573 -row 18574 -row 18575 -row 18576 -row 18577 -row 18578 -row 18579 -row 18580 -row 18581 -row 18582 -row 18583 -row 18584 -row 18585 -row 18586 -row 18587 -row 18588 -row 18589 -row 18590 -row 18591 -row 18592 -row 18593 -row 18594 -row 18595 -row 18596 -row 18597 -row 18598 -row 18599 -row 18600 -row 18601 -row 18602 -row 18603 -row 18604 -row 18605 -row 18606 -row 18607 -row 18608 -row 18609 -row 18610 -row 18611 -row 18612 -row 18613 -row 18614 -row 18615 -row 18616 -row 18617 -row 18618 -row 18619 -row 18620 -row 18621 -row 18622 -row 18623 -row 18624 -row 18625 -row 18626 -row 18627 -row 18628 -row 18629 -row 18630 -row 18631 -row 18632 -row 18633 -row 18634 -row 18635 -row 18636 -row 18637 -row 18638 -row 18639 -row 18640 -row 18641 -row 18642 -row 18643 -row 18644 -row 18645 -row 18646 -row 18647 -row 18648 -row 18649 -row 18650 -row 18651 -row 18652 -row 18653 -row 18654 -row 18655 -row 18656 -row 18657 -row 18658 -row 18659 -row 18660 -row 18661 -row 18662 -row 18663 -row 18664 -row 18665 -row 18666 -row 18667 -row 18668 -row 18669 -row 18670 -row 18671 -row 18672 -row 18673 -row 18674 -row 18675 -row 18676 -row 18677 -row 18678 -row 18679 -row 18680 -row 18681 -row 18682 -row 18683 -row 18684 -row 18685 -row 18686 -row 18687 -row 18688 -row 18689 -row 18690 -row 18691 -row 18692 -row 18693 -row 18694 -row 18695 -row 18696 -row 18697 -row 18698 -row 18699 -row 18700 -row 18701 -row 18702 -row 18703 -row 18704 -row 18705 -row 18706 -row 18707 -row 18708 -row 18709 -row 18710 -row 18711 -row 18712 -row 18713 -row 18714 -row 18715 -row 18716 -row 18717 -row 18718 -row 18719 -row 18720 -row 18721 -row 18722 -row 18723 -row 18724 -row 18725 -row 18726 -row 18727 -row 18728 -row 18729 -row 18730 -row 18731 -row 18732 -row 18733 -row 18734 -row 18735 -row 18736 -row 18737 -row 18738 -row 18739 -row 18740 -row 18741 -row 18742 -row 18743 -row 18744 -row 18745 -row 18746 -row 18747 -row 18748 -row 18749 -row 18750 -row 18751 -row 18752 -row 18753 -row 18754 -row 18755 -row 18756 -row 18757 -row 18758 -row 18759 -row 18760 -row 18761 -row 18762 -row 18763 -row 18764 -row 18765 -row 18766 -row 18767 -row 18768 -row 18769 -row 18770 -row 18771 -row 18772 -row 18773 -row 18774 -row 18775 -row 18776 -row 18777 -row 18778 -row 18779 -row 18780 -row 18781 -row 18782 -row 18783 -row 18784 -row 18785 -row 18786 -row 18787 -row 18788 -row 18789 -row 18790 -row 18791 -row 18792 -row 18793 -row 18794 -row 18795 -row 18796 -row 18797 -row 18798 -row 18799 -row 18800 -row 18801 -row 18802 -row 18803 -row 18804 -row 18805 -row 18806 -row 18807 -row 18808 -row 18809 -row 18810 -row 18811 -row 18812 -row 18813 -row 18814 -row 18815 -row 18816 -row 18817 -row 18818 -row 18819 -row 18820 -row 18821 -row 18822 -row 18823 -row 18824 -row 18825 -row 18826 -row 18827 -row 18828 -row 18829 -row 18830 -row 18831 -row 18832 -row 18833 -row 18834 -row 18835 -row 18836 -row 18837 -row 18838 -row 18839 -row 18840 -row 18841 -row 18842 -row 18843 -row 18844 -row 18845 -row 18846 -row 18847 -row 18848 -row 18849 -row 18850 -row 18851 -row 18852 -row 18853 -row 18854 -row 18855 -row 18856 -row 18857 -row 18858 -row 18859 -row 18860 -row 18861 -row 18862 -row 18863 -row 18864 -row 18865 -row 18866 -row 18867 -row 18868 -row 18869 -row 18870 -row 18871 -row 18872 -row 18873 -row 18874 -row 18875 -row 18876 -row 18877 -row 18878 -row 18879 -row 18880 -row 18881 -row 18882 -row 18883 -row 18884 -row 18885 -row 18886 -row 18887 -row 18888 -row 18889 -row 18890 -row 18891 -row 18892 -row 18893 -row 18894 -row 18895 -row 18896 -row 18897 -row 18898 -row 18899 -row 18900 -row 18901 -row 18902 -row 18903 -row 18904 -row 18905 -row 18906 -row 18907 -row 18908 -row 18909 -row 18910 -row 18911 -row 18912 -row 18913 -row 18914 -row 18915 -row 18916 -row 18917 -row 18918 -row 18919 -row 18920 -row 18921 -row 18922 -row 18923 -row 18924 -row 18925 -row 18926 -row 18927 -row 18928 -row 18929 -row 18930 -row 18931 -row 18932 -row 18933 -row 18934 -row 18935 -row 18936 -row 18937 -row 18938 -row 18939 -row 18940 -row 18941 -row 18942 -row 18943 -row 18944 -row 18945 -row 18946 -row 18947 -row 18948 -row 18949 -row 18950 -row 18951 -row 18952 -row 18953 -row 18954 -row 18955 -row 18956 -row 18957 -row 18958 -row 18959 -row 18960 -row 18961 -row 18962 -row 18963 -row 18964 -row 18965 -row 18966 -row 18967 -row 18968 -row 18969 -row 18970 -row 18971 -row 18972 -row 18973 -row 18974 -row 18975 -row 18976 -row 18977 -row 18978 -row 18979 -row 18980 -row 18981 -row 18982 -row 18983 -row 18984 -row 18985 -row 18986 -row 18987 -row 18988 -row 18989 -row 18990 -row 18991 -row 18992 -row 18993 -row 18994 -row 18995 -row 18996 -row 18997 -row 18998 -row 18999 -row 19000 -row 19001 -row 19002 -row 19003 -row 19004 -row 19005 -row 19006 -row 19007 -row 19008 -row 19009 -row 19010 -row 19011 -row 19012 -row 19013 -row 19014 -row 19015 -row 19016 -row 19017 -row 19018 -row 19019 -row 19020 -row 19021 -row 19022 -row 19023 -row 19024 -row 19025 -row 19026 -row 19027 -row 19028 -row 19029 -row 19030 -row 19031 -row 19032 -row 19033 -row 19034 -row 19035 -row 19036 -row 19037 -row 19038 -row 19039 -row 19040 -row 19041 -row 19042 -row 19043 -row 19044 -row 19045 -row 19046 -row 19047 -row 19048 -row 19049 -row 19050 -row 19051 -row 19052 -row 19053 -row 19054 -row 19055 -row 19056 -row 19057 -row 19058 -row 19059 -row 19060 -row 19061 -row 19062 -row 19063 -row 19064 -row 19065 -row 19066 -row 19067 -row 19068 -row 19069 -row 19070 -row 19071 -row 19072 -row 19073 -row 19074 -row 19075 -row 19076 -row 19077 -row 19078 -row 19079 -row 19080 -row 19081 -row 19082 -row 19083 -row 19084 -row 19085 -row 19086 -row 19087 -row 19088 -row 19089 -row 19090 -row 19091 -row 19092 -row 19093 -row 19094 -row 19095 -row 19096 -row 19097 -row 19098 -row 19099 -row 19100 -row 19101 -row 19102 -row 19103 -row 19104 -row 19105 -row 19106 -row 19107 -row 19108 -row 19109 -row 19110 -row 19111 -row 19112 -row 19113 -row 19114 -row 19115 -row 19116 -row 19117 -row 19118 -row 19119 -row 19120 -row 19121 -row 19122 -row 19123 -row 19124 -row 19125 -row 19126 -row 19127 -row 19128 -row 19129 -row 19130 -row 19131 -row 19132 -row 19133 -row 19134 -row 19135 -row 19136 -row 19137 -row 19138 -row 19139 -row 19140 -row 19141 -row 19142 -row 19143 -row 19144 -row 19145 -row 19146 -row 19147 -row 19148 -row 19149 -row 19150 -row 19151 -row 19152 -row 19153 -row 19154 -row 19155 -row 19156 -row 19157 -row 19158 -row 19159 -row 19160 -row 19161 -row 19162 -row 19163 -row 19164 -row 19165 -row 19166 -row 19167 -row 19168 -row 19169 -row 19170 -row 19171 -row 19172 -row 19173 -row 19174 -row 19175 -row 19176 -row 19177 -row 19178 -row 19179 -row 19180 -row 19181 -row 19182 -row 19183 -row 19184 -row 19185 -row 19186 -row 19187 -row 19188 -row 19189 -row 19190 -row 19191 -row 19192 -row 19193 -row 19194 -row 19195 -row 19196 -row 19197 -row 19198 -row 19199 -row 19200 -row 19201 -row 19202 -row 19203 -row 19204 -row 19205 -row 19206 -row 19207 -row 19208 -row 19209 -row 19210 -row 19211 -row 19212 -row 19213 -row 19214 -row 19215 -row 19216 -row 19217 -row 19218 -row 19219 -row 19220 -row 19221 -row 19222 -row 19223 -row 19224 -row 19225 -row 19226 -row 19227 -row 19228 -row 19229 -row 19230 -row 19231 -row 19232 -row 19233 -row 19234 -row 19235 -row 19236 -row 19237 -row 19238 -row 19239 -row 19240 -row 19241 -row 19242 -row 19243 -row 19244 -row 19245 -row 19246 -row 19247 -row 19248 -row 19249 -row 19250 -row 19251 -row 19252 -row 19253 -row 19254 -row 19255 -row 19256 -row 19257 -row 19258 -row 19259 -row 19260 -row 19261 -row 19262 -row 19263 -row 19264 -row 19265 -row 19266 -row 19267 -row 19268 -row 19269 -row 19270 -row 19271 -row 19272 -row 19273 -row 19274 -row 19275 -row 19276 -row 19277 -row 19278 -row 19279 -row 19280 -row 19281 -row 19282 -row 19283 -row 19284 -row 19285 -row 19286 -row 19287 -row 19288 -row 19289 -row 19290 -row 19291 -row 19292 -row 19293 -row 19294 -row 19295 -row 19296 -row 19297 -row 19298 -row 19299 -row 19300 -row 19301 -row 19302 -row 19303 -row 19304 -row 19305 -row 19306 -row 19307 -row 19308 -row 19309 -row 19310 -row 19311 -row 19312 -row 19313 -row 19314 -row 19315 -row 19316 -row 19317 -row 19318 -row 19319 -row 19320 -row 19321 -row 19322 -row 19323 -row 19324 -row 19325 -row 19326 -row 19327 -row 19328 -row 19329 -row 19330 -row 19331 -row 19332 -row 19333 -row 19334 -row 19335 -row 19336 -row 19337 -row 19338 -row 19339 -row 19340 -row 19341 -row 19342 -row 19343 -row 19344 -row 19345 -row 19346 -row 19347 -row 19348 -row 19349 -row 19350 -row 19351 -row 19352 -row 19353 -row 19354 -row 19355 -row 19356 -row 19357 -row 19358 -row 19359 -row 19360 -row 19361 -row 19362 -row 19363 -row 19364 -row 19365 -row 19366 -row 19367 -row 19368 -row 19369 -row 19370 -row 19371 -row 19372 -row 19373 -row 19374 -row 19375 -row 19376 -row 19377 -row 19378 -row 19379 -row 19380 -row 19381 -row 19382 -row 19383 -row 19384 -row 19385 -row 19386 -row 19387 -row 19388 -row 19389 -row 19390 -row 19391 -row 19392 -row 19393 -row 19394 -row 19395 -row 19396 -row 19397 -row 19398 -row 19399 -row 19400 -row 19401 -row 19402 -row 19403 -row 19404 -row 19405 -row 19406 -row 19407 -row 19408 -row 19409 -row 19410 -row 19411 -row 19412 -row 19413 -row 19414 -row 19415 -row 19416 -row 19417 -row 19418 -row 19419 -row 19420 -row 19421 -row 19422 -row 19423 -row 19424 -row 19425 -row 19426 -row 19427 -row 19428 -row 19429 -row 19430 -row 19431 -row 19432 -row 19433 -row 19434 -row 19435 -row 19436 -row 19437 -row 19438 -row 19439 -row 19440 -row 19441 -row 19442 -row 19443 -row 19444 -row 19445 -row 19446 -row 19447 -row 19448 -row 19449 -row 19450 -row 19451 -row 19452 -row 19453 -row 19454 -row 19455 -row 19456 -row 19457 -row 19458 -row 19459 -row 19460 -row 19461 -row 19462 -row 19463 -row 19464 -row 19465 -row 19466 -row 19467 -row 19468 -row 19469 -row 19470 -row 19471 -row 19472 -row 19473 -row 19474 -row 19475 -row 19476 -row 19477 -row 19478 -row 19479 -row 19480 -row 19481 -row 19482 -row 19483 -row 19484 -row 19485 -row 19486 -row 19487 -row 19488 -row 19489 -row 19490 -row 19491 -row 19492 -row 19493 -row 19494 -row 19495 -row 19496 -row 19497 -row 19498 -row 19499 -row 19500 -row 19501 -row 19502 -row 19503 -row 19504 -row 19505 -row 19506 -row 19507 -row 19508 -row 19509 -row 19510 -row 19511 -row 19512 -row 19513 -row 19514 -row 19515 -row 19516 -row 19517 -row 19518 -row 19519 -row 19520 -row 19521 -row 19522 -row 19523 -row 19524 -row 19525 -row 19526 -row 19527 -row 19528 -row 19529 -row 19530 -row 19531 -row 19532 -row 19533 -row 19534 -row 19535 -row 19536 -row 19537 -row 19538 -row 19539 -row 19540 -row 19541 -row 19542 -row 19543 -row 19544 -row 19545 -row 19546 -row 19547 -row 19548 -row 19549 -row 19550 -row 19551 -row 19552 -row 19553 -row 19554 -row 19555 -row 19556 -row 19557 -row 19558 -row 19559 -row 19560 -row 19561 -row 19562 -row 19563 -row 19564 -row 19565 -row 19566 -row 19567 -row 19568 -row 19569 -row 19570 -row 19571 -row 19572 -row 19573 -row 19574 -row 19575 -row 19576 -row 19577 -row 19578 -row 19579 -row 19580 -row 19581 -row 19582 -row 19583 -row 19584 -row 19585 -row 19586 -row 19587 -row 19588 -row 19589 -row 19590 -row 19591 -row 19592 -row 19593 -row 19594 -row 19595 -row 19596 -row 19597 -row 19598 -row 19599 -row 19600 -row 19601 -row 19602 -row 19603 -row 19604 -row 19605 -row 19606 -row 19607 -row 19608 -row 19609 -row 19610 -row 19611 -row 19612 -row 19613 -row 19614 -row 19615 -row 19616 -row 19617 -row 19618 -row 19619 -row 19620 -row 19621 -row 19622 -row 19623 -row 19624 -row 19625 -row 19626 -row 19627 -row 19628 -row 19629 -row 19630 -row 19631 -row 19632 -row 19633 -row 19634 -row 19635 -row 19636 -row 19637 -row 19638 -row 19639 -row 19640 -row 19641 -row 19642 -row 19643 -row 19644 -row 19645 -row 19646 -row 19647 -row 19648 -row 19649 -row 19650 -row 19651 -row 19652 -row 19653 -row 19654 -row 19655 -row 19656 -row 19657 -row 19658 -row 19659 -row 19660 -row 19661 -row 19662 -row 19663 -row 19664 -row 19665 -row 19666 -row 19667 -row 19668 -row 19669 -row 19670 -row 19671 -row 19672 -row 19673 -row 19674 -row 19675 -row 19676 -row 19677 -row 19678 -row 19679 -row 19680 -row 19681 -row 19682 -row 19683 -row 19684 -row 19685 -row 19686 -row 19687 -row 19688 -row 19689 -row 19690 -row 19691 -row 19692 -row 19693 -row 19694 -row 19695 -row 19696 -row 19697 -row 19698 -row 19699 -row 19700 -row 19701 -row 19702 -row 19703 -row 19704 -row 19705 -row 19706 -row 19707 -row 19708 -row 19709 -row 19710 -row 19711 -row 19712 -row 19713 -row 19714 -row 19715 -row 19716 -row 19717 -row 19718 -row 19719 -row 19720 -row 19721 -row 19722 -row 19723 -row 19724 -row 19725 -row 19726 -row 19727 -row 19728 -row 19729 -row 19730 -row 19731 -row 19732 -row 19733 -row 19734 -row 19735 -row 19736 -row 19737 -row 19738 -row 19739 -row 19740 -row 19741 -row 19742 -row 19743 -row 19744 -row 19745 -row 19746 -row 19747 -row 19748 -row 19749 -row 19750 -row 19751 -row 19752 -row 19753 -row 19754 -row 19755 -row 19756 -row 19757 -row 19758 -row 19759 -row 19760 -row 19761 -row 19762 -row 19763 -row 19764 -row 19765 -row 19766 -row 19767 -row 19768 -row 19769 -row 19770 -row 19771 -row 19772 -row 19773 -row 19774 -row 19775 -row 19776 -row 19777 -row 19778 -row 19779 -row 19780 -row 19781 -row 19782 -row 19783 -row 19784 -row 19785 -row 19786 -row 19787 -row 19788 -row 19789 -row 19790 -row 19791 -row 19792 -row 19793 -row 19794 -row 19795 -row 19796 -row 19797 -row 19798 -row 19799 -row 19800 -row 19801 -row 19802 -row 19803 -row 19804 -row 19805 -row 19806 -row 19807 -row 19808 -row 19809 -row 19810 -row 19811 -row 19812 -row 19813 -row 19814 -row 19815 -row 19816 -row 19817 -row 19818 -row 19819 -row 19820 -row 19821 -row 19822 -row 19823 -row 19824 -row 19825 -row 19826 -row 19827 -row 19828 -row 19829 -row 19830 -row 19831 -row 19832 -row 19833 -row 19834 -row 19835 -row 19836 -row 19837 -row 19838 -row 19839 -row 19840 -row 19841 -row 19842 -row 19843 -row 19844 -row 19845 -row 19846 -row 19847 -row 19848 -row 19849 -row 19850 -row 19851 -row 19852 -row 19853 -row 19854 -row 19855 -row 19856 -row 19857 -row 19858 -row 19859 -row 19860 -row 19861 -row 19862 -row 19863 -row 19864 -row 19865 -row 19866 -row 19867 -row 19868 -row 19869 -row 19870 -row 19871 -row 19872 -row 19873 -row 19874 -row 19875 -row 19876 -row 19877 -row 19878 -row 19879 -row 19880 -row 19881 -row 19882 -row 19883 -row 19884 -row 19885 -row 19886 -row 19887 -row 19888 -row 19889 -row 19890 -row 19891 -row 19892 -row 19893 -row 19894 -row 19895 -row 19896 -row 19897 -row 19898 -row 19899 -row 19900 -row 19901 -row 19902 -row 19903 -row 19904 -row 19905 -row 19906 -row 19907 -row 19908 -row 19909 -row 19910 -row 19911 -row 19912 -row 19913 -row 19914 -row 19915 -row 19916 -row 19917 -row 19918 -row 19919 -row 19920 -row 19921 -row 19922 -row 19923 -row 19924 -row 19925 -row 19926 -row 19927 -row 19928 -row 19929 -row 19930 -row 19931 -row 19932 -row 19933 -row 19934 -row 19935 -row 19936 -row 19937 -row 19938 -row 19939 -row 19940 -row 19941 -row 19942 -row 19943 -row 19944 -row 19945 -row 19946 -row 19947 -row 19948 -row 19949 -row 19950 -row 19951 -row 19952 -row 19953 -row 19954 -row 19955 -row 19956 -row 19957 -row 19958 -row 19959 -row 19960 -row 19961 -row 19962 -row 19963 -row 19964 -row 19965 -row 19966 -row 19967 -row 19968 -row 19969 -row 19970 -row 19971 -row 19972 -row 19973 -row 19974 -row 19975 -row 19976 -row 19977 -row 19978 -row 19979 -row 19980 -row 19981 -row 19982 -row 19983 -row 19984 -row 19985 -row 19986 -row 19987 -row 19988 -row 19989 -row 19990 -row 19991 -row 19992 -row 19993 -row 19994 -row 19995 -row 19996 -row 19997 -row 19998 -row 19999 -row 20000 -row 20001 -row 20002 -row 20003 -row 20004 -row 20005 -row 20006 -row 20007 -row 20008 -row 20009 -row 20010 -row 20011 -row 20012 -row 20013 -row 20014 -row 20015 -row 20016 -row 20017 -row 20018 -row 20019 -row 20020 -row 20021 -row 20022 -row 20023 -row 20024 -row 20025 -row 20026 -row 20027 -row 20028 -row 20029 -row 20030 -row 20031 -row 20032 -row 20033 -row 20034 -row 20035 -row 20036 -row 20037 -row 20038 -row 20039 -row 20040 -row 20041 -row 20042 -row 20043 -row 20044 -row 20045 -row 20046 -row 20047 -row 20048 -row 20049 -row 20050 -row 20051 -row 20052 -row 20053 -row 20054 -row 20055 -row 20056 -row 20057 -row 20058 -row 20059 -row 20060 -row 20061 -row 20062 -row 20063 -row 20064 -row 20065 -row 20066 -row 20067 -row 20068 -row 20069 -row 20070 -row 20071 -row 20072 -row 20073 -row 20074 -row 20075 -row 20076 -row 20077 -row 20078 -row 20079 -row 20080 -row 20081 -row 20082 -row 20083 -row 20084 -row 20085 -row 20086 -row 20087 -row 20088 -row 20089 -row 20090 -row 20091 -row 20092 -row 20093 -row 20094 -row 20095 -row 20096 -row 20097 -row 20098 -row 20099 -row 20100 -row 20101 -row 20102 -row 20103 -row 20104 -row 20105 -row 20106 -row 20107 -row 20108 -row 20109 -row 20110 -row 20111 -row 20112 -row 20113 -row 20114 -row 20115 -row 20116 -row 20117 -row 20118 -row 20119 -row 20120 -row 20121 -row 20122 -row 20123 -row 20124 -row 20125 -row 20126 -row 20127 -row 20128 -row 20129 -row 20130 -row 20131 -row 20132 -row 20133 -row 20134 -row 20135 -row 20136 -row 20137 -row 20138 -row 20139 -row 20140 -row 20141 -row 20142 -row 20143 -row 20144 -row 20145 -row 20146 -row 20147 -row 20148 -row 20149 -row 20150 -row 20151 -row 20152 -row 20153 -row 20154 -row 20155 -row 20156 -row 20157 -row 20158 -row 20159 -row 20160 -row 20161 -row 20162 -row 20163 -row 20164 -row 20165 -row 20166 -row 20167 -row 20168 -row 20169 -row 20170 -row 20171 -row 20172 -row 20173 -row 20174 -row 20175 -row 20176 -row 20177 -row 20178 -row 20179 -row 20180 -row 20181 -row 20182 -row 20183 -row 20184 -row 20185 -row 20186 -row 20187 -row 20188 -row 20189 -row 20190 -row 20191 -row 20192 -row 20193 -row 20194 -row 20195 -row 20196 -row 20197 -row 20198 -row 20199 -row 20200 -row 20201 -row 20202 -row 20203 -row 20204 -row 20205 -row 20206 -row 20207 -row 20208 -row 20209 -row 20210 -row 20211 -row 20212 -row 20213 -row 20214 -row 20215 -row 20216 -row 20217 -row 20218 -row 20219 -row 20220 -row 20221 -row 20222 -row 20223 -row 20224 -row 20225 -row 20226 -row 20227 -row 20228 -row 20229 -row 20230 -row 20231 -row 20232 -row 20233 -row 20234 -row 20235 -row 20236 -row 20237 -row 20238 -row 20239 -row 20240 -row 20241 -row 20242 -row 20243 -row 20244 -row 20245 -row 20246 -row 20247 -row 20248 -row 20249 -row 20250 -row 20251 -row 20252 -row 20253 -row 20254 -row 20255 -row 20256 -row 20257 -row 20258 -row 20259 -row 20260 -row 20261 -row 20262 -row 20263 -row 20264 -row 20265 -row 20266 -row 20267 -row 20268 -row 20269 -row 20270 -row 20271 -row 20272 -row 20273 -row 20274 -row 20275 -row 20276 -row 20277 -row 20278 -row 20279 -row 20280 -row 20281 -row 20282 -row 20283 -row 20284 -row 20285 -row 20286 -row 20287 -row 20288 -row 20289 -row 20290 -row 20291 -row 20292 -row 20293 -row 20294 -row 20295 -row 20296 -row 20297 -row 20298 -row 20299 -row 20300 -row 20301 -row 20302 -row 20303 -row 20304 -row 20305 -row 20306 -row 20307 -row 20308 -row 20309 -row 20310 -row 20311 -row 20312 -row 20313 -row 20314 -row 20315 -row 20316 -row 20317 -row 20318 -row 20319 -row 20320 -row 20321 -row 20322 -row 20323 -row 20324 -row 20325 -row 20326 -row 20327 -row 20328 -row 20329 -row 20330 -row 20331 -row 20332 -row 20333 -row 20334 -row 20335 -row 20336 -row 20337 -row 20338 -row 20339 -row 20340 -row 20341 -row 20342 -row 20343 -row 20344 -row 20345 -row 20346 -row 20347 -row 20348 -row 20349 -row 20350 -row 20351 -row 20352 -row 20353 -row 20354 -row 20355 -row 20356 -row 20357 -row 20358 -row 20359 -row 20360 -row 20361 -row 20362 -row 20363 -row 20364 -row 20365 -row 20366 -row 20367 -row 20368 -row 20369 -row 20370 -row 20371 -row 20372 -row 20373 -row 20374 -row 20375 -row 20376 -row 20377 -row 20378 -row 20379 -row 20380 -row 20381 -row 20382 -row 20383 -row 20384 -row 20385 -row 20386 -row 20387 -row 20388 -row 20389 -row 20390 -row 20391 -row 20392 -row 20393 -row 20394 -row 20395 -row 20396 -row 20397 -row 20398 -row 20399 -row 20400 -row 20401 -row 20402 -row 20403 -row 20404 -row 20405 -row 20406 -row 20407 -row 20408 -row 20409 -row 20410 -row 20411 -row 20412 -row 20413 -row 20414 -row 20415 -row 20416 -row 20417 -row 20418 -row 20419 -row 20420 -row 20421 -row 20422 -row 20423 -row 20424 -row 20425 -row 20426 -row 20427 -row 20428 -row 20429 -row 20430 -row 20431 -row 20432 -row 20433 -row 20434 -row 20435 -row 20436 -row 20437 -row 20438 -row 20439 -row 20440 -row 20441 -row 20442 -row 20443 -row 20444 -row 20445 -row 20446 -row 20447 -row 20448 -row 20449 -row 20450 -row 20451 -row 20452 -row 20453 -row 20454 -row 20455 -row 20456 -row 20457 -row 20458 -row 20459 -row 20460 -row 20461 -row 20462 -row 20463 -row 20464 -row 20465 -row 20466 -row 20467 -row 20468 -row 20469 -row 20470 -row 20471 -row 20472 -row 20473 -row 20474 -row 20475 -row 20476 -row 20477 -row 20478 -row 20479 -row 20480 -row 20481 -row 20482 -row 20483 -row 20484 -row 20485 -row 20486 -row 20487 -row 20488 -row 20489 -row 20490 -row 20491 -row 20492 -row 20493 -row 20494 -row 20495 -row 20496 -row 20497 -row 20498 -row 20499 -row 20500 -row 20501 -row 20502 -row 20503 -row 20504 -row 20505 -row 20506 -row 20507 -row 20508 -row 20509 -row 20510 -row 20511 -row 20512 -row 20513 -row 20514 -row 20515 -row 20516 -row 20517 -row 20518 -row 20519 -row 20520 -row 20521 -row 20522 -row 20523 -row 20524 -row 20525 -row 20526 -row 20527 -row 20528 -row 20529 -row 20530 -row 20531 -row 20532 -row 20533 -row 20534 -row 20535 -row 20536 -row 20537 -row 20538 -row 20539 -row 20540 -row 20541 -row 20542 -row 20543 -row 20544 -row 20545 -row 20546 -row 20547 -row 20548 -row 20549 -row 20550 -row 20551 -row 20552 -row 20553 -row 20554 -row 20555 -row 20556 -row 20557 -row 20558 -row 20559 -row 20560 -row 20561 -row 20562 -row 20563 -row 20564 -row 20565 -row 20566 -row 20567 -row 20568 -row 20569 -row 20570 -row 20571 -row 20572 -row 20573 -row 20574 -row 20575 -row 20576 -row 20577 -row 20578 -row 20579 -row 20580 -row 20581 -row 20582 -row 20583 -row 20584 -row 20585 -row 20586 -row 20587 -row 20588 -row 20589 -row 20590 -row 20591 -row 20592 -row 20593 -row 20594 -row 20595 -row 20596 -row 20597 -row 20598 -row 20599 -row 20600 -row 20601 -row 20602 -row 20603 -row 20604 -row 20605 -row 20606 -row 20607 -row 20608 -row 20609 -row 20610 -row 20611 -row 20612 -row 20613 -row 20614 -row 20615 -row 20616 -row 20617 -row 20618 -row 20619 -row 20620 -row 20621 -row 20622 -row 20623 -row 20624 -row 20625 -row 20626 -row 20627 -row 20628 -row 20629 -row 20630 -row 20631 -row 20632 -row 20633 -row 20634 -row 20635 -row 20636 -row 20637 -row 20638 -row 20639 -row 20640 -row 20641 -row 20642 -row 20643 -row 20644 -row 20645 -row 20646 -row 20647 -row 20648 -row 20649 -row 20650 -row 20651 -row 20652 -row 20653 -row 20654 -row 20655 -row 20656 -row 20657 -row 20658 -row 20659 -row 20660 -row 20661 -row 20662 -row 20663 -row 20664 -row 20665 -row 20666 -row 20667 -row 20668 -row 20669 -row 20670 -row 20671 -row 20672 -row 20673 -row 20674 -row 20675 -row 20676 -row 20677 -row 20678 -row 20679 -row 20680 -row 20681 -row 20682 -row 20683 -row 20684 -row 20685 -row 20686 -row 20687 -row 20688 -row 20689 -row 20690 -row 20691 -row 20692 -row 20693 -row 20694 -row 20695 -row 20696 -row 20697 -row 20698 -row 20699 -row 20700 -row 20701 -row 20702 -row 20703 -row 20704 -row 20705 -row 20706 -row 20707 -row 20708 -row 20709 -row 20710 -row 20711 -row 20712 -row 20713 -row 20714 -row 20715 -row 20716 -row 20717 -row 20718 -row 20719 -row 20720 -row 20721 -row 20722 -row 20723 -row 20724 -row 20725 -row 20726 -row 20727 -row 20728 -row 20729 -row 20730 -row 20731 -row 20732 -row 20733 -row 20734 -row 20735 -row 20736 -row 20737 -row 20738 -row 20739 -row 20740 -row 20741 -row 20742 -row 20743 -row 20744 -row 20745 -row 20746 -row 20747 -row 20748 -row 20749 -row 20750 -row 20751 -row 20752 -row 20753 -row 20754 -row 20755 -row 20756 -row 20757 -row 20758 -row 20759 -row 20760 -row 20761 -row 20762 -row 20763 -row 20764 -row 20765 -row 20766 -row 20767 -row 20768 -row 20769 -row 20770 -row 20771 -row 20772 -row 20773 -row 20774 -row 20775 -row 20776 -row 20777 -row 20778 -row 20779 -row 20780 -row 20781 -row 20782 -row 20783 -row 20784 -row 20785 -row 20786 -row 20787 -row 20788 -row 20789 -row 20790 -row 20791 -row 20792 -row 20793 -row 20794 -row 20795 -row 20796 -row 20797 -row 20798 -row 20799 -row 20800 -row 20801 -row 20802 -row 20803 -row 20804 -row 20805 -row 20806 -row 20807 -row 20808 -row 20809 -row 20810 -row 20811 -row 20812 -row 20813 -row 20814 -row 20815 -row 20816 -row 20817 -row 20818 -row 20819 -row 20820 -row 20821 -row 20822 -row 20823 -row 20824 -row 20825 -row 20826 -row 20827 -row 20828 -row 20829 -row 20830 -row 20831 -row 20832 -row 20833 -row 20834 -row 20835 -row 20836 -row 20837 -row 20838 -row 20839 -row 20840 -row 20841 -row 20842 -row 20843 -row 20844 -row 20845 -row 20846 -row 20847 -row 20848 -row 20849 -row 20850 -row 20851 -row 20852 -row 20853 -row 20854 -row 20855 -row 20856 -row 20857 -row 20858 -row 20859 -row 20860 -row 20861 -row 20862 -row 20863 -row 20864 -row 20865 -row 20866 -row 20867 -row 20868 -row 20869 -row 20870 -row 20871 -row 20872 -row 20873 -row 20874 -row 20875 -row 20876 -row 20877 -row 20878 -row 20879 -row 20880 -row 20881 -row 20882 -row 20883 -row 20884 -row 20885 -row 20886 -row 20887 -row 20888 -row 20889 -row 20890 -row 20891 -row 20892 -row 20893 -row 20894 -row 20895 -row 20896 -row 20897 -row 20898 -row 20899 -row 20900 -row 20901 -row 20902 -row 20903 -row 20904 -row 20905 -row 20906 -row 20907 -row 20908 -row 20909 -row 20910 -row 20911 -row 20912 -row 20913 -row 20914 -row 20915 -row 20916 -row 20917 -row 20918 -row 20919 -row 20920 -row 20921 -row 20922 -row 20923 -row 20924 -row 20925 -row 20926 -row 20927 -row 20928 -row 20929 -row 20930 -row 20931 -row 20932 -row 20933 -row 20934 -row 20935 -row 20936 -row 20937 -row 20938 -row 20939 -row 20940 -row 20941 -row 20942 -row 20943 -row 20944 -row 20945 -row 20946 -row 20947 -row 20948 -row 20949 -row 20950 -row 20951 -row 20952 -row 20953 -row 20954 -row 20955 -row 20956 -row 20957 -row 20958 -row 20959 -row 20960 -row 20961 -row 20962 -row 20963 -row 20964 -row 20965 -row 20966 -row 20967 -row 20968 -row 20969 -row 20970 -row 20971 -row 20972 -row 20973 -row 20974 -row 20975 -row 20976 -row 20977 -row 20978 -row 20979 -row 20980 -row 20981 -row 20982 -row 20983 -row 20984 -row 20985 -row 20986 -row 20987 -row 20988 -row 20989 -row 20990 -row 20991 -row 20992 -row 20993 -row 20994 -row 20995 -row 20996 -row 20997 -row 20998 -row 20999 -row 21000 -row 21001 -row 21002 -row 21003 -row 21004 -row 21005 -row 21006 -row 21007 -row 21008 -row 21009 -row 21010 -row 21011 -row 21012 -row 21013 -row 21014 -row 21015 -row 21016 -row 21017 -row 21018 -row 21019 -row 21020 -row 21021 -row 21022 -row 21023 -row 21024 -row 21025 -row 21026 -row 21027 -row 21028 -row 21029 -row 21030 -row 21031 -row 21032 -row 21033 -row 21034 -row 21035 -row 21036 -row 21037 -row 21038 -row 21039 -row 21040 -row 21041 -row 21042 -row 21043 -row 21044 -row 21045 -row 21046 -row 21047 -row 21048 -row 21049 -row 21050 -row 21051 -row 21052 -row 21053 -row 21054 -row 21055 -row 21056 -row 21057 -row 21058 -row 21059 -row 21060 -row 21061 -row 21062 -row 21063 -row 21064 -row 21065 -row 21066 -row 21067 -row 21068 -row 21069 -row 21070 -row 21071 -row 21072 -row 21073 -row 21074 -row 21075 -row 21076 -row 21077 -row 21078 -row 21079 -row 21080 -row 21081 -row 21082 -row 21083 -row 21084 -row 21085 -row 21086 -row 21087 -row 21088 -row 21089 -row 21090 -row 21091 -row 21092 -row 21093 -row 21094 -row 21095 -row 21096 -row 21097 -row 21098 -row 21099 -row 21100 -row 21101 -row 21102 -row 21103 -row 21104 -row 21105 -row 21106 -row 21107 -row 21108 -row 21109 -row 21110 -row 21111 -row 21112 -row 21113 -row 21114 -row 21115 -row 21116 -row 21117 -row 21118 -row 21119 -row 21120 -row 21121 -row 21122 -row 21123 -row 21124 -row 21125 -row 21126 -row 21127 -row 21128 -row 21129 -row 21130 -row 21131 -row 21132 -row 21133 -row 21134 -row 21135 -row 21136 -row 21137 -row 21138 -row 21139 -row 21140 -row 21141 -row 21142 -row 21143 -row 21144 -row 21145 -row 21146 -row 21147 -row 21148 -row 21149 -row 21150 -row 21151 -row 21152 -row 21153 -row 21154 -row 21155 -row 21156 -row 21157 -row 21158 -row 21159 -row 21160 -row 21161 -row 21162 -row 21163 -row 21164 -row 21165 -row 21166 -row 21167 -row 21168 -row 21169 -row 21170 -row 21171 -row 21172 -row 21173 -row 21174 -row 21175 -row 21176 -row 21177 -row 21178 -row 21179 -row 21180 -row 21181 -row 21182 -row 21183 -row 21184 -row 21185 -row 21186 -row 21187 -row 21188 -row 21189 -row 21190 -row 21191 -row 21192 -row 21193 -row 21194 -row 21195 -row 21196 -row 21197 -row 21198 -row 21199 -row 21200 -row 21201 -row 21202 -row 21203 -row 21204 -row 21205 -row 21206 -row 21207 -row 21208 -row 21209 -row 21210 -row 21211 -row 21212 -row 21213 -row 21214 -row 21215 -row 21216 -row 21217 -row 21218 -row 21219 -row 21220 -row 21221 -row 21222 -row 21223 -row 21224 -row 21225 -row 21226 -row 21227 -row 21228 -row 21229 -row 21230 -row 21231 -row 21232 -row 21233 -row 21234 -row 21235 -row 21236 -row 21237 -row 21238 -row 21239 -row 21240 -row 21241 -row 21242 -row 21243 -row 21244 -row 21245 -row 21246 -row 21247 -row 21248 -row 21249 -row 21250 -row 21251 -row 21252 -row 21253 -row 21254 -row 21255 -row 21256 -row 21257 -row 21258 -row 21259 -row 21260 -row 21261 -row 21262 -row 21263 -row 21264 -row 21265 -row 21266 -row 21267 -row 21268 -row 21269 -row 21270 -row 21271 -row 21272 -row 21273 -row 21274 -row 21275 -row 21276 -row 21277 -row 21278 -row 21279 -row 21280 -row 21281 -row 21282 -row 21283 -row 21284 -row 21285 -row 21286 -row 21287 -row 21288 -row 21289 -row 21290 -row 21291 -row 21292 -row 21293 -row 21294 -row 21295 -row 21296 -row 21297 -row 21298 -row 21299 -row 21300 -row 21301 -row 21302 -row 21303 -row 21304 -row 21305 -row 21306 -row 21307 -row 21308 -row 21309 -row 21310 -row 21311 -row 21312 -row 21313 -row 21314 -row 21315 -row 21316 -row 21317 -row 21318 -row 21319 -row 21320 -row 21321 -row 21322 -row 21323 -row 21324 -row 21325 -row 21326 -row 21327 -row 21328 -row 21329 -row 21330 -row 21331 -row 21332 -row 21333 -row 21334 -row 21335 -row 21336 -row 21337 -row 21338 -row 21339 -row 21340 -row 21341 -row 21342 -row 21343 -row 21344 -row 21345 -row 21346 -row 21347 -row 21348 -row 21349 -row 21350 -row 21351 -row 21352 -row 21353 -row 21354 -row 21355 -row 21356 -row 21357 -row 21358 -row 21359 -row 21360 -row 21361 -row 21362 -row 21363 -row 21364 -row 21365 -row 21366 -row 21367 -row 21368 -row 21369 -row 21370 -row 21371 -row 21372 -row 21373 -row 21374 -row 21375 -row 21376 -row 21377 -row 21378 -row 21379 -row 21380 -row 21381 -row 21382 -row 21383 -row 21384 -row 21385 -row 21386 -row 21387 -row 21388 -row 21389 -row 21390 -row 21391 -row 21392 -row 21393 -row 21394 -row 21395 -row 21396 -row 21397 -row 21398 -row 21399 -row 21400 -row 21401 -row 21402 -row 21403 -row 21404 -row 21405 -row 21406 -row 21407 -row 21408 -row 21409 -row 21410 -row 21411 -row 21412 -row 21413 -row 21414 -row 21415 -row 21416 -row 21417 -row 21418 -row 21419 -row 21420 -row 21421 -row 21422 -row 21423 -row 21424 -row 21425 -row 21426 -row 21427 -row 21428 -row 21429 -row 21430 -row 21431 -row 21432 -row 21433 -row 21434 -row 21435 -row 21436 -row 21437 -row 21438 -row 21439 -row 21440 -row 21441 -row 21442 -row 21443 -row 21444 -row 21445 -row 21446 -row 21447 -row 21448 -row 21449 -row 21450 -row 21451 -row 21452 -row 21453 -row 21454 -row 21455 -row 21456 -row 21457 -row 21458 -row 21459 -row 21460 -row 21461 -row 21462 -row 21463 -row 21464 -row 21465 -row 21466 -row 21467 -row 21468 -row 21469 -row 21470 -row 21471 -row 21472 -row 21473 -row 21474 -row 21475 -row 21476 -row 21477 -row 21478 -row 21479 -row 21480 -row 21481 -row 21482 -row 21483 -row 21484 -row 21485 -row 21486 -row 21487 -row 21488 -row 21489 -row 21490 -row 21491 -row 21492 -row 21493 -row 21494 -row 21495 -row 21496 -row 21497 -row 21498 -row 21499 -row 21500 -row 21501 -row 21502 -row 21503 -row 21504 -row 21505 -row 21506 -row 21507 -row 21508 -row 21509 -row 21510 -row 21511 -row 21512 -row 21513 -row 21514 -row 21515 -row 21516 -row 21517 -row 21518 -row 21519 -row 21520 -row 21521 -row 21522 -row 21523 -row 21524 -row 21525 -row 21526 -row 21527 -row 21528 -row 21529 -row 21530 -row 21531 -row 21532 -row 21533 -row 21534 -row 21535 -row 21536 -row 21537 -row 21538 -row 21539 -row 21540 -row 21541 -row 21542 -row 21543 -row 21544 -row 21545 -row 21546 -row 21547 -row 21548 -row 21549 -row 21550 -row 21551 -row 21552 -row 21553 -row 21554 -row 21555 -row 21556 -row 21557 -row 21558 -row 21559 -row 21560 -row 21561 -row 21562 -row 21563 -row 21564 -row 21565 -row 21566 -row 21567 -row 21568 -row 21569 -row 21570 -row 21571 -row 21572 -row 21573 -row 21574 -row 21575 -row 21576 -row 21577 -row 21578 -row 21579 -row 21580 -row 21581 -row 21582 -row 21583 -row 21584 -row 21585 -row 21586 -row 21587 -row 21588 -row 21589 -row 21590 -row 21591 -row 21592 -row 21593 -row 21594 -row 21595 -row 21596 -row 21597 -row 21598 -row 21599 -row 21600 -row 21601 -row 21602 -row 21603 -row 21604 -row 21605 -row 21606 -row 21607 -row 21608 -row 21609 -row 21610 -row 21611 -row 21612 -row 21613 -row 21614 -row 21615 -row 21616 -row 21617 -row 21618 -row 21619 -row 21620 -row 21621 -row 21622 -row 21623 -row 21624 -row 21625 -row 21626 -row 21627 -row 21628 -row 21629 -row 21630 -row 21631 -row 21632 -row 21633 -row 21634 -row 21635 -row 21636 -row 21637 -row 21638 -row 21639 -row 21640 -row 21641 -row 21642 -row 21643 -row 21644 -row 21645 -row 21646 -row 21647 -row 21648 -row 21649 -row 21650 -row 21651 -row 21652 -row 21653 -row 21654 -row 21655 -row 21656 -row 21657 -row 21658 -row 21659 -row 21660 -row 21661 -row 21662 -row 21663 -row 21664 -row 21665 -row 21666 -row 21667 -row 21668 -row 21669 -row 21670 -row 21671 -row 21672 -row 21673 -row 21674 -row 21675 -row 21676 -row 21677 -row 21678 -row 21679 -row 21680 -row 21681 -row 21682 -row 21683 -row 21684 -row 21685 -row 21686 -row 21687 -row 21688 -row 21689 -row 21690 -row 21691 -row 21692 -row 21693 -row 21694 -row 21695 -row 21696 -row 21697 -row 21698 -row 21699 -row 21700 -row 21701 -row 21702 -row 21703 -row 21704 -row 21705 -row 21706 -row 21707 -row 21708 -row 21709 -row 21710 -row 21711 -row 21712 -row 21713 -row 21714 -row 21715 -row 21716 -row 21717 -row 21718 -row 21719 -row 21720 -row 21721 -row 21722 -row 21723 -row 21724 -row 21725 -row 21726 -row 21727 -row 21728 -row 21729 -row 21730 -row 21731 -row 21732 -row 21733 -row 21734 -row 21735 -row 21736 -row 21737 -row 21738 -row 21739 -row 21740 -row 21741 -row 21742 -row 21743 -row 21744 -row 21745 -row 21746 -row 21747 -row 21748 -row 21749 -row 21750 -row 21751 -row 21752 -row 21753 -row 21754 -row 21755 -row 21756 -row 21757 -row 21758 -row 21759 -row 21760 -row 21761 -row 21762 -row 21763 -row 21764 -row 21765 -row 21766 -row 21767 -row 21768 -row 21769 -row 21770 -row 21771 -row 21772 -row 21773 -row 21774 -row 21775 -row 21776 -row 21777 -row 21778 -row 21779 -row 21780 -row 21781 -row 21782 -row 21783 -row 21784 -row 21785 -row 21786 -row 21787 -row 21788 -row 21789 -row 21790 -row 21791 -row 21792 -row 21793 -row 21794 -row 21795 -row 21796 -row 21797 -row 21798 -row 21799 -row 21800 -row 21801 -row 21802 -row 21803 -row 21804 -row 21805 -row 21806 -row 21807 -row 21808 -row 21809 -row 21810 -row 21811 -row 21812 -row 21813 -row 21814 -row 21815 -row 21816 -row 21817 -row 21818 -row 21819 -row 21820 -row 21821 -row 21822 -row 21823 -row 21824 -row 21825 -row 21826 -row 21827 -row 21828 -row 21829 -row 21830 -row 21831 -row 21832 -row 21833 -row 21834 -row 21835 -row 21836 -row 21837 -row 21838 -row 21839 -row 21840 -row 21841 -row 21842 -row 21843 -row 21844 -row 21845 -row 21846 -row 21847 -row 21848 -row 21849 -row 21850 -row 21851 -row 21852 -row 21853 -row 21854 -row 21855 -row 21856 -row 21857 -row 21858 -row 21859 -row 21860 -row 21861 -row 21862 -row 21863 -row 21864 -row 21865 -row 21866 -row 21867 -row 21868 -row 21869 -row 21870 -row 21871 -row 21872 -row 21873 -row 21874 -row 21875 -row 21876 -row 21877 -row 21878 -row 21879 -row 21880 -row 21881 -row 21882 -row 21883 -row 21884 -row 21885 -row 21886 -row 21887 -row 21888 -row 21889 -row 21890 -row 21891 -row 21892 -row 21893 -row 21894 -row 21895 -row 21896 -row 21897 -row 21898 -row 21899 -row 21900 -row 21901 -row 21902 -row 21903 -row 21904 -row 21905 -row 21906 -row 21907 -row 21908 -row 21909 -row 21910 -row 21911 -row 21912 -row 21913 -row 21914 -row 21915 -row 21916 -row 21917 -row 21918 -row 21919 -row 21920 -row 21921 -row 21922 -row 21923 -row 21924 -row 21925 -row 21926 -row 21927 -row 21928 -row 21929 -row 21930 -row 21931 -row 21932 -row 21933 -row 21934 -row 21935 -row 21936 -row 21937 -row 21938 -row 21939 -row 21940 -row 21941 -row 21942 -row 21943 -row 21944 -row 21945 -row 21946 -row 21947 -row 21948 -row 21949 -row 21950 -row 21951 -row 21952 -row 21953 -row 21954 -row 21955 -row 21956 -row 21957 -row 21958 -row 21959 -row 21960 -row 21961 -row 21962 -row 21963 -row 21964 -row 21965 -row 21966 -row 21967 -row 21968 -row 21969 -row 21970 -row 21971 -row 21972 -row 21973 -row 21974 -row 21975 -row 21976 -row 21977 -row 21978 -row 21979 -row 21980 -row 21981 -row 21982 -row 21983 -row 21984 -row 21985 -row 21986 -row 21987 -row 21988 -row 21989 -row 21990 -row 21991 -row 21992 -row 21993 -row 21994 -row 21995 -row 21996 -row 21997 -row 21998 -row 21999 -row 22000 -row 22001 -row 22002 -row 22003 -row 22004 -row 22005 -row 22006 -row 22007 -row 22008 -row 22009 -row 22010 -row 22011 -row 22012 -row 22013 -row 22014 -row 22015 -row 22016 -row 22017 -row 22018 -row 22019 -row 22020 -row 22021 -row 22022 -row 22023 -row 22024 -row 22025 -row 22026 -row 22027 -row 22028 -row 22029 -row 22030 -row 22031 -row 22032 -row 22033 -row 22034 -row 22035 -row 22036 -row 22037 -row 22038 -row 22039 -row 22040 -row 22041 -row 22042 -row 22043 -row 22044 -row 22045 -row 22046 -row 22047 -row 22048 -row 22049 -row 22050 -row 22051 -row 22052 -row 22053 -row 22054 -row 22055 -row 22056 -row 22057 -row 22058 -row 22059 -row 22060 -row 22061 -row 22062 -row 22063 -row 22064 -row 22065 -row 22066 -row 22067 -row 22068 -row 22069 -row 22070 -row 22071 -row 22072 -row 22073 -row 22074 -row 22075 -row 22076 -row 22077 -row 22078 -row 22079 -row 22080 -row 22081 -row 22082 -row 22083 -row 22084 -row 22085 -row 22086 -row 22087 -row 22088 -row 22089 -row 22090 -row 22091 -row 22092 -row 22093 -row 22094 -row 22095 -row 22096 -row 22097 -row 22098 -row 22099 -row 22100 -row 22101 -row 22102 -row 22103 -row 22104 -row 22105 -row 22106 -row 22107 -row 22108 -row 22109 -row 22110 -row 22111 -row 22112 -row 22113 -row 22114 -row 22115 -row 22116 -row 22117 -row 22118 -row 22119 -row 22120 -row 22121 -row 22122 -row 22123 -row 22124 -row 22125 -row 22126 -row 22127 -row 22128 -row 22129 -row 22130 -row 22131 -row 22132 -row 22133 -row 22134 -row 22135 -row 22136 -row 22137 -row 22138 -row 22139 -row 22140 -row 22141 -row 22142 -row 22143 -row 22144 -row 22145 -row 22146 -row 22147 -row 22148 -row 22149 -row 22150 -row 22151 -row 22152 -row 22153 -row 22154 -row 22155 -row 22156 -row 22157 -row 22158 -row 22159 -row 22160 -row 22161 -row 22162 -row 22163 -row 22164 -row 22165 -row 22166 -row 22167 -row 22168 -row 22169 -row 22170 -row 22171 -row 22172 -row 22173 -row 22174 -row 22175 -row 22176 -row 22177 -row 22178 -row 22179 -row 22180 -row 22181 -row 22182 -row 22183 -row 22184 -row 22185 -row 22186 -row 22187 -row 22188 -row 22189 -row 22190 -row 22191 -row 22192 -row 22193 -row 22194 -row 22195 -row 22196 -row 22197 -row 22198 -row 22199 -row 22200 -row 22201 -row 22202 -row 22203 -row 22204 -row 22205 -row 22206 -row 22207 -row 22208 -row 22209 -row 22210 -row 22211 -row 22212 -row 22213 -row 22214 -row 22215 -row 22216 -row 22217 -row 22218 -row 22219 -row 22220 -row 22221 -row 22222 -row 22223 -row 22224 -row 22225 -row 22226 -row 22227 -row 22228 -row 22229 -row 22230 -row 22231 -row 22232 -row 22233 -row 22234 -row 22235 -row 22236 -row 22237 -row 22238 -row 22239 -row 22240 -row 22241 -row 22242 -row 22243 -row 22244 -row 22245 -row 22246 -row 22247 -row 22248 -row 22249 -row 22250 -row 22251 -row 22252 -row 22253 -row 22254 -row 22255 -row 22256 -row 22257 -row 22258 -row 22259 -row 22260 -row 22261 -row 22262 -row 22263 -row 22264 -row 22265 -row 22266 -row 22267 -row 22268 -row 22269 -row 22270 -row 22271 -row 22272 -row 22273 -row 22274 -row 22275 -row 22276 -row 22277 -row 22278 -row 22279 -row 22280 -row 22281 -row 22282 -row 22283 -row 22284 -row 22285 -row 22286 -row 22287 -row 22288 -row 22289 -row 22290 -row 22291 -row 22292 -row 22293 -row 22294 -row 22295 -row 22296 -row 22297 -row 22298 -row 22299 -row 22300 -row 22301 -row 22302 -row 22303 -row 22304 -row 22305 -row 22306 -row 22307 -row 22308 -row 22309 -row 22310 -row 22311 -row 22312 -row 22313 -row 22314 -row 22315 -row 22316 -row 22317 -row 22318 -row 22319 -row 22320 -row 22321 -row 22322 -row 22323 -row 22324 -row 22325 -row 22326 -row 22327 -row 22328 -row 22329 -row 22330 -row 22331 -row 22332 -row 22333 -row 22334 -row 22335 -row 22336 -row 22337 -row 22338 -row 22339 -row 22340 -row 22341 -row 22342 -row 22343 -row 22344 -row 22345 -row 22346 -row 22347 -row 22348 -row 22349 -row 22350 -row 22351 -row 22352 -row 22353 -row 22354 -row 22355 -row 22356 -row 22357 -row 22358 -row 22359 -row 22360 -row 22361 -row 22362 -row 22363 -row 22364 -row 22365 -row 22366 -row 22367 -row 22368 -row 22369 -row 22370 -row 22371 -row 22372 -row 22373 -row 22374 -row 22375 -row 22376 -row 22377 -row 22378 -row 22379 -row 22380 -row 22381 -row 22382 -row 22383 -row 22384 -row 22385 -row 22386 -row 22387 -row 22388 -row 22389 -row 22390 -row 22391 -row 22392 -row 22393 -row 22394 -row 22395 -row 22396 -row 22397 -row 22398 -row 22399 -row 22400 -row 22401 -row 22402 -row 22403 -row 22404 -row 22405 -row 22406 -row 22407 -row 22408 -row 22409 -row 22410 -row 22411 -row 22412 -row 22413 -row 22414 -row 22415 -row 22416 -row 22417 -row 22418 -row 22419 -row 22420 -row 22421 -row 22422 -row 22423 -row 22424 -row 22425 -row 22426 -row 22427 -row 22428 -row 22429 -row 22430 -row 22431 -row 22432 -row 22433 -row 22434 -row 22435 -row 22436 -row 22437 -row 22438 -row 22439 -row 22440 -row 22441 -row 22442 -row 22443 -row 22444 -row 22445 -row 22446 -row 22447 -row 22448 -row 22449 -row 22450 -row 22451 -row 22452 -row 22453 -row 22454 -row 22455 -row 22456 -row 22457 -row 22458 -row 22459 -row 22460 -row 22461 -row 22462 -row 22463 -row 22464 -row 22465 -row 22466 -row 22467 -row 22468 -row 22469 -row 22470 -row 22471 -row 22472 -row 22473 -row 22474 -row 22475 -row 22476 -row 22477 -row 22478 -row 22479 -row 22480 -row 22481 -row 22482 -row 22483 -row 22484 -row 22485 -row 22486 -row 22487 -row 22488 -row 22489 -row 22490 -row 22491 -row 22492 -row 22493 -row 22494 -row 22495 -row 22496 -row 22497 -row 22498 -row 22499 -row 22500 -row 22501 -row 22502 -row 22503 -row 22504 -row 22505 -row 22506 -row 22507 -row 22508 -row 22509 -row 22510 -row 22511 -row 22512 -row 22513 -row 22514 -row 22515 -row 22516 -row 22517 -row 22518 -row 22519 -row 22520 -row 22521 -row 22522 -row 22523 -row 22524 -row 22525 -row 22526 -row 22527 -row 22528 -row 22529 -row 22530 -row 22531 -row 22532 -row 22533 -row 22534 -row 22535 -row 22536 -row 22537 -row 22538 -row 22539 -row 22540 -row 22541 -row 22542 -row 22543 -row 22544 -row 22545 -row 22546 -row 22547 -row 22548 -row 22549 -row 22550 -row 22551 -row 22552 -row 22553 -row 22554 -row 22555 -row 22556 -row 22557 -row 22558 -row 22559 -row 22560 -row 22561 -row 22562 -row 22563 -row 22564 -row 22565 -row 22566 -row 22567 -row 22568 -row 22569 -row 22570 -row 22571 -row 22572 -row 22573 -row 22574 -row 22575 -row 22576 -row 22577 -row 22578 -row 22579 -row 22580 -row 22581 -row 22582 -row 22583 -row 22584 -row 22585 -row 22586 -row 22587 -row 22588 -row 22589 -row 22590 -row 22591 -row 22592 -row 22593 -row 22594 -row 22595 -row 22596 -row 22597 -row 22598 -row 22599 -row 22600 -row 22601 -row 22602 -row 22603 -row 22604 -row 22605 -row 22606 -row 22607 -row 22608 -row 22609 -row 22610 -row 22611 -row 22612 -row 22613 -row 22614 -row 22615 -row 22616 -row 22617 -row 22618 -row 22619 -row 22620 -row 22621 -row 22622 -row 22623 -row 22624 -row 22625 -row 22626 -row 22627 -row 22628 -row 22629 -row 22630 -row 22631 -row 22632 -row 22633 -row 22634 -row 22635 -row 22636 -row 22637 -row 22638 -row 22639 -row 22640 -row 22641 -row 22642 -row 22643 -row 22644 -row 22645 -row 22646 -row 22647 -row 22648 -row 22649 -row 22650 -row 22651 -row 22652 -row 22653 -row 22654 -row 22655 -row 22656 -row 22657 -row 22658 -row 22659 -row 22660 -row 22661 -row 22662 -row 22663 -row 22664 -row 22665 -row 22666 -row 22667 -row 22668 -row 22669 -row 22670 -row 22671 -row 22672 -row 22673 -row 22674 -row 22675 -row 22676 -row 22677 -row 22678 -row 22679 -row 22680 -row 22681 -row 22682 -row 22683 -row 22684 -row 22685 -row 22686 -row 22687 -row 22688 -row 22689 -row 22690 -row 22691 -row 22692 -row 22693 -row 22694 -row 22695 -row 22696 -row 22697 -row 22698 -row 22699 -row 22700 -row 22701 -row 22702 -row 22703 -row 22704 -row 22705 -row 22706 -row 22707 -row 22708 -row 22709 -row 22710 -row 22711 -row 22712 -row 22713 -row 22714 -row 22715 -row 22716 -row 22717 -row 22718 -row 22719 -row 22720 -row 22721 -row 22722 -row 22723 -row 22724 -row 22725 -row 22726 -row 22727 -row 22728 -row 22729 -row 22730 -row 22731 -row 22732 -row 22733 -row 22734 -row 22735 -row 22736 -row 22737 -row 22738 -row 22739 -row 22740 -row 22741 -row 22742 -row 22743 -row 22744 -row 22745 -row 22746 -row 22747 -row 22748 -row 22749 -row 22750 -row 22751 -row 22752 -row 22753 -row 22754 -row 22755 -row 22756 -row 22757 -row 22758 -row 22759 -row 22760 -row 22761 -row 22762 -row 22763 -row 22764 -row 22765 -row 22766 -row 22767 -row 22768 -row 22769 -row 22770 -row 22771 -row 22772 -row 22773 -row 22774 -row 22775 -row 22776 -row 22777 -row 22778 -row 22779 -row 22780 -row 22781 -row 22782 -row 22783 -row 22784 -row 22785 -row 22786 -row 22787 -row 22788 -row 22789 -row 22790 -row 22791 -row 22792 -row 22793 -row 22794 -row 22795 -row 22796 -row 22797 -row 22798 -row 22799 -row 22800 -row 22801 -row 22802 -row 22803 -row 22804 -row 22805 -row 22806 -row 22807 -row 22808 -row 22809 -row 22810 -row 22811 -row 22812 -row 22813 -row 22814 -row 22815 -row 22816 -row 22817 -row 22818 -row 22819 -row 22820 -row 22821 -row 22822 -row 22823 -row 22824 -row 22825 -row 22826 -row 22827 -row 22828 -row 22829 -row 22830 -row 22831 -row 22832 -row 22833 -row 22834 -row 22835 -row 22836 -row 22837 -row 22838 -row 22839 -row 22840 -row 22841 -row 22842 -row 22843 -row 22844 -row 22845 -row 22846 -row 22847 -row 22848 -row 22849 -row 22850 -row 22851 -row 22852 -row 22853 -row 22854 -row 22855 -row 22856 -row 22857 -row 22858 -row 22859 -row 22860 -row 22861 -row 22862 -row 22863 -row 22864 -row 22865 -row 22866 -row 22867 -row 22868 -row 22869 -row 22870 -row 22871 -row 22872 -row 22873 -row 22874 -row 22875 -row 22876 -row 22877 -row 22878 -row 22879 -row 22880 -row 22881 -row 22882 -row 22883 -row 22884 -row 22885 -row 22886 -row 22887 -row 22888 -row 22889 -row 22890 -row 22891 -row 22892 -row 22893 -row 22894 -row 22895 -row 22896 -row 22897 -row 22898 -row 22899 -row 22900 -row 22901 -row 22902 -row 22903 -row 22904 -row 22905 -row 22906 -row 22907 -row 22908 -row 22909 -row 22910 -row 22911 -row 22912 -row 22913 -row 22914 -row 22915 -row 22916 -row 22917 -row 22918 -row 22919 -row 22920 -row 22921 -row 22922 -row 22923 -row 22924 -row 22925 -row 22926 -row 22927 -row 22928 -row 22929 -row 22930 -row 22931 -row 22932 -row 22933 -row 22934 -row 22935 -row 22936 -row 22937 -row 22938 -row 22939 -row 22940 -row 22941 -row 22942 -row 22943 -row 22944 -row 22945 -row 22946 -row 22947 -row 22948 -row 22949 -row 22950 -row 22951 -row 22952 -row 22953 -row 22954 -row 22955 -row 22956 -row 22957 -row 22958 -row 22959 -row 22960 -row 22961 -row 22962 -row 22963 -row 22964 -row 22965 -row 22966 -row 22967 -row 22968 -row 22969 -row 22970 -row 22971 -row 22972 -row 22973 -row 22974 -row 22975 -row 22976 -row 22977 -row 22978 -row 22979 -row 22980 -row 22981 -row 22982 -row 22983 -row 22984 -row 22985 -row 22986 -row 22987 -row 22988 -row 22989 -row 22990 -row 22991 -row 22992 -row 22993 -row 22994 -row 22995 -row 22996 -row 22997 -row 22998 -row 22999 -row 23000 -row 23001 -row 23002 -row 23003 -row 23004 -row 23005 -row 23006 -row 23007 -row 23008 -row 23009 -row 23010 -row 23011 -row 23012 -row 23013 -row 23014 -row 23015 -row 23016 -row 23017 -row 23018 -row 23019 -row 23020 -row 23021 -row 23022 -row 23023 -row 23024 -row 23025 -row 23026 -row 23027 -row 23028 -row 23029 -row 23030 -row 23031 -row 23032 -row 23033 -row 23034 -row 23035 -row 23036 -row 23037 -row 23038 -row 23039 -row 23040 -row 23041 -row 23042 -row 23043 -row 23044 -row 23045 -row 23046 -row 23047 -row 23048 -row 23049 -row 23050 -row 23051 -row 23052 -row 23053 -row 23054 -row 23055 -row 23056 -row 23057 -row 23058 -row 23059 -row 23060 -row 23061 -row 23062 -row 23063 -row 23064 -row 23065 -row 23066 -row 23067 -row 23068 -row 23069 -row 23070 -row 23071 -row 23072 -row 23073 -row 23074 -row 23075 -row 23076 -row 23077 -row 23078 -row 23079 -row 23080 -row 23081 -row 23082 -row 23083 -row 23084 -row 23085 -row 23086 -row 23087 -row 23088 -row 23089 -row 23090 -row 23091 -row 23092 -row 23093 -row 23094 -row 23095 -row 23096 -row 23097 -row 23098 -row 23099 -row 23100 -row 23101 -row 23102 -row 23103 -row 23104 -row 23105 -row 23106 -row 23107 -row 23108 -row 23109 -row 23110 -row 23111 -row 23112 -row 23113 -row 23114 -row 23115 -row 23116 -row 23117 -row 23118 -row 23119 -row 23120 -row 23121 -row 23122 -row 23123 -row 23124 -row 23125 -row 23126 -row 23127 -row 23128 -row 23129 -row 23130 -row 23131 -row 23132 -row 23133 -row 23134 -row 23135 -row 23136 -row 23137 -row 23138 -row 23139 -row 23140 -row 23141 -row 23142 -row 23143 -row 23144 -row 23145 -row 23146 -row 23147 -row 23148 -row 23149 -row 23150 -row 23151 -row 23152 -row 23153 -row 23154 -row 23155 -row 23156 -row 23157 -row 23158 -row 23159 -row 23160 -row 23161 -row 23162 -row 23163 -row 23164 -row 23165 -row 23166 -row 23167 -row 23168 -row 23169 -row 23170 -row 23171 -row 23172 -row 23173 -row 23174 -row 23175 -row 23176 -row 23177 -row 23178 -row 23179 -row 23180 -row 23181 -row 23182 -row 23183 -row 23184 -row 23185 -row 23186 -row 23187 -row 23188 -row 23189 -row 23190 -row 23191 -row 23192 -row 23193 -row 23194 -row 23195 -row 23196 -row 23197 -row 23198 -row 23199 -row 23200 -row 23201 -row 23202 -row 23203 -row 23204 -row 23205 -row 23206 -row 23207 -row 23208 -row 23209 -row 23210 -row 23211 -row 23212 -row 23213 -row 23214 -row 23215 -row 23216 -row 23217 -row 23218 -row 23219 -row 23220 -row 23221 -row 23222 -row 23223 -row 23224 -row 23225 -row 23226 -row 23227 -row 23228 -row 23229 -row 23230 -row 23231 -row 23232 -row 23233 -row 23234 -row 23235 -row 23236 -row 23237 -row 23238 -row 23239 -row 23240 -row 23241 -row 23242 -row 23243 -row 23244 -row 23245 -row 23246 -row 23247 -row 23248 -row 23249 -row 23250 -row 23251 -row 23252 -row 23253 -row 23254 -row 23255 -row 23256 -row 23257 -row 23258 -row 23259 -row 23260 -row 23261 -row 23262 -row 23263 -row 23264 -row 23265 -row 23266 -row 23267 -row 23268 -row 23269 -row 23270 -row 23271 -row 23272 -row 23273 -row 23274 -row 23275 -row 23276 -row 23277 -row 23278 -row 23279 -row 23280 -row 23281 -row 23282 -row 23283 -row 23284 -row 23285 -row 23286 -row 23287 -row 23288 -row 23289 -row 23290 -row 23291 -row 23292 -row 23293 -row 23294 -row 23295 -row 23296 -row 23297 -row 23298 -row 23299 -row 23300 -row 23301 -row 23302 -row 23303 -row 23304 -row 23305 -row 23306 -row 23307 -row 23308 -row 23309 -row 23310 -row 23311 -row 23312 -row 23313 -row 23314 -row 23315 -row 23316 -row 23317 -row 23318 -row 23319 -row 23320 -row 23321 -row 23322 -row 23323 -row 23324 -row 23325 -row 23326 -row 23327 -row 23328 -row 23329 -row 23330 -row 23331 -row 23332 -row 23333 -row 23334 -row 23335 -row 23336 -row 23337 -row 23338 -row 23339 -row 23340 -row 23341 -row 23342 -row 23343 -row 23344 -row 23345 -row 23346 -row 23347 -row 23348 -row 23349 -row 23350 -row 23351 -row 23352 -row 23353 -row 23354 -row 23355 -row 23356 -row 23357 -row 23358 -row 23359 -row 23360 -row 23361 -row 23362 -row 23363 -row 23364 -row 23365 -row 23366 -row 23367 -row 23368 -row 23369 -row 23370 -row 23371 -row 23372 -row 23373 -row 23374 -row 23375 -row 23376 -row 23377 -row 23378 -row 23379 -row 23380 -row 23381 -row 23382 -row 23383 -row 23384 -row 23385 -row 23386 -row 23387 -row 23388 -row 23389 -row 23390 -row 23391 -row 23392 -row 23393 -row 23394 -row 23395 -row 23396 -row 23397 -row 23398 -row 23399 -row 23400 -row 23401 -row 23402 -row 23403 -row 23404 -row 23405 -row 23406 -row 23407 -row 23408 -row 23409 -row 23410 -row 23411 -row 23412 -row 23413 -row 23414 -row 23415 -row 23416 -row 23417 -row 23418 -row 23419 -row 23420 -row 23421 -row 23422 -row 23423 -row 23424 -row 23425 -row 23426 -row 23427 -row 23428 -row 23429 -row 23430 -row 23431 -row 23432 -row 23433 -row 23434 -row 23435 -row 23436 -row 23437 -row 23438 -row 23439 -row 23440 -row 23441 -row 23442 -row 23443 -row 23444 -row 23445 -row 23446 -row 23447 -row 23448 -row 23449 -row 23450 -row 23451 -row 23452 -row 23453 -row 23454 -row 23455 -row 23456 -row 23457 -row 23458 -row 23459 -row 23460 -row 23461 -row 23462 -row 23463 -row 23464 -row 23465 -row 23466 -row 23467 -row 23468 -row 23469 -row 23470 -row 23471 -row 23472 -row 23473 -row 23474 -row 23475 -row 23476 -row 23477 -row 23478 -row 23479 -row 23480 -row 23481 -row 23482 -row 23483 -row 23484 -row 23485 -row 23486 -row 23487 -row 23488 -row 23489 -row 23490 -row 23491 -row 23492 -row 23493 -row 23494 -row 23495 -row 23496 -row 23497 -row 23498 -row 23499 -row 23500 -row 23501 -row 23502 -row 23503 -row 23504 -row 23505 -row 23506 -row 23507 -row 23508 -row 23509 -row 23510 -row 23511 -row 23512 -row 23513 -row 23514 -row 23515 -row 23516 -row 23517 -row 23518 -row 23519 -row 23520 -row 23521 -row 23522 -row 23523 -row 23524 -row 23525 -row 23526 -row 23527 -row 23528 -row 23529 -row 23530 -row 23531 -row 23532 -row 23533 -row 23534 -row 23535 -row 23536 -row 23537 -row 23538 -row 23539 -row 23540 -row 23541 -row 23542 -row 23543 -row 23544 -row 23545 -row 23546 -row 23547 -row 23548 -row 23549 -row 23550 -row 23551 -row 23552 -row 23553 -row 23554 -row 23555 -row 23556 -row 23557 -row 23558 -row 23559 -row 23560 -row 23561 -row 23562 -row 23563 -row 23564 -row 23565 -row 23566 -row 23567 -row 23568 -row 23569 -row 23570 -row 23571 -row 23572 -row 23573 -row 23574 -row 23575 -row 23576 -row 23577 -row 23578 -row 23579 -row 23580 -row 23581 -row 23582 -row 23583 -row 23584 -row 23585 -row 23586 -row 23587 -row 23588 -row 23589 -row 23590 -row 23591 -row 23592 -row 23593 -row 23594 -row 23595 -row 23596 -row 23597 -row 23598 -row 23599 -row 23600 -row 23601 -row 23602 -row 23603 -row 23604 -row 23605 -row 23606 -row 23607 -row 23608 -row 23609 -row 23610 -row 23611 -row 23612 -row 23613 -row 23614 -row 23615 -row 23616 -row 23617 -row 23618 -row 23619 -row 23620 -row 23621 -row 23622 -row 23623 -row 23624 -row 23625 -row 23626 -row 23627 -row 23628 -row 23629 -row 23630 -row 23631 -row 23632 -row 23633 -row 23634 -row 23635 -row 23636 -row 23637 -row 23638 -row 23639 -row 23640 -row 23641 -row 23642 -row 23643 -row 23644 -row 23645 -row 23646 -row 23647 -row 23648 -row 23649 -row 23650 -row 23651 -row 23652 -row 23653 -row 23654 -row 23655 -row 23656 -row 23657 -row 23658 -row 23659 -row 23660 -row 23661 -row 23662 -row 23663 -row 23664 -row 23665 -row 23666 -row 23667 -row 23668 -row 23669 -row 23670 -row 23671 -row 23672 -row 23673 -row 23674 -row 23675 -row 23676 -row 23677 -row 23678 -row 23679 -row 23680 -row 23681 -row 23682 -row 23683 -row 23684 -row 23685 -row 23686 -row 23687 -row 23688 -row 23689 -row 23690 -row 23691 -row 23692 -row 23693 -row 23694 -row 23695 -row 23696 -row 23697 -row 23698 -row 23699 -row 23700 -row 23701 -row 23702 -row 23703 -row 23704 -row 23705 -row 23706 -row 23707 -row 23708 -row 23709 -row 23710 -row 23711 -row 23712 -row 23713 -row 23714 -row 23715 -row 23716 -row 23717 -row 23718 -row 23719 -row 23720 -row 23721 -row 23722 -row 23723 -row 23724 -row 23725 -row 23726 -row 23727 -row 23728 -row 23729 -row 23730 -row 23731 -row 23732 -row 23733 -row 23734 -row 23735 -row 23736 -row 23737 -row 23738 -row 23739 -row 23740 -row 23741 -row 23742 -row 23743 -row 23744 -row 23745 -row 23746 -row 23747 -row 23748 -row 23749 -row 23750 -row 23751 -row 23752 -row 23753 -row 23754 -row 23755 -row 23756 -row 23757 -row 23758 -row 23759 -row 23760 -row 23761 -row 23762 -row 23763 -row 23764 -row 23765 -row 23766 -row 23767 -row 23768 -row 23769 -row 23770 -row 23771 -row 23772 -row 23773 -row 23774 -row 23775 -row 23776 -row 23777 -row 23778 -row 23779 -row 23780 -row 23781 -row 23782 -row 23783 -row 23784 -row 23785 -row 23786 -row 23787 -row 23788 -row 23789 -row 23790 -row 23791 -row 23792 -row 23793 -row 23794 -row 23795 -row 23796 -row 23797 -row 23798 -row 23799 -row 23800 -row 23801 -row 23802 -row 23803 -row 23804 -row 23805 -row 23806 -row 23807 -row 23808 -row 23809 -row 23810 -row 23811 -row 23812 -row 23813 -row 23814 -row 23815 -row 23816 -row 23817 -row 23818 -row 23819 -row 23820 -row 23821 -row 23822 -row 23823 -row 23824 -row 23825 -row 23826 -row 23827 -row 23828 -row 23829 -row 23830 -row 23831 -row 23832 -row 23833 -row 23834 -row 23835 -row 23836 -row 23837 -row 23838 -row 23839 -row 23840 -row 23841 -row 23842 -row 23843 -row 23844 -row 23845 -row 23846 -row 23847 -row 23848 -row 23849 -row 23850 -row 23851 -row 23852 -row 23853 -row 23854 -row 23855 -row 23856 -row 23857 -row 23858 -row 23859 -row 23860 -row 23861 -row 23862 -row 23863 -row 23864 -row 23865 -row 23866 -row 23867 -row 23868 -row 23869 -row 23870 -row 23871 -row 23872 -row 23873 -row 23874 -row 23875 -row 23876 -row 23877 -row 23878 -row 23879 -row 23880 -row 23881 -row 23882 -row 23883 -row 23884 -row 23885 -row 23886 -row 23887 -row 23888 -row 23889 -row 23890 -row 23891 -row 23892 -row 23893 -row 23894 -row 23895 -row 23896 -row 23897 -row 23898 -row 23899 -row 23900 -row 23901 -row 23902 -row 23903 -row 23904 -row 23905 -row 23906 -row 23907 -row 23908 -row 23909 -row 23910 -row 23911 -row 23912 -row 23913 -row 23914 -row 23915 -row 23916 -row 23917 -row 23918 -row 23919 -row 23920 -row 23921 -row 23922 -row 23923 -row 23924 -row 23925 -row 23926 -row 23927 -row 23928 -row 23929 -row 23930 -row 23931 -row 23932 -row 23933 -row 23934 -row 23935 -row 23936 -row 23937 -row 23938 -row 23939 -row 23940 -row 23941 -row 23942 -row 23943 -row 23944 -row 23945 -row 23946 -row 23947 -row 23948 -row 23949 -row 23950 -row 23951 -row 23952 -row 23953 -row 23954 -row 23955 -row 23956 -row 23957 -row 23958 -row 23959 -row 23960 -row 23961 -row 23962 -row 23963 -row 23964 -row 23965 -row 23966 -row 23967 -row 23968 -row 23969 -row 23970 -row 23971 -row 23972 -row 23973 -row 23974 -row 23975 -row 23976 -row 23977 -row 23978 -row 23979 -row 23980 -row 23981 -row 23982 -row 23983 -row 23984 -row 23985 -row 23986 -row 23987 -row 23988 -row 23989 -row 23990 -row 23991 -row 23992 -row 23993 -row 23994 -row 23995 -row 23996 -row 23997 -row 23998 -row 23999 -row 24000 -row 24001 -row 24002 -row 24003 -row 24004 -row 24005 -row 24006 -row 24007 -row 24008 -row 24009 -row 24010 -row 24011 -row 24012 -row 24013 -row 24014 -row 24015 -row 24016 -row 24017 -row 24018 -row 24019 -row 24020 -row 24021 -row 24022 -row 24023 -row 24024 -row 24025 -row 24026 -row 24027 -row 24028 -row 24029 -row 24030 -row 24031 -row 24032 -row 24033 -row 24034 -row 24035 -row 24036 -row 24037 -row 24038 -row 24039 -row 24040 -row 24041 -row 24042 -row 24043 -row 24044 -row 24045 -row 24046 -row 24047 -row 24048 -row 24049 -row 24050 -row 24051 -row 24052 -row 24053 -row 24054 -row 24055 -row 24056 -row 24057 -row 24058 -row 24059 -row 24060 -row 24061 -row 24062 -row 24063 -row 24064 -row 24065 -row 24066 -row 24067 -row 24068 -row 24069 -row 24070 -row 24071 -row 24072 -row 24073 -row 24074 -row 24075 -row 24076 -row 24077 -row 24078 -row 24079 -row 24080 -row 24081 -row 24082 -row 24083 -row 24084 -row 24085 -row 24086 -row 24087 -row 24088 -row 24089 -row 24090 -row 24091 -row 24092 -row 24093 -row 24094 -row 24095 -row 24096 -row 24097 -row 24098 -row 24099 -row 24100 -row 24101 -row 24102 -row 24103 -row 24104 -row 24105 -row 24106 -row 24107 -row 24108 -row 24109 -row 24110 -row 24111 -row 24112 -row 24113 -row 24114 -row 24115 -row 24116 -row 24117 -row 24118 -row 24119 -row 24120 -row 24121 -row 24122 -row 24123 -row 24124 -row 24125 -row 24126 -row 24127 -row 24128 -row 24129 -row 24130 -row 24131 -row 24132 -row 24133 -row 24134 -row 24135 -row 24136 -row 24137 -row 24138 -row 24139 -row 24140 -row 24141 -row 24142 -row 24143 -row 24144 -row 24145 -row 24146 -row 24147 -row 24148 -row 24149 -row 24150 -row 24151 -row 24152 -row 24153 -row 24154 -row 24155 -row 24156 -row 24157 -row 24158 -row 24159 -row 24160 -row 24161 -row 24162 -row 24163 -row 24164 -row 24165 -row 24166 -row 24167 -row 24168 -row 24169 -row 24170 -row 24171 -row 24172 -row 24173 -row 24174 -row 24175 -row 24176 -row 24177 -row 24178 -row 24179 -row 24180 -row 24181 -row 24182 -row 24183 -row 24184 -row 24185 -row 24186 -row 24187 -row 24188 -row 24189 -row 24190 -row 24191 -row 24192 -row 24193 -row 24194 -row 24195 -row 24196 -row 24197 -row 24198 -row 24199 -row 24200 -row 24201 -row 24202 -row 24203 -row 24204 -row 24205 -row 24206 -row 24207 -row 24208 -row 24209 -row 24210 -row 24211 -row 24212 -row 24213 -row 24214 -row 24215 -row 24216 -row 24217 -row 24218 -row 24219 -row 24220 -row 24221 -row 24222 -row 24223 -row 24224 -row 24225 -row 24226 -row 24227 -row 24228 -row 24229 -row 24230 -row 24231 -row 24232 -row 24233 -row 24234 -row 24235 -row 24236 -row 24237 -row 24238 -row 24239 -row 24240 -row 24241 -row 24242 -row 24243 -row 24244 -row 24245 -row 24246 -row 24247 -row 24248 -row 24249 -row 24250 -row 24251 -row 24252 -row 24253 -row 24254 -row 24255 -row 24256 -row 24257 -row 24258 -row 24259 -row 24260 -row 24261 -row 24262 -row 24263 -row 24264 -row 24265 -row 24266 -row 24267 -row 24268 -row 24269 -row 24270 -row 24271 -row 24272 -row 24273 -row 24274 -row 24275 -row 24276 -row 24277 -row 24278 -row 24279 -row 24280 -row 24281 -row 24282 -row 24283 -row 24284 -row 24285 -row 24286 -row 24287 -row 24288 -row 24289 -row 24290 -row 24291 -row 24292 -row 24293 -row 24294 -row 24295 -row 24296 -row 24297 -row 24298 -row 24299 -row 24300 -row 24301 -row 24302 -row 24303 -row 24304 -row 24305 -row 24306 -row 24307 -row 24308 -row 24309 -row 24310 -row 24311 -row 24312 -row 24313 -row 24314 -row 24315 -row 24316 -row 24317 -row 24318 -row 24319 -row 24320 -row 24321 -row 24322 -row 24323 -row 24324 -row 24325 -row 24326 -row 24327 -row 24328 -row 24329 -row 24330 -row 24331 -row 24332 -row 24333 -row 24334 -row 24335 -row 24336 -row 24337 -row 24338 -row 24339 -row 24340 -row 24341 -row 24342 -row 24343 -row 24344 -row 24345 -row 24346 -row 24347 -row 24348 -row 24349 -row 24350 -row 24351 -row 24352 -row 24353 -row 24354 -row 24355 -row 24356 -row 24357 -row 24358 -row 24359 -row 24360 -row 24361 -row 24362 -row 24363 -row 24364 -row 24365 -row 24366 -row 24367 -row 24368 -row 24369 -row 24370 -row 24371 -row 24372 -row 24373 -row 24374 -row 24375 -row 24376 -row 24377 -row 24378 -row 24379 -row 24380 -row 24381 -row 24382 -row 24383 -row 24384 -row 24385 -row 24386 -row 24387 -row 24388 -row 24389 -row 24390 -row 24391 -row 24392 -row 24393 -row 24394 -row 24395 -row 24396 -row 24397 -row 24398 -row 24399 -row 24400 -row 24401 -row 24402 -row 24403 -row 24404 -row 24405 -row 24406 -row 24407 -row 24408 -row 24409 -row 24410 -row 24411 -row 24412 -row 24413 -row 24414 -row 24415 -row 24416 -row 24417 -row 24418 -row 24419 -row 24420 -row 24421 -row 24422 -row 24423 -row 24424 -row 24425 -row 24426 -row 24427 -row 24428 -row 24429 -row 24430 -row 24431 -row 24432 -row 24433 -row 24434 -row 24435 -row 24436 -row 24437 -row 24438 -row 24439 -row 24440 -row 24441 -row 24442 -row 24443 -row 24444 -row 24445 -row 24446 -row 24447 -row 24448 -row 24449 -row 24450 -row 24451 -row 24452 -row 24453 -row 24454 -row 24455 -row 24456 -row 24457 -row 24458 -row 24459 -row 24460 -row 24461 -row 24462 -row 24463 -row 24464 -row 24465 -row 24466 -row 24467 -row 24468 -row 24469 -row 24470 -row 24471 -row 24472 -row 24473 -row 24474 -row 24475 -row 24476 -row 24477 -row 24478 -row 24479 -row 24480 -row 24481 -row 24482 -row 24483 -row 24484 -row 24485 -row 24486 -row 24487 -row 24488 -row 24489 -row 24490 -row 24491 -row 24492 -row 24493 -row 24494 -row 24495 -row 24496 -row 24497 -row 24498 -row 24499 -row 24500 -row 24501 -row 24502 -row 24503 -row 24504 -row 24505 -row 24506 -row 24507 -row 24508 -row 24509 -row 24510 -row 24511 -row 24512 -row 24513 -row 24514 -row 24515 -row 24516 -row 24517 -row 24518 -row 24519 -row 24520 -row 24521 -row 24522 -row 24523 -row 24524 -row 24525 -row 24526 -row 24527 -row 24528 -row 24529 -row 24530 -row 24531 -row 24532 -row 24533 -row 24534 -row 24535 -row 24536 -row 24537 -row 24538 -row 24539 -row 24540 -row 24541 -row 24542 -row 24543 -row 24544 -row 24545 -row 24546 -row 24547 -row 24548 -row 24549 -row 24550 -row 24551 -row 24552 -row 24553 -row 24554 -row 24555 -row 24556 -row 24557 -row 24558 -row 24559 -row 24560 -row 24561 -row 24562 -row 24563 -row 24564 -row 24565 -row 24566 -row 24567 -row 24568 -row 24569 -row 24570 -row 24571 -row 24572 -row 24573 -row 24574 -row 24575 -row 24576 -row 24577 -row 24578 -row 24579 -row 24580 -row 24581 -row 24582 -row 24583 -row 24584 -row 24585 -row 24586 -row 24587 -row 24588 -row 24589 -row 24590 -row 24591 -row 24592 -row 24593 -row 24594 -row 24595 -row 24596 -row 24597 -row 24598 -row 24599 -row 24600 -row 24601 -row 24602 -row 24603 -row 24604 -row 24605 -row 24606 -row 24607 -row 24608 -row 24609 -row 24610 -row 24611 -row 24612 -row 24613 -row 24614 -row 24615 -row 24616 -row 24617 -row 24618 -row 24619 -row 24620 -row 24621 -row 24622 -row 24623 -row 24624 -row 24625 -row 24626 -row 24627 -row 24628 -row 24629 -row 24630 -row 24631 -row 24632 -row 24633 -row 24634 -row 24635 -row 24636 -row 24637 -row 24638 -row 24639 -row 24640 -row 24641 -row 24642 -row 24643 -row 24644 -row 24645 -row 24646 -row 24647 -row 24648 -row 24649 -row 24650 -row 24651 -row 24652 -row 24653 -row 24654 -row 24655 -row 24656 -row 24657 -row 24658 -row 24659 -row 24660 -row 24661 -row 24662 -row 24663 -row 24664 -row 24665 -row 24666 -row 24667 -row 24668 -row 24669 -row 24670 -row 24671 -row 24672 -row 24673 -row 24674 -row 24675 -row 24676 -row 24677 -row 24678 -row 24679 -row 24680 -row 24681 -row 24682 -row 24683 -row 24684 -row 24685 -row 24686 -row 24687 -row 24688 -row 24689 -row 24690 -row 24691 -row 24692 -row 24693 -row 24694 -row 24695 -row 24696 -row 24697 -row 24698 -row 24699 -row 24700 -row 24701 -row 24702 -row 24703 -row 24704 -row 24705 -row 24706 -row 24707 -row 24708 -row 24709 -row 24710 -row 24711 -row 24712 -row 24713 -row 24714 -row 24715 -row 24716 -row 24717 -row 24718 -row 24719 -row 24720 -row 24721 -row 24722 -row 24723 -row 24724 -row 24725 -row 24726 -row 24727 -row 24728 -row 24729 -row 24730 -row 24731 -row 24732 -row 24733 -row 24734 -row 24735 -row 24736 -row 24737 -row 24738 -row 24739 -row 24740 -row 24741 -row 24742 -row 24743 -row 24744 -row 24745 -row 24746 -row 24747 -row 24748 -row 24749 -row 24750 -row 24751 -row 24752 -row 24753 -row 24754 -row 24755 -row 24756 -row 24757 -row 24758 -row 24759 -row 24760 -row 24761 -row 24762 -row 24763 -row 24764 -row 24765 -row 24766 -row 24767 -row 24768 -row 24769 -row 24770 -row 24771 -row 24772 -row 24773 -row 24774 -row 24775 -row 24776 -row 24777 -row 24778 -row 24779 -row 24780 -row 24781 -row 24782 -row 24783 -row 24784 -row 24785 -row 24786 -row 24787 -row 24788 -row 24789 -row 24790 -row 24791 -row 24792 -row 24793 -row 24794 -row 24795 -row 24796 -row 24797 -row 24798 -row 24799 -row 24800 -row 24801 -row 24802 -row 24803 -row 24804 -row 24805 -row 24806 -row 24807 -row 24808 -row 24809 -row 24810 -row 24811 -row 24812 -row 24813 -row 24814 -row 24815 -row 24816 -row 24817 -row 24818 -row 24819 -row 24820 -row 24821 -row 24822 -row 24823 -row 24824 -row 24825 -row 24826 -row 24827 -row 24828 -row 24829 -row 24830 -row 24831 -row 24832 -row 24833 -row 24834 -row 24835 -row 24836 -row 24837 -row 24838 -row 24839 -row 24840 -row 24841 -row 24842 -row 24843 -row 24844 -row 24845 -row 24846 -row 24847 -row 24848 -row 24849 -row 24850 -row 24851 -row 24852 -row 24853 -row 24854 -row 24855 -row 24856 -row 24857 -row 24858 -row 24859 -row 24860 -row 24861 -row 24862 -row 24863 -row 24864 -row 24865 -row 24866 -row 24867 -row 24868 -row 24869 -row 24870 -row 24871 -row 24872 -row 24873 -row 24874 -row 24875 -row 24876 -row 24877 -row 24878 -row 24879 -row 24880 -row 24881 -row 24882 -row 24883 -row 24884 -row 24885 -row 24886 -row 24887 -row 24888 -row 24889 -row 24890 -row 24891 -row 24892 -row 24893 -row 24894 -row 24895 -row 24896 -row 24897 -row 24898 -row 24899 -row 24900 -row 24901 -row 24902 -row 24903 -row 24904 -row 24905 -row 24906 -row 24907 -row 24908 -row 24909 -row 24910 -row 24911 -row 24912 -row 24913 -row 24914 -row 24915 -row 24916 -row 24917 -row 24918 -row 24919 -row 24920 -row 24921 -row 24922 -row 24923 -row 24924 -row 24925 -row 24926 -row 24927 -row 24928 -row 24929 -row 24930 -row 24931 -row 24932 -row 24933 -row 24934 -row 24935 -row 24936 -row 24937 -row 24938 -row 24939 -row 24940 -row 24941 -row 24942 -row 24943 -row 24944 -row 24945 -row 24946 -row 24947 -row 24948 -row 24949 -row 24950 -row 24951 -row 24952 -row 24953 -row 24954 -row 24955 -row 24956 -row 24957 -row 24958 -row 24959 -row 24960 -row 24961 -row 24962 -row 24963 -row 24964 -row 24965 -row 24966 -row 24967 -row 24968 -row 24969 -row 24970 -row 24971 -row 24972 -row 24973 -row 24974 -row 24975 -row 24976 -row 24977 -row 24978 -row 24979 -row 24980 -row 24981 -row 24982 -row 24983 -row 24984 -row 24985 -row 24986 -row 24987 -row 24988 -row 24989 -row 24990 -row 24991 -row 24992 -row 24993 -row 24994 -row 24995 -row 24996 -row 24997 -row 24998 -row 24999 -row 25000 -``` diff --git a/examples/mdview2/testfiles/TABLES.MD b/examples/mdview2/testfiles/TABLES.MD deleted file mode 100644 index b5e6360..0000000 --- a/examples/mdview2/testfiles/TABLES.MD +++ /dev/null @@ -1,10502 +0,0 @@ -# Tables test - content cache exhaustion trigger - -## Table 1 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 2 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 3 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 4 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 5 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 6 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 7 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 8 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 9 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 10 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 11 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 12 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 13 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 14 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 15 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 16 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 17 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 18 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 19 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 20 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 21 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 22 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 23 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 24 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 25 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 26 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 27 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 28 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 29 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 30 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 31 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 32 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 33 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 34 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 35 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 36 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 37 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 38 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 39 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 40 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 41 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 42 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 43 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 44 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 45 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 46 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 47 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 48 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 49 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 50 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 51 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 52 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 53 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 54 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 55 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 56 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 57 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 58 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 59 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 60 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 61 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 62 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 63 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 64 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 65 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 66 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 67 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 68 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 69 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 70 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 71 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 72 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 73 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 74 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 75 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 76 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 77 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 78 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 79 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 80 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 81 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 82 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 83 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 84 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 85 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 86 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 87 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 88 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 89 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 90 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 91 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 92 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 93 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 94 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 95 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 96 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 97 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 98 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 99 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 100 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 101 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 102 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 103 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 104 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 105 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 106 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 107 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 108 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 109 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 110 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 111 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 112 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 113 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 114 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 115 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 116 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 117 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 118 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 119 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 120 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 121 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 122 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 123 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 124 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 125 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 126 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 127 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 128 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 129 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 130 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 131 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 132 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 133 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 134 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 135 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 136 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 137 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 138 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 139 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 140 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 141 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 142 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 143 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 144 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 145 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 146 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 147 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 148 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 149 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 150 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 151 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 152 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 153 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 154 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 155 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 156 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 157 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 158 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 159 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 160 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 161 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 162 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 163 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 164 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 165 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 166 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 167 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 168 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 169 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 170 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 171 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 172 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 173 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 174 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 175 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 176 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 177 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 178 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 179 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 180 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 181 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 182 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 183 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 184 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 185 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 186 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 187 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 188 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 189 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 190 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 191 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 192 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 193 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 194 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 195 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 196 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 197 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 198 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 199 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 200 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 201 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 202 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 203 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 204 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 205 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 206 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 207 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 208 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 209 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 210 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 211 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 212 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 213 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 214 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 215 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 216 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 217 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 218 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 219 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 220 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 221 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 222 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 223 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 224 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 225 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 226 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 227 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 228 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 229 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 230 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 231 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 232 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 233 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 234 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 235 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 236 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 237 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 238 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 239 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 240 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 241 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 242 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 243 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 244 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 245 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 246 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 247 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 248 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 249 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 250 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 251 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 252 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 253 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 254 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 255 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 256 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 257 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 258 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 259 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 260 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 261 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 262 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 263 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 264 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 265 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 266 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 267 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 268 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 269 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 270 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 271 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 272 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 273 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 274 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 275 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 276 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 277 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 278 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 279 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 280 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 281 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 282 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 283 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 284 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 285 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 286 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 287 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 288 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 289 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 290 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 291 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 292 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 293 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 294 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 295 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 296 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 297 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 298 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 299 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - -## Table 300 -| Column A | B | -|---|---| -| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | y | -| a0 | b | -| a1 | b | -| a2 | b | -| a3 | b | -| a4 | b | -| a5 | b | -| a6 | b | -| a7 | b | -| a8 | b | -| a9 | b | -| a10 | b | -| a11 | b | -| a12 | b | -| a13 | b | -| a14 | b | -| a15 | b | -| a16 | b | -| a17 | b | -| a18 | b | -| a19 | b | -| a20 | b | -| a21 | b | -| a22 | b | -| a23 | b | -| a24 | b | -| a25 | b | -| a26 | b | -| a27 | b | -| a28 | b | -| a29 | b | - diff --git a/examples/mdview2/testfiles/gen_testfiles.py b/examples/mdview2/testfiles/gen_testfiles.py deleted file mode 100644 index 47fe694..0000000 --- a/examples/mdview2/testfiles/gen_testfiles.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python3 -"""Генератор тестовых файлов mdview2 для проверки лимита 256 КБ и путей обрыва. - -Результат кладётся рядом со скриптом (examples/mdview2/testfiles/): - BIG.MD — ~216 КБ обычной прозы (CP866): успешный рендер большого файла. - TABLES.MD — таблично-тяжёлый (ASCII): пробивает кап контент-кэша (40 стр./набор), - т.к. неровные таблицы (короткие ячейки + один широкий столбец) - раздувают число (символ,атрибут)-ячеек кратно относительно источника. - LINES.MD — ~22000 коротких строк (ASCII): пробивает лимит строк (18432). - -Запуск: python3 gen_testfiles.py -""" -import os - -HERE = os.path.dirname(os.path.abspath(__file__)) -ROOT = os.path.dirname(HERE) - - -def write(name, data): - path = os.path.join(HERE, name) - mode = 'wb' if isinstance(data, (bytes, bytearray)) else 'w' - with open(path, mode) as f: - f.write(data) - print(f"{name}: {os.path.getsize(path)} bytes") - - -# --- BIG.MD / HUGE.MD: склейка README + READMEBG, перекодировано в CP866 ----- -# BIG (×2 ≈170 КБ) — успешный рендер большого файла (<256 КБ). -# HUGE (×4 ≈340 КБ) — БОЛЬШЕ 256 КБ: проверка клампа (читаются первые 256 КБ + -# строка-предупреждение TRUNC_FILE). -prose = bytearray() -for fn in ('README.MD', 'READMEBG.MD'): - with open(os.path.join(ROOT, fn), encoding='utf-8') as f: - prose += f.read().encode('cp866', errors='replace') -write('BIG.MD', bytes(prose * 2)) -write('HUGE.MD', bytes(prose * 4)) - -# --- TABLES.MD: неровные таблицы -> исчерпание контент-кэша ------------------ -# Один широкий столбец (72 симв.) на таблицу задаёт ширину; остальные ячейки -# короткие, но при рендере добиваются до этой ширины -> ~80 ячеек/строка из -# ~8 байт источника. ~300 таблиц × 30 строк дают ~840K ячеек (1.68 МБ контента) -# при ~108 КБ источника -> кап 640 КБ/набор пробивается, строк (~11K) < 18432, -# значит первой срабатывает причина TRUNC_CONTENT. -NT, ROWS = 300, 30 -wide = 'x' * 72 -out = ['# Tables test - content cache exhaustion trigger', ''] -for t in range(NT): - out.append(f'## Table {t + 1}') - out.append('| Column A | B |') - out.append('|---|---|') - out.append(f'| {wide} | y |') - for r in range(ROWS): - out.append(f'| a{r} | b |') - out.append('') -write('TABLES.MD', '\n'.join(out) + '\n') - -# --- LINES.MD: масса коротких строк -> исчерпание лимита строк --------------- -# ВАЖНО: вьювер склеивает подряд идущие непустые строки в один параграф -# (markdown soft-wrap), поэтому простые строки потекли бы в абзац. Нужен -# контент БЕЗ склейки -> кладём всё в fenced code block (verbatim, 1:1 -# строка-источник = экранная строка). 22000 строк > 18432 -> TRUNC_LINES; -# контент мал (~9 ячеек/строка), кап контента не задевается. -N = 25000 # ~250 КБ -> 16 страниц -> max_lines = 18432; строк больше -> обрыв на 18432 -out = ['# Short lines test - line limit trigger', '', '```'] -out += [f'row {i:05d}' for i in range(1, N + 1)] -out.append('```') -write('LINES.MD', '\n'.join(out) + '\n') diff --git a/examples/rpgwalk/.gitignore b/examples/rpgwalk/.gitignore deleted file mode 100644 index 7db6d1a..0000000 --- a/examples/rpgwalk/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.raw -*.atl -bard.pal -sprites.stamp diff --git a/examples/rpgwalk/Makefile b/examples/rpgwalk/Makefile deleted file mode 100644 index ecfe305..0000000 --- a/examples/rpgwalk/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -EXAMPLE := rpgwalk -EXTRA_FLAGS ?= --gfx 256 --memory tiny -# rpgprof.exe в EXTRA_DATA — `make floppy` кладёт на дискету ОБА exe. -EXTRA_DATA := bard1.atl bard2.atl bard.pal rpgprof.exe -include $(CURDIR)/../sdk.mk - -# Профилировочная копия демо (бордер-полосы + маркеры для wpiset) — -# собирается вместе с rpgwalk; инструкция в шапке rpgprof.c. -# ВНИМАНИЕ: --memory small (не tiny!) — rpgprof толще rpgwalk -# (my_update + профиль-обвязка): в tiny его BSS вылезал за W2 -# (0xC05B > 0xBFFF) → мгновенный «Unexpected application termination» -# (кусало 2026-07-14; mkexe это переполнение пока НЕ ловит). -all: rpgprof.exe -floppy: rpgprof.exe - -rpgprof.exe: rpgprof.c $(MKEXE) $(LIB) $(RUNTIME_DEPS) $(PROJ_ROOT)/libbgi/_bgi.h - $(SPRINTER_CC) --gfx 256 --memory small -o $@ rpgprof.c - -clean: clean-prof -clean-prof: - rm -f rpgprof.exe -.PHONY: clean-prof - -# 8 персонажей по 12 кадров = 24 КБ пикселей — НЕ лезут в одну -# EMM-страницу (лимит 16К-0x100): два атласа по 4 персонажа. -sprites.stamp: conv_sprites.py $(PROJ_ROOT)/toolchain/mkatlas.py - python3 conv_sprites.py - python3 $(PROJ_ROOT)/toolchain/mkatlas.py bard1.atl \ - char0.raw:16x16:1x12 char1.raw:16x16:1x12 \ - char2.raw:16x16:1x12 char3.raw:16x16:1x12 - python3 $(PROJ_ROOT)/toolchain/mkatlas.py bard2.atl \ - char4.raw:16x16:1x12 char5.raw:16x16:1x12 \ - char6.raw:16x16:1x12 char7.raw:16x16:1x12 - touch $@ - -$(EXAMPLE).exe: sprites.stamp diff --git a/examples/rpgwalk/conv_sprites.py b/examples/rpgwalk/conv_sprites.py deleted file mode 100644 index 97778a3..0000000 --- a/examples/rpgwalk/conv_sprites.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 -"""Конвертер third_party/16x16-RPG-characters (02-bard.png) → ленты -атласа + палитра для examples/rpgwalk. - -Источник: RGBA 192×128 = 8 рядов × 12 картинок 16×16; персонаж = -блок 3×4 (4 блока в ряду, 2 ряда блоков = 8 персонажей): - строка блока 0 — идёт ВНИЗ (на нас), кадры 0/1/2 (маятник); - строка 1 — ВЛЕВО; строка 2 — ВПРАВО; строка 3 — ВВЕРХ. - -Выход: - char0.raw..char7.raw — вертикальные ленты 16×192 (12 кадров: - dir*3 + frame — sprite_anim(dir*3, dir*3+2, PINGPONG)); - bard.pal — палитра gfx_pal_fload: слоты 0..15 = EGA (как ставит - initgraph, таблица из _bgi_mode_set), слоты 16.. = цвета PNG. -Пиксели: прозрачное (alpha<128) → 0xFF, цвет → 16 + индекс. -""" -from PIL import Image - -SRC = ("../third_party/16x16-RPG-characters/sprites/" - "old-style/02-bard.png") -BASE = 16 # цвета PNG кладём после EGA-шестнадцати - -# B, G, R, pad — как в libbgi/bgi256/_bgi_mode_set.c -EGA = [ - (0, 0, 0), (168, 0, 0), (0, 168, 0), (168, 168, 0), - (0, 0, 168), (168, 0, 168), (0, 84, 168), (168, 168, 168), - (84, 84, 84), (255, 84, 84), (84, 255, 84), (255, 255, 84), - (84, 84, 255), (255, 84, 255), (84, 255, 255), (255, 255, 255), -] - -im = Image.open(SRC).convert("RGBA") -assert im.size == (192, 128), im.size - -colors = {} # (r,g,b) -> палитровый индекс - - -def px(x, y): - r, g, b, a = im.getpixel((x, y)) - if a < 128: - return 0xFF - key = (r, g, b) - if key not in colors: - assert BASE + len(colors) < 0xFF, "палитра переполнена" - colors[key] = BASE + len(colors) - return colors[key] - - -for c in range(8): - bx, by = (c % 4) * 48, (c // 4) * 64 - data = bytearray() - for d in range(4): # вниз/влево/вправо/вверх - for f in range(3): # кадры маятника - for y in range(16): - for x in range(16): - data.append(px(bx + f * 16 + x, by + d * 16 + y)) - open(f"char{c}.raw", "wb").write(data) - -pal = bytearray() -for b, g, r in EGA: - pal += bytes((b, g, r, 0)) -for (r, g, b) in colors: # dict сохраняет порядок вставки - pal += bytes((b, g, r, 0)) -open("bard.pal", "wb").write(pal) - -print(f"conv_sprites: 8 лент 16x192, палитра EGA+{len(colors)} цветов " - f"({len(pal)} Б)") diff --git a/examples/rpgwalk/rpgprof.c b/examples/rpgwalk/rpgprof.c deleted file mode 100644 index ce46e3f..0000000 --- a/examples/rpgwalk/rpgprof.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * rpgprof — профилировочная копия rpgwalk (НЕ собирается по умолчанию). - * Два инструмента в одном: - * - * 1. ВИЗУАЛЬНЫЙ профайлер — цвет ГРАНИЦЫ экрана меняется на входе в - * каждую секцию кадра; луч бежит сверху вниз (~320 строк растра за - * кадр 48.83 Гц, 1344 такта на строку), так что толщина цветной - * полосы бордера ∝ времени секции. - * ВНИМАНИЕ: бордер принимает только биты 1-2 порта #FE (бит 0 = - * красный ИГНОРИРУЕТСЯ — проверено 2026-07-14) → доступны РОВНО - * четыре цвета: 0 чёрный, 2 синий, 4 зелёный, 6 голубой: - * синий (2) — логика (короткая) и blit-проход (банк 0x5C) - * зелёный (4) — тик авто-анимаций (_spr_tick_fn) - * голубой (6) — heal-проход (банк 0x50) - * чёрный (0) — gfx_wait_vsync (простой до конца кадра) - * Много чёрного = запас; синий+зелёный через весь бордер = не - * влезаем. dev-MAME перерисовывает бордер крупными кусками (не - * per-scanline) — полосы приблизительные, для точных цифр см. п.2. - * - * 2. ТАКТОВЫЙ профайлер — те же OUT'ы ловятся watchpoint'ом дебаггера - * dev-MAME (рецепт: memory/mame_mcp_bridge и docs/sprite-api-design.md - * §9д): wpiset по маске порта + printf totalcycles/wpdata + авто-g, - * затем оп clog моста. Для уникальности wpdata при одинаковом цвете - * можно кодировать секцию в битах 3+ (бордер их не видит): - * напр. логика 0x12, blit 0x02 — оба синие, но различимы в логе. - * - * Сборка и запуск (из этого каталога; атласы уже собраны make'ом): - * ../../bin/sprinter-cc --memory tiny --gfx 256 -o rpgprof.exe rpgprof.c - * cd ../../mame/v306 && python3 make_disk.py IMG/mc.img \ - * ../../examples/rpgwalk/rpgprof.exe \ - * ../../examples/rpgwalk/bard1.atl ../../examples/rpgwalk/bard2.atl \ - * ../../examples/rpgwalk/bard.pal - * ./run_mame.sh (или ./run_bridge.sh для тактового профиля) - * в CLI Sprinter: a:\rpgprof.exe - * - * my_update — копия libbgi sprite_update с воткнутыми маркерами; при - * изменении оригинала синхронизировать вручную. - */ -#include -#include -#include -#include -#include -#include -#include -#include "../../libbgi/_bgi.h" - -__sfr __at (0xFE) io_border; - -/* Копия sprite_update с суб-маркерами (см. шапку): 6 тик анимаций, - * 3 heal-проход, 7 blit-проход. */ -static void my_update(sprite_t *arr, uint8_t count) -{ - uint8_t p = gfx_get_draw_page(); - uint8_t dbit = p ? _SPR_DIRTY1 : _SPR_DIRTY0; - uint8_t saved = gfx_get_bank(); - uint8_t i; - const uint8_t *rec; - sprite_t *s; - - io_border = 4; /* зеленый: тик анимаций */ - if (_spr_tick_fn) - (*_spr_tick_fn)(arr, count); - - io_border = 6; /* голубой: heal */ - gfx_set_bank(GFX_BANK_NORMAL); - _bgi_begin(); - for (i = 0, s = arr; i < count; i++, s++) { - if (s->drawn[p].on && (s->flags & (dbit | SPR_ALWAYS))) { - (*_gfx_heal_fn)(s->drawn[p].x, s->drawn[p].y, s->w, s->h); - s->drawn[p].on = 0; - } - } - _bgi_end(); - - /* Y-сортировка (как в sprite_update; входит в «синюю» blit-секцию - * профиля — её цену видно по маркерам). */ - io_border = 2; /* синий: blit (+ сортировка) */ - rec = 0; - if (_spr_ysort_fn && count <= _GFX_YSORT_MAX) - rec = (*_spr_ysort_fn)(arr, count) + 2; - gfx_set_bank(GFX_BANK_SPRITE); - _bgi_begin(); - { - uint8_t mapped = 0; - - for (i = 0; i < count; i++) { - if (rec) { - s = *(sprite_t * const *)rec; /* Y-порядок */ - rec += 4; - } else - s = arr + i; - if (s->flags & (dbit | SPR_ALWAYS)) { - if (s->flags & SPR_VISIBLE) { - if (s->page != mapped && s->page) { - _gfx_w0_cur = s->page; - _io_page_w0 = s->page; - mapped = s->page; - } - (*_gfx_blit_fn)(s->x, s->y, s->src, s->stride, s->w, s->h); - s->drawn[p].x = s->x; - s->drawn[p].y = s->y; - s->drawn[p].on = 1; - } - s->flags &= ~dbit; - } - } - if (mapped) - _io_page_w0 = _gfx_w0_dss; - } - _bgi_end(); - - gfx_set_bank(saved); -} - -#define NCHR 15 -#define MINX 0 -#define MAXX (320 - 16) -#define MINY 14 -#define MAXY (256 - 16) -#define QX 80 -#define QY 64 - -static atlas_t at1, at2; -static sprite_t sp[NCHR]; -static uint8_t dir[NCHR]; -static uint8_t phase[NCHR]; - -static void set_dir(uint8_t i, uint8_t d) -{ - dir[i] = d; -// sprite_anim(&sp[i], (uint8_t)(d * 3), (uint8_t)(d * 3 + 2), -// (uint8_t)(5 + (i & 3)), ANIM_PINGPONG); - sprite_anim(&sp[i], (uint8_t)(d * 3), (uint8_t)(d * 3 + 2), - (uint8_t)5, ANIM_PINGPONG); -} - -static void go_edge(uint8_t i) -{ - sprite_t *s = &sp[i]; - int tx = s->x, ty = s->y; - - switch (dir[i]) { - case 0: ty = MAXY; break; - case 1: tx = MINX; break; - case 2: tx = MAXX; break; - default: ty = MINY; break; - } - sprite_moveto(s, tx, ty, 1, (uint8_t)(1 + (i & 1))); - phase[i] = 0; -} - -static void go_back_random(uint8_t i) -{ - sprite_t *s = &sp[i]; - int tx = s->x, ty = s->y, avail, dist; - uint8_t d = dir[i]; - - d = (uint8_t)(3 - d); /* 0↔3, 1↔2 — разворот */ - set_dir(i, d); - switch (d) { - case 0: avail = MAXY - s->y; dist = QY; break; - case 1: avail = s->x - MINX; dist = QX; break; - case 2: avail = MAXX - s->x; dist = QX; break; - default: avail = s->y - MINY; dist = QY; break; - } - if (avail > dist) - dist += rand() % (avail - dist + 1); - else - dist = avail; - switch (d) { - case 0: ty += dist; break; - case 1: tx -= dist; break; - case 2: tx += dist; break; - default: ty -= dist; break; - } - sprite_moveto(s, tx, ty, 1, (uint8_t)(1 + (i & 1))); - phase[i] = 1; -} - -static void turn_90(uint8_t i) -{ - uint8_t d = dir[i]; - uint8_t vert = (d == 0 || d == 3); - - set_dir(i, vert ? (uint8_t)(1 + (rand() & 1)) - : (uint8_t)((rand() & 1) ? 0 : 3)); - go_edge(i); -} - -// static void show_fps(uint8_t v) -// { -// char buf[4]; - -// buf[0] = (char)('0' + (v / 100) % 10); -// buf[1] = (char)('0' + (v / 10) % 10); -// buf[2] = (char)('0' + v % 10); -// buf[3] = 0; -// setcolor(WHITE); -// setfillstyle(SOLID_FILL, BLACK); -// bar(0, 0, 27, 9); -// outtextxy(2, 1, buf); -// } - -static void show_fps(uint8_t v) -{ - uint8_t color = BLACK; - setfillstyle(SOLID_FILL, color); - bar(0, 2, 60, 5); - if(v < 30) { - color = RED; - } else if(v < 40) { - color = BLUE; - } else if(v < 47) { - color = MAGENTA; - } else if(v < 52) { - color = CYAN; - } else { - color = YELLOW; - } - setfillstyle(SOLID_FILL, color); - bar(0, 2, v, 5); -} - -static void draw_field(void) -{ - int i; - - srand(4321); - setfillstyle(SOLID_FILL, GREEN); - bar(0, 0, 319, 255); - for (i = 0; i < 400; i++) - putpixel(rand() % 320, rand() % 244 + 12, - (i & 3) ? LIGHTGREEN : BROWN); - for (i = 0; i < 24; i++) { - int x = rand() % 312 + 4, y = rand() % 232 + 16; - putpixel(x, y, (i & 1) ? WHITE : YELLOW); - putpixel(x - 1, y, LIGHTGREEN); - putpixel(x + 1, y, LIGHTGREEN); - } - setfillstyle(SOLID_FILL, BLACK); - setcolor(BLACK); - line(0, 0, 0, 7); - line(10, 0, 10, 7); - line(20, 0, 20, 7); - line(30, 0, 30, 7); - line(40, 0, 40, 7); - line(50, 0, 50, 7); - line(60, 0, 60, 7); - - setcolor(WHITE); - outtextxy(52, 3, "RPGPROF: border = frame profile, ESC"); -} - -int main(void) -{ - uint8_t i, page, hidden; - uint8_t frames = 0, last_sec, fps_val = 0, fps_draw = 0; - datetime_t dt; - - if (atlas_load(&at1, "bard1.atl") != 0 && - atlas_load(&at1, "a:\\bard1.atl") != 0) - return 1; - if (atlas_load(&at2, "bard2.atl") != 0 && - atlas_load(&at2, "a:\\bard2.atl") != 0) - return 1; - - initgraph(); - gfx_sprite_clip(0); - if (gfx_pal_fload(0, "bard.pal") < 0) - gfx_pal_fload(0, "a:\\bard.pal"); - gfx_pal_sync(); - gfx_sprite_ysort(YSORT_Y); - - getdatetime(&dt); - srand((unsigned)(dt.second * 77u + dt.minute)); - - for (i = 0; i < NCHR; i++) { - /* Персонажи циклом из двух атласов: 0-3 at1, 4-7 at2, 8-11 at1… - * (группы по 4 подряд — минимум смен W0-страницы за проход). */ - atlas_sprite_init(&sp[i], (i & 4) ? &at2 : &at1, (uint8_t)(i & 3)); - sp[i].x = 24 + (rand() % (MAXX - 48)); - sp[i].y = MINY + 8 + (rand() % (MAXY - MINY - 16)); - set_dir(i, (uint8_t)(rand() & 3)); - go_edge(i); - sprite_show(&sp[i]); - } - - for (page = 0; page < 2; page++) { - gfx_set_draw_page(page); - draw_field(); - sprite_update(sp, NCHR); - } - gfx_set_visible_page(0); - getdatetime(&dt); - srand((unsigned)(dt.second * 77u + dt.minute)); - last_sec = dt.second; - - for (;;) { - io_border = 2; /* синий: логика */ - if (kbhit() && getch() == 27) - break; - - for (i = 0; i < NCHR; i++) - if (!sprite_moving(&sp[i])) { - if (phase[i] == 0) - go_back_random(i); - else - turn_90(i); - } - - hidden = gfx_get_visible_page() ^ 1; - gfx_set_draw_page(hidden); - frames++; - getdatetime(&dt); - if (dt.second != last_sec) { - last_sec = dt.second; - fps_val = frames; - frames = 0; - fps_draw = 2; - } - if (fps_draw) { - fps_draw--; - show_fps(fps_val); - } - my_update(sp, NCHR); /* маркеры 2/4/6 внутри */ - io_border = 0; /* черный: ждём vsync */ - gfx_wait_vsync(); - gfx_set_visible_page(hidden); - } - - io_border = 0; - closegraph(); - atlas_free(&at2); - atlas_free(&at1); - return 0; -} diff --git a/examples/rpgwalk/rpgwalk.c b/examples/rpgwalk/rpgwalk.c deleted file mode 100644 index bd2687f..0000000 --- a/examples/rpgwalk/rpgwalk.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * rpgwalk — демо: 8 уникальных RPG-персонажей ходят по травяному полю. - * - * Спрайты — third_party/16x16-RPG-characters (bard, CC), два W0-атласа - * по 4 персонажа (12 кадров каждому не лезут в одну страницу на - * восьмерых); палитра — из PNG через gfx_pal_fload (слоты 0..15 EGA, - * 16+ цвета художника) + gfx_pal_sync для дабл-буфера. - * - * Лента персонажа: 12 кадров вертикально, dir*3 + кадр: - * dir 0 = вниз (на нас), 1 = влево, 2 = вправо, 3 = вверх; - * шаг ноги — sprite_anim(dir*3, dir*3+2, ANIM_PINGPONG) (маятник 0/1/2). - * - * Правила хождения (машина из двух фаз на sprite_moveto): - * фаза 0: идём ДО КРАЯ экрана; у края — разворот на 180° и - * фаза 1: идём до случайной точки (но не меньше ЧЕТВЕРТИ экрана); - * там — поворот на ±90° (случайно) и снова фаза 0. - * - * ESC — выход. - */ -#include -#include -#include -#include -#include -#include - -#define NCHR 15 -#define MINX 0 -#define MAXX (320 - 16) -#define MINY 14 /* под заголовком */ -#define MAXY (256 - 16) -#define QX 80 /* четверть экрана */ -#define QY 64 - -static atlas_t at1, at2; -static sprite_t sp[NCHR]; -static uint8_t dir[NCHR]; /* 0 вниз / 1 влево / 2 вправо / 3 вверх */ -static uint8_t phase[NCHR]; /* 0 = до края, 1 = до случайной точки */ - -/* Направление → шаг ноги той же скоростью (у всех чуть разной). */ -static void set_dir(uint8_t i, uint8_t d) -{ - dir[i] = d; -// sprite_anim(&sp[i], (uint8_t)(d * 3), (uint8_t)(d * 3 + 2), -// (uint8_t)(5 + (i & 3)), ANIM_PINGPONG); - sprite_anim(&sp[i], (uint8_t)(d * 3), (uint8_t)(d * 3 + 2), - (uint8_t)20, ANIM_PINGPONG); -} - -/* Идти до края экрана вдоль dir[i] (скорость: 1px, интервал 1-2). */ -static void go_edge(uint8_t i) -{ - sprite_t *s = &sp[i]; - int tx = s->x, ty = s->y; - - switch (dir[i]) { - case 0: ty = MAXY; break; - case 1: tx = MINX; break; - case 2: tx = MAXX; break; - default: ty = MINY; break; - } - sprite_moveto(s, tx, ty, 1, (uint8_t)(1 + (i & 1))); - phase[i] = 0; -} - -/* Развернуться на 180° и идти до случайной точки (≥ четверти экрана). */ -static void go_back_random(uint8_t i) -{ - sprite_t *s = &sp[i]; - int tx = s->x, ty = s->y, avail, dist; - uint8_t d = dir[i]; - - // d = (uint8_t)(d ^ ((d < 2) ? 3 : 1)); /* 0↔3, 1↔2 — разворот */ - d = 3 - d; /* 0↔3, 1↔2 — разворот */ - set_dir(i, d); - switch (d) { - case 0: avail = MAXY - s->y; dist = QY; break; - case 1: avail = s->x - MINX; dist = QX; break; - case 2: avail = MAXX - s->x; dist = QX; break; - default: avail = s->y - MINY; dist = QY; break; - } - if (avail > dist) - dist += rand() % (avail - dist + 1); - else - dist = avail; /* некуда — до края */ - switch (d) { - case 0: ty += dist; break; - case 1: tx -= dist; break; - case 2: tx += dist; break; - default: ty -= dist; break; - } - sprite_moveto(s, tx, ty, 1, (uint8_t)(1 + (i & 1))); - phase[i] = 1; -} - -/* Повернуть на ±90° (случайно) и идти до края. */ -static void turn_90(uint8_t i) -{ - uint8_t d = dir[i]; - uint8_t vert = (d == 0 || d == 3); - - set_dir(i, vert ? (uint8_t)(1 + (rand() & 1)) /* → влево/вправо */ - : (uint8_t)((rand() & 1) ? 0 : 3)); /* → вниз/вверх */ - go_edge(i); -} - -/* FPS-плашка слева-сверху: значение раз в секунду, рисуется на ОБЕИХ - * страницах (fps_draw = 2 кадра), банк 0x50 — heal её учитывает. */ -static void show_fps(uint8_t v) -{ - char buf[4]; - - buf[0] = (char)('0' + (v / 100) % 10); - buf[1] = (char)('0' + (v / 10) % 10); - buf[2] = (char)('0' + v % 10); - buf[3] = 0; - setcolor(WHITE); - setfillstyle(SOLID_FILL, BLACK); - bar(0, 0, 27, 9); - outtextxy(2, 1, buf); -} - -static void draw_field(void) -{ - int i; - - srand(4321); /* фон идентичен на обеих страницах */ - setfillstyle(SOLID_FILL, GREEN); - bar(0, 0, 319, 255); - for (i = 0; i < 400; i++) /* травинки */ - putpixel(rand() % 320, rand() % 244 + 12, - (i & 3) ? LIGHTGREEN : BROWN); - for (i = 0; i < 24; i++) { /* цветочки */ - int x = rand() % 312 + 4, y = rand() % 232 + 16; - putpixel(x, y, (i & 1) ? WHITE : YELLOW); - putpixel(x - 1, y, LIGHTGREEN); - putpixel(x + 1, y, LIGHTGREEN); - } - setcolor(WHITE); - outtextxy(52, 3, "RPGWALK: 8 bards on patrol, ESC"); -} - -int main(void) -{ - uint8_t i, page, hidden; - uint8_t frames = 0, last_sec, fps_val = 0, fps_draw = 0; - datetime_t dt; - - if (atlas_load(&at1, "bard1.atl") != 0 && - atlas_load(&at1, "a:\\bard1.atl") != 0) - return 1; - if (atlas_load(&at2, "bard2.atl") != 0 && - atlas_load(&at2, "a:\\bard2.atl") != 0) - return 1; - - initgraph(); - gfx_sprite_clip(0); /* маршруты в пределах */ - if (gfx_pal_fload(0, "bard.pal") < 0) - gfx_pal_fload(0, "a:\\bard.pal"); /* EGA + цвета PNG */ - gfx_pal_sync(); - - getdatetime(&dt); - srand((unsigned)(dt.second * 77u + dt.minute)); - - for (i = 0; i < NCHR; i++) { - /* Персонажи 0-3 из первого атласа, 4-7 из второго: спрайты - * лежат подряд по атласам — один OUT смены страницы на кадр. */ - atlas_sprite_init(&sp[i], (i < 4) ? &at1 : &at2, (uint8_t)(i & 3)); - sp[i].x = 24 + (rand() % (MAXX - 48)); - sp[i].y = MINY + 8 + (rand() % (MAXY - MINY - 16)); - set_dir(i, (uint8_t)(rand() & 3)); - go_edge(i); - sprite_show(&sp[i]); - } - - for (page = 0; page < 2; page++) { /* фон на обе страницы */ - gfx_set_draw_page(page); - draw_field(); - sprite_update(sp, NCHR); - } - gfx_set_visible_page(0); - getdatetime(&dt); - srand((unsigned)(dt.second * 77u + dt.minute)); - last_sec = dt.second; - - for (;;) { - if (kbhit()) { - int c = getch(); - if (c == 27) - break; - /* 1/2/3 — FPS-делитель: логический кадр = n vsync'ов. - * Скорость персонажей (пикселей на итерацию) не меняется, но - * итераций/сек делится на n — визуальная проверка стабильности - * тайминга (метр стоит ровно на 50/25/16). */ - if (c >= '1' && c <= '9') - gfx_set_fps_div((uint8_t)(c - '0')); - } - - for (i = 0; i < NCHR; i++) - if (!sprite_moving(&sp[i])) { - if (phase[i] == 0) - go_back_random(i); /* у края: разворот */ - else - turn_90(i); /* дошли: поворот ±90° */ - } - - hidden = gfx_get_visible_page() ^ 1; - gfx_set_draw_page(hidden); - frames++; - getdatetime(&dt); - if (dt.second != last_sec) { - last_sec = dt.second; - fps_val = frames; - frames = 0; - fps_draw = 2; - } - if (fps_draw) { - fps_draw--; - show_fps(fps_val); - } - sprite_update(sp, NCHR); - gfx_wait_vsync(); - gfx_set_visible_page(hidden); - } - - closegraph(); - atlas_free(&at2); - atlas_free(&at1); - return 0; -} diff --git a/examples/scroll/Makefile b/examples/scroll/Makefile deleted file mode 100644 index 13c1bda..0000000 --- a/examples/scroll/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -EXAMPLE := scroll -EXTRA_FLAGS ?= --gfx 256 -include $(CURDIR)/../sdk.mk diff --git a/examples/scroll/run.sh b/examples/scroll/run.sh deleted file mode 100755 index e9e8c78..0000000 --- a/examples/scroll/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -make clean -make run diff --git a/examples/scroll/scroll-impl-guide.md b/examples/scroll/scroll-impl-guide.md deleted file mode 100644 index 52d9380..0000000 --- a/examples/scroll/scroll-impl-guide.md +++ /dev/null @@ -1,226 +0,0 @@ -# Руководство: скроллинг-примитивы для libbgi - -Самодостаточное ТЗ на реализацию двух функций скролла в libbgi. Написано так, -чтобы реализовать с нуля без остального контекста. Итог изысканий по железу -Sprinter (см. §1) и разбора существующего кода libbgi (см. §3). - -**Что делаем:** `gfx_scroll_h` (горизонтальный — нужен Loom для комнат шире -320 px) и `gfx_scroll_v` (вертикальный — для симметрии API; Loom пока не -требует, высота комнат 144 < 256). - -**Где живёт:** расширение `C-Compiler/libbgi` — accel-leaf в `bgi256/`, -обёртка в `common/`, объявление в `include/gfx.h`. - -**СНАЧАЛА прочти §7 (блокеры).** Два пункта надо проверить в dev-MAME до -написания рабочего кода — если они не пройдут, схема «чистого фона» едет. - ---- - -## 1. Аппаратная модель (всё, что нужно знать) - -**Режим `0x81`: 320×256×256, chunky — 1 байт/пиксель.** Пиксель (x,y): CPU-адрес -`0xC000 + x`, строку выбирает **Port_Y** (порт `0x89`) `= y`. Горизонталь — в -адресе, вертикаль — в Port_Y. (Ссылка: `libbgi/include/gfx.h` шапка.) - -**Две видеостраницы различаются ТОЛЬКО базой строки:** page0 → `0xC000`, -page1 → `0xC140` (шаг `0x140` = 320). Обе адресуемы в W3 **одновременно**; -Port_Y и номера банков — общие для обеих. Значит: -> `dst_addr = src_addr + 0x140` копирует page0→page1 без ремаппинга W3. -> Со сдвигом: `dst = src ± 0x140 + dx`. - -**Банки (значение W3-страницы, порт `0xE2` = `0x50..0x5F`)** — режим ЗАПИСИ, -действует на все примитивы до смены (`libbgi/include/gfx.h`): -| Банк | Имя | Запись | Чтение | -|---|---|---|---| -| `0x50` | `GFX_BANK_NORMAL` | VRAM **+ ОЗУ-копия** | ОЗУ-копия | -| `0x54` | `GFX_BANK_NOSHADOW` | только VRAM | ОЗУ-копия | -| `0x58` | `GFX_BANK_TRANSPARENT` | VRAM, байт `0xFF` не пишется | ОЗУ-копия | -| `0x5C` | `GFX_BANK_SPRITE` | VRAM, `0xFF` не пишется | ОЗУ-копия | - -Чтение `#50..#5F` **всегда** отдаёт ОЗУ-копию (видео-ОЗУ write-only). `0xFF` — -аппаратно-прозрачный цвет (`GFX_TRANSPARENT`). - -**Акселератор** (`C-Compiler/docs/converted/accel_r.txt`): внутр. ОЗУ-буфер -1..256 байт. Управление опкодами-«NOP»: -- `LD B,B` — стоп; `LD D,D` затем `LD A,n` — размер блока (`n=0` → 256); -- `LD L,L` — **горизонтальная** копия (буфер ← `LD A,(HL)`; буфер → `LD (DE),A`); -- `LD A,A` (`0x7F`) — **вертикальная** копия (линии экрана, Port_Y шагает внутри - burst'а); -- `LD C,C` / `LD E,E` — fill (гориз./верт.). - -Скорость ≈ **байт / 7 мкс**. Полный экран 320×256 ≈ **26 мс** (это **>1 кадра** -@ 50 Гц — держать в уме, §9). На время работы акселератора — **DI** (меняется -система команд, ISR сломается). - ---- - -## 2. Ключевой инсайт: чистый фон достаётся бесплатно - -- Спрайты рисуются банком с битом 2 (`0x54`/`0x5C`) → **только VRAM**, - ОЗУ-копию не трогают. -- ⇒ ОЗУ-копия (`0x50`) любой страницы — это фон **без спрайтов**, всегда. -- ⇒ скролл-копия из `0x50` в `0x50` читает чистый фон и пишет VRAM+тень — это - **одновременно скролл И heal** целевой страницы (её старые спрайты затираются - сдвинутым фоном). - -Ровно так работает `gfx_heal` (частный случай `src==dst`, §3). Скролл — тот же -механизм, но `src != dst`. - ---- - -## 3. Что уже есть в libbgi — образцы для копирования - -| Файл | Что | Зачем образец | -|---|---|---| -| `bgi256/_bgi_copy_rows_raw.c` | гориз. leaf (`LD L,L`), src/dst + страйды, Port_Y на строку | ядро всех блитов; страйды, SMC, DI | -| `bgi256/_bgi_heal_rows_raw.c` | `src==dst==экран`, страйды 0 | спец-случай heal | -| `bgi256/_bgi_blit_cols_raw.c` | **верт. leaf** (`LD A,A`), колонка=burst, Port_Y сброс на колонку, `dst+=1` | **прямой образец для `scroll_h`** | -| `common/_gfx_heal_full.c` | клип по экрану + нарезка полос >256 + вызов leaf | образец обёртки-ядра | -| `common/gfx_heal.c` | save-банк → `0x50` → `_bgi_begin` → ядро → `_bgi_end` → restore | образец верхней обёртки | -| `docs/converted/accel_r.txt` | screen→screen верт. копия LDIR-идиомом, `dst=src+0x140` | канонический скролл-паттерн | - -Правило из `_bgi_copy_rows_raw`: **одна сторона копии — видео (в W3, строку даёт -Port_Y), другая может быть линейным буфером ВНЕ W3** (`< 0xC000`). Для скролла -обе стороны — видео (см. §4). - ---- - -## 4. `gfx_scroll_h` — горизонтальный (наш, приоритет) - -**Идиом: вертикальный режим `LD A,A`, проход по колонкам.** Высота playfield -(144) ≤ 256 → одна колонка = один burst, **резать 320-строку на 256+64 НЕ надо** -(главная причина брать верт. режим). Это доковый screen→screen паттерн -(`accel_r.txt`) + `_bgi_blit_cols_raw`. - -Параметры на колонку: -- `src = base(src_page) + x` — банк `0x50`, чтение = ОЗУ-копия = чистый фон; -- `dst = base(dst_page) + x + dx` — банк `0x50`, запись = VRAM + тень dst; -- `base`: page0 `0xC000`, page1 `0xC140`; -- размер блока = высота playfield (`h`, для Loom 144); -- Port_Y start = верх playfield (`y0`, для Loom 0); -- копируем `(w − |dx|)` колонок; вакантные `|dx|` колонок **не трогаем**. - -**Знак:** зафиксировать `dx > 0` = сдвиг вправо (открывается слева), `dx < 0` = -влево; вернуть открывшийся край в `*dirty`. - -**Порядок (по образцу докового LDIR-примера + `_bgi_blit_cols_raw`):** банда -колонок под DI → `LD D,D`/`LD A,h` (block) → `LD A,A` (верт. режим) → на колонку -`{ read col в accel; write col из accel; src++; dst++ }` → `EI`. При straight-copy -Port_Y авто-шагает (доковый LDIR его не трогает) — но `_bgi_blit_cols_raw` -сбрасывает Port_Y=`y0` на каждую колонку; **проверить, нужен ли сброс** (§7.1 -покажет). - -**Возврат:** `*dirty` = прямоугольник `|dx|` колонок с открывшегося края; -полосу не чистим — её дозаполняет вызывающий (в Loom — декодер EGA-страйпов). - -```c -/* Горизонтальный скролл playfield. Копирует чистый фон (ОЗУ-копия src_page) - со сдвигом dx в dst_page (обе стороны банк 0x50: запись бьёт VRAM+тень). - dx>0 — вправо. Вакантную полосу НЕ заполняет — возвращает в *dirty. */ -void gfx_scroll_h(uint8_t src_page, uint8_t dst_page, - const gfx_rect_t *area, int dx, gfx_rect_t *dirty); -``` - ---- - -## 5. `gfx_scroll_v` — вертикальный (симметрия; Loom не нужен) - -**Асимметрия железа:** горизонтальный сдвиг — это смещение CPU-адреса (src и dst -независимы) → легко. **Вертикальный сдвиг — это смещение Port_Y, а Port_Y один -на burst** → в простом LDIR-идиоме read и write идут с одного Port_Y, сдвига по -Y нет. Поэтому `scroll_v` сложнее `scroll_h`. Два пути: - -- **(A) менять Port_Y между fill и flush одного burst'а**: `OUT Port_Y=y_src`; - read col в accel; `OUT Port_Y=y_src+dy`; write col из accel. Требует, чтобы - accel-буфер пережил `OUT` между чтением и записью — **не доказано** доком/ - тестами (§7.3). Если пройдёт — симметрично `scroll_h`, верт. режим. -- **(B) безопасный fallback**: grab (video→RAM-буфер вне W3, гориз. режим), - затем blit (буфер→video со сдвигом Y — буферная сторона CPU-адресуема, сдвиг - любой). Два прохода + буфер, но заведомо работает. - -**Рекомендация:** реализовать (A), если §7.3 пройдёт; иначе (B). Делать -**последним** — для Loom не критично. API симметричный (`dy` вместо `dx`): - -```c -void gfx_scroll_v(uint8_t src_page, uint8_t dst_page, - const gfx_rect_t *area, int dy, gfx_rect_t *dirty); -``` - ---- - -## 6. Общие требования реализации - -- **DI по банде, НЕ один DI на весь проход.** Полный playfield ~14 мс под DI - порвёт будущий CBL-звук. Одна колонка (144 Б ≈ 20 мкс) — безопасное DI-окно; - резать проход на банды колонок/строк с `EI` между (как `_bgi_copy_rows_raw` - режет ≤16 строк). Пока звука нет — не критично, но заложить сразу. -- **Клип по `area`** (образец — `_gfx_heal_full`). -- **Буфер (если путь (B))** — вне W3 (`< 0xC000`). -- **Верхняя обёртка**: save банк → `gfx_set_bank(GFX_BANK_NORMAL)` → - `_bgi_begin()` → ядро → `_bgi_end()` → restore (образец — `gfx_heal.c`). -- **Размещение**: leaf `bgi256/_bgi_scroll_cols_raw.c` (+ `_rows_raw` для (A)), - обёртки `common/gfx_scroll_h.c` / `gfx_scroll_v.c`, объявления в - `include/gfx.h`. `gfx_rect_t` — если ещё нет, определить там же - (`int x,y,w,h`). -- **Для Loom (не требование к примитиву, но контекст)**: вызывающий держит - `dx` кратным 8 (ширина EGA-страйпа) → `dirty` = целые страйпы. Обёртка этому - не мешает, но и не навязывает. - ---- - -## 7. ПРОВЕРИТЬ ПЕРВЫМ — блокеры (dev-MAME) - -1. **Верт. режим (`LD A,A`) в банке `0x50` читает ОЗУ-копию (чистый фон) и - пишет VRAM+тень.** Гориз. heal это доказывает построчно; для верт. прохода — - прогон: нарисовать фон, поверх спрайт банком `0x5C`, сделать `scroll_h` на - dx>0, убедиться что спрайт **не размазался** (копировался фон, не VRAM со - спрайтом). Заодно выяснить, нужен ли сброс Port_Y на колонку (§4). -2. **ОЗУ-копия per-page или общая.** Ранний план дабл-буфера PoP исходил из - «теневая копия одна — общая»; **практикой это опровергнуто**: у каждой - страницы СВОЯ теневая ОЗУ-копия, heal берёт фон из копии той страницы, в - которую рисуем (`applications/PoP/roomtest/CLAUDE.md`, раздел - «Дабл-буфер»; `roomtest.c enter_room` рисует фон в обе страницы по - отдельности именно поэтому). Значит пинг-понг (§8) в принципе рабочий, - но перед опорой на него — подтвердить на своём коде: записать разный фон - в page0 и page1 банком `0x50`, сверить чтение обеих. -3. **(только для `scroll_v`, путь A)** accel-буфер переживает `OUT Port_Y` между - fill и flush одного burst'а. Доковый straight-copy этого не проверяет. - ---- - -## 8. Порядок кадра в Loom (контекст использования `scroll_h`) - -Пинг-понг двух страниц; спрайты в VRAM-only банк, поэтому тень каждой страницы -остаётся чистым фоном: - -1. `gfx_set_draw_page(B)` — B = скрытая; -2. `gfx_scroll_h(A, B, area, dx, &dirty)` — B получает сдвинутый чистый фон в - VRAM+тень; старые спрайты B затёрты (copy = heal); -3. декодировать EGA-страйпы фона в `dirty`, **банк `0x50`** (иначе тень B - останется с дырой — следующий скролл из B прочитает мусор); -4. вывести актора/спрайты в B, банк `0x5C`; -5. `gfx_wait_vsync()` → `gfx_set_visible_page(B)`. - -Следующий кадр: роли A/B меняются, читаем уже чистую тень B. Смаза нет by design -(§2). При `dx==0` (камера стоит) — обычный per-sprite `gfx_heal`, а не полная -копия. - ---- - -## 9. Бюджет и проверка корректности - -- 320×256 ≈ 26 мс (**>кадр**); playfield 320×144 ≈ **14–15 мс** (<кадр); скролл - копирует `(320−dx)` колонок → чуть меньше. Итого копия ~кадр + докод полосы + - спрайты; целевой бюджет ~2 кадра/шаг (plan.md §6). **Замерить на PoC-1** - маркерами `OUT (0xFE)` (до появления звука порт свободен). -- Корректность — визуально/дифф-тестом в dev-MAME. Фон-декодер уже проверен - диффом против ScummVM; скролл проверять на реальной комнате шире 320. - ---- - -## 10. Связанные документы - -`plan.md §6` (графика, скроллинг), `toolchain-requirements.md §1` (этот -примитив), `decisions.md`. Железо: `C-Compiler/docs/converted/accel_r.txt`, -`libbgi/include/gfx.h`, `C-Compiler/docs/sprite-api-design.md`, -`C-Compiler/docs/memory-management.md`. diff --git a/examples/scroll/scroll.c b/examples/scroll/scroll.c deleted file mode 100644 index 7be012f..0000000 --- a/examples/scroll/scroll.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * scroll — визуальная проверка скролл-примитивов libbgi (gfx_scroll_h / - * gfx_scroll_v). Копирует регион из НЕактивной страницы в активную со - * сдвигом и переключает страницы. - * - * ДИАГНОСТИКА. Страница 0 — диагональные полосы color=((x+y)>>3)&15 - * (любой сдвиг ломает непрерывность диагоналей; фаза по Y ловит - * вертикальный «съезд» Port_Y-квирка). Страница 1 — сплошной серый - * фон-маркер: после скролла на нём проступают ровно скопированные - * прямоугольники, по их границам видно корректность (позицию/высоту). - * - * Два региона в одном кадре: слева ГОРИЗОНТАЛЬНЫЙ скролл (dx>0 — картинка - * вправо, открывается серая полоса слева), справа ВЕРТИКАЛЬНЫЙ (dy>0 — - * вниз, открывается серая полоса сверху). Если верт. Port_Y-квирк не - * вылечен — правый прямоугольник съедет вниз/опустеет сверху. Esc — - * выход. - */ - -#include -#include -#include -#include - -static uint8_t egapal[16 * 4]; - -__sfr __at (0xFE) io_border; - -/* Диагональные полосы 16-цветной палитры (8-пиксельные ступени). */ -static void draw_diagonals(void) -{ - int bx, by; - for (by = 0; by < 256; by += 8) - for (bx = 0; bx < 320; bx += 8) { - setfillstyle(SOLID_FILL, (((bx + by) >> 3) & 15)); - bar(bx, by, bx + 7, by + 7); - } -} - -/* Регион горизонтального скролла (слева) и вертикального (справа). */ -static gfx_rect_t area_h = { 0, 56, 320, 144 }; -// static gfx_rect_t area_h = { 16, 48, 80, 80 }; -// static gfx_rect_t area_v = { 176, 48, 80, 80 }; - -int main(void) -{ - uint8_t hidden; - - initgraph(); - - /* Палитра страницы 1 = палитре страницы 0 (initgraph грузит EGA - * только в палитру 0). */ - gfx_pal_get(0, 0, 16, egapal); - gfx_pal_load(1, 0, 16, egapal); - - /* Страница 0 — диагонали (источник фона). */ - gfx_set_draw_page(0); - draw_diagonals(); - - gfx_set_draw_page(1); - draw_diagonals(); - - gfx_set_visible_page(0); - - /* Рисуем на скрытой странице (1), потом флип. */ - hidden = gfx_get_visible_page() ^ 1; /* = 1 */ - gfx_set_draw_page(hidden); - - - - for (;;) { - if (kbhit() && getch() == 27) - break; - io_border = 2; - gfx_scroll_v(&area_h, 2, 0); /* вправо на 48 px */ - // gfx_scroll_v(&area_v, 2, 0); /* вниз на 48 px */ - io_border = 0; - gfx_wait_vsync(); - gfx_wait_vsync(); - - hidden = gfx_get_visible_page() ^ 1; - gfx_set_draw_page(hidden ^ 1); - gfx_set_visible_page(hidden); - } - - closegraph(); - return 0; -} diff --git a/examples/sdk.mk b/examples/sdk.mk deleted file mode 100644 index 66c24be..0000000 --- a/examples/sdk.mk +++ /dev/null @@ -1,8 +0,0 @@ -# Общая привязка примеров к установленному Sprinter-CC. --include $(CURDIR)/../local.mk -SPRINTER_ROOT ?= $(if $(wildcard $(CURDIR)/../../bin/sprinter-cc),$(abspath $(CURDIR)/../..),) -ifeq ($(strip $(SPRINTER_ROOT)),) -$(error Задайте SPRINTER_ROOT или создайте Examples/local.mk) -endif -PROJ_ROOT := $(SPRINTER_ROOT) -include $(PROJ_ROOT)/app.mk diff --git a/examples/space/.gitignore b/examples/space/.gitignore deleted file mode 100644 index 948892b..0000000 --- a/examples/space/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -space.atl -*.raw diff --git a/examples/space/Makefile b/examples/space/Makefile deleted file mode 100644 index 35b13ef..0000000 --- a/examples/space/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -EXAMPLE := space -EXTRA_FLAGS ?= --gfx 256 --memory tiny -EXTRA_DATA := space.atl -include $(CURDIR)/../sdk.mk - -# Атлас: три ленты 16×16×4 (астероид/взрыв/маяк) → одна EMM-страница. -space.atl: gen_sprites.py $(PROJ_ROOT)/toolchain/mkatlas.py - python3 gen_sprites.py - python3 $(PROJ_ROOT)/toolchain/mkatlas.py $@ \ - asteroid.raw:16x16:1x4 explode.raw:16x16:1x4 beacon.raw:16x16:1x4 - -$(EXAMPLE).exe: space.atl diff --git a/examples/space/gen_sprites.py b/examples/space/gen_sprites.py deleted file mode 100644 index e8670e7..0000000 --- a/examples/space/gen_sprites.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python3 -"""Генератор спрайтов для examples/space (raw-ленты для mkatlas.py). - -asteroid.raw — 16×16×4 (LOOP): серый астероид, кратеры вращаются по -фазам (шаг 22.5°). -explode.raw — 16×16×4 (ONCE): расширяющийся взрыв (жёлтое ядро → -кольца → разлёт искр). -beacon.raw — 16×16×4 (PINGPONG): пульсирующий маяк (крест с -растущим свечением). - -Палитра — дефолтная EGA (индексы 0..15), 0xFF = прозрачно. -""" -import math - -T = 0xFF # прозрачный -W = H = 16 -CX = CY = 7.5 - - -def blank(): - return [[T] * W for _ in range(H)] - - -def disc(px, cx, cy, r, c): - for y in range(H): - for x in range(W): - if (x - cx) ** 2 + (y - cy) ** 2 <= r * r: - px[y][x] = c - - -def ring(px, cx, cy, r, c): - for a in range(0, 360, 4): - x = int(round(cx + r * math.cos(math.radians(a)))) - y = int(round(cy + r * math.sin(math.radians(a)))) - if 0 <= x < W and 0 <= y < H: - px[y][x] = c - - -def dot(px, x, y, c): - if 0 <= x < W and 0 <= y < H: - px[int(y)][int(x)] = c - - -def asteroid(phase): - px = blank() - disc(px, CX, CY, 6.8, 7) # LIGHTGRAY тело - ring(px, CX, CY, 6.8, 8) # DARKGRAY контур - for i in range(3): # кратеры на орбите - a = math.radians(phase * 22.5 + i * 120) - disc(px, CX + 3.6 * math.cos(a), CY + 3.6 * math.sin(a), 1.4, 8) - dot(px, CX - 2, CY - 3, 15) # блик WHITE - return px - - -def explode(phase): - px = blank() - if phase == 0: - disc(px, CX, CY, 2.2, 14) # YELLOW ядро - elif phase == 1: - disc(px, CX, CY, 3.8, 14) - ring(px, CX, CY, 4.6, 12) # LIGHTRED - elif phase == 2: - ring(px, CX, CY, 4.2, 14) - ring(px, CX, CY, 6.0, 12) - ring(px, CX, CY, 7.0, 4) # RED - else: - for a in range(0, 360, 30): # разлёт искр - r = 6.5 - dot(px, CX + r * math.cos(math.radians(a)), - CY + r * math.sin(math.radians(a)), 4) - dot(px, CX + 4 * math.cos(math.radians(a + 15)), - CY + 4 * math.sin(math.radians(a + 15)), 6) # BROWN - return px - - -def beacon(phase): - px = blank() - r = 2 + phase * 1.6 - for d in range(int(r) + 1): # крест-лучи - for xx, yy in ((CX - d, CY), (CX + d, CY), (CX, CY - d), (CX, CY + d)): - dot(px, round(xx), round(yy), 11) # LIGHTCYAN - disc(px, CX, CY, 1.4, 15) # WHITE ядро - if phase >= 2: - ring(px, CX, CY, r + 1, 9) # LIGHTBLUE ореол - return px - - -def save(name, frames): - with open(name, "wb") as f: - for fr in frames: - for row in fr: - f.write(bytes(row)) - print(f"gen_sprites: {name} ({len(frames)} кадров)") - - -save("asteroid.raw", [asteroid(k) for k in range(4)]) -save("explode.raw", [explode(k) for k in range(4)]) -save("beacon.raw", [beacon(k) for k in range(4)]) diff --git a/examples/space/space.c b/examples/space/space.c deleted file mode 100644 index 98c3f82..0000000 --- a/examples/space/space.c +++ /dev/null @@ -1,189 +0,0 @@ -/* - * space — демо всего спрайтового стека libbgi (2026-07-13): - * - * .atl-атлас с диска (mkatlas.py) → EMM-страница, подключаемая в W0 - * (atlas_load: ISR-стаб, ESTEX READ в W3) → движок sprite_t.page - * (sprite_update сам мапит страницу на блит-проход) → авто-анимации: - * - * - 5 астероидов: кадровая ANIM_LOOP (вращение, 4 кадра) + - * sprite_moveto к случайным целям с разными скоростями; - * по прибытии (sprite_moving()==0) — ВЗРЫВ в точке + новая цель; - * - 5 взрывов: ANIM_ONCE (4 кадра); по SPR_ANIM_DONE — sprite_hide; - * - маяк: ANIM_PINGPONG (пульс 0..3..0); - * - дабл-буфер: фон (звёзды + планеты) на обеих страницах, кадр = - * sprite_update на скрытой + vsync + флип; FPS-метр. - * - * Цели держатся в пределах экрана → gfx_sprite_clip(0) (noclip-ядра). - * ESC — выход. - */ -#include -#include -#include -#include -#include -#include - -#define NAST 5 /* астероиды: sp[0..4] */ -#define NEX NAST /* взрывы: sp[5..9] */ -#define IBEAC (NAST + NEX) /* маяк: sp[10] */ -#define NSPR (IBEAC + 1) - -#define MAXX (320 - 16) -#define MAXY (256 - 16) - -static atlas_t at; -static sprite_t sp[NSPR]; - -/* Случайная точка в пределах экрана (модуль — не горячий путь). */ -static int rnd_x(void) { return rand() % (MAXX - 32) + 16; } -static int rnd_y(void) { return rand() % (MAXY - 48) + 32; } - -/* Новая цель астероиду i: скорость по номеру (шаг 1..3, интервал 1..2). */ -static void new_target(uint8_t i) -{ - sprite_moveto(&sp[i], rnd_x(), rnd_y(), - (uint8_t)(1 + i % 3), (uint8_t)(1 + (i & 1))); -} - -static void draw_space(void) -{ - int i; - - /* Фон обязан быть ИДЕНТИЧЕН на обеих страницах (иначе флип 25 Гц - * мерцает разными звёздами) — фиксированный seed на отрисовку. */ - srand(1234); - - cleardevice(); - setfillstyle(SOLID_FILL, BLUE); /* планеты */ - fillellipse(60, 200, 26, 26); - setfillstyle(SOLID_FILL, LIGHTBLUE); - fillellipse(60, 200, 18, 18); - setfillstyle(SOLID_FILL, BROWN); - fillellipse(270, 190, 14, 14); - - for (i = 0; i < 90; i++) /* звёзды */ - putpixel(rand() % 320, rand() % 256, - (i & 3) ? DARKGRAY : WHITE); - - setcolor(WHITE); - outtextxy(36, 4, "SPACE: atlas W0 + auto-anim, ESC"); -} - -#define FPS_METER 1 -#if FPS_METER -static void show_fps(uint8_t v) -{ - char buf[4]; - - buf[0] = (char)('0' + (v / 100) % 10); - buf[1] = (char)('0' + (v / 10) % 10); - buf[2] = (char)('0' + v % 10); - buf[3] = 0; - setcolor(WHITE); - setfillstyle(SOLID_FILL, BLACK); - bar(0, 0, 27, 9); - outtextxy(2, 1, buf); -} -#endif - -int main(void) -{ - uint8_t i, page, hidden; - datetime_t dt; -#if FPS_METER - uint8_t frames = 0, last_sec, fps_val = 0, fps_draw = 0; -#endif - - if (atlas_load(&at, "space.atl") != 0 && - atlas_load(&at, "a:\\space.atl") != 0) - return 1; - - getdatetime(&dt); - srand((unsigned)(dt.second * 77u + dt.minute)); - - initgraph(); - gfx_sprite_clip(0); /* цели в пределах */ - - gfx_pal_sync(); /* обязательный шаг дабл-буфера (см. gfx.h) */ - - /* Спрайты: астероиды (LOOP + цель), взрывы (скрыты), маяк (PINGPONG). */ - for (i = 0; i < NAST; i++) { - atlas_sprite_init(&sp[i], &at, 0); - sp[i].x = rnd_x(); - sp[i].y = rnd_y(); - sprite_anim(&sp[i], 0, 3, (uint8_t)(4 + i), ANIM_LOOP); - new_target(i); - sprite_show(&sp[i]); - } - for (i = 0; i < NEX; i++) - atlas_sprite_init(&sp[NAST + i], &at, 1); - atlas_sprite_init(&sp[IBEAC], &at, 2); - sp[IBEAC].x = 292; sp[IBEAC].y = 20; - sprite_anim(&sp[IBEAC], 0, 3, 6, ANIM_PINGPONG); - sprite_show(&sp[IBEAC]); - - /* Фон на обеих страницах (ОЗУ-копия = бэкап для heal). */ - for (page = 0; page < 2; page++) { - gfx_set_draw_page(page); - draw_space(); - sprite_update(sp, NSPR); - } - gfx_set_visible_page(0); - getdatetime(&dt); - srand((unsigned)(dt.second * 77u + dt.minute)); /* цели — случайные */ -#if FPS_METER - getdatetime(&dt); - last_sec = dt.second; -#endif - - for (;;) { - if (kbhit() && getch() == 27) - break; - - /* Прибывшие астероиды: взрыв в точке + новая цель. */ - for (i = 0; i < NAST; i++) { - if (!sprite_moving(&sp[i])) { - sprite_t *ex = &sp[NAST + i]; - - sprite_move(ex, sp[i].x, sp[i].y); - sprite_anim(ex, 0, 3, 4, ANIM_ONCE); - sprite_show(ex); - new_target(i); - } - } - /* Отгоревшие взрывы — спрятать. */ - for (i = 0; i < NEX; i++) { - sprite_t *ex = &sp[NAST + i]; - - if ((sprite_anim_status(ex) & SPR_ANIM_DONE) && - (ex->flags & SPR_VISIBLE)) - sprite_hide(ex); - } - - hidden = gfx_get_visible_page() ^ 1; - gfx_set_draw_page(hidden); -#if FPS_METER - /* Свежую цифру рисовать на ОБЕИХ страницах (fps_draw = 2 кадра - * подряд) — иначе она живёт на одной и мерцает при флипе. */ - frames++; - getdatetime(&dt); - if (dt.second != last_sec) { - last_sec = dt.second; - fps_val = frames; - frames = 0; - fps_draw = 2; - } - if (fps_draw) { - fps_draw--; - show_fps(fps_val); - } -#endif - sprite_update(sp, NSPR); - gfx_wait_vsync(); - gfx_set_visible_page(hidden); - } - - closegraph(); - atlas_free(&at); - return 0; -} diff --git a/examples/third_party/16x16-RPG-characters/LICENSE b/examples/third_party/16x16-RPG-characters/LICENSE deleted file mode 100644 index 604209a..0000000 --- a/examples/third_party/16x16-RPG-characters/LICENSE +++ /dev/null @@ -1,359 +0,0 @@ -Creative Commons Legal Code - -Attribution-ShareAlike 3.0 Unported - - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR - DAMAGES RESULTING FROM ITS USE. - -License - -THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE -COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY -COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS -AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. - -BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE -TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY -BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS -CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND -CONDITIONS. - -1. Definitions - - a. "Adaptation" means a work based upon the Work, or upon the Work and - other pre-existing works, such as a translation, adaptation, - derivative work, arrangement of music or other alterations of a - literary or artistic work, or phonogram or performance and includes - cinematographic adaptations or any other form in which the Work may be - recast, transformed, or adapted including in any form recognizably - derived from the original, except that a work that constitutes a - Collection will not be considered an Adaptation for the purpose of - this License. For the avoidance of doubt, where the Work is a musical - work, performance or phonogram, the synchronization of the Work in - timed-relation with a moving image ("synching") will be considered an - Adaptation for the purpose of this License. - b. "Collection" means a collection of literary or artistic works, such as - encyclopedias and anthologies, or performances, phonograms or - broadcasts, or other works or subject matter other than works listed - in Section 1(f) below, which, by reason of the selection and - arrangement of their contents, constitute intellectual creations, in - which the Work is included in its entirety in unmodified form along - with one or more other contributions, each constituting separate and - independent works in themselves, which together are assembled into a - collective whole. A work that constitutes a Collection will not be - considered an Adaptation (as defined below) for the purposes of this - License. - c. "Creative Commons Compatible License" means a license that is listed - at https://creativecommons.org/compatiblelicenses that has been - approved by Creative Commons as being essentially equivalent to this - License, including, at a minimum, because that license: (i) contains - terms that have the same purpose, meaning and effect as the License - Elements of this License; and, (ii) explicitly permits the relicensing - of adaptations of works made available under that license under this - License or a Creative Commons jurisdiction license with the same - License Elements as this License. - d. "Distribute" means to make available to the public the original and - copies of the Work or Adaptation, as appropriate, through sale or - other transfer of ownership. - e. "License Elements" means the following high-level license attributes - as selected by Licensor and indicated in the title of this License: - Attribution, ShareAlike. - f. "Licensor" means the individual, individuals, entity or entities that - offer(s) the Work under the terms of this License. - g. "Original Author" means, in the case of a literary or artistic work, - the individual, individuals, entity or entities who created the Work - or if no individual or entity can be identified, the publisher; and in - addition (i) in the case of a performance the actors, singers, - musicians, dancers, and other persons who act, sing, deliver, declaim, - play in, interpret or otherwise perform literary or artistic works or - expressions of folklore; (ii) in the case of a phonogram the producer - being the person or legal entity who first fixes the sounds of a - performance or other sounds; and, (iii) in the case of broadcasts, the - organization that transmits the broadcast. - h. "Work" means the literary and/or artistic work offered under the terms - of this License including without limitation any production in the - literary, scientific and artistic domain, whatever may be the mode or - form of its expression including digital form, such as a book, - pamphlet and other writing; a lecture, address, sermon or other work - of the same nature; a dramatic or dramatico-musical work; a - choreographic work or entertainment in dumb show; a musical - composition with or without words; a cinematographic work to which are - assimilated works expressed by a process analogous to cinematography; - a work of drawing, painting, architecture, sculpture, engraving or - lithography; a photographic work to which are assimilated works - expressed by a process analogous to photography; a work of applied - art; an illustration, map, plan, sketch or three-dimensional work - relative to geography, topography, architecture or science; a - performance; a broadcast; a phonogram; a compilation of data to the - extent it is protected as a copyrightable work; or a work performed by - a variety or circus performer to the extent it is not otherwise - considered a literary or artistic work. - i. "You" means an individual or entity exercising rights under this - License who has not previously violated the terms of this License with - respect to the Work, or who has received express permission from the - Licensor to exercise rights under this License despite a previous - violation. - j. "Publicly Perform" means to perform public recitations of the Work and - to communicate to the public those public recitations, by any means or - process, including by wire or wireless means or public digital - performances; to make available to the public Works in such a way that - members of the public may access these Works from a place and at a - place individually chosen by them; to perform the Work to the public - by any means or process and the communication to the public of the - performances of the Work, including by public digital performance; to - broadcast and rebroadcast the Work by any means including signs, - sounds or images. - k. "Reproduce" means to make copies of the Work by any means including - without limitation by sound or visual recordings and the right of - fixation and reproducing fixations of the Work, including storage of a - protected performance or phonogram in digital form or other electronic - medium. - -2. Fair Dealing Rights. Nothing in this License is intended to reduce, -limit, or restrict any uses free from copyright or rights arising from -limitations or exceptions that are provided for in connection with the -copyright protection under copyright law or other applicable laws. - -3. License Grant. Subject to the terms and conditions of this License, -Licensor hereby grants You a worldwide, royalty-free, non-exclusive, -perpetual (for the duration of the applicable copyright) license to -exercise the rights in the Work as stated below: - - a. to Reproduce the Work, to incorporate the Work into one or more - Collections, and to Reproduce the Work as incorporated in the - Collections; - b. to create and Reproduce Adaptations provided that any such Adaptation, - including any translation in any medium, takes reasonable steps to - clearly label, demarcate or otherwise identify that changes were made - to the original Work. For example, a translation could be marked "The - original work was translated from English to Spanish," or a - modification could indicate "The original work has been modified."; - c. to Distribute and Publicly Perform the Work including as incorporated - in Collections; and, - d. to Distribute and Publicly Perform Adaptations. - e. For the avoidance of doubt: - - i. Non-waivable Compulsory License Schemes. In those jurisdictions in - which the right to collect royalties through any statutory or - compulsory licensing scheme cannot be waived, the Licensor - reserves the exclusive right to collect such royalties for any - exercise by You of the rights granted under this License; - ii. Waivable Compulsory License Schemes. In those jurisdictions in - which the right to collect royalties through any statutory or - compulsory licensing scheme can be waived, the Licensor waives the - exclusive right to collect such royalties for any exercise by You - of the rights granted under this License; and, - iii. Voluntary License Schemes. The Licensor waives the right to - collect royalties, whether individually or, in the event that the - Licensor is a member of a collecting society that administers - voluntary licensing schemes, via that society, from any exercise - by You of the rights granted under this License. - -The above rights may be exercised in all media and formats whether now -known or hereafter devised. The above rights include the right to make -such modifications as are technically necessary to exercise the rights in -other media and formats. Subject to Section 8(f), all rights not expressly -granted by Licensor are hereby reserved. - -4. Restrictions. The license granted in Section 3 above is expressly made -subject to and limited by the following restrictions: - - a. You may Distribute or Publicly Perform the Work only under the terms - of this License. You must include a copy of, or the Uniform Resource - Identifier (URI) for, this License with every copy of the Work You - Distribute or Publicly Perform. You may not offer or impose any terms - on the Work that restrict the terms of this License or the ability of - the recipient of the Work to exercise the rights granted to that - recipient under the terms of the License. You may not sublicense the - Work. You must keep intact all notices that refer to this License and - to the disclaimer of warranties with every copy of the Work You - Distribute or Publicly Perform. When You Distribute or Publicly - Perform the Work, You may not impose any effective technological - measures on the Work that restrict the ability of a recipient of the - Work from You to exercise the rights granted to that recipient under - the terms of the License. This Section 4(a) applies to the Work as - incorporated in a Collection, but this does not require the Collection - apart from the Work itself to be made subject to the terms of this - License. If You create a Collection, upon notice from any Licensor You - must, to the extent practicable, remove from the Collection any credit - as required by Section 4(c), as requested. If You create an - Adaptation, upon notice from any Licensor You must, to the extent - practicable, remove from the Adaptation any credit as required by - Section 4(c), as requested. - b. You may Distribute or Publicly Perform an Adaptation only under the - terms of: (i) this License; (ii) a later version of this License with - the same License Elements as this License; (iii) a Creative Commons - jurisdiction license (either this or a later license version) that - contains the same License Elements as this License (e.g., - Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible - License. If you license the Adaptation under one of the licenses - mentioned in (iv), you must comply with the terms of that license. If - you license the Adaptation under the terms of any of the licenses - mentioned in (i), (ii) or (iii) (the "Applicable License"), you must - comply with the terms of the Applicable License generally and the - following provisions: (I) You must include a copy of, or the URI for, - the Applicable License with every copy of each Adaptation You - Distribute or Publicly Perform; (II) You may not offer or impose any - terms on the Adaptation that restrict the terms of the Applicable - License or the ability of the recipient of the Adaptation to exercise - the rights granted to that recipient under the terms of the Applicable - License; (III) You must keep intact all notices that refer to the - Applicable License and to the disclaimer of warranties with every copy - of the Work as included in the Adaptation You Distribute or Publicly - Perform; (IV) when You Distribute or Publicly Perform the Adaptation, - You may not impose any effective technological measures on the - Adaptation that restrict the ability of a recipient of the Adaptation - from You to exercise the rights granted to that recipient under the - terms of the Applicable License. This Section 4(b) applies to the - Adaptation as incorporated in a Collection, but this does not require - the Collection apart from the Adaptation itself to be made subject to - the terms of the Applicable License. - c. If You Distribute, or Publicly Perform the Work or any Adaptations or - Collections, You must, unless a request has been made pursuant to - Section 4(a), keep intact all copyright notices for the Work and - provide, reasonable to the medium or means You are utilizing: (i) the - name of the Original Author (or pseudonym, if applicable) if supplied, - and/or if the Original Author and/or Licensor designate another party - or parties (e.g., a sponsor institute, publishing entity, journal) for - attribution ("Attribution Parties") in Licensor's copyright notice, - terms of service or by other reasonable means, the name of such party - or parties; (ii) the title of the Work if supplied; (iii) to the - extent reasonably practicable, the URI, if any, that Licensor - specifies to be associated with the Work, unless such URI does not - refer to the copyright notice or licensing information for the Work; - and (iv) , consistent with Ssection 3(b), in the case of an - Adaptation, a credit identifying the use of the Work in the Adaptation - (e.g., "French translation of the Work by Original Author," or - "Screenplay based on original Work by Original Author"). The credit - required by this Section 4(c) may be implemented in any reasonable - manner; provided, however, that in the case of a Adaptation or - Collection, at a minimum such credit will appear, if a credit for all - contributing authors of the Adaptation or Collection appears, then as - part of these credits and in a manner at least as prominent as the - credits for the other contributing authors. For the avoidance of - doubt, You may only use the credit required by this Section for the - purpose of attribution in the manner set out above and, by exercising - Your rights under this License, You may not implicitly or explicitly - assert or imply any connection with, sponsorship or endorsement by the - Original Author, Licensor and/or Attribution Parties, as appropriate, - of You or Your use of the Work, without the separate, express prior - written permission of the Original Author, Licensor and/or Attribution - Parties. - d. Except as otherwise agreed in writing by the Licensor or as may be - otherwise permitted by applicable law, if You Reproduce, Distribute or - Publicly Perform the Work either by itself or as part of any - Adaptations or Collections, You must not distort, mutilate, modify or - take other derogatory action in relation to the Work which would be - prejudicial to the Original Author's honor or reputation. Licensor - agrees that in those jurisdictions (e.g. Japan), in which any exercise - of the right granted in Section 3(b) of this License (the right to - make Adaptations) would be deemed to be a distortion, mutilation, - modification or other derogatory action prejudicial to the Original - Author's honor and reputation, the Licensor will waive or not assert, - as appropriate, this Section, to the fullest extent permitted by the - applicable national law, to enable You to reasonably exercise Your - right under Section 3(b) of this License (right to make Adaptations) - but not otherwise. - -5. Representations, Warranties and Disclaimer - -UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR -OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY -KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, -INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, -FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF -LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, -WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION -OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. - -6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE -LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR -ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES -ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS -BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -7. Termination - - a. This License and the rights granted hereunder will terminate - automatically upon any breach by You of the terms of this License. - Individuals or entities who have received Adaptations or Collections - from You under this License, however, will not have their licenses - terminated provided such individuals or entities remain in full - compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will - survive any termination of this License. - b. Subject to the above terms and conditions, the license granted here is - perpetual (for the duration of the applicable copyright in the Work). - Notwithstanding the above, Licensor reserves the right to release the - Work under different license terms or to stop distributing the Work at - any time; provided, however that any such election will not serve to - withdraw this License (or any other license that has been, or is - required to be, granted under the terms of this License), and this - License will continue in full force and effect unless terminated as - stated above. - -8. Miscellaneous - - a. Each time You Distribute or Publicly Perform the Work or a Collection, - the Licensor offers to the recipient a license to the Work on the same - terms and conditions as the license granted to You under this License. - b. Each time You Distribute or Publicly Perform an Adaptation, Licensor - offers to the recipient a license to the original Work on the same - terms and conditions as the license granted to You under this License. - c. If any provision of this License is invalid or unenforceable under - applicable law, it shall not affect the validity or enforceability of - the remainder of the terms of this License, and without further action - by the parties to this agreement, such provision shall be reformed to - the minimum extent necessary to make such provision valid and - enforceable. - d. No term or provision of this License shall be deemed waived and no - breach consented to unless such waiver or consent shall be in writing - and signed by the party to be charged with such waiver or consent. - e. This License constitutes the entire agreement between the parties with - respect to the Work licensed here. There are no understandings, - agreements or representations with respect to the Work not specified - here. Licensor shall not be bound by any additional provisions that - may appear in any communication from You. This License may not be - modified without the mutual written agreement of the Licensor and You. - f. The rights granted under, and the subject matter referenced, in this - License were drafted utilizing the terminology of the Berne Convention - for the Protection of Literary and Artistic Works (as amended on - September 28, 1979), the Rome Convention of 1961, the WIPO Copyright - Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 - and the Universal Copyright Convention (as revised on July 24, 1971). - These rights and subject matter take effect in the relevant - jurisdiction in which the License terms are sought to be enforced - according to the corresponding provisions of the implementation of - those treaty provisions in the applicable national law. If the - standard suite of rights granted under applicable copyright law - includes additional rights not granted under this License, such - additional rights are deemed to be included in the License; this - License is not intended to restrict the license of any rights under - applicable law. - - -Creative Commons Notice - - Creative Commons is not a party to this License, and makes no warranty - whatsoever in connection with the Work. Creative Commons will not be - liable to You or any party on any legal theory for any damages - whatsoever, including without limitation any general, special, - incidental or consequential damages arising in connection to this - license. Notwithstanding the foregoing two (2) sentences, if Creative - Commons has expressly identified itself as the Licensor hereunder, it - shall have all rights and obligations of Licensor. - - Except for the limited purpose of indicating to the public that the - Work is licensed under the CCPL, Creative Commons does not authorize - the use by either party of the trademark "Creative Commons" or any - related trademark or logo of Creative Commons without the prior - written consent of Creative Commons. Any permitted use will be in - compliance with Creative Commons' then-current trademark usage - guidelines, as may be published on its website or otherwise made - available upon request from time to time. For the avoidance of doubt, - this trademark restriction does not form part of the License. - - Creative Commons may be contacted at https://creativecommons.org/. diff --git a/examples/third_party/16x16-RPG-characters/README.md b/examples/third_party/16x16-RPG-characters/README.md deleted file mode 100644 index b3b78a0..0000000 --- a/examples/third_party/16x16-RPG-characters/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# 16x16 RPG characters `v3.0` - -16x16px RPG character sprite sheet for up to down games. - -This asset pack has been downloaded from https://route1rodent.itch.io/16x16-rpg-character-sprite-sheet - -Adapted from opengameart.org 's "[NES-Style RPG Characters](https://opengameart.org/content/nes-style-rpg-characters)" and "[More NES-style RPG Characters](https://opengameart.org/content/more-nes-style-rpg-characters)", with additional content made by [@route1rodent](https://route1rodent.itch.io). - -## License - -16x16 RPG character sprite sheet (c) by @route1rodent - -"16x16 RPG character sprite sheet" is licensed under a -Creative Commons Attribution-ShareAlike 3.0 Unported License (CC BY-SA 3.0). - -You should have received a copy of the license along with this -work. If not, see http://creativecommons.org/licenses/by-sa/3.0/. - - -## Credits - -Maintained by @route1rodent: - -- itch.io: https://route1rodent.itch.io -- Twitter: https://twitter.com/route1rodent -- Github: https://github.com/itsjavi -- Blog: https://blog.itsjavi.com diff --git a/examples/third_party/16x16-RPG-characters/sprites/old-style/02-bard.png b/examples/third_party/16x16-RPG-characters/sprites/old-style/02-bard.png deleted file mode 100644 index 98bf1c7..0000000 Binary files a/examples/third_party/16x16-RPG-characters/sprites/old-style/02-bard.png and /dev/null differ diff --git a/release_docs/en/examples.md b/release_docs/en/examples.md index 6a4a0e2..338462a 100644 --- a/release_docs/en/examples.md +++ b/release_docs/en/examples.md @@ -1,76 +1,30 @@ -# Examples tour +# Programs and examples -The release ships with 27 example programs in `examples/`. Every one of them -is a self-contained demo with comments — they were used as regression tests -during development. +The SDK release includes its regression programs in `tests/`: `hello`, +`cat`, `gfx_demo`, `bgitest` and others. They exercise compiler, libc, +graphics and Sprinter runtime behavior. Build them with `make`; `make floppy` +packs them into a local `build/media/toolkit-tests.img`. -## Build any example +Larger demonstrations (`balls`, `mdview`, `mdview2`, `rpgwalk`, `scroll`, +`space`) are published in the separate Examples project. Clone it separately +and point it at this installed SDK: ```sh -cd examples/hello +export SPRINTER_ROOT=/path/to/sprinter-c +cd /path/to/Examples +pyenv exec python -m pip install -r requirements.txt make ``` -That produces `hello.exe` next to `hello.c` using `examples/example.mk`. - -## Categories - -### Hello world / basics -* **`hello`** — stdio + conio Turbo-C-style colours -* **`argv`** — argv parsing in crt0 -* **`conio`** — conio API smoke test -* **`attrprob`** — probe Sprinter text-attribute byte layout - -### File I/O -* **`cat`** — read & print TEST.TXT -* **`seek`** — 32-bit lseek over a 100 KB file -* **`ls`** — directory listing via ffirst/fnext -* **`filetest`** — FILE* streams (`fopen`/`fread`/`fwrite`/`fclose`) -* **`stattest`** — `stat`/`fstat` on files and directories -* **`openenv`** — open() flags + environment variables - -### Memory & banking -* **`malloc`** — heap stress test (200+ allocations) -* **`mem_test`** — page allocator + `bank_read`/`bank_write` -* **`banked`** — banked code in W3 (huge mode) -* **`bankedbg`** — banked code in W1 (big mode) -* **`banklocl`** — bank-local static data and BSS - -### Mouse -* **`mouse`** — driver in text mode -* **`gfx_mous`** — mouse with custom bitmap cursor in graphics mode - -### Graphics -* **`gfx_demo`** — 320×256×256: lines, rectangles, fill via accelerator -* **`gfx_d16`** — 640×256×16: same primitives in 16-color mode -* **`gfx_text`** — bitmap-font text on graphics screen - -### Misc -* **`errno`** — errno / strerror / perror -* **`timedir`** — date/time + directory listing -* **`ptime`** — POSIX time API (time / localtime / mktime) -* **`strtest`** — `` test (from SDCC's z80.lib) -* **`stdlib`** — `` test (qsort / rand / strtol / etc.) -* **`assrtest`** — assert() -* **`rt_test`** — runtime helpers (sleep, setjmp, atexit) - -## Example.mk - -Every example uses `examples/example.mk`. A minimal Makefile looks like: +For your own program, use the SDK's `app.mk`: ```makefile -PROJ_ROOT := $(abspath $(CURDIR)/../..) -EXAMPLE := my_app -include $(PROJ_ROOT)/examples/example.mk +SPRINTER_ROOT ?= /path/to/sprinter-c +EXAMPLE := my_app +include $(SPRINTER_ROOT)/app.mk ``` -Optional knobs (set before `include`): - -```makefile -MEMORY := huge # default tiny -STACK_SIZE := 4096 # default ~1278 -EXTRA_SRCS := helper.c util.c # extra .c files in same dir -EXTRA_FLAGS := --bank 1=engine.c --debug # pass-through to sprinter-cc -``` - -Use this template for your own programs. +`make floppy` creates an image under the application's `build/media/`. +For an emulator launch, set `MAME_HOME` to a prepared MAME runtime with +`mame.arm`, `roms/` and `IMG/`; `MAME_BIN`, `MAME_ROMPATH`, `MAME_DSS_IMAGE`, +`MAME_SYSTEM_HDD_IMAGE` and `MAME_BIOS` can override each part. diff --git a/release_docs/en/getting_started.md b/release_docs/en/getting_started.md index 3e25adc..dccf295 100644 --- a/release_docs/en/getting_started.md +++ b/release_docs/en/getting_started.md @@ -10,14 +10,15 @@ After extracting `sprinter-c-v1.0-.tar.gz` you've got: * **`lib/sprinter.lib`** — the Sprinter target libc (prebuilt; rebuilt by `make` if you modify libc sources). * **`runtime/`** — crt0 variants and runtime helpers (assembled per-build). * **`toolchain/mkexe/`** — host utility that packs SDCC's `.ihx` into a SprintEXE. -* **`examples/`** — 27 ready-to-build programs. +* **`tests/` + `testkit/`** — SDK regression programs and host tests. +* **`app.mk`** — standalone application build/run rules. * **`docs/{en,ru}/`** — this documentation. ## First build ```sh cd sprinter-c-v1.0- -make all # rebuild lib + every example (~30 s) +make all # rebuild tools, libraries and SDK tests ``` If `make` complains about a missing SDCC binary, fetch it once: @@ -48,8 +49,9 @@ ESTEX DSS shell. The release does **not** include the MAME emulator or the Sprinter ROM / DSS / HDD images — those are large and have their own licensing. To test: -* **MAME:** install MAME 0.283+ separately, obtain Sprinter Sp2000 images from - Peters Plus, mount a FAT12 floppy with your `.exe` files as `-flop1`. +* **MAME:** install a build with the Sprinter driver and prepare `MAME_HOME` + containing `mame.arm`, `roms/`, DSS floppy and system CHD. `make floppy` + creates a local SDK test disk; `make run MAME_HOME=/path/to/runtime` mounts it. * **Real Sprinter:** copy `.exe` to a floppy or HDD partition that DSS can see, then `RUN HELLO` from the shell. @@ -57,5 +59,5 @@ DSS / HDD images — those are large and have their own licensing. To test: * Read `sprinter_cc.md` for compiler flags. * Read `memory_modes.md` when you start needing more than 14 KB of code. -* Browse `examples/` — every file is a working program with comments. +* See `examples.md` for SDK tests and the separate Examples project. * See `headers.md` for the public API surface. diff --git a/release_docs/ru/examples.md b/release_docs/ru/examples.md index 9104a97..51e345c 100644 --- a/release_docs/ru/examples.md +++ b/release_docs/ru/examples.md @@ -1,75 +1,30 @@ -# Обзор примеров +# Программы и примеры -Релиз содержит 27 примеров в каталоге `examples/`. Каждый — самодостаточная -демо-программа с комментариями (использовались как regression-тесты при разработке). +Релиз SDK содержит регрессионные программы в `tests/`: `hello`, `cat`, +`gfx_demo`, `bgitest` и другие. Они проверяют компилятор, libc, графику и +runtime Sprinter. `make` собирает их, а `make floppy` упаковывает в локальный +`build/media/toolkit-tests.img`. -## Сборка примера +Большие демонстрации (`balls`, `mdview`, `mdview2`, `rpgwalk`, `scroll`, +`space`) выпускаются отдельным проектом Examples. Для него задаётся только +путь к установленному SDK: ```sh -cd examples/hello +export SPRINTER_ROOT=/путь/к/sprinter-c +cd /путь/к/Examples +pyenv exec python -m pip install -r requirements.txt make ``` -Результат — `hello.exe` рядом с `hello.c` через `examples/example.mk`. - -## Категории - -### Hello world / основы -* **`hello`** — stdio + conio Turbo-C-стиль цвета -* **`argv`** — парсинг argv в crt0 -* **`conio`** — smoke test conio API -* **`attrprob`** — пробинг байта атрибутов Sprinter - -### Файловый ввод-вывод -* **`cat`** — читает и печатает TEST.TXT -* **`seek`** — 32-битный lseek по файлу в 100 КБ -* **`ls`** — листинг каталога через ffirst/fnext -* **`filetest`** — FILE* стримы (`fopen`/`fread`/`fwrite`/`fclose`) -* **`stattest`** — `stat`/`fstat` для файлов и каталогов -* **`openenv`** — флаги open() + environment variables - -### Память и банки -* **`malloc`** — стресс-тест heap (200+ allocations) -* **`mem_test`** — page allocator + `bank_read`/`bank_write` -* **`banked`** — banked-код в W3 (huge mode) -* **`bankedbg`** — banked-код в W1 (big mode) -* **`banklocl`** — bank-local статические данные и BSS - -### Мышь -* **`mouse`** — драйвер в текстовом режиме -* **`gfx_mous`** — мышь с пользовательским bitmap-курсором в графическом режиме - -### Графика -* **`gfx_demo`** — 320×256×256: линии, прямоугольники, fill через accelerator -* **`gfx_d16`** — 640×256×16: те же примитивы в 16-цветном режиме -* **`gfx_text`** — bitmap-текст на графическом экране - -### Прочее -* **`errno`** — errno / strerror / perror -* **`timedir`** — дата/время + листинг каталога -* **`ptime`** — POSIX time API (time / localtime / mktime) -* **`strtest`** — `` тест (из SDCC's z80.lib) -* **`stdlib`** — `` тест (qsort / rand / strtol / etc.) -* **`assrtest`** — assert() -* **`rt_test`** — runtime helpers (sleep, setjmp, atexit) - -## Example.mk - -Каждый пример использует `examples/example.mk`. Минимальный Makefile: +Для своей программы подключайте `app.mk` SDK: ```makefile -PROJ_ROOT := $(abspath $(CURDIR)/../..) -EXAMPLE := my_app -include $(PROJ_ROOT)/examples/example.mk +SPRINTER_ROOT ?= /путь/к/sprinter-c +EXAMPLE := my_app +include $(SPRINTER_ROOT)/app.mk ``` -Опциональные параметры (задаются до `include`): - -```makefile -MEMORY := huge # по умолчанию tiny -STACK_SIZE := 4096 # по умолчанию ~1278 -EXTRA_SRCS := helper.c util.c # дополнительные .c в той же папке -EXTRA_FLAGS := --bank 1=engine.c --debug # pass-through в sprinter-cc -``` - -Используйте этот template для своих программ. +`make floppy` создаёт образ в локальном `build/media/` приложения. Для +запуска в эмуляторе задайте `MAME_HOME` с `mame.arm`, `roms/` и `IMG/`; +`MAME_BIN`, `MAME_ROMPATH`, `MAME_DSS_IMAGE`, `MAME_SYSTEM_HDD_IMAGE` и +`MAME_BIOS` позволяют переопределить отдельные пути. diff --git a/release_docs/ru/getting_started.md b/release_docs/ru/getting_started.md index bc62b8e..468caf5 100644 --- a/release_docs/ru/getting_started.md +++ b/release_docs/ru/getting_started.md @@ -10,14 +10,15 @@ * **`lib/sprinter.lib`** — целевая libc для Sprinter (предсобрана; пересобирается через `make`, если изменили исходники libc). * **`runtime/`** — варианты crt0 и runtime-помощники (ассемблируются per-build). * **`toolchain/mkexe/`** — host-утилита, упаковывающая `.ihx` SDCC в SprintEXE. -* **`examples/`** — 27 готовых программ. +* **`tests/` + `testkit/`** — регрессионные программы и host-тесты SDK. +* **`app.mk`** — правила сборки и запуска автономных приложений. * **`docs/{en,ru}/`** — эта документация. ## Первая сборка ```sh cd sprinter-c-v1.0- -make all # пересобрать lib + все примеры (~30 с) +make all # пересобрать инструменты, библиотеки и SDK-тесты ``` Если `make` жалуется на отсутствующий бинарник SDCC — загрузите его один раз: @@ -48,8 +49,9 @@ Sprinter / MAME / в любом ESTEX DSS шелле. Релиз **не содержит** эмулятор MAME или образы ROM / DSS / HDD Sprinter — они большие и имеют свои лицензии. Для тестирования: -* **MAME:** установите MAME 0.283+ отдельно, получите образы Sprinter Sp2000 - у Peters Plus, смонтируйте FAT12-флоп с вашими `.exe` файлами как `-flop1`. +* **MAME:** установите сборку с драйвером Sprinter и подготовьте `MAME_HOME` + с `mame.arm`, `roms/`, дискетой DSS и системным CHD. `make floppy` создаёт + локальный диск SDK-тестов; `make run MAME_HOME=/путь/к/runtime` монтирует его. * **Реальный Sprinter:** скопируйте `.exe` на флоп или раздел HDD, видимый для DSS, затем `RUN HELLO` в шелле. @@ -57,6 +59,6 @@ Sprinter / MAME / в любом ESTEX DSS шелле. * Прочитайте `sprinter_cc.md` про флаги компилятора. * Прочитайте `memory_modes.md` когда не хватит 14 КБ кода. -* Просмотрите `examples/` — каждый файл это рабочая программа с комментариями. +* `examples.md` описывает SDK-тесты и отдельный проект Examples. * `headers.md` — список публичных API. * `platform_reference.md` — глубокие нюансы платформы и компилятора (от граблей до подводных камней). diff --git a/release_docs/ru/platform_reference.md b/release_docs/ru/platform_reference.md index 26416ed..0104e76 100644 --- a/release_docs/ru/platform_reference.md +++ b/release_docs/ru/platform_reference.md @@ -366,8 +366,8 @@ Prepend'ит `DEBUG_RT = 1` в crt0 + передаёт `-DDEBUG_RT` в SDCC. О ### MAME testing workflow ```sh -make floppy # пакует все .exe + data в mame/v306/IMG/mc.img -cd mame/v306 && ./run_mame.sh +make floppy # пакует SDK-тесты в build/media/toolkit-tests.img +make run MAME_HOME=/путь/к/MAME/runtime ``` Имена файлов на флопе должны быть 8.3 (FAT12). Все примеры названы соответственно — `banked_big → bankedbg`, `seek_demo → seek`, `time_dir_test → timedir`, etc. diff --git a/tests/sdbg/run_mame_probe.py b/tests/sdbg/run_mame_probe.py index 5ee3625..9c0ea05 100644 --- a/tests/sdbg/run_mame_probe.py +++ b/tests/sdbg/run_mame_probe.py @@ -153,8 +153,6 @@ void worker(void) __banked { try: launcher.wait(timeout=8) except subprocess.TimeoutExpired: launcher.kill();launcher.wait() profile=MameProfile.resolve() - if profile.binary is None and (ROOT/'mame/v306/mame.arm').is_file(): - profile=MameProfile.resolve({'MAME_HOME':str(ROOT/'mame/v306')}) profile.validate(dss=True) create_floppy_image(str(work/'probe.img'),[('PROBE.EXE',str(exe))]) system=work/'system.chd' @@ -264,9 +262,6 @@ end) installed = profile.home.parent / 'plugins' if installed.is_dir(): plugin_paths.append(installed) - legacy = ROOT / 'mame/sources/MAME/plugins' - if legacy.is_dir(): - plugin_paths.append(legacy) command.extend(['-verbose','-plugin','sdbgbridge','-pluginspath', ';'.join(map(str, plugin_paths))]) with (work/'mame.log').open('w') as log: diff --git a/tests/sdbg/run_vscode_dap_probe.py b/tests/sdbg/run_vscode_dap_probe.py index 85ddf9f..3813de0 100644 --- a/tests/sdbg/run_vscode_dap_probe.py +++ b/tests/sdbg/run_vscode_dap_probe.py @@ -5,6 +5,7 @@ """ from __future__ import annotations +import argparse import json import os from pathlib import Path @@ -85,9 +86,15 @@ def wait_event(process, buffer, event, timeout): def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--build', default=None) + parser.add_argument('--app-hdd', default=None) + parser.add_argument('--launch-path', default=None) + options, _ = parser.parse_known_args() if not PYTHON.is_file(): raise RuntimeError('Нет local pyenv shim: '+str(PYTHON)) - build = ROOT/'tests/hello/.sprinter-cc-hello' + build = Path(options.build).resolve() if options.build else \ + ROOT/'tests/hello/.sprinter-cc-hello' command = [str(PYTHON), str(ROOT/'toolchain/sdbg_dap.py')] process = subprocess.Popen(command, cwd=ROOT, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -95,8 +102,13 @@ def main(): try: send(process, 1, 'initialize') _, buffer, _ = wait_response(process, buffer, 'initialize', 10) - send(process, 2, 'launch', {'build': str(build), 'debugger': 'sdbg', - 'dssTimeout': 30}) + debugger = 'osx' if '--osx' in sys.argv else 'sdbg' + launch = {'build': str(build), 'debugger': debugger, 'dssTimeout': 30} + if options.app_hdd: + launch['appHdd'] = str(Path(options.app_hdd).resolve()) + if options.launch_path: + launch['launchPath'] = options.launch_path + send(process, 2, 'launch', launch) launched, buffer, events = wait_response(process, buffer, 'launch', 80) if not any(item.get('event') == 'initialized' for item in events): initialized, buffer = receive(process, buffer, 5) diff --git a/tests/sdbg/test_dap.py b/tests/sdbg/test_dap.py index 1d3edbc..ed168b5 100644 --- a/tests/sdbg/test_dap.py +++ b/tests/sdbg/test_dap.py @@ -128,19 +128,5 @@ class DapTests(unittest.TestCase): self.assertEqual([item['type'] for item in messages], ['response', 'response']) - def test_vscode_extension_activates_before_debug_adapter_factory(self): - extension_dir = ROOT / 'toolchain' / 'vscode-sprinter-debug' - manifest = json.loads((extension_dir / 'package.json').read_text()) - self.assertIn('onDebug', manifest['activationEvents']) - self.assertNotIn('onDebug:sprinter-mame', manifest['activationEvents']) - self.assertEqual('auto', manifest['contributes']['configuration'] - ['properties']['sprinterDebugger.pythonCommand']['default']) - self.assertTrue((extension_dir / manifest['main']).is_file()) - debugger_types = { - item['type'] for item in manifest['contributes']['debuggers'] - } - self.assertIn('sprinter-mame', debugger_types) - - if __name__ == '__main__': unittest.main() diff --git a/tests/sdbg/test_sdbg.py b/tests/sdbg/test_sdbg.py index acde7ae..2038cf8 100644 --- a/tests/sdbg/test_sdbg.py +++ b/tests/sdbg/test_sdbg.py @@ -114,6 +114,22 @@ class Integration(unittest.TestCase): self.assertEqual(value['bank'],1) self.assertEqual(value['window'],1) + def test_04a_absolute_variable_is_not_confused_with_code(self): + source=self.work/'absolute.c' + source.write_text(''' +__at (0x4000) volatile unsigned char scratch; +int main(void) { scratch = 42; return 0; } +''') + self.build('absolute',[source],'--src-debug') + model=self.model('absolute') + scratch=next(v for v in model.variables if v['name']=='scratch') + self.assertEqual(scratch['section'],'_ABS') + self.assertEqual(scratch['logical_address'],0x4000) + self.assertEqual(scratch['window'],1) + self.assertIsNone(scratch['bank']) + with self.assertRaisesRegex(ValueError,'секция'): + model.location(0x4000) + def test_05_stale_sources_and_corrupt_artifacts(self): source=self.work/'stale.c' source.write_text('int main(void) {return 42;}') diff --git a/third_party/16x16-RPG-characters/LICENSE b/third_party/16x16-RPG-characters/LICENSE deleted file mode 100644 index 604209a..0000000 --- a/third_party/16x16-RPG-characters/LICENSE +++ /dev/null @@ -1,359 +0,0 @@ -Creative Commons Legal Code - -Attribution-ShareAlike 3.0 Unported - - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR - DAMAGES RESULTING FROM ITS USE. - -License - -THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE -COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY -COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS -AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. - -BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE -TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY -BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS -CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND -CONDITIONS. - -1. Definitions - - a. "Adaptation" means a work based upon the Work, or upon the Work and - other pre-existing works, such as a translation, adaptation, - derivative work, arrangement of music or other alterations of a - literary or artistic work, or phonogram or performance and includes - cinematographic adaptations or any other form in which the Work may be - recast, transformed, or adapted including in any form recognizably - derived from the original, except that a work that constitutes a - Collection will not be considered an Adaptation for the purpose of - this License. For the avoidance of doubt, where the Work is a musical - work, performance or phonogram, the synchronization of the Work in - timed-relation with a moving image ("synching") will be considered an - Adaptation for the purpose of this License. - b. "Collection" means a collection of literary or artistic works, such as - encyclopedias and anthologies, or performances, phonograms or - broadcasts, or other works or subject matter other than works listed - in Section 1(f) below, which, by reason of the selection and - arrangement of their contents, constitute intellectual creations, in - which the Work is included in its entirety in unmodified form along - with one or more other contributions, each constituting separate and - independent works in themselves, which together are assembled into a - collective whole. A work that constitutes a Collection will not be - considered an Adaptation (as defined below) for the purposes of this - License. - c. "Creative Commons Compatible License" means a license that is listed - at https://creativecommons.org/compatiblelicenses that has been - approved by Creative Commons as being essentially equivalent to this - License, including, at a minimum, because that license: (i) contains - terms that have the same purpose, meaning and effect as the License - Elements of this License; and, (ii) explicitly permits the relicensing - of adaptations of works made available under that license under this - License or a Creative Commons jurisdiction license with the same - License Elements as this License. - d. "Distribute" means to make available to the public the original and - copies of the Work or Adaptation, as appropriate, through sale or - other transfer of ownership. - e. "License Elements" means the following high-level license attributes - as selected by Licensor and indicated in the title of this License: - Attribution, ShareAlike. - f. "Licensor" means the individual, individuals, entity or entities that - offer(s) the Work under the terms of this License. - g. "Original Author" means, in the case of a literary or artistic work, - the individual, individuals, entity or entities who created the Work - or if no individual or entity can be identified, the publisher; and in - addition (i) in the case of a performance the actors, singers, - musicians, dancers, and other persons who act, sing, deliver, declaim, - play in, interpret or otherwise perform literary or artistic works or - expressions of folklore; (ii) in the case of a phonogram the producer - being the person or legal entity who first fixes the sounds of a - performance or other sounds; and, (iii) in the case of broadcasts, the - organization that transmits the broadcast. - h. "Work" means the literary and/or artistic work offered under the terms - of this License including without limitation any production in the - literary, scientific and artistic domain, whatever may be the mode or - form of its expression including digital form, such as a book, - pamphlet and other writing; a lecture, address, sermon or other work - of the same nature; a dramatic or dramatico-musical work; a - choreographic work or entertainment in dumb show; a musical - composition with or without words; a cinematographic work to which are - assimilated works expressed by a process analogous to cinematography; - a work of drawing, painting, architecture, sculpture, engraving or - lithography; a photographic work to which are assimilated works - expressed by a process analogous to photography; a work of applied - art; an illustration, map, plan, sketch or three-dimensional work - relative to geography, topography, architecture or science; a - performance; a broadcast; a phonogram; a compilation of data to the - extent it is protected as a copyrightable work; or a work performed by - a variety or circus performer to the extent it is not otherwise - considered a literary or artistic work. - i. "You" means an individual or entity exercising rights under this - License who has not previously violated the terms of this License with - respect to the Work, or who has received express permission from the - Licensor to exercise rights under this License despite a previous - violation. - j. "Publicly Perform" means to perform public recitations of the Work and - to communicate to the public those public recitations, by any means or - process, including by wire or wireless means or public digital - performances; to make available to the public Works in such a way that - members of the public may access these Works from a place and at a - place individually chosen by them; to perform the Work to the public - by any means or process and the communication to the public of the - performances of the Work, including by public digital performance; to - broadcast and rebroadcast the Work by any means including signs, - sounds or images. - k. "Reproduce" means to make copies of the Work by any means including - without limitation by sound or visual recordings and the right of - fixation and reproducing fixations of the Work, including storage of a - protected performance or phonogram in digital form or other electronic - medium. - -2. Fair Dealing Rights. Nothing in this License is intended to reduce, -limit, or restrict any uses free from copyright or rights arising from -limitations or exceptions that are provided for in connection with the -copyright protection under copyright law or other applicable laws. - -3. License Grant. Subject to the terms and conditions of this License, -Licensor hereby grants You a worldwide, royalty-free, non-exclusive, -perpetual (for the duration of the applicable copyright) license to -exercise the rights in the Work as stated below: - - a. to Reproduce the Work, to incorporate the Work into one or more - Collections, and to Reproduce the Work as incorporated in the - Collections; - b. to create and Reproduce Adaptations provided that any such Adaptation, - including any translation in any medium, takes reasonable steps to - clearly label, demarcate or otherwise identify that changes were made - to the original Work. For example, a translation could be marked "The - original work was translated from English to Spanish," or a - modification could indicate "The original work has been modified."; - c. to Distribute and Publicly Perform the Work including as incorporated - in Collections; and, - d. to Distribute and Publicly Perform Adaptations. - e. For the avoidance of doubt: - - i. Non-waivable Compulsory License Schemes. In those jurisdictions in - which the right to collect royalties through any statutory or - compulsory licensing scheme cannot be waived, the Licensor - reserves the exclusive right to collect such royalties for any - exercise by You of the rights granted under this License; - ii. Waivable Compulsory License Schemes. In those jurisdictions in - which the right to collect royalties through any statutory or - compulsory licensing scheme can be waived, the Licensor waives the - exclusive right to collect such royalties for any exercise by You - of the rights granted under this License; and, - iii. Voluntary License Schemes. The Licensor waives the right to - collect royalties, whether individually or, in the event that the - Licensor is a member of a collecting society that administers - voluntary licensing schemes, via that society, from any exercise - by You of the rights granted under this License. - -The above rights may be exercised in all media and formats whether now -known or hereafter devised. The above rights include the right to make -such modifications as are technically necessary to exercise the rights in -other media and formats. Subject to Section 8(f), all rights not expressly -granted by Licensor are hereby reserved. - -4. Restrictions. The license granted in Section 3 above is expressly made -subject to and limited by the following restrictions: - - a. You may Distribute or Publicly Perform the Work only under the terms - of this License. You must include a copy of, or the Uniform Resource - Identifier (URI) for, this License with every copy of the Work You - Distribute or Publicly Perform. You may not offer or impose any terms - on the Work that restrict the terms of this License or the ability of - the recipient of the Work to exercise the rights granted to that - recipient under the terms of the License. You may not sublicense the - Work. You must keep intact all notices that refer to this License and - to the disclaimer of warranties with every copy of the Work You - Distribute or Publicly Perform. When You Distribute or Publicly - Perform the Work, You may not impose any effective technological - measures on the Work that restrict the ability of a recipient of the - Work from You to exercise the rights granted to that recipient under - the terms of the License. This Section 4(a) applies to the Work as - incorporated in a Collection, but this does not require the Collection - apart from the Work itself to be made subject to the terms of this - License. If You create a Collection, upon notice from any Licensor You - must, to the extent practicable, remove from the Collection any credit - as required by Section 4(c), as requested. If You create an - Adaptation, upon notice from any Licensor You must, to the extent - practicable, remove from the Adaptation any credit as required by - Section 4(c), as requested. - b. You may Distribute or Publicly Perform an Adaptation only under the - terms of: (i) this License; (ii) a later version of this License with - the same License Elements as this License; (iii) a Creative Commons - jurisdiction license (either this or a later license version) that - contains the same License Elements as this License (e.g., - Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible - License. If you license the Adaptation under one of the licenses - mentioned in (iv), you must comply with the terms of that license. If - you license the Adaptation under the terms of any of the licenses - mentioned in (i), (ii) or (iii) (the "Applicable License"), you must - comply with the terms of the Applicable License generally and the - following provisions: (I) You must include a copy of, or the URI for, - the Applicable License with every copy of each Adaptation You - Distribute or Publicly Perform; (II) You may not offer or impose any - terms on the Adaptation that restrict the terms of the Applicable - License or the ability of the recipient of the Adaptation to exercise - the rights granted to that recipient under the terms of the Applicable - License; (III) You must keep intact all notices that refer to the - Applicable License and to the disclaimer of warranties with every copy - of the Work as included in the Adaptation You Distribute or Publicly - Perform; (IV) when You Distribute or Publicly Perform the Adaptation, - You may not impose any effective technological measures on the - Adaptation that restrict the ability of a recipient of the Adaptation - from You to exercise the rights granted to that recipient under the - terms of the Applicable License. This Section 4(b) applies to the - Adaptation as incorporated in a Collection, but this does not require - the Collection apart from the Adaptation itself to be made subject to - the terms of the Applicable License. - c. If You Distribute, or Publicly Perform the Work or any Adaptations or - Collections, You must, unless a request has been made pursuant to - Section 4(a), keep intact all copyright notices for the Work and - provide, reasonable to the medium or means You are utilizing: (i) the - name of the Original Author (or pseudonym, if applicable) if supplied, - and/or if the Original Author and/or Licensor designate another party - or parties (e.g., a sponsor institute, publishing entity, journal) for - attribution ("Attribution Parties") in Licensor's copyright notice, - terms of service or by other reasonable means, the name of such party - or parties; (ii) the title of the Work if supplied; (iii) to the - extent reasonably practicable, the URI, if any, that Licensor - specifies to be associated with the Work, unless such URI does not - refer to the copyright notice or licensing information for the Work; - and (iv) , consistent with Ssection 3(b), in the case of an - Adaptation, a credit identifying the use of the Work in the Adaptation - (e.g., "French translation of the Work by Original Author," or - "Screenplay based on original Work by Original Author"). The credit - required by this Section 4(c) may be implemented in any reasonable - manner; provided, however, that in the case of a Adaptation or - Collection, at a minimum such credit will appear, if a credit for all - contributing authors of the Adaptation or Collection appears, then as - part of these credits and in a manner at least as prominent as the - credits for the other contributing authors. For the avoidance of - doubt, You may only use the credit required by this Section for the - purpose of attribution in the manner set out above and, by exercising - Your rights under this License, You may not implicitly or explicitly - assert or imply any connection with, sponsorship or endorsement by the - Original Author, Licensor and/or Attribution Parties, as appropriate, - of You or Your use of the Work, without the separate, express prior - written permission of the Original Author, Licensor and/or Attribution - Parties. - d. Except as otherwise agreed in writing by the Licensor or as may be - otherwise permitted by applicable law, if You Reproduce, Distribute or - Publicly Perform the Work either by itself or as part of any - Adaptations or Collections, You must not distort, mutilate, modify or - take other derogatory action in relation to the Work which would be - prejudicial to the Original Author's honor or reputation. Licensor - agrees that in those jurisdictions (e.g. Japan), in which any exercise - of the right granted in Section 3(b) of this License (the right to - make Adaptations) would be deemed to be a distortion, mutilation, - modification or other derogatory action prejudicial to the Original - Author's honor and reputation, the Licensor will waive or not assert, - as appropriate, this Section, to the fullest extent permitted by the - applicable national law, to enable You to reasonably exercise Your - right under Section 3(b) of this License (right to make Adaptations) - but not otherwise. - -5. Representations, Warranties and Disclaimer - -UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR -OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY -KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, -INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, -FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF -LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, -WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION -OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. - -6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE -LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR -ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES -ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS -BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -7. Termination - - a. This License and the rights granted hereunder will terminate - automatically upon any breach by You of the terms of this License. - Individuals or entities who have received Adaptations or Collections - from You under this License, however, will not have their licenses - terminated provided such individuals or entities remain in full - compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will - survive any termination of this License. - b. Subject to the above terms and conditions, the license granted here is - perpetual (for the duration of the applicable copyright in the Work). - Notwithstanding the above, Licensor reserves the right to release the - Work under different license terms or to stop distributing the Work at - any time; provided, however that any such election will not serve to - withdraw this License (or any other license that has been, or is - required to be, granted under the terms of this License), and this - License will continue in full force and effect unless terminated as - stated above. - -8. Miscellaneous - - a. Each time You Distribute or Publicly Perform the Work or a Collection, - the Licensor offers to the recipient a license to the Work on the same - terms and conditions as the license granted to You under this License. - b. Each time You Distribute or Publicly Perform an Adaptation, Licensor - offers to the recipient a license to the original Work on the same - terms and conditions as the license granted to You under this License. - c. If any provision of this License is invalid or unenforceable under - applicable law, it shall not affect the validity or enforceability of - the remainder of the terms of this License, and without further action - by the parties to this agreement, such provision shall be reformed to - the minimum extent necessary to make such provision valid and - enforceable. - d. No term or provision of this License shall be deemed waived and no - breach consented to unless such waiver or consent shall be in writing - and signed by the party to be charged with such waiver or consent. - e. This License constitutes the entire agreement between the parties with - respect to the Work licensed here. There are no understandings, - agreements or representations with respect to the Work not specified - here. Licensor shall not be bound by any additional provisions that - may appear in any communication from You. This License may not be - modified without the mutual written agreement of the Licensor and You. - f. The rights granted under, and the subject matter referenced, in this - License were drafted utilizing the terminology of the Berne Convention - for the Protection of Literary and Artistic Works (as amended on - September 28, 1979), the Rome Convention of 1961, the WIPO Copyright - Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 - and the Universal Copyright Convention (as revised on July 24, 1971). - These rights and subject matter take effect in the relevant - jurisdiction in which the License terms are sought to be enforced - according to the corresponding provisions of the implementation of - those treaty provisions in the applicable national law. If the - standard suite of rights granted under applicable copyright law - includes additional rights not granted under this License, such - additional rights are deemed to be included in the License; this - License is not intended to restrict the license of any rights under - applicable law. - - -Creative Commons Notice - - Creative Commons is not a party to this License, and makes no warranty - whatsoever in connection with the Work. Creative Commons will not be - liable to You or any party on any legal theory for any damages - whatsoever, including without limitation any general, special, - incidental or consequential damages arising in connection to this - license. Notwithstanding the foregoing two (2) sentences, if Creative - Commons has expressly identified itself as the Licensor hereunder, it - shall have all rights and obligations of Licensor. - - Except for the limited purpose of indicating to the public that the - Work is licensed under the CCPL, Creative Commons does not authorize - the use by either party of the trademark "Creative Commons" or any - related trademark or logo of Creative Commons without the prior - written consent of Creative Commons. Any permitted use will be in - compliance with Creative Commons' then-current trademark usage - guidelines, as may be published on its website or otherwise made - available upon request from time to time. For the avoidance of doubt, - this trademark restriction does not form part of the License. - - Creative Commons may be contacted at https://creativecommons.org/. diff --git a/third_party/16x16-RPG-characters/README.md b/third_party/16x16-RPG-characters/README.md deleted file mode 100644 index b3b78a0..0000000 --- a/third_party/16x16-RPG-characters/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# 16x16 RPG characters `v3.0` - -16x16px RPG character sprite sheet for up to down games. - -This asset pack has been downloaded from https://route1rodent.itch.io/16x16-rpg-character-sprite-sheet - -Adapted from opengameart.org 's "[NES-Style RPG Characters](https://opengameart.org/content/nes-style-rpg-characters)" and "[More NES-style RPG Characters](https://opengameart.org/content/more-nes-style-rpg-characters)", with additional content made by [@route1rodent](https://route1rodent.itch.io). - -## License - -16x16 RPG character sprite sheet (c) by @route1rodent - -"16x16 RPG character sprite sheet" is licensed under a -Creative Commons Attribution-ShareAlike 3.0 Unported License (CC BY-SA 3.0). - -You should have received a copy of the license along with this -work. If not, see http://creativecommons.org/licenses/by-sa/3.0/. - - -## Credits - -Maintained by @route1rodent: - -- itch.io: https://route1rodent.itch.io -- Twitter: https://twitter.com/route1rodent -- Github: https://github.com/itsjavi -- Blog: https://blog.itsjavi.com diff --git a/third_party/16x16-RPG-characters/preview.png b/third_party/16x16-RPG-characters/preview.png deleted file mode 100644 index 2d1a7b9..0000000 Binary files a/third_party/16x16-RPG-characters/preview.png and /dev/null differ diff --git a/third_party/16x16-RPG-characters/sprites/new-style/chibi-layered.aseprite b/third_party/16x16-RPG-characters/sprites/new-style/chibi-layered.aseprite deleted file mode 100644 index b2cc1f7..0000000 Binary files a/third_party/16x16-RPG-characters/sprites/new-style/chibi-layered.aseprite and /dev/null differ diff --git a/third_party/16x16-RPG-characters/sprites/new-style/chibi-layered.png b/third_party/16x16-RPG-characters/sprites/new-style/chibi-layered.png deleted file mode 100644 index ed9be61..0000000 Binary files a/third_party/16x16-RPG-characters/sprites/new-style/chibi-layered.png and /dev/null differ diff --git a/third_party/16x16-RPG-characters/sprites/old-style/00-avatars.png b/third_party/16x16-RPG-characters/sprites/old-style/00-avatars.png deleted file mode 100644 index 0f1da2e..0000000 Binary files a/third_party/16x16-RPG-characters/sprites/old-style/00-avatars.png and /dev/null differ diff --git a/third_party/16x16-RPG-characters/sprites/old-style/00-guide.png b/third_party/16x16-RPG-characters/sprites/old-style/00-guide.png deleted file mode 100644 index bde23f1..0000000 Binary files a/third_party/16x16-RPG-characters/sprites/old-style/00-guide.png and /dev/null differ diff --git a/third_party/16x16-RPG-characters/sprites/old-style/01-generic.png b/third_party/16x16-RPG-characters/sprites/old-style/01-generic.png deleted file mode 100644 index 419b8a2..0000000 Binary files a/third_party/16x16-RPG-characters/sprites/old-style/01-generic.png and /dev/null differ diff --git a/third_party/16x16-RPG-characters/sprites/old-style/02-bard.png b/third_party/16x16-RPG-characters/sprites/old-style/02-bard.png deleted file mode 100644 index 98bf1c7..0000000 Binary files a/third_party/16x16-RPG-characters/sprites/old-style/02-bard.png and /dev/null differ diff --git a/third_party/16x16-RPG-characters/sprites/old-style/03-soldier.png b/third_party/16x16-RPG-characters/sprites/old-style/03-soldier.png deleted file mode 100644 index a6d6408..0000000 Binary files a/third_party/16x16-RPG-characters/sprites/old-style/03-soldier.png and /dev/null differ diff --git a/third_party/16x16-RPG-characters/sprites/old-style/04-scout.png b/third_party/16x16-RPG-characters/sprites/old-style/04-scout.png deleted file mode 100644 index dfbf05b..0000000 Binary files a/third_party/16x16-RPG-characters/sprites/old-style/04-scout.png and /dev/null differ diff --git a/third_party/16x16-RPG-characters/sprites/old-style/05-devout.png b/third_party/16x16-RPG-characters/sprites/old-style/05-devout.png deleted file mode 100644 index 6e9db7f..0000000 Binary files a/third_party/16x16-RPG-characters/sprites/old-style/05-devout.png and /dev/null differ diff --git a/third_party/16x16-RPG-characters/sprites/old-style/06-conjurer.png b/third_party/16x16-RPG-characters/sprites/old-style/06-conjurer.png deleted file mode 100644 index 296c434..0000000 Binary files a/third_party/16x16-RPG-characters/sprites/old-style/06-conjurer.png and /dev/null differ diff --git a/toolchain/apply-mame-sdbg-patch.sh b/toolchain/apply-mame-sdbg-patch.sh deleted file mode 100755 index d366d26..0000000 --- a/toolchain/apply-mame-sdbg-patch.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh -# Применяет воспроизводимый backend sdbg к checkout MAME 0.287. -set -eu - -project_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -mame_source=${1:-"$project_dir/mame/sources/MAME"} -patch_file="$project_dir/toolchain/mame-patches/0001-sdbg-debugger-backend.patch" - -if git -C "$mame_source" apply --reverse --check "$patch_file" 2>/dev/null; then - echo "MAME sdbg backend уже применён: $mame_source" -elif git -C "$mame_source" apply --check "$patch_file"; then - git -C "$mame_source" apply "$patch_file" - echo "MAME sdbg backend применён: $mame_source" -else - echo "Не удалось применить sdbg patch к $mame_source" >&2 - echo "Ожидается MAME 0.287, commit b0c4527c." >&2 - exit 1 -fi diff --git a/toolchain/make_hdd.sh b/toolchain/make_hdd.sh index 3e007d7..6146818 100755 --- a/toolchain/make_hdd.sh +++ b/toolchain/make_hdd.sh @@ -11,7 +11,7 @@ # Sprinter DSS штатно монтирует стандартный MBR+FAT16 (подтверждено). # # Использование: -# toolchain/make_hdd.sh mame/v306/IMG/test_hdd.chd prog.exe data1 data2 ... +# toolchain/make_hdd.sh build/hdd/test.chd prog.exe data1 data2 ... # toolchain/make_hdd.sh --dest games/sprpop out.chd prog.exe data1 ... # toolchain/make_hdd.sh out.chd prog.exe KID:kid0.atl GUARD:g0.atl ... # — аргумент вида КАТАЛОГ:файл кладёт файл в подкаталог относительно diff --git a/toolchain/make_release.sh b/toolchain/make_release.sh index c49e856..e020c16 100755 --- a/toolchain/make_release.sh +++ b/toolchain/make_release.sh @@ -13,9 +13,10 @@ # - runtime/ : crt0 + bank/heap support # - libc/include/ : public headers # - libc/{io,stdio,mem,gfx}/ : libc sources (assembled into sprinter.lib) -# - lib/Makefile + lib/sprinter.lib : prebuilt library + recipe to rebuild +# - lib/*.lib : prebuilt libc/BGI variants # - app.mk + disk/run tools : standalone application integration # - libbgi/ + lib/bgi256*.lib : graphics library +# - tests/ + testkit/ : SDK regression programs # - third_party/sdcc/ : vendored SDCC 4.5 # - release_docs/ → docs/ : user-facing documentation # - root Makefile, README, RELEASE_NOTES, LICENSE @@ -54,7 +55,7 @@ mkdir -p "$STAGE" cp README.md RELEASE_NOTES.md LICENSE Makefile app.mk "$STAGE/" # Toolchain -cp -R bin runtime libc libbgi "$STAGE/" +cp -R bin runtime libc libbgi tests testkit "$STAGE/" # Core tools and source debugger backend needed by sprinter-cc/app.mk. mkdir -p "$STAGE/toolchain/mkexe" cp toolchain/mkexe/mkexe "$STAGE/toolchain/mkexe/" @@ -72,9 +73,8 @@ cp -R toolchain/mcp "$STAGE/toolchain/" # Lib — keep Makefile and the prebuilt archive only; sources are in libc/ mkdir -p "$STAGE/lib" -cp lib/Makefile "$STAGE/lib/" -cp lib/sprinter.lib "$STAGE/lib/" -cp lib/sprinter_safe.lib lib/bgi256.lib lib/bgi256_safe.lib "$STAGE/lib/" +cp lib/sprinter.lib lib/sprinter_safe.lib \ + lib/bgi256.lib lib/bgi256_safe.lib "$STAGE/lib/" # Vendored SDCC — follow symlinks so the real content lands in the tarball mkdir -p "$STAGE/third_party" @@ -90,6 +90,7 @@ mkdir -p "$STAGE/third_party" echo ">> stripping build artefacts and macOS metadata" find "$STAGE" \( \ -name '.sprinter-cc-*' -o \ + -name '.resource-stamps' -o -name build -o \ -name '*.rel' -o -name '*.lst' -o -name '*.sym' -o -name '*.asm' -o \ -name '*.ihx' -o -name '*.lk' -o -name '*.map' -o -name '*.noi' -o \ -name '*.exe' -o \ diff --git a/toolchain/mame-patches/0001-sdbg-debugger-backend.patch b/toolchain/mame-patches/0001-sdbg-debugger-backend.patch deleted file mode 100644 index 67e46b9..0000000 --- a/toolchain/mame-patches/0001-sdbg-debugger-backend.patch +++ /dev/null @@ -1,129 +0,0 @@ -diff --git a/scripts/src/osd/modules.lua b/scripts/src/osd/modules.lua ---- a/scripts/src/osd/modules.lua -+++ b/scripts/src/osd/modules.lua -@@ -69,6 +69,7 @@ - MAME_DIR .. "src/osd/modules/debugger/debugkeyconfig.h", - MAME_DIR .. "src/osd/modules/debugger/debuggdbstub.cpp", - MAME_DIR .. "src/osd/modules/debugger/debugimgui.cpp", -+ MAME_DIR .. "src/osd/modules/debugger/sdbg.cpp", - MAME_DIR .. "src/osd/modules/debugger/debugwin.cpp", - MAME_DIR .. "src/osd/modules/debugger/none.cpp", - MAME_DIR .. "src/osd/modules/debugger/xmlconfig.cpp", -diff --git a/scripts/src/osd/mac.lua b/scripts/src/osd/mac.lua ---- a/scripts/src/osd/mac.lua -+++ b/scripts/src/osd/mac.lua -@@ -78,6 +78,7 @@ - - files { - MAME_DIR .. "src/osd/modules/debugger/debugosx.mm", -+ MAME_DIR .. "src/osd/modules/debugger/sdbgmac.mm", - MAME_DIR .. "src/osd/modules/debugger/osx/breakpointsview.mm", - MAME_DIR .. "src/osd/modules/debugger/osx/breakpointsview.h", - MAME_DIR .. "src/osd/modules/debugger/osx/consoleview.mm", -diff --git a/src/osd/modules/debugger/sdbg.cpp b/src/osd/modules/debugger/sdbg.cpp -new file mode 100644 ---- /dev/null -+++ b/src/osd/modules/debugger/sdbg.cpp -@@ -0,0 +1,72 @@ -+// license:BSD-3-Clause -+// copyright-holders:Sprinter C-Compiler contributors -+//============================================================ -+// -+// sdbg.cpp - headless debugger wait loop for an external DAP bridge -+// -+//============================================================ -+ -+#include "emu.h" -+#include "debug_module.h" -+#include "modules/lib/osdobj_common.h" -+ -+#if defined(OSD_MAC) -+void sdbg_poll_mac_events(); -+#endif -+ -+ -+namespace osd { -+ -+namespace { -+ -+class debug_sdbg : public osd_module, public debug_module -+{ -+public: -+ debug_sdbg() : osd_module(OSD_DEBUG_PROVIDER, "sdbg"), debug_module(), -+ m_osd(nullptr), m_next_host_poll(0) -+ { } -+ -+ virtual int init(osd_interface &osd, const osd_options &options) override -+ { -+ m_osd = &dynamic_cast(osd); -+ return 0; -+ } -+ virtual void exit() override { } -+ -+ virtual void init_debugger(running_machine &machine) override { } -+ virtual void wait_for_debugger(device_t &device, bool firststop) override; -+ virtual void debugger_update() override { } -+ -+private: -+ osd_common_t *m_osd; -+ osd_ticks_t m_next_host_poll; -+}; -+ -+void debug_sdbg::wait_for_debugger(device_t &device, bool firststop) -+{ -+ // debugger_cpu::wait_for_debugger вызывает Lua periodic перед каждым -+ // обращением сюда. Короткий sleep исключает busy-loop, после возврата -+ // внешний bridge получает следующую возможность обработать RPC. -+ // Без обработки событий окон macOS считает MAME зависшим, пока CPU -+ // удерживается на точке VS Code. 100 Hz достаточно для Dock/Cmd-Tab. -+ osd_ticks_t const current = osd_ticks(); -+ if (current >= m_next_host_poll) -+ { -+#if defined(OSD_MAC) -+ sdbg_poll_mac_events(); -+#else -+ // Текущий mame.arm собран на SDL3: pump + poll необходимы, -+ // поскольку обычный frame_update не выполняется при остановке CPU. -+ m_osd->input_update(false); -+ m_osd->process_events(); -+#endif -+ m_next_host_poll = current + osd_ticks_per_second() / 100; -+ } -+ osd_sleep(osd_ticks_per_second() / 1000); -+} -+ -+} // anonymous namespace -+ -+} // namespace osd -+ -+MODULE_DEFINITION(DEBUG_SDBG, osd::debug_sdbg) -diff --git a/src/osd/modules/debugger/sdbgmac.mm b/src/osd/modules/debugger/sdbgmac.mm -new file mode 100644 ---- /dev/null -+++ b/src/osd/modules/debugger/sdbgmac.mm -@@ -0,0 +1,14 @@ -+// license:BSD-3-Clause -+// copyright-holders:Sprinter C-Compiler contributors -+// Прокачка событий Cocoa в headless debugger без окна штатного debugger. -+ -+#import -+ -+extern void MacPollInputs(); -+ -+void sdbg_poll_mac_events() -+{ -+ NSAutoreleasePool *const pool = [[NSAutoreleasePool alloc] init]; -+ MacPollInputs(); -+ [pool release]; -+} -diff --git a/src/osd/modules/lib/osdobj_common.cpp b/src/osd/modules/lib/osdobj_common.cpp ---- a/src/osd/modules/lib/osdobj_common.cpp -+++ b/src/osd/modules/lib/osdobj_common.cpp -@@ -282,6 +282,7 @@ - REGISTER_MODULE(m_mod_man, DEBUG_QT); - REGISTER_MODULE(m_mod_man, DEBUG_IMGUI); - REGISTER_MODULE(m_mod_man, DEBUG_GDBSTUB); -+ REGISTER_MODULE(m_mod_man, DEBUG_SDBG); - REGISTER_MODULE(m_mod_man, DEBUG_NONE); - #endif - diff --git a/toolchain/mame_interactive.py b/toolchain/mame_interactive.py index c68c991..8f3c635 100644 --- a/toolchain/mame_interactive.py +++ b/toolchain/mame_interactive.py @@ -221,8 +221,6 @@ def main(): ap.add_argument("--snapshot-dir", help="каталог кадров, по умолчанию build/mame-autotest") args = ap.parse_args() profile = from_arguments(args) - if profile.binary is None and (Path(PROJECT_ROOT)/"mame/v306/mame.arm").is_file(): - profile = MameProfile.resolve({"MAME_HOME":str(Path(PROJECT_ROOT)/"mame/v306")}) try: profile.validate(dss=True) except ValueError as error: diff --git a/toolchain/png_strip.py b/toolchain/png_strip.py index d7c860a..5165b4a 100644 --- a/toolchain/png_strip.py +++ b/toolchain/png_strip.py @@ -27,7 +27,7 @@ png_strip.py — генерик-конвертер произвольного н следующего вызова. Так несколько независимо собранных лент (напр. разные типы тайлов) можно свести в ОДНУ палитру для одновременного показа - на экране (см. applications/PoP/poc/tools/ + на экране (см. ../Applications/PoP-Archive/poc/tools/ build_room_palette.py). Каждая картинка — источник ОДНОГО кадра ленты (не атлас-сетка; diff --git a/toolchain/pop_extract_font.py b/toolchain/pop_extract_font.py deleted file mode 100644 index 12c939e..0000000 --- a/toolchain/pop_extract_font.py +++ /dev/null @@ -1,208 +0,0 @@ -#!/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 - -ROOT = Path(__file__).resolve().parent.parent -SRC_SMALL = ROOT / "applications" / "PoP" / "SDLPoP" / "src" / "menu.c" -SRC_BIG = ROOT / "applications" / "PoP" / "SDLPoP" / "src" / "seg009.c" -OUT_DIR = ROOT / "applications" / "PoP" / "poc" / "res" / "font" -OUT_ATL = OUT_DIR / "font.atl" -OUT_H = ROOT / "applications" / "PoP" / "roomtest" / "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/toolchain/pop_pack_intro.py b/toolchain/pop_pack_intro.py deleted file mode 100644 index 469b258..0000000 --- a/toolchain/pop_pack_intro.py +++ /dev/null @@ -1,349 +0,0 @@ -#!/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 - -ROOT = Path(__file__).resolve().parent.parent -TITLE = ROOT / "applications" / "PoP" / "SDLPoP" / "data" / "TITLE" -PV = ROOT / "applications" / "PoP" / "SDLPoP" / "data" / "PV" -PRINCE = ROOT / "applications" / "PoP" / "SDLPoP" / "data" / "PRINCE" -KID = ROOT / "applications" / "PoP" / "SDLPoP" / "data" / "KID" -OUT = ROOT / "applications" / "PoP" / "poc" / "res" / "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/toolchain/pop_pack_title.py b/toolchain/pop_pack_title.py deleted file mode 100644 index f94eaf1..0000000 --- a/toolchain/pop_pack_title.py +++ /dev/null @@ -1,137 +0,0 @@ -#!/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 - -ROOT = Path(__file__).resolve().parent.parent -SRC = ROOT / "applications" / "PoP" / "SDLPoP" / "data" / "TITLE" -OUT = ROOT / "applications" / "PoP" / "poc" / "res" / "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/toolchain/room_compose.py b/toolchain/room_compose.py index e438178..ce25b9f 100644 --- a/toolchain/room_compose.py +++ b/toolchain/room_compose.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ room_compose.py — генерик-компоновщик полноэкранного фона комнаты из -тайлов ПРОИЗВОЛЬНОГО размера (см. applications/PoP/docs/PORT_PLAN.md §4: +тайлов ПРОИЗВОЛЬНОГО размера (см. ../Applications/PoP-Archive/docs/PORT_PLAN.md §4: один полноэкранный фон на комнату готовится офлайн, поверх — спрайты через sprite.h). Не завязан ни на какой конкретный проект/игру — просто берёт раскладку тайлов, которую вы сами опишете, и картинки, которые diff --git a/toolchain/run-mame-mcp.sh b/toolchain/run-mame-mcp.sh index 3db93ac..ce5d8ec 100644 --- a/toolchain/run-mame-mcp.sh +++ b/toolchain/run-mame-mcp.sh @@ -6,7 +6,7 @@ if [ -f "$mame_root/.codex/mame.local.env" ]; then . "$mame_root/.codex/mame.local.env" fi : "${MAME_UV:=uv}" -: "${MAME_MCP_SCRIPT:=mame/sources/MAME/src/mame_mcp.py}" +: "${MAME_MCP_SCRIPT:=../MAME/src/mame_mcp.py}" cd "$mame_root" if ! command -v "$MAME_UV" >/dev/null 2>&1; then echo "mame-z80: uv не найден; добавьте его в PATH или задайте MAME_UV в .codex/mame.local.env" >&2 @@ -16,4 +16,11 @@ if [ ! -f "$MAME_MCP_SCRIPT" ]; then echo "mame-z80: сервер не найден: $MAME_MCP_SCRIPT; задайте MAME_MCP_SCRIPT в .codex/mame.local.env" >&2 exit 1 fi -exec "$MAME_UV" run --python 3.12 --no-project --with 'mcp<2' "$MAME_MCP_SCRIPT" +if [ -z "${MAME_PYTHON:-}" ]; then + if ! command -v pyenv >/dev/null 2>&1; then + echo "mame-z80: pyenv не найден; задайте MAME_PYTHON" >&2 + exit 1 + fi + MAME_PYTHON=$(pyenv which python) +fi +exec "$MAME_UV" run --python "$MAME_PYTHON" --no-project --with 'mcp<2' "$MAME_MCP_SCRIPT" diff --git a/toolchain/sdbg/model.py b/toolchain/sdbg/model.py index 4b874f8..12afb48 100644 --- a/toolchain/sdbg/model.py +++ b/toolchain/sdbg/model.py @@ -66,6 +66,20 @@ class DebugMap: logical = address & 0xffff return asdict(Location(address, logical, section, bank, logical >> 14)) + def variable_location(self, address, symbol): + """Разрешить доказанный linker-символ `__at` вне обычных секций. + + Код/точки по-прежнему обязаны принадлежать ровно одной секции. Для + global/static SDCC может намеренно поместить объект по абсолютному + адресу (например, scratch в W1); его адрес подтверждают и CDB, и NOI. + """ + try: + return self.location(address) + except ValueError: + if not (0 <= address <= 0xffff and self.symbols.get(symbol) == address): + raise + return asdict(Location(address, address, '_ABS', None, address >> 14)) + def _load(self, records): addresses, declarations = {}, [] module = None @@ -128,10 +142,12 @@ class DebugMap: if address is None and parts[0] == 'G': address = self.symbols.get('_'+parts[1]) if address is None: continue supported = bool(re.fullmatch(r'S[ICL]:[SU]', ctype) or ctype.startswith('DG,')) + linker_symbol = key if key in self.symbols else '_'+parts[1] variable = {'name': parts[1], 'module': module if parts[0] != 'G' else None, 'size': int(size), 'type': ctype, 'signed': ctype.endswith(':S') and not ctype.startswith('D'), - 'supported': supported, **self.location(address)} + 'supported': supported, + **self.variable_location(address, linker_symbol)} if variable not in self.variables: self.variables.append(variable) self.functions.sort(key=lambda f: f['start']) self.markers.sort(key=lambda m: m['link_address']) diff --git a/toolchain/sdbg_dap.py b/toolchain/sdbg_dap.py index 5e4a431..106588f 100644 --- a/toolchain/sdbg_dap.py +++ b/toolchain/sdbg_dap.py @@ -106,6 +106,10 @@ class Protocol: for key, option in profile_arguments.items(): if arguments.get(key): command.extend([option, arguments[key]]) + if arguments.get('appHdd'): + command.extend(['--app-hdd', arguments['appHdd']]) + if arguments.get('launchPath'): + command.extend(['--launch-path', arguments['launchPath']]) if arguments.get('debugger'): command.extend(['--debugger', arguments['debugger']]) if arguments.get('launchAt') is not None: diff --git a/toolchain/sdbg_driver.py b/toolchain/sdbg_driver.py index 1a38b43..2820c13 100644 --- a/toolchain/sdbg_driver.py +++ b/toolchain/sdbg_driver.py @@ -84,8 +84,12 @@ def run(wrapper, args): locks.mkdir(exist_ok=True) with (locks / (final_work.name + '.lock')).open('a') as lock: fcntl.flock(lock, fcntl.LOCK_EX) - with tempfile.TemporaryDirectory(prefix='.sdbg-build-', dir=output.parent) as temporary: + keep_stage = bool(os.environ.get('SDBG_KEEP_FAILED_STAGE')) + with tempfile.TemporaryDirectory(prefix='.sdbg-build-', dir=output.parent, + delete=not keep_stage) as temporary: stage = Path(temporary) + if keep_stage: + print('sdbg: диагностическая stage ' + str(stage), file=sys.stderr) new_exe = stage / output.name command = list(args) command[command.index('-o')+1] = str(new_exe) diff --git a/toolchain/sdbg_launcher.py b/toolchain/sdbg_launcher.py index 8958c04..b6b2878 100644 --- a/toolchain/sdbg_launcher.py +++ b/toolchain/sdbg_launcher.py @@ -7,6 +7,7 @@ from contextlib import redirect_stdout import json import os from pathlib import Path +import re import shutil import signal import subprocess @@ -157,6 +158,10 @@ def main(): parser.add_argument('--build',required=True) parser.add_argument('--socket',required=True) parser.add_argument('--data',action='append',default=[]) + parser.add_argument('--app-hdd',default=None, + help='локальный CHD приложения для -hard2') + parser.add_argument('--launch-path',default=None, + help='путь к EXE в DSS, например d:\\games\\sprpop\\sprpop.exe') parser.add_argument('--launch-at',type=float,default=0, help='не начинать ввод раньше этой секунды эмуляции') parser.add_argument('--dss-timeout',type=float,default=30, @@ -181,8 +186,6 @@ def main(): if args.mame and not args.mame_bin: args.mame_bin=args.mame profile=from_arguments(args) - if profile.binary is None and (ROOT/'mame/v306/mame.arm').is_file(): - profile=MameProfile.resolve({'MAME_HOME':str(ROOT/'mame/v306')}) profile.validate(dss=True) mame=profile.binary stopping=False @@ -203,7 +206,15 @@ def main(): with redirect_stdout(sys.stderr): if not create_floppy_image(str(disk_path),files): raise RuntimeError('Не удалось создать debug-дискету') - command_text='a:\\'+basename_83(exe.name).replace(' ','')+'\n' + if args.launch_path: + launch_path=args.launch_path.replace('/', '\\') + if not re.fullmatch(r'[A-Za-z]:\\[A-Za-z0-9_.\\-]+',launch_path): + raise ValueError('launch-path: ожидается путь DOS вида d:\\dir\\app.exe') + if Path(launch_path.split('\\')[-1]).name.upper() != exe.name.upper(): + raise ValueError('launch-path должен заканчиваться именем debug EXE: '+exe.name) + command_text=launch_path+'\n' + else: + command_text='a:\\'+basename_83(exe.name).replace(' ','')+'\n' events=build_events([(0,command_text)]) ready=state/'main.json' lua=state/'launch.lua' @@ -215,9 +226,6 @@ def main(): installed_plugins=profile.home.parent/'plugins' if profile.home else None if installed_plugins and installed_plugins.is_dir(): plugin_paths.append(str(installed_plugins)) - legacy_plugins=ROOT/'mame/sources/MAME/plugins' - if legacy_plugins.is_dir(): - plugin_paths.append(str(legacy_plugins)) command=[str(mame),'sprinter','-noreadconfig','-rompath',str(profile.rompath), '-bios',profile.bios,'-kbd','ms_naturl,bios=sp2k','-video','soft','-window', '-sound','none','-skip_gameinfo','-beta:wd179x:0','35hd', @@ -226,6 +234,13 @@ def main(): '-debug','-debugger',args.debugger, '-plugin','sdbgbridge','-pluginspath',';'.join(plugin_paths), '-autoboot_delay','0','-autoboot_script',str(lua)] + if args.app_hdd: + app_hdd=Path(args.app_hdd).resolve() + if not app_hdd.is_file(): + raise ValueError('Не найден CHD приложения: '+str(app_hdd)) + mounted_app_hdd=state/'application.chd' + shutil.copyfile(app_hdd,mounted_app_hdd) + command.extend(['-hard2',str(mounted_app_hdd)]) for name in ('nvram','cfg','diff','snapshot'): command.extend(['-'+name+'_directory',str(state/name)]) log=(state/'mame.log').open('w') diff --git a/toolchain/size_check.py b/toolchain/size_check.py index 3316bb1..ca59336 100644 --- a/toolchain/size_check.py +++ b/toolchain/size_check.py @@ -13,9 +13,9 @@ docs/size_baseline.tsv. программы (нет в эталоне) — предупреждение, добавить через --update. Запускать после `make all`. -ТОЛЬКО tests/, без examples/: регресс ловит разжирение libc, а для этого +ТОЛЬКО tests/, без отдельного репозитория Examples: регресс ловит разжирение libc, а для этого хватает мелких тестов — каждый тянет свой кусок библиотеки и пересобирается -за секунды. Крупные приложения (examples/mdview компилируется минутами) +за секунды. Крупные приложения (`../Examples/mdview` компилируется минутами) не показывают ничего сверх этого и лишь удлиняют цикл, поэтому и из `make all` они убраны (см. корневой Makefile). """ diff --git a/toolchain/vscode-sprinter-debug/LICENSE b/toolchain/vscode-sprinter-debug/LICENSE deleted file mode 100644 index a738358..0000000 --- a/toolchain/vscode-sprinter-debug/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Sprinter C Compiler contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/toolchain/vscode-sprinter-debug/README.md b/toolchain/vscode-sprinter-debug/README.md deleted file mode 100644 index 2b9aa7c..0000000 --- a/toolchain/vscode-sprinter-debug/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# Sprinter MAME Debug - -VS Code-клиент сборки и отладки для `toolchain/sdbg_dap.py`. Режим `launch` сам -поднимает изолированные MAME и session server; перед `attach` их запускают -вручную. - -> [!WARNING] -> Native Windows пока не поддерживает полный launch/attach: `windows` -> выбирает только debugger provider MAME, а host-часть всё ещё зависит от -> `fcntl`, Unix domain sockets и Unix launcher. - -Для разработки расширение можно открыть отдельным окном VS Code и запустить -Extension Development Host. Конфигурация проекта: - -```sh -code --extensionDevelopmentPath="$PWD/toolchain/vscode-sprinter-debug" "$PWD" -``` - -Python выбирается без зависимости от `PATH` GUI: при наличии local -`.python-version` используется `~/.pyenv/shims/python`; для внешнего workspace -без неё расширение ищет установленный `~/.pyenv/versions/3.12*/bin/python`. -Версия local передаётся задаче через `PYENV_VERSION`, поэтому сборка работает -и если `project` лежит вне workspace. При необходимости задайте абсолютный путь через -`sprinterDebugger.pythonCommand`. Выбранные пути печатаются в канале Output -`Sprinter MAME Debug`. - -```json -{ - "type": "sprinter-mame", - "request": "launch", - "name": "Sprinter MAME: Launch", - "build": "${workspaceFolder}/tests/hello/.sprinter-cc-hello" -} -``` - -Перед F5 расширение по умолчанию находит Makefile проекта по пути `build` и -выполняет задачу `make SRC_DEBUG=1` в каталоге проекта через local Python 3.12. -Ошибки SDCC с файлом и строкой попадают в Problems. При ненулевом коде -сборки debug launch отменяется до старта MAME. Для нестандартной раскладки -укажите `"project": "${workspaceFolder}/path/to/app"`; для уже собранного -пакета можно задать `"autoBuild": false`. Явный `preLaunchTask` остаётся под -контролем стандартного механизма VS Code и отключает автоматическую задачу -расширения. - -Команда палитры `Sprinter: Build Active Project` строит проект открытого -C-файла. В `Tasks: Run Task` доступны задачи `Sprinter: Build ...` для -Makefile, включающих `app.mk`. Сборка не заменяет исходный `make` и не пишет -в общий образ дискеты MAME. - -По умолчанию patched backend `sdbg` не открывает отдельное окно debugger MAME. -Чтобы пользоваться им одновременно с VS Code, добавьте в launch: - -```json -"debugger": "osx" -``` - -Без project patch можно задать `auto`; явные native provider: `osx` на macOS, -`windows` в Windows, `qt` или `imgui` в Linux. Host-инструменты sdbg сейчас -рассчитаны на macOS/Linux (`fcntl`, Unix sockets и Unix launcher); native -Windows transport и end-to-end тесты ещё требуются. - -Готовность DSS определяется по стабильному prompt в VRAM. `dssTimeout` -задаёт таймаут (по умолчанию 30 секунд), а `launchAt` — необязательное самое -раннее время ввода команды. - -Ручной attach: - -```json -{ - "type": "sprinter-mame", - "request": "attach", - "name": "Sprinter MAME: Attach", - "socket": "/tmp/sprinter-sdbg.sock" -} -``` - -Сейчас доступны точки по исходнику/функции, `logMessage` с безопасными -подстановками `{variable}`, один проверенный frame, регистры, поддержанные -global/static, continue/pause и instruction step. Source step поддержан для -F10/F11/Shift+F11; банковский step-over и step-out проходят -служебные trampoline до следующей C-позиции. diff --git a/toolchain/vscode-sprinter-debug/build.js b/toolchain/vscode-sprinter-debug/build.js deleted file mode 100644 index c03a6ca..0000000 --- a/toolchain/vscode-sprinter-debug/build.js +++ /dev/null @@ -1,53 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -function absolutePath(value, workspace) { - if (!value || !workspace) return null; - const expanded = value.replace(/\$\{workspaceFolder\}/g, workspace); - return path.resolve(workspace, expanded); -} - -function projectForLaunch(configuration, workspace) { - const explicit = absolutePath(configuration.project, workspace); - if (explicit) { - if (!fs.existsSync(path.join(explicit, 'Makefile'))) { - throw new Error(`Нет Makefile в каталоге проекта: ${explicit}`); - } - return explicit; - } - const build = absolutePath(configuration.build, workspace); - if (!build) return null; - let directory = path.dirname(build); - while (directory !== path.dirname(directory)) { - if (fs.existsSync(path.join(directory, 'Makefile'))) { - return directory; - } - if (directory === workspace) break; - directory = path.dirname(directory); - } - return null; -} - -function isSprinterMakefile(filename) { - try { - return /include\s+(?:\$\((?:PROJ_ROOT|SPRINTER_ROOT)\)\/app\.mk|\$\(CURDIR\)\/(?:\.\.\/){0,2}sdk\.mk)/.test( - fs.readFileSync(filename, 'utf8')); - } catch (_) { - return false; - } -} - -function taskLabel(project, workspace) { - const relative = path.relative(workspace, project); - return `Sprinter: Build ${relative || '.'}`; -} - -function makeCommand() { - if (process.platform === 'win32') { - throw new Error('Native Windows build/debug пока не поддерживается'); - } - return fs.existsSync('/usr/bin/make') ? '/usr/bin/make' : 'make'; -} - -module.exports = {absolutePath, projectForLaunch, isSprinterMakefile, - taskLabel, makeCommand}; diff --git a/toolchain/vscode-sprinter-debug/build.test.js b/toolchain/vscode-sprinter-debug/build.test.js deleted file mode 100644 index db6216d..0000000 --- a/toolchain/vscode-sprinter-debug/build.test.js +++ /dev/null @@ -1,40 +0,0 @@ -const assert = require('node:assert/strict'); -const path = require('node:path'); -const test = require('node:test'); -const {projectForLaunch, isSprinterMakefile, taskLabel} = - require('./build'); -const manifest = require('./package.json'); - -const workspace = path.resolve(__dirname, '..', '..'); -const hello = path.join(workspace, 'tests', 'hello'); - -test('launch находит Makefile рядом с debug-пакетом', () => { - assert.equal(projectForLaunch({ - build: '${workspaceFolder}/tests/hello/.sprinter-cc-hello', - }, workspace), hello); - assert.equal(taskLabel(hello, workspace), 'Sprinter: Build tests/hello'); - assert.equal(isSprinterMakefile(path.join(hello, 'Makefile')), true); -}); - -test('пакет в build/ находит Makefile приложения уровнем выше', () => { - const project = path.join(workspace, 'applications', 'SprPoP'); - assert.equal(projectForLaunch({ - build: '${workspaceFolder}/applications/SprPoP/build/.sprinter-cc-sprpop', - }, workspace), project); -}); - -test('явный project без Makefile даёт ошибку вместо stale запуска', () => { - assert.throws(() => projectForLaunch({ - project: 'tests/sdbg/fixtures', build: 'tests/hello/.sprinter-cc-hello', - }, workspace), /Нет Makefile/); -}); - -test('matcher разрешает относительную ошибку SDCC внутри проекта', () => { - const matcher = manifest.contributes.problemMatchers[0]; - const line = 'hello.c:62: error 20: Undefined identifier \'missing_name\''; - const match = new RegExp(matcher.pattern.regexp).exec(line); - assert.deepEqual(match?.slice(1), - ['hello.c', '62', 'error', '20', - "Undefined identifier 'missing_name'"]); - assert.equal(matcher.fileLocation, 'relative'); -}); diff --git a/toolchain/vscode-sprinter-debug/extension.js b/toolchain/vscode-sprinter-debug/extension.js deleted file mode 100644 index 5bc790a..0000000 --- a/toolchain/vscode-sprinter-debug/extension.js +++ /dev/null @@ -1,199 +0,0 @@ -const path = require('path'); -const vscode = require('vscode'); -const {resolvePython, pyenvEnvironment, resolveSdkRoot} = require('./runtime'); -const {projectForLaunch, isSprinterMakefile, taskLabel, makeCommand} = - require('./build'); - -class SprinterAdapterFactory { - constructor(output) { - this.output = output; - } - - createDebugAdapterDescriptor(session) { - const settings = vscode.workspace.getConfiguration('sprinterDebugger'); - const folder = session.workspaceFolder || vscode.workspace.workspaceFolders?.[0]; - const configured = session.configuration.adapterPath; - const sdk = configured ? null : resolveSdkRoot( - settings.get('sdkRoot'), folder?.uri.fsPath); - const adapter = configured || path.join(sdk, 'toolchain', 'sdbg_dap.py'); - const runtime = resolvePython({ - command: settings.get('pythonCommand', 'auto'), - args: settings.get('pythonArguments', []), - workspace: folder?.uri.fsPath, - }); - const cwd = folder?.uri.fsPath || path.dirname(adapter); - this.output.appendLine(`Python: ${runtime.command}`); - this.output.appendLine(`DAP: ${adapter}`); - const options = {cwd}; - const env = pyenvEnvironment(runtime.command, cwd); - if (env) options.env = env; - return new vscode.DebugAdapterExecutable( - runtime.command, [...runtime.args, adapter], options); - } -} - -class SprinterTaskProvider { - createTask(folder, project, definitionOverride) { - const settings = vscode.workspace.getConfiguration('sprinterDebugger'); - const runtime = resolvePython({ - command: settings.get('pythonCommand', 'auto'), - args: settings.get('pythonArguments', []), - workspace: folder.uri.fsPath, - }); - const relative = path.relative(folder.uri.fsPath, project); - const definition = definitionOverride || - {type: 'sprinter', project: relative || '.'}; - const options = {cwd: project}; - const sdk = resolveSdkRoot(settings.get('sdkRoot'), folder.uri.fsPath); - const env = {SPRINTER_ROOT: sdk, ...( - pyenvEnvironment(runtime.command, project) || - pyenvEnvironment(runtime.command, folder.uri.fsPath) || {})}; - if (env) options.env = env; - const task = new vscode.Task( - definition, folder, taskLabel(project, folder.uri.fsPath), 'sprinter', - new vscode.ProcessExecution(makeCommand(), [ - 'SRC_DEBUG=1', `PYTHON=${runtime.command}`, - ], options), ['$sprinter-sdcc']); - task.group = vscode.TaskGroup.Build; - return task; - } - - async provideTasks() { - const folders = vscode.workspace.workspaceFolders || []; - const results = []; - for (const folder of folders) { - const files = await vscode.workspace.findFiles( - new vscode.RelativePattern(folder, '**/Makefile'), - '**/{third_party,mame,libc,libbgi,toolchain}/**'); - for (const file of files) { - if (isSprinterMakefile(file.fsPath)) { - results.push(this.createTask(folder, path.dirname(file.fsPath))); - } - } - } - return results; - } - - resolveTask(task) { - const folder = task.scope?.uri ? task.scope : - vscode.workspace.workspaceFolders?.[0]; - const project = task.definition.project; - if (!folder || typeof project !== 'string') return undefined; - const directory = path.resolve(folder.uri.fsPath, project); - if (!isSprinterMakefile(path.join(directory, 'Makefile'))) return undefined; - return this.createTask(folder, directory, task.definition); - } -} - -async function runBuildTask(task) { - const early = [], ended = []; - let execution, resolveResult, completed = false; - const result = new Promise(resolve => {resolveResult = resolve;}); - function finish(code) { - if (!completed) { - completed = true; - resolveResult(code); - } - } - const processListener = vscode.tasks.onDidEndTaskProcess(event => { - early.push(event); - if (execution && event.execution === execution) finish(event.exitCode); - }); - const endListener = vscode.tasks.onDidEndTask(event => { - ended.push(event); - if (execution && event.execution === execution) { - // При отмене задачи process exit event может не появиться. - setTimeout(() => finish(undefined), 100); - } - }); - try { - execution = await vscode.tasks.executeTask(task); - const finished = early.find(event => event.execution === execution); - if (finished) finish(finished.exitCode); - if (ended.some(event => event.execution === execution)) { - setTimeout(() => finish(undefined), 100); - } - return await result; - } finally { - processListener.dispose(); - endListener.dispose(); - } -} - -class SprinterConfigurationProvider { - constructor(tasks, output) { - this.tasks = tasks; - this.output = output; - } - - async resolveDebugConfiguration(folder, configuration) { - if (configuration.type !== 'sprinter-mame' || - configuration.request !== 'launch' || - configuration.autoBuild === false || configuration.preLaunchTask) { - return configuration; - } - const workspace = folder?.uri.fsPath || - vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; - if (!workspace) return configuration; - const mameHome = vscode.workspace.getConfiguration('sprinterDebugger') - .get('mameHome'); - if (!configuration.mameHome && mameHome) configuration.mameHome = mameHome; - try { - const project = projectForLaunch(configuration, workspace); - if (project && isSprinterMakefile(path.join(project, 'Makefile'))) { - const scope = folder || vscode.workspace.workspaceFolders[0]; - const task = this.tasks.createTask(scope, project); - this.output.appendLine(`Build: ${project}`); - const code = await runBuildTask(task); - if (code !== 0) { - vscode.window.showErrorMessage( - `Сборка Sprinter не прошла (код ${code ?? 'отмена'}); MAME не запущен`); - return undefined; - } - } - } catch (error) { - vscode.window.showErrorMessage(`Sprinter Build: ${error.message}`); - return undefined; - } - return configuration; - } -} - -function activate(context) { - const output = vscode.window.createOutputChannel('Sprinter MAME Debug'); - context.subscriptions.push(output); - context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory( - 'sprinter-mame', new SprinterAdapterFactory(output))); - const tasks = new SprinterTaskProvider(); - context.subscriptions.push(vscode.tasks.registerTaskProvider('sprinter', tasks)); - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider( - 'sprinter-mame', new SprinterConfigurationProvider(tasks, output))); - context.subscriptions.push(vscode.commands.registerCommand('sprinter.buildActive', async () => { - const editor = vscode.window.activeTextEditor; - const folder = editor && vscode.workspace.getWorkspaceFolder(editor.document.uri); - if (!editor || !folder) { - vscode.window.showErrorMessage('Откройте C-файл проекта Sprinter'); - return; - } - let directory = path.dirname(editor.document.uri.fsPath); - const root = folder.uri.fsPath; - while (directory !== path.dirname(directory)) { - if (isSprinterMakefile(path.join(directory, 'Makefile'))) { - try { - await vscode.tasks.executeTask(tasks.createTask(folder, directory)); - } catch (error) { - vscode.window.showErrorMessage(`Sprinter Build: ${error.message}`); - } - return; - } - if (directory === root) break; - directory = path.dirname(directory); - } - vscode.window.showErrorMessage('Не найден проект Sprinter с app.mk'); - })); -} - -function deactivate() {} - -module.exports = {activate, deactivate, SprinterTaskProvider, - SprinterConfigurationProvider, runBuildTask}; diff --git a/toolchain/vscode-sprinter-debug/extension.test.js b/toolchain/vscode-sprinter-debug/extension.test.js deleted file mode 100644 index 7f75d95..0000000 --- a/toolchain/vscode-sprinter-debug/extension.test.js +++ /dev/null @@ -1,134 +0,0 @@ -const assert = require('node:assert/strict'); -const fs = require('node:fs'); -const path = require('node:path'); -const test = require('node:test'); -const vm = require('node:vm'); - -test('F5 выполняет TaskProvider build и не запускает MAME после ошибки', async () => { - let factory, configurationProvider, taskProvider; - let onProcess, onEnd, exitCode = 0; - const errors = []; - const output = {appendLine() {}, dispose() {}}; - class DebugAdapterExecutable { - constructor(command, args, options) { - Object.assign(this, {command, args, options}); - } - } - class ProcessExecution { - constructor(command, args, options) { - Object.assign(this, {command, args, options}); - } - } - class Task { - constructor(definition, scope, name, source, execution, problemMatchers) { - Object.assign(this, {definition, scope, name, source, - execution, problemMatchers}); - } - } - class RelativePattern { - constructor(folder, pattern) {Object.assign(this, {folder, pattern});} - } - const vscode = { - workspace: { - workspaceFolders: [], - getConfiguration: () => ({get: (key, fallback) => fallback}), - findFiles: async () => [], - }, - window: {createOutputChannel: () => output, - showErrorMessage: message => errors.push(message)}, - debug: { - registerDebugAdapterDescriptorFactory: (type, value) => { - assert.equal(type, 'sprinter-mame'); - factory = value; - return {dispose() {}}; - }, - registerDebugConfigurationProvider: (type, value) => { - assert.equal(type, 'sprinter-mame'); - configurationProvider = value; - return {dispose() {}}; - }, - }, - tasks: { - registerTaskProvider: (type, value) => { - assert.equal(type, 'sprinter'); - taskProvider = value; - return {dispose() {}}; - }, - onDidEndTaskProcess: listener => { - onProcess = listener; - return {dispose() {onProcess = null;}}; - }, - onDidEndTask: listener => { - onEnd = listener; - return {dispose() {onEnd = null;}}; - }, - executeTask: async task => { - const execution = {task}; - onProcess({execution, exitCode}); - onEnd({execution}); - return execution; - }, - }, - commands: {registerCommand: (name) => { - assert.equal(name, 'sprinter.buildActive'); - return {dispose() {}}; - }}, - DebugAdapterExecutable, - ProcessExecution, Task, RelativePattern, - TaskGroup: {Build: 'build'}, - }; - const source = fs.readFileSync(path.join(__dirname, 'extension.js'), 'utf8'); - const module = {exports: {}}; - vm.runInNewContext(source, { - require: name => name === 'vscode' ? vscode : require(name), - module, setTimeout, - }, {filename: 'extension.js'}); - const context = {subscriptions: []}; - module.exports.activate(context); - assert.equal(context.subscriptions.length, 5); - - const workspace = path.resolve(__dirname, '..', '..'); - const session = { - workspaceFolder: {uri: {fsPath: workspace}}, - configuration: {}, - }; - const descriptor = factory.createDebugAdapterDescriptor(session); - assert.equal(descriptor.command, - path.join(require('node:os').homedir(), '.pyenv', 'shims', 'python')); - assert.equal(descriptor.args.at(-1), - path.join(workspace, 'toolchain', 'sdbg_dap.py')); - assert.equal(descriptor.options.cwd, workspace); - assert.equal(descriptor.options.env.PYENV_VERSION, '3.12'); - const configuration = { - type: 'sprinter-mame', request: 'launch', - build: '${workspaceFolder}/tests/hello/.sprinter-cc-hello', - }; - assert.equal(await configurationProvider.resolveDebugConfiguration( - session.workspaceFolder, configuration), configuration, errors.join('\n')); - const task = taskProvider.createTask(session.workspaceFolder, - path.join(workspace, 'tests', 'hello')); - assert.equal(task.name, 'Sprinter: Build tests/hello'); - assert.equal(task.execution.command, '/usr/bin/make'); - assert.equal(task.execution.options.cwd, - path.join(workspace, 'tests', 'hello')); - assert.equal(task.execution.options.env.PYENV_VERSION, '3.12'); - assert.deepEqual(Array.from(task.execution.args), - ['SRC_DEBUG=1', `PYTHON=${descriptor.command}`]); - assert.equal(task.problemMatchers[0], '$sprinter-sdcc'); - vscode.workspace.workspaceFolders = [session.workspaceFolder]; - vscode.workspace.findFiles = async () => [ - {fsPath: path.join(workspace, 'tests', 'hello', 'Makefile')}, - {fsPath: path.join(workspace, 'Makefile')}, - ]; - const discovered = await taskProvider.provideTasks(); - assert.equal(discovered.length, 1); - assert.equal(discovered[0].name, task.name); - const unresolved = {scope: session.workspaceFolder, - definition: {type: 'sprinter', project: 'tests/hello'}}; - assert.equal(taskProvider.resolveTask(unresolved).definition, - unresolved.definition); - exitCode = 1; - assert.equal(await configurationProvider.resolveDebugConfiguration( - session.workspaceFolder, {...configuration}), undefined); - assert.match(errors.at(-1), /MAME не запущен/); -}); diff --git a/toolchain/vscode-sprinter-debug/package.json b/toolchain/vscode-sprinter-debug/package.json deleted file mode 100644 index bf36466..0000000 --- a/toolchain/vscode-sprinter-debug/package.json +++ /dev/null @@ -1,205 +0,0 @@ -{ - "name": "sprinter-mame-debug", - "displayName": "Sprinter MAME Debug", - "description": "Сборка и DAP-отладка C-приложений Sprinter в MAME", - "version": "0.2.0", - "publisher": "sprinter-c-compiler", - "engines": {"vscode": "^1.85.0"}, - "categories": ["Debuggers"], - "main": "./extension.js", - "activationEvents": ["onDebug", "onTaskType:sprinter", "onCommand:sprinter.buildActive"], - "contributes": { - "commands": [ - {"command": "sprinter.buildActive", "title": "Sprinter: Build Active Project"} - ], - "taskDefinitions": [ - { - "type": "sprinter", - "required": ["project"], - "properties": { - "project": { - "type": "string", - "description": "Путь к каталогу с Makefile приложения относительно workspace" - } - } - } - ], - "problemMatchers": [ - { - "name": "sprinter-sdcc", - "owner": "sprinter-sdcc", - "fileLocation": "relative", - "pattern": { - "regexp": "^(.+?):(\\d+):\\s+(error|warning)\\s+(\\d+):\\s+(.+)$", - "file": 1, - "line": 2, - "severity": 3, - "code": 4, - "message": 5 - } - } - ], - "configuration": { - "title": "Sprinter MAME Debug", - "properties": { - "sprinterDebugger.pythonCommand": { - "type": "string", - "default": "auto", - "description": "Python 3.12 для DAP; auto выбирает local pyenv shim без зависимости от PATH GUI" - }, - "sprinterDebugger.pythonArguments": { - "type": "array", - "items": {"type": "string"}, - "default": [], - "description": "Аргументы пользовательской Python-команды перед путём DAP-адаптера" - }, - "sprinterDebugger.sdkRoot": { - "type": "string", - "default": "", - "description": "Путь к Sprinter-CC для внешнего проекта; можно задать SPRINTER_ROOT" - }, - "sprinterDebugger.mameHome": { - "type": "string", - "default": "", - "description": "Подготовленная среда MAME для launch, если не указана в launch.json" - } - } - }, - "debuggers": [ - { - "type": "sprinter-mame", - "label": "Sprinter MAME", - "languages": ["c"], - "configurationAttributes": { - "launch": { - "required": ["build"], - "properties": { - "build": { - "type": "string", - "description": "Путь к каталогу .sprinter-cc-NAME" - }, - "project": { - "type": "string", - "description": "Каталог с Makefile для сборки перед запуском; определяется из build, если не задан" - }, - "autoBuild": { - "type": "boolean", - "default": true, - "description": "Добавить Sprinter build task перед F5, когда найден app.mk" - }, - "data": { - "type": "array", - "items": {"type": "string"}, - "description": "Дополнительные файлы на debug-дискету" - }, - "mame": { - "type": "string", - "description": "Совместимый alias пути к mame.arm" - }, - "mameHome": { - "type": "string", - "description": "Среда MAME: стандартные пути к бинарнику, ROM и DSS" - }, - "mameBin": { - "type": "string", - "description": "Переопределить бинарник MAME (stock или sdbg)" - }, - "mameRompath": { - "type": "string", - "description": "Переопределить каталог ROM/BIOS MAME" - }, - "mameDssImage": { - "type": "string", - "description": "Переопределить образ DSS-дискеты" - }, - "mameSystemHddImage": { - "type": "string", - "description": "Переопределить системный HDD-образ Sprinter" - }, - "mameBios": { - "type": "string", - "default": "v3.06", - "description": "Имя варианта BIOS для -bios" - }, - "debugger": { - "type": "string", - "default": "sdbg", - "enum": ["sdbg", "auto", "osx", "windows", "qt", "imgui"], - "enumDescriptions": [ - "Только окно Sprinter и управление из VS Code", - "Выбрать доступный штатный backend MAME", - "VS Code вместе с Cocoa debugger MAME на macOS", - "VS Code вместе с native debugger MAME на Windows", - "Qt debugger MAME, если сборка включает USE_QTDEBUG", - "Debugger MAME внутри основного графического окна" - ], - "description": "OSD debugger provider; sdbg не открывает Cocoa debugger" - }, - "launchAt": { - "type": "number", - "default": 0, - "description": "Не начинать ввод раньше этой секунды эмуляции" - }, - "dssTimeout": { - "type": "number", - "default": 30, - "description": "Таймаут появления стабильного prompt DSS" - } - } - }, - "attach": { - "required": ["socket"], - "properties": { - "socket": { - "type": "string", - "description": "Unix socket запущенного sdbg_server.py" - }, - "adapterPath": { - "type": "string", - "description": "Путь к toolchain/sdbg_dap.py; по умолчанию из workspace" - } - } - } - }, - "configurationSnippets": [ - { - "label": "Sprinter MAME: Launch", - "description": "Запустить изолированный MAME и остановиться на main", - "body": { - "type": "sprinter-mame", - "request": "launch", - "name": "Sprinter MAME: Launch", - "build": "^\"${workspaceFolder}/tests/hello/.sprinter-cc-hello\"" - } - }, - { - "label": "Sprinter MAME: Launch + native debugger", - "description": "Запустить VS Code debugger вместе с Cocoa debugger MAME", - "body": { - "type": "sprinter-mame", - "request": "launch", - "name": "Sprinter MAME: Launch + native debugger", - "build": "^\"${workspaceFolder}/tests/hello/.sprinter-cc-hello\"", - "debugger": "osx" - } - }, - { - "label": "Sprinter MAME: Attach", - "description": "Подключиться к проверенной sdbg-сессии", - "body": { - "type": "sprinter-mame", - "request": "attach", - "name": "Sprinter MAME: Attach", - "socket": "^\"/tmp/sprinter-sdbg.sock\"" - } - } - ] - } - ] - }, - "scripts": { - "check": "node --check extension.js && node --check build.js && node --test runtime.test.js build.test.js extension.test.js" - }, - "files": ["extension.js", "runtime.js", "build.js", "README.md"], - "license": "MIT" -} diff --git a/toolchain/vscode-sprinter-debug/runtime.js b/toolchain/vscode-sprinter-debug/runtime.js deleted file mode 100644 index 7deab08..0000000 --- a/toolchain/vscode-sprinter-debug/runtime.js +++ /dev/null @@ -1,97 +0,0 @@ -const fs = require('fs'); -const os = require('os'); -const path = require('path'); - -function executable(filename) { - try { - fs.accessSync(filename, fs.constants.X_OK); - return true; - } catch (_) { - return false; - } -} - -function localPythonVersion(workspace) { - if (!workspace) return null; - let directory = path.resolve(workspace); - while (directory !== path.dirname(directory)) { - try { - const version = fs.readFileSync(path.join(directory, '.python-version'), - 'utf8').trim().split(/\s+/)[0]; - return /^[A-Za-z0-9._-]+$/.test(version) ? version : null; - } catch (_) {} - directory = path.dirname(directory); - } - return null; -} - -function installedPyenvPythons(home) { - try { - const versions = fs.readdirSync(path.join(home, '.pyenv', 'versions')); - return versions.filter(version => /^3\.12(?:\.\d+)?$/.test(version)) - .sort((a, b) => Number(b.split('.')[2] || 0) - - Number(a.split('.')[2] || 0)) - .map(version => path.join(home, '.pyenv', 'versions', version, - 'bin', 'python')); - } catch (_) { - return []; - } -} - -function resolvePython(options = {}) { - const command = String(options.command || 'auto').trim(); - const args = Array.isArray(options.args) ? options.args : []; - if (command !== 'auto') { - return {command, args}; - } - - const home = options.home || os.homedir(); - const workspace = options.workspace; - const isExecutable = options.isExecutable || executable; - const candidates = []; - const shim = path.join(home, '.pyenv', 'shims', 'python'); - const version = options.localVersion === undefined ? - localPythonVersion(workspace) : options.localVersion; - const installed = options.installedPyenv === undefined ? - installedPyenvPythons(home) : options.installedPyenv; - if (version) candidates.push(shim); - candidates.push(...installed); - if (!version) candidates.push(shim); - if (workspace) { - candidates.push(path.join(workspace, '.venv', 'bin', 'python')); - } - candidates.push( - '/opt/homebrew/bin/python3.12', - '/usr/local/bin/python3.12', - '/usr/bin/python3.12', - ); - const found = candidates.find(isExecutable); - if (!found) { - throw new Error( - 'Не найден Python 3.12. Задайте абсолютный путь в ' + - 'sprinterDebugger.pythonCommand.'); - } - return {command: found, args: []}; -} - -function pyenvEnvironment(command, workspace, home = os.homedir()) { - const shim = path.join(home, '.pyenv', 'shims', 'python'); - const version = command === shim ? localPythonVersion(workspace) : null; - return version ? {PYENV_VERSION: version} : undefined; -} - -function resolveSdkRoot(configured, workspace, environment = process.env) { - const candidates = [configured, environment.SPRINTER_ROOT, workspace] - .filter(Boolean).map(value => path.resolve(value)); - const found = candidates.find(root => - fs.existsSync(path.join(root, 'toolchain', 'sdbg_dap.py')) && - fs.existsSync(path.join(root, 'bin', 'sprinter-cc'))); - if (!found) { - throw new Error('Не найден Sprinter-CC: задайте sprinterDebugger.sdkRoot ' + - 'или SPRINTER_ROOT'); - } - return found; -} - -module.exports = {resolvePython, localPythonVersion, installedPyenvPythons, - pyenvEnvironment, resolveSdkRoot}; diff --git a/toolchain/vscode-sprinter-debug/runtime.test.js b/toolchain/vscode-sprinter-debug/runtime.test.js deleted file mode 100644 index 5ead0bd..0000000 --- a/toolchain/vscode-sprinter-debug/runtime.test.js +++ /dev/null @@ -1,67 +0,0 @@ -const assert = require('node:assert/strict'); -const path = require('node:path'); -const test = require('node:test'); -const {resolvePython, localPythonVersion, pyenvEnvironment} = require('./runtime'); - -test('auto использует pyenv shim без PATH GUI', () => { - const home = path.join(path.sep, 'Users', 'tester'); - const shim = path.join(home, '.pyenv', 'shims', 'python'); - const result = resolvePython({ - command: 'auto', - workspace: path.join(home, 'project'), - home, - isExecutable: filename => filename === shim, - }); - assert.deepEqual(result, {command: shim, args: []}); -}); - -test('local pyenv имеет приоритет над случайным .venv', () => { - const home = path.join(path.sep, 'Users', 'tester'); - const workspace = path.join(home, 'project'); - const shim = path.join(home, '.pyenv', 'shims', 'python'); - const venv = path.join(workspace, '.venv', 'bin', 'python'); - const result = resolvePython({ - command: 'auto', workspace, home, - isExecutable: filename => filename === shim || filename === venv, - }); - assert.equal(result.command, shim); -}); - -test('явная команда и аргументы сохраняются', () => { - const result = resolvePython({ - command: '/python/custom', - args: ['-I'], - isExecutable: () => false, - }); - assert.deepEqual(result, {command: '/python/custom', args: ['-I']}); -}); - -test('auto сообщает понятную ошибку без Python 3.12', () => { - assert.throws( - () => resolvePython({command: 'auto', isExecutable: () => false}), - /Задайте абсолютный путь/, - ); -}); - -test('внешний workspace без .python-version выбирает установленный pyenv 3.12', () => { - const home = path.join(path.sep, 'Users', 'tester'); - const installed = path.join(home, '.pyenv', 'versions', '3.12.14', - 'bin', 'python'); - const result = resolvePython({ - command: 'auto', workspace: '/tmp/external-project', home, - localVersion: null, installedPyenv: [installed], - isExecutable: filename => filename === installed || - filename === path.join(home, '.pyenv', 'shims', 'python'), - }); - assert.equal(result.command, installed); -}); - -test('shim получает версию workspace при сборке за пределами его дерева', () => { - const workspace = path.resolve(__dirname, '..', '..'); - const version = localPythonVersion(workspace); - assert.match(version, /^3\.12/); - const shim = path.join(require('node:os').homedir(), '.pyenv', - 'shims', 'python'); - assert.deepEqual(pyenvEnvironment(shim, workspace), - {PYENV_VERSION: version}); -});