#!/usr/bin/env python3
"""
SCI Picture Resource Extractor

Extracts and renders background images (PIC resources) from Sierra's Creative Interpreter (SCI) games.
Supports SCI0 format (King's Quest IV).
"""

import os
import sys
from pathlib import Path
from typing import Tuple, Optional
from collections import deque
import numpy as np
from PIL import Image

PIC_OP_SET_COLOR = 0xf0
PIC_OP_DISABLE_VISUAL = 0xf1
PIC_OP_SET_PRIORITY = 0xf2
PIC_OP_DISABLE_PRIORITY = 0xf3
PIC_OP_RELATIVE_PATTERNS = 0xf4
PIC_OP_RELATIVE_MEDIUM_LINES = 0xf5
PIC_OP_RELATIVE_LONG_LINES = 0xf6
PIC_OP_RELATIVE_SHORT_LINES = 0xf7
PIC_OP_FILL = 0xf8
PIC_OP_SET_PATTERN = 0xf9
PIC_OP_ABSOLUTE_PATTERNS = 0xfa
PIC_OP_SET_CONTROL = 0xfb
PIC_OP_DISABLE_CONTROL = 0xfc
PIC_OP_RELATIVE_MEDIUM_PATTERNS = 0xfd
PIC_OP_OPX = 0xfe
PIC_OP_END = 0xff

WIDTH, HEIGHT = 320, 190

PATTERN_FLAG_RECTANGLE = 0x10
PATTERN_FLAG_USE_PATTERN = 0x20

CIRCLES = [
    [0x80],
    [0x4e, 0x40],
    [0x73, 0xef, 0xbe, 0x70],
    [0x38, 0x7c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x00],
    [0x1c, 0x1f, 0xcf, 0xfb, 0xfe, 0xff, 0xbf, 0xef, 0xf9, 0xfc, 0x1c],
    [0x0e, 0x03, 0xf8, 0x7f, 0xc7, 0xfc, 0xff, 0xef, 0xfe, 0xff, 0xe7, 0xfc, 0x7f, 0xc3, 0xf8, 0x1f, 0x00],
    [0x0f, 0x80, 0xff, 0x87, 0xff, 0x1f, 0xfc, 0xff, 0xfb, 0xff, 0xef, 0xff, 0xbf, 0xfe, 0xff, 0xf9, 0xff, 0xc7, 0xff, 0x0f, 0xf8, 0x0f, 0x80],
    [0x07, 0xc0, 0x1f, 0xf0, 0x3f, 0xf8, 0x7f, 0xfc, 0x7f, 0xfc, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfc, 0x7f, 0xfc, 0x3f, 0xf8, 0x1f, 0xf0, 0x07, 0xc0],
]

JUNQ = [0x20, 0x94, 0x02, 0x24, 0x90, 0x82, 0xa4, 0xa2, 0x82, 0x09, 0x0a, 0x22, 0x12, 0x10, 0x42, 0x14, 0x91, 0x4a, 0x91, 0x11, 0x08, 0x12, 0x25, 0x10, 0x22, 0xa8, 0x14, 0x24, 0x00, 0x50, 0x24, 0x04]

JUNQINDEX = [0x00, 0x18, 0x30, 0xc4, 0xdc, 0x65, 0xeb, 0x48, 0x60, 0xbd, 0x89, 0x05, 0x0a, 0xf4, 0x7d, 0x7d, 0x85, 0xb0, 0x8e, 0x95, 0x1f, 0x22, 0x0d, 0xdf, 0x2a, 0x78, 0xd5, 0x73, 0x1c, 0xb4, 0x40, 0xa1, 0xb9, 0x3c, 0xca, 0x58, 0x92, 0x34, 0xcc, 0xce, 0xd7, 0x42, 0x90, 0x0f, 0x8b, 0x7f, 0x32, 0xed, 0x5c, 0x9d, 0xc8, 0x99, 0xad, 0x4e, 0x56, 0xa6, 0xf7, 0x68, 0xb7, 0x25, 0x82, 0x37, 0x3a, 0x51, 0x69, 0x26, 0x38, 0x52, 0x9e, 0x9a, 0x4f, 0xa7, 0x43, 0x10, 0x80, 0xee, 0x3d, 0x59, 0x35, 0xcf, 0x79, 0x74, 0xb5, 0xa2, 0xb1, 0x96, 0x23, 0xe0, 0xbe, 0x05, 0xf5, 0x6e, 0x19, 0xc5, 0x66, 0x49, 0xf0, 0xd1, 0x54, 0xa9, 0x70, 0x4b, 0xa4, 0xe2, 0xe6, 0xe5, 0xab, 0xe4, 0xd2, 0xaa, 0x4c, 0xe3, 0x06, 0x6f, 0xc6, 0x4a, 0xa4, 0x75, 0x97, 0xe1]

