|
|
#!/usr/bin/env python3 |
|
|
""" |
|
|
Example: Loading StarCraft Assets into a Game Engine |
|
|
===================================================== |
|
|
|
|
|
This is a conceptual example showing how to load and use extracted |
|
|
StarCraft assets in a hypothetical game engine. |
|
|
|
|
|
This demonstrates: |
|
|
- Loading audio files |
|
|
- Loading and displaying graphics |
|
|
- Parsing data files |
|
|
- Organizing assets for runtime use |
|
|
|
|
|
Note: This is educational/demonstration code for engine developers. |
|
|
""" |
|
|
|
|
|
from pathlib import Path |
|
|
from typing import Dict, List, Optional |
|
|
import struct |
|
|
|
|
|
|
|
|
class AssetManager: |
|
|
""" |
|
|
Example asset manager for a game engine. |
|
|
|
|
|
This shows how to organize and load StarCraft assets for use |
|
|
in an alternative game engine. |
|
|
""" |
|
|
|
|
|
def __init__(self, assets_dir: str = "assets"): |
|
|
self.assets_dir = Path(assets_dir) |
|
|
self.audio_cache = {} |
|
|
self.graphics_cache = {} |
|
|
self.data_cache = {} |
|
|
|
|
|
print(f"AssetManager initialized with: {self.assets_dir}") |
|
|
|
|
|
def load_audio(self, audio_id: int) -> Optional[bytes]: |
|
|
""" |
|
|
Load an audio file by its ID. |
|
|
|
|
|
Args: |
|
|
audio_id: Numeric ID (e.g., 123 for File00000123.wav) |
|
|
|
|
|
Returns: |
|
|
Audio data as bytes, or None if not found |
|
|
""" |
|
|
# Check cache first |
|
|
if audio_id in self.audio_cache: |
|
|
return self.audio_cache[audio_id] |
|
|
|
|
|
# Find file |
|
|
filename = f"File{audio_id:08d}.wav" |
|
|
audio_path = self.assets_dir / "audio" / filename |
|
|
|
|
|
if not audio_path.exists(): |
|
|
print(f"⚠️ Audio {audio_id} not found: {filename}") |
|
|
return None |
|
|
|
|
|
# Load file |
|
|
with open(audio_path, 'rb') as f: |
|
|
data = f.read() |
|
|
|
|
|
# Cache it |
|
|
self.audio_cache[audio_id] = data |
|
|
|
|
|
print(f"✓ Loaded audio {audio_id}: {len(data)} bytes") |
|
|
return data |
|
|
|
|
|
def load_pcx_image(self, filename: str) -> Optional[Dict]: |
|
|
""" |
|
|
Load a PCX image file. |
|
|
|
|
|
Args: |
|
|
filename: Name of PCX file |
|
|
|
|
|
Returns: |
|
|
Dictionary with image data, or None if failed |
|
|
""" |
|
|
# Check cache |
|
|
if filename in self.graphics_cache: |
|
|
return self.graphics_cache[filename] |
|
|
|
|
|
pcx_path = self.assets_dir / "graphics" / filename |
|
|
|
|
|
if not pcx_path.exists(): |
|
|
print(f"⚠️ Image not found: {filename}") |
|
|
return None |
|
|
|
|
|
# Try to load with PIL if available |
|
|
try: |
|
|
from PIL import Image |
|
|
|
|
|
img = Image.open(pcx_path) |
|
|
|
|
|
image_data = { |
|
|
'width': img.width, |
|
|
'height': img.height, |
|
|
'mode': img.mode, |
|
|
'data': img.tobytes(), |
|
|
'palette': img.getpalette() if img.mode == 'P' else None, |
|
|
'path': str(pcx_path) |
|
|
} |
|
|
|
|
|
self.graphics_cache[filename] = image_data |
|
|
print(f"✓ Loaded image {filename}: {img.width}x{img.height} {img.mode}") |
|
|
|
|
|
return image_data |
|
|
|
|
|
except ImportError: |
|
|
print("⚠️ PIL/Pillow not available - returning raw data") |
|
|
with open(pcx_path, 'rb') as f: |
|
|
raw_data = f.read() |
|
|
return {'raw': raw_data, 'path': str(pcx_path)} |
|
|
except Exception as e: |
|
|
print(f"✗ Failed to load {filename}: {e}") |
|
|
return None |
|
|
|
|
|
def load_palette(self, palette_name: str) -> Optional[List[tuple]]: |
|
|
""" |
|
|
Load a color palette file. |
|
|
|
|
|
Args: |
|
|
palette_name: Name of palette file (.pal or .wpe) |
|
|
|
|
|
Returns: |
|
|
List of (R, G, B) tuples, or None if failed |
|
|
""" |
|
|
pal_path = self.assets_dir / "data" / palette_name |
|
|
|
|
|
if not pal_path.exists(): |
|
|
print(f"⚠️ Palette not found: {palette_name}") |
|
|
return None |
|
|
|
|
|
try: |
|
|
with open(pal_path, 'rb') as f: |
|
|
data = f.read(768) # 256 colors * 3 bytes |
|
|
|
|
|
palette = [] |
|
|
for i in range(0, 768, 3): |
|
|
r, g, b = data[i], data[i+1], data[i+2] |
|
|
palette.append((r, g, b)) |
|
|
|
|
|
print(f"✓ Loaded palette {palette_name}: 256 colors") |
|
|
return palette |
|
|
|
|
|
except Exception as e: |
|
|
print(f"✗ Failed to load palette {palette_name}: {e}") |
|
|
return None |
|
|
|
|
|
def search_assets(self, pattern: str) -> List[Path]: |
|
|
""" |
|
|
Search for assets matching a pattern. |
|
|
|
|
|
Args: |
|
|
pattern: Search pattern (e.g., "*.wav", "unit*") |
|
|
|
|
|
Returns: |
|
|
List of matching file paths |
|
|
""" |
|
|
matches = list(self.assets_dir.rglob(pattern)) |
|
|
return [m for m in matches if m.is_file()] |
|
|
|
|
|
def get_asset_info(self, category: str) -> Dict: |
|
|
""" |
|
|
Get information about a category of assets. |
|
|
|
|
|
Args: |
|
|
category: Category name (e.g., "audio", "graphics") |
|
|
|
|
|
Returns: |
|
|
Dictionary with count and size information |
|
|
""" |
|
|
category_dir = self.assets_dir / category |
|
|
|
|
|
if not category_dir.exists(): |
|
|
return {'exists': False} |
|
|
|
|
|
files = [f for f in category_dir.rglob("*") if f.is_file()] |
|
|
total_size = sum(f.stat().st_size for f in files) |
|
|
|
|
|
return { |
|
|
'exists': True, |
|
|
'count': len(files), |
|
|
'total_size': total_size, |
|
|
'files': [f.name for f in files[:10]] # Sample |
|
|
} |
|
|
|
|
|
|
|
|
class GameEngine: |
|
|
""" |
|
|
Conceptual game engine showing asset integration. |
|
|
|
|
|
This demonstrates how a real game engine might use the |
|
|
extracted StarCraft assets. |
|
|
""" |
|
|
|
|
|
def __init__(self): |
|
|
self.assets = AssetManager() |
|
|
self.loaded_sounds = {} |
|
|
self.loaded_textures = {} |
|
|
|
|
|
print("\n" + "="*80) |
|
|
print("Game Engine Initialized") |
|
|
print("="*80) |
|
|
|
|
|
def preload_common_sounds(self): |
|
|
"""Preload commonly used sound effects""" |
|
|
print("\nPreloading common sounds...") |
|
|
|
|
|
# Example: Load first 10 sound files |
|
|
for sound_id in range(29, 39): # Based on actual file numbers |
|
|
audio_data = self.assets.load_audio(sound_id) |
|
|
if audio_data: |
|
|
self.loaded_sounds[sound_id] = audio_data |
|
|
|
|
|
print(f"✓ Preloaded {len(self.loaded_sounds)} sounds") |
|
|
|
|
|
def preload_ui_graphics(self): |
|
|
"""Preload UI graphics""" |
|
|
print("\nPreloading UI graphics...") |
|
|
|
|
|
# Search for PCX files |
|
|
pcx_files = self.assets.search_assets("*.pcx") |
|
|
|
|
|
# Load first few as examples |
|
|
for pcx_file in pcx_files[:3]: |
|
|
img_data = self.assets.load_pcx_image(pcx_file.name) |
|
|
if img_data: |
|
|
self.loaded_textures[pcx_file.stem] = img_data |
|
|
|
|
|
print(f"✓ Preloaded {len(self.loaded_textures)} textures") |
|
|
|
|
|
def print_asset_summary(self): |
|
|
"""Print summary of available assets""" |
|
|
print("\n" + "="*80) |
|
|
print("Asset Summary") |
|
|
print("="*80) |
|
|
|
|
|
categories = ['audio', 'graphics', 'video', 'data', 'maps'] |
|
|
|
|
|
for category in categories: |
|
|
info = self.assets.get_asset_info(category) |
|
|
if info['exists']: |
|
|
size_mb = info['total_size'] / 1024 / 1024 |
|
|
print(f"\n📁 {category.upper()}/") |
|
|
print(f" Files: {info['count']}") |
|
|
print(f" Size: {size_mb:.1f} MB") |
|
|
|
|
|
if info['files']: |
|
|
print(f" Sample files:") |
|
|
for filename in info['files'][:3]: |
|
|
print(f" - {filename}") |
|
|
|
|
|
def play_sound(self, sound_id: int): |
|
|
""" |
|
|
Play a sound effect (conceptual). |
|
|
|
|
|
In a real engine, this would: |
|
|
1. Get audio data from cache or load it |
|
|
2. Decode the WAV format |
|
|
3. Send to audio mixer |
|
|
4. Play through audio device |
|
|
""" |
|
|
print(f"\n🔊 Playing sound {sound_id}...") |
|
|
|
|
|
if sound_id in self.loaded_sounds: |
|
|
audio_data = self.loaded_sounds[sound_id] |
|
|
print(f" Using cached audio: {len(audio_data)} bytes") |
|
|
else: |
|
|
audio_data = self.assets.load_audio(sound_id) |
|
|
if not audio_data: |
|
|
print(f" ✗ Sound not found!") |
|
|
return |
|
|
|
|
|
# In real engine: decode and play audio |
|
|
print(f" ✓ Audio ready for playback") |
|
|
|
|
|
def render_texture(self, texture_name: str): |
|
|
""" |
|
|
Render a texture (conceptual). |
|
|
|
|
|
In a real engine, this would: |
|
|
1. Get texture from cache or load it |
|
|
2. Upload to GPU |
|
|
3. Render to screen with proper coordinates |
|
|
""" |
|
|
print(f"\n🖼️ Rendering texture: {texture_name}") |
|
|
|
|
|
if texture_name in self.loaded_textures: |
|
|
tex_data = self.loaded_textures[texture_name] |
|
|
if 'width' in tex_data: |
|
|
print(f" Using cached texture: {tex_data['width']}x{tex_data['height']}") |
|
|
else: |
|
|
print(f" Using raw texture data") |
|
|
else: |
|
|
print(f" ⚠️ Texture not in cache - would load on demand") |
|
|
|
|
|
# In real engine: upload to GPU and render |
|
|
print(f" ✓ Texture rendered") |
|
|
|
|
|
|
|
|
def demo(): |
|
|
"""Run a demonstration of asset loading""" |
|
|
print("\n" + "="*80) |
|
|
print("StarCraft Asset Loading Demo") |
|
|
print("="*80) |
|
|
print("\nThis demonstrates how a game engine would load extracted assets") |
|
|
print() |
|
|
|
|
|
# Check if assets exist |
|
|
if not Path("assets").exists(): |
|
|
print("❌ Assets directory not found!") |
|
|
print("\nPlease extract assets first:") |
|
|
print(" python extract_starcraft_assets.py") |
|
|
return |
|
|
|
|
|
# Create engine instance |
|
|
engine = GameEngine() |
|
|
|
|
|
# Show available assets |
|
|
engine.print_asset_summary() |
|
|
|
|
|
# Preload common assets |
|
|
print("\n" + "="*80) |
|
|
print("Preloading Assets") |
|
|
print("="*80) |
|
|
engine.preload_common_sounds() |
|
|
engine.preload_ui_graphics() |
|
|
|
|
|
# Simulate gameplay |
|
|
print("\n" + "="*80) |
|
|
print("Simulating Game Actions") |
|
|
print("="*80) |
|
|
|
|
|
# Play some sounds |
|
|
engine.play_sound(29) |
|
|
engine.play_sound(35) |
|
|
|
|
|
# Render some textures |
|
|
if engine.loaded_textures: |
|
|
first_texture = list(engine.loaded_textures.keys())[0] |
|
|
engine.render_texture(first_texture) |
|
|
|
|
|
# Summary |
|
|
print("\n" + "="*80) |
|
|
print("Demo Complete!") |
|
|
print("="*80) |
|
|
print(f"\nLoaded in memory:") |
|
|
print(f" Sounds: {len(engine.loaded_sounds)}") |
|
|
print(f" Textures: {len(engine.loaded_textures)}") |
|
|
print("\nThis demonstrates the basics of asset loading.") |
|
|
print("A real engine would include:") |
|
|
print(" - Audio decoding and playback") |
|
|
print(" - Texture uploading to GPU") |
|
|
print(" - Sprite rendering with animations") |
|
|
print(" - Data table parsing (units, weapons, etc.)") |
|
|
print(" - Map loading and terrain rendering") |
|
|
print() |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo()
|
|
|
|