Implement QuetzalWriter._generate_ifhd_chunk()
The IFhd chunk contains 13 bytes of metadata identifying the story and current execution state: - Release number (2 bytes) from header - Serial number (6 bytes) from header - Checksum (2 bytes) from header - Program counter (3 bytes) from CPU state This allows save files to be validated against the story file.
This commit is contained in:
parent
8097bbcf55
commit
0c6eadb0da
2 changed files with 83 additions and 7 deletions
|
|
@ -377,15 +377,40 @@ class QuetzalWriter:
|
|||
"""Return a chunk of type IFhd, containing metadata about the
|
||||
zmachine and story being played."""
|
||||
|
||||
### TODO: write this. payload must be *exactly* 13 bytes, even if
|
||||
### it means padding the program counter.
|
||||
mem = self._zmachine._mem
|
||||
|
||||
### Some old infocom games don't have checksums stored in header.
|
||||
### If not, generate it from the *original* story file memory
|
||||
### image and put it into this chunk. See ZMemory.generate_checksum().
|
||||
pass
|
||||
# Release number (2 bytes, big-endian) from header bytes 2-3
|
||||
release = mem.read_word(2)
|
||||
|
||||
return "0"
|
||||
# Serial number (6 bytes) from header bytes 0x12-0x17
|
||||
serial = bytes(mem[0x12:0x18])
|
||||
|
||||
# Checksum (2 bytes, big-endian) from header bytes 0x1C-0x1D
|
||||
checksum = mem.read_word(0x1C)
|
||||
|
||||
# Program counter (3 bytes, big-endian) - current PC
|
||||
pc = self._zmachine._cpu._program_counter
|
||||
|
||||
# Build the 13-byte chunk
|
||||
chunk_data = bytearray(13)
|
||||
|
||||
# Bytes 0-1: Release number
|
||||
chunk_data[0] = (release >> 8) & 0xFF
|
||||
chunk_data[1] = release & 0xFF
|
||||
|
||||
# Bytes 2-7: Serial number
|
||||
chunk_data[2:8] = serial
|
||||
|
||||
# Bytes 8-9: Checksum
|
||||
chunk_data[8] = (checksum >> 8) & 0xFF
|
||||
chunk_data[9] = checksum & 0xFF
|
||||
|
||||
# Bytes 10-12: Program counter (24-bit)
|
||||
chunk_data[10] = (pc >> 16) & 0xFF
|
||||
chunk_data[11] = (pc >> 8) & 0xFF
|
||||
chunk_data[12] = pc & 0xFF
|
||||
|
||||
return bytes(chunk_data)
|
||||
|
||||
def _generate_cmem_chunk(self):
|
||||
"""Return a compressed chunk of data representing the compressed
|
||||
|
|
|
|||
|
|
@ -784,6 +784,57 @@ class ZMachineComplexOpcodeTests(TestCase):
|
|||
self.cpu.op_restart()
|
||||
|
||||
|
||||
class QuetzalWriterTests(TestCase):
|
||||
"""Test suite for QuetzalWriter save functionality."""
|
||||
|
||||
def test_generate_ifhd_chunk(self):
|
||||
"""Test _generate_ifhd_chunk() produces correct 13-byte IFhd chunk."""
|
||||
from mudlib.zmachine.quetzal import QuetzalWriter
|
||||
|
||||
# Create a mock zmachine with known header values
|
||||
mock_zmachine = Mock()
|
||||
mock_zmachine._mem = MockMemory()
|
||||
|
||||
# Set header values in memory:
|
||||
# Bytes 2-3: Release number (0x1234)
|
||||
mock_zmachine._mem.write_word(0x02, 0x1234)
|
||||
# Bytes 0x12-0x17: Serial number (6 bytes: "860509")
|
||||
serial = b"860509"
|
||||
for i, byte in enumerate(serial):
|
||||
mock_zmachine._mem[0x12 + i] = byte
|
||||
# Bytes 0x1C-0x1D: Checksum (0xABCD)
|
||||
mock_zmachine._mem.write_word(0x1C, 0xABCD)
|
||||
|
||||
# Set program counter
|
||||
mock_cpu = Mock()
|
||||
mock_cpu._program_counter = 0x123456 # 24-bit PC
|
||||
mock_zmachine._cpu = mock_cpu
|
||||
|
||||
# Create writer and generate chunk
|
||||
writer = QuetzalWriter(mock_zmachine)
|
||||
chunk_data = writer._generate_ifhd_chunk()
|
||||
|
||||
# Verify chunk is exactly 13 bytes
|
||||
self.assertEqual(len(chunk_data), 13)
|
||||
|
||||
# Verify release number (bytes 0-1)
|
||||
self.assertEqual(chunk_data[0], 0x12)
|
||||
self.assertEqual(chunk_data[1], 0x34)
|
||||
|
||||
# Verify serial number (bytes 2-7)
|
||||
for i, expected in enumerate(serial):
|
||||
self.assertEqual(chunk_data[2 + i], expected)
|
||||
|
||||
# Verify checksum (bytes 8-9)
|
||||
self.assertEqual(chunk_data[8], 0xAB)
|
||||
self.assertEqual(chunk_data[9], 0xCD)
|
||||
|
||||
# Verify program counter (bytes 10-12)
|
||||
self.assertEqual(chunk_data[10], 0x12)
|
||||
self.assertEqual(chunk_data[11], 0x34)
|
||||
self.assertEqual(chunk_data[12], 0x56)
|
||||
|
||||
|
||||
# Note: ZObjectParser methods are tested through integration tests
|
||||
# with real story files, not unit tests with mock memory, as the
|
||||
# interaction with ZStringFactory makes mocking complex.
|
||||
|
|
|
|||
Loading…
Reference in a new issue