#!/usr/bin/env python3
"""Small GDR/XDVD SCSI command parser bridge for persistent sessions.

This is intentionally a narrow parser for the commands proven by v130-v134:
READ CAPACITY(10), MODE SENSE(10) page 3Eh, MODE SELECT(10),
MODE SELECT(6), READ DVD STRUCTURE, READ(10/12), XDVD read-map diagnostics, and selected host/OS probes
(TEST UNIT READY, INQUIRY, REQUEST SENSE, GET CONFIGURATION, READ TOC,
GET EVENT STATUS NOTIFICATION, READ DISC INFORMATION, and no-data control CDBs). It turns raw CDB scripts
into the command descriptors consumed by the persistent MN103S firmware session.
"""
from __future__ import annotations

import json, re
from pathlib import Path
from typing import Any

from gdr_media_model import GDRMediaModel
from hlds_profiles import load_device_profile

SECTION_RE = re.compile(r"^--- (?P<kind>Sending CDB|Received Data|Sending Data) \[(?P<label>[^\]]+)\] \((?P<n>\d+) bytes\) ---")
HEX_RE = re.compile(r"\b[0-9A-Fa-f]{2}\b")


def hx(data: bytes | bytearray | list[int]) -> str:
    return ' '.join(f'{int(b)&0xFF:02X}' for b in data)


def parse_hex_bytes(text: Any) -> bytes:
    if text is None:
        return b''
    if isinstance(text, (bytes, bytearray)):
        return bytes(text)
    if isinstance(text, list):
        return bytes((int(x, 0) if isinstance(x, str) else int(x)) & 0xFF for x in text)
    t = str(text).strip()
    if not t:
        return b''
    if ' ' not in t and ',' not in t and len(t) % 2 == 0:
        try:
            return bytes.fromhex(t)
        except ValueError:
            pass
    out=[]
    for tok in t.replace(',', ' ').split():
        if tok == '...':
            break
        if tok.lower().startswith('0x'):
            out.append(int(tok,0)&0xFF)
        else:
            out.append(int(tok,16)&0xFF)
    return bytes(out)


def be32(n: int) -> bytes:
    return int(n).to_bytes(4, 'big')


def be16(n: int) -> bytes:
    return int(n).to_bytes(2, 'big')


def make_read10_cdb(lba: int, blocks: int) -> bytes:
    if not (0 <= lba <= 0xFFFFFFFF):
        raise ValueError(f'READ(10) LBA out of range: {lba}')
    if not (1 <= blocks <= 0xFFFF):
        raise ValueError(f'READ(10) transfer length out of range: {blocks}')
    return bytes([0x28, 0x00]) + be32(lba) + bytes([0x00]) + be16(blocks) + bytes([0x00])


def parse_read10_specs(specs: list[str]) -> list[tuple[int,int]]:
    out=[]
    for spec in specs:
        text=str(spec).strip()
        if not text:
            continue
        if ':' in text:
            a,b=text.split(':',1)
            out.append((int(a,0), int(b,0)))
        else:
            out.append((int(text,0), 1))
    return out


def parse_unlock_log(path: Path) -> dict[tuple[str,str], bytes]:
    out={}
    lines=path.read_text(encoding='utf-8', errors='replace').splitlines()
    i=0
    while i < len(lines):
        m=SECTION_RE.match(lines[i].strip())
        if not m:
            i += 1
            continue
        kind,label,n=m.group('kind'),m.group('label'),int(m.group('n'))
        i += 1
        toks=[]
        while i < len(lines) and not lines[i].startswith('--- '):
            if lines[i].startswith('----------------'):
                i += 1
                break
            toks.extend(HEX_RE.findall(lines[i]))
            i += 1
        data=bytes(int(x,16) for x in toks[:n])
        if len(data)==n:
            out[(kind,label)] = data
    return out


