Source code for scfile.content.base
"""Content representation containers."""
from dataclasses import dataclass, field
from typing import Any, ClassVar
from warnings import warn
from scfile.enums import FileKind
from .models import Feature, ModelMeta, ModelScene
from .regions import RegionChunk
from .textures import CubemapTexture, DefaultTexture, Texture, TextureMeta
type DocumentPrimitive = int | float | bytes | str
type DocumentValue = None | DocumentPrimitive | list[DocumentValue] | dict[str, DocumentValue]
type ArchiveEntry = tuple[str, bytes]
def _deprecated(name: str, replacement: str) -> None:
warn(f"{name} is deprecated; use {replacement} instead.", DeprecationWarning, stacklevel=3)
def _alias(name: str, target: str) -> property:
parent, _, attribute = target.rpartition(".")
def get(content: Any) -> Any:
cls = type(content).__name__
_deprecated(f"{cls}.{name}", f"{cls}.{target}")
return getattr(getattr(content, parent) if parent else content, attribute)
def set(content: Any, value: Any) -> None:
cls = type(content).__name__
_deprecated(f"{cls}.{name}", f"{cls}.{target}")
setattr(getattr(content, parent) if parent else content, attribute, value)
return property(get, set)
[docs]
class BaseContent:
"""Base class for content representations."""
kind: ClassVar[FileKind]
[docs]
@dataclass
class ModelContent(BaseContent):
"""Content representation of 3D model."""
kind: ClassVar[FileKind] = FileKind.MODEL
meta: ModelMeta = field(default_factory=ModelMeta)
scene: ModelScene = field(default_factory=ModelScene)
[docs]
def has(
self,
feature: Feature,
) -> bool:
return self.scene.has(feature)
[docs]
@dataclass
class TextureContent(BaseContent):
"""Content representation of texture."""
kind: ClassVar[FileKind] = FileKind.TEXTURE
width: int = 0
height: int = 0
meta: TextureMeta = field(default_factory=TextureMeta)
texture: Texture = field(default_factory=DefaultTexture)
mipmap_count = _alias("mipmap_count", "meta.mipmap_count")
format = _alias("format", "meta.format")
path_hash = _alias("path_hash", "meta.path_hash")
@property
def is_cubemap(self) -> bool:
return isinstance(self.texture, CubemapTexture)
@property
def is_compressed(self) -> bool:
return self.fourcc in (b"DXT1", b"DXT3", b"DXT5", b"ATI1", b"ATI2", b"DX10")
@property
def fourcc(self) -> bytes:
match self.meta.format:
case b"DXN_X":
return b"ATI1"
case b"DXN_XY":
return b"ATI2"
case b"RGBA32F":
return b"DX10"
case _:
return self.meta.format
[docs]
@dataclass
class ImageContent(BaseContent):
"""Content representation of image."""
kind: ClassVar[FileKind] = FileKind.IMAGE
image: bytes = field(default_factory=bytes)
[docs]
@dataclass
class ArchiveContent(BaseContent):
"""Content representation of archive."""
kind: ClassVar[FileKind] = FileKind.ARCHIVE
entries: list[ArchiveEntry] = field(default_factory=list)
[docs]
@dataclass
class DocumentContent(BaseContent):
"""Content representation of structured document."""
kind: ClassVar[FileKind] = FileKind.DOCUMENT
value: DocumentValue = None
[docs]
@dataclass
class RegionContent(BaseContent):
"""Content representation of world region."""
kind: ClassVar[FileKind] = FileKind.REGION
x: int = 0
z: int = 0
offsets: list[int] = field(default_factory=list)
counts: list[int] = field(default_factory=list)
uuids: list[bytes] = field(default_factory=list)
chunks: list[RegionChunk] = field(default_factory=list)
rx = _alias("rx", "x")
rz = _alias("rz", "z")
sector_offsets = _alias("sector_offsets", "offsets")
sector_counts = _alias("sector_counts", "counts")