feat: continue improving BMapSharp.

- add BMFileWriter in BMapSharp.
This commit is contained in:
yyc12345 2024-10-27 11:41:49 +08:00
parent 623334f863
commit 4d04b38d52
2 changed files with 100 additions and 68 deletions

View File

@ -58,11 +58,11 @@ namespace BMapSharp {
// the native memory we created is a simple array and each item is a pointer to a NULL-terminated UTF8 string. // the native memory we created is a simple array and each item is a pointer to a NULL-terminated UTF8 string.
// Please note the array self is also NULL-terminated otherwise we don't know its length. // Please note the array self is also NULL-terminated otherwise we don't know its length.
public nint MarshalManagedToNative(object ManagedObj) { public IntPtr MarshalManagedToNative(object ManagedObj) {
// Check marshaler type // Check marshaler type
if (!m_MarshalerType.HasFlag(MarshalerType.In)) return nint.Zero; if (!m_MarshalerType.HasFlag(MarshalerType.In)) return IntPtr.Zero;
// Check nullptr object. // Check nullptr object.
if (ManagedObj is null) return nint.Zero; if (ManagedObj is null) return IntPtr.Zero;
// Check argument type. // Check argument type.
string[] castManagedObj = ManagedObj as string[]; string[] castManagedObj = ManagedObj as string[];
if (castManagedObj is null) if (castManagedObj is null)
@ -70,45 +70,45 @@ namespace BMapSharp {
// Allocate string items first // Allocate string items first
int szArrayItemCount = castManagedObj.Length; int szArrayItemCount = castManagedObj.Length;
int szArrayItemSize = Marshal.SizeOf<nint>(); int szArrayItemSize = Marshal.SizeOf<IntPtr>();
nint[] apString = new nint[szArrayItemCount]; IntPtr[] apString = new IntPtr[szArrayItemCount];
for (int i = 0; i < szArrayItemCount; ++i) { for (int i = 0; i < szArrayItemCount; ++i) {
// Check null string // Check null string
string stringObj = castManagedObj[i]; string stringObj = castManagedObj[i];
if (stringObj is null) apString[i] = nint.Zero; if (stringObj is null) apString[i] = IntPtr.Zero;
else apString[i] = BMStringMarshaler.ToNative(stringObj); else apString[i] = BMStringMarshaler.ToNative(stringObj);
} }
// Allocate array pointer now. // Allocate array pointer now.
nint pArray = Marshal.AllocHGlobal(szArrayItemSize * (szArrayItemCount + 1)); IntPtr pArray = Marshal.AllocHGlobal(szArrayItemSize * (szArrayItemCount + 1));
// Copy string pointer data // Copy string pointer data
Marshal.Copy(apString, 0, pArray, szArrayItemCount); Marshal.Copy(apString, 0, pArray, szArrayItemCount);
// Setup NULL ternimal // Setup NULL ternimal
Marshal.WriteIntPtr(pArray + (szArrayItemSize * szArrayItemCount), nint.Zero); Marshal.WriteIntPtr(pArray + (szArrayItemSize * szArrayItemCount), IntPtr.Zero);
// Return value // Return value
return pArray; return pArray;
} }
public object MarshalNativeToManaged(nint pNativeData) { public object MarshalNativeToManaged(IntPtr pNativeData) {
// Check marshaler type // Check marshaler type
if (!m_MarshalerType.HasFlag(MarshalerType.Out)) return null; if (!m_MarshalerType.HasFlag(MarshalerType.Out)) return null;
// Check nullptr // Check nullptr
if (pNativeData == nint.Zero) return null; if (pNativeData == IntPtr.Zero) return null;
// Get the length of array // Get the length of array
int szArrayItemCount = BMStringArrayMarshaler.GetArrayLength(pNativeData); int szArrayItemCount = BMStringArrayMarshaler.GetArrayLength(pNativeData);
int szArrayItemSize = Marshal.SizeOf<nint>(); int szArrayItemSize = Marshal.SizeOf<IntPtr>();
// Prepare array cache and read it. // Prepare array cache and read it.
nint[] apString = new nint[szArrayItemCount]; IntPtr[] apString = new IntPtr[szArrayItemCount];
Marshal.Copy(pNativeData, apString, 0, szArrayItemCount); Marshal.Copy(pNativeData, apString, 0, szArrayItemCount);
// Iterate the array and process each string one by one. // Iterate the array and process each string one by one.
string[] ret = new string[szArrayItemCount]; string[] ret = new string[szArrayItemCount];
for (int i = 0; i < szArrayItemCount; ++i) { for (int i = 0; i < szArrayItemCount; ++i) {
// Get string pointer // Get string pointer
nint pString = apString[i]; IntPtr pString = apString[i];
if (pString == nint.Zero) { if (pString == IntPtr.Zero) {
ret[i] = null; ret[i] = null;
continue; continue;
} }
@ -120,25 +120,25 @@ namespace BMapSharp {
return ret; return ret;
} }
public void CleanUpNativeData(nint pNativeData) { public void CleanUpNativeData(IntPtr pNativeData) {
// Check marshaler type // Check marshaler type
if (!m_MarshalerType.HasFlag(MarshalerType.In)) return; if (!m_MarshalerType.HasFlag(MarshalerType.In)) return;
// Check nullptr // Check nullptr
if (pNativeData == nint.Zero) return; if (pNativeData == IntPtr.Zero) return;
// Get the length of array // Get the length of array
int szArrayItemCount = BMStringArrayMarshaler.GetArrayLength(pNativeData); int szArrayItemCount = BMStringArrayMarshaler.GetArrayLength(pNativeData);
int szArrayItemSize = Marshal.SizeOf<nint>(); int szArrayItemSize = Marshal.SizeOf<IntPtr>();
// Prepare array cache and read it. // Prepare array cache and read it.
nint[] apString = new nint[szArrayItemCount]; IntPtr[] apString = new IntPtr[szArrayItemCount];
Marshal.Copy(pNativeData, apString, 0, szArrayItemCount); Marshal.Copy(pNativeData, apString, 0, szArrayItemCount);
// Free array self // Free array self
Marshal.FreeHGlobal(pNativeData); Marshal.FreeHGlobal(pNativeData);
// Iterate the string pointer array and free them one by one. // Iterate the string pointer array and free them one by one.
foreach (nint pString in apString) { foreach (IntPtr pString in apString) {
// Free string pointer // Free string pointer
if (pString == nint.Zero) continue; if (pString == IntPtr.Zero) continue;
Marshal.FreeHGlobal(pString); Marshal.FreeHGlobal(pString);
} }
} }
@ -157,9 +157,9 @@ namespace BMapSharp {
/// </summary> /// </summary>
/// <param name="ptr">The pointer to array for checking.</param> /// <param name="ptr">The pointer to array for checking.</param>
/// <returns>The length of array (NULL terminal exclusive).</returns> /// <returns>The length of array (NULL terminal exclusive).</returns>
internal static int GetArrayLength(nint ptr) { internal static int GetArrayLength(IntPtr ptr) {
int count = 0, unit = Marshal.SizeOf<nint>(); int count = 0, unit = Marshal.SizeOf<IntPtr>();
while (Marshal.ReadIntPtr(ptr) != nint.Zero) { while (Marshal.ReadIntPtr(ptr) != IntPtr.Zero) {
ptr += unit; ptr += unit;
++count; ++count;
} }
@ -181,11 +181,11 @@ namespace BMapSharp {
m_MarshalerType = marshaler_type; m_MarshalerType = marshaler_type;
} }
public nint MarshalManagedToNative(object ManagedObj) { public IntPtr MarshalManagedToNative(object ManagedObj) {
// Check marshaler type // Check marshaler type
if (!m_MarshalerType.HasFlag(MarshalerType.In)) return nint.Zero; if (!m_MarshalerType.HasFlag(MarshalerType.In)) return IntPtr.Zero;
// Check requirements. // Check requirements.
if (ManagedObj is null) return nint.Zero; if (ManagedObj is null) return IntPtr.Zero;
string castManagedObj = ManagedObj as string; string castManagedObj = ManagedObj as string;
if (castManagedObj is null) if (castManagedObj is null)
throw new MarshalDirectiveException("BMStringMarshaler must be used on a string."); throw new MarshalDirectiveException("BMStringMarshaler must be used on a string.");
@ -193,20 +193,20 @@ namespace BMapSharp {
return BMStringMarshaler.ToNative(castManagedObj); return BMStringMarshaler.ToNative(castManagedObj);
} }
public object MarshalNativeToManaged(nint pNativeData) { public object MarshalNativeToManaged(IntPtr pNativeData) {
// Check marshaler type // Check marshaler type
if (!m_MarshalerType.HasFlag(MarshalerType.Out)) return null; if (!m_MarshalerType.HasFlag(MarshalerType.Out)) return null;
// Check nullptr // Check nullptr
if (pNativeData == nint.Zero) return null; if (pNativeData == IntPtr.Zero) return null;
// Call self // Call self
return BMStringMarshaler.ToManaged(pNativeData); return BMStringMarshaler.ToManaged(pNativeData);
} }
public void CleanUpNativeData(nint pNativeData) { public void CleanUpNativeData(IntPtr pNativeData) {
// Check marshaler type // Check marshaler type
if (!m_MarshalerType.HasFlag(MarshalerType.In)) return; if (!m_MarshalerType.HasFlag(MarshalerType.In)) return;
// Check nullptr // Check nullptr
if (pNativeData == nint.Zero) return; if (pNativeData == IntPtr.Zero) return;
// Free native pointer // Free native pointer
Marshal.FreeHGlobal(pNativeData); Marshal.FreeHGlobal(pNativeData);
} }
@ -225,7 +225,7 @@ namespace BMapSharp {
/// </summary> /// </summary>
/// <param name="ptr">The pointer for checking.</param> /// <param name="ptr">The pointer for checking.</param>
/// <returns>The length of C style string (NUL exclusive).</returns> /// <returns>The length of C style string (NUL exclusive).</returns>
internal static int GetCStringLength(nint ptr) { internal static int GetCStringLength(IntPtr ptr) {
int count = 0, unit = Marshal.SizeOf<byte>(); int count = 0, unit = Marshal.SizeOf<byte>();
while (Marshal.ReadByte(ptr) != (byte)0) { while (Marshal.ReadByte(ptr) != (byte)0) {
ptr += unit; ptr += unit;
@ -240,13 +240,13 @@ namespace BMapSharp {
/// </summary> /// </summary>
/// <param name="obj">String object. Caller must make sure this object is not null.</param> /// <param name="obj">String object. Caller must make sure this object is not null.</param>
/// <returns>The created native data pointer.</returns> /// <returns>The created native data pointer.</returns>
internal static nint ToNative(string obj) { internal static IntPtr ToNative(string obj) {
// Encode string first // Encode string first
byte[] encString = Encoding.UTF8.GetBytes(obj); byte[] encString = Encoding.UTF8.GetBytes(obj);
// Allocate string memory with extra NUL. // Allocate string memory with extra NUL.
int szStringItemCount = encString.Length; int szStringItemCount = encString.Length;
int szStringItemSize = Marshal.SizeOf<byte>(); int szStringItemSize = Marshal.SizeOf<byte>();
nint pString = Marshal.AllocHGlobal(szStringItemSize * (szStringItemCount + 1)); IntPtr pString = Marshal.AllocHGlobal(szStringItemSize * (szStringItemCount + 1));
// Copy encoded string data // Copy encoded string data
Marshal.Copy(encString, 0, pString, szStringItemCount); Marshal.Copy(encString, 0, pString, szStringItemCount);
// Setup NUL // Setup NUL
@ -260,7 +260,7 @@ namespace BMapSharp {
/// </summary> /// </summary>
/// <param name="ptr">Native pointer holding string data. Caller must make sure this pointer is not nullptr.</param> /// <param name="ptr">Native pointer holding string data. Caller must make sure this pointer is not nullptr.</param>
/// <returns>The extracted managed string data.</returns> /// <returns>The extracted managed string data.</returns>
internal static string ToManaged(nint ptr) { internal static string ToManaged(IntPtr ptr) {
// Get the length of given string. // Get the length of given string.
int szStringItemCount = BMStringMarshaler.GetCStringLength(ptr); int szStringItemCount = BMStringMarshaler.GetCStringLength(ptr);
int szStringItemSize = Marshal.SizeOf<byte>(); int szStringItemSize = Marshal.SizeOf<byte>();

View File

@ -94,16 +94,19 @@ namespace BMapSharp.BMapWrapper {
#endregion #endregion
} }
public abstract class AbstractCKObject { public abstract class AbstractCKObject : SafeHandle {
internal AbstractCKObject(IntPtr raw_pointer, uint ckid) { // Same as AbstractPointer, but not own this handle.
m_RawPointer = raw_pointer; internal AbstractCKObject(IntPtr raw_pointer, uint ckid) : base(Utils.INVALID_PTR, false) {
this.handle = raw_pointer;
m_CKID = ckid; m_CKID = ckid;
} }
private readonly IntPtr m_RawPointer; public override bool IsInvalid => this.handle == Utils.INVALID_PTR;
protected override bool ReleaseHandle() => throw new NotImplementedException();
private readonly uint m_CKID; private readonly uint m_CKID;
protected bool isValid() => m_RawPointer != Utils.INVALID_PTR && m_RawPointer != Utils.INVALID_CKID; protected bool isValid() => this.handle != Utils.INVALID_PTR && m_CKID != Utils.INVALID_CKID;
protected IntPtr getPointer() => m_RawPointer; protected IntPtr getPointer() => this.handle;
protected uint getCKID() => m_CKID; protected uint getCKID() => m_CKID;
// private uint m_CKID; // private uint m_CKID;
@ -143,14 +146,14 @@ namespace BMapSharp.BMapWrapper {
#region Misc #region Misc
public override int GetHashCode() => HashCode.Combine(m_RawPointer, m_CKID); public override int GetHashCode() => HashCode.Combine(this.handle, m_CKID);
public override string ToString() => $"{m_RawPointer}, {m_CKID}"; public override string ToString() => $"{this.handle}, {m_CKID}";
#endregion #endregion
} }
public class BMObject : AbstractCKObject { public class BMObject : AbstractCKObject {
internal BMObject(nint raw_pointer, uint ckid) : base(raw_pointer, ckid) { } internal BMObject(IntPtr raw_pointer, uint ckid) : base(raw_pointer, ckid) { }
public string GetName() { public string GetName() {
BMapException.ThrowIfFailed(BMap.BMObject_GetName( BMapException.ThrowIfFailed(BMap.BMObject_GetName(
@ -166,27 +169,27 @@ namespace BMapSharp.BMapWrapper {
} }
public class BMTexture : BMObject { public class BMTexture : BMObject {
internal BMTexture(nint raw_pointer, uint ckid) : base(raw_pointer, ckid) { } internal BMTexture(IntPtr raw_pointer, uint ckid) : base(raw_pointer, ckid) { }
} }
public class BMMaterial : BMObject { public class BMMaterial : BMObject {
internal BMMaterial(nint raw_pointer, uint ckid) : base(raw_pointer, ckid) { } internal BMMaterial(IntPtr raw_pointer, uint ckid) : base(raw_pointer, ckid) { }
} }
public class BMMesh : BMObject { public class BMMesh : BMObject {
internal BMMesh(nint raw_pointer, uint ckid) : base(raw_pointer, ckid) { } internal BMMesh(IntPtr raw_pointer, uint ckid) : base(raw_pointer, ckid) { }
} }
public class BM3dObject : BMObject { public class BM3dObject : BMObject {
internal BM3dObject(nint raw_pointer, uint ckid) : base(raw_pointer, ckid) { } internal BM3dObject(IntPtr raw_pointer, uint ckid) : base(raw_pointer, ckid) { }
} }
public class BMGroup : BMObject { public class BMGroup : BMObject {
internal BMGroup(nint raw_pointer, uint ckid) : base(raw_pointer, ckid) { } internal BMGroup(IntPtr raw_pointer, uint ckid) : base(raw_pointer, ckid) { }
} }
public sealed class BMFileReader : AbstractPointer { public sealed class BMFileReader : AbstractPointer {
private static IntPtr AllocateHandle(string file_name, string temp_folder, string texture_folder, string[] encodings) { private static IntPtr allocateHandle(string file_name, string temp_folder, string texture_folder, string[] encodings) {
BMapException.ThrowIfFailed(BMap.BMFile_Load( BMapException.ThrowIfFailed(BMap.BMFile_Load(
file_name, temp_folder, texture_folder, file_name, temp_folder, texture_folder,
Utils.BMapSharpCallback, Utils.BMapSharpCallback,
@ -196,21 +199,20 @@ namespace BMapSharp.BMapWrapper {
return out_file; return out_file;
} }
protected override bool ReleaseHandle() { protected override bool ReleaseHandle() {
return BMap.BMFile_Free(this.handle); return BMap.BMFile_Free(this.getPointer());
} }
public BMFileReader(string file_name, string temp_folder, string texture_folder, string[] encodings) public BMFileReader(string file_name, string temp_folder, string texture_folder, string[] encodings)
: base(AllocateHandle(file_name, temp_folder, texture_folder, encodings)) { } : base(allocateHandle(file_name, temp_folder, texture_folder, encodings)) { }
private delegate bool FctProtoGetCount(IntPtr bmf, out uint cnt);
private delegate bool FctProtoGetCount(nint bmf, out uint cnt); private delegate bool FctProtoGetObject(IntPtr bmf, uint idx, out uint id);
private delegate bool FctProtoGetObject(nint bmf, uint idx, out uint id); private delegate T FctProtoCreateInstance<T>(IntPtr bmf, uint id);
private delegate T FctProtoCreateInstance<T>(nint bmf, uint id); private uint getCKObjectCount(FctProtoGetCount fct_cnt) {
private uint GetCKObjectCount(FctProtoGetCount fct_cnt) {
BMapException.ThrowIfFailed(fct_cnt(this.getPointer(), out uint out_count)); BMapException.ThrowIfFailed(fct_cnt(this.getPointer(), out uint out_count));
return out_count; return out_count;
} }
private IEnumerable<T> GetCKObjects<T>(FctProtoGetCount fct_cnt, FctProtoGetObject fct_obj, FctProtoCreateInstance<T> fct_crt) { private IEnumerable<T> getCKObjects<T>(FctProtoGetCount fct_cnt, FctProtoGetObject fct_obj, FctProtoCreateInstance<T> fct_crt) {
uint count = GetCKObjectCount(fct_cnt); uint count = getCKObjectCount(fct_cnt);
for (uint i = 0; i < count; ++i) { for (uint i = 0; i < count; ++i) {
BMapException.ThrowIfFailed(fct_obj(this.getPointer(), i, out uint out_id)); BMapException.ThrowIfFailed(fct_obj(this.getPointer(), i, out uint out_id));
yield return fct_crt(this.getPointer(), out_id); yield return fct_crt(this.getPointer(), out_id);
@ -218,26 +220,56 @@ namespace BMapSharp.BMapWrapper {
} }
public uint GetTextureCount() => public uint GetTextureCount() =>
GetCKObjectCount(BMap.BMFile_GetTextureCount); getCKObjectCount(BMap.BMFile_GetTextureCount);
public IEnumerable<BMTexture> GetTextures() => public IEnumerable<BMTexture> GetTextures() =>
GetCKObjects<BMTexture>(BMap.BMFile_GetTextureCount, BMap.BMFile_GetTexture, (bmf, id) => new BMTexture(bmf, id)); getCKObjects<BMTexture>(BMap.BMFile_GetTextureCount, BMap.BMFile_GetTexture, (bmf, id) => new BMTexture(bmf, id));
public uint GetMaterialCount() => public uint GetMaterialCount() =>
GetCKObjectCount(BMap.BMFile_GetMaterialCount); getCKObjectCount(BMap.BMFile_GetMaterialCount);
public IEnumerable<BMMaterial> GetMaterials() => public IEnumerable<BMMaterial> GetMaterials() =>
GetCKObjects<BMMaterial>(BMap.BMFile_GetMaterialCount, BMap.BMFile_GetMaterial, (bmf, id) => new BMMaterial(bmf, id)); getCKObjects<BMMaterial>(BMap.BMFile_GetMaterialCount, BMap.BMFile_GetMaterial, (bmf, id) => new BMMaterial(bmf, id));
public uint GetMeshCount() => public uint GetMeshCount() =>
GetCKObjectCount(BMap.BMFile_GetMeshCount); getCKObjectCount(BMap.BMFile_GetMeshCount);
public IEnumerable<BMMesh> GetMeshes() => public IEnumerable<BMMesh> GetMeshes() =>
GetCKObjects<BMMesh>(BMap.BMFile_GetMeshCount, BMap.BMFile_GetMesh, (bmf, id) => new BMMesh(bmf, id)); getCKObjects<BMMesh>(BMap.BMFile_GetMeshCount, BMap.BMFile_GetMesh, (bmf, id) => new BMMesh(bmf, id));
public uint Get3dObjectCount() => public uint Get3dObjectCount() =>
GetCKObjectCount(BMap.BMFile_Get3dObjectCount); getCKObjectCount(BMap.BMFile_Get3dObjectCount);
public IEnumerable<BM3dObject> Get3dObjects() => public IEnumerable<BM3dObject> Get3dObjects() =>
GetCKObjects<BM3dObject>(BMap.BMFile_Get3dObjectCount, BMap.BMFile_Get3dObject, (bmf, id) => new BM3dObject(bmf, id)); getCKObjects<BM3dObject>(BMap.BMFile_Get3dObjectCount, BMap.BMFile_Get3dObject, (bmf, id) => new BM3dObject(bmf, id));
public uint GetGroupCount() => public uint GetGroupCount() =>
GetCKObjectCount(BMap.BMFile_GetGroupCount); getCKObjectCount(BMap.BMFile_GetGroupCount);
public IEnumerable<BMGroup> GetGroups() => public IEnumerable<BMGroup> GetGroups() =>
GetCKObjects<BMGroup>(BMap.BMFile_GetGroupCount, BMap.BMFile_GetGroup, (bmf, id) => new BMGroup(bmf, id)); getCKObjects<BMGroup>(BMap.BMFile_GetGroupCount, BMap.BMFile_GetGroup, (bmf, id) => new BMGroup(bmf, id));
} }
public sealed class BMFileWriter : AbstractPointer {
private static IntPtr allocateHandle(string temp_folder, string texture_folder, string[] encodings) {
BMapException.ThrowIfFailed(BMap.BMFile_Create(
temp_folder, texture_folder,
Utils.BMapSharpCallback,
(uint)encodings.Length, encodings,
out IntPtr out_file
));
return out_file;
}
protected override bool ReleaseHandle() {
return BMap.BMFile_Free(this.getPointer());
}
public BMFileWriter(string temp_folder, string texture_folder, string[] encodings)
: base(allocateHandle(temp_folder, texture_folder, encodings)) { }
private delegate bool FctProtoCreateObject(IntPtr bmf, out uint id);
private delegate T FctProtoCreateInstance<T>(IntPtr bmf, uint id);
private T createCKObject<T>(FctProtoCreateObject fct_crt, FctProtoCreateInstance<T> fct_inst) {
BMapException.ThrowIfFailed(fct_crt(this.getPointer(), out uint out_id));
return fct_inst(this.getPointer(), out_id);
}
public BMTexture CreateTexture() => createCKObject<BMTexture>(BMap.BMFile_CreateTexture, (bmf, id) => new BMTexture(bmf, id));
public BMMaterial CreateMaterial() => createCKObject<BMMaterial>(BMap.BMFile_CreateMaterial, (bmf, id) => new BMMaterial(bmf, id));
public BMMesh CreateMesh() => createCKObject<BMMesh>(BMap.BMFile_CreateMesh, (bmf, id) => new BMMesh(bmf, id));
public BM3dObject Create3dObject() => createCKObject<BM3dObject>(BMap.BMFile_Create3dObject, (bmf, id) => new BM3dObject(bmf, id));
public BMGroup CreateGroup() => createCKObject<BMGroup>(BMap.BMFile_CreateGroup, (bmf, id) => new BMGroup(bmf, id));
}
} }