EGA_COLORS = [
    (0x00, 0x00, 0x00), (0x00, 0x00, 0xA0), (0x00, 0xA0, 0x00), (0x00, 0xA0, 0xA0),
    (0xA0, 0x00, 0x00), (0xA0, 0x00, 0xA0), (0xA0, 0x50, 0x00), (0xA0, 0xA0, 0xA0),
    (0x50, 0x50, 0x50), (0x50, 0x50, 0xFF), (0x00, 0xFF, 0x50), (0x50, 0xFF, 0xFF),
    (0xFF, 0x50, 0x50), (0xFF, 0x50, 0xFF), (0xFF, 0xFF, 0x50), (0xFF, 0xFF, 0xFF),
]

DEFAULT_PALETTE = [
    (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7),
    (8, 8), (9, 9), (10, 10), (11, 11), (12, 12), (13, 13), (14, 14), (8, 8),
    (8, 8), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (8, 8),
    (8, 8), (15, 9), (15, 10), (15, 11), (15, 12), (15, 13), (15, 14), (15, 15),
    (0, 8), (9, 1), (2, 10), (3, 11), (4, 12), (5, 13), (6, 14), (8, 8),
]


class PictureRenderer:
    VISUAL = 1
    PRIORITY = 2
    CONTROL = 4
    
    def __init__(self):
        self.visual = np.full((HEIGHT, WIDTH), 0x0f, dtype=np.uint8)
        self.priority = np.zeros((HEIGHT, WIDTH), dtype=np.uint8)
        self.control = np.zeros((HEIGHT, WIDTH), dtype=np.uint8)
        self.aux = np.zeros((HEIGHT, WIDTH), dtype=np.uint8)
        self.palettes = [list(DEFAULT_PALETTE) for _ in range(4)]
        self.locked = [0] * 40
        self.reset_state()
    
    def reset_state(self):
        self.palette_number = 0
        self.palette_offset = 0
        self.ega_color = (0, 0)
        self.current_priority = 0
        self.current_control = 0
        self.draw_enable = self.VISUAL
        self.palette_to_draw = 0
        self.pattern_size = 0
        self.pattern_nr = 0
        self.pattern_use_pattern = False
        self.pattern_is_rect = False
    
    def is_white(self):
        return self.ega_color[0] == 15 and self.ega_color[1] == 15
    
    def get_aux_set(self):
        aux = self.draw_enable
        if self.ega_color[0] == 15 and self.ega_color[1] == 15:
            aux &= ~self.VISUAL
        if self.current_priority == 0:
            aux &= ~self.PRIORITY
        if self.current_control == 0:
            aux &= ~self.CONTROL
        return aux
    
    def plot_pixel(self, x: int, y: int):
        if 0 <= x < WIDTH and 0 <= y < HEIGHT:
            aux_set = self.get_aux_set()
            if self.draw_enable & self.VISUAL:
                # EGA dithering: color1 on odd pixels, color2 on even pixels
                color_idx = self.ega_color[0] if ((x ^ y) & 1) else self.ega_color[1]
                self.visual[y, x] = color_idx
            if self.draw_enable & self.PRIORITY:
                self.priority[y, x] = self.current_priority
            if self.draw_enable & self.CONTROL:
                self.control[y, x] = self.current_control
            self.aux[y, x] |= aux_set
    
    def draw_line(self, x1: int, y1: int, x2: int, y2: int):
        dx = abs(x2 - x1)
        dy = abs(y2 - y1)
        sx = 1 if x1 < x2 else -1
        sy = 1 if y1 < y2 else -1
        err = dx - dy
        while True:
            self.plot_pixel(x1, y1)
            if x1 == x2 and y1 == y2:
                break
            e2 = 2 * err
            if e2 > -dy:
                err -= dy
                x1 += sx
            if e2 < dx:
                err += dx
                y1 += sy
    
    def draw_pattern(self, x: int, y: int, size: int, pattern_nr: int, use_pattern: bool, is_rect: bool):
        wsize = size
        x_max, y_max = WIDTH - 1, HEIGHT - 1
        
        if x < wsize:
            x = wsize
        if x + wsize > x_max:
            x = x_max - wsize
        if y < wsize:
            y = wsize
        if y + wsize > y_max:
            y = y_max - wsize
        
        if pattern_nr >= len(JUNQINDEX):
            return
        
        junqbit = JUNQINDEX[pattern_nr]
        
        if is_rect:
            for ly in range(y - wsize, y + wsize + 1):
                for lx in range(x - wsize, x + wsize + 2):
                    if use_pattern:
                        if (JUNQ[junqbit >> 3] >> (7 - (junqbit & 7))) & 1:
                            self.plot_pixel(lx, ly)
                        junqbit = (junqbit + 1) & 0xFF
                    else:
                        self.plot_pixel(lx, ly)
        else:
            circlebit = 0
            circle_data = CIRCLES[size]
            circle_data_bits = len(circle_data) * 8
            for ly in range(y - wsize, y + wsize + 1):
                for lx in range(x - wsize, x + wsize + 2):
                    circle_on = False
                    if circlebit < circle_data_bits:
                        circle_on = bool((circle_data[circlebit >> 3] >> (7 - (circlebit & 7))) & 1)
                    if circle_on:
                        if use_pattern:
                            if (JUNQ[junqbit >> 3] >> (7 - (junqbit & 7))) & 1:
                                self.plot_pixel(lx, ly)
                            junqbit = (junqbit + 1) & 0xFF
                        else:
                            self.plot_pixel(lx, ly)
                    circlebit += 1
    
    def _fill_bounds(self, x: int, y: int, draw_enable: int) -> bool:
        """Check if (x, y) is a fill boundary, using the given draw_enable flags.
        
        Returns True if the pixel is a boundary (should NOT be filled).
        Matches SCICompanion's FILL_BOUNDS macro:
            (dwDrawEnable & aux) && !(visual_enabled && pixel_is_white)
        """
        aux = self.aux[y, x]
        overlap = draw_enable & aux
        if overlap:
            # There IS something drawn here that overlaps with what we want to draw.
            # But if visual is enabled and the pixel is white, treat it as empty (not a boundary).
            if (draw_enable & self.VISUAL) and (self.visual[y, x] == 0x0f):
                return False
            return True
        return False
    
    def _ok_to_fill(self, x: int, y: int, draw_enable: int) -> bool:
        if not (0 <= x < WIDTH and 0 <= y < HEIGHT):
            return False
        return not self._fill_bounds(x, y, draw_enable)
    
    def flood_fill(self, start_x: int, start_y: int):
        if not (0 <= start_x < WIDTH and 0 <= start_y < HEIGHT):
            return
        
        # Compute auxSet from the color/priority/control and draw_enable.
        # Then replace draw_enable with auxSet (matching SCICompanion behavior).
        aux_set = self.get_aux_set()
        draw_enable = aux_set
        
        # Guard against filling with pure white (would hang/infinite loop).
        if self.is_white():
            return
        
        # If no screen is being drawn to, bail.
        if draw_enable == 0:
            return
        
        # Check if starting point is already a boundary.
        if self._fill_bounds(start_x, start_y, draw_enable):
            return
        
        # Use a stack (LIFO) to match SCICompanion's qstore/qretrieve behavior.
        stack = [(start_x, start_y)]
        
        while stack:
            x, y = stack.pop()
            
            if not self._ok_to_fill(x, y, draw_enable):
                continue
            
            # Plot the pixel
            if draw_enable & self.VISUAL:
                self.visual[y, x] = self.ega_color[0] if ((x ^ y) & 1) else self.ega_color[1]
            if draw_enable & self.PRIORITY:
                self.priority[y, x] = self.current_priority
            if draw_enable & self.CONTROL:
                self.control[y, x] = self.current_control
            self.aux[y, x] |= aux_set
            
            if y > 0 and self._ok_to_fill(x, y - 1, draw_enable):
                stack.append((x, y - 1))
            if x > 0 and self._ok_to_fill(x - 1, y, draw_enable):
                stack.append((x - 1, y))
            if x < WIDTH - 1 and self._ok_to_fill(x + 1, y, draw_enable):
                stack.append((x + 1, y))
            if y < HEIGHT - 1 and self._ok_to_fill(x, y + 1, draw_enable):
                stack.append((x, y + 1))
    
    def read_abs_coords(self, data: bytes, i: int) -> Tuple[int, int, int]:
        prefix = data[i]
        x = data[i + 1] | ((prefix & 0xF0) << 4)
        y = data[i + 2] | ((prefix & 0x0F) << 8)
        return x, y, i + 3
    
    def render(self, data: bytes) -> Tuple[Image.Image, Image.Image, Image.Image]:
        self.visual.fill(0x0f)
        self.priority.fill(0)
        self.control.fill(0)
        self.aux.fill(0)
        self.palettes = [list(DEFAULT_PALETTE) for _ in range(4)]
        self.locked = [0] * 40
        self.reset_state()
        
        i = 0
        x, y = 0, 0
        
        while i < len(data):
            opcode = data[i]
            
            if opcode >= 0xF0:
                i += 1
            
            if opcode == PIC_OP_END:
                break
            
            elif opcode == PIC_OP_SET_COLOR:
                color_code = data[i]
                i += 1
                # Palette number and offset within palette
                palette_num = color_code // 40
                palette_offset = color_code % 40
                # If palette_num is 0, use the global palette_to_draw instead
                palette_to_use = palette_num if palette_num != 0 else self.palette_to_draw
                # Store for UI/state tracking
                self.palette_number = palette_num
                self.palette_offset = palette_offset
                # Look up the actual color from the palette
                if self.locked[palette_offset]:
                    self.ega_color = self.palettes[0][palette_offset]
                else:
                    self.ega_color = self.palettes[palette_to_use][palette_offset]
                self.draw_enable |= self.VISUAL
            
            elif opcode == PIC_OP_DISABLE_VISUAL:
                self.draw_enable &= ~self.VISUAL
            
            elif opcode == PIC_OP_SET_PRIORITY:
                self.current_priority = data[i] & 0x0F
                i += 1
                self.draw_enable |= self.PRIORITY
            
            elif opcode == PIC_OP_DISABLE_PRIORITY:
                self.draw_enable &= ~self.PRIORITY
            
            elif opcode == PIC_OP_SET_CONTROL:
                self.current_control = data[i]
                i += 1
                self.draw_enable |= self.CONTROL
            
            elif opcode == PIC_OP_DISABLE_CONTROL:
                self.draw_enable &= ~self.CONTROL
            
            elif opcode == PIC_OP_RELATIVE_MEDIUM_LINES:
                x, y, i = self.read_abs_coords(data, i)
                while i < len(data) and data[i] < 0xF0:
                    by = data[i]
                    bx = data[i + 1]
                    i += 2
                    dy = (by & 0x7F) if not (by & 0x80) else -(by & 0x7F)
                    dx = bx if bx < 128 else bx - 256
                    self.draw_line(x, y, x + dx, y + dy)
                    x += dx
                    y += dy
            
            elif opcode == PIC_OP_RELATIVE_LONG_LINES:
                x, y, i = self.read_abs_coords(data, i)
                while i < len(data) and data[i] < 0xF0:
                    x2, y2, i = self.read_abs_coords(data, i)
                    self.draw_line(x, y, x2, y2)
                    x, y = x2, y2
            
            elif opcode == PIC_OP_RELATIVE_SHORT_LINES:
                x, y, i = self.read_abs_coords(data, i)
                while i < len(data) and data[i] < 0xF0:
                    b = data[i]
                    i += 1
                    dx = ((b >> 4) & 0x7) if not (b & 0x80) else -((b >> 4) & 0x7)
                    dy = (b & 0x7) if not (b & 0x08) else -(b & 0x7)
                    self.draw_line(x, y, x + dx, y + dy)
                    x += dx
                    y += dy
            
            elif opcode == PIC_OP_FILL:
                while i < len(data) and data[i] < 0xF0:
                    fx, fy, i = self.read_abs_coords(data, i)
                    self.flood_fill(fx, fy)
            
            elif opcode == PIC_OP_RELATIVE_PATTERNS:
                pattern_nr_byte = 0
                if self.pattern_use_pattern and i < len(data) and data[i] < 0xF0:
                    pattern_nr_byte = data[i]
                    i += 1
                x, y, i = self.read_abs_coords(data, i)
                self.draw_pattern(x, y, self.pattern_size, (pattern_nr_byte >> 1) & 0x7f, self.pattern_use_pattern, self.pattern_is_rect)
                while i < len(data) and data[i] < 0xF0:
                    pattern_nr_byte = 0
                    if self.pattern_use_pattern and i < len(data) and data[i] < 0xF0:
                        pattern_nr_byte = data[i]
                        i += 1
                    # Relative short coords for pattern
                    b = data[i]
                    i += 1
                    dx = ((b >> 4) & 0x7) if not (b & 0x80) else -((b >> 4) & 0x7)
                    dy = (b & 0x7) if not (b & 0x08) else -(b & 0x7)
                    x += dx
                    y += dy
                    self.draw_pattern(x, y, self.pattern_size, (pattern_nr_byte >> 1) & 0x7f, self.pattern_use_pattern, self.pattern_is_rect)
            
            elif opcode == PIC_OP_ABSOLUTE_PATTERNS:
                while i < len(data) and data[i] < 0xF0:
                    pattern_nr_byte = 0
                    if self.pattern_use_pattern and i < len(data) and data[i] < 0xF0:
                        pattern_nr_byte = data[i]
                        i += 1
                    x, y, i = self.read_abs_coords(data, i)
                    self.draw_pattern(x, y, self.pattern_size, (pattern_nr_byte >> 1) & 0x7f, self.pattern_use_pattern, self.pattern_is_rect)
            
            elif opcode == PIC_OP_RELATIVE_MEDIUM_PATTERNS:
                pattern_nr_byte = 0
                if self.pattern_use_pattern and i < len(data) and data[i] < 0xF0:
                    pattern_nr_byte = data[i]
                    i += 1
                x, y, i = self.read_abs_coords(data, i)
                self.draw_pattern(x, y, self.pattern_size, (pattern_nr_byte >> 1) & 0x7f, self.pattern_use_pattern, self.pattern_is_rect)
                while i < len(data) and data[i] < 0xF0:
                    pattern_nr_byte = 0
                    if self.pattern_use_pattern and i < len(data) and data[i] < 0xF0:
                        pattern_nr_byte = data[i]
                        i += 1
                    by = data[i]
                    bx = data[i + 1]
                    i += 2
                    dy = (by & 0x7F) if not (by & 0x80) else -(by & 0x7F)
                    dx = bx if bx < 128 else bx - 256
                    x += dx
                    y += dy
                    self.draw_pattern(x, y, self.pattern_size, (pattern_nr_byte >> 1) & 0x7f, self.pattern_use_pattern, self.pattern_is_rect)
            
            elif opcode == PIC_OP_SET_PATTERN:
                if i < len(data) and data[i] < 0xF0:
                    val = data[i]
                    i += 1
                    val &= 0x37  # Mask to valid bits as SCICompanion does
                    self.pattern_size = val & 0x07
                    self.pattern_is_rect = bool(val & PATTERN_FLAG_RECTANGLE)
                    self.pattern_use_pattern = bool(val & PATTERN_FLAG_USE_PATTERN)
            
            elif opcode == PIC_OP_OPX:
                if i >= len(data):
                    break
                ext = data[i]
                i += 1
                if ext == 0x00:
                    # PIC_OPX_SET_PALETTE_ENTRY: variable length pairs of (index, color)
                    while i < len(data) and data[i] < 0xF0:
                        b_index = data[i]
                        b_color = data[i + 1]
                        i += 2
                        pal_num = b_index // 40
                        pal_offset = b_index % 40
                        if pal_num < 4:
                            # color1 is high nibble, color2 is low nibble
                            color1 = (b_color >> 4) & 0x0f
                            color2 = b_color & 0x0f
                            self.palettes[pal_num][pal_offset] = (color1, color2)
                            if pal_num == 0:
                                self.locked[pal_offset] = 0xff
                elif ext == 0x01:
                    # PIC_OPX_SET_PALETTE: 1 byte palette number + 40 color bytes
                    pal_num = data[i]
                    i += 1
                    for j in range(40):
                        b_color = data[i]
                        i += 1
                        color1 = (b_color >> 4) & 0x0f
                        color2 = b_color & 0x0f
                        self.palettes[pal_num][j] = (color1, color2)
                    # If this palette is the one currently in use, update egaColor
                    if pal_num == self.palette_number:
                        self.ega_color = self.palettes[pal_num][self.palette_offset]
                elif ext == 0x08:
                    # PIC_OPX_SET_PRIORITY_TABLE: 14 bytes
                    i += 14
                else:
                    # Unknown extended opcode, try to skip gracefully
                    pass
            
            else:
                break
        
        visual_img = Image.new('RGB', (WIDTH, HEIGHT))
        for py in range(HEIGHT):
            for px in range(WIDTH):
                visual_img.putpixel((px, py), EGA_COLORS[self.visual[py, px]])
        
        priority_img = Image.fromarray(self.priority * 17, mode='L')
        control_img = Image.fromarray(self.control * 17, mode='L')
        
        return visual_img, priority_img, control_img


