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;
}
}
}
}