diff --git a/project/thscoreboard/replays/kaitai_parsers/th06.py b/project/thscoreboard/replays/kaitai_parsers/th06.py index d6d79677..9f689acb 100644 --- a/project/thscoreboard/replays/kaitai_parsers/th06.py +++ b/project/thscoreboard/replays/kaitai_parsers/th06.py @@ -47,10 +47,43 @@ def _read(self): self.unknown_5 = self._io.read_u4le() self.stage_offsets = [] for i in range(7): - self.stage_offsets.append(self._io.read_u4le()) + self.stage_offsets.append(Th06.StageOffset(self._io, self, self._root)) + class StageOffset(KaitaiStruct): + def __init__(self, _io, _parent=None, _root=None): + self._io = _io + self._parent = _parent + self._root = _root if _root else self + self._read() + + def _read(self): + self.raw_offset = self._io.read_u4le() + + @property + def offset(self): + """Offset relative to decrypted file.""" + if hasattr(self, '_m_offset'): + return self._m_offset + + self._m_offset = (self.raw_offset - 15) + return getattr(self, '_m_offset', None) + + @property + def body(self): + if hasattr(self, '_m_body'): + return self._m_body + + if self.raw_offset != 0: + _pos = self._io.pos() + self._io.seek(self.offset) + self._m_body = Th06.Stage(self._io, self, self._root) + self._io.seek(_pos) + + return getattr(self, '_m_body', None) + + class Stage(KaitaiStruct): def __init__(self, _io, _parent=None, _root=None): self._io = _io @@ -68,25 +101,4 @@ def _read(self): self.rank = self._io.read_u1() - @property - def stages(self): - if hasattr(self, '_m_stages'): - return self._m_stages - - _pos = self._io.pos() - self._m_stages = [] - for i in range(7): - _on = self.file_header.stage_offsets[i] - if _on == 0: - self._m_stages.append(Th06.Dummy(self._io, self, self._root)) - else: - # the th06 decryptor only returns the decrypted data instead of the full file - # the game stores these stage offsets from the start of the file - # thus we must adjust them to account for this difference when reading the stage data - self._io.seek(self.file_header.stage_offsets[i]-15) - self._m_stages.append(Th06.Stage(self._io, self, self._root)) - - self._io.seek(_pos) - return getattr(self, '_m_stages', None) - diff --git a/project/thscoreboard/replays/replay_parsing.py b/project/thscoreboard/replays/replay_parsing.py index a6d5d055..5d15e73b 100644 --- a/project/thscoreboard/replays/replay_parsing.py +++ b/project/thscoreboard/replays/replay_parsing.py @@ -126,9 +126,9 @@ def _Parse06(rep_raw): rep_stages = [] enumerated_non_dummy_stages = [ - (i, _stage) - for i, _stage in enumerate(replay.stages) - if replay.file_header.stage_offsets[i] != 0 + (i, _stage_header.body) + for i, _stage_header in enumerate(replay.file_header.stage_offsets) + if _stage_header.body ] # TH06 stores stage data values from the start of the stage but score from the end for (i, current_stage), (j, next_stage) in zip( diff --git a/ref/threp-ksy/th06.ksy b/ref/threp-ksy/th06.ksy index a15f8a22..63459c80 100644 --- a/ref/threp-ksy/th06.ksy +++ b/ref/threp-ksy/th06.ksy @@ -5,16 +5,6 @@ meta: seq: - id: file_header type: file_header -instances: - stages: - pos: file_header.stage_offsets[_index] - 15 - type: - switch-on: file_header.stage_offsets[_index] - cases: - 0: dummy - _: stage - repeat: expr - repeat-expr: 7 types: dummy: doc: blank type @@ -43,9 +33,25 @@ types: - id: unknown_5 type: u4 - id: stage_offsets - type: u4 + type: stage_offset repeat: expr repeat-expr: 7 + stage_offset: + seq: + - id: raw_offset + doc: Raw offset, relative to encrypted file + type: u4 + instances: + offset: + doc: Offset relative to decrypted file + value: raw_offset - 15 + # See https://github.com/kaitai-io/kaitai_struct/issues/14 + # for an explanation of this pattern. + body: + pos: offset + type: stage + if: raw_offset != 0 + stage: seq: - id: score