Files
90CNServer/pvf/internal/npk/dds_test.go
T
hanfuit 645b64fe21 DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go):
- GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面
- GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新
- 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新)
- 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍
- 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0)
- 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算

启动器(Python/JS):
- 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭
- GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐)
- 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
2026-11-04 14:31:55 +08:00

107 lines
3.3 KiB
Go

package npk
import (
"bytes"
"compress/zlib"
"encoding/binary"
"image/color"
"testing"
)
func ddsTexture(format string, width, height uint32, blocks []byte) []byte {
data := make([]byte, 128)
copy(data, "DDS ")
binary.LittleEndian.PutUint32(data[4:8], 124)
binary.LittleEndian.PutUint32(data[12:16], height)
binary.LittleEndian.PutUint32(data[16:20], width)
binary.LittleEndian.PutUint32(data[76:80], 32)
binary.LittleEndian.PutUint32(data[80:84], 4)
copy(data[84:88], format)
return append(data, blocks...)
}
func TestSpriteDDSHeaderAndAtlasCrop(t *testing.T) {
for _, tc := range []struct {
name string
format int32
blockSize, colors int
}{{"DXT1", FormatDXT1, 8, 0}, {"DXT3", FormatDXT3, 16, 8}, {"DXT5", FormatDXT5, 16, 8}} {
for _, wrapped := range []bool{false, true} {
name := tc.name + "/raw"
if wrapped {
name = tc.name + "/dds"
}
t.Run(name, func(t *testing.T) {
// Two atlas blocks: red at x=0, green at x=4. The frame selects green.
blocks := make([]byte, tc.blockSize*2)
for i, c := range []uint16{0xf800, 0x07e0} {
block := blocks[i*tc.blockSize : (i+1)*tc.blockSize]
if tc.format == FormatDXT3 {
for j := 0; j < 8; j++ {
block[j] = 255
}
}
if tc.format == FormatDXT5 {
block[0] = 255
}
binary.LittleEndian.PutUint16(block[tc.colors:], c)
}
payload := blocks
if wrapped {
payload = ddsTexture(tc.name, 8, 4, blocks)
}
var compressed bytes.Buffer
writer := zlib.NewWriter(&compressed)
_, _ = writer.Write(payload)
_ = writer.Close()
prefix := []byte{}
putInt32(&prefix, 1, int32(compressed.Len()), 0) // sprite count, data size, palette count
putInt32(&prefix, 1, tc.format, 0, int32(compressed.Len()), int32(len(payload)), 8, 4)
frame := imageHeader(tc.format, ExtraSprite, 4, 4, 0)
putInt32(&frame, 0, 0, 4, 0, 8, 4, 0)
data := makeIMG(5, append(prefix, frame...), compressed.Bytes(), int32(len(frame)))
parsed, err := ParseIMGAt(bytes.NewReader(data), 0, int64(len(data)))
if err != nil {
t.Fatal(err)
}
decoded, err := DecodeImage(bytes.NewReader(data), 0, int64(len(data)), parsed, 0)
if err != nil {
t.Fatal(err)
}
if decoded.Bounds().Dx() != 4 || decoded.Bounds().Dy() != 4 {
t.Fatal(decoded.Bounds())
}
for y := 0; y < 4; y++ {
for x := 0; x < 4; x++ {
got := color.NRGBAModel.Convert(decoded.At(x, y)).(color.NRGBA)
if got != (color.NRGBA{G: 255, A: 255}) {
t.Fatalf("裁剪到相邻图标或文件头:(%d,%d)=%+v", x, y, got)
}
}
}
})
}
}
}
func TestSpriteDDSInvalidHeaders(t *testing.T) {
valid := ddsTexture("DXT1", 4, 4, make([]byte, 8))
for _, tc := range []struct {
name string
data []byte
sprite SpriteMeta
}{
{"truncated", []byte("DDS "), SpriteMeta{Format: FormatDXT1, Width: 4, Height: 4}},
{"dimensions", valid, SpriteMeta{Format: FormatDXT1, Width: 8, Height: 4}},
{"format", valid, SpriteMeta{Format: FormatDXT5, Width: 4, Height: 4}},
{"unsupported", ddsTexture("DX10", 4, 4, nil), SpriteMeta{Format: FormatDXT1, Width: 4, Height: 4}},
{"pixels", valid[:128], SpriteMeta{Format: FormatDXT1, Width: 4, Height: 4}},
} {
t.Run(tc.name, func(t *testing.T) {
if _, err := decodeSpriteTexture(tc.data, tc.sprite); err == nil {
t.Fatal("应拒绝无效DDS")
}
})
}
}