|
| 1 | +//! \file ImageGXT.cs |
| 2 | +//! \date 2019 Feb 25 |
| 3 | +//! \brief CRI Middleware image format. |
| 4 | +// |
| 5 | +// Copyright (C) 2019 by morkt |
| 6 | +// |
| 7 | +// Permission is hereby granted, free of charge, to any person obtaining a copy |
| 8 | +// of this software and associated documentation files (the "Software"), to |
| 9 | +// deal in the Software without restriction, including without limitation the |
| 10 | +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
| 11 | +// sell copies of the Software, and to permit persons to whom the Software is |
| 12 | +// furnished to do so, subject to the following conditions: |
| 13 | +// |
| 14 | +// The above copyright notice and this permission notice shall be included in |
| 15 | +// all copies or substantial portions of the Software. |
| 16 | +// |
| 17 | +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 18 | +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 19 | +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 20 | +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 21 | +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| 22 | +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| 23 | +// IN THE SOFTWARE. |
| 24 | +// |
| 25 | + |
| 26 | +using System; |
| 27 | +using System.ComponentModel.Composition; |
| 28 | +using System.IO; |
| 29 | +using System.Windows.Media; |
| 30 | +using GameRes.Formats.DirectDraw; |
| 31 | + |
| 32 | +namespace GameRes.Formats.Cri |
| 33 | +{ |
| 34 | + internal class GxtMetaData : ImageMetaData |
| 35 | + { |
| 36 | + public uint TextureOffset; |
| 37 | + public int TextureLength; |
| 38 | + public int PaletteIndex; |
| 39 | + public uint Flags; |
| 40 | + public GxtTextureType TextureType; |
| 41 | + public GxtTextureFormat TextureFormat; |
| 42 | + } |
| 43 | + |
| 44 | + internal enum GxtTextureType : uint |
| 45 | + { |
| 46 | + Swizzled = 0x00000000, |
| 47 | + Cube = 0x40000000, |
| 48 | + Linear = 0x60000000, |
| 49 | + Tiled = 0x80000000, |
| 50 | + LinearStrided = 0xC0000000, |
| 51 | + }; |
| 52 | + |
| 53 | + internal enum GxtTextureFormat : uint |
| 54 | + { |
| 55 | + UBC3 = 0x87000000, |
| 56 | + } |
| 57 | + |
| 58 | + [Export(typeof(ImageFormat))] |
| 59 | + public class GxtFormat : ImageFormat |
| 60 | + { |
| 61 | + public override string Tag { get { return "GXT"; } } |
| 62 | + public override string Description { get { return "CRI Middleware image format"; } } |
| 63 | + public override uint Signature { get { return 0x545847; } } // 'GXT' |
| 64 | + |
| 65 | + public override ImageMetaData ReadMetaData (IBinaryStream file) |
| 66 | + { |
| 67 | + var header = file.ReadHeader (0x40); |
| 68 | + if (header.ToInt32 (4) != 0x10000003) |
| 69 | + return null; |
| 70 | + return new GxtMetaData { |
| 71 | + Width = header.ToUInt16 (0x38), |
| 72 | + Height = header.ToUInt16 (0x3A), |
| 73 | + TextureOffset = header.ToUInt32 (0x20), |
| 74 | + TextureLength = header.ToInt32 (0x24), |
| 75 | + PaletteIndex = header.ToInt32 (0x28), |
| 76 | + Flags = header.ToUInt32 (0x2C), |
| 77 | + TextureType = (GxtTextureType)header.ToUInt32 (0x30), |
| 78 | + TextureFormat = (GxtTextureFormat)header.ToUInt32 (0x34), |
| 79 | + }; |
| 80 | + } |
| 81 | + |
| 82 | + public override ImageData Read (IBinaryStream file, ImageMetaData info) |
| 83 | + { |
| 84 | + var meta = (GxtMetaData)info; |
| 85 | + file.Position = meta.TextureOffset; |
| 86 | + var data = file.ReadBytes (meta.TextureLength); |
| 87 | + if (GxtTextureFormat.UBC3 == meta.TextureFormat) |
| 88 | + { |
| 89 | + var pixels = UnpackDXT5 (data, meta); |
| 90 | + return ImageData.Create (info, PixelFormats.Bgra32, null, pixels); |
| 91 | + } |
| 92 | + else |
| 93 | + throw new NotSupportedException (string.Format ("GXT texture format {0:X8} not supported.", meta.TextureFormat)); |
| 94 | + } |
| 95 | + |
| 96 | + byte[] UnpackDXT5 (byte[] input, GxtMetaData info) |
| 97 | + { |
| 98 | + var decoder = new DxtDecoder (input, info); |
| 99 | + int src = 0; |
| 100 | + for (int y = 0; y < info.iHeight; y += 4) |
| 101 | + for (int x = 0; x < info.iWidth; x += 4) |
| 102 | + { |
| 103 | + int px, py; |
| 104 | + GetSwizzledCoords (x / 4, y / 4, info.iWidth / 4, info.iHeight / 4, out px, out py); |
| 105 | + decoder.DecompressDXT5Block (input, src, py * 4, px * 4); |
| 106 | + src += 16; |
| 107 | + } |
| 108 | + return decoder.Output; |
| 109 | + } |
| 110 | + |
| 111 | + void GetSwizzledCoords (int origX, int origY, int width, int height, out int trX, out int trY) |
| 112 | + { |
| 113 | + if (width == 0) |
| 114 | + width = 16; |
| 115 | + if (height == 0) |
| 116 | + height = 16; |
| 117 | + |
| 118 | + int i = (origY * width) + origX; |
| 119 | + |
| 120 | + int min = Math.Min (width, height); |
| 121 | + int k = BitScanReverse ((uint)min); // Math.Log (min, 2); |
| 122 | + |
| 123 | + if (height < width) |
| 124 | + { |
| 125 | + // XXXyxyxyx → XXXxxxyyy |
| 126 | + int j = i >> (2 * k) << (2 * k) |
| 127 | + | (DecodeCoord2Y (i) & (min - 1)) << k |
| 128 | + | (DecodeCoord2X (i) & (min - 1)); |
| 129 | + trX = j / height; |
| 130 | + trY = j % height; |
| 131 | + } |
| 132 | + else |
| 133 | + { |
| 134 | + // YYYyxyxyx → YYYyyyxxx |
| 135 | + int j = i >> (2 * k) << (2 * k) |
| 136 | + | (DecodeCoord2X (i) & (min - 1)) << k |
| 137 | + | (DecodeCoord2Y (i) & (min - 1)); |
| 138 | + trX = j % width; |
| 139 | + trY = j / width; |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + internal static int BitScanReverse (uint x) |
| 144 | + { |
| 145 | + int n = 0; |
| 146 | + while ((x >>= 1) != 0) |
| 147 | + ++n; |
| 148 | + return n; |
| 149 | + } |
| 150 | + |
| 151 | + private static int DecodeCoord2X (int code) |
| 152 | + { |
| 153 | + return Compact1By1 (code); |
| 154 | + } |
| 155 | + |
| 156 | + private static int DecodeCoord2Y (int code) |
| 157 | + { |
| 158 | + return Compact1By1 (code >> 1); |
| 159 | + } |
| 160 | + |
| 161 | + private static int Compact1By1 (int x) |
| 162 | + { |
| 163 | + x &= 0x55555555; |
| 164 | + x = (x ^ (x >> 1)) & 0x33333333; |
| 165 | + x = (x ^ (x >> 2)) & 0x0f0f0f0f; |
| 166 | + x = (x ^ (x >> 4)) & 0x00ff00ff; |
| 167 | + x = (x ^ (x >> 8)) & 0x0000ffff; |
| 168 | + return x; |
| 169 | + } |
| 170 | + |
| 171 | + public override void Write (Stream file, ImageData image) |
| 172 | + { |
| 173 | + throw new System.NotImplementedException ("GxtFormat.Write not implemented"); |
| 174 | + } |
| 175 | + } |
| 176 | +} |
0 commit comments