diff --git a/.run/Package Unity.run.xml b/.run/Package Unity.run.xml
new file mode 100644
index 0000000..c339d98
--- /dev/null
+++ b/.run/Package Unity.run.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Wacs.Core Publish Wacs.Core.dll.run.xml b/.run/Wacs.Core Publish Wacs.Core.dll.run.xml
new file mode 100644
index 0000000..9ab2962
--- /dev/null
+++ b/.run/Wacs.Core Publish Wacs.Core.dll.run.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/Wacs.Core Publish Wacs.WASIp1.dll.run.xml b/.run/Wacs.Core Publish Wacs.WASIp1.dll.run.xml
new file mode 100644
index 0000000..b864a2d
--- /dev/null
+++ b/.run/Wacs.Core Publish Wacs.WASIp1.dll.run.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/sync_version.sh b/.run/sync_version.sh
new file mode 100755
index 0000000..2c72ef2
--- /dev/null
+++ b/.run/sync_version.sh
@@ -0,0 +1,102 @@
+#!/bin/bash
+
+# --------------------------------------------
+# Script to Sync .NET Assembly Version to package.json
+# --------------------------------------------
+
+# Exit immediately if a command exits with a non-zero status
+set -e
+
+# Function to display usage
+usage() {
+ echo "Usage: $0 [path/to/project.csproj] [path/to/package.json]"
+ echo "If no arguments are provided, it searches for the first .csproj file and assumes package.json is in the current directory."
+ exit 1
+}
+
+# Check for help flag
+if [[ "$1" == "--help" || "$1" == "-h" ]]; then
+ usage
+fi
+
+# Assign arguments or set defaults
+CSPROJ_FILE="${1:-}"
+PACKAGE_JSON_FILE="${2:-package.json}"
+
+# Function to find the first .csproj file if not provided
+find_csproj() {
+ local csproj
+ csproj=$(find . -maxdepth 1 -name "*.csproj" | head -n 1)
+ echo "$csproj"
+}
+
+# Determine the .csproj file
+if [[ -z "$CSPROJ_FILE" ]]; then
+ CSPROJ_FILE=$(find_csproj)
+ if [[ -z "$CSPROJ_FILE" ]]; then
+ echo "Error: No .csproj file found in the current directory."
+ exit 1
+ fi
+fi
+
+# Check if the .csproj file exists
+if [[ ! -f "$CSPROJ_FILE" ]]; then
+ echo "Error: .csproj file '$CSPROJ_FILE' does not exist."
+ exit 1
+fi
+
+# Check if package.json exists
+if [[ ! -f "$PACKAGE_JSON_FILE" ]]; then
+ echo "Error: package.json file '$PACKAGE_JSON_FILE' does not exist."
+ exit 1
+fi
+
+# Extract the version from the .csproj file using sed
+# This sed command captures the content between and
+VERSION=$(sed -n 's:.*\(.*\).*:\1:p' "$CSPROJ_FILE")
+
+# If VERSION is empty, try to extract from AssemblyInfo.cs
+if [[ -z "$VERSION" ]]; then
+ # Assuming AssemblyInfo.cs is located in Properties directory
+ ASSEMBLY_INFO=$(find . -path "*/Properties/AssemblyInfo.cs" | head -n 1)
+ if [[ -n "$ASSEMBLY_INFO" ]]; then
+ VERSION=$(sed -n 's/.*AssemblyVersion("\([^"]*\)").*/\1/p' "$ASSEMBLY_INFO")
+ fi
+fi
+
+# If VERSION is still empty, exit with error
+if [[ -z "$VERSION" ]]; then
+ echo "Error: Could not find the version in $CSPROJ_FILE or AssemblyInfo.cs."
+ exit 1
+fi
+
+echo "Detected .NET project version: $VERSION"
+
+# Function to update package.json using jq
+update_package_json_jq() {
+ local ver="$1"
+ local pkg="$2"
+ jq --arg ver "$ver" '.version = $ver' "$pkg" > "${pkg}.tmp" && mv "${pkg}.tmp" "$pkg"
+}
+
+# Function to update package.json using sed
+update_package_json_sed() {
+ local ver="$1"
+ local pkg="$2"
+ # This regex matches the "version": "x.y.z" pattern and replaces it
+ sed -i.bak -E 's/("version"\s*:\s*")[^"]+(")/\1'"$ver"'\2/' "$pkg"
+}
+
+# Update package.json
+if command -v jq > /dev/null 2>&1; then
+ echo "Updating package.json using jq..."
+ update_package_json_jq "$VERSION" "$PACKAGE_JSON_FILE"
+ echo "Successfully updated 'version' in $PACKAGE_JSON_FILE to $VERSION."
+else
+ echo "jq is not installed. Falling back to sed."
+ update_package_json_sed "$VERSION" "$PACKAGE_JSON_FILE"
+ echo "Successfully updated 'version' in $PACKAGE_JSON_FILE to $VERSION."
+ echo "A backup of the original package.json is saved as package.json.bak."
+fi
+
+exit 0
\ No newline at end of file
diff --git a/global.json b/global.json
new file mode 100644
index 0000000..87aef9f
--- /dev/null
+++ b/global.json
@@ -0,0 +1,7 @@
+{
+ "sdk": {
+ "version": "6.0.0",
+ "rollForward": "latestMajor",
+ "allowPrerelease": false
+ }
+}
\ No newline at end of file
diff --git a/unity/CHANGELOG.md b/unity/CHANGELOG.md
new file mode 100644
index 0000000..64310b6
--- /dev/null
+++ b/unity/CHANGELOG.md
@@ -0,0 +1,10 @@
+# Changelog
+
+All notable changes to the unity package will be documented in this file.
+
+## [0.1.4]
+### Added
+- Initial project setup for Unity.
+
+### Changed
+- Updated project structure to allow installation as a Unity package from git.
diff --git a/unity/Documentation~/manual.md b/unity/Documentation~/manual.md
new file mode 100644
index 0000000..8c80acf
--- /dev/null
+++ b/unity/Documentation~/manual.md
@@ -0,0 +1,7 @@
+## Documentation...
+
+I build and maintain WACS as a solo dev.
+
+I hope someday I'll have time for some great documentation.
+
+Until then, you can browse the [github repo](https://github.com/kelnishi/WACS/discussions) and connect with me there.
\ No newline at end of file
diff --git a/unity/Editor/AssetImporters/WasmImporter.cs b/unity/Editor/AssetImporters/WasmImporter.cs
new file mode 100644
index 0000000..0f8de10
--- /dev/null
+++ b/unity/Editor/AssetImporters/WasmImporter.cs
@@ -0,0 +1,22 @@
+using UnityEngine;
+using UnityEditor;
+using UnityEditor.AssetImporters;
+using System.IO;
+
+[ScriptedImporter(1, "wasm")]
+public class WasmImporter : ScriptedImporter
+{
+ public override void OnImportAsset(AssetImportContext ctx)
+ {
+ // Load the binary data from the .kelvin file
+ byte[] data = File.ReadAllBytes(ctx.assetPath);
+
+ // Create an instance of KelvinAsset and assign the data
+ WasmAsset wasmAsset = ScriptableObject.CreateInstance();
+ wasmAsset.data = data;
+
+ // Add the asset to the import context
+ ctx.AddObjectToAsset("Wasm Asset", wasmAsset);
+ ctx.SetMainObject(wasmAsset);
+ }
+}
\ No newline at end of file
diff --git a/unity/README.md b/unity/README.md
new file mode 100644
index 0000000..23447a3
--- /dev/null
+++ b/unity/README.md
@@ -0,0 +1,55 @@
+# WACS - WebAssembly Interpreter
+
+**WACS** is a pure C# WebAssembly Interpreter designed for .NET environments, including Unity's IL2CPP.
+
+## Features
+
+- **Execute WebAssembly**: Load and run .wasm files or data streams.
+- **Interpreter-Only**: No JIT compilation, works in AOT modes like IL2CPP on iOS.
+- **Magic Interop**: Reflection based interop allows for easy function binding with little boilerplate code.
+- **Open Source**: Library is fully open source, free to inspect, change, and improve.
+
+## Unity Installation
+
+ - Window>Package Manager
+ - Click + Add package from git URL...
+ - Enter the repo URL: ```https://github.com/kelnishi/WACS```
+ - Click Add
+
+## Usage
+
+To use Wacs, you'll need to instantiate a runtime and a module.
+
+```csharp
+using Wacs.Core;
+using Wacs.Core.Runtime;
+using Wacs.Core.Runtime.Types;
+
+public class ExampleClass : MonoBehaviour
+{
+ [SerializeField] private WasmAsset wasmAsset;
+ public string moduleName = "_";
+
+ void Start()
+ {
+ var stream = new MemoryStream(wasmAsset.data);
+ var module = BinaryModuleParser.ParseWasm(stream);
+
+ _runtime = new WasmRuntime();
+ _output = new();
+ _runtime.BindHostFunction>(("env", "sayc"), c =>
+ {
+ _output.Append(c);
+ });
+
+ _moduleInstance = _runtime.InstantiateModule(module, new RuntimeOptions { SkipModuleValidation = true});
+ _runtime.RegisterModule(moduleName, _moduleInstance);
+
+ var fa = runtime.GetExportedFunction((moduleName, "main"));
+ var main = runtime.CreateInvoker>(fa);
+
+ //Execute the module
+ int result = main();
+ }
+}
+```
diff --git a/unity/Runtime/Plugins/FluentValidation.dll b/unity/Runtime/Plugins/FluentValidation.dll
new file mode 100755
index 0000000..1c73e5c
Binary files /dev/null and b/unity/Runtime/Plugins/FluentValidation.dll differ
diff --git a/unity/Runtime/Plugins/Microsoft.Extensions.ObjectPool.dll b/unity/Runtime/Plugins/Microsoft.Extensions.ObjectPool.dll
new file mode 100755
index 0000000..2ecee33
Binary files /dev/null and b/unity/Runtime/Plugins/Microsoft.Extensions.ObjectPool.dll differ
diff --git a/unity/Runtime/Plugins/Wacs.Core.deps.json b/unity/Runtime/Plugins/Wacs.Core.deps.json
new file mode 100644
index 0000000..b6ff191
--- /dev/null
+++ b/unity/Runtime/Plugins/Wacs.Core.deps.json
@@ -0,0 +1,58 @@
+{
+ "runtimeTarget": {
+ "name": ".NETStandard,Version=v2.1/",
+ "signature": ""
+ },
+ "compilationOptions": {},
+ "targets": {
+ ".NETStandard,Version=v2.1": {},
+ ".NETStandard,Version=v2.1/": {
+ "Wacs.Core/0.1.4": {
+ "dependencies": {
+ "FluentValidation": "11.10.0",
+ "Microsoft.Extensions.ObjectPool": "9.0.0"
+ },
+ "runtime": {
+ "Wacs.Core.dll": {}
+ }
+ },
+ "FluentValidation/11.10.0": {
+ "runtime": {
+ "lib/netstandard2.1/FluentValidation.dll": {
+ "assemblyVersion": "11.0.0.0",
+ "fileVersion": "11.10.0.0"
+ }
+ }
+ },
+ "Microsoft.Extensions.ObjectPool/9.0.0": {
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll": {
+ "assemblyVersion": "9.0.0.0",
+ "fileVersion": "9.0.24.52903"
+ }
+ }
+ }
+ }
+ },
+ "libraries": {
+ "Wacs.Core/0.1.4": {
+ "type": "project",
+ "serviceable": false,
+ "sha512": ""
+ },
+ "FluentValidation/11.10.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-qsJGSJDdZ8qiG+lVJ70PZfJHcEdq8UQZ/tZDXoj78/iHKG6lVKtMJsD11zyyv/IPc7rwqGqnFoFLTNzpo3IPYg==",
+ "path": "fluentvalidation/11.10.0",
+ "hashPath": "fluentvalidation.11.10.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.ObjectPool/9.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-UbsU/gYe4nv1DeqMXIVzDfNNek7Sk2kKuAOXL/Y+sLcAR0HwFUqzg1EPiU88jeHNe0g81aPvvHbvHarQr3r9IA==",
+ "path": "microsoft.extensions.objectpool/9.0.0",
+ "hashPath": "microsoft.extensions.objectpool.9.0.0.nupkg.sha512"
+ }
+ }
+}
\ No newline at end of file
diff --git a/unity/Runtime/Plugins/Wacs.Core.dll b/unity/Runtime/Plugins/Wacs.Core.dll
new file mode 100644
index 0000000..20511c5
Binary files /dev/null and b/unity/Runtime/Plugins/Wacs.Core.dll differ
diff --git a/unity/Runtime/Plugins/Wacs.Core.pdb b/unity/Runtime/Plugins/Wacs.Core.pdb
new file mode 100644
index 0000000..2f4de56
Binary files /dev/null and b/unity/Runtime/Plugins/Wacs.Core.pdb differ
diff --git a/unity/Runtime/Plugins/Wacs.WASIp1.deps.json b/unity/Runtime/Plugins/Wacs.WASIp1.deps.json
new file mode 100644
index 0000000..041d873
--- /dev/null
+++ b/unity/Runtime/Plugins/Wacs.WASIp1.deps.json
@@ -0,0 +1,85 @@
+{
+ "runtimeTarget": {
+ "name": ".NETStandard,Version=v2.1/",
+ "signature": ""
+ },
+ "compilationOptions": {},
+ "targets": {
+ ".NETStandard,Version=v2.1": {},
+ ".NETStandard,Version=v2.1/": {
+ "Wacs.WASIp1/0.9.1": {
+ "dependencies": {
+ "WACS": "0.1.4",
+ "Wacs.Core": "0.1.4.0"
+ },
+ "runtime": {
+ "Wacs.WASIp1.dll": {}
+ }
+ },
+ "FluentValidation/11.10.0": {
+ "runtime": {
+ "lib/netstandard2.1/FluentValidation.dll": {
+ "assemblyVersion": "11.0.0.0",
+ "fileVersion": "11.10.0.0"
+ }
+ }
+ },
+ "Microsoft.Extensions.ObjectPool/9.0.0": {
+ "runtime": {
+ "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll": {
+ "assemblyVersion": "9.0.0.0",
+ "fileVersion": "9.0.24.52903"
+ }
+ }
+ },
+ "WACS/0.1.4": {
+ "dependencies": {
+ "FluentValidation": "11.10.0",
+ "Microsoft.Extensions.ObjectPool": "9.0.0"
+ },
+ "runtime": {
+ "Wacs.Core.dll": {}
+ }
+ },
+ "Wacs.Core/0.1.4.0": {
+ "runtime": {
+ "Wacs.Core.dll": {
+ "assemblyVersion": "0.1.4.0",
+ "fileVersion": "0.1.4.0"
+ }
+ }
+ }
+ }
+ },
+ "libraries": {
+ "Wacs.WASIp1/0.9.1": {
+ "type": "project",
+ "serviceable": false,
+ "sha512": ""
+ },
+ "FluentValidation/11.10.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-qsJGSJDdZ8qiG+lVJ70PZfJHcEdq8UQZ/tZDXoj78/iHKG6lVKtMJsD11zyyv/IPc7rwqGqnFoFLTNzpo3IPYg==",
+ "path": "fluentvalidation/11.10.0",
+ "hashPath": "fluentvalidation.11.10.0.nupkg.sha512"
+ },
+ "Microsoft.Extensions.ObjectPool/9.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-UbsU/gYe4nv1DeqMXIVzDfNNek7Sk2kKuAOXL/Y+sLcAR0HwFUqzg1EPiU88jeHNe0g81aPvvHbvHarQr3r9IA==",
+ "path": "microsoft.extensions.objectpool/9.0.0",
+ "hashPath": "microsoft.extensions.objectpool.9.0.0.nupkg.sha512"
+ },
+ "WACS/0.1.4": {
+ "type": "project",
+ "serviceable": false,
+ "sha512": ""
+ },
+ "Wacs.Core/0.1.4.0": {
+ "type": "reference",
+ "serviceable": false,
+ "sha512": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/unity/Runtime/Plugins/Wacs.WASIp1.dll b/unity/Runtime/Plugins/Wacs.WASIp1.dll
new file mode 100644
index 0000000..5337727
Binary files /dev/null and b/unity/Runtime/Plugins/Wacs.WASIp1.dll differ
diff --git a/unity/Runtime/Plugins/Wacs.WASIp1.pdb b/unity/Runtime/Plugins/Wacs.WASIp1.pdb
new file mode 100644
index 0000000..eb908eb
Binary files /dev/null and b/unity/Runtime/Plugins/Wacs.WASIp1.pdb differ
diff --git a/unity/Runtime/WasmAsset.cs b/unity/Runtime/WasmAsset.cs
new file mode 100644
index 0000000..bfa5a4f
--- /dev/null
+++ b/unity/Runtime/WasmAsset.cs
@@ -0,0 +1,8 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public class WasmAsset : ScriptableObject
+{
+ public byte[] data;
+}
diff --git a/unity/Samples~/WasmRunner/Scripts/WasmRunner.cs b/unity/Samples~/WasmRunner/Scripts/WasmRunner.cs
new file mode 100644
index 0000000..9b2449d
--- /dev/null
+++ b/unity/Samples~/WasmRunner/Scripts/WasmRunner.cs
@@ -0,0 +1,98 @@
+using System;
+using System.IO;
+using System.Text;
+using UnityEngine;
+using Wacs.Core;
+using Wacs.Core.Runtime;
+using Wacs.Core.Runtime.Types;
+using Wacs.Core.WASIp1;
+
+public class WasmRunner : MonoBehaviour
+{
+ [SerializeField] private WasmAsset wasmAsset;
+
+ public string moduleName = "_";
+
+ private WasmRuntime _runtime;
+
+ private ModuleInstance _moduleInstance;
+
+ private StringBuilder _output;
+
+ private TextMesh text;
+
+ private void OnEnable()
+ {
+ text = GetComponent();
+ var stream = new MemoryStream(wasmAsset.data);
+ var module = BinaryModuleParser.ParseWasm(stream);
+
+ _runtime = new WasmRuntime();
+ _output = new();
+ _runtime.BindHostFunction>(("env", "sayc"), c =>
+ {
+ _output.Append(c);
+ });
+
+ _moduleInstance = _runtime.InstantiateModule(module, new RuntimeOptions { SkipModuleValidation = true});
+ _runtime.RegisterModule(moduleName, _moduleInstance);
+ }
+
+ // Start is called before the first frame update
+ void Start()
+ {
+ var callOptions = new InvokerOptions {
+ LogGas = false,
+ LogProgressEvery = 0,
+ LogInstructionExecution = InstructionLogging.None,
+ CalculateLineNumbers = false,
+ CollectStats = false,
+ };
+
+ //Wasm/WASI entry points
+ if (_moduleInstance.StartFunc != null)
+ {
+ var caller = _runtime.CreateInvoker(_moduleInstance.StartFunc, callOptions);
+ try
+ {
+ caller();
+ }
+ catch (TrapException exc)
+ {
+ Debug.LogError(exc);
+ }
+ catch (SignalException exc)
+ {
+ ErrNo sig = (ErrNo)exc.Signal;
+ Debug.LogError($"WasmRunner: Module exited with signal {exc}");
+ }
+ }
+ else if (_runtime.TryGetExportedFunction((moduleName, "main"), out var mainAddr))
+ {
+ var caller = _runtime.CreateInvoker>(mainAddr, callOptions);
+ try
+ {
+ int result = caller();
+ if (text)
+ text.text = _output.ToString();
+
+ Debug.Log($"WasmRunner: Module returned result:{result}");
+ }
+ catch (TrapException exc)
+ {
+ Debug.LogError(exc);
+ }
+ catch (SignalException exc)
+ {
+ ErrNo sig = (ErrNo)exc.Signal;
+ Debug.LogError($"WasmRunner: Module exited with signal {exc}");
+ }
+ }
+ }
+
+ // Update is called once per frame
+ void Update()
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/unity/Samples~/WasmRunner/WASM/HelloWorld.wasm b/unity/Samples~/WasmRunner/WASM/HelloWorld.wasm
new file mode 100644
index 0000000..9080e49
Binary files /dev/null and b/unity/Samples~/WasmRunner/WASM/HelloWorld.wasm differ
diff --git a/unity/package.json b/unity/package.json
new file mode 100644
index 0000000..b9de279
--- /dev/null
+++ b/unity/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "com.kelnishi.wacs",
+ "version": "0.1.4",
+ "displayName": "WACS - WebAssembly Interpreter",
+ "description": "This package includes the WACS WebAssembly Interpreter runtime and Unity asset types for handling .wasm files.",
+ "documentationUrl": "https://github.com/kelnishi/WACS/tree/main/unity/Documentation~/manual.md",
+ "changelogUrl": "https://github.com/kelnishi/WACS/tree/main/unity/CHANGELOG.md",
+ "licensesUrl": "https://github.com/kelnishi/WACS/tree/main/LICENSE",
+ "unity": "2021.3",
+ "dependencies": {},
+ "keywords": [
+ "wasm",
+ "WACS",
+ "WebAssebly",
+ "wasi"
+ ],
+ "author": {
+ "name": "Kelvin Nishikawa",
+ "email": "kelnishiplays@gmail.com",
+ "url": "https://github.com/kelnishi/WACS"
+ },
+ "samples": [
+ {
+ "displayName": "WasmRunner Example",
+ "description": "Contains a MonoBehavior showing how to load a wasm file and execute it.",
+ "path": "Samples~/WasmRunner"
+ }
+ ]
+}