-
-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* sma * linting and hook up to filetypes * Update README.rst * dependencies lint cleanup * ruff fix * ruff format fix * fix animations for icons --------- Co-authored-by: Marco Köpcke <[email protected]>
- Loading branch information
1 parent
27d9b8a
commit 0ac0a2e
Showing
6 changed files
with
324 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
SMA File Format | ||
=============== | ||
manpu_su.sma and manpu_ma.sma are both found in the /SYSTEM/ folder. manpu_su.sma contains status effect icon data and has the format detailed in this file. manpu_ma.sma contains no image data and its structure/use is currently unknown. | ||
|
||
|
||
The file uses SIR0 headers to store its pointers. General SIR0 details can be found in the main SIR0 documentation. The sections below will cover only manpu_su.sma-specific blocks of data. | ||
|
||
+-----------------------+-----------------------------+---------------------+------------------------------+--------------------------------------------------------------------------------------------------+ | ||
| Name | Offset | Size (Per Element) | # of Elements | Description | | ||
+=======================+=============================+=====================+==============================+==================================================================================================+ | ||
| SIR0 Header | 0x00 | 16 Bytes | 1 | Details in the SIR0 documentation | | ||
+-----------------------+-----------------------------+---------------------+------------------------------+--------------------------------------------------------------------------------------------------+ | ||
| Animation Data | Pointed by Content Header | 12 | Specified by Content Header | Each element contains animation data of a status icon, including size and number of frames. | | ||
+-----------------------+-----------------------------+---------------------+------------------------------+--------------------------------------------------------------------------------------------------+ | ||
| Image Data | Pointed by Content Header | Varies | 1 | One continuous block of image data that is read nibble-by-nibble. | | ||
+-----------------------+-----------------------------+---------------------+------------------------------+--------------------------------------------------------------------------------------------------+ | ||
| Palette Data | Pointed by Content Header | 64 Bytes | 16 | A block of palette data that is separated into 16 palettes, each with 16 colors of 4 bytes each. | | ||
+-----------------------+-----------------------------+---------------------+------------------------------+--------------------------------------------------------------------------------------------------+ | ||
| Content Header | Pointed by SIR0 Header | 32 Bytes | 1 | Contains the pointers to Animation Data, Image Data, Palette Data, and the number of animations. | | ||
+-----------------------+-----------------------------+---------------------+------------------------------+--------------------------------------------------------------------------------------------------+ | ||
| Pointer Offsets List | Pointed by SIR0 Header | 1 Byte | Varies | Details in the SIR0 documentation | | ||
+-----------------------+-----------------------------+---------------------+------------------------------+--------------------------------------------------------------------------------------------------+ | ||
| SIR0 Padding | After Pointer Offsets List | Varies | --- | Details in the SIR0 documentation | | ||
+-----------------------+-----------------------------+---------------------+------------------------------+--------------------------------------------------------------------------------------------------+ | ||
|
||
|
||
Content Header | ||
~~~~~~~~~~~~~~ | ||
|
||
The 32-byte header appears to be split into 8 sections, each with 4 bytes: | ||
|
||
1. Unknown | ||
2. Pointer to start of Animation Data | ||
3. Number of animations | ||
4. Pointer to image data | ||
5. Unknown | ||
6. Pointer to palette data | ||
7. Unknown | ||
8. Unknown | ||
|
||
Animation Data | ||
~~~~~~~~~~~~~~ | ||
|
||
This block contains an array of elements, each 12 bytes and representing an animation. Contains 7 elements: | ||
|
||
AA BB CC CC DD DD 00 00 EE EE FF FF | ||
|
||
A: Width of all frames in this animation, in blocks (8 pixels) | ||
B: Height of all frames in this animation, in blocks (8 pixels) | ||
C: Unknown | ||
D: The offset, in bytes, from which to start reading from the image data. | ||
E. The number of frames in this animation. | ||
F. Unknown. Possibly a mapping table for the destination of where to load in memory? | ||
|
||
A fully zeroed out animation exists as the first element. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# Copyright 2020-2024 Capypara and the SkyTemple Contributors | ||
# | ||
# This file is part of SkyTemple. | ||
# | ||
# SkyTemple is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 3 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# SkyTemple is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with SkyTemple. If not, see <https://www.gnu.org/licenses/>. | ||
|
||
from __future__ import annotations | ||
|
||
BANNER_FONT_ENTRY_LEN = 0x8 | ||
BANNER_FONT_DATA_LEN = 576 | ||
BANNER_FONT_SIZE = 24 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# Copyright 2020-2024 Capypara and the SkyTemple Contributors | ||
# | ||
# This file is part of SkyTemple. | ||
# | ||
# SkyTemple is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 3 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# SkyTemple is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with SkyTemple. If not, see <https://www.gnu.org/licenses/>. | ||
from __future__ import annotations | ||
|
||
from skytemple_files.common.types.data_handler import DataHandler | ||
from skytemple_files.common.util import OptionalKwargs | ||
from skytemple_files.graphics.sma.model import SmaFile | ||
from skytemple_files.graphics.sma.sheets import ExportSheets | ||
|
||
|
||
class SmaHandler(DataHandler[SmaFile]): | ||
@classmethod | ||
def deserialize(cls, data: bytes, **kwargs: OptionalKwargs) -> SmaFile: | ||
from skytemple_files.common.types.file_types import FileType | ||
|
||
return FileType.SIR0.unwrap_obj(FileType.SIR0.deserialize(data), SmaFile) # type: ignore | ||
|
||
@classmethod | ||
def serialize(cls, data: SmaFile, **kwargs: OptionalKwargs) -> bytes: | ||
from skytemple_files.common.types.file_types import FileType | ||
|
||
return FileType.SIR0.serialize(FileType.SIR0.wrap_obj(data)) # type: ignore | ||
|
||
@classmethod | ||
def export_sheets(cls, out_dir: str, sma: SmaFile, palette_idx: int) -> None: | ||
return ExportSheets(out_dir, sma, palette_idx) # type: ignore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
# Copyright 2020-2024 Capypara and the SkyTemple Contributors | ||
# | ||
# This file is part of SkyTemple. | ||
# | ||
# SkyTemple is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 3 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# SkyTemple is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with SkyTemple. If not, see <https://www.gnu.org/licenses/>. | ||
# mypy: ignore-errors | ||
from __future__ import annotations | ||
|
||
from range_typed_integers import u32 | ||
|
||
from io import BytesIO | ||
|
||
from skytemple_files.container.sir0.sir0_serializable import Sir0Serializable | ||
|
||
TEX_SIZE = 8 | ||
|
||
DEBUG_PRINT = False | ||
|
||
|
||
class SmaFile(Sir0Serializable): | ||
def __init__(self, data: bytes | None = None, header_pnt: int = 0): | ||
if data is None: | ||
self.imgData = None | ||
self.animData = None | ||
self.customPalette = None | ||
else: | ||
self.ImportSma(data, header_pnt) | ||
|
||
@classmethod | ||
def sir0_unwrap( | ||
cls, | ||
content_data: bytes, | ||
data_pointer: int, | ||
) -> Sir0Serializable: | ||
return cls(content_data, data_pointer) | ||
|
||
def sir0_serialize_parts(self) -> tuple[bytes, list[u32], u32 | None]: | ||
raise NotImplementedError("Serialization not currently supported.") | ||
|
||
def ImportSma(self, data, ptrSMA=0): | ||
in_file = BytesIO() | ||
in_file.write(data) | ||
in_file.seek(0) | ||
|
||
##Read SMA header: ptr to AnimData, ptr to ImgData, PaletteData | ||
in_file.seek(ptrSMA) | ||
updateUnusedStats([], "Unk#1", int.from_bytes(in_file.read(4), "little")) | ||
ptrAnimData = int.from_bytes(in_file.read(4), "little") | ||
nbFrames = int.from_bytes(in_file.read(4), "little") | ||
ptrImgData = int.from_bytes(in_file.read(4), "little") | ||
updateUnusedStats([], "Unk#2", int.from_bytes(in_file.read(4), "little")) | ||
ptrPaletteDataBlock = int.from_bytes(in_file.read(4), "little") | ||
updateUnusedStats([], "Unk#3", int.from_bytes(in_file.read(4), "little")) | ||
updateUnusedStats([], "Unk#4", int.from_bytes(in_file.read(4), "little")) | ||
|
||
##Read palette info | ||
nbColorsPerRow = 16 | ||
in_file.seek(ptrPaletteDataBlock) | ||
totalColors = (ptrSMA - ptrPaletteDataBlock) // 4 | ||
totalPalettes = totalColors // nbColorsPerRow | ||
self.customPalette = [] | ||
for ii in range(totalPalettes): | ||
palette = [] | ||
for jj in range(nbColorsPerRow): | ||
red = int.from_bytes(in_file.read(1), "little") | ||
blue = int.from_bytes(in_file.read(1), "little") | ||
green = int.from_bytes(in_file.read(1), "little") | ||
in_file.read(1) | ||
palette.append((red, blue, green, 255)) | ||
self.customPalette.append(palette) | ||
|
||
##read image data | ||
self.imgData = [] | ||
in_file.seek(ptrImgData) | ||
while in_file.tell() < ptrPaletteDataBlock: | ||
px = int.from_bytes(in_file.read(1), "little") | ||
self.imgData.append(px % 16) | ||
self.imgData.append(px // 16) | ||
|
||
self.animData = [] | ||
in_file.seek(ptrAnimData) | ||
for ii in range(nbFrames): | ||
blockX = int.from_bytes(in_file.read(1), "little") | ||
blockY = int.from_bytes(in_file.read(1), "little") | ||
updateUnusedStats([], "Unk#5", int.from_bytes(in_file.read(2), "little")) | ||
byteOffset = int.from_bytes(in_file.read(2), "little") | ||
in_file.read(2) | ||
blockLength = int.from_bytes(in_file.read(2), "little") | ||
updateUnusedStats([], "Unk#6", int.from_bytes(in_file.read(2), "little")) | ||
anim = SmaAnim(blockX, blockY, byteOffset, blockLength) | ||
self.animData.append(anim) | ||
|
||
|
||
class SmaAnim(object): | ||
def __init__(self, blockWidth, blockHeight, byteOffset, frameCount): | ||
self.blockWidth = blockWidth | ||
self.blockHeight = blockHeight | ||
self.byteOffset = byteOffset | ||
self.frameCount = frameCount | ||
|
||
|
||
def updateUnusedStats(log_params, name, val): | ||
# stats.append([log_params[0], log_params[1], name, log_params[2:], val]) | ||
if DEBUG_PRINT and val != 0: | ||
print(" " + name + ":" + str(val)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
# Copyright 2020-2024 Capypara and the SkyTemple Contributors | ||
# | ||
# This file is part of SkyTemple. | ||
# | ||
# SkyTemple is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 3 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# SkyTemple is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with SkyTemple. If not, see <https://www.gnu.org/licenses/>. | ||
# mypy: ignore-errors | ||
|
||
from __future__ import annotations | ||
|
||
import os | ||
|
||
from PIL import Image | ||
|
||
from skytemple_files.graphics.sma.model import TEX_SIZE | ||
|
||
|
||
def ExportSheets(outDir, effectData, paletteIndex): | ||
if not os.path.isdir(outDir): | ||
os.makedirs(outDir) | ||
|
||
for anim_idx, statusEffectAnim in enumerate(effectData.animData): | ||
if statusEffectAnim.blockWidth == 0: | ||
continue | ||
frames = [] | ||
for idx in range(statusEffectAnim.frameCount): | ||
frameImg = GenerateStatusFrame( | ||
effectData.imgData, | ||
effectData.customPalette, | ||
paletteIndex, | ||
statusEffectAnim.byteOffset, | ||
idx, | ||
statusEffectAnim.blockWidth, | ||
statusEffectAnim.blockHeight, | ||
) | ||
frames.append(frameImg) | ||
animImg = CombineFramesIntoAnim(frames) | ||
animImg.save(os.path.join(outDir, "A-" + format(anim_idx, "02d") + "-" + format(paletteIndex, "02d") + ".png")) | ||
|
||
|
||
def GenerateStatusFrame(imgData, inPalette, paletteIndex, byteOffset, idx, width, height): | ||
##creates a tex piece out of the imgdata, with the specified piece index and dimensions | ||
newImg = Image.new("RGBA", (width * TEX_SIZE, height * TEX_SIZE), (0, 0, 0, 0)) | ||
datas = [(0, 0, 0, 0)] * (width * TEX_SIZE * height * TEX_SIZE) | ||
|
||
lengthPixels = TEX_SIZE * TEX_SIZE * width * height | ||
imgPx = [] | ||
# flatten the list to include all strips | ||
for nn in range(lengthPixels): | ||
imgPx.append(imgData[byteOffset * 2 + idx * lengthPixels + nn]) | ||
|
||
for yy in range(height): | ||
for xx in range(width): | ||
blockIndex = yy * width + xx | ||
texPosition = blockIndex * TEX_SIZE * TEX_SIZE | ||
|
||
for py in range(TEX_SIZE): | ||
for px in range(TEX_SIZE): | ||
paletteElement = imgPx[texPosition + py * TEX_SIZE + px] | ||
##print('palette:' + str(paletteIndex) + ' element:' + str(paletteElement)) | ||
if paletteElement == 0: | ||
color = (0, 0, 0, 0) | ||
else: | ||
color = inPalette[paletteIndex][paletteElement] | ||
|
||
imgPosition = (xx * TEX_SIZE + px, yy * TEX_SIZE + py) | ||
datas[imgPosition[1] * width * TEX_SIZE + imgPosition[0]] = color | ||
newImg.putdata(datas) | ||
return newImg | ||
|
||
|
||
def CombineFramesIntoAnim(img_list): | ||
##combines all frames into a horizontal animation sheet | ||
##ASSUMES ALL IMGS ARE THE SAME SIZE | ||
size = img_list[0].size | ||
imgNew = Image.new("RGBA", (size[0] * len(img_list), size[1]), (0, 0, 0, 0)) | ||
for img_index in range(len(img_list)): | ||
imgNew.paste(img_list[img_index], (size[0] * img_index, 0), img_list[img_index]) | ||
return imgNew |