mirror of
https://github.com/WerWolv/ImHex-Patterns.git
synced 2026-03-27 23:37:04 -05:00
* patterns: Add support for smk * patterns: Use builtin function and separate SMK struct --------- Co-authored-by: paxcut <53811119+paxcut@users.noreply.github.com>
219 lines
5.5 KiB
Rust
219 lines
5.5 KiB
Rust
#pragma author xZise
|
|
#pragma description Smacker video file
|
|
#pragma endian little
|
|
#pragma magic [53 4D 4B 32] @ 0x0
|
|
|
|
import std.core;
|
|
import std.io;
|
|
import std.mem;
|
|
|
|
bitfield HeaderFlags {
|
|
ContainsRingFrame : 1;
|
|
YInterlaced : 1;
|
|
YDoubled : 1;
|
|
padding : 29;
|
|
};
|
|
|
|
bitfield HeaderAudioFlags {
|
|
isCompressed : 1;
|
|
hasAudio : 1;
|
|
is16BitAudio : 1;
|
|
isStereo : 1;
|
|
soundDecompression : 2;
|
|
padding : 2;
|
|
audioSampleRate : 24;
|
|
};
|
|
|
|
fn format_frame_rate(auto frame_rate) {
|
|
float fps;
|
|
if (frame_rate > 0) {
|
|
fps = 1000.0 / frame_rate;
|
|
} else if (frame_rate < 0) {
|
|
fps = 100000.0 / (-frame_rate);
|
|
} else {
|
|
fps = 10;
|
|
}
|
|
return std::format("{} fps", fps);
|
|
};
|
|
|
|
struct Header {
|
|
char Signature[4];
|
|
u32 Width;
|
|
u32 Height;
|
|
u32 FrameCount;
|
|
s32 FrameRate [[format("format_frame_rate")]];
|
|
HeaderFlags Flags;
|
|
u32 AudioSize[7];
|
|
u32 TreesSize;
|
|
u32 MMap_Size;
|
|
u32 MClr_Size;
|
|
u32 Full_Size;
|
|
u32 Type_Size;
|
|
HeaderAudioFlags AudioRate[7];
|
|
padding[4];
|
|
};
|
|
|
|
fn format_size(auto size) {
|
|
if (size.keyframe != 0) {
|
|
return std::format("[K] {} B", size.size);
|
|
}
|
|
return std::format("[ ] {} B", size.size);
|
|
};
|
|
|
|
bitfield Size {
|
|
keyframe : 1;
|
|
padding : 1;
|
|
dwordCount : 30;
|
|
|
|
u32 size = dwordCount * 4;
|
|
} [[format("format_size")]];
|
|
|
|
|
|
bitfield FrameType {
|
|
ContainsPaletteRecord : 1;
|
|
ContainsAudioTrack0 : 1;
|
|
ContainsAudioTrack1 : 1;
|
|
ContainsAudioTrack2 : 1;
|
|
ContainsAudioTrack3 : 1;
|
|
ContainsAudioTrack4 : 1;
|
|
ContainsAudioTrack5 : 1;
|
|
ContainsAudioTrack6 : 1;
|
|
};
|
|
|
|
fn format_palette_copy_only(auto copy_only) {
|
|
return std::format("copy: {0}", copy_only.copy);
|
|
};
|
|
|
|
bitfield PaletteCopyOnly {
|
|
copy: 7;
|
|
mode: 1;
|
|
} [[format("format_palette_copy_only")]];
|
|
|
|
fn format_palette_copy_skip(auto copy_skip) {
|
|
return std::format("copy: {0}, skip: {1}", copy_skip.copy, copy_skip.skip);
|
|
};
|
|
|
|
bitfield PaletteCopySkip {
|
|
copy: 6;
|
|
mode: 2;
|
|
skip: 8;
|
|
} [[format("format_palette_copy_skip")]];
|
|
|
|
bitfield PaletteColor {
|
|
blue: 6 [[color("0000FF")]];
|
|
mode: 2;
|
|
green: 6 [[color("00FF00")]];
|
|
padding: 2;
|
|
red: 6 [[color("FF0000")]];
|
|
padding: 2;
|
|
|
|
u8 r8 = red << 2 | red >> 4;
|
|
u8 g8 = green << 2 | green >> 4;
|
|
u8 b8 = blue << 2 | blue >> 4;
|
|
} [[hex::inline_visualize("color", r8, g8, b8, 0xff)]];
|
|
|
|
fn format_palette_chunk_block(auto entry) {
|
|
u8 first = entry.type;
|
|
if (first & 0x80 == 0x80) {
|
|
return format_palette_copy_only(entry.copy);
|
|
} else if (first & 0x40 == 0x40) {
|
|
return format_palette_copy_skip(entry.copy_skip);
|
|
} else {
|
|
return std::format("color: 0x{0:02x}{1:02x}{2:02x}", entry.color.r8, entry.color.g8, entry.color.b8);
|
|
}
|
|
};
|
|
|
|
enum PaletteChunkBlockType: u8 {
|
|
Color = 0x00 ... 0x3F,
|
|
CopySkip = 0x40 ... 0x7F,
|
|
CopyOnly = 0x80 ... 0xFF
|
|
};
|
|
|
|
// Unfortunately the match expression does not support ranges, so this is a helper for the following code:
|
|
// PaletteChunkBlockType type = std::mem::read_unsigned($, 1)
|
|
fn get_palette_chunk_block_type() {
|
|
u8 type = std::mem::read_unsigned($, 1);
|
|
if (type & 0x80 == 0x80) {
|
|
return PaletteChunkBlockType::CopyOnly;
|
|
} else if (type & 0x40 == 0x40) {
|
|
return PaletteChunkBlockType::CopySkip;
|
|
} else {
|
|
return PaletteChunkBlockType::Color;
|
|
}
|
|
};
|
|
|
|
fn get_last_position() {
|
|
PaletteChunkBlockType type = get_palette_chunk_block_type();
|
|
match (type) {
|
|
(PaletteChunkBlockType::CopyOnly): return $;
|
|
(PaletteChunkBlockType::CopySkip): return $ + 1;
|
|
(PaletteChunkBlockType::Color): return $ + 2;
|
|
}
|
|
};
|
|
|
|
struct PaletteChunkBlock {
|
|
PaletteChunkBlockType type = get_palette_chunk_block_type() [[export]];
|
|
match (type) {
|
|
(PaletteChunkBlockType::CopyOnly): PaletteCopyOnly copy;
|
|
(PaletteChunkBlockType::CopySkip): PaletteCopySkip copy_skip;
|
|
(PaletteChunkBlockType::Color): PaletteColor color;
|
|
}
|
|
} [[format("format_palette_chunk_block")]];
|
|
|
|
struct PaletteChunk {
|
|
u8 length;
|
|
u128 end = $ + length * 4 - 1;
|
|
PaletteChunkBlock blocks[while(get_last_position() < end)];
|
|
if ($ < end) {
|
|
padding[end - $];
|
|
}
|
|
};
|
|
|
|
struct AudioTrack<auto trackIndex> {
|
|
u32 length;
|
|
if (parent.parent.header.AudioRate[trackIndex].isCompressed) {
|
|
u32 decompressedSize;
|
|
}
|
|
u8 trackData[length - ($ - addressof(this))];
|
|
};
|
|
|
|
struct FramesData {
|
|
u32 frame_index = std::core::array_index();
|
|
u32 size = parent.sizes[frame_index].size;
|
|
if (parent.frameTypes[frame_index].ContainsPaletteRecord) {
|
|
PaletteChunk palette;
|
|
}
|
|
if (parent.frameTypes[frame_index].ContainsAudioTrack0) {
|
|
AudioTrack<0> audioTrack0;
|
|
}
|
|
if (parent.frameTypes[frame_index].ContainsAudioTrack1) {
|
|
AudioTrack<1> audioTrack1;
|
|
}
|
|
if (parent.frameTypes[frame_index].ContainsAudioTrack2) {
|
|
AudioTrack<2> audioTrack2;
|
|
}
|
|
if (parent.frameTypes[frame_index].ContainsAudioTrack3) {
|
|
AudioTrack<3> audioTrack3;
|
|
}
|
|
if (parent.frameTypes[frame_index].ContainsAudioTrack4) {
|
|
AudioTrack<4> audioTrack4;
|
|
}
|
|
if (parent.frameTypes[frame_index].ContainsAudioTrack5) {
|
|
AudioTrack<5> audioTrack5;
|
|
}
|
|
if (parent.frameTypes[frame_index].ContainsAudioTrack6) {
|
|
AudioTrack<6> audioTrack6;
|
|
}
|
|
u8 video[size - ($ - addressof(this))];
|
|
};
|
|
|
|
struct SMK {
|
|
Header header;
|
|
Size sizes[header.FrameCount];
|
|
FrameType frameTypes[header.FrameCount] ;
|
|
u8 trees[header.TreesSize];
|
|
FramesData frames[header.FrameCount];
|
|
};
|
|
|
|
SMK smk @ 0x00 [[inline]];
|