diff --git a/README.md b/README.md index 96fb2931..d64ffe9f 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | BLF | | [`patterns/blf.hexpat`](patterns/blf.hexpat) | Vector BLF Frame Logging Files | | BMP | `image/bmp` | [`patterns/bmp.hexpat`](patterns/bmp.hexpat) | OS2/Windows Bitmap files | | BIN | | [`patterns/selinux.hexpat`](patterns/selinux.pat) | SE Linux modules | +| BINK Container | `video/vnd.radgamettools.bink` | [`patterns/bink_container.hexpat`](patterns/bink_container.hexpat) | [RAD Game Tools Bink Video Container files](https://en.wikipedia.org/wiki/Bink_Video) | | BINKA | | [`patterns/binka.hexpat`](patterns/binka.pat) | RAD Game Tools Bink Audio (BINKA) files | | BSON | `application/bson` | [`patterns/bson.hexpat`](patterns/bson.hexpat) | BSON (Binary JSON) format | | BTRFS Send Stream | | [`patterns/btrfs_send_stream.hexpat`](patterns/btrfs_send_stream.hexpat) | BTRFS Send Stream format | diff --git a/patterns/bink_container.hexpat b/patterns/bink_container.hexpat new file mode 100644 index 00000000..3eb780b1 --- /dev/null +++ b/patterns/bink_container.hexpat @@ -0,0 +1,143 @@ +// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. +// If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + + +#pragma endian little +#pragma magic [42 49 4B] @ 0x00 +#pragma MIME video/vnd.radgamettools.bink + +#pragma author Xavier "XJDHDR" du Hecquet de Rauville +#pragma description Bink Video Container - RAD Game Tools + + +// GLOBALS +u32 CurrentFrame = 0; + + +// ENUMS +enum WidthHeightScaling : u8 +{ + NoScaling = 0, + HeightDoubled = 1, + HeightInterlaced = 2, + WidthDoubled = 3, + WidthHeightDoubled = 4, + WidthHeightInterlaced = 5, +}; + +enum AudioAlgorithm : bool +{ + DCT = 0, + FFT = 1, +}; + + +// BITFIELDS +bitfield VideoFlags +{ + Unknown1 : 16; + bool Greyscale : 1; + Unknown2 : 2; + bool HasAlphaPLane : 1; + Unknown3 : 7; + WidthHeightScaling Scaling : 4; +}; + +bitfield AudioPropertyFlags +{ + Unknown1 : 11; + AudioAlgorithm Algorithm : 1; + bool IsStereo : 1; + bool HasAlphaPLane : 1; + Unknown2 : 2; +}; + +fn maskFirstBit(u32 originalValue) { + return (originalValue & 0xFFFFFFFE); +}; + +bitfield FrameAddressData { + bool IsKeyframe: 1 [[no_unique_address]]; + FrameAddress: 32 [[transform("maskFirstBit")]]; +}; + + +// STRUCTS +struct AudioChannelData +{ + u16 Unknown; + u16 NotAuthoritativeAudioChannelQty; +}; + +struct AudioChannelProperties +{ + u16 SampleRate; + AudioPropertyFlags NotAuthoritativeAudioChannelQty; +}; + +struct BinkHeader +{ + char MagicNumber[3]; + char BinkVersion; + u32 SizeOfRemainingBytes; + u32 NumberOfFrames; + u32 LargestFrameSize; + u32 NumberOfFramesAgain; + u32 VideoFrameWidth; + u32 VideoFrameHeight; + u32 VideoFPSDividend; + u32 VideoFPSDivisor; + VideoFlags VideoFlags; + u32 AudioTrackQty; + AudioChannelData AudioChannelData[AudioTrackQty]; + AudioChannelProperties AudioChannelProperties[AudioTrackQty]; + u32 AudioTrackIDs[AudioTrackQty]; +}; + +struct BinkAudioTrack +{ + u32 LengthOfRemainingTrackData; + + if (LengthOfRemainingTrackData > 0) + { + u32 SamplesQty; + u8 AudioPacket[LengthOfRemainingTrackData - 4]; + } +}; + +fn calculateVideoPacketSize(BinkHeader header, ref FrameAddressData frameAddresses, ref BinkAudioTrack audioTracks) +{ + u32 thisFrameTotalSize = frameAddresses[CurrentFrame + 1].FrameAddress - frameAddresses[CurrentFrame].FrameAddress; + + u32 sizeOfAllAudioTracks = 0; + for (u8 i = 0, i < header.AudioTrackQty, i += 1) + { + sizeOfAllAudioTracks += audioTracks[i].LengthOfRemainingTrackData + 4; + } + + u32 videoPacketSize = thisFrameTotalSize - sizeOfAllAudioTracks; + return videoPacketSize; +}; + +struct BinkFrame +{ + BinkAudioTrack AudioTracks[parent.Header.AudioTrackQty]; + u8 VideoPacket[calculateVideoPacketSize(parent.Header, parent.FrameAddresses, AudioTracks)]; + + CurrentFrame += 1; + $ = parent.FrameAddresses[CurrentFrame].FrameAddress; +}; + +struct BinkFile +{ + BinkHeader Header; + FrameAddressData FrameAddresses[Header.NumberOfFrames + 1]; + + $ = FrameAddresses[0].FrameAddress; + + BinkFrame Frames[Header.NumberOfFrames]; +}; + + +// ENTRYPOINT +BinkFile binkFile @ 0x0; diff --git a/tests/patterns/test_data/bink_container.hexpat.bik b/tests/patterns/test_data/bink_container.hexpat.bik new file mode 100644 index 00000000..1c662d67 Binary files /dev/null and b/tests/patterns/test_data/bink_container.hexpat.bik differ