#!/usr/bin/env python3 """ Parser dla plików .mar (MultiArray) z gier Reksio """ import struct import sys from pathlib import Path class MultiArrayParser: def __init__(self, filepath): self.filepath = Path(filepath) self.dimensions = [] self.total_elements = 0 self.data = {} # sparse dictionary: index -> value def read_int(self, f): """Czyta int32 little-endian""" return struct.unpack('= self.total_elements: print(f"WARNING: Index out of bounds: {flat_index}") break # Czytaj zmienną var_type, var_value = self.read_variable(f) # Zapisz w sparse dictionary self.data[flat_index] = (var_type, var_value) loaded_count += 1 except struct.error: pass # Koniec pliku print(f"Loaded {loaded_count}/{self.total_elements} elements ({100*loaded_count/self.total_elements:.1f}% filled)") def print_summary(self): """Wyświetla podsumowanie""" print("\n" + "="*80) print(f"File: {self.filepath.name}") print(f"Dimensions: {len(self.dimensions)}D array") print(f"Shape: {' × '.join(map(str, self.dimensions))}") print(f"Total slots: {self.total_elements}") print(f"Filled slots: {len(self.data)}") print(f"Empty slots: {self.total_elements - len(self.data)}") print(f"Fill rate: {100*len(self.data)/self.total_elements:.1f}%") print("="*80) def print_data(self, max_items=50): """Wyświetla dane""" print("\nData:") print("-" * 80) if not self.data: print(" (empty)") return for i, (flat_index, (var_type, var_value)) in enumerate(sorted(self.data.items())): if i >= max_items: remaining = len(self.data) - max_items print(f" ... and {remaining} more items") break indices = self.flat_to_indices(flat_index) indices_str = '[' + ']['.join(map(str, indices)) + ']' # Formatuj wartość if var_type == 'STRING': value_str = f'"{var_value}"' elif var_type == 'BOOL': value_str = 'true' if var_value else 'false' else: value_str = str(var_value) print(f" {indices_str:20s} (flat: {flat_index:5d}) = {var_type:8s} {value_str}") def export_to_python(self, output_file=None): """Eksportuje do Pythona jako nested lists""" if output_file is None: output_file = self.filepath.with_suffix('.py') def create_nested_structure(dims): """Tworzy zagnieżdżoną strukturę list""" if len(dims) == 1: return [None] * dims[0] else: return [create_nested_structure(dims[1:]) for _ in range(dims[0])] def set_value(arr, indices, value): """Ustawia wartość w zagnieżdżonej strukturze""" for idx in indices[:-1]: arr = arr[idx] arr[indices[-1]] = value # Stwórz strukturę nested = create_nested_structure(self.dimensions) # Wypełnij danymi for flat_index, (var_type, var_value) in self.data.items(): indices = self.flat_to_indices(flat_index) set_value(nested, indices, (var_type, var_value)) # Zapisz do pliku with open(output_file, 'w', encoding='utf-8') as f: f.write(f"# Generated from {self.filepath.name}\n") f.write(f"# Dimensions: {self.dimensions}\n\n") f.write(f"data = {nested!r}\n") print(f"\nExported to: {output_file}") def main(): if len(sys.argv) < 2: print("Usage: mar_parser.py [--export] [--verbose] [--max-items N]") print() print("Options:") print(" --export Export to Python file") print(" --verbose Show all data items") print(" --max-items N Show max N items (default: 50)") sys.exit(1) filepath = sys.argv[1] export = '--export' in sys.argv verbose = '--verbose' in sys.argv max_items = 50 if '--max-items' in sys.argv: idx = sys.argv.index('--max-items') if idx + 1 < len(sys.argv): max_items = int(sys.argv[idx + 1]) if verbose: max_items = 999999 # Parsuj plik parser = MultiArrayParser(filepath) parser.parse() # Pokaż wyniki parser.print_summary() parser.print_data(max_items=max_items) # Eksportuj jeśli trzeba if export: parser.export_to_python() if __name__ == '__main__': main()