Compare commits

...

2 Commits

Author SHA1 Message Date
4d04b38d52 feat: continue improving BMapSharp.
- add BMFileWriter in BMapSharp.
2024-10-27 11:41:49 +08:00
623334f863 feat: update BMapSharp.
- update the function calling in BMapSharp.BMFileReader. (hope JIT can optimize my bad code served for beauty)
- remove outdated content in COMPILE.md
2024-10-24 16:39:11 +08:00
3 changed files with 118 additions and 117 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.
// 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
if (!m_MarshalerType.HasFlag(MarshalerType.In)) return nint.Zero;
if (!m_MarshalerType.HasFlag(MarshalerType.In)) return IntPtr.Zero;
// Check nullptr object.
if (ManagedObj is null) return nint.Zero;
if (ManagedObj is null) return IntPtr.Zero;
// Check argument type.
string[] castManagedObj = ManagedObj as string[];
if (castManagedObj is null)
@ -70,45 +70,45 @@ namespace BMapSharp {
// Allocate string items first
int szArrayItemCount = castManagedObj.Length;
int szArrayItemSize = Marshal.SizeOf<nint>();
nint[] apString = new nint[szArrayItemCount];
int szArrayItemSize = Marshal.SizeOf<IntPtr>();
IntPtr[] apString = new IntPtr[szArrayItemCount];
for (int i = 0; i < szArrayItemCount; ++i) {
// Check null string
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);
}
// Allocate array pointer now.
nint pArray = Marshal.AllocHGlobal(szArrayItemSize * (szArrayItemCount + 1));
IntPtr pArray = Marshal.AllocHGlobal(szArrayItemSize * (szArrayItemCount + 1));
// Copy string pointer data
Marshal.Copy(apString, 0, pArray, szArrayItemCount);
// Setup NULL ternimal
Marshal.WriteIntPtr(pArray + (szArrayItemSize * szArrayItemCount), nint.Zero);
Marshal.WriteIntPtr(pArray + (szArrayItemSize * szArrayItemCount), IntPtr.Zero);
// Return value
return pArray;
}
public object MarshalNativeToManaged(nint pNativeData) {
public object MarshalNativeToManaged(IntPtr pNativeData) {
// Check marshaler type
if (!m_MarshalerType.HasFlag(MarshalerType.Out)) return null;
// Check nullptr
if (pNativeData == nint.Zero) return null;
if (pNativeData == IntPtr.Zero) return null;
// Get the length of array
int szArrayItemCount = BMStringArrayMarshaler.GetArrayLength(pNativeData);
int szArrayItemSize = Marshal.SizeOf<nint>();
int szArrayItemSize = Marshal.SizeOf<IntPtr>();
// Prepare array cache and read it.
nint[] apString = new nint[szArrayItemCount];
IntPtr[] apString = new IntPtr[szArrayItemCount];
Marshal.Copy(pNativeData, apString, 0, szArrayItemCount);
// Iterate the array and process each string one by one.
string[] ret = new string[szArrayItemCount];
for (int i = 0; i < szArrayItemCount; ++i) {
// Get string pointer
nint pString = apString[i];
if (pString == nint.Zero) {
IntPtr pString = apString[i];
if (pString == IntPtr.Zero) {
ret[i] = null;
continue;
}
@ -120,25 +120,25 @@ namespace BMapSharp {
return ret;
}
public void CleanUpNativeData(nint pNativeData) {
public void CleanUpNativeData(IntPtr pNativeData) {
// Check marshaler type
if (!m_MarshalerType.HasFlag(MarshalerType.In)) return;
// Check nullptr
if (pNativeData == nint.Zero) return;
if (pNativeData == IntPtr.Zero) return;
// Get the length of array
int szArrayItemCount = BMStringArrayMarshaler.GetArrayLength(pNativeData);
int szArrayItemSize = Marshal.SizeOf<nint>();
int szArrayItemSize = Marshal.SizeOf<IntPtr>();
// Prepare array cache and read it.
nint[] apString = new nint[szArrayItemCount];
IntPtr[] apString = new IntPtr[szArrayItemCount];
Marshal.Copy(pNativeData, apString, 0, szArrayItemCount);
// Free array self
Marshal.FreeHGlobal(pNativeData);
// Iterate the string pointer array and free them one by one.
foreach (nint pString in apString) {
foreach (IntPtr pString in apString) {
// Free string pointer
if (pString == nint.Zero) continue;
if (pString == IntPtr.Zero) continue;
Marshal.FreeHGlobal(pString);
}
}
@ -157,9 +157,9 @@ namespace BMapSharp {
/// </summary>
/// <param name="ptr">The pointer to array for checking.</param>
/// <returns>The length of array (NULL terminal exclusive).</returns>
internal static int GetArrayLength(nint ptr) {
int count = 0, unit = Marshal.SizeOf<nint>();
while (Marshal.ReadIntPtr(ptr) != nint.Zero) {
internal static int GetArrayLength(IntPtr ptr) {
int count = 0, unit = Marshal.SizeOf<IntPtr>();
while (Marshal.ReadIntPtr(ptr) != IntPtr.Zero) {
ptr += unit;
++count;
}
@ -181,11 +181,11 @@ namespace BMapSharp {
m_MarshalerType = marshaler_type;
}
public nint MarshalManagedToNative(object ManagedObj) {
public IntPtr MarshalManagedToNative(object ManagedObj) {
// Check marshaler type
if (!m_MarshalerType.HasFlag(MarshalerType.In)) return nint.Zero;
if (!m_MarshalerType.HasFlag(MarshalerType.In)) return IntPtr.Zero;
// Check requirements.
if (ManagedObj is null) return nint.Zero;
if (ManagedObj is null) return IntPtr.Zero;
string castManagedObj = ManagedObj as string;
if (castManagedObj is null)
throw new MarshalDirectiveException("BMStringMarshaler must be used on a string.");
@ -193,20 +193,20 @@ namespace BMapSharp {
return BMStringMarshaler.ToNative(castManagedObj);
}
public object MarshalNativeToManaged(nint pNativeData) {
public object MarshalNativeToManaged(IntPtr pNativeData) {
// Check marshaler type
if (!m_MarshalerType.HasFlag(MarshalerType.Out)) return null;
// Check nullptr
if (pNativeData == nint.Zero) return null;
if (pNativeData == IntPtr.Zero) return null;
// Call self
return BMStringMarshaler.ToManaged(pNativeData);
}
public void CleanUpNativeData(nint pNativeData) {
public void CleanUpNativeData(IntPtr pNativeData) {
// Check marshaler type
if (!m_MarshalerType.HasFlag(MarshalerType.In)) return;
// Check nullptr
if (pNativeData == nint.Zero) return;
if (pNativeData == IntPtr.Zero) return;
// Free native pointer
Marshal.FreeHGlobal(pNativeData);
}
@ -225,7 +225,7 @@ namespace BMapSharp {
/// </summary>
/// <param name="ptr">The pointer for checking.</param>
/// <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>();
while (Marshal.ReadByte(ptr) != (byte)0) {
ptr += unit;
@ -240,13 +240,13 @@ namespace BMapSharp {
/// </summary>
/// <param name="obj">String object. Caller must make sure this object is not null.</param>
/// <returns>The created native data pointer.</returns>
internal static nint ToNative(string obj) {
internal static IntPtr ToNative(string obj) {
// Encode string first
byte[] encString = Encoding.UTF8.GetBytes(obj);
// Allocate string memory with extra NUL.
int szStringItemCount = encString.Length;
int szStringItemSize = Marshal.SizeOf<byte>();
nint pString = Marshal.AllocHGlobal(szStringItemSize * (szStringItemCount + 1));
IntPtr pString = Marshal.AllocHGlobal(szStringItemSize * (szStringItemCount + 1));
// Copy encoded string data
Marshal.Copy(encString, 0, pString, szStringItemCount);
// Setup NUL
@ -260,7 +260,7 @@ namespace BMapSharp {
/// </summary>
/// <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>
internal static string ToManaged(nint ptr) {
internal static string ToManaged(IntPtr ptr) {
// Get the length of given string.
int szStringItemCount = BMStringMarshaler.GetCStringLength(ptr);
int szStringItemSize = Marshal.SizeOf<byte>();

View File

@ -94,16 +94,19 @@ namespace BMapSharp.BMapWrapper {
#endregion
}
public abstract class AbstractCKObject {
internal AbstractCKObject(IntPtr raw_pointer, uint ckid) {
m_RawPointer = raw_pointer;
public abstract class AbstractCKObject : SafeHandle {
// Same as AbstractPointer, but not own this handle.
internal AbstractCKObject(IntPtr raw_pointer, uint ckid) : base(Utils.INVALID_PTR, false) {
this.handle = raw_pointer;
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;
protected bool isValid() => m_RawPointer != Utils.INVALID_PTR && m_RawPointer != Utils.INVALID_CKID;
protected IntPtr getPointer() => m_RawPointer;
protected bool isValid() => this.handle != Utils.INVALID_PTR && m_CKID != Utils.INVALID_CKID;
protected IntPtr getPointer() => this.handle;
protected uint getCKID() => m_CKID;
// private uint m_CKID;
@ -143,14 +146,14 @@ namespace BMapSharp.BMapWrapper {
#region Misc
public override int GetHashCode() => HashCode.Combine(m_RawPointer, m_CKID);
public override string ToString() => $"{m_RawPointer}, {m_CKID}";
public override int GetHashCode() => HashCode.Combine(this.handle, m_CKID);
public override string ToString() => $"{this.handle}, {m_CKID}";
#endregion
}
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() {
BMapException.ThrowIfFailed(BMap.BMObject_GetName(
@ -166,101 +169,107 @@ namespace BMapSharp.BMapWrapper {
}
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 {
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 {
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 {
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 {
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 {
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(
file_name, temp_folder, texture_folder,
Utils.BMapSharpCallback,
file_name, 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.handle);
return BMap.BMFile_Free(this.getPointer());
}
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)) { }
public uint GetTextureCount() {
BMapException.ThrowIfFailed(BMap.BMFile_GetTextureCount(this.getPointer(), out uint out_count));
private delegate bool FctProtoGetCount(IntPtr bmf, out uint cnt);
private delegate bool FctProtoGetObject(IntPtr bmf, uint idx, out uint id);
private delegate T FctProtoCreateInstance<T>(IntPtr bmf, uint id);
private uint getCKObjectCount(FctProtoGetCount fct_cnt) {
BMapException.ThrowIfFailed(fct_cnt(this.getPointer(), out uint out_count));
return out_count;
}
public IEnumerable<BMTexture> GetTextures() {
uint count = GetTextureCount();
private IEnumerable<T> getCKObjects<T>(FctProtoGetCount fct_cnt, FctProtoGetObject fct_obj, FctProtoCreateInstance<T> fct_crt) {
uint count = getCKObjectCount(fct_cnt);
for (uint i = 0; i < count; ++i) {
BMapException.ThrowIfFailed(BMap.BMFile_GetTexture(this.getPointer(), i, out uint out_id));
yield return new BMTexture(this.getPointer(), out_id);
BMapException.ThrowIfFailed(fct_obj(this.getPointer(), i, out uint out_id));
yield return fct_crt(this.getPointer(), out_id);
}
}
public uint GetMaterialCount() {
BMapException.ThrowIfFailed(BMap.BMFile_GetMaterialCount(this.getPointer(), out uint out_count));
return out_count;
}
public IEnumerable<BMMaterial> GetMaterials() {
uint count = GetMaterialCount();
for (uint i = 0; i < count; ++i) {
BMapException.ThrowIfFailed(BMap.BMFile_GetMaterial(this.getPointer(), i, out uint out_id));
yield return new BMMaterial(this.getPointer(), out_id);
}
}
public uint GetMeshCount() {
BMapException.ThrowIfFailed(BMap.BMFile_GetMeshCount(this.getPointer(), out uint out_count));
return out_count;
}
public IEnumerable<BMMesh> GetMeshes() {
uint count = GetMeshCount();
for (uint i = 0; i < count; ++i) {
BMapException.ThrowIfFailed(BMap.BMFile_GetMesh(this.getPointer(), i, out uint out_id));
yield return new BMMesh(this.getPointer(), out_id);
}
}
public uint Get3dObjectCount() {
BMapException.ThrowIfFailed(BMap.BMFile_Get3dObjectCount(this.getPointer(), out uint out_count));
return out_count;
}
public IEnumerable<BM3dObject> Get3dObjects() {
uint count = Get3dObjectCount();
for (uint i = 0; i < count; ++i) {
BMapException.ThrowIfFailed(BMap.BMFile_Get3dObject(this.getPointer(), i, out uint out_id));
yield return new BM3dObject(this.getPointer(), out_id);
}
}
public uint GetGroupCount() {
BMapException.ThrowIfFailed(BMap.BMFile_GetGroupCount(this.getPointer(), out uint out_count));
return out_count;
}
public IEnumerable<BMGroup> GetGroups() {
uint count = GetGroupCount();
for (uint i = 0; i < count; ++i) {
BMapException.ThrowIfFailed(BMap.BMFile_GetGroup(this.getPointer(), i, out uint out_id));
yield return new BMGroup(this.getPointer(), out_id);
}
}
public uint GetTextureCount() =>
getCKObjectCount(BMap.BMFile_GetTextureCount);
public IEnumerable<BMTexture> GetTextures() =>
getCKObjects<BMTexture>(BMap.BMFile_GetTextureCount, BMap.BMFile_GetTexture, (bmf, id) => new BMTexture(bmf, id));
public uint GetMaterialCount() =>
getCKObjectCount(BMap.BMFile_GetMaterialCount);
public IEnumerable<BMMaterial> GetMaterials() =>
getCKObjects<BMMaterial>(BMap.BMFile_GetMaterialCount, BMap.BMFile_GetMaterial, (bmf, id) => new BMMaterial(bmf, id));
public uint GetMeshCount() =>
getCKObjectCount(BMap.BMFile_GetMeshCount);
public IEnumerable<BMMesh> GetMeshes() =>
getCKObjects<BMMesh>(BMap.BMFile_GetMeshCount, BMap.BMFile_GetMesh, (bmf, id) => new BMMesh(bmf, id));
public uint Get3dObjectCount() =>
getCKObjectCount(BMap.BMFile_Get3dObjectCount);
public IEnumerable<BM3dObject> Get3dObjects() =>
getCKObjects<BM3dObject>(BMap.BMFile_Get3dObjectCount, BMap.BMFile_Get3dObject, (bmf, id) => new BM3dObject(bmf, id));
public uint GetGroupCount() =>
getCKObjectCount(BMap.BMFile_GetGroupCount);
public IEnumerable<BMGroup> GetGroups() =>
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));
}
}

View File

@ -42,7 +42,7 @@ First, create subdirectory `Bin/build` and `Bin/install`.
Then enter subdirectory `Bin/build` and use following command to configure CMake:
- Windows: `cmake -DNEMO_BUILD_UNVIRT=ON -DNEMO_BUILD_BMAP=ON -DNEMO_BUILD_DOC=OFF -DSTB_IMAGE_PATH=<path-to-stb> -DYYCC_PATH=<path-to-yycc-install> -DZLIB_HEADER_PATH=<path-to-zlib-hdr> -DZLIB_BINARY_PATH=<path-to-zlib-bin> ../..`
- Windows (MSVC): `cmake -DNEMO_BUILD_UNVIRT=ON -DNEMO_BUILD_BMAP=ON -DNEMO_BUILD_DOC=OFF -DSTB_IMAGE_PATH=<path-to-stb> -DYYCC_PATH=<path-to-yycc-install> -DZLIB_HEADER_PATH=<path-to-zlib-hdr> -DZLIB_BINARY_PATH=<path-to-zlib-bin> ../..`
- non-Windows: `cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DNEMO_BUILD_UNVIRT=ON -DNEMO_BUILD_BMAP=ON -DNEMO_BUILD_DOC=OFF -DSTB_IMAGE_PATH=<path-to-stb> -DYYCC_PATH=<path-to-yycc-install> ../..`
The arguments in command should be replaced by:
@ -62,8 +62,8 @@ The switches in command can be switched as you wish:
Execute following command to build libcmo21.
* Windows`cmake --build . --config RelWithDebInfo`
* non-Windows`cmake --build .`
* Windows: `cmake --build . --config RelWithDebInfo`
* non-Windows: `cmake --build .`
### Build Type
@ -76,11 +76,3 @@ Currently the CMake install script still has some bugs and can not work as expec
## Note
You may face issue when compiling this program on Linux or macOS because I develop this project on Windows frequently. The compatibility with Linux will only be checked just before releasing. And I don't have any Apple device to check the compatibility with macOS. So, for Linux issue, please report it directly and I will try to fix. For macOS bug, PR is welcomed.
It can be compiled on Windows via sln file. You should set up `LibRef.props` when using sln file to build this project on Windows.
You also can use CMake file to compile this project on Linux or anything else platform. However CMake may not be updated in time because I develop this project on Windows frequently.
You may need use this command to configure CMake: `cmake .. -DSTB_IMAGE_PATH="/path/to/stb-image" -DCMAKE_BUILD_TYPE=Release`