# PyStorm - Python Bindings for StormLib PyStorm provides Python bindings for [StormLib](https://github.com/ladislav-zezula/StormLib), a library for reading and writing MPQ (MoPaQ) archives used by Blizzard Entertainment games. ## Features - **Read MPQ archives**: Open and extract files from MPQ archives - **Write MPQ archives**: Create new archives and add files - **Modify archives**: Add, remove, and rename files in existing archives - **File search**: Find files in archives using wildcards - **Archive verification**: Verify archive integrity - **High-level and low-level APIs**: Choose the interface that suits your needs - **Cross-platform**: Works on Windows, Linux, and macOS - **๐ŸŽฎ Asset Extraction**: Tools for extracting and organizing game assets - **๐Ÿ–ผ๏ธ GUI Inspector**: Visual MPQ archive browser ## Quick Links - ๐Ÿ“ฆ **[Asset Extraction Guide](ASSET_EXTRACTION.md)** - Extract & organize MPQ assets - ๐Ÿ“– **[StarCraft Assets Guide](STARCRAFT_ASSETS.md)** - File format documentation - ๐Ÿ–ผ๏ธ **[MPQ Inspector](MPQ_INSPECTOR_README.md)** - GUI tool documentation - ๐Ÿงช **[Testing Guide](TESTING.md)** - Testing and debugging ## Requirements - Python 3.7 or higher - StormLib shared library (libstorm.so, StormLib.dll, or libstorm.dylib) ## Installation ### 1. Install StormLib First, you need to install the StormLib C library: #### Linux/macOS ```bash # Clone the repository git clone https://github.com/ladislav-zezula/StormLib.git cd StormLib # Build and install mkdir build && cd build cmake .. make sudo make install # Update library cache (Linux) sudo ldconfig ``` #### Windows Download pre-built binaries from the [StormLib releases](https://github.com/ladislav-zezula/StormLib/releases) or build from source using Visual Studio. ### 2. Install PyStorm ```bash pip install pystorm ``` Or install from source: ```bash git clone https://github.com/enne2/pystorm.git cd pystorm pip install -e . ``` ## Quick Start ### High-Level API (Recommended) ```python from pystorm import MPQArchive # Open an existing archive with MPQArchive("example.mpq") as archive: # Check if a file exists if archive.has_file("path/to/file.txt"): # Read a file with archive.open_file("path/to/file.txt") as mpq_file: content = mpq_file.read() print(content.decode('utf-8')) # Extract a file archive.extract_file("path/to/file.txt", "output.txt") # List all files files = archive.find_files("*") for file_info in files: print(f"{file_info['name']}: {file_info['size']} bytes") # Create a new archive with MPQArchive("new_archive.mpq", flags=MPQ_CREATE_ARCHIVE_V2) as archive: # Add a file archive.add_file("local_file.txt", "archived_name.txt") # Remove a file archive.remove_file("old_file.txt") # Rename a file archive.rename_file("old_name.txt", "new_name.txt") # Flush changes to disk archive.flush() ``` ### Low-Level API ```python from pystorm import ( SFileOpenArchive, SFileCloseArchive, SFileOpenFileEx, SFileReadFile, SFileCloseFile, SFileGetFileSize, SFILE_OPEN_FROM_MPQ ) # Open archive archive_handle = SFileOpenArchive("example.mpq") try: # Open file file_handle = SFileOpenFileEx(archive_handle, "file.txt", SFILE_OPEN_FROM_MPQ) try: # Get file size file_size = SFileGetFileSize(file_handle) # Read file data = SFileReadFile(file_handle, file_size) print(data.decode('utf-8')) finally: SFileCloseFile(file_handle) finally: SFileCloseArchive(archive_handle) ``` ## API Reference ### MPQArchive Class High-level wrapper for MPQ archive operations. #### Methods - `__init__(path, flags=0, priority=0)`: Open an MPQ archive - `close()`: Close the archive - `has_file(filename)`: Check if a file exists - `open_file(filename, search_scope=SFILE_OPEN_FROM_MPQ)`: Open a file - `extract_file(filename, output_path, search_scope=SFILE_OPEN_FROM_MPQ)`: Extract a file - `add_file(local_path, archived_name, flags=MPQ_FILE_COMPRESS, compression=MPQ_COMPRESSION_ZLIB)`: Add a file - `remove_file(filename, search_scope=SFILE_OPEN_FROM_MPQ)`: Remove a file - `rename_file(old_name, new_name)`: Rename a file - `find_files(mask="*")`: Find files matching a pattern - `flush()`: Flush changes to disk - `compact(listfile=None)`: Compact the archive - `verify()`: Verify archive integrity ### MPQFile Class High-level wrapper for file operations within an MPQ archive. #### Methods - `__init__(archive, filename, search_scope=SFILE_OPEN_FROM_MPQ)`: Open a file - `close()`: Close the file - `get_size()`: Get file size - `read(size=None)`: Read data from the file - `seek(offset, whence=FILE_BEGIN)`: Seek to a position ### Constants #### Open Flags - `MPQ_OPEN_NO_LISTFILE`: Don't load the listfile - `MPQ_OPEN_NO_ATTRIBUTES`: Don't load attributes - `MPQ_OPEN_READ_ONLY`: Open in read-only mode #### Create Flags - `MPQ_CREATE_LISTFILE`: Create with listfile - `MPQ_CREATE_ATTRIBUTES`: Create with attributes - `MPQ_CREATE_ARCHIVE_V1`: Create version 1 archive (max 4GB) - `MPQ_CREATE_ARCHIVE_V2`: Create version 2 archive - `MPQ_CREATE_ARCHIVE_V3`: Create version 3 archive - `MPQ_CREATE_ARCHIVE_V4`: Create version 4 archive #### File Flags - `MPQ_FILE_COMPRESS`: Compress the file - `MPQ_FILE_ENCRYPTED`: Encrypt the file - `MPQ_FILE_FIX_KEY`: Use fixed encryption key - `MPQ_FILE_SINGLE_UNIT`: Store as single unit - `MPQ_FILE_DELETE_MARKER`: File is marked for deletion - `MPQ_FILE_SECTOR_CRC`: File has sector CRCs #### Compression Types - `MPQ_COMPRESSION_HUFFMANN`: Huffman compression - `MPQ_COMPRESSION_ZLIB`: ZLIB compression - `MPQ_COMPRESSION_PKWARE`: PKWARE compression - `MPQ_COMPRESSION_BZIP2`: BZIP2 compression - `MPQ_COMPRESSION_SPARSE`: Sparse compression - `MPQ_COMPRESSION_ADPCM_MONO`: ADPCM mono compression - `MPQ_COMPRESSION_ADPCM_STEREO`: ADPCM stereo compression - `MPQ_COMPRESSION_LZMA`: LZMA compression ## Tools Included ### ๐ŸŽฎ Asset Extraction Tool Extract and organize game assets for engine development: ```bash python extract_starcraft_assets.py ``` Automatically categorizes files into: - `audio/` - Sound effects, music - `graphics/` - Sprites, images - `video/` - Cinematics - `data/` - Game data tables - And more... See **[ASSET_EXTRACTION.md](ASSET_EXTRACTION.md)** for complete guide. ### ๐Ÿ–ผ๏ธ MPQ Inspector (GUI) Visual MPQ archive browser with tkinter: ```bash python mpq_inspector.py ``` Features: - Browse and open MPQ archives visually - View file listings with compression details - Extract individual files or entire archives - Verify archive integrity - File information viewer See [MPQ_INSPECTOR_README.md](MPQ_INSPECTOR_README.md) for full documentation. ### ๐Ÿ“Š Asset Demo Tool Explore and analyze extracted assets: ```bash python demo_assets.py ``` Features: - List assets by category - Analyze audio/graphics files - Search functionality - Interactive menu ## Examples More examples can be found in the `examples/` directory. ### Extract All Files from an Archive ```python from pystorm import MPQArchive from pathlib import Path def extract_all(archive_path, output_dir): output_dir = Path(output_dir) output_dir.mkdir(parents=True, exist_ok=True) with MPQArchive(archive_path) as archive: files = archive.find_files("*") for file_info in files: filename = file_info['name'] output_path = output_dir / filename output_path.parent.mkdir(parents=True, exist_ok=True) try: archive.extract_file(filename, str(output_path)) print(f"Extracted: {filename}") except Exception as e: print(f"Failed to extract {filename}: {e}") extract_all("game.mpq", "extracted_files") ``` ### Create an Archive from a Directory ```python from pystorm import MPQArchive, MPQ_CREATE_ARCHIVE_V2, MPQ_FILE_COMPRESS from pathlib import Path def create_archive_from_dir(source_dir, archive_path): source_dir = Path(source_dir) with MPQArchive(archive_path, flags=MPQ_CREATE_ARCHIVE_V2) as archive: for file_path in source_dir.rglob("*"): if file_path.is_file(): # Get relative path for archived name archived_name = str(file_path.relative_to(source_dir)) # Replace backslashes with forward slashes archived_name = archived_name.replace("\\", "/") archive.add_file(str(file_path), archived_name) print(f"Added: {archived_name}") archive.flush() create_archive_from_dir("my_files", "output.mpq") ``` ### Verify Archive Integrity ```python from pystorm import MPQArchive with MPQArchive("example.mpq") as archive: result = archive.verify() if result == 0: print("Archive is valid!") else: print(f"Archive verification failed with code: {result}") ``` ## Troubleshooting ### Library Not Found Error If you get an error about not finding the StormLib library: 1. Make sure StormLib is installed on your system 2. Check that the library is in your system's library path 3. On Linux, try running `sudo ldconfig` after installation 4. Alternatively, copy the library file to the `pystorm` package directory ### Permission Errors Some operations (like creating or modifying archives) require write permissions. Make sure you have the necessary permissions for the files and directories you're working with. ## Contributing Contributions are welcome! Please feel free to submit a Pull Request. ## License This project is licensed under the MIT License - see the LICENSE file for details. ## Credits - **StormLib**: Created and maintained by [Ladislav Zezula](https://github.com/ladislav-zezula) - **PyStorm**: Python bindings by Matteo Benedetto ## Links - [StormLib Repository](https://github.com/ladislav-zezula/StormLib) - [PyStorm Repository](https://github.com/enne2/pystorm) - [MPQ Format Documentation](http://www.zezula.net/en/mpq/mpqformat.html)