Skip to content

Commit

Permalink
Fix: JGRPP's NGRF chunk doesn't contain length indicators for fixed-s…
Browse files Browse the repository at this point in the history
…ize arrays
  • Loading branch information
TrueBrain committed Mar 30, 2024
1 parent 688b397 commit ecaa206
Showing 1 changed file with 42 additions and 5 deletions.
47 changes: 42 additions & 5 deletions bananas_api/new_upload/readers/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ def __init__(self):
# aren't influencing the histogram.
self._histogram_old = None

# To read JGRPP savegames in the best way, we need to know
# what features are enabled.
self._jgrpp_features = {}

def read(self, fp):
"""
Read savegame meta data.
Expand Down Expand Up @@ -150,8 +154,32 @@ def read(self, fp):
if ext_flags & 0x01:
size += reader.uint32(be=True) << 28

# JGRPP's feature-flags chunk
if tag == b"SLXI":
version = reader.uint32(be=True)
flags = reader.uint32(be=True)
features = reader.uint32(be=True)

if version != 0 or flags != 0:
raise ValidationException("Invalid savegame.")

for i in range(features):
feature_flags = reader.uint32(be=True)
feature_version = reader.uint16(be=True)
feature_name = reader.gamma_str().decode()

if feature_flags & 0x4: # XSCF_EXTRA_DATA_PRESENT
extra_data_length = reader.uint32(be=True)
reader.skip(extra_data_length)

if feature_flags & 0x8: # XSCF_CHUNK_ID_LIST_PRESENT
chunk_id_list_count = reader.uint32(be=True)
reader.skip(chunk_id_list_count * 4)

self._jgrpp_features[feature_name] = feature_version

# New savegame format, with the height of the tile in MAPH.
if tag == b"MAPH":
elif tag == b"MAPH":
self.histogram = [0] * 256
while size > 0:
data = reader.read(min(size, 8192))
Expand All @@ -160,7 +188,7 @@ def read(self, fp):
size -= min(size, 8192)

# Old savegame format, with the height of the tile in MAPT.
if tag == b"MAPT":
elif tag == b"MAPT":
self._histogram_old = [0] * 256
while size > 0:
data = reader.read(min(size, 8192))
Expand All @@ -169,7 +197,7 @@ def read(self, fp):
size -= min(size, 8192)

# JGRPP-based map chunk containing the height of the tile.
if tag == b"WMAP":
elif tag == b"WMAP":
self.histogram = [0] * 256

# First chunk is 8 bytes per tile for the whole map.
Expand All @@ -194,7 +222,7 @@ def read(self, fp):
reader.skip(min(size, 8192))
size -= min(size, 8192)

if tag in (
elif tag in (
b"MAPO",
b"MAP2",
b"M3LO",
Expand Down Expand Up @@ -339,7 +367,16 @@ def read_table(self, fields, reader):
size, _ = reader.gamma()
value = reader.read(size)
elif is_list:
count, _ = reader.gamma()
# Older versions of JGRPP didn't store the length for fixed-length arrays.
# As such, we inject the right sizes here. Later versions of JGRPP address
# that issue, in which case the table_newgrf_sl is no longer 1.
if self._jgrpp_features.get("table_newgrf_sl") == 1 and field_key == "ident.md5sum":
count = 16
elif self._jgrpp_features.get("table_newgrf_sl") == 1 and field_key == "param":
count = 0x80
else:
count, _ = reader.gamma()

value = [self.read_field(field_type, reader) for _ in range(count)]
else:
value = self.read_field(field_type, reader)
Expand Down

0 comments on commit ecaa206

Please sign in to comment.