diff --git a/docs/file-formats/SPR.md b/docs/file-formats/SPR.md index d2d9b8b1..52dbcd96 100644 --- a/docs/file-formats/SPR.md +++ b/docs/file-formats/SPR.md @@ -43,75 +43,182 @@ This feature isn't used for the truecolor (TGA) segments as pixels are stored in ### Transparency -Alpha values in the BMP palette are completely ignored. RGBA colors will be stored raw and not premultiplied. +Alpha values in the BMP palette should be completely ignored. RGBA colors are stored raw and not premultiplied. -The color with palette index 0 should be considered the "background color". It must be cleared manually, though. +The color with palette index 0 can be considered the "background color". It must be cleared manually on load. ## Layout -### Version 1.1 +### Versions + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + +```cpp title="SPR File Format (v1.1)" +struct BitmapSprite { + uint16_t ImageWidth; + uint16_t ImageHeight; + uint8_t PaletteIndices[ImageWidth * ImageHeight]; +}; + +struct PaletteColor { + uint8_t Red; + uint8_t Green; + uint8_t Blue; + uint8_t Alpha; +}; + +struct RagnarokSPR { + char Signature[2]; + uint8_t VersionMajor; + uint8_t VersionMinor; + uint16_t BitmapImageCount; + struct BitmapSprite BitmapSprites[BitmapImageCount]; + struct PaletteColor BitmapColors[256]; +}; +``` + + + + +```cpp title="SPR File Format (v2.0)" +struct BitmapSprite { + uint16_t ImageWidth; + uint16_t ImageHeight; + uint8_t PaletteIndices[ImageWidth * ImageHeight]; +}; + +// diff-add-start +struct TrueColorPixel { + uint8_t Alpha; + uint8_t Blue; + uint8_t Green; + uint8_t Red; +}; + +struct TrueColorSprite { + uint16_t ImageWidth; + uint16_t ImageHeight; + struct TrueColorPixel PixelBuffer[ImageWidth * ImageHeight]; +}; +// diff-add-end + +struct PaletteColor { + uint8_t Red; + uint8_t Green; + uint8_t Blue; + uint8_t Alpha; +}; + +struct RagnarokSPR { + char Signature[2]; + uint8_t VersionMajor; + uint8_t VersionMinor; + uint16_t BitmapImageCount; + // diff-add + uint16_t TrueColorImageCount; + struct BitmapSprite BitmapSprites[BitmapImageCount]; + // diff-add + struct TrueColorSprite TrueColorSprites[TrueColorImageCount]; + struct PaletteColor BitmapColors[256]; +}; +``` + + + + +```cpp title="SPR File Format (v2.1)" +// diff-remove +struct BitmapSprite { +// diff-add +struct CompressedBitmapSprite { + uint16_t ImageWidth; + uint16_t ImageHeight; + // diff-remove + uint8_t PaletteIndices[ImageWidth * ImageHeight]; + // diff-add-start + uint16_t CompressedBufferSize; + uint8_t CompressedPaletteIndices[CompressedBufferSize]; + // diff-add-end +}; + +struct TrueColorPixel { + uint8_t Alpha; + uint8_t Blue; + uint8_t Green; + uint8_t Red; +}; + +struct TrueColorSprite { + uint16_t ImageWidth; + uint16_t ImageHeight; + struct TrueColorPixel PixelBuffer[ImageWidth * ImageHeight]; +}; + +struct PaletteColor { + uint8_t Red; + uint8_t Green; + uint8_t Blue; + uint8_t Alpha; +}; + +struct RagnarokSPR { + char Signature[2]; + uint8_t VersionMajor; + uint8_t VersionMinor; + uint16_t BitmapImageCount; + uint16_t TrueColorImageCount; + // diff-remove + struct BitmapSprite BitmapSprites[BitmapImageCount]; + // diff-add + struct CompressedBitmapSprite CompressedBitmapSprites[BitmapImageCount]; + struct TrueColorSprite TrueColorSprites[TrueColorImageCount]; + struct PaletteColor BitmapColors[256]; +}; +``` + + + + +### Fields + +#### Signature + +Uniquely identifies the file format. Must always be `"SP"` (ASCII-encoded, fixed-size string). + +#### Version + +Signals the presence of optional features. Version `1.1` is used only in [Arcturus](/arcturus), but not in any known build of the RO client. + +#### BitmapSprites + +Indexed-color sprite images (BMP segment). All pixels are stored as palette indices that need to be resolved on load. + +A size of `(-1, -1)` indicates an invalid image, which consists of a single blank pixel that can safely be discarded. + +#### BitmapColors + +The BMP color palette, consisting of RGBA pixels. Alpha can be discarded. Equivalent to the file contents of a [PAL](/file-formats/pal) file. + +#### TrueColorSprites + +True color sprite images (TGA segment). Pixels are stored raw in order ABGR and they do make use of the alpha channel. + +#### CompressedBitmapSprites + +Indexed-color sprite images (BMP segment). All pixels are stored as palette indices that need to be resolved on load. + +Background pixels are [RLE](#run-length-encoding)-compressed, so all pixels referring to palette index zero must be decompressed first. -This most basic variant only supports BMP sprites. It's used in [Arcturus](/arcturus), but not in any known version of the RO client. +## References + +Multiple open-source SPR decoders exist: -| Field | Offset | Length | Type | Description | -| :----------------: | :------: | :------: | :------: | :---------------------------------------------------------------------: | -| `Header` | 0 | 2 | `string` | `"SP"` as an ASCII-encoded, fixed-size string | -| `MinorVersion` | 2 | 1 | `byte` | Versioning information | -| `MajorVersion` | 3 | 1 | `byte` | Versioning information | -| `BitmapImageCount` | 4 | 2 | `ushort` | How many images are stored in the BMP segment | -| `BitmapSprites` | 6 | variable | `array` | Indexed-color sprite images; pixels are stored as palette indices | -| `ColorPalette` | variable | 1024 | `array` | RGBA pixel colors (`byte` values); always stored at the end of the file | +- [GPL-licensed implementation in JavaScript (RoBrowser)](https://github.com/MrAntares/roBrowserLegacy/blob/master/src/Loaders/Sprite.js) +- [MIT-licensed implementation in Rust (Korangar)](https://github.com/vE5li/korangar/blob/main/src/loaders/sprite/mod.rs) +- [MPL-licensed implementation in LuaJIT (RagLite SDK)](https://github.com/RagnarokResearchLab/RagLite/blob/main/Core/FileFormats/RagnarokSPR.lua) -#### Bitmap Sprites - -This repeating structure contains the raw pixel data in the form of references to palette entries that need to be resolved. - -| Field | Offset | Length | Type | Description | -| :--------------: | :----: | :------: | :-----: | :----------------------------------------------------: | -| `ImageWidth` | 0 | 2 | `short` | The width of the sprite image, given in pixels | -| `ImageHeight` | 2 | 2 | `short` | The height of the sprite image, given in pixels | -| `PaletteIndices` | 4 | variable | `array` | Array of indexed-color palette indices (`byte` values) | - -A size of `-1, -1` (`FF FF FF FF`) indicates an invalid image, which consists of a single pixel that can (probably) be discarded. - -### Version 2.0 - -This version adds the ability to store truecolor images (RGBA pixels with an alpha channel). It appears in RO and Arcturus. - -| Field | Offset | Length | Type | Description | -| :-------------------: | :------: | :------: | :------: | :---------------------------------------------------------------------: | -| `Header` | 0 | 2 | `string` | `"SP"` as an ASCII-encoded, fixed-size string | -| `MinorVersion` | 2 | 1 | `byte` | Versioning information | -| `MajorVersion` | 3 | 1 | `byte` | Versioning information | -| `BitmapImageCount` | 4 | 2 | `ushort` | How many images are stored in the BMP segment | -| `TruecolorImageCount` | 6 | 2 | `ushort` | How many images are stored in the TGA segment | -| `BitmapSprites` | 8 | variable | `array` | Indexed-color sprite images; pixels are stored as palette indices | -| `TruecolorSprites` | variable | variable | `array` | Truecolor sprite images; pixels are stored raw (in order ABGR) | -| `ColorPalette` | variable | 1024 | `array` | RGBA pixel colors (`byte` values); always stored at the end of the file | - -#### Truecolor Sprites - -This repeating structure contains the raw pixel data in an uncompressed form. - -| Field | Offset | Length | Type | Description | -| :-----------: | :----: | :------: | :-----: | :---------------------------------------------: | -| `ImageWidth` | 0 | 2 | `short` | The width of the sprite image, given in pixels | -| `ImageHeight` | 2 | 4 | `short` | The height of the sprite image, given in pixels | -| `PixelBuffer` | 6 | variable | `array` | Array of raw ABGR image data (`byte` values) | - -### Version 2.1 - -This version adds compression (via [RLE](#run-length-encoding)) for images in the BMP segment of the file. It's used in all modern RO clients. - -#### Compressed Bitmap Sprites - -The only difference here is that runs of zero palette indices (i.e., multiple background pixels) are RLE-compressed. - -| Field | Offset | Length | Type | Description | -| :------------------------: | :----: | :------: | :------: | :----------------------------------------------------------: | -| `ImageWidth` | 0 | 2 | `short` | The width of the sprite image, given in pixels | -| `ImageHeight` | 2 | 2 | `short` | The height of the sprite image, given in pixels | -| `CompressedBufferSize` | 4 | 2 | `ushort` | How many bytes to feed into the RLE decompressor | -| `CompressedPaletteIndices` | 6 | variable | `array` | RLE-compressed indexed-color palette indices (`byte` values) | - -The size of the decompressed image is still `ImageWidth * ImageHeight`, but you must read `CompressedBufferSize` bytes only. +This list only includes actively-maintained versions; [various other community projects](/community-projects) may also be of interest. diff --git a/docusaurus.config.js b/docusaurus.config.js index 00320dcf..bc50c1e0 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -155,6 +155,18 @@ const config = { theme: lightCodeTheme, darkTheme: darkCodeTheme, additionalLanguages: ["c", "cpp", "lua", "rust", "wgsl"], + magicComments: [ + { + className: "code-block-diff-add-line", + line: "diff-add", + block: { start: "diff-add-start", end: "diff-add-end" }, + }, + { + className: "code-block-diff-remove-line", + line: "diff-remove", + block: { start: "diff-remove-start", end: "diff-remove-end" }, + }, + ], }, }), }; diff --git a/src/css/color-theme-dark.css b/src/css/color-theme-dark.css index 5a729d12..0a1995ff 100644 --- a/src/css/color-theme-dark.css +++ b/src/css/color-theme-dark.css @@ -64,3 +64,32 @@ code { font-size: 85%; border: none; } + +/* Prism.js: Better syntax highlighting for important language keywords */ +span.token.keyword { + color: blue !important; + font-weight: bold; +} + +span.token.class-name { + color: rgb(255, 0, 0) !important; +} + +/* Prism.js: Magic comments for diff highlights */ +.code-block-diff-add-line { + background-color: #ccffd8; + background-color: rgb(230, 255, 235); + background-color: rgb(220, 255, 225); + display: block; + margin: 0 calc(var(--ifm-pre-padding) * -1); + padding: 0 var(--ifm-pre-padding); +} + +.code-block-diff-remove-line { + background-color: #ffebe9; + background-color: rgb(255, 235, 235); + background-color: rgb(255, 225, 225); + display: block; + margin: 0 calc(var(--ifm-pre-padding) * -1); + padding: 0 var(--ifm-pre-padding); +}