using System; using System.Collections.Generic; using System.IO; using UnityEngine; // Filename : CBlockImageFix16.cs // Creator : ported from C++ (AutoPFImp/AutoMoveImp/blockimage.h) // Date : 2026/01/09 namespace AutoMove { /// /// Block image (FIX16) used by AutoPF to store delta-height (DH map). /// 自动寻路使用的块图(FIX16),用于存储高度差(DH 图)。 /// public class CBlockImageFix16 { // Current Version // 当前版本 private const uint BLOCKIMAGE_VER = 0x00000002; private const int NULL_ID = -1; private int[] m_BlockIDs; private readonly List m_arrBlocks = new List(256); private int m_iBlockSize; private int m_iBlockSizeExp; private int m_iWidth; // blocks in width // 块宽 private int m_iLength; // blocks in length // 块长 private float m_fPixelSize; private int m_iImageWidth; private int m_iImageLength; private short m_defaultVal; public CBlockImageFix16(short defaultVal = 0, int iExp = 4) { m_defaultVal = defaultVal; SetBlockSizeExp(iExp); } private void SetBlockSizeExp(int iExp) { m_iBlockSizeExp = iExp; m_iBlockSize = 1 << iExp; } public void Release() { m_BlockIDs = null; m_arrBlocks.Clear(); } public void GetImageSize(out int width, out int length) { width = m_iImageWidth; length = m_iImageLength; } public float GetPixelSize() => m_fPixelSize; public short GetPixel(int u, int v) { if (m_BlockIDs == null) return m_defaultVal; if (u < 0 || u >= m_iImageWidth || v < 0 || v >= m_iImageLength) return m_defaultVal; int uBlkID = u >> m_iBlockSizeExp; int vBlkID = v >> m_iBlockSizeExp; int blkIndex = vBlkID * m_iWidth + uBlkID; int blkId = m_BlockIDs[blkIndex]; if (blkId == NULL_ID) return m_defaultVal; int uBlkOffset = u & (m_iBlockSize - 1); int vBlkOffset = v & (m_iBlockSize - 1); return m_arrBlocks[blkId][(vBlkOffset << m_iBlockSizeExp) + uBlkOffset]; } /// /// Load from bytes (binary). Matches C++ CBlockImage::Load logic for FIX16. /// 从字节加载(二进制)。匹配 C++ 的 CBlockImage::Load 逻辑(FIX16)。 /// public bool Load(byte[] fileBytes) { try { if (fileBytes == null || fileBytes.Length < 8) return false; using var ms = new MemoryStream(fileBytes, false); using var br = new BinaryReader(ms); uint dwFileVersion = br.ReadUInt32(); if (dwFileVersion > BLOCKIMAGE_VER) { return false; } uint bufSize = br.ReadUInt32(); if (bufSize == 0 || bufSize > fileBytes.Length) return false; byte[] buf = br.ReadBytes((int)bufSize); if (buf.Length != bufSize) return false; int cur = 0; if (dwFileVersion == BLOCKIMAGE_VER) { // Version 2: includes T size and default T value. // 版本2:包含 T 的大小与默认值。 uint tSize = BitConverter.ToUInt32(buf, cur); cur += 4; if (tSize != 2) { // FIX16 should be 2 bytes. // FIX16 应为 2 字节。 return false; } m_defaultVal = BitConverter.ToInt16(buf, cur); cur += 2; } m_iWidth = BitConverter.ToInt32(buf, cur); cur += 4; m_iLength = BitConverter.ToInt32(buf, cur); cur += 4; m_iBlockSizeExp = BitConverter.ToInt32(buf, cur); cur += 4; m_iBlockSize = 1 << m_iBlockSizeExp; m_iImageWidth = BitConverter.ToInt32(buf, cur); cur += 4; m_iImageLength = BitConverter.ToInt32(buf, cur); cur += 4; m_fPixelSize = BitConverter.ToSingle(buf, cur); cur += 4; int blkIdCount = m_iWidth * m_iLength; if (blkIdCount <= 0) return false; int blkIdBytes = blkIdCount * 4; if (cur + blkIdBytes > buf.Length) return false; m_BlockIDs = new int[blkIdCount]; Buffer.BlockCopy(buf, cur, m_BlockIDs, 0, blkIdBytes); cur += blkIdBytes; int nBlocks = BitConverter.ToInt32(buf, cur); cur += 4; if (nBlocks < 0) return false; m_arrBlocks.Clear(); int blkSizeShorts = m_iBlockSize * m_iBlockSize; int blkSizeBytes = blkSizeShorts * 2; for (int i = 0; i < nBlocks; i++) { if (cur + blkSizeBytes > buf.Length) return false; short[] block = new short[blkSizeShorts]; Buffer.BlockCopy(buf, cur, block, 0, blkSizeBytes); cur += blkSizeBytes; m_arrBlocks.Add(block); } return true; } catch (Exception ex) { Debug.LogWarning($"[CBlockImageFix16] Load failed: {ex.Message}"); return false; } } } }