"""Obiekt DATABASE silnika PIK — pliki .DTA. Wiersze rozdzielone CRLF, kolumny pionową kreską, brak nagłówka. Nazwy i typy kolumn żyją w definicji STRUCT w pliku .cnv — patrz ricsave.schema. Puste pole tekstowe zapisywane jest jako literalne NULL. """ from __future__ import annotations from pathlib import Path ENCODING = "cp1250" ROW_SEP = "\r\n" COL_SEP = "|" NULL = "NULL" class DtaError(ValueError): """Plik .DTA nie trzyma się formatu.""" class Table: """Tabela .DTA: lista wierszy, każdy to lista surowych stringów. Świadomie trzymamy surowe stringi — konwersję na int robi dopiero warstwa schematu, dzięki czemu zapis jest bajt-w-bajt identyczny gdy nic nie zmieniasz. """ def __init__(self, rows: list[list[str]], *, trailing_newline: bool = True) -> None: self.rows = rows # Pliki *_def.dta nie mają końcowego CRLF, te zapisane przez grę mają. self.trailing_newline = trailing_newline @property def width(self) -> int: return len(self.rows[0]) if self.rows else 0 @property def widths(self) -> set[int]: return {len(row) for row in self.rows} def check_width(self, expected: int, label: str = "tabela") -> None: """Waliduje szerokość wobec znanego schematu STRUCT.""" found = self.widths if found - {expected}: raise DtaError( f"{label}: oczekiwano {expected} kolumn, znaleziono {sorted(found)}" ) def __len__(self) -> int: return len(self.rows) def __getitem__(self, index: int) -> list[str]: return self.rows[index] def __repr__(self) -> str: return f"