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 — to jest właściwa dokumentacja, tu tylko obsługa.

Start

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

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

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<n>.ARR lub SPELLS<n>.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

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<n>.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)
S
Description
No description provided
Readme
76 KiB
Languages
Python 78.4%
JavaScript 13.8%
CSS 6.6%
HTML 1.2%