class SCIPictureExtractor:
    def __init__(self, game_dir: str):
        self.game_dir = Path(game_dir)
        import importlib.util
        spec = importlib.util.spec_from_file_location(
            "extract_sci_text", 
            self.game_dir.parent.parent / "extract_sci_text.py"
        )
        if spec and spec.loader:
            self.extractor_module = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(self.extractor_module)
            self.extractor = self.extractor_module.SCIResourceExtractor(game_dir)
        else:
            raise ImportError("Could not load extract_sci_text module")
    
    def extract_pic(self, pic_number: int, output_dir: str = ".") -> Optional[Tuple[str, str, str]]:
        resources = self.extractor.read_resource_map()
        pic_resources = [r for r in resources if r['type'] == 1 and r['number'] == pic_number]
        
        if not pic_resources:
            print(f"PIC {pic_number} not found")
            return None
        
        res = pic_resources[0]
        print(f"Extracting PIC {pic_number} from package {res['package']}...")
        
        header = self.extractor.read_resource_header(res['package'], res['offset'])
        if not header:
            print(f"Failed to read header for PIC {pic_number}")
            return None
        
        print(f"  Compression: {header['method_name']}, Size: {header['decompressed_size']} bytes")
        
        data = self.extractor.extract_resource_data(
            res['package'],
            res['offset'],
            header['compressed_size'],
            header['decompressed_size'],
            header['method']
        )
        
        if not data:
            print(f"Failed to extract data for PIC {pic_number}")
            return None
        
        renderer = PictureRenderer()
        visual_img, priority_img, control_img = renderer.render(data)
        
        output_path = Path(output_dir)
        output_path.mkdir(parents=True, exist_ok=True)
        
        base_name = f"pic_{pic_number:03d}"
        visual_path = output_path / f"{base_name}_visual.png"
        priority_path = output_path / f"{base_name}_priority.png"
        control_path = output_path / f"{base_name}_control.png"
        
        visual_img.save(visual_path)
        priority_img.save(priority_path)
        control_img.save(control_path)
        
        print(f"  Saved: {visual_path}")
        
        return (str(visual_path), str(priority_path), str(control_path))


def main():
    import argparse
    
    parser = argparse.ArgumentParser(description="Extract SCI picture resources")
    parser.add_argument("--pic", type=int, help="Specific PIC number to extract")
    parser.add_argument("--output", "-o", default="pics", help="Output directory")
    parser.add_argument("--game-dir", "-g", default="King's Quest IV - The Perils of Rosella (1988)/KQ4",
                        help="Game directory")
    
    args = parser.parse_args()
    
    if not os.path.exists(args.game_dir):
        print(f"Error: Game directory not found: {args.game_dir}")
        sys.exit(1)
    
    print(f"Extracting pictures from: {args.game_dir}")
    
    extractor = SCIPictureExtractor(args.game_dir)
    
    if args.pic is not None:
        extractor.extract_pic(args.pic, args.output)
    else:
        resources = extractor.extractor.read_resource_map()
        pic_resources = sorted(set(r['number'] for r in resources if r['type'] == 1))
        print(f"Found {len(pic_resources)} PIC resources")
        for pic_num in pic_resources:
            extractor.extract_pic(pic_num, args.output)
    
    print(f"\nExtraction complete! Images saved to {args.output}/")


if __name__ == "__main__":
    main()
