Files
BlooMooTestScripts/dane/game/debug/arraytest/mar_parser.py
Patryk Gensch bb190c6937 Added files
2026-02-23 21:11:22 +01:00

242 lines
8.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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('<i', f.read(4))[0]
def read_double(self, f):
"""Czyta double jako int32/10000"""
raw = struct.unpack('<i', f.read(4))[0]
return raw / 10000.0
def read_bool(self, f):
"""Czyta bool jako byte"""
return struct.unpack('<?', f.read(1))[0]
def read_string(self, f):
"""Czyta string: [int32 length][bytes data]"""
length = self.read_int(f)
if length <= 0:
return ""
data = f.read(length)
# Usuń null terminatory
return data.decode('utf-8', errors='ignore').rstrip('\x00')
def read_variable(self, f):
"""Czyta zmienną: [int32 type][data]"""
data_type = self.read_int(f)
if data_type == 1: # INTEGER
value = self.read_int(f)
return ('INTEGER', value)
elif data_type == 2: # STRING
value = self.read_string(f)
return ('STRING', value)
elif data_type == 3: # BOOL
value = self.read_bool(f)
return ('BOOL', value)
elif data_type == 4: # DOUBLE
value = self.read_double(f)
return ('DOUBLE', value)
else:
raise ValueError(f"Unknown data type: {data_type}")
def flat_to_indices(self, flat_index):
"""Konwertuje flat index na wielowymiarowe indeksy"""
indices = []
remaining = flat_index
for i in range(len(self.dimensions) - 1, -1, -1):
indices.insert(0, remaining % self.dimensions[i])
remaining //= self.dimensions[i]
return indices
def indices_to_flat(self, indices):
"""Konwertuje wielowymiarowe indeksy na flat index"""
flat_index = 0
multiplier = 1
for i in range(len(self.dimensions) - 1, -1, -1):
flat_index += indices[i] * multiplier
multiplier *= self.dimensions[i]
return flat_index
def parse(self):
"""Parsuje plik .mar"""
with open(self.filepath, 'rb') as f:
# Czytaj liczbę wymiarów
dimensions_count = self.read_int(f)
print(f"Dimensions count: {dimensions_count}")
# Czytaj rozmiary wymiarów
self.total_elements = 1
for i in range(dimensions_count):
dim_size = self.read_int(f)
self.dimensions.append(dim_size)
self.total_elements *= dim_size
print(f" Dimension {i}: {dim_size}")
print(f"Total elements: {self.total_elements}")
print(f"Array shape: {self.dimensions}")
print()
# Czytaj elementy (sparse format)
loaded_count = 0
try:
while True:
# Sprawdź czy są jeszcze dane
pos = f.tell()
if f.read(1) == b'':
break
f.seek(pos)
# Czytaj indeks
flat_index = self.read_int(f)
if flat_index < 0 or flat_index >= 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 <file.mar> [--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()