diff --git a/.editorconfig b/.editorconfig index a652278..0b2bf57 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,3 +14,8 @@ dotnet_diagnostic.UNT0014.severity = error charset = utf-8 indent_size = 4 indent_style = space + +[*.csproj] +charset = utf-8 +indent_size = 2 +indent_style = space diff --git a/DependencyDeclaration~/.gitignore b/DependencyDeclaration~/.gitignore new file mode 100644 index 0000000..1746e32 --- /dev/null +++ b/DependencyDeclaration~/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/DependencyDeclaration~/DependencyDeclaration~.csproj b/DependencyDeclaration~/DependencyDeclaration~.csproj new file mode 100644 index 0000000..813817c --- /dev/null +++ b/DependencyDeclaration~/DependencyDeclaration~.csproj @@ -0,0 +1,15 @@ + + + + + netstandard2.0 + portable + ResoniteImportHelper.Internal.DependencyDeclaration + true + PackageReference + + + + + + diff --git a/DependencyDeclaration~/Program.cs b/DependencyDeclaration~/Program.cs new file mode 100644 index 0000000..ce9e473 --- /dev/null +++ b/DependencyDeclaration~/Program.cs @@ -0,0 +1,8 @@ +// Please run `dotnet publish` and copy them into `0ReflectionMetadata` respectively. +namespace ResoniteImportHelper.Internal.DependencyDeclaration +{ + internal sealed class Program + { + private static void Main(string[] args) {} + } +} diff --git a/DependencyManager~/.gitignore b/DependencyManager~/.gitignore new file mode 100644 index 0000000..1746e32 --- /dev/null +++ b/DependencyManager~/.gitignore @@ -0,0 +1,2 @@ +bin +obj diff --git a/DependencyManager~/DependencyManager~.csproj b/DependencyManager~/DependencyManager~.csproj new file mode 100644 index 0000000..ff72638 --- /dev/null +++ b/DependencyManager~/DependencyManager~.csproj @@ -0,0 +1,16 @@ + + + + Exe + netstandard2.0 + ResoniteImportHelper.Internal.DependencyManager + disable + enable + 13 + + + + + + + diff --git a/DependencyManager~/Program.cs b/DependencyManager~/Program.cs new file mode 100644 index 0000000..b3c1378 --- /dev/null +++ b/DependencyManager~/Program.cs @@ -0,0 +1,19 @@ +namespace ResoniteImportHelper.Internal.DependencyManager +{ + public sealed class Args(string From, string To) + { + } + + public sealed class Program + { + private static void Main(string[] args) + { + Main0(new Args(args[0], args[1])); + } + + public static void Main0(Args args) + { + // TODO: dotnet publish + } + } +} diff --git a/Editor/0ReflectionMetadata.meta b/Editor/0ReflectionMetadata.meta new file mode 100644 index 0000000..a8ee96e --- /dev/null +++ b/Editor/0ReflectionMetadata.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c75c0b9bc1ac4903acfe5854cac5ffa2 +timeCreated: 1738982722 \ No newline at end of file diff --git a/Editor/0ReflectionMetadata/AssemblyInfo.cs b/Editor/0ReflectionMetadata/AssemblyInfo.cs new file mode 100644 index 0000000..c3cee86 --- /dev/null +++ b/Editor/0ReflectionMetadata/AssemblyInfo.cs @@ -0,0 +1 @@ +// This file is dummy! diff --git a/Editor/0ReflectionMetadata/AssemblyInfo.cs.meta b/Editor/0ReflectionMetadata/AssemblyInfo.cs.meta new file mode 100644 index 0000000..ecf193b --- /dev/null +++ b/Editor/0ReflectionMetadata/AssemblyInfo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 03c4b506f0ef4898848d5c6011e13f78 +timeCreated: 1738982788 \ No newline at end of file diff --git a/Editor/0ReflectionMetadata/ResoniteImportHelper.0ReflectionMetadata.asmdef b/Editor/0ReflectionMetadata/ResoniteImportHelper.0ReflectionMetadata.asmdef new file mode 100644 index 0000000..88393e3 --- /dev/null +++ b/Editor/0ReflectionMetadata/ResoniteImportHelper.0ReflectionMetadata.asmdef @@ -0,0 +1,5 @@ +{ + "name": "ResoniteImportHelper.0ReflectionMetadata", + "autoReferenced": false, + "noEngineReferences": true +} diff --git a/Editor/0ReflectionMetadata/ResoniteImportHelper.0ReflectionMetadata.asmdef.meta b/Editor/0ReflectionMetadata/ResoniteImportHelper.0ReflectionMetadata.asmdef.meta new file mode 100644 index 0000000..5ca3043 --- /dev/null +++ b/Editor/0ReflectionMetadata/ResoniteImportHelper.0ReflectionMetadata.asmdef.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e40e5567821840249692e1985c2558f0 +timeCreated: 1738982741 \ No newline at end of file diff --git a/Editor/0ReflectionMetadata/System.Buffers.dll b/Editor/0ReflectionMetadata/System.Buffers.dll new file mode 100644 index 0000000..c0970c0 Binary files /dev/null and b/Editor/0ReflectionMetadata/System.Buffers.dll differ diff --git a/Editor/0ReflectionMetadata/System.Buffers.dll.meta b/Editor/0ReflectionMetadata/System.Buffers.dll.meta new file mode 100644 index 0000000..fade708 --- /dev/null +++ b/Editor/0ReflectionMetadata/System.Buffers.dll.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1179d56e7dd8425ea87d92f3081f4b04 +timeCreated: 1738995205 \ No newline at end of file diff --git a/Editor/0ReflectionMetadata/System.Collections.Immutable.dll b/Editor/0ReflectionMetadata/System.Collections.Immutable.dll new file mode 100644 index 0000000..b81b2ee Binary files /dev/null and b/Editor/0ReflectionMetadata/System.Collections.Immutable.dll differ diff --git a/Editor/0ReflectionMetadata/System.Collections.Immutable.dll.meta b/Editor/0ReflectionMetadata/System.Collections.Immutable.dll.meta new file mode 100644 index 0000000..973e5fb --- /dev/null +++ b/Editor/0ReflectionMetadata/System.Collections.Immutable.dll.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b2c6c8e045a34d1b870fbce0af11ba60 +timeCreated: 1738995205 \ No newline at end of file diff --git a/Editor/0ReflectionMetadata/System.Memory.dll b/Editor/0ReflectionMetadata/System.Memory.dll new file mode 100644 index 0000000..1e6aef8 Binary files /dev/null and b/Editor/0ReflectionMetadata/System.Memory.dll differ diff --git a/Editor/0ReflectionMetadata/System.Memory.dll.meta b/Editor/0ReflectionMetadata/System.Memory.dll.meta new file mode 100644 index 0000000..1686241 --- /dev/null +++ b/Editor/0ReflectionMetadata/System.Memory.dll.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 749c744de6664ab4b2de77f1634f6e2b +timeCreated: 1738995205 \ No newline at end of file diff --git a/Editor/0ReflectionMetadata/System.Numerics.Vectors.dll b/Editor/0ReflectionMetadata/System.Numerics.Vectors.dll new file mode 100644 index 0000000..a808165 Binary files /dev/null and b/Editor/0ReflectionMetadata/System.Numerics.Vectors.dll differ diff --git a/Editor/0ReflectionMetadata/System.Numerics.Vectors.dll.meta b/Editor/0ReflectionMetadata/System.Numerics.Vectors.dll.meta new file mode 100644 index 0000000..9626c74 --- /dev/null +++ b/Editor/0ReflectionMetadata/System.Numerics.Vectors.dll.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 80da7bb84a7c46c58aeb4c6f5a759da0 +timeCreated: 1738995205 \ No newline at end of file diff --git a/Editor/0ReflectionMetadata/System.Reflection.Metadata.dll b/Editor/0ReflectionMetadata/System.Reflection.Metadata.dll new file mode 100644 index 0000000..b7e6a89 Binary files /dev/null and b/Editor/0ReflectionMetadata/System.Reflection.Metadata.dll differ diff --git a/Editor/0ReflectionMetadata/System.Reflection.Metadata.dll.meta b/Editor/0ReflectionMetadata/System.Reflection.Metadata.dll.meta new file mode 100644 index 0000000..671d271 --- /dev/null +++ b/Editor/0ReflectionMetadata/System.Reflection.Metadata.dll.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9977817ffec64db294e3d3b7462b30e8 +timeCreated: 1738995205 \ No newline at end of file diff --git a/Editor/0ReflectionMetadata/System.Runtime.CompilerServices.Unsafe.dll b/Editor/0ReflectionMetadata/System.Runtime.CompilerServices.Unsafe.dll new file mode 100644 index 0000000..491a80a Binary files /dev/null and b/Editor/0ReflectionMetadata/System.Runtime.CompilerServices.Unsafe.dll differ diff --git a/Editor/0ReflectionMetadata/System.Runtime.CompilerServices.Unsafe.dll.meta b/Editor/0ReflectionMetadata/System.Runtime.CompilerServices.Unsafe.dll.meta new file mode 100644 index 0000000..37d3d77 --- /dev/null +++ b/Editor/0ReflectionMetadata/System.Runtime.CompilerServices.Unsafe.dll.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3fdc17ab0d7b4ede9e4c2c69f75dcdd0 +timeCreated: 1738995205 \ No newline at end of file diff --git a/Editor/Package.meta b/Editor/Package.meta new file mode 100644 index 0000000..20a8720 --- /dev/null +++ b/Editor/Package.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0836cbbc0af740aa8ff7def34f766bb4 +timeCreated: 1730750481 \ No newline at end of file diff --git a/Editor/Package/Asset.meta b/Editor/Package/Asset.meta new file mode 100644 index 0000000..e093d04 --- /dev/null +++ b/Editor/Package/Asset.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: daeeb1234063493baf68eea4b2e718ef +timeCreated: 1739006385 \ No newline at end of file diff --git a/Editor/Package/Asset/Inspector.meta b/Editor/Package/Asset/Inspector.meta new file mode 100644 index 0000000..bc3c9df --- /dev/null +++ b/Editor/Package/Asset/Inspector.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0740328faa0a455db6708e3106acb763 +timeCreated: 1739006424 \ No newline at end of file diff --git a/Editor/Package/Asset/Inspector/MainTreeAssetInspector.cs b/Editor/Package/Asset/Inspector/MainTreeAssetInspector.cs new file mode 100644 index 0000000..1878ed1 --- /dev/null +++ b/Editor/Package/Asset/Inspector/MainTreeAssetInspector.cs @@ -0,0 +1,32 @@ +using System.IO; +using ResoniteImportHelper.Editor.Package.Asset.Types; +using UnityEngine; +using UnityEngine.UIElements; +using CustomInspector = UnityEditor.CustomEditor; +using UserMadeInspector = UnityEditor.Editor; + +namespace ResoniteImportHelper.Package.Asset.Inspector +{ + [CustomInspector(typeof(MainTreeAsset))] + public class MainTreeAssetInspector: UserMadeInspector + { + public override VisualElement CreateInspectorGUI() + { + var root = new VisualElement(); + + root.Add(new Label("Content")); + var b = new Button(() => + { + var x = Path.GetTempFileName(); + File.WriteAllText(x, (this.target as MainTreeAsset)!.text); + Debug.Log($"Wrote full text temporary file located in {x}."); + }); + b.Add(new Label("Write whole text to temporary file")); + root.Add(b); + + root.Add(new TextField() { multiline = true, value = (this.target as MainTreeAsset)!.text.Substring(0, 5000) }); + + return root; + } + } +} diff --git a/Editor/Package/Asset/Inspector/MainTreeAssetInspector.cs.meta b/Editor/Package/Asset/Inspector/MainTreeAssetInspector.cs.meta new file mode 100644 index 0000000..bc0b9ca --- /dev/null +++ b/Editor/Package/Asset/Inspector/MainTreeAssetInspector.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0f5b9bda1bd44025bb5dbb6dd3dcac94 +timeCreated: 1739006610 \ No newline at end of file diff --git a/Editor/Package/Asset/Inspector/ResoniteImportHelper.Package.Asset.Inspector.asmdef b/Editor/Package/Asset/Inspector/ResoniteImportHelper.Package.Asset.Inspector.asmdef new file mode 100644 index 0000000..3d29ad1 --- /dev/null +++ b/Editor/Package/Asset/Inspector/ResoniteImportHelper.Package.Asset.Inspector.asmdef @@ -0,0 +1,7 @@ +{ + "name": "ResoniteImportHelper.Package.Asset.Inspector", + "rootNamespace": "global::ResoniteImportHelper.Package.Asset.Inspector", + "autoReferenced": false, + "includePlatforms": ["Editor"], + "references":[ "ResoniteImportHelper.Package.Asset.Types" ] +} diff --git a/Editor/Package/Asset/Inspector/ResoniteImportHelper.Package.Asset.Inspector.asmdef.meta b/Editor/Package/Asset/Inspector/ResoniteImportHelper.Package.Asset.Inspector.asmdef.meta new file mode 100644 index 0000000..4219f12 --- /dev/null +++ b/Editor/Package/Asset/Inspector/ResoniteImportHelper.Package.Asset.Inspector.asmdef.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d363f0985d5e4a30b79223dcb9e379a1 +timeCreated: 1739006442 \ No newline at end of file diff --git a/Editor/Package/Asset/Types.meta b/Editor/Package/Asset/Types.meta new file mode 100644 index 0000000..46351ce --- /dev/null +++ b/Editor/Package/Asset/Types.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3c81fcb497654d5d9c520551672074c3 +timeCreated: 1739006417 \ No newline at end of file diff --git a/Editor/Package/Asset/Types/MainTreeAsset.cs b/Editor/Package/Asset/Types/MainTreeAsset.cs new file mode 100644 index 0000000..7bea5ae --- /dev/null +++ b/Editor/Package/Asset/Types/MainTreeAsset.cs @@ -0,0 +1,14 @@ + +using UnityEngine; + +namespace ResoniteImportHelper.Editor.Package.Asset.Types +{ + public class MainTreeAsset: ScriptableObject + { + public string text; + public MainTreeAsset(string text) + { + this.text = text; + } + } +} diff --git a/Editor/Package/Asset/Types/MainTreeAsset.cs.meta b/Editor/Package/Asset/Types/MainTreeAsset.cs.meta new file mode 100644 index 0000000..20b895a --- /dev/null +++ b/Editor/Package/Asset/Types/MainTreeAsset.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 232cfa3213d848ab91a73f26a4cea290 +timeCreated: 1739006568 \ No newline at end of file diff --git a/Editor/Package/Asset/Types/ResoniteImportHelper.Package.Asset.Types.asmdef b/Editor/Package/Asset/Types/ResoniteImportHelper.Package.Asset.Types.asmdef new file mode 100644 index 0000000..1186eeb --- /dev/null +++ b/Editor/Package/Asset/Types/ResoniteImportHelper.Package.Asset.Types.asmdef @@ -0,0 +1,5 @@ +{ + "name": "ResoniteImportHelper.Package.Asset.Types", + "rootNamespace": "global::ResoniteImportHelper.Package.Asset.Types", + "autoReferenced": false +} diff --git a/Editor/Package/Asset/Types/ResoniteImportHelper.Package.Asset.Types.asmdef.meta b/Editor/Package/Asset/Types/ResoniteImportHelper.Package.Asset.Types.asmdef.meta new file mode 100644 index 0000000..c26f5ee --- /dev/null +++ b/Editor/Package/Asset/Types/ResoniteImportHelper.Package.Asset.Types.asmdef.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 687c0e6bcb684fae8b0dad77543471d8 +timeCreated: 1739006485 \ No newline at end of file diff --git a/Editor/Package/Import.meta b/Editor/Package/Import.meta new file mode 100644 index 0000000..a4a52f2 --- /dev/null +++ b/Editor/Package/Import.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 42f0889f107f4cabbf716e931570a98b +timeCreated: 1730750504 \ No newline at end of file diff --git a/Editor/Package/Import/Deserialize.meta b/Editor/Package/Import/Deserialize.meta new file mode 100644 index 0000000..2582e24 --- /dev/null +++ b/Editor/Package/Import/Deserialize.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a703848c3a0e463cbb5943f416ec6e5b +timeCreated: 1730802987 \ No newline at end of file diff --git a/Editor/Package/Import/Deserialize/Bitmap.meta b/Editor/Package/Import/Deserialize/Bitmap.meta new file mode 100644 index 0000000..0de22f6 --- /dev/null +++ b/Editor/Package/Import/Deserialize/Bitmap.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 225e25a473ad486fba210bde154695f0 +timeCreated: 1730803243 \ No newline at end of file diff --git a/Editor/Package/Import/Deserialize/Bitmap/types.cs b/Editor/Package/Import/Deserialize/Bitmap/types.cs new file mode 100644 index 0000000..a6e5a8b --- /dev/null +++ b/Editor/Package/Import/Deserialize/Bitmap/types.cs @@ -0,0 +1,44 @@ +using System; +using Newtonsoft.Json; +using ResoniteImportHelper.Package.Import.Deserialize.Metadata; + +namespace ResoniteImportHelper.Package.Import.Deserialize.Bitmap +{ + internal sealed class BitmapTag: IAssetTag {} + + [Serializable] + public sealed class BitmapMetadata : IMetadata + { + [JsonProperty("width")] + public uint Width; + + [JsonProperty("height")] + public uint Height; + + [JsonProperty("mipMapCount")] + public uint MipMapCount; + + /// + /// Example. `png` + /// + [JsonProperty("baseFormat")] + public string Format; + + [JsonProperty("bitsPerPixel")] + public uint BitsPerPixel; + + [JsonProperty("channelCount")] + public uint ChannelCount; + + [JsonIgnore] + public uint BitsPerChannel => BitsPerPixel / ChannelCount; + + /// + /// Example. `FullyOpaque` + /// + /// Example. `Alpha` + /// + [JsonProperty("alphaData")] + public string AlphaTreat; + } +} diff --git a/Editor/Package/Import/Deserialize/Bitmap/types.cs.meta b/Editor/Package/Import/Deserialize/Bitmap/types.cs.meta new file mode 100644 index 0000000..22b3271 --- /dev/null +++ b/Editor/Package/Import/Deserialize/Bitmap/types.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: db1c706255334e0a86586c69cb63c27f +timeCreated: 1730803248 \ No newline at end of file diff --git a/Editor/Package/Import/Deserialize/IAssetTag.cs b/Editor/Package/Import/Deserialize/IAssetTag.cs new file mode 100644 index 0000000..89d4a52 --- /dev/null +++ b/Editor/Package/Import/Deserialize/IAssetTag.cs @@ -0,0 +1,7 @@ +namespace ResoniteImportHelper.Package.Import.Deserialize.Metadata +{ + internal interface IAssetTag + { + + } +} diff --git a/Editor/Package/Import/Deserialize/IAssetTag.cs.meta b/Editor/Package/Import/Deserialize/IAssetTag.cs.meta new file mode 100644 index 0000000..25009cd --- /dev/null +++ b/Editor/Package/Import/Deserialize/IAssetTag.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6951b18f0a3e426abb37191687968cae +timeCreated: 1730803938 \ No newline at end of file diff --git a/Editor/Package/Import/Deserialize/IMetadata.cs b/Editor/Package/Import/Deserialize/IMetadata.cs new file mode 100644 index 0000000..a2e04c5 --- /dev/null +++ b/Editor/Package/Import/Deserialize/IMetadata.cs @@ -0,0 +1,4 @@ +namespace ResoniteImportHelper.Package.Import.Deserialize.Metadata +{ + internal interface IMetadata where TAssetTag : IAssetTag {} +} diff --git a/Editor/Package/Import/Deserialize/IMetadata.cs.meta b/Editor/Package/Import/Deserialize/IMetadata.cs.meta new file mode 100644 index 0000000..fa89ffd --- /dev/null +++ b/Editor/Package/Import/Deserialize/IMetadata.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 59cfc29569a842c6813662999782e96d +timeCreated: 1730803867 \ No newline at end of file diff --git a/Editor/Package/Import/Deserialize/Mesh.meta b/Editor/Package/Import/Deserialize/Mesh.meta new file mode 100644 index 0000000..03e8985 --- /dev/null +++ b/Editor/Package/Import/Deserialize/Mesh.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fd1f4642f74a4dc499e38051d82470f7 +timeCreated: 1730803089 \ No newline at end of file diff --git a/Editor/Package/Import/Deserialize/Mesh/types.cs b/Editor/Package/Import/Deserialize/Mesh/types.cs new file mode 100644 index 0000000..2c2412f --- /dev/null +++ b/Editor/Package/Import/Deserialize/Mesh/types.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using Newtonsoft.Json; +using ResoniteImportHelper.Package.Import.Deserialize.Metadata; +using ResoniteImportHelper.Package.Import.Deserialize.Shape; + +namespace ResoniteImportHelper.Package.Import.Deserialize.Mesh +{ + internal sealed class MeshTag : IAssetTag {} + + [Serializable] + internal sealed class MeshMetadata : IMetadata + { + [JsonProperty("bones")] + internal MeshBound Bounds; + + [JsonProperty("submeshMetadata")] + internal List SubMeshes; + + [JsonProperty("boneMetadata")] + internal List Bones; + + [JsonProperty("approximateBoneBounds")] + internal List ApproximateBoneBound; + } + + [Serializable] + internal sealed class MeshBound + { + [JsonProperty("min")] + internal Point3Mut Min; + + [JsonProperty("max")] + internal Point3Mut Max; + + public override string ToString() + { + return $"({ShortenInfinityP(Min)})..({ShortenInfinityP(Max)})"; + + string ShortenInfinityP(Point3Mut p) => + $"{ShortenInfinity(p.X)}, {ShortenInfinity(p.Y)}, {ShortenInfinity(p.Z)}"; + + string ShortenInfinity(float v) + { + if (float.IsPositiveInfinity(v)) + { + return "+inf"; + } + + // ReSharper disable once ConvertIfStatementToReturnStatement + if (float.IsNegativeInfinity(v)) + { + return "-inf"; + } + + return v.ToString(CultureInfo.InvariantCulture); + } + } + } + + [Serializable] + internal sealed class SubMeshMetadata + { + [JsonProperty("elementCount")] + internal int Polys; + + [JsonProperty("bounds")] + internal MeshBound Bounds = null!; + } + + [Serializable] + internal sealed class BoneMetadata + { + [JsonProperty("weight0count")] + internal int Weight0Count; + + [JsonProperty("weight1count")] + internal int Weight1Count; + + [JsonProperty("weight2count")] + internal int Weight2Count; + + [JsonProperty("weight3count")] + internal int Weight3Count; + + [JsonProperty("bounds")] + internal MeshBound Bounds = null!; + } + + [Serializable] + internal sealed class ApproximateBoneBound + { + [JsonProperty("rootBoneIndex")] + internal int RootBoneIndex; + + [JsonProperty("bounds")] + internal Sphere3dMut Bounds; + } +} diff --git a/Editor/Package/Import/Deserialize/Mesh/types.cs.meta b/Editor/Package/Import/Deserialize/Mesh/types.cs.meta new file mode 100644 index 0000000..15c5ffa --- /dev/null +++ b/Editor/Package/Import/Deserialize/Mesh/types.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dcd7b22fd3484e1a924f51591793176b +timeCreated: 1730803095 \ No newline at end of file diff --git a/Editor/Package/Import/Deserialize/ResoniteImportHelper.Package.Deserialize.asmdef b/Editor/Package/Import/Deserialize/ResoniteImportHelper.Package.Deserialize.asmdef new file mode 100644 index 0000000..23859dc --- /dev/null +++ b/Editor/Package/Import/Deserialize/ResoniteImportHelper.Package.Deserialize.asmdef @@ -0,0 +1,6 @@ +{ + "name": "ResoniteImportHelper.Package.Deserialize", + "rootNamespace": "global::ResoniteImportHelper.Package.Deserialize", + "includePlatforms": ["Editor"] + +} diff --git a/Editor/Package/Import/Deserialize/ResoniteImportHelper.Package.Deserialize.asmdef.meta b/Editor/Package/Import/Deserialize/ResoniteImportHelper.Package.Deserialize.asmdef.meta new file mode 100644 index 0000000..a462e90 --- /dev/null +++ b/Editor/Package/Import/Deserialize/ResoniteImportHelper.Package.Deserialize.asmdef.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b5993fcb75a54a2babaf459e17e7fc75 +timeCreated: 1739007878 \ No newline at end of file diff --git a/Editor/Package/Import/Deserialize/Shape.meta b/Editor/Package/Import/Deserialize/Shape.meta new file mode 100644 index 0000000..3d932b3 --- /dev/null +++ b/Editor/Package/Import/Deserialize/Shape.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 56b21e5e2ab848b7a2f5cb76dc40f5ee +timeCreated: 1730803006 \ No newline at end of file diff --git a/Editor/Package/Import/Deserialize/Shape/Point3Mut.cs b/Editor/Package/Import/Deserialize/Shape/Point3Mut.cs new file mode 100644 index 0000000..f08161b --- /dev/null +++ b/Editor/Package/Import/Deserialize/Shape/Point3Mut.cs @@ -0,0 +1,18 @@ +using System; +using Newtonsoft.Json; + +namespace ResoniteImportHelper.Package.Import.Deserialize.Shape +{ + [Serializable] + internal struct Point3Mut + { + [JsonProperty("x")] + internal float X; + + [JsonProperty("y")] + internal float Y; + + [JsonProperty("z")] + internal float Z; + } +} diff --git a/Editor/Package/Import/Deserialize/Shape/Point3Mut.cs.meta b/Editor/Package/Import/Deserialize/Shape/Point3Mut.cs.meta new file mode 100644 index 0000000..3934502 --- /dev/null +++ b/Editor/Package/Import/Deserialize/Shape/Point3Mut.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 517e538ab4af4924a5bc87cc4215bba2 +timeCreated: 1730803043 \ No newline at end of file diff --git a/Editor/Package/Import/Deserialize/Shape/Sphere3dMut.cs b/Editor/Package/Import/Deserialize/Shape/Sphere3dMut.cs new file mode 100644 index 0000000..befec44 --- /dev/null +++ b/Editor/Package/Import/Deserialize/Shape/Sphere3dMut.cs @@ -0,0 +1,15 @@ +using System; +using Newtonsoft.Json; + +namespace ResoniteImportHelper.Package.Import.Deserialize.Shape +{ + [Serializable] + internal struct Sphere3dMut + { + [JsonProperty("center")] + internal Point3Mut Center; + + [JsonProperty("radius")] + internal float Radius; + } +} diff --git a/Editor/Package/Import/Deserialize/Shape/Sphere3dMut.cs.meta b/Editor/Package/Import/Deserialize/Shape/Sphere3dMut.cs.meta new file mode 100644 index 0000000..02660f9 --- /dev/null +++ b/Editor/Package/Import/Deserialize/Shape/Sphere3dMut.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 24a1cd75a8f0451480f0ca1c4b833b35 +timeCreated: 1730803164 \ No newline at end of file diff --git a/Editor/Package/Import/Deserialize/Support.meta b/Editor/Package/Import/Deserialize/Support.meta new file mode 100644 index 0000000..8d8da6e --- /dev/null +++ b/Editor/Package/Import/Deserialize/Support.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 560a553de9c54b6b859204e350039619 +timeCreated: 1738998104 \ No newline at end of file diff --git a/Editor/Package/Import/Deserialize/Support/IIdentifiable.cs b/Editor/Package/Import/Deserialize/Support/IIdentifiable.cs new file mode 100644 index 0000000..6906718 --- /dev/null +++ b/Editor/Package/Import/Deserialize/Support/IIdentifiable.cs @@ -0,0 +1,7 @@ +namespace ResoniteImportHelper.Package.Import.Deserialize.Support +{ + public interface IIdentifiable + { + public string GetIdentifier(); + } +} diff --git a/Editor/Package/Import/Deserialize/Support/IIdentifiable.cs.meta b/Editor/Package/Import/Deserialize/Support/IIdentifiable.cs.meta new file mode 100644 index 0000000..de79394 --- /dev/null +++ b/Editor/Package/Import/Deserialize/Support/IIdentifiable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c799e747e83a4a6c93302992171d6579 +timeCreated: 1739029369 \ No newline at end of file diff --git a/Editor/Package/Import/Deserialize/Support/IdentifiableDataCell.cs b/Editor/Package/Import/Deserialize/Support/IdentifiableDataCell.cs new file mode 100644 index 0000000..7f2de86 --- /dev/null +++ b/Editor/Package/Import/Deserialize/Support/IdentifiableDataCell.cs @@ -0,0 +1,8 @@ +namespace ResoniteImportHelper.Package.Import.Deserialize.Support +{ + public struct IdentifiableDataCell + { + public string ID; + public T Data; + } +} diff --git a/Editor/Package/Import/Deserialize/Support/IdentifiableDataCell.cs.meta b/Editor/Package/Import/Deserialize/Support/IdentifiableDataCell.cs.meta new file mode 100644 index 0000000..f649763 --- /dev/null +++ b/Editor/Package/Import/Deserialize/Support/IdentifiableDataCell.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d73700f970084c6496fc524ae8def4c6 +timeCreated: 1739007753 \ No newline at end of file diff --git a/Editor/Package/Import/Deserialize/Support/UniquePointer.cs b/Editor/Package/Import/Deserialize/Support/UniquePointer.cs new file mode 100644 index 0000000..35698a6 --- /dev/null +++ b/Editor/Package/Import/Deserialize/Support/UniquePointer.cs @@ -0,0 +1,16 @@ +using JetBrains.Annotations; +using Newtonsoft.Json; + +namespace ResoniteImportHelper.Package.Import.Deserialize.Support +{ + public struct UniquePointer : IIdentifiable + { + [UsedImplicitly] + public string ID; + + [JsonProperty("Data")] + public string ReferenceeID; + + public string GetIdentifier() => ID; + } +} diff --git a/Editor/Package/Import/Deserialize/Support/UniquePointer.cs.meta b/Editor/Package/Import/Deserialize/Support/UniquePointer.cs.meta new file mode 100644 index 0000000..2c28483 --- /dev/null +++ b/Editor/Package/Import/Deserialize/Support/UniquePointer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ae2088a4e2a24050877c054ee346a79a +timeCreated: 1739005657 \ No newline at end of file diff --git a/Editor/Package/Import/Importer.cs b/Editor/Package/Import/Importer.cs new file mode 100644 index 0000000..daaa8de --- /dev/null +++ b/Editor/Package/Import/Importer.cs @@ -0,0 +1,356 @@ +#nullable enable +// TODO: そのうち Newtonsoft.Json も JsonUtility も使わない、新しいJSONパーサーを作る。 +// 設計思想: +// 1. 型システムとアナライザーによってスキーマが論理的に間違っているときは絶対にコンパイルエラーになる。 +// 2. エンドユーザーが意識するのはジェネリックな関数である serializer.Serialize(T): CharSequence と +// deserializer.Deserialize(CharSequence): T のみ。 +// 3. untyped なパースを行う際は dynamic 型を用い、 JSONの抽象構文木を意識させるAPI設計はしない。 +// 4. 相互運用性を最大限高めるため、入出力において UTF-8 と UTF-16 のどちらも対応する。 +// 5. 属性を用いたソースジェネレーターによってボイラープレートを最小限にしながらパフォーマンスの良いコードを生成する。 +// 6. メモリアロケーションを極力行わない。 +// 7. Unity でも動作する C# のサブセットで記述する。 + +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Bson; +using ResoniteImportHelper.Editor.Package.Asset.Types; +using ResoniteImportHelper.Editor.Package.Import.Stub; +using ResoniteImportHelper.Package.Import.Deserialize.Bitmap; +using ResoniteImportHelper.Package.Import.Metadata; +using ResoniteImportHelper.Package.Import.Stub; +using UnityEditor.AssetImporters; +using UnityEngine; + +namespace ResoniteImportHelper.Package.Import +{ + [ScriptedImporter(1, "resonitepackage")] + internal class Importer : ScriptedImporter + { + private delegate void OnInitializeGameObject(GameObject target, string deserializedSlotId); + + public override void OnImportAsset(AssetImportContext ctx) + { + // TODO: adjust this later + var rootGameObject = new GameObject(); + ctx.AddObjectToAsset("$$main", rootGameObject); + ctx.SetMainObject(rootGameObject); + + using var zipArchive = ZipFile.OpenRead(ctx.assetPath); + + var mainRecordEntry = zipArchive.GetEntry("R-Main.record"); + if (mainRecordEntry == null) + { + throw new FormatException("ResonitePackage must contain R-Main.record under the archive root."); + } + + { + // TODO: validate only -> UTF-8 only JSON parser to faster parse? + var recordManifest = ReadZipArchiveContentAsUtf8Sequence(mainRecordEntry); + Debug.Log($"root decoded: {recordManifest}"); + + var pd = JsonConvert.DeserializeObject(recordManifest); + if (pd is null) + { + throw new FormatException("Failed to deserialize descriptor"); + } + + var manifests = pd.AssetManifest; + + var metadataArchiveEntries = zipArchive.Entries.Where(a => a.FullName.StartsWith("Metadata/")).ToList(); + var mainArchiveEntries = zipArchive.Entries.Where(a => a.FullName.StartsWith("Assets/")).ToList(); + + var mainDataTreeEntry = + mainArchiveEntries.SingleOrDefault(e => e.Name == pd.AssetUri.Replace("packdb:///", "")); + + if (mainDataTreeEntry is null) + { + throw new FormatException($"Failed to find main DataTreeDirectory: {pd.AssetUri} is not contained by the package."); + } + + var mainDataTree = ReadZipArchiveContent(mainDataTreeEntry); + if (!IsValidDataTreeHeader(mainDataTree[0..4])) + { + ctx.LogImportError("The main data tree do not have correct header magic."); + throw new System.FormatException("The main data tree do not have correct header magic."); + } + + Debug.Log("mode"); + // TODO: 本来ここはVarIntで読むのが正しいが、一旦無視 + var compressionMode = mainDataTree[8]; + Stream? stream; + if (compressionMode == 3) + { + Debug.Log("Main record is compressed by Brotli."); + stream = new BrotliStream(new MemoryStream(mainDataTree[9..]), + CompressionMode.Decompress); + } else if (compressionMode == 0) + { + Debug.Log("Main record is not compressed."); + stream = new MemoryStream(mainDataTree[9..]); + } + else + { + ctx.LogImportWarning($"Main record is compressed by unrecognized format. (kind: {compressionMode}"); + stream = null; + } + + if (stream != null) + { + using (stream) + { +#pragma warning disable CS0618 // Type or member is obsolete + using var bsonTree1 = new BsonReader(stream); +#pragma warning restore CS0618 // Type or member is obsolete + var s = new JsonSerializer(); + var rawRoot = s.Deserialize(bsonTree1); + var toAdd = ScriptableObject.CreateInstance(); + toAdd.name = "DecodedMainRecord"; + toAdd.text = rawRoot?.ToString() ?? "null"; + ctx.AddObjectToAsset("DecodedMainRecord", toAdd); + } + + Stream stream2; + if (compressionMode == 3) + { + Debug.Log("Main record is compressed by Brotli."); + stream2 = new BrotliStream(new MemoryStream(mainDataTree[9..]), + CompressionMode.Decompress); + } else if (compressionMode == 0) + { + Debug.Log("Main record is not compressed."); + stream2 = new MemoryStream(mainDataTree[9..]); + } + else + { + throw new NotImplementedException("unreachable"); + } + + using (stream2) + { + var graphRoot = DeserializeFromBsonStream(stream2); + if (graphRoot == null) + { + ctx.LogImportWarning("root deserialize failed"); + } + else + { + ConstructBaseHierarchy(graphRoot, rootGameObject, (go, id) => + { + Debug.Log($"{go} was initialized! ID: {id}"); + ctx.AddObjectToAsset($"GameObject_{id}", go); + }); + ImportTexture(ctx, graphRoot, rootGameObject, mainArchiveEntries); + ImportMesh(ctx, graphRoot, rootGameObject); + ImportMaterial(ctx, graphRoot, rootGameObject); + ConstructRenderers(ctx, graphRoot, rootGameObject); + var animator = ConstructAnimator(ctx, graphRoot, rootGameObject); + ConstructAnimation(ctx, graphRoot, rootGameObject, animator); + } + } + } + } + } + + private static TValue? DeserializeFromBsonStream(Stream stream) + { + var s = new JsonSerializer(); +#pragma warning disable CS0618 // Type or member is obsolete + using var bsonTree = new BsonReader(stream); +#pragma warning restore CS0618 // Type or member is obsolete + + var value = s.Deserialize(bsonTree); + + return value; + } + + private static void ConstructBaseHierarchy(GraphRoot root, GameObject rootGo, OnInitializeGameObject onInitializeGameObject) + { + Debug.Log($"Software build: {root.VersionNumber}"); + Debug.Log($"Feature Flags: {Prettify(root.FeatureFlags)}"); + Debug.Log($"Types: {Prettify(root.Types)}"); + Debug.Log($"TypeVersions: {Prettify(root.TypeVersions)}"); + Debug.Log($"Object: {root.RootSlot}"); + Debug.Log($"Assets: {Prettify(root.ContainedAssets)}"); + + AttachSlotRecursively(new Dictionary(), root.RootSlot, rootGo, onInitializeGameObject, true); + } + + private static void AttachSlotRecursively(Dictionary versions, Slot slot, GameObject go, OnInitializeGameObject onInitializeGameObject, bool isRoot) + { + var slotName = slot.Name.Data; + if (versions.TryGetValue(slotName, out var previousVersion)) + { + var currentVersion = previousVersion + 1; + Debug.Log($"Duplicated name: {slotName} on ID={slot.ID}; version: {previousVersion} -> {currentVersion}"); + versions[slotName] = currentVersion; + slotName += $".{currentVersion}"; + } + else + { + versions[slotName] = 0; + } + // go.nameはヒエラルキー上で一意である必要があるらしい。知るか! + go.name = slotName; + go.transform.SetLocalPositionAndRotation(slot.Position, slot.Rotation); + go.transform.localScale = slot.Scale; + if (!isRoot) + { + onInitializeGameObject(go, slot.ID); + } + + foreach (var child in slot.Children) + { + var childGo = new GameObject(); + childGo.transform.SetParent(go.transform); + AttachSlotRecursively(versions, child, childGo, onInitializeGameObject, false); + } + } + + private static void ImportTexture(AssetImportContext ctx, GraphRoot root, GameObject rootGo, IMetadataAccessor metadataAccessor, IAssetVariantAccessor assetVariantAccessor) + { + var staticTexture2DProviderCandidate = root.Types + .Select((e, i) => (e.Parse(), i)) + .Cast<((AssemblyName Assembly, TypeName typeName) parsed, int i)?>() + .FirstOrDefault(t => t!.Value.parsed.Assembly.FullName == "FrooxEngine" && t!.Value.parsed.typeName.FullName == "FrooxEngine.StaticTexture2D"); + + if (!staticTexture2DProviderCandidate.HasValue) + { + ctx.LogImportWarning("This target do not have StaticTexture2D, skipping."); + return; + } + + var staticTexture2DProviderIndex = staticTexture2DProviderCandidate.Value.i; + + root.ContainedAssets + .Where(a => a.ComponentTableIndex == staticTexture2DProviderIndex) + .Select(a => a.AsTyped()) + .Select(st => new + { + st.GetComponentData().IsNormalMap, + AssetIdentifier = st.GetComponentData().GetAssetIdentifier(), + WrapModeU = st.GetComponentData().GetWrapModeU(), + WrapModeV = st.GetComponentData().GetWrapModeV(), + Metadata = metadataAccessor.GetAndDeserialize( + $"{st.GetComponentData().GetAssetIdentifier()}.bitmap"), + TextureFormat = assetVariantAccessor.GetByKey(st.GetComponentData().GetAssetIdentifier()).First((_) => true).FileName.Split('&').Select(w => w.Split('=', 2)).First(a => a[0] == "compression")[1] + }) + .Select(data => + { + new Texture2D((int)data.Metadata.Width, (int)data.Metadata.Height, TextureFormat, 0, true, true); + }); + // TODO: 今後Slotにあるコンポーネントを死ぬほど反復するが、それはO(nm)になって遅くないか? + // 何らかのデータ構造の導入を検討するべき。 + + /* + TODO: + Get variants and load largest one. + -- https://docs.unity3d.com/2022.3/Documentation/ScriptReference/Texture2D.LoadRawTextureData.html + How can we handle mips=True case? + * BC3 (w/ C.C.) => DXT5_Crunched + * BC3 (w/o C.C.) => DXT5 + * BC1 (w/ C.C.) => DXT1_Crunched + * BC1 (w/o C.C.) => DXT1 + * RawRGBA => RGBA32 + https://www.webtech.co.jp/blog/optpix_labs/format/6993/ + If there's no one, fallback to PNG and upload it with initial properties. + Considering DXT7 on fallback-ed import? + */ + + ctx.LogImportWarning("This phase (ImportTexture) is not fully-implemented yet."); + } + + private static void ImportMesh(AssetImportContext ctx, GraphRoot root, GameObject rootGo) + { + // TODO: Read Metadata/*.mesh (represented as JSON). + ctx.LogImportWarning("This phase (ImportMesh) is not implemented yet."); + } + + private static void ImportMaterial(AssetImportContext ctx, GraphRoot root, GameObject rootGo) + { + // TODO: Branch to control whether import as lilToon (or any toon shader that supports Matcap) or Standard Material (to emulate PBS). + ctx.LogImportWarning("This phase (ImportMaterial) is not implemented yet."); + } + + private static void ConstructRenderers(AssetImportContext ctx, GraphRoot root, GameObject rootGo) + { + // TODO: Read both MeshRenderer and SkinnedMeshRenderer. + ctx.LogImportWarning("This phase (ConstructRenderers) is not implemented yet."); + } + + private static Animator ConstructAnimator(AssetImportContext ctx, GraphRoot root, GameObject rootGo) + { + var animator = rootGo.AddComponent(); + // TODO: read bones from VRIK component. + ctx.LogImportWarning("This phase (ConstructAnimator) is not fully-implemented yet."); + return animator; + } + + private static void ConstructAnimation(AssetImportContext ctx, GraphRoot root, GameObject rootGo, Animator animator) + { + ctx.LogImportWarning("This phase(ConstructAnimation) is not implemented yet."); + } + + private static string Prettify(IEnumerable> dictionary) + { + return string.Join(",\n", dictionary.Select((a) => $"{a.Key}: {(a.Value)}")); + } + + private static string Prettify(IEnumerable enumerable) + { + return string.Join(",\n", enumerable); + } + + private static bool IsValidDataTreeHeader(ReadOnlySpan header) + { + return header.Length == 4 && header[0] == 'F' && header[1] == 'r' && header[2] == 'D' && header[3] == 'T'; + } + + private static byte[] ReadZipArchiveContent(ZipArchiveEntry entry) + { + var content = new byte[entry.Length]; + { + using var meta = entry.Open(); + using var s = new BufferedStream(meta); + + s.Read(content); + } + + return content; + } + + private static string ReadZipArchiveContentAsUtf8Sequence(ZipArchiveEntry entry) + { + return Encoding.UTF8.GetString(ReadZipArchiveContent(entry)); + } + + internal sealed class FormatException : Exception + { + internal FormatException(string message) : base(message) {} + } + + [Serializable] + internal sealed class PartialDescriptor + { + [JsonProperty("assetUri")] + internal string AssetUri; + [JsonProperty("assetManifest")] + internal List AssetManifest; + } + + [Serializable] + internal sealed class AssetStatistic + { + [JsonProperty("hash")] + internal string Hash; + [JsonProperty("bytes")] + internal int Length; + } + } +} diff --git a/Editor/Package/Import/Importer.cs.meta b/Editor/Package/Import/Importer.cs.meta new file mode 100644 index 0000000..2c968b1 --- /dev/null +++ b/Editor/Package/Import/Importer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 152ee6b4f0cb4f0db0df22317f9d5843 +timeCreated: 1730750560 \ No newline at end of file diff --git a/Editor/Package/Import/Metadata.meta b/Editor/Package/Import/Metadata.meta new file mode 100644 index 0000000..b5af802 --- /dev/null +++ b/Editor/Package/Import/Metadata.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9edd1b9487cb4f55b68695748810034b +timeCreated: 1730803858 \ No newline at end of file diff --git a/Editor/Package/Import/Metadata/GraphRoot.cs b/Editor/Package/Import/Metadata/GraphRoot.cs new file mode 100644 index 0000000..abb7105 --- /dev/null +++ b/Editor/Package/Import/Metadata/GraphRoot.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using ResoniteImportHelper.Package.Import.Stub; + +namespace ResoniteImportHelper.Package.Import.Metadata +{ + [Serializable] + public sealed class GraphRoot + { + // ReSharper disable once InconsistentNaming + public string VersionNumber; + public Dictionary FeatureFlags; + [JsonProperty("Types")] + private string[] _types; + + [JsonIgnore] + private TypeRef[] _typesCache; + + [JsonIgnore] + public TypeRef[] Types => (_typesCache ??= _types.Select(type => new TypeRef(type)).ToArray()); + + [JsonProperty("TypeVersions")] + private Dictionary _typeVersions; + + [JsonIgnore] + private Dictionary _typeVersionsCache; + + [JsonIgnore] + public Dictionary TypeVersions => + _typeVersionsCache ??= + _typeVersions + .Select(entry => KeyValuePair.Create(new TypeRef(entry.Key), entry.Value)) + .ToDictionary(entry => entry.Key, entry => entry.Value); + + [JsonProperty("Object")] + public Slot RootSlot; + + [JsonProperty("Assets")] + public UntypedComponentReference[] ContainedAssets; + } +} diff --git a/Editor/Package/Import/Metadata/GraphRoot.cs.meta b/Editor/Package/Import/Metadata/GraphRoot.cs.meta new file mode 100644 index 0000000..b988523 --- /dev/null +++ b/Editor/Package/Import/Metadata/GraphRoot.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 61314c334dc54b07b685720de7f2987a +timeCreated: 1738982426 \ No newline at end of file diff --git a/Editor/Package/Import/Metadata/IAssetVariantAccessor.cs b/Editor/Package/Import/Metadata/IAssetVariantAccessor.cs new file mode 100644 index 0000000..f694990 --- /dev/null +++ b/Editor/Package/Import/Metadata/IAssetVariantAccessor.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; + +namespace ResoniteImportHelper.Package.Import.Metadata +{ + public interface IAssetVariantAccessor + { + public IEnumerable GetByKey(string Key); + } + + public sealed class LazyLoadEntry + { + public readonly string FileName; + private ZipArchiveEntry _entry; + + public LazyLoadEntry(ZipArchiveEntry entry) + { + _entry = entry; + FileName = entry.Name; + } + + public byte[] Decompress() + { + var len = this._entry.Length; + if (len > int.MaxValue) + { + throw new Exception("too large entry"); + } + + using var ms = new MemoryStream(); + using (var w = _entry.Open()) + { + w.CopyTo(ms); + } + + return ms.GetBuffer(); + } + } +} diff --git a/Editor/Package/Import/Metadata/IAssetVariantAccessor.cs.meta b/Editor/Package/Import/Metadata/IAssetVariantAccessor.cs.meta new file mode 100644 index 0000000..e5ae41d --- /dev/null +++ b/Editor/Package/Import/Metadata/IAssetVariantAccessor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dadb6527de4e4692809f03ff4aa2219f +timeCreated: 1739096966 diff --git a/Editor/Package/Import/Metadata/IComponentReference.cs b/Editor/Package/Import/Metadata/IComponentReference.cs new file mode 100644 index 0000000..a9952f0 --- /dev/null +++ b/Editor/Package/Import/Metadata/IComponentReference.cs @@ -0,0 +1,9 @@ +namespace ResoniteImportHelper.Package.Import.Metadata +{ + public interface IComponentReference where T: notnull + { + public int GetComponentTableIndex(); + + public T GetComponentData(); + } +} diff --git a/Editor/Package/Import/Metadata/IComponentReference.cs.meta b/Editor/Package/Import/Metadata/IComponentReference.cs.meta new file mode 100644 index 0000000..eb4df19 --- /dev/null +++ b/Editor/Package/Import/Metadata/IComponentReference.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 42100ebf89ef4fa0aaf43ae8e4c4c199 +timeCreated: 1739094698 \ No newline at end of file diff --git a/Editor/Package/Import/Metadata/IMetadataAccessor.cs b/Editor/Package/Import/Metadata/IMetadataAccessor.cs new file mode 100644 index 0000000..083d021 --- /dev/null +++ b/Editor/Package/Import/Metadata/IMetadataAccessor.cs @@ -0,0 +1,7 @@ +namespace ResoniteImportHelper.Package.Import.Metadata +{ + public interface IMetadataAccessor + { + public T GetAndDeserialize(string key); + } +} diff --git a/Editor/Package/Import/Metadata/IMetadataAccessor.cs.meta b/Editor/Package/Import/Metadata/IMetadataAccessor.cs.meta new file mode 100644 index 0000000..0e78101 --- /dev/null +++ b/Editor/Package/Import/Metadata/IMetadataAccessor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 73c891857dd449beacaf7bd42c826015 +timeCreated: 1739096059 diff --git a/Editor/Package/Import/Metadata/ResoniteImportHelper.Package.Import.Metadata.asmdef b/Editor/Package/Import/Metadata/ResoniteImportHelper.Package.Import.Metadata.asmdef new file mode 100644 index 0000000..a672894 --- /dev/null +++ b/Editor/Package/Import/Metadata/ResoniteImportHelper.Package.Import.Metadata.asmdef @@ -0,0 +1,6 @@ +{ + "name": "ResoniteImportHelper.Package.Import.Metadata", + "rootNamespace": "global::ResoniteImportHelper.Package.Import.Metadata", + "includePlatforms": ["Editor"], + "references": ["ResoniteImportHelper.Package.Import.Stub", "ResoniteImportHelper.0ReflectionMetadata"] +} diff --git a/Editor/Package/Import/Metadata/ResoniteImportHelper.Package.Import.Metadata.asmdef.meta b/Editor/Package/Import/Metadata/ResoniteImportHelper.Package.Import.Metadata.asmdef.meta new file mode 100644 index 0000000..ede88c0 --- /dev/null +++ b/Editor/Package/Import/Metadata/ResoniteImportHelper.Package.Import.Metadata.asmdef.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ad964f63120f45a9aac389bdb19e4cad +timeCreated: 1739008029 \ No newline at end of file diff --git a/Editor/Package/Import/Metadata/TypeRef.cs b/Editor/Package/Import/Metadata/TypeRef.cs new file mode 100644 index 0000000..ec7f0ba --- /dev/null +++ b/Editor/Package/Import/Metadata/TypeRef.cs @@ -0,0 +1,96 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Reflection.Metadata; +using System.Text.RegularExpressions; + +namespace ResoniteImportHelper.Package.Import.Metadata +{ + [Serializable] + public sealed class TypeRef: IEquatable, IEqualityComparer + { + public string Raw { get; } + private bool? _hasCorrectSyntax; + private Exception? _cachedException; + private AssemblyName? _cachedAssemblyName; + private TypeName? _cachedTypeName; + + public TypeRef(string raw) + { + this.Raw = raw; + } + + public (AssemblyName assembly, TypeName typeName) Parse() + { + switch (this._hasCorrectSyntax) + { + case false: + throw this._cachedException!; + case true: + return (this._cachedAssemblyName!, this._cachedTypeName!); + } + + var regex = new Regex(@"^\[(?[^\]]+)\](?.+)$"); + var matchResult = regex.Match(this.Raw); + if (matchResult.Success) + { + this._hasCorrectSyntax = true; + var assemblyNameCaptureResult = matchResult.Groups["assemblyName"]; + if (!assemblyNameCaptureResult.Success) + { + InvalidateAndThrow(); + } + + this._cachedAssemblyName = new AssemblyName(assemblyNameCaptureResult.Value); + + var typeNameCaptureResult = matchResult.Groups["typeName"]; + if (!typeNameCaptureResult.Success) + { + InvalidateAndThrow(); + } + + if (!TypeName.TryParse(typeNameCaptureResult.Value, out var parsedTypeName)) + { + InvalidateAndThrow(); + } + + this._cachedTypeName = parsedTypeName; + + // Debug.Log($"asm: {_cachedAssemblyName.FullName}, ty: {_cachedTypeName.FullName}"); + return (_cachedAssemblyName, _cachedTypeName); + } + else + { + InvalidateAndThrow(); + } + + throw new InvalidOperationException("unreachable."); + } + + [DoesNotReturn] + private void InvalidateAndThrow() + { + this._hasCorrectSyntax = false; + var exception = new Exception($"Malformed TypeRef: {Raw}"); + this._cachedException = exception; + throw exception; + } + + public bool Equals(TypeRef other) => other != null && this.Raw == other.Raw; + + public bool Equals(TypeRef x, TypeRef y) => + (x, y) switch + { + (null, null) => true, + (_, null) => false, + (null, _) => false, + (_, _) => x.Equals(y) + }; + + public int GetHashCode(TypeRef obj) => obj.Raw.GetHashCode(); + + public override string ToString() => $"TypeRef({Raw})"; + } +} diff --git a/Editor/Package/Import/Metadata/TypeRef.cs.meta b/Editor/Package/Import/Metadata/TypeRef.cs.meta new file mode 100644 index 0000000..a02b11e --- /dev/null +++ b/Editor/Package/Import/Metadata/TypeRef.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6d5e1f74f6de4ed99aaef3566b343b8f +timeCreated: 1738982616 \ No newline at end of file diff --git a/Editor/Package/Import/Metadata/TypedComponentReference.cs b/Editor/Package/Import/Metadata/TypedComponentReference.cs new file mode 100644 index 0000000..75c1c4d --- /dev/null +++ b/Editor/Package/Import/Metadata/TypedComponentReference.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace ResoniteImportHelper.Package.Import.Metadata +{ + public sealed class TypedComponentReference : IComponentReference where T : notnull + { + [JsonProperty("Type")] + public int ComponentTableIndex; + [JsonProperty("Data")] + public T ComponentProperties; + + public int GetComponentTableIndex() => ComponentTableIndex; + + public T GetComponentData() => ComponentProperties; + } +} diff --git a/Editor/Package/Import/Metadata/TypedComponentReference.cs.meta b/Editor/Package/Import/Metadata/TypedComponentReference.cs.meta new file mode 100644 index 0000000..9181bda --- /dev/null +++ b/Editor/Package/Import/Metadata/TypedComponentReference.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fc3c3a5f16854debb0064a9abd003cef +timeCreated: 1739094679 \ No newline at end of file diff --git a/Editor/Package/Import/Metadata/UntypedComponentReference.cs b/Editor/Package/Import/Metadata/UntypedComponentReference.cs new file mode 100644 index 0000000..0437bb8 --- /dev/null +++ b/Editor/Package/Import/Metadata/UntypedComponentReference.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace ResoniteImportHelper.Package.Import.Metadata +{ + public sealed class UntypedComponentReference : IComponentReference> + { + [JsonProperty("Type")] + public int ComponentTableIndex; + [JsonProperty("Data")] + public Dictionary UntypedComponentProperties; + + public int GetComponentTableIndex() => ComponentTableIndex; + public Dictionary GetComponentData() => UntypedComponentProperties; + + public TypedComponentReference AsTyped() + { + return new TypedComponentReference + { + ComponentTableIndex = ComponentTableIndex, + ComponentProperties = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(UntypedComponentProperties)) + }; + } + } +} diff --git a/Editor/Package/Import/Metadata/UntypedComponentReference.cs.meta b/Editor/Package/Import/Metadata/UntypedComponentReference.cs.meta new file mode 100644 index 0000000..1bc1d86 --- /dev/null +++ b/Editor/Package/Import/Metadata/UntypedComponentReference.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e3a9fd84bce44bf28a1913b66b467a5c +timeCreated: 1739094573 \ No newline at end of file diff --git a/Editor/Package/Import/ResoniteImportHelper.Package.Import.asmdef b/Editor/Package/Import/ResoniteImportHelper.Package.Import.asmdef new file mode 100644 index 0000000..e87e0a0 --- /dev/null +++ b/Editor/Package/Import/ResoniteImportHelper.Package.Import.asmdef @@ -0,0 +1,6 @@ +{ + "name": "ResoniteImportHelper.Package.Import", + "rootNamespace": "global::ResoniteImportHelper.Package.Import", + "includePlatforms": ["Editor"], + "references":[ "ResoniteImportHelper.0ReflectionMetadata", "ResoniteImportHelper.Package.Import.Stub", "ResoniteImportHelper.Package.Asset.Types", "ResoniteImportHelper.Package.Import.Metadata", "ResoniteImportHelper.Package.Deserialize" ] +} diff --git a/Editor/Package/Import/ResoniteImportHelper.Package.Import.asmdef.meta b/Editor/Package/Import/ResoniteImportHelper.Package.Import.asmdef.meta new file mode 100644 index 0000000..b4f0501 --- /dev/null +++ b/Editor/Package/Import/ResoniteImportHelper.Package.Import.asmdef.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8db65e83cd19478ca4d494ee98716064 +timeCreated: 1730750524 \ No newline at end of file diff --git a/Editor/Package/Import/Stub.meta b/Editor/Package/Import/Stub.meta new file mode 100644 index 0000000..79bfe82 --- /dev/null +++ b/Editor/Package/Import/Stub.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f330b92d2fbb49518478beaaf700be87 +timeCreated: 1730805177 \ No newline at end of file diff --git a/Editor/Package/Import/Stub/ResoniteImportHelper.Package.Import.Stub.asmdef b/Editor/Package/Import/Stub/ResoniteImportHelper.Package.Import.Stub.asmdef new file mode 100644 index 0000000..0f2d0f2 --- /dev/null +++ b/Editor/Package/Import/Stub/ResoniteImportHelper.Package.Import.Stub.asmdef @@ -0,0 +1,5 @@ +{ + "name": "ResoniteImportHelper.Package.Import.Stub", + "rootNamespace": "global::ResoniteImportHelper.Package.Import.Stub", + "references":[ "GUID:da758e96819041b2b57ad6379d1c0c58", "ResoniteImportHelper.Package.Deserialize" ] +} diff --git a/Editor/Package/Import/Stub/ResoniteImportHelper.Package.Import.Stub.asmdef.meta b/Editor/Package/Import/Stub/ResoniteImportHelper.Package.Import.Stub.asmdef.meta new file mode 100644 index 0000000..964a2dc --- /dev/null +++ b/Editor/Package/Import/Stub/ResoniteImportHelper.Package.Import.Stub.asmdef.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ec6a53d6b953460588f6cbd0313f0275 +timeCreated: 1730805193 \ No newline at end of file diff --git a/Editor/Package/Import/Stub/Slot.cs b/Editor/Package/Import/Stub/Slot.cs new file mode 100644 index 0000000..fbad696 --- /dev/null +++ b/Editor/Package/Import/Stub/Slot.cs @@ -0,0 +1,39 @@ +using Newtonsoft.Json; +using ResoniteImportHelper.Package.Import.Deserialize.Support; +using UnityEngine; + +namespace ResoniteImportHelper.Package.Import.Stub +{ + public sealed class Slot : IIdentifiable + { + public string ID; + public IdentifiableDataCell Name; + + [JsonProperty("Position")] + private IdentifiableDataCell _position; + + [JsonIgnore] + private Vector3? _cachedPosition; + + [JsonIgnore] public Vector3 Position => _cachedPosition ??= new Vector3(_position.Data[0], _position.Data[1], _position.Data[2]); + + [JsonProperty("Rotation")] + private IdentifiableDataCell _rotation; + + [JsonIgnore] + private Quaternion? _cachedRotation; + + [JsonIgnore] public Quaternion Rotation => _cachedRotation ??= new Quaternion(_rotation.Data[0], _rotation.Data[1], _rotation.Data[2], _rotation.Data[3]); + + [JsonProperty("Scale")] + private IdentifiableDataCell _scale; + + [JsonIgnore] + private Vector3? _cachedScale; + + [JsonIgnore] public Vector3 Scale => _cachedScale ??= new Vector3(_scale.Data[0], _scale.Data[1], _scale.Data[2]); + + public Slot[] Children; + public string GetIdentifier() => ID; + } +} diff --git a/Editor/Package/Import/Stub/Slot.cs.meta b/Editor/Package/Import/Stub/Slot.cs.meta new file mode 100644 index 0000000..0b12042 --- /dev/null +++ b/Editor/Package/Import/Stub/Slot.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 03461696fb614364b7511d5e44279dcc +timeCreated: 1739005586 \ No newline at end of file diff --git a/Editor/Package/Import/Stub/StaticTexture2D.cs b/Editor/Package/Import/Stub/StaticTexture2D.cs new file mode 100644 index 0000000..4cf8604 --- /dev/null +++ b/Editor/Package/Import/Stub/StaticTexture2D.cs @@ -0,0 +1,43 @@ +using System; +using ResoniteImportHelper.Package.Import.Deserialize.Support; +using UnityEngine; + +namespace ResoniteImportHelper.Editor.Package.Import.Stub +{ + public sealed class StaticTexture2D : IIdentifiable + { + public string ID; + public IdentifiableDataCell Enabled; + public IdentifiableDataCell URL; + public IdentifiableDataCell IsNormalMap; + public IdentifiableDataCell WrapModeU; + public IdentifiableDataCell WrapModeV; + public IdentifiableDataCell CrunchCompressed; + public IdentifiableDataCell MipMapFilter; + + public string GetAssetIdentifier() + { + const string PACKDB_PREFIX = "@packdb:///"; + var url = URL.Data; + if (!url.StartsWith("@")) throw new Exception("URL does not start with at-mark."); + if (!url.StartsWith(PACKDB_PREFIX)) throw new Exception("URL does not start with packdb prefix."); + + return url[PACKDB_PREFIX.Length..]; + } + + public TextureWrapMode GetWrapModeU() => DeserializeToWrapMode(WrapModeU.Data); + + public TextureWrapMode GetWrapModeV() => DeserializeToWrapMode(WrapModeV.Data); + + private TextureWrapMode DeserializeToWrapMode(string raw) + { + return raw switch + { + "Repeat" => TextureWrapMode.Repeat, + _ => throw new IndexOutOfRangeException($"{WrapModeU} is not supported.") + }; + } + + public string GetIdentifier() => ID; + } +} diff --git a/Editor/Package/Import/Stub/StaticTexture2D.cs.meta b/Editor/Package/Import/Stub/StaticTexture2D.cs.meta new file mode 100644 index 0000000..72fce76 --- /dev/null +++ b/Editor/Package/Import/Stub/StaticTexture2D.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e2e9e7d934b346eabb7c9235919bf10e +timeCreated: 1730805229 \ No newline at end of file