Write `/app/termzip_ansi.py`.

The verifier will run:

```bash
python3 /app/termzip_ansi.py /app/frames.tza > /tmp/out.ansi
```

Your program must read the binary **frame file** (`.tza`) and print a **Tiny-ANSI** byte stream to stdout that reproduces every frame, in order, under the reference terminal emulator.

This task is a tiny "terminal video codec": you must stream screen updates efficiently enough to fit a tight byte budget.

## 1) Input format: `frames.tza`

Little-endian binary:

- Bytes `0..3`: ASCII magic `TZA1`
- `u16 width`
- `u16 height`
- `u32 n_frames`
- `u32 max_bytes` (your stdout length must be `<= max_bytes`)
- Then `n_frames` frames, each exactly `width * height` bytes:
  - Row-major, top to bottom, left to right
  - Each byte is a printable ASCII character (`0x20..0x7e`), including spaces

## 2) Output format: Tiny-ANSI stream

Your stdout is a stream of bytes containing:

- Printable ASCII (`0x20..0x7e`) which prints at the cursor
- A small set of control bytes / escape sequences (below)
- A **frame delimiter** byte `0x1e` (Record Separator). Each time the decoder reads `0x1e`, it snapshots the current screen. You must output **exactly `n_frames` delimiters**, producing exactly `n_frames` snapshots.

Your output must end with the final `0x1e` (so the last frame is captured).

### 2.1 Terminal model

The reference emulator starts with:

- Screen filled with spaces
- Cursor at row 1, column 1
- Scroll region is the full screen

Supported behavior:

- Printable byte: write to current cell, advance 1 column, wrapping to next line when past the last column.
- Wrapping past the last column uses the same scroll behavior as `\n`: if the cursor would move past the bottom of the scroll region, the scroll region scrolls up by 1 line and the cursor stays on the bottom row.
- `\\n` (LF, `0x0a`): move to start of next line (col=1). If at the bottom of the scroll region, scroll that region up by 1 line and keep the cursor on the bottom row.
- `\\r` (CR, `0x0d`): move cursor to column 1.
- `\\b` (BS, `0x08`): move cursor left by 1 column (clamped).
- `\\t` (TAB, `0x09`): move to next multiple-of-8 column (clamped).

### 2.2 Supported escape sequences

Only these ANSI-like sequences are recognized:

- `ESC[<r>;<c>H` (CUP): set cursor position (1-indexed). Missing params default to 1.
- `ESC[<n>A/B/C/D` (CUU/CUD/CUF/CUB): move cursor up/down/right/left by `n` (default 1).
- `ESC[<mode>J` (ED): erase display (`mode` 0/1/2).
- `ESC[<mode>K` (EL): erase line (`mode` 0/1/2).
- `ESC[<top>;<bottom>r` (DECSTBM): set scroll region (1-indexed, inclusive). Missing params reset to full screen. Setting the scroll region also moves the cursor to (1,1).
- `ESC[...m` (SGR): accepted but treated as a no-op by the verifier (you may ignore colors).

For all supported `ESC[...` sequences above, parameters must contain only ASCII decimal digits (`0`-`9`) and semicolons (`;`).

If your output contains other control bytes or unsupported escape sequences, it will be rejected.

## 3) Key constraint: size budget

Your stdout byte length must be `<= max_bytes` from the input header.

This budget is intentionally tight: solutions that redraw most of the screen each frame will fail.

## 4) Notes

- Write only the Tiny-ANSI stream to stdout.
- Stderr must be empty.
- Do not assume the verifier only uses the provided `/app/frames.tza`. Hidden tests will use additional `.tza` files of the same format.
