Implement playable Mac client and rendering validation
This commit is contained in:
+33
-11
@@ -67,7 +67,19 @@ void decode_dxt5_alpha(const uint8_t* blk, uint8_t out[16][4]) {
|
||||
}
|
||||
}
|
||||
|
||||
enum Fmt { F_NONE, F_DXT1, F_DXT3, F_DXT5, F_BGRA8 };
|
||||
enum Fmt { F_NONE, F_DXT1, F_DXT3, F_DXT5, F_RGB };
|
||||
|
||||
bool valid_mask(uint32_t mask, uint32_t bits) {
|
||||
if (!mask || (bits < 32 && (mask >> bits))) return false;
|
||||
while (!(mask & 1)) mask >>= 1;
|
||||
return (mask & (mask + 1)) == 0; // contiguous channel bits
|
||||
}
|
||||
|
||||
uint8_t channel(uint32_t pixel, uint32_t mask) {
|
||||
if (!mask) return 255;
|
||||
while (!(mask & 1)) { mask >>= 1; pixel >>= 1; }
|
||||
return uint8_t((uint64_t(pixel & mask) * 255 + mask / 2) / mask);
|
||||
}
|
||||
|
||||
Image decode(const uint8_t* d, size_t len) {
|
||||
Image img;
|
||||
@@ -85,8 +97,8 @@ Image decode(const uint8_t* d, size_t len) {
|
||||
if (!std::memcmp(fourcc, "DXT1", 4)) fmt = F_DXT1;
|
||||
else if (!std::memcmp(fourcc, "DXT3", 4)) fmt = F_DXT3;
|
||||
else if (!std::memcmp(fourcc, "DXT5", 4)) fmt = F_DXT5;
|
||||
} else if ((pf_flags & 0x40) && rgb_bitcount == 32) { // DDPF_RGB, 32bpp
|
||||
fmt = F_BGRA8; // Metin2 的非压缩 DDS 一般是 B8G8R8A8
|
||||
} else if ((pf_flags & 0x40) && (rgb_bitcount == 16 || rgb_bitcount == 24 || rgb_bitcount == 32)) {
|
||||
fmt = F_RGB;
|
||||
}
|
||||
if (fmt == F_NONE || w == 0 || h == 0 || w > 8192 || h > 8192) return img;
|
||||
|
||||
@@ -96,15 +108,25 @@ Image decode(const uint8_t* d, size_t len) {
|
||||
img.h = uint16_t(h);
|
||||
img.rgba.assign(size_t(w) * h * 4, 0);
|
||||
|
||||
if (fmt == F_BGRA8) {
|
||||
if (avail < size_t(w) * h * 4) { img = Image{}; return img; }
|
||||
for (size_t i = 0; i < size_t(w) * h; ++i) {
|
||||
img.rgba[i * 4 + 0] = src[i * 4 + 2];
|
||||
img.rgba[i * 4 + 1] = src[i * 4 + 1];
|
||||
img.rgba[i * 4 + 2] = src[i * 4 + 0];
|
||||
img.rgba[i * 4 + 3] = src[i * 4 + 3];
|
||||
if (fmt == F_RGB) {
|
||||
const uint32_t r = rd_le32(d + 92), g = rd_le32(d + 96), b = rd_le32(d + 100);
|
||||
const uint32_t a = (pf_flags & 1) ? rd_le32(d + 104) : 0;
|
||||
if (!valid_mask(r, rgb_bitcount) || !valid_mask(g, rgb_bitcount) || !valid_mask(b, rgb_bitcount) ||
|
||||
(a && !valid_mask(a, rgb_bitcount)) || ((pf_flags & 1) && !a) ||
|
||||
(r & g) || (r & b) || (g & b) || (a & (r | g | b))) return {};
|
||||
const size_t bytes = rgb_bitcount / 8, row_bytes = size_t(w) * bytes;
|
||||
const size_t pitch = (rd_le32(d + 8) & 8) ? rd_le32(d + 20) : row_bytes;
|
||||
if (pitch < row_bytes || avail < row_bytes || pitch > avail || (h - 1) > (avail - row_bytes) / pitch) return {};
|
||||
for (size_t y = 0; y < h; ++y) {
|
||||
for (size_t x = 0; x < w; ++x) {
|
||||
uint32_t pixel = 0;
|
||||
for (size_t k = 0; k < bytes; ++k) pixel |= uint32_t(src[y * pitch + x * bytes + k]) << (8 * k);
|
||||
auto* out = &img.rgba[(y * w + x) * 4];
|
||||
out[0] = channel(pixel, r); out[1] = channel(pixel, g);
|
||||
out[2] = channel(pixel, b); out[3] = channel(pixel, a);
|
||||
}
|
||||
}
|
||||
img.format = "BGRA8";
|
||||
img.format = rgb_bitcount == 32 && r == 0xff0000 && b == 0xff ? "BGRA8" : "RGB_MASKED";
|
||||
return img;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user