Adding New Platforms: Plugin & Extension Guide¶
MioROM is designed with an extensible, plugin-friendly architecture. Developers can add support for new console architectures, disc images, and cartridge containers without modifying or forking the core library.
Extension Mechanisms¶
MioROM provides two parallel avenues for platform extensibility:
1. Inheritance: Subclass miorom.rom.base.BaseRomHandler.
2. Structural Typing (Protocols): Implement miorom.rom.protocols.RomHandlerProtocol without inheriting from any MioROM class.
Both approaches work identically with RomManager.
1. Defining a Platform Handler¶
A platform handler defines three fundamental operations:
- can_handle(data: bytes, filepath: Optional[str]) -> bool: Inspects magic bytes or file extension.
- unpack(data: bytes, output_dir: str, **kwargs) -> Dict[str, Any]: Decompresses and extracts files into output_dir.
- repack(input_dir: str, **kwargs) -> bytes: Compiles directory contents back into a valid ROM image and recalibrates hardware checksums.
Example: Custom Console Handler¶
import os
import struct
from typing import Dict, Any, Optional
from miorom.rom.base import BaseRomHandler
from miorom.core.schema import BinaryStruct, FixedString, U32
from miorom.security import sanitize_extract_path
class WonderSwanHeader(BinaryStruct):
_endian = "<"
magic = FixedString(4, default="WS01")
rom_size = U32()
class WonderSwanRomHandler(BaseRomHandler):
name = "wonderswan"
description = "Bandai WonderSwan / Color ROM Image"
extensions = [".ws", ".wsc"]
def can_handle(self, data: bytes, filepath: Optional[str] = None) -> bool:
if filepath:
ext = os.path.splitext(filepath)[1].lower()
if ext in self.extensions:
return True
if len(data) >= 0x10 and data.startswith(b"WS01"):
return True
return False
def unpack(self, data: bytes, output_dir: str, **kwargs) -> Dict[str, Any]:
os.makedirs(output_dir, exist_ok=True)
# 1. Parse header
hdr = WonderSwanHeader.from_bytes(data)
# 2. Extract system header
sys_dir = os.path.join(output_dir, "sys")
os.makedirs(sys_dir, exist_ok=True)
with open(os.path.join(sys_dir, "header.bin"), "wb") as f:
f.write(data[:0x10])
# 3. Extract ROM payload
root_dir = os.path.join(output_dir, "root")
os.makedirs(root_dir, exist_ok=True)
with open(os.path.join(root_dir, "rom.bin"), "wb") as f:
f.write(data[0x10:])
return {
"format": self.name,
"platform": "Bandai WonderSwan",
"rom_size": len(data),
}
def repack(self, input_dir: str, **kwargs) -> bytes:
sys_dir = os.path.join(input_dir, "sys")
root_dir = os.path.join(input_dir, "root")
with open(os.path.join(sys_dir, "header.bin"), "rb") as f:
hdr_bytes = f.read()
with open(os.path.join(root_dir, "rom.bin"), "rb") as f:
payload = f.read()
return hdr_bytes + payload
2. In-Code Registration¶
Register your handler dynamically at runtime with RomManager:
from miorom.rom.manager import RomManager
# Instantiate manager
manager = RomManager()
# Register custom handler
manager.register(WonderSwanRomHandler())
# Now unpack and repack will auto-detect your platform!
meta = manager.unpack("game.wsc", "unpacked_ws/")
manager.repack("unpacked_ws/", output_path="game_patched.wsc")
3. Automatic Discovery via entry_points¶
To publish a standalone package (e.g. pip install miorom-wonderswan), expose your handler in your package's pyproject.toml under the miorom.platforms entry-point group:
[project.entry-points."miorom.platforms"]
wonderswan = "miorom_wonderswan.handler:WonderSwanRomHandler"
When users install your package, miorom's default RomManager will automatically discover and register your handler on startup via importlib.metadata.entry_points. Users can immediately use miorom unpack game.wsc out/ via CLI without writing any code.
4. Custom Compression Codecs¶
Custom compression algorithms can also be registered into MioROM's central codec registry:
from miorom.registry import register_codec
class CustomLzCodec:
@staticmethod
def decompress(data: bytes) -> bytes:
...
@staticmethod
def compress(data: bytes) -> bytes:
...
# Register by unique algorithm identifier
register_codec("custom_lz", CustomLzCodec)