-
-
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.
* parser for screen effect * screen effect documentation * ruff * linting fix * update readme --------- Co-authored-by: Marco Köpcke <[email protected]>
- Loading branch information
1 parent
d7163a5
commit 7af86f4
Showing
6 changed files
with
399 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,87 @@ | ||
Screen Effect File Format | ||
=============== | ||
The file ``/EFFECT/effect.bin`` contains vfx used for dungeon battle, weather effects, and some ground mode vfx. | ||
|
||
The file contains 293 effect entries, most of which are WAN format. | ||
Specifically the files effect0268-00289 are not WAN. | ||
They are used for screen effects in moves and cutscenes. | ||
|
||
|
||
The file uses SIR0 headers to store its pointers. General SIR0 details can be found in the main SIR0 documentation. | ||
|
||
+-----------------------+-----------------------------+---------------------+------------------------------+--------------------------------------------------------------------------------------------------+ | ||
| Name | Offset | Size (Per Element) | # of Elements | Description | | ||
+=======================+=============================+=====================+==============================+==================================================================================================+ | ||
| SIR0 Header | 0x00 | 16 Bytes | 1 | Details in the SIR0 documentation | | ||
+-----------------------+-----------------------------+---------------------+------------------------------+--------------------------------------------------------------------------------------------------+ | ||
| Animation Data | Pointed by Animation Ptrs | Varies | Specified by Content Header | A 36-byte header with draw parameters, plus a list of draw instructions to put textures onscreen | | ||
+-----------------------+-----------------------------+---------------------+------------------------------+--------------------------------------------------------------------------------------------------+ | ||
| Animation Pointers | Pointed by Content Header | 4 Bytes | Specified by Content Header | List of pointers to each frame of Animation Data | | ||
+-----------------------+-----------------------------+---------------------+------------------------------+--------------------------------------------------------------------------------------------------+ | ||
| 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. | | ||
+-----------------------+-----------------------------+---------------------+------------------------------+--------------------------------------------------------------------------------------------------+ | ||
| Image Data | Pointed by Content Header | Varies | 1 | One continuous block of image data that is read nibble-by-nibble. | | ||
+-----------------------+-----------------------------+---------------------+------------------------------+--------------------------------------------------------------------------------------------------+ | ||
| 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 6 sections, each with 4 bytes: | ||
|
||
1. Number of frames in the animation | ||
2. Pointer to start of Animation Data | ||
3. Unknown | ||
4. Pointer to image data | ||
5. Pointer to palette data | ||
6. Unknown | ||
|
||
Animation Data | ||
~~~~~~~~~~~~~~ | ||
|
||
Represents one frame of screen animation per element. | ||
It is always a header of 36 bytes, followed by a variable number of 2-byte draw instructions. | ||
This frame of animation is drawn tile-by-tile: Starting from the top-left, row by row, then column by column. | ||
Each tile is 8x8 texture. First the number of tiles to draw are specified by the header (rows x columns) | ||
Then, by reading each two-byte value and interpreting it as either a draw instruction that advances one tile, | ||
or a skip instruction that advances the specified number of tiles. | ||
Once all tiles (rows x columns) have been traversed, the frame has finished drawing and will not be read any further. | ||
|
||
Header | ||
------ | ||
|
||
The header 36 bytes is split up into the following regions: | ||
|
||
AA AA BB BB CC CC DD DD EE EE 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF 00 00 00 GG 00 00 | ||
|
||
A: Always a multiple of section A | ||
B: Always a multiple of section D | ||
C: Number of textures per row. | ||
D: Number of textures per column. | ||
E: Frame duration in 1/60th of a second. | ||
F: Transparency. | ||
G: Unknown. One of various possible numbers: 0x00,0x40,0x60,0x7F,0x80,0xC0,0xF0,0xFF | ||
|
||
Draw Instructions | ||
----------------- | ||
|
||
Two bytes that tell the game how to draw a single 8x8 texture in the current position, or how many tiles to skip. | ||
The draw instruction is a little endian, 16 bit value made up of 4 parts. | ||
|
||
AAA0 BCDD DDDD DDDD | ||
|
||
A: If 1, draw the texture specified in D. If 0, skip a number of tiles specified in D. | ||
B: Flip the source image on Y axis before drawing | ||
C: Flip the source image on X axis before drawing | ||
D: Draw value. If selected as a tile to draw, interpret this as the point in imgData to start reading an 8x8 texture. | ||
|
||
|
||
Credits | ||
------- | ||
Thanks to BitDrifter for experimenting and deducing Header and Draw Instructions. |
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.effect_screen.model import ScreenEffectFile | ||
from skytemple_files.graphics.effect_screen.sheets import ExportSheets | ||
|
||
|
||
class ScreenEffectHandler(DataHandler[ScreenEffectFile]): | ||
@classmethod | ||
def deserialize(cls, data: bytes, **kwargs: OptionalKwargs) -> ScreenEffectFile: | ||
from skytemple_files.common.types.file_types import FileType | ||
|
||
return FileType.SIR0.unwrap_obj(FileType.SIR0.deserialize(data), ScreenEffectFile) # type: ignore | ||
|
||
@classmethod | ||
def serialize(cls, data: ScreenEffectFile, **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, screen_effect: ScreenEffectFile, include_alpha: bool) -> None: | ||
return ExportSheets(out_dir, screen_effect, include_alpha) # 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,166 @@ | ||
# 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 | ||
SCREEN_ATTR_DrawMask = 0x8000 # 1000 0000 0000 0000 | ||
SCREEN_ATTR_FlipYMask = 0x0800 # 0000 1000 0000 0000 | ||
SCREEN_ATTR_FlipXMask = 0x0400 # 0000 0100 0000 0000 | ||
SCREEN_ATTR_ValueMask = 0x03FF # 0000 0011 1111 1111 | ||
|
||
DEBUG_PRINT = False | ||
|
||
|
||
class ScreenEffectFile(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.ImportScreenEffect(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 ImportScreenEffect(self, data, ptrEffect=0): | ||
in_file = BytesIO() | ||
in_file.write(data) | ||
in_file.seek(0) | ||
|
||
##Read Effect header: ptr to AnimData, ptr to ImgData, PaletteData | ||
in_file.seek(ptrEffect) | ||
nbFrames = int.from_bytes(in_file.read(4), "little") | ||
ptrAnimData = int.from_bytes(in_file.read(4), "little") | ||
updateUnusedStats([], "Unk#3", int.from_bytes(in_file.read(4), "little")) | ||
ptrImgData = int.from_bytes(in_file.read(4), "little") | ||
ptrPaletteDataBlock = int.from_bytes(in_file.read(4), "little") | ||
updateUnusedStats([], "Unk#1", int.from_bytes(in_file.read(2), "little")) | ||
updateUnusedStats([], "Unk#2", int.from_bytes(in_file.read(2), "little")) | ||
|
||
##Read palette info | ||
nbColorsPerRow = 16 | ||
in_file.seek(ptrPaletteDataBlock) | ||
totalColors = (ptrImgData - 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() < ptrEffect: | ||
px = int.from_bytes(in_file.read(1), "little") | ||
self.imgData.append(px % 16) | ||
self.imgData.append(px // 16) | ||
|
||
ptrFrames = [] | ||
in_file.seek(ptrAnimData) | ||
for idx in range(nbFrames): | ||
##read the location | ||
ptrFrame = int.from_bytes(in_file.read(4), "little") | ||
ptrFrames.append(ptrFrame) | ||
|
||
self.animData = [] | ||
for frame_idx, ptrFrame in enumerate(ptrFrames): | ||
in_file.seek(ptrFrame) | ||
|
||
updateUnusedStats([], "Unk#5", int.from_bytes(in_file.read(2), "little")) | ||
updateUnusedStats([], "Unk#7", int.from_bytes(in_file.read(2), "little")) | ||
|
||
# Must be 0x21 or else the animation doesn't play | ||
updateUnusedStats([], "Unk#6", int.from_bytes(in_file.read(2), "little")) | ||
row_height = int.from_bytes(in_file.read(2), "little") | ||
frame_dur = int.from_bytes(in_file.read(2), "little") | ||
in_file.read(18) | ||
alpha = int.from_bytes(in_file.read(2), "little") | ||
in_file.read(3) | ||
updateUnusedStats([], "Unk#4", int.from_bytes(in_file.read(1), "little")) | ||
in_file.read(2) | ||
|
||
pieces = [] | ||
totalSlots = 0 | ||
while True: | ||
drawValue = int.from_bytes(in_file.read(2), "little") | ||
skip = (SCREEN_ATTR_DrawMask & drawValue) == 0 | ||
flipX = (SCREEN_ATTR_FlipXMask & drawValue) != 0 | ||
flipY = (SCREEN_ATTR_FlipYMask & drawValue) != 0 | ||
drawArg = SCREEN_ATTR_ValueMask & drawValue | ||
|
||
pieces.append(ScreenPiece(drawArg, flipX, flipY, skip)) | ||
if skip: | ||
totalSlots += drawArg | ||
else: | ||
totalSlots += 1 | ||
|
||
if totalSlots >= row_height * 33: | ||
break | ||
|
||
end_ptr = ptrAnimData | ||
if frame_idx < len(ptrFrames) - 1: | ||
end_ptr = ptrFrames[frame_idx + 1] | ||
|
||
cur_pos = in_file.tell() | ||
if cur_pos != end_ptr and cur_pos != end_ptr - 2: | ||
raise Exception() | ||
|
||
self.animData.append(ScreenFrame(frame_dur, alpha, row_height, pieces)) | ||
|
||
|
||
class ScreenFrame(object): | ||
def __init__(self, duration, alpha, rowHeight, pieces): | ||
self.duration = duration | ||
self.alpha = alpha | ||
self.rowHeight = rowHeight | ||
self.pieces = pieces | ||
|
||
|
||
class ScreenPiece(object): | ||
def __init__(self, index, flipX, flipY, skip): | ||
self.index = index | ||
self.flipX = flipX | ||
self.flipY = flipY | ||
self.skip = skip | ||
|
||
|
||
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)) |
Oops, something went wrong.