using System;
using System.Runtime.InteropServices;
namespace AliceZip
{
public enum AliceError
{
Success = 0,
NullPointer = 1,
InvalidParameter = 2,
CompressionError = 3,
DecompressionError = 4,
AllocationError = 5,
InvalidData = 6
}
[StructLayout(LayoutKind.Sequential)]
internal struct AliceBuffer
{
public IntPtr data;
public UIntPtr len;
public UIntPtr capacity;
}
[StructLayout(LayoutKind.Sequential)]
internal struct AliceFloatBuffer
{
public IntPtr data;
public UIntPtr len;
public UIntPtr capacity;
}
[StructLayout(LayoutKind.Sequential)]
public struct FourierCoefficient
{
public UIntPtr frequency;
public float amplitude;
public float phase;
public FourierCoefficient(int frequency, float amplitude, float phase)
{
this.frequency = (UIntPtr)frequency;
this.amplitude = amplitude;
this.phase = phase;
}
}
internal static class NativeMethods
{
#if UNITY_IOS && !UNITY_EDITOR
private const string LibraryName = "__Internal";
#elif UNITY_ANDROID && !UNITY_EDITOR
private const string LibraryName = "alice_core";
#elif UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
private const string LibraryName = "alice_core.dll";
#elif UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX
private const string LibraryName = "libalice_core.dylib";
#elif UNITY_STANDALONE_LINUX || UNITY_EDITOR_LINUX
private const string LibraryName = "libalice_core.so";
#else
private const string LibraryName = "alice_core";
#endif
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern void alice_free_buffer(ref AliceBuffer buffer);
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern void alice_free_float_buffer(ref AliceFloatBuffer buffer);
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr alice_version();
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern void alice_version_numbers(out uint major, out uint minor, out uint patch);
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr alice_get_last_error();
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern AliceError alice_perlin_2d(
UIntPtr width, UIntPtr height, ulong seed,
float scale, uint octaves, out AliceFloatBuffer buffer);
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern AliceError alice_perlin_advanced(
UIntPtr width, UIntPtr height, ulong seed,
float scale, uint octaves, float persistence, float lacunarity,
out AliceFloatBuffer buffer);
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern AliceError alice_sine_wave(
UIntPtr n, float frequency, float amplitude, float phase, float dcOffset,
out AliceFloatBuffer buffer);
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern AliceError alice_fourier_generate(
UIntPtr n, [In] FourierCoefficient[] coefficients, UIntPtr numCoefficients,
float dcOffset, out AliceFloatBuffer buffer);
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern AliceError alice_polynomial_generate(
UIntPtr n, [In] double[] coefficients, UIntPtr numCoefficients,
out AliceFloatBuffer buffer);
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern AliceError alice_lzma_compress(
[In] byte[] data, UIntPtr len, uint preset, out AliceBuffer buffer);
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern AliceError alice_lzma_decompress(
[In] byte[] data, UIntPtr len, out AliceBuffer buffer);
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern AliceError alice_zlib_compress(
[In] byte[] data, UIntPtr len, uint level, out AliceBuffer buffer);
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern AliceError alice_zlib_decompress(
[In] byte[] data, UIntPtr len, out AliceBuffer buffer);
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern AliceError alice_residual_compress(
[In] float[] residual, UIntPtr len, byte bits, uint lzmaPreset,
out AliceBuffer buffer);
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern AliceError alice_residual_decompress(
[In] byte[] data, UIntPtr len, out AliceFloatBuffer buffer);
}
public class AliceException : Exception
{
public AliceError ErrorCode { get; }
public AliceException(AliceError error, string message = null)
: base(message ?? GetDefaultMessage(error))
{
ErrorCode = error;
}
private static string GetDefaultMessage(AliceError error)
{
return error switch
{
AliceError.NullPointer => "A required pointer was null",
AliceError.InvalidParameter => "Invalid parameter value",
AliceError.CompressionError => "Compression operation failed",
AliceError.DecompressionError => "Decompression operation failed",
AliceError.AllocationError => "Memory allocation failed",
AliceError.InvalidData => "Input data is invalid or corrupted",
_ => $"Unknown error: {error}"
};
}
}
public static class Alice
{
public static string Version
{
get
{
IntPtr ptr = NativeMethods.alice_version();
return Marshal.PtrToStringAnsi(ptr);
}
}
public static (uint Major, uint Minor, uint Patch) VersionNumbers
{
get
{
NativeMethods.alice_version_numbers(out uint major, out uint minor, out uint patch);
return (major, minor, patch);
}
}
public static string LastError
{
get
{
IntPtr ptr = NativeMethods.alice_get_last_error();
return ptr == IntPtr.Zero ? null : Marshal.PtrToStringAnsi(ptr);
}
}
private static void CheckError(AliceError error)
{
if (error != AliceError.Success)
{
string message = LastError;
throw new AliceException(error, message);
}
}
#region Perlin Noise
public static float[] PerlinNoise2D(int width, int height, ulong seed = 42,
float scale = 10.0f, uint octaves = 4)
{
AliceError error = NativeMethods.alice_perlin_2d(
(UIntPtr)width, (UIntPtr)height, seed, scale, octaves, out AliceFloatBuffer buffer);
CheckError(error);
try
{
int len = (int)buffer.len;
float[] result = new float[len];
Marshal.Copy(buffer.data, result, 0, len);
return result;
}
finally
{
NativeMethods.alice_free_float_buffer(ref buffer);
}
}
public static float[] PerlinNoiseAdvanced(int width, int height, ulong seed = 42,
float scale = 10.0f, uint octaves = 4, float persistence = 0.5f, float lacunarity = 2.0f)
{
AliceError error = NativeMethods.alice_perlin_advanced(
(UIntPtr)width, (UIntPtr)height, seed, scale, octaves, persistence, lacunarity,
out AliceFloatBuffer buffer);
CheckError(error);
try
{
int len = (int)buffer.len;
float[] result = new float[len];
Marshal.Copy(buffer.data, result, 0, len);
return result;
}
finally
{
NativeMethods.alice_free_float_buffer(ref buffer);
}
}
#endregion
#region Signal Generation
public static float[] SineWave(int n, float frequency, float amplitude = 1.0f,
float phase = 0.0f, float dcOffset = 0.0f)
{
AliceError error = NativeMethods.alice_sine_wave(
(UIntPtr)n, frequency, amplitude, phase, dcOffset, out AliceFloatBuffer buffer);
CheckError(error);
try
{
int len = (int)buffer.len;
float[] result = new float[len];
Marshal.Copy(buffer.data, result, 0, len);
return result;
}
finally
{
NativeMethods.alice_free_float_buffer(ref buffer);
}
}
public static float[] FourierGenerate(int n, FourierCoefficient[] coefficients,
float dcOffset = 0.0f)
{
AliceError error = NativeMethods.alice_fourier_generate(
(UIntPtr)n, coefficients, (UIntPtr)coefficients.Length, dcOffset,
out AliceFloatBuffer buffer);
CheckError(error);
try
{
int len = (int)buffer.len;
float[] result = new float[len];
Marshal.Copy(buffer.data, result, 0, len);
return result;
}
finally
{
NativeMethods.alice_free_float_buffer(ref buffer);
}
}
public static float[] PolynomialGenerate(int n, double[] coefficients)
{
AliceError error = NativeMethods.alice_polynomial_generate(
(UIntPtr)n, coefficients, (UIntPtr)coefficients.Length, out AliceFloatBuffer buffer);
CheckError(error);
try
{
int len = (int)buffer.len;
float[] result = new float[len];
Marshal.Copy(buffer.data, result, 0, len);
return result;
}
finally
{
NativeMethods.alice_free_float_buffer(ref buffer);
}
}
#endregion
#region Compression
public static byte[] LzmaCompress(byte[] data, uint preset = 6)
{
AliceError error = NativeMethods.alice_lzma_compress(
data, (UIntPtr)data.Length, preset, out AliceBuffer buffer);
CheckError(error);
try
{
int len = (int)buffer.len;
byte[] result = new byte[len];
Marshal.Copy(buffer.data, result, 0, len);
return result;
}
finally
{
NativeMethods.alice_free_buffer(ref buffer);
}
}
public static byte[] LzmaDecompress(byte[] data)
{
AliceError error = NativeMethods.alice_lzma_decompress(
data, (UIntPtr)data.Length, out AliceBuffer buffer);
CheckError(error);
try
{
int len = (int)buffer.len;
byte[] result = new byte[len];
Marshal.Copy(buffer.data, result, 0, len);
return result;
}
finally
{
NativeMethods.alice_free_buffer(ref buffer);
}
}
public static byte[] ZlibCompress(byte[] data, uint level = 6)
{
AliceError error = NativeMethods.alice_zlib_compress(
data, (UIntPtr)data.Length, level, out AliceBuffer buffer);
CheckError(error);
try
{
int len = (int)buffer.len;
byte[] result = new byte[len];
Marshal.Copy(buffer.data, result, 0, len);
return result;
}
finally
{
NativeMethods.alice_free_buffer(ref buffer);
}
}
public static byte[] ZlibDecompress(byte[] data)
{
AliceError error = NativeMethods.alice_zlib_decompress(
data, (UIntPtr)data.Length, out AliceBuffer buffer);
CheckError(error);
try
{
int len = (int)buffer.len;
byte[] result = new byte[len];
Marshal.Copy(buffer.data, result, 0, len);
return result;
}
finally
{
NativeMethods.alice_free_buffer(ref buffer);
}
}
public static byte[] ResidualCompress(float[] residual, byte bits = 8, uint lzmaPreset = 6)
{
AliceError error = NativeMethods.alice_residual_compress(
residual, (UIntPtr)residual.Length, bits, lzmaPreset, out AliceBuffer buffer);
CheckError(error);
try
{
int len = (int)buffer.len;
byte[] result = new byte[len];
Marshal.Copy(buffer.data, result, 0, len);
return result;
}
finally
{
NativeMethods.alice_free_buffer(ref buffer);
}
}
public static float[] ResidualDecompress(byte[] data)
{
AliceError error = NativeMethods.alice_residual_decompress(
data, (UIntPtr)data.Length, out AliceFloatBuffer buffer);
CheckError(error);
try
{
int len = (int)buffer.len;
float[] result = new float[len];
Marshal.Copy(buffer.data, result, 0, len);
return result;
}
finally
{
NativeMethods.alice_free_float_buffer(ref buffer);
}
}
#endregion
}
}