class GDRScsiParser:
    def __init__(self, xdvd_json: Path, iso_path: str | Path | None = None, log_path: str | Path | None = None, *, read_map: str = 'windows', read_trace_path: str | Path | None = None, read_trace_limit: int = 0, device_profile: str | Path | dict[str, Any] | None = None):
        self.device_profile = load_device_profile(device_profile) if isinstance(device_profile, (str, Path)) else (device_profile or {})
        self.xdvd_json = Path(xdvd_json)
        self.sidecar = json.loads(self.xdvd_json.read_text(encoding='utf-8'))
        self.iso_path = Path(iso_path) if iso_path else None
        self.media_model = GDRMediaModel.from_paths(self.xdvd_json, self.iso_path, self.sidecar)
        if self.iso_path is None and self.media_model.iso_path is not None:
            self.iso_path = self.media_model.iso_path
        self.log_path = Path(log_path) if log_path else None
        self.log_sections = parse_unlock_log(self.log_path) if self.log_path and self.log_path.exists() else {}
        self.auth_stage = 'locked'
        self.read_map = str(read_map or 'windows').lower().replace('-', '_')
        self.read_trace_path = Path(read_trace_path) if read_trace_path else None
        self.read_trace_limit = int(read_trace_limit or 0)
        self.read_trace_count = 0
        self.last_read_info: dict[str, Any] = {}
        # v165 raw-preflight aliases are intentionally not promoted in v165.
        # Accuracy work should use firmware_window instrumentation or strict
        # physical/raw modes instead of host-LBA spoofing. These legacy names
        # are recognized only to fail direct rather than silently spoof.
        self._deprecated_raw_preflight_alias_modes = {
            'physical_raw_xdfs_probe', 'raw_full_xdfs_probe', 'option1_raw_xdfs_probe',
            'physical_raw_probe', 'raw_preflight_alias', 'raw_with_xdfs_probe',
            'option1_raw_probe', 'full_raw_probe'
        }
        if self.read_trace_path:
            self.read_trace_path.parent.mkdir(parents=True, exist_ok=True)
            self.read_trace_path.write_text('', encoding='utf-8')
        self._load_sidecar_maps()
        self.firmware_events: list[dict[str, Any]] = []
        self._record_firmware_event('init', detail='parser_created')

    def _layout(self) -> dict[str, Any]:
        # v165: centralize media facts in GDRMediaModel. Sidecar layout remains
        # as fallback metadata when a field has not been derived elsewhere.
        if hasattr(self, 'media_model') and self.media_model:
            return self.media_model.layout_dict()
        return (self.sidecar.get('layout') or self.sidecar.get('xbox_layout') or
                ((self.sidecar.get('image') or {}).get('layout') or {}))

    def media_model_summary(self) -> dict[str, Any]:
        return self.media_model.summary() if hasattr(self, 'media_model') else {}

    def default_read10_specs(self) -> list[tuple[int,int]]:
        layout = self._layout()
        candidates=[0,32]
        for key in ('game_output_start_lba','game_unlocked_source_start_lba','visible_start_lba'):
            val = layout.get(key)
            if val is None:
                continue
            try:
                n = int(val,0) if isinstance(val,str) else int(val)
            except Exception:
                continue
            if n not in candidates:
                candidates.append(n)
        return [(lba,1) for lba in candidates[:3]]

    def _load_sidecar_maps(self) -> None:
        states = self.sidecar.get('drive_states') or {}
        self.locked = states.get('locked') or {}
        self.unlocked = states.get('unlocked') or {}
        self.sidecar_cdb_responses: dict[bytes, tuple[str,bytes]] = {}
        for rec in self.sidecar.get('scsi_responses') or []:
            if not isinstance(rec, dict):
                continue
            cdb=parse_hex_bytes(rec.get('cdb_hex'))
            if not cdb:
                continue
            data=b''
            if rec.get('response_file'):
                rp=Path(str(rec.get('response_file')))
                if not rp.is_absolute():
                    rp=self.xdvd_json.parent / rp
                if not rp.exists():
                    raise FileNotFoundError(f'SCSI response file not found: {rp}')
                data=rp.read_bytes()
            elif rec.get('response_hex'):
                data=parse_hex_bytes(rec.get('response_hex'))
            if data:
                self.sidecar_cdb_responses[cdb]= (str(rec.get('name') or f'resp_{cdb[0]:02x}'), data)

    def _log_data(self, kind: str, label: str, *, required: bool = True) -> bytes:
        data=self.log_sections.get((kind,label), b'')
        if required and not data:
            raise ValueError(f'missing {kind} [{label}] in unlock log; needed by parser state {self.auth_stage}')
        return data

    def _mode_sense_3e_response(self) -> bytes:
        if self.auth_stage == 'after_chal1':
            return self._log_data('Received Data', 'Step 5: Mode Sense Verify 1')
        if self.auth_stage == 'after_chal2':
            return self._log_data('Received Data', 'Step 7: Mode Sense Verify 2')
        if self.auth_stage == 'unlocked':
            data = parse_hex_bytes(self.unlocked.get('mode_sense_3e_hex'))
            if data:
                return data
            data = self.media_model.mode_sense_3e_response('unlocked') if hasattr(self.media_model, 'mode_sense_3e_response') else b''
            if data:
                return data
            return b''
        data = parse_hex_bytes(self.locked.get('mode_sense_3e_hex'))
        if data:
            return data
        return self.media_model.mode_sense_3e_response('locked') if hasattr(self.media_model, 'mode_sense_3e_response') else b''

    def _read_capacity_10_response(self) -> bytes:
        # Prefer captured sidecar/profile exact bytes when available to preserve
        # the known-good handshake while allowing the corrected .media.json
        # drive_state_observations schema to be authoritative for new dumps.
        if self.auth_stage == 'unlocked':
            data = parse_hex_bytes(self.unlocked.get('read_capacity_10_hex'))
            if data:
                return data
            data = self.media_model.read_capacity_10_response('unlocked') if hasattr(self.media_model, 'read_capacity_10_response') else b''
            if data:
                return data
            sectors = self.media_model.current_capacity_sector_count('unlocked') if hasattr(self.media_model, 'current_capacity_sector_count') else (self.media_model.iso_sector_count or self.media_model.xbox_physical_sectors)
        else:
            data = parse_hex_bytes(self.locked.get('read_capacity_10_hex'))
            if data:
                return data
            data = self.media_model.read_capacity_10_response('locked') if hasattr(self.media_model, 'read_capacity_10_response') else b''
            if data:
                return data
            sectors = self.media_model.current_capacity_sector_count('locked') if hasattr(self.media_model, 'current_capacity_sector_count') else (self.media_model.visible_sector_count or self.media_model.iso_sector_count)
        last_lba = max(0, int(sectors or 0) - 1)
        return be32(last_lba) + be32(self.media_model.sector_size or 2048)

    def _set_auth_stage(self, stage: str, *, event: str, detail: str = '') -> None:
        old = self.auth_stage
        self.auth_stage = str(stage)
        if old != self.auth_stage:
            self._record_firmware_event(event, old_auth_stage=old, detail=detail)

    def _update_auth_from_mode_select(self, data: bytes) -> None:
        # Captured GDR unlock flow mode page 3Eh signatures. This remains a
        # host-bridge approximation, but v165 logs it as firmware/media state
        # movement rather than hiding it inside READ LBA aliases.
        # Step 4 challenge 1 contains ... D1 01 6B A2 E7 C8 ED ...
        # Step 6 challenge 2 contains bytes 10..14 = 00 00 01 00 00.
        # Step 8 partition unlock contains bytes 10..14 = 01 01 01 D1 01.
        if len(data) >= 15 and data[8] == 0x3E:
            if data[10:15] == bytes([0x01,0x01,0x01,0xD1,0x01]):
                self._set_auth_stage('unlocked', event='mode_select_partition_unlock', detail='mode_page_3e_step8_signature')
            elif data[10:15] == bytes([0x00,0x00,0x01,0x00,0x00]):
                self._set_auth_stage('after_chal2', event='mode_select_challenge2', detail='mode_page_3e_step6_signature')
            elif len(data) >= 20 and data[14] == 0x01 and data[15] == 0x6B:
                self._set_auth_stage('after_chal1', event='mode_select_challenge1', detail='mode_page_3e_step4_signature')


    def _device_identity(self) -> dict[str, Any]:
        ident = dict((self.device_profile or {}).get('identity') or {})
        # Preserve old known-good GDR identity when no external profile is loaded.
        ident.setdefault('inquiry_vendor', 'HL-DT-ST')
        ident.setdefault('inquiry_product', 'DVD-ROM GDR8050L')
        ident.setdefault('inquiry_revision', '0L23')
        ident.setdefault('peripheral_device_type', 5)
        ident.setdefault('removable', True)
        ident.setdefault('default_serial', 'MN103S-GDR8050L-EMU')
        return ident

    def _device_scsi_caps(self) -> dict[str, Any]:
        return dict((self.device_profile or {}).get('scsi_capabilities') or {})

    def _device_mmc_caps(self) -> dict[str, Any]:
        return dict((self.device_profile or {}).get('mmc_capabilities') or {})

    def _device_write_caps(self) -> dict[str, Any]:
        return dict((self.device_profile or {}).get('write_capabilities') or {})

    def _profile_supports(self, key: str, default: bool = False) -> bool:
        caps = self._device_scsi_caps()
        return bool(caps.get(key, default))

    def _is_mutating_media_cdb(self, cdb: bytes) -> bool:
        if not cdb:
            return False
        op = cdb[0]
        # MMC media-write/erase/session-mutating opcodes. Firmware-update
        # mutation remains separate (e.g. WRITE BUFFER / vendor commands).
        return op in (0x2A, 0xAA, 0x2E, 0xA1, 0x5B, 0x53, 0x04)

    def _mutating_media_response(self, cdb: bytes, *, data_out: bytes, tag: str, title: str) -> dict[str, str]:
        """Capture-only response for WRITE/BLANK/FORMAT/CLOSE style commands.

        v166 does not mutate backing ISO images by default. It classifies these
        commands so firmware-updater/burning experiments can capture payloads
        without pretending media was changed. Later session safety flags can opt
        into image mutation.
        """
        wcaps = self._device_write_caps()
        action = str(wcaps.get('default_write_action') or 'capture_and_check_condition')
        if cdb and cdb[0] in (0xA1, 0x04):
            action = str(wcaps.get('default_erase_action') or action)
        row = {'tag': tag, 'title': title, 'direction': 'out' if data_out else 'none', 'cdb_hex': hx(cdb)}
        if data_out:
            row['data_out_hex'] = hx(data_out)
        row['media_mutating_command'] = 'true'
        row['capture_only'] = 'true'
        row['policy_action'] = action
        if action in ('capture_and_success', 'success'):
            return row
        raise ValueError(f'{tag}: media-mutating command captured but not executed by default policy: {hx(cdb)}')


    def _inquiry_response(self, cdb: bytes) -> bytes:
        # Standard INQUIRY data plus a few VPD pages commonly requested by
        # Windows during iSCSI/optical-device enumeration. This is a host-facing
        # bridge profile, not a claim about every byte from physical firmware.
        evpd = bool(len(cdb) >= 2 and (cdb[1] & 0x01))
        page = cdb[2] if len(cdb) >= 3 else 0
        alloc = cdb[4] if len(cdb) >= 5 else 36
        ident = self._device_identity()
        vendor = str(ident.get('inquiry_vendor') or 'HL-DT-ST').encode('ascii', errors='replace')
        product = str(ident.get('inquiry_product') or 'DVD-ROM GDR8050L').encode('ascii', errors='replace')
        revision = str(ident.get('inquiry_revision') or '0L23').encode('ascii', errors='replace')
        if evpd:
            return self._clip_inquiry_alloc(self._inquiry_vpd_response(page), alloc)
        data = bytearray(36)
        data[0] = int(ident.get('peripheral_device_type', 5)) & 0x1F
        data[1] = 0x80 if ident.get('removable', True) else 0x00
        data[2] = 0x05  # SPC-3-ish version field for bridge probing
        data[3] = 0x32  # response data format
        data[4] = 31
        data[8:16] = vendor[:8].ljust(8, b' ')
        data[16:32] = product[:16].ljust(16, b' ')
        data[32:36] = revision[:4].ljust(4, b' ')
        return bytes(data[:alloc or len(data)])

    def _clip_inquiry_alloc(self, data: bytes, alloc: int) -> bytes:
        return data if not alloc else data[:alloc]

    def _inquiry_vpd_response(self, page: int) -> bytes:
        if page == 0x00:
            # Supported VPD pages.
            pages = bytes([0x00, 0x80, 0x83])
            return bytes([0x05, 0x00, 0x00, len(pages)]) + pages
        if page == 0x80:
            serial_text = str(self._device_identity().get('default_serial') or 'MN103S-GDR8050L-EMU')
            serial = serial_text.encode('ascii', errors='replace')
            return bytes([0x05, 0x80]) + len(serial).to_bytes(2, 'big') + serial
        if page == 0x83:
            ident_text = str(((self.device_profile or {}).get('bridge_defaults') or {}).get('target_name') or 'iqn.2026-06.local.mn103s:gdr8050l')
            ident = ident_text.encode('ascii', errors='replace')
            # Identification descriptor: binary code set, target-port designator.
            desc = bytes([0x01, 0x03, 0x00, len(ident)]) + ident
            return bytes([0x05, 0x83]) + len(desc).to_bytes(2, 'big') + desc
        # Unknown VPD page: return a legal empty page instead of failing attach.
        return bytes([0x05, page & 0xFF, 0x00, 0x00])

    def _request_sense_response(self, cdb: bytes) -> bytes:
        alloc = cdb[4] if len(cdb) >= 5 else 18
        data = bytes([0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
        return data[:alloc or len(data)]


    def _allocation_len(self, cdb: bytes, default: int = 0) -> int:
        if not cdb:
            return default
        op = cdb[0]
        if op in (0x03, 0x12, 0x1A):
            return cdb[4] if len(cdb) >= 5 else default
        if op in (0x43, 0x46, 0x4A, 0x51, 0x5A):
            return int.from_bytes(cdb[7:9], 'big') if len(cdb) >= 9 else default
        return default

    def _clip_alloc(self, data: bytes, cdb: bytes, default: int | None = None) -> bytes:
        alloc = self._allocation_len(cdb, len(data) if default is None else default)
        if alloc == 0:
            return data
        return data[:alloc]

    def _get_configuration_response(self, cdb: bytes) -> bytes:
        # Compact MMC-style GET CONFIGURATION response for a DVD-ROM class device.
        # Header: data length, reserved, current profile 0010h (DVD-ROM).
        features = bytearray()
        # Profile List feature 0000h, current bit set, with CD-ROM and DVD-ROM profiles.
        profile_list = bytearray()
        profile_list += bytes([0x00, 0x08, 0x00, 0x00])  # CD-ROM profile, not current
        profile_list += bytes([0x00, 0x10, 0x01, 0x00])  # DVD-ROM profile, current
        features += bytes([0x00, 0x00, 0x03, len(profile_list)]) + profile_list
        # Core feature 0001h, persistent/current, no extra payload in this bridge profile.
        features += bytes([0x00, 0x01, 0x03, 0x00])
        # Removable Medium feature 0003h, persistent/current, tray-style byte payload.
        features += bytes([0x00, 0x03, 0x03, 0x04, 0x29, 0x00, 0x00, 0x00])
        # Random Readable feature 0010h, persistent/current, 2048-byte blocks.
        features += bytes([0x00, 0x10, 0x03, 0x08]) + bytes([0x00,0x00,0x08,0x00, 0x00,0x00,0x00,0x00])
        body_len = 4 + len(features)
        data = bytearray(8)
        data[0:4] = int(body_len).to_bytes(4, 'big')
        data[6:8] = bytes([0x00, 0x10])
        data += features
        return self._clip_alloc(bytes(data), cdb)

    def _read_toc_response(self, cdb: bytes) -> bytes:
        # Minimal format-0 TOC: first track 1, last track 1, track 1 starts at 0,
        # lead-out derived from the currently visible sector count when available.
        leadout = self._drive_visible_count(self.auth_stage) or 0
        desc1 = bytes([0x00, 0x14, 0x01, 0x00]) + (0).to_bytes(4, 'big')
        desc2 = bytes([0x00, 0x14, 0xAA, 0x00]) + max(0, leadout).to_bytes(4, 'big')
        body = bytes([0x01, 0x01]) + desc1 + desc2
        data = len(body).to_bytes(2, 'big') + body
        return self._clip_alloc(data, cdb)

    def _get_event_status_notification_response(self, cdb: bytes) -> bytes:
        # No pending events, media class supported. 8-byte notification header.
        # This is a benign host-probe bridge response.
        data = bytes([0x00,0x06, 0x00, 0x10, 0x00,0x00,0x00,0x00])
        return self._clip_alloc(data, cdb)

    def _read_disc_information_response(self, cdb: bytes) -> bytes:
        # Compact read-only finalized-disc response sufficient for host probing.
        data = bytearray(34)
        data[0:2] = (32).to_bytes(2, 'big')
        data[2] = 0x0E  # finalized/complete-ish status bits for read-only media probing
        data[3] = 0x01  # first track number
        data[4] = 0x01  # number of sessions/low byte in this bridge profile
        data[5] = 0x01  # first track in last session/low byte
        data[6] = 0x01  # last track in last session/low byte
        data[8] = 0x00
        return self._clip_alloc(bytes(data), cdb)

    def _read_format_capacities_response(self, cdb: bytes) -> bytes:
        # MMC READ FORMAT CAPACITIES response. Report one current capacity using
        # the currently visible sector count and 2048-byte blocks.
        nsectors = self._drive_visible_count(self.auth_stage) or 0
        desc = max(0, nsectors).to_bytes(4, 'big') + bytes([0x02]) + (2048).to_bytes(3, 'big')
        data = bytes([0x00, 0x00, 0x00, len(desc)]) + desc
        return self._clip_alloc(data, cdb)

    def _report_luns_response(self, cdb: bytes) -> bytes:
        # One LUN, addressed as flat LUN 0. Allocation length is bytes 6..9.
        data = (8).to_bytes(4, 'big') + b'\x00\x00\x00\x00' + b'\x00' * 8
        alloc = int.from_bytes(cdb[6:10], 'big') if len(cdb) >= 10 else len(data)
        return data if not alloc else data[:alloc]

    def _mode_page_capabilities(self) -> bytes:
        # MMC-3-ish CD/DVD capabilities/mechanical status page (2Ah). Keep it
        # conservative: read-only DVD-ROM/CD-ROM with tray loading.
        body = bytearray(0x14)
        body[0] = 0x2A
        body[1] = len(body) - 2
        mmc = self._device_mmc_caps()
        read_media = mmc.get('read_media') or {}
        write_media = mmc.get('write_media') or {}
        # Conservative MMC page 2Ah capability bits. The exact bit layout is
        # approximate in this bridge, but now profile-driven rather than hardcoded
        # read-only for every model.
        body[2] = (0x01 if write_media.get('cd_r') else 0x00) | (0x02 if write_media.get('cd_rw') else 0x00)
        body[3] = 0x08 if read_media.get('dvd_rom', True) else 0x00
        body[4] = 0x00
        if read_media.get('cd_rom', True): body[4] |= 0x01
        if read_media.get('cd_r', True): body[4] |= 0x10
        if read_media.get('cd_rw', True): body[4] |= 0x20
        if read_media.get('dvd_rom', True): body[4] |= 0x40
        body[5] = 0x00
        body[6] = 0x29  # tray loading, eject, lock/prevent medium removal
        body[7] = 0x00
        body[8:10] = (706).to_bytes(2, 'big')   # max read speed placeholder
        body[10:12] = (0).to_bytes(2, 'big')
        body[12:14] = (706).to_bytes(2, 'big')
        body[18:20] = (2048).to_bytes(2, 'big')
        return bytes(body)

    def _mode_sense_pages(self, page: int) -> bytes:
        if page == 0x3E:
            ms10 = self._mode_sense_3e_response()
            return ms10[8:] if len(ms10) >= 8 else b''
        if page == 0x2A:
            return self._mode_page_capabilities()
        if page in (0x00,):
            return b''
        if page == 0x3F:
            return self._mode_page_capabilities()
        raise ValueError(f'unsupported MODE SENSE page 0x{page:02X}')

    def _mode_sense_10_response(self, cdb: bytes) -> bytes:
        page = cdb[2] & 0x3F if len(cdb) >= 3 else 0
        page_data = self._mode_sense_pages(page)
        data = bytearray(8 + len(page_data))
        data[0:2] = max(0, len(data) - 2).to_bytes(2, 'big')
        data[2] = 0x00
        data[3] = 0x00
        data[6:8] = (0).to_bytes(2, 'big')
        data[8:] = page_data
        return self._clip_alloc(bytes(data), cdb)

    def _read_dvd_structure_response(self, cdb: bytes) -> bytes:
        # v165: exact CDB responses must win before generic format-byte
        # media-model routing. Xbox 8050L authentication uses a vendor-shaped
        # READ DVD STRUCTURE CDB:
        #   AD 00 FF 02 FD FF FE 00 06 64 00 C0
        # Its byte 7 is 0x00, so a format-byte-first router incorrectly serves
        # PFI and breaks the host challenge table. Exact CDB matching restores
        # the proven v159/v160 auth behavior while preserving v165 media-model
        # payload routing for normal MMC PFI/DMI requests.
        if cdb in self.sidecar_cdb_responses:
            _name, data = self.sidecar_cdb_responses[cdb]
        else:
            fmt = cdb[7] if len(cdb) >= 8 else 0
            # Compatibility guard for the Xbox authentication CDB if a sidecar
            # exact CDB table is missing but the AD-C0 payload file is present.
            if len(cdb) >= 12 and cdb[0] == 0xAD and cdb[11] == 0xC0 and self.media_model.payloads.get('ad_c0'):
                data = self.media_model.payloads['ad_c0']
            else:
                media_payload = self.media_model.get_dvd_structure_payload(fmt) if hasattr(self, 'media_model') else None
                if media_payload:
                    _name, data = media_payload
                else:
                    wanted = None
                    if fmt == 0x00:
                        wanted = 'read_dvd_structure_pfi'
                    elif fmt == 0x04:
                        wanted = 'read_dvd_structure_dmi'
                    elif fmt == 0xC0:
                        wanted = 'read_dvd_structure_xbox_control_block'
                    by_name={str(r.get('name','')).lower():r for r in self.sidecar.get('scsi_responses') or [] if isinstance(r,dict)}
                    rec=by_name.get(wanted or '')
                    if not rec:
                        raise ValueError(f'unsupported READ DVD STRUCTURE format 0x{fmt:02X}: {hx(cdb)}')
                    rp=Path(str(rec.get('response_file')))
                    if not rp.is_absolute():
                        rp=self.xdvd_json.parent / rp
                    data=rp.read_bytes()
        alloc = int.from_bytes(cdb[8:10], 'big') if len(cdb) >= 10 else len(data)
        return data if not alloc else data[:alloc]

    def _mode_sense_6_response(self, cdb: bytes) -> bytes:
        page = cdb[2] & 0x3F if len(cdb) >= 3 else 0
        page_data = self._mode_sense_pages(page)
        data = bytearray(4 + len(page_data))
        data[0] = max(0, len(data) - 1) & 0xFF
        data[1] = 0x00
        data[2] = 0x00
        data[3] = 0x00
        data[4:] = page_data
        return self._clip_alloc(bytes(data), cdb)

    def _layout_int(self, key: str, default: int = 0) -> int:
        val = self._layout().get(key, default)
        try:
            return int(val, 0) if isinstance(val, str) else int(val)
        except Exception:
            return int(default)

    def _drive_visible_count(self, stage: str | None = None) -> int:
        stage = stage or self.auth_stage
        state = self.unlocked if stage == 'unlocked' else self.locked
        val = state.get('visible_sector_count') or 0
        try:
            n = int(val, 0) if isinstance(val, str) else int(val)
            if n:
                return n
        except Exception:
            pass
        if hasattr(self, 'media_model') and self.media_model and hasattr(self.media_model, 'current_capacity_sector_count'):
            return int(self.media_model.current_capacity_sector_count(stage) or 0)
        return 0

    def firmware_lba_state(self) -> dict[str, Any]:
        """Return the current firmware/media LBA-window model.

        This is v165 instrumentation, not a new spoof layer. It makes the
        current bridge assumptions explicit so we can replace them with true
        MMIO/DMA-derived state as more firmware paths are understood.
        """
        layout = self._layout()
        locked_count = self._drive_visible_count('locked') or self.media_model.visible_sector_count or self.media_model.iso_sector_count
        unlocked_count = self._drive_visible_count('unlocked') or self.media_model.iso_sector_count or self.media_model.xbox_physical_sectors
        game_host_start = self._layout_int('game_unlocked_source_start_lba', 0)
        game_iso_start = self._layout_int('game_output_start_lba', 0)
        game_count = self._layout_int('game_sector_count', 0)
        if self.auth_stage == 'unlocked' and game_count and game_iso_start:
            window_name = 'unlocked_game_window'
            host_start = game_host_start
            iso_start = game_iso_start
            count = game_count
            capacity = unlocked_count
        else:
            window_name = 'locked_visible_window'
            host_start = 0
            iso_start = self._layout_int('visible_start_lba', 0)
            count = locked_count
            capacity = locked_count
        host_end = host_start + max(0, count) - 1 if count else 0
        iso_end = iso_start + max(0, count) - 1 if count else 0
        return {
            'auth_stage': self.auth_stage,
            'media_present': True,
            'window_name': window_name,
            'host_start_lba': host_start,
            'host_end_lba': host_end,
            'iso_start_lba': iso_start,
            'iso_end_lba': iso_end,
            'window_sector_count': count,
            'capacity_sector_count': capacity,
            'current_lba_base': iso_start - host_start,
            'layer_break_lba': self._layout_int('layer_break_lba', 0),
            'source': 'v165_bridge_instrumentation_pending_mmio_dma',
        }

    def _record_firmware_event(self, event: str, **extra: Any) -> None:
        row = {'event': event, 'auth_stage': self.auth_stage, 'firmware_lba_state': self.firmware_lba_state() if hasattr(self, 'media_model') else {}}
        row.update(extra)
        self.firmware_events.append(row)

    def firmware_state_summary(self) -> dict[str, Any]:
        return {
            'version': 'v165-firmware-lba-state',
            'auth_stage': self.auth_stage,
            'read_map': self.read_map,
            'firmware_lba_state': self.firmware_lba_state(),
            'event_count': len(self.firmware_events),
            'recent_events': self.firmware_events[-16:],
        }

    def _guess_read_signature(self, data: bytes) -> str:
        if not data:
            return 'empty'
        head = data[:2048]
        if head.startswith(b'MICROSOFT*XBOX*MEDIA') or b'MICROSOFT*XBOX*MEDIA' in head[:512]:
            return 'xdfs_volume_descriptor'
        if b'XBEH' in head[:256]:
            return 'xbe_header'
        if b'CD001' in head[:256]:
            return 'iso9660'
        if b'BEA01' in head[:256] or b'NSR0' in head[:256]:
            return 'udf_anchor_or_volume'
        if head[:64].strip(b'\x00') == b'':
            return 'zero'
        ascii_head = ''.join(chr(b) if 32 <= b < 127 else '.' for b in head[:32])
        return 'data:' + ascii_head

    def _map_iso_lba(self, lba: int, blocks: int) -> tuple[int, str]:
        mode = self.read_map
        layout = self._layout()
        # Pure physical/raw modes: host LBA is ISO/raw2048 LBA. This is the
        # correct option-1 full-disc dump model, but some existing dumpers first
        # do a tiny Xbox/XDFS metadata probe at unlocked LBA 32 before offering
        # the full raw dump option. Keep these strict modes fully direct.
        if mode in ('direct', 'windows', 'v157', 'physical_raw', 'raw_full', 'raw_iso', 'option1_raw', 'full_raw'):
            return lba, 'physical_raw_direct' if mode not in ('direct','windows','v157') else 'direct'
        if mode in ('firmware_window', 'firmware_lba_state', 'firmware', 'level2'):
            st = self.firmware_lba_state()
            hs, he = int(st.get('host_start_lba') or 0), int(st.get('host_end_lba') or -1)
            base = int(st.get('current_lba_base') or 0)
            if hs <= lba and lba + max(blocks, 1) - 1 <= he:
                return lba + base, f"firmware_window:{st.get('window_name','unknown')}"
            return lba, f"firmware_window:direct_outside_{st.get('window_name','unknown')}"
        if mode in self._deprecated_raw_preflight_alias_modes:
            return lba, f'{mode}:deprecated_no_alias_direct'
        if self.auth_stage != 'unlocked' or not layout:
            return lba, 'direct_locked_or_no_layout'
        game_output = self._layout_int('game_output_start_lba', 0)
        game_src = self._layout_int('game_unlocked_source_start_lba', 0)
        game_count = self._layout_int('game_sector_count', 0)
        visible_count = self._layout_int('visible_sector_count', 0)
        if game_count <= 0:
            return lba, 'direct_no_game_count'

        if mode in ('xbox_raw', 'dual_alias', 'xbox_alias', 'xiso_alias', 'xbox_xiso', 'game_partition'):
            if game_src <= lba < game_src + game_count:
                return game_output + (lba - game_src), f'{mode}:game_src_to_output'
            return lba, f'{mode}:direct_outside_game_src'
        if mode in ('game_offset0', 'game_zero'):
            if 0 <= lba < game_count:
                return game_output + lba, f'{mode}:game_zero_to_output'
            return lba, f'{mode}:direct_outside_game_zero'
        if mode in ('physical', 'output'):
            return lba, 'direct_output_layout'
        return lba, f'unknown_read_map_{mode}_direct'

    def _emit_read_trace(self, info: dict[str, Any]) -> None:
        if not self.read_trace_path:
            return
        if self.read_trace_limit and self.read_trace_count >= self.read_trace_limit:
            return
        self.read_trace_count += 1
        with self.read_trace_path.open('a', encoding='utf-8') as fh:
            fh.write(json.dumps(info, separators=(',', ':')) + '\n')

    def _read_iso(self, lba: int, blocks: int) -> bytes:
        if not self.iso_path:
            raise ValueError('READ requires an ISO/raw2048 path')
        if not self.iso_path.exists():
            raise FileNotFoundError(f'ISO/raw2048 not found: {self.iso_path}')
        sector=2048
        model_map_info: dict[str, Any] = {}
        model_map_modes = {'firmware_window', 'firmware_lba_state', 'firmware', 'level2', 'stateful_xbox'}
        if hasattr(self, 'media_model') and self.media_model and self.read_map in model_map_modes:
            data, model_map_info = self.media_model.read_host(self.iso_path, self.auth_stage, lba, blocks, read_map=self.read_map)
            iso_lba = int(model_map_info.get('media_lba', lba))
            map_note = str(model_map_info.get('read_map_note') or 'media_model')
            size = self.iso_path.stat().st_size
        else:
            iso_lba, map_note = self._map_iso_lba(lba, blocks)
            off=iso_lba*sector
            length=blocks*sector
            size=self.iso_path.stat().st_size
            if off < 0 or off + length > size:
                raise ValueError(f'READ outside ISO: host_lba={lba} iso_lba={iso_lba} blocks={blocks} off={off} len={length} size={size} map={map_note}')
            with self.iso_path.open('rb') as fh:
                fh.seek(off)
                data=fh.read(length)
            if len(data) != length:
                raise IOError(f'short READ from ISO: got={len(data)} expected={length}')
        sig = self._guess_read_signature(data)
        layer_break = self._layout_int('layer_break_lba', 0)
        host_end = lba + max(blocks, 1) - 1
        iso_end = iso_lba + max(blocks, 1) - 1
        host_layer = 1 if layer_break and lba >= layer_break else 0
        iso_layer = 1 if layer_break and iso_lba >= layer_break else 0
        fw_state = self.firmware_lba_state()
        info = {
            'read_map_mode': self.read_map,
            'read_map_note': map_note,
            'auth_stage': self.auth_stage,
            'host_lba': lba,
            'host_end_lba': host_end,
            'host_layer': host_layer,
            'host_crosses_layer_break': bool(layer_break and lba < layer_break <= host_end),
            'iso_lba': iso_lba,
            'media_lba': iso_lba,
            'iso_end_lba': iso_end,
            'media_lba_end': iso_end,
            'effective_media_lba_delta': (iso_lba - lba),
            'iso_layer': iso_layer,
            'iso_crosses_layer_break': bool(layer_break and iso_lba < layer_break <= iso_end),
            'blocks': blocks,
            'bytes': len(data),
            'signature': sig,
            'preview_hex': hx(data[:64]),
            'visible_sector_count': self._layout_int('visible_sector_count', 0),
            'game_output_start_lba': self._layout_int('game_output_start_lba', 0),
            'game_unlocked_source_start_lba': self._layout_int('game_unlocked_source_start_lba', 0),
            'game_sector_count': self._layout_int('game_sector_count', 0),
            'layer_break_lba': layer_break,
            'xbox_physical_sectors': self.media_model.xbox_physical_sectors if hasattr(self, 'media_model') else 0,
            'iso_sector_count': self.media_model.iso_sector_count if hasattr(self, 'media_model') else size // sector,
            'media_id': self.media_model.media_id if hasattr(self, 'media_model') else '',
            'firmware_window_name': fw_state.get('window_name',''),
            'firmware_current_lba_base': fw_state.get('current_lba_base', 0),
            'firmware_host_start_lba': fw_state.get('host_start_lba', 0),
            'firmware_host_end_lba': fw_state.get('host_end_lba', 0),
            'firmware_iso_start_lba': fw_state.get('iso_start_lba', 0),
            'firmware_iso_end_lba': fw_state.get('iso_end_lba', 0),
            'firmware_window_sector_count': fw_state.get('window_sector_count', 0),
            'firmware_capacity_sector_count': fw_state.get('capacity_sector_count', 0),
            'firmware_state_source': fw_state.get('source',''),
            'firmware_event_count': len(self.firmware_events),
            'media_layout_type': model_map_info.get('media_layout_type', getattr(self.media_model, 'layout_type', '') if hasattr(self, 'media_model') else ''),
            'media_is_redump_style': model_map_info.get('media_is_redump_style', getattr(self.media_model, 'is_redump_style', False) if hasattr(self, 'media_model') else False),
            'media_profile_loaded': model_map_info.get('media_profile_loaded', getattr(self.media_model, 'media_profile_loaded', False) if hasattr(self, 'media_model') else False),
            'media_segment_count': model_map_info.get('media_segment_count', 1),
            'media_read_segments_json': json.dumps(model_map_info.get('read_segments', []), separators=(',', ':')) if model_map_info else '',
        }
        self.last_read_info = info
        self._emit_read_trace(info)
        return data

    def resolve(self, cdb: bytes, *, data_out: bytes = b'', tag: str | None = None, title: str | None = None) -> dict[str,str]:
        if not cdb:
            raise ValueError('empty CDB')
        op=cdb[0]
        tag = tag or f'cdb_{op:02x}'
        title = title or tag
        # TEST UNIT READY / no data path.
        if op == 0x00:
            return {'tag': tag, 'title': title, 'direction': 'none', 'cdb_hex': hx(cdb)}
        # INQUIRY(6), standard and VPD responses for host probing.
        if op == 0x12:
            data=self._inquiry_response(cdb)
            return {'tag': tag, 'title': title, 'direction': 'in', 'cdb_hex': hx(cdb), 'data_in_hex': hx(data)}
        # READ FORMAT CAPACITIES and REPORT LUNS are common during Windows iSCSI attach.
        if op == 0x23:
            data=self._read_format_capacities_response(cdb)
            return {'tag': tag, 'title': title, 'direction': 'in', 'cdb_hex': hx(cdb), 'data_in_hex': hx(data)}
        if op == 0xA0:
            data=self._report_luns_response(cdb)
            return {'tag': tag, 'title': title, 'direction': 'in', 'cdb_hex': hx(cdb), 'data_in_hex': hx(data)}
        # REQUEST SENSE(6), no-sense response for host probing.
        if op == 0x03:
            data=self._request_sense_response(cdb)
            return {'tag': tag, 'title': title, 'direction': 'in', 'cdb_hex': hx(cdb), 'data_in_hex': hx(data)}
        # MODE SENSE(6) for common host probes.
        if op == 0x1A:
            data=self._mode_sense_6_response(cdb)
            return {'tag': tag, 'title': title, 'direction': 'in', 'cdb_hex': hx(cdb), 'data_in_hex': hx(data)}
        # GET CONFIGURATION(10), READ TOC/PMA/ATIP, GET EVENT STATUS NOTIFICATION, READ DISC INFORMATION.
        if op == 0x46:
            data=self._get_configuration_response(cdb)
            return {'tag': tag, 'title': title, 'direction': 'in', 'cdb_hex': hx(cdb), 'data_in_hex': hx(data)}
        if op == 0x43:
            data=self._read_toc_response(cdb)
            return {'tag': tag, 'title': title, 'direction': 'in', 'cdb_hex': hx(cdb), 'data_in_hex': hx(data)}
        if op == 0x4A:
            data=self._get_event_status_notification_response(cdb)
            return {'tag': tag, 'title': title, 'direction': 'in', 'cdb_hex': hx(cdb), 'data_in_hex': hx(data)}
        if op == 0x51:
            data=self._read_disc_information_response(cdb)
            return {'tag': tag, 'title': title, 'direction': 'in', 'cdb_hex': hx(cdb), 'data_in_hex': hx(data)}
        # PREVENT/ALLOW MEDIUM REMOVAL, START STOP UNIT, SYNCHRONIZE CACHE(10): no-data host probes.
        if op in (0x1E, 0x1B, 0x35):
            return {'tag': tag, 'title': title, 'direction': 'none', 'cdb_hex': hx(cdb)}
        # READ CAPACITY(10)
        if op == 0x25:
            data=self._read_capacity_10_response()
            return {'tag': tag, 'title': title, 'direction': 'in', 'cdb_hex': hx(cdb), 'data_in_hex': hx(data)}
        # MODE SENSE(10) page 3Eh and common MMC pages.
        if op == 0x5A:
            data=self._mode_sense_10_response(cdb)
            return {'tag': tag, 'title': title, 'direction': 'in', 'cdb_hex': hx(cdb), 'data_in_hex': hx(data)}
        # READ DVD STRUCTURE and other sidecar exact CDB responses.
        if op == 0xAD:
            data=self._read_dvd_structure_response(cdb)
            return {'tag': tag, 'title': title, 'direction': 'in', 'cdb_hex': hx(cdb), 'data_in_hex': hx(data)}
        if cdb in self.sidecar_cdb_responses:
            _name,data=self.sidecar_cdb_responses[cdb]
            return {'tag': tag, 'title': title, 'direction': 'in', 'cdb_hex': hx(cdb), 'data_in_hex': hx(data)}
        # MODE SELECT(10)
        if op == 0x55:
            if not data_out:
                raise ValueError(f'{tag}: MODE SELECT(10) requires data_out')
            self._update_auth_from_mode_select(data_out)
            return {'tag': tag, 'title': title, 'direction': 'out', 'cdb_hex': hx(cdb), 'data_out_hex': hx(data_out)}
        # MODE SELECT(6)
        if op == 0x15:
            if not data_out:
                raise ValueError(f'{tag}: MODE SELECT(6) requires data_out')
            return {'tag': tag, 'title': title, 'direction': 'out', 'cdb_hex': hx(cdb), 'data_out_hex': hx(data_out)}
        # Media-writing/session-mutating commands. Keep capture-only by default;
        # future writable-media sessions can opt into backing-image mutation.
        if self._is_mutating_media_cdb(cdb):
            return self._mutating_media_response(cdb, data_out=data_out, tag=tag, title=title)
        # READ(10)
        if op == 0x28:
            if len(cdb) < 10:
                raise ValueError(f'{tag}: READ(10) CDB too short')
            lba=int.from_bytes(cdb[2:6], 'big')
            blocks=int.from_bytes(cdb[7:9], 'big')
            if blocks == 0:
                data=b''
                self.last_read_info={'read_map_mode': self.read_map, 'host_lba': lba, 'iso_lba': lba, 'blocks': blocks, 'bytes': 0, 'signature': 'empty'}
            else:
                data=self._read_iso(lba, blocks)
            out={'tag': tag, 'title': title, 'direction': 'in', 'cdb_hex': hx(cdb), 'data_in_hex': hx(data)}
            out.update({k: str(v) for k,v in self.last_read_info.items() if k not in ('preview_hex',)})
            out['read_preview_hex'] = self.last_read_info.get('preview_hex','')
            return out
        # READ(12)
        if op == 0xA8:
            if len(cdb) < 12:
                raise ValueError(f'{tag}: READ(12) CDB too short')
            lba=int.from_bytes(cdb[2:6], 'big')
            blocks=int.from_bytes(cdb[6:10], 'big')
            if blocks == 0:
                data=b''
                self.last_read_info={'read_map_mode': self.read_map, 'host_lba': lba, 'iso_lba': lba, 'blocks': blocks, 'bytes': 0, 'signature': 'empty'}
            else:
                data=self._read_iso(lba, blocks)
            out={'tag': tag, 'title': title, 'direction': 'in', 'cdb_hex': hx(cdb), 'data_in_hex': hx(data)}
            out.update({k: str(v) for k,v in self.last_read_info.items() if k not in ('preview_hex',)})
            out['read_preview_hex'] = self.last_read_info.get('preview_hex','')
            return out
        raise ValueError(f'{tag}: unsupported CDB opcode 0x{op:02X}: {hx(cdb)}')

    def commands_from_script_items(self, items: list[dict[str,Any]]) -> list[dict[str,str]]:
        out=[]
        for i,item in enumerate(items):
            cdb=parse_hex_bytes(item.get('cdb_hex', item.get('cdb')))
            data_out=parse_hex_bytes(item.get('data_out_hex', item.get('data_out')))
            tag=str(item.get('tag') or item.get('title') or f'cmd{i+1:02d}_{cdb[0]:02x}')
            title=str(item.get('title') or tag)
            out.append(self.resolve(cdb, data_out=data_out, tag=tag, title=title))
        return out

    def commands_from_script_file(self, path: Path) -> list[dict[str,str]]:
        raw=json.loads(Path(path).read_text(encoding='utf-8'))
        items=raw.get('commands', raw) if isinstance(raw, dict) else raw
        if not isinstance(items, list):
            raise ValueError('SCSI script must contain a list or an object with commands')
        return self.commands_from_script_items(items)

    def script_from_unlock_log(self) -> list[dict[str,str]]:
        if not self.log_path or not self.log_path.exists():
            raise FileNotFoundError('unlock log required for default script')
        sec=self.log_sections
        steps=[
            ('step01_locked_read_capacity','in','Step 1: Init Read Cap','Step 1: Init Read Cap'),
            ('step02_locked_mode_sense_3e','in','Step 2: Mode Sense','Step 2: Mode Sense'),
            ('step03_read_dvd_structure_ad_c0','in','Step 3: Read DVD Struct 0xAD','Step 3: Read DVD Struct 0xAD'),
            ('step04_mode_select_chal1','out','Step 4: Mode Select Chal 1','Step 4: Mode Select Chal 1'),
            ('step05_mode_sense_verify1','in','Step 5: Mode Sense Verify 1','Step 5: Mode Sense Verify 1'),
            ('step06_mode_select_chal2','out','Step 6: Mode Select Chal 2','Step 6: Mode Select Chal 2'),
            ('step07_mode_sense_verify2','in','Step 7: Mode Sense Verify 2','Step 7: Mode Sense Verify 2'),
            ('step08_mode_select_partition1_unlock','out','Step 8: Mode Select Part1 Unlock','Step 8: Mode Select Part1 Unlock'),
            ('step09_sticky_descramble_mode31','out','Step 9: Sticky Descrambling','Step 9: Sticky Descrambling'),
            ('step10_unlocked_read_capacity','in','Step 10: Final Verify','Step 10: Final Verify'),
        ]
        items=[]
        for tag,direction,cdb_label,data_label in steps:
            cdb=sec.get(('Sending CDB', cdb_label), b'')
            if not cdb:
                raise ValueError(f'missing CDB [{cdb_label}] in unlock log')
            ent={'tag':tag, 'cdb_hex':hx(cdb)}
            if direction == 'out':
                data=sec.get(('Sending Data', data_label), b'')
                if not data:
                    raise ValueError(f'missing DATA OUT [{data_label}] in unlock log')
                ent['data_out_hex']=hx(data)
            items.append(ent)
        return items

    def append_post_unlock_script(self, items: list[dict[str,str]]) -> list[dict[str,str]]:
        out=list(items)
        unlocked=self.unlocked
        rc=parse_hex_bytes(unlocked.get('read_capacity_10_cdb_hex'))
        if rc:
            out.append({'tag':'post01_unlocked_read_capacity', 'cdb_hex':hx(rc)})
        ms=parse_hex_bytes(unlocked.get('mode_sense_3e_cdb_hex'))
        if ms:
            out.append({'tag':'post02_unlocked_mode_sense_3e', 'cdb_hex':hx(ms)})
        # stable order for known sidecar packets
        name_order=[
            ('post03_read_dvd_structure_pfi','read_dvd_structure_pfi'),
            ('post04_read_dvd_structure_dmi','read_dvd_structure_dmi'),
            ('post05_read_dvd_structure_ad_c0','read_dvd_structure_xbox_control_block'),
        ]
        by_name={str(r.get('name','')).lower():r for r in self.sidecar.get('scsi_responses') or [] if isinstance(r,dict)}
        for tag,name in name_order:
            rec=by_name.get(name)
            if rec and rec.get('cdb_hex'):
                out.append({'tag':tag, 'cdb_hex':hx(parse_hex_bytes(rec.get('cdb_hex')))})
        return out



    def append_standard_probe_script(self, items: list[dict[str,str]]) -> list[dict[str,str]]:
        out=list(items)
        out.extend([
            {'tag':'post_probe_01_test_unit_ready', 'cdb_hex':'00 00 00 00 00 00'},
            {'tag':'post_probe_02_inquiry_36', 'cdb_hex':'12 00 00 00 24 00'},
            {'tag':'post_probe_03_request_sense_18', 'cdb_hex':'03 00 00 00 12 00'},
        ])
        return out

    def append_windows_probe_script(self, items: list[dict[str,str]]) -> list[dict[str,str]]:
        # A small Windows/OS optical-device style probe set, intentionally still
        # deterministic and parser-resolved for batch validation.
        out=self.append_standard_probe_script(items)
        out.extend([
            {'tag':'post_probe_04_get_configuration_64', 'cdb_hex':'46 02 00 00 00 00 00 00 40 00'},
            {'tag':'post_probe_05_read_toc_20', 'cdb_hex':'43 00 00 00 00 00 00 00 14 00'},
            {'tag':'post_probe_06_get_event_status_8', 'cdb_hex':'4A 01 00 00 10 00 00 00 08 00'},
            {'tag':'post_probe_07_read_disc_information_34', 'cdb_hex':'51 00 00 00 00 00 00 00 22 00'},
            {'tag':'post_probe_08_mode_sense6_3e_24', 'cdb_hex':'1A 00 3E 00 18 00'},
            {'tag':'post_probe_09_prevent_allow_medium_removal', 'cdb_hex':'1E 00 00 00 00 00'},
            {'tag':'post_probe_10_start_stop_unit_nop', 'cdb_hex':'1B 00 00 00 00 00'},
            {'tag':'post_probe_11_synchronize_cache_10', 'cdb_hex':'35 00 00 00 00 00 00 00 00 00'},
        ])
        return out

    def windows_attach_probe_script(self) -> list[dict[str,str]]:
        # Broader host/initiator attach probes, separate from the firmware-session
        # exact 29-command baseline. These exercise the live iSCSI target parser.
        return [
            {'tag':'win_attach_00_report_luns_16', 'cdb_hex':'A0 00 00 00 00 00 00 00 00 10 00 00'},
            {'tag':'win_attach_01_inquiry_standard_96', 'cdb_hex':'12 00 00 00 60 00'},
            {'tag':'win_attach_02_inquiry_vpd_supported', 'cdb_hex':'12 01 00 00 40 00'},
            {'tag':'win_attach_03_inquiry_vpd_serial', 'cdb_hex':'12 01 80 00 40 00'},
            {'tag':'win_attach_04_inquiry_vpd_device_id', 'cdb_hex':'12 01 83 00 60 00'},
            {'tag':'win_attach_05_read_capacity_locked', 'cdb_hex':'25 00 00 00 00 00 00 00 00 00'},
            {'tag':'win_attach_06_read_format_capacities', 'cdb_hex':'23 00 00 00 00 00 00 00 20 00'},
            {'tag':'win_attach_07_get_configuration_252', 'cdb_hex':'46 02 00 00 00 00 00 00 FC 00'},
            {'tag':'win_attach_08_mode_sense10_page2a', 'cdb_hex':'5A 00 2A 00 00 00 00 00 40 00'},
            {'tag':'win_attach_09_mode_sense6_page2a', 'cdb_hex':'1A 00 2A 00 40 00'},
            {'tag':'win_attach_10_mode_sense10_all', 'cdb_hex':'5A 00 3F 00 00 00 00 00 80 00'},
            {'tag':'win_attach_11_read_toc_64', 'cdb_hex':'43 00 00 00 00 00 00 00 40 00'},
            {'tag':'win_attach_12_get_event_status_16', 'cdb_hex':'4A 01 00 00 10 00 00 00 10 00'},
            {'tag':'win_attach_13_read_disc_information_34', 'cdb_hex':'51 00 00 00 00 00 00 00 22 00'},
            {'tag':'win_attach_14_read_dvd_structure_pfi_32', 'cdb_hex':'AD 00 00 00 00 00 00 00 00 20 00 00'},
            {'tag':'win_attach_15_read_dvd_structure_dmi_32', 'cdb_hex':'AD 00 00 00 00 00 00 04 00 20 00 00'},
            {'tag':'win_attach_16_test_unit_ready', 'cdb_hex':'00 00 00 00 00 00'},
        ]

    def append_read10_script(self, items: list[dict[str,str]], specs: list[str]) -> list[dict[str,str]]:
        selected=parse_read10_specs(specs) if specs else self.default_read10_specs()
        out=list(items)
        for i,(lba,blocks) in enumerate(selected,1):
            out.append({'tag':f'post_read10_{i:02d}_lba{lba}_blocks{blocks}', 'cdb_hex':hx(make_read10_cdb(lba, blocks))})
        return out
