# ricsave — edytor zapisów „Reksio i Czarodzieje" Narzędzie debugowe do zapisów gry z 2004 r. (silnik PIK, `PIKLIB8.dll`). Gra nie ma jednego pliku zapisu — stan to **76 plików na slot** w katalogu `common/`, kopiowanych plik-po-pliku. Ta biblioteka czyta oba formaty, waliduje niezmienniki i daje UI. Rozpoznanie formatu: **[docs/format-zapisow.md](docs/format-zapisow.md)** — to jest właściwa dokumentacja, tu tylko obsługa. ## Start ```bash python3 -m venv .venv && .venv/bin/pip install -e ".[dev]" export RIC_GAME="$HOME/Reksio/Reksio i Czarodzieje" ricsave info # przegląd slotów ricsave serve # edytor w przeglądarce (127.0.0.1:8765) ``` Rdzeń (`ricsave/`) nie ma żadnych zależności — serwer chodzi na `http.server` ze stdliba, front to trzy pliki statyczne bez frameworka. **Zrób kopię zanim cokolwiek zapiszesz:** `ricsave backup ~/kopia-common`. ## CLI ```bash ricsave info # które sloty zajęte, gdzie stoisz ricsave validate 1 # niezmienniki, które gra połyka po cichu ricsave gamesets 1 # GAME1.ARR z rozwiniętymi nazwami pól ricsave scene 1 GULDRYK --explain # tabela obiektów lokacji + legenda TYPE ricsave items 1 # ekwipunek ricsave dump 1 --key scene:PIWNICA # JSON ricsave diff 0 1 -v # co się różni między slotami ricsave set 0 GULDRYK KULA_SEN IPARAM0=1 # punktowa zmiana pola ricsave copy 1 0 # slot 1 → żywy stan (to samo co LOADGAME) ricsave newgame # reset żywego stanu z szablonów _DEF ``` `diff` jest tu główną bronią: zapisz przed i po jakiejś akcji w grze, porównaj sloty i od razu widzisz, które pole za nią odpowiada. ## Biblioteka ```python from ricsave import Game game = Game("~/Reksio/Reksio i Czarodzieje") slot = game.slot(0) # 0 = żywy stan, 1..4 = sloty menu slot.summary() slot.validate() # [] gdy OK scene = slot.scene("PIWNICA") i = scene.find("GARNEK") # po IDNAME scene.set(i, "IPARAM0", 1) slot.touch("scene:PIWNICA") slot.flush() # zapisuje tylko dotknięte pliki ``` ## Model slotów — jedyna rzecz, którą trzeba zrozumieć ``` slot 0 ←→ slot 1..4 ↑ ↑ | └─ cztery pozycje z menu gry └─ ŻYWY STAN bieżącej rozgrywki ``` Gra w trakcie grania czyta i pisze **wyłącznie slot 0**. Menu „Zapisz"/„Wczytaj" to zwykłe kopiowanie plików między 0 a n. Praktyczny wniosek: edycja slotu 3 nie zrobi nic, dopóki nie wczytasz go w grze (albo nie zrobisz `ricsave copy 3 0`). ## Czego pilnować przy edycji - **Nie dodawaj ani nie usuwaj wierszy `.DTA`.** Skrypty scen adresują je po indeksie (`DBOBJECTS^SELECT(10)`), więc przesunięcie rozjeżdża logikę. `ricsave validate` i UI blokują zmianę liczby wierszy. - **Za krótkie `GAME.ARR` lub `SPELLS.ARR` gra połknie po cichu** i zresetuje do domyślnych — nie zobaczysz błędu, tylko utratę pozycji. - **`EMPTY = 1` w `ITEMS` oznacza slot ZAJĘTY**, nie pusty. - Edytuj przy zamkniętej grze — `SAVEGAME` nadpisuje slot bez pytania. ## Testy ```bash RIC_GAME="$HOME/Reksio/Reksio i Czarodzieje" .venv/bin/python -m pytest ``` Kluczowe są testy korpusowe: przejeżdżają po wszystkich plikach `.ARR`/`.DTA` w `common/` i sprawdzają round-trip **bajt w bajt**. Jeśli parser i writer się rozjadą, edytor po cichu zepsułby zapis — to ta siatka bezpieczeństwa. ## Układ ``` ricsave/ arr.py ARRAY (.ARR/.SAV) — binarny, int32 + string dta.py DATABASE (.DTA) — CRLF + pionowa kreska catalog.py manifest slotu: 52 lokacje, 11 postaci, 6 minigier schema.py kolumny STRUCT + znaczenia pól ARR slot.py Game / Slot / widoki / operacje SAVEGAME-LOADGAME-NEWGAME cli.py webapp/ server.py http.server + JSON API static/ index.html, app.js, style.css docs/ format-zapisow.md ``` ## Co jeszcze nie jest zdekodowane - `INVEST.ARR` — karta śledztwa, ≥8 elementów (`INVESTIGATION.class:622`) - `IPARAM0/1/2` dla obiektów scen z `TYPE < 100` innym niż 2 — znaczą co innego w każdej lokacji, trzeba czytać `.cnv` sceny - Element 0 w `SETTINGS.ARR` (element 1 to głośność 0..800)