├── Assets ├── csc.rsp ├── editor │ ├── SyntyStore_Search │ │ ├── no_prefab_preview.png │ │ ├── resize_image_icon.png │ │ ├── synty_search_icon.png │ │ ├── synty_search_header.png │ │ ├── StringUtility.cs.meta │ │ ├── TarFileUtility.cs.meta │ │ ├── SyntyStoreSearch.cs.meta │ │ ├── CoroutineEditorUtility.cs.meta │ │ ├── StringUtility.cs │ │ ├── CoroutineEditorUtility.cs │ │ ├── no_prefab_preview.png.meta │ │ ├── resize_image_icon.png.meta │ │ ├── synty_search_header.png.meta │ │ ├── synty_search_icon.png.meta │ │ ├── TarFileUtility.cs │ │ └── SyntyStoreSearch.cs │ └── SyntyStore_Search.meta ├── csc.rsp.meta └── editor.meta ├── .gitattributes ├── EditorWindow.PNG ├── .vsconfig ├── SyntyPrefabSearch.unitypackage ├── ProjectSettings └── XRSettings.asset ├── .gitignore ├── Packages ├── manifest.json └── packages-lock.json ├── README.md └── LICENSE /Assets/csc.rsp: -------------------------------------------------------------------------------- 1 | -r:System.IO.Compression.FileSystem.dll -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /EditorWindow.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixelshenanigans/SyntySearchEditorWindow/HEAD/EditorWindow.PNG -------------------------------------------------------------------------------- /.vsconfig: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "components": [ 4 | "Microsoft.VisualStudio.Workload.ManagedGame" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /SyntyPrefabSearch.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixelshenanigans/SyntySearchEditorWindow/HEAD/SyntyPrefabSearch.unitypackage -------------------------------------------------------------------------------- /Assets/editor/SyntyStore_Search/no_prefab_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixelshenanigans/SyntySearchEditorWindow/HEAD/Assets/editor/SyntyStore_Search/no_prefab_preview.png -------------------------------------------------------------------------------- /Assets/editor/SyntyStore_Search/resize_image_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixelshenanigans/SyntySearchEditorWindow/HEAD/Assets/editor/SyntyStore_Search/resize_image_icon.png -------------------------------------------------------------------------------- /Assets/editor/SyntyStore_Search/synty_search_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixelshenanigans/SyntySearchEditorWindow/HEAD/Assets/editor/SyntyStore_Search/synty_search_icon.png -------------------------------------------------------------------------------- /Assets/editor/SyntyStore_Search/synty_search_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pixelshenanigans/SyntySearchEditorWindow/HEAD/Assets/editor/SyntyStore_Search/synty_search_header.png -------------------------------------------------------------------------------- /Assets/csc.rsp.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b63cc9e844f20de45b96a836eda0107b 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Assets/editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 24306684ca9899241b2107afc5e7df27 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /ProjectSettings/XRSettings.asset: -------------------------------------------------------------------------------- 1 | { 2 | "m_SettingKeys": [ 3 | "VR Device Disabled", 4 | "VR Device User Alert" 5 | ], 6 | "m_SettingValues": [ 7 | "False", 8 | "False" 9 | ] 10 | } -------------------------------------------------------------------------------- /Assets/editor/SyntyStore_Search.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 27fc13e88f7116b43b603e69a265d83c 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/editor/SyntyStore_Search/StringUtility.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 290692bf36119cf41a80bb50fa32e074 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/editor/SyntyStore_Search/TarFileUtility.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8c8ca99a298feec48849c322c468fbf7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/editor/SyntyStore_Search/SyntyStoreSearch.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7d1a7b36a4beb494e9468eabf062360c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/editor/SyntyStore_Search/CoroutineEditorUtility.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cd4246883a5f9fc4cbde4e068c221997 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/editor/SyntyStore_Search/StringUtility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Text.RegularExpressions; 4 | 5 | namespace PixelShenanigans.FileUtilities 6 | { 7 | public static class StringUtility 8 | { 9 | public static string[] SplitCamelCase(this string str) 10 | { 11 | string splitCamelCase = Regex.Replace(str, @"(? Coroutines = new List(); 13 | 14 | static CoroutineEditorUtility() 15 | { 16 | EditorApplication.update += ExecuteCoroutine; 17 | } 18 | 19 | public static IEnumerator StartCoroutine(IEnumerator coroutine) 20 | { 21 | Coroutines.Add(coroutine); 22 | return coroutine; 23 | } 24 | 25 | static void ExecuteCoroutine() 26 | { 27 | if (Coroutines.Count <= 0) 28 | { 29 | return; 30 | } 31 | 32 | currentExecute = (currentExecute + 1) % Coroutines.Count; 33 | 34 | bool finish = !Coroutines[currentExecute].MoveNext(); 35 | 36 | if (finish) 37 | { 38 | Coroutines.RemoveAt(currentExecute); 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This .gitignore file should be placed at the root of your Unity project directory 2 | # 3 | # Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore 4 | # 5 | /[Ll]ibrary/ 6 | /[Tt]emp/ 7 | /[Oo]bj/ 8 | /[Bb]uild/ 9 | /[Bb]uilds/ 10 | /[Ll]ogs/ 11 | /[Uu]ser[Ss]ettings/ 12 | 13 | # MemoryCaptures can get excessive in size. 14 | # They also could contain extremely sensitive data 15 | /[Mm]emoryCaptures/ 16 | 17 | # Recordings can get excessive in size 18 | /[Rr]ecordings/ 19 | 20 | # Uncomment this line if you wish to ignore the asset store tools plugin 21 | # /[Aa]ssets/AssetStoreTools* 22 | 23 | # Autogenerated Jetbrains Rider plugin 24 | /[Aa]ssets/Plugins/Editor/JetBrains* 25 | 26 | # Visual Studio cache directory 27 | .vs/ 28 | 29 | # Gradle cache directory 30 | .gradle/ 31 | 32 | # Autogenerated VS/MD/Consulo solution and project files 33 | ExportedObj/ 34 | .consulo/ 35 | *.csproj 36 | *.unityproj 37 | *.sln 38 | *.suo 39 | *.tmp 40 | *.user 41 | *.userprefs 42 | *.pidb 43 | *.booproj 44 | *.svd 45 | *.pdb 46 | *.mdb 47 | *.opendb 48 | *.VC.db 49 | 50 | # Unity3D generated meta files 51 | *.pidb.meta 52 | *.pdb.meta 53 | *.mdb.meta 54 | 55 | # Unity3D generated file on crash reports 56 | sysinfo.txt 57 | 58 | # Builds 59 | *.apk 60 | *.aab 61 | *.app 62 | 63 | # Crashlytics generated file 64 | crashlytics-build.properties 65 | 66 | # Packed Addressables 67 | /[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* 68 | 69 | # Temporary auto-generated Android Assets 70 | /[Aa]ssets/[Ss]treamingAssets/aa.meta 71 | /[Aa]ssets/[Ss]treamingAssets/aa/* 72 | -------------------------------------------------------------------------------- /Packages/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.unity.collab-proxy": "1.14.12", 4 | "com.unity.ide.rider": "1.2.1", 5 | "com.unity.ide.visualstudio": "2.0.14", 6 | "com.unity.ide.vscode": "1.2.4", 7 | "com.unity.test-framework": "1.1.30", 8 | "com.unity.textmeshpro": "2.1.6", 9 | "com.unity.timeline": "1.2.18", 10 | "com.unity.ugui": "1.0.0", 11 | "com.unity.modules.ai": "1.0.0", 12 | "com.unity.modules.androidjni": "1.0.0", 13 | "com.unity.modules.animation": "1.0.0", 14 | "com.unity.modules.assetbundle": "1.0.0", 15 | "com.unity.modules.audio": "1.0.0", 16 | "com.unity.modules.cloth": "1.0.0", 17 | "com.unity.modules.director": "1.0.0", 18 | "com.unity.modules.imageconversion": "1.0.0", 19 | "com.unity.modules.imgui": "1.0.0", 20 | "com.unity.modules.jsonserialize": "1.0.0", 21 | "com.unity.modules.particlesystem": "1.0.0", 22 | "com.unity.modules.physics": "1.0.0", 23 | "com.unity.modules.physics2d": "1.0.0", 24 | "com.unity.modules.screencapture": "1.0.0", 25 | "com.unity.modules.terrain": "1.0.0", 26 | "com.unity.modules.terrainphysics": "1.0.0", 27 | "com.unity.modules.tilemap": "1.0.0", 28 | "com.unity.modules.ui": "1.0.0", 29 | "com.unity.modules.uielements": "1.0.0", 30 | "com.unity.modules.umbra": "1.0.0", 31 | "com.unity.modules.unityanalytics": "1.0.0", 32 | "com.unity.modules.unitywebrequest": "1.0.0", 33 | "com.unity.modules.unitywebrequestassetbundle": "1.0.0", 34 | "com.unity.modules.unitywebrequestaudio": "1.0.0", 35 | "com.unity.modules.unitywebrequesttexture": "1.0.0", 36 | "com.unity.modules.unitywebrequestwww": "1.0.0", 37 | "com.unity.modules.vehicles": "1.0.0", 38 | "com.unity.modules.video": "1.0.0", 39 | "com.unity.modules.vr": "1.0.0", 40 | "com.unity.modules.wind": "1.0.0", 41 | "com.unity.modules.xr": "1.0.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Assets/editor/SyntyStore_Search/no_prefab_preview.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f7b8464bcab9e59429c59cf8559c2eac 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 11 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: 1 35 | aniso: 1 36 | mipBias: 0 37 | wrapU: 0 38 | wrapV: 0 39 | wrapW: 0 40 | nPOTScale: 1 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 0 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 1 52 | alphaIsTransparency: 0 53 | spriteTessellationDetail: -1 54 | textureType: 0 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | applyGammaDecoding: 0 61 | platformSettings: 62 | - serializedVersion: 3 63 | buildTarget: DefaultTexturePlatform 64 | maxTextureSize: 2048 65 | resizeAlgorithm: 0 66 | textureFormat: -1 67 | textureCompression: 1 68 | compressionQuality: 50 69 | crunchedCompression: 0 70 | allowsAlphaSplitting: 0 71 | overridden: 0 72 | androidETC2FallbackOverride: 0 73 | forceMaximumCompressionQuality_BC6H_BC7: 0 74 | spriteSheet: 75 | serializedVersion: 2 76 | sprites: [] 77 | outline: [] 78 | physicsShape: [] 79 | bones: [] 80 | spriteID: 81 | internalID: 0 82 | vertices: [] 83 | indices: 84 | edges: [] 85 | weights: [] 86 | secondaryTextures: [] 87 | spritePackingTag: 88 | pSDRemoveMatte: 0 89 | pSDShowRemoveMatteOption: 0 90 | userData: 91 | assetBundleName: 92 | assetBundleVariant: 93 | -------------------------------------------------------------------------------- /Assets/editor/SyntyStore_Search/resize_image_icon.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c0364ddb0c545844a8c53382049ac642 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 11 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: 1 35 | aniso: 1 36 | mipBias: 0 37 | wrapU: 0 38 | wrapV: 0 39 | wrapW: 0 40 | nPOTScale: 1 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 0 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 1 52 | alphaIsTransparency: 0 53 | spriteTessellationDetail: -1 54 | textureType: 0 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | applyGammaDecoding: 0 61 | platformSettings: 62 | - serializedVersion: 3 63 | buildTarget: DefaultTexturePlatform 64 | maxTextureSize: 2048 65 | resizeAlgorithm: 0 66 | textureFormat: -1 67 | textureCompression: 1 68 | compressionQuality: 50 69 | crunchedCompression: 0 70 | allowsAlphaSplitting: 0 71 | overridden: 0 72 | androidETC2FallbackOverride: 0 73 | forceMaximumCompressionQuality_BC6H_BC7: 0 74 | spriteSheet: 75 | serializedVersion: 2 76 | sprites: [] 77 | outline: [] 78 | physicsShape: [] 79 | bones: [] 80 | spriteID: 81 | internalID: 0 82 | vertices: [] 83 | indices: 84 | edges: [] 85 | weights: [] 86 | secondaryTextures: [] 87 | spritePackingTag: 88 | pSDRemoveMatte: 0 89 | pSDShowRemoveMatteOption: 0 90 | userData: 91 | assetBundleName: 92 | assetBundleVariant: 93 | -------------------------------------------------------------------------------- /Assets/editor/SyntyStore_Search/synty_search_header.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c0bf54ecf351fad479a5fcaa74d92db0 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 11 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: 1 35 | aniso: 1 36 | mipBias: 0 37 | wrapU: 0 38 | wrapV: 0 39 | wrapW: 0 40 | nPOTScale: 1 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 0 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 1 52 | alphaIsTransparency: 0 53 | spriteTessellationDetail: -1 54 | textureType: 0 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | applyGammaDecoding: 0 61 | platformSettings: 62 | - serializedVersion: 3 63 | buildTarget: DefaultTexturePlatform 64 | maxTextureSize: 2048 65 | resizeAlgorithm: 0 66 | textureFormat: -1 67 | textureCompression: 1 68 | compressionQuality: 50 69 | crunchedCompression: 0 70 | allowsAlphaSplitting: 0 71 | overridden: 0 72 | androidETC2FallbackOverride: 0 73 | forceMaximumCompressionQuality_BC6H_BC7: 0 74 | spriteSheet: 75 | serializedVersion: 2 76 | sprites: [] 77 | outline: [] 78 | physicsShape: [] 79 | bones: [] 80 | spriteID: 81 | internalID: 0 82 | vertices: [] 83 | indices: 84 | edges: [] 85 | weights: [] 86 | secondaryTextures: [] 87 | spritePackingTag: 88 | pSDRemoveMatte: 0 89 | pSDShowRemoveMatteOption: 0 90 | userData: 91 | assetBundleName: 92 | assetBundleVariant: 93 | -------------------------------------------------------------------------------- /Assets/editor/SyntyStore_Search/synty_search_icon.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7f6a00db2b0ae4242b3cb34ac39c18d7 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 11 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | grayScaleToAlpha: 0 27 | generateCubemap: 6 28 | cubemapConvolution: 0 29 | seamlessCubemap: 0 30 | textureFormat: 1 31 | maxTextureSize: 2048 32 | textureSettings: 33 | serializedVersion: 2 34 | filterMode: 1 35 | aniso: 1 36 | mipBias: 0 37 | wrapU: 0 38 | wrapV: 0 39 | wrapW: 0 40 | nPOTScale: 1 41 | lightmap: 0 42 | compressionQuality: 50 43 | spriteMode: 0 44 | spriteExtrude: 1 45 | spriteMeshType: 1 46 | alignment: 0 47 | spritePivot: {x: 0.5, y: 0.5} 48 | spritePixelsToUnits: 100 49 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 50 | spriteGenerateFallbackPhysicsShape: 1 51 | alphaUsage: 1 52 | alphaIsTransparency: 0 53 | spriteTessellationDetail: -1 54 | textureType: 0 55 | textureShape: 1 56 | singleChannelComponent: 0 57 | maxTextureSizeSet: 0 58 | compressionQualitySet: 0 59 | textureFormatSet: 0 60 | applyGammaDecoding: 0 61 | platformSettings: 62 | - serializedVersion: 3 63 | buildTarget: DefaultTexturePlatform 64 | maxTextureSize: 2048 65 | resizeAlgorithm: 0 66 | textureFormat: -1 67 | textureCompression: 1 68 | compressionQuality: 50 69 | crunchedCompression: 0 70 | allowsAlphaSplitting: 0 71 | overridden: 0 72 | androidETC2FallbackOverride: 0 73 | forceMaximumCompressionQuality_BC6H_BC7: 0 74 | spriteSheet: 75 | serializedVersion: 2 76 | sprites: [] 77 | outline: [] 78 | physicsShape: [] 79 | bones: [] 80 | spriteID: 81 | internalID: 0 82 | vertices: [] 83 | indices: 84 | edges: [] 85 | weights: [] 86 | secondaryTextures: [] 87 | spritePackingTag: 88 | pSDRemoveMatte: 0 89 | pSDShowRemoveMatteOption: 0 90 | userData: 91 | assetBundleName: 92 | assetBundleVariant: 93 | -------------------------------------------------------------------------------- /Assets/editor/SyntyStore_Search/TarFileUtility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.IO.Compression; 5 | using System.Text; 6 | 7 | namespace PixelShenanigans.FileUtilities 8 | { 9 | public class TarFileUtility 10 | { 11 | public static MemoryStream DecompressGzFile(Stream stream) 12 | { 13 | const int BytesToRead = 4096; 14 | 15 | using (var gzipStream = new GZipStream(stream, CompressionMode.Decompress)) 16 | { 17 | using (var memStream = new MemoryStream()) 18 | { 19 | var buffer = new byte[BytesToRead]; 20 | int bytesRead; 21 | 22 | do 23 | { 24 | bytesRead = gzipStream.Read(buffer, 0, BytesToRead); 25 | memStream.Write(buffer, 0, bytesRead); 26 | } 27 | while (bytesRead == BytesToRead); 28 | 29 | memStream.Seek(0, SeekOrigin.Begin); 30 | 31 | return memStream; 32 | } 33 | } 34 | } 35 | 36 | public static IEnumerable> ReadFiles(Stream stream) 37 | { 38 | // https://en.wikipedia.org/wiki/Tar_(computing) 39 | const int FileNameLength = 100; 40 | const int FileModeLength = 8; 41 | const int FileOwnerUserIDLength = 8; 42 | const int FileGroupUserIDLength = 8; 43 | const int FileSizeLength = 12; 44 | const int LastModifiedTimeLength = 12; 45 | const int ChecksumLength = 18; 46 | const int LinkIndicatorLength = 1; 47 | const int LinkedFileNameLength = 100; 48 | const int MaxFieldLength = FileNameLength; 49 | const int RemainingHeader = LastModifiedTimeLength + ChecksumLength + LinkIndicatorLength + LinkedFileNameLength; 50 | const int HeaderLength = FileNameLength + FileModeLength + FileOwnerUserIDLength + FileGroupUserIDLength + FileSizeLength + RemainingHeader; 51 | const int BlockSize = 512; 52 | const int RemainingBytes = BlockSize - HeaderLength + RemainingHeader; 53 | 54 | var buffer = new byte[MaxFieldLength]; 55 | 56 | while (true) 57 | { 58 | stream.Read(buffer, 0, FileNameLength); 59 | string fileName = Encoding.ASCII.GetString(buffer).Trim('\0', ' '); 60 | 61 | if (string.IsNullOrWhiteSpace(fileName)) break; 62 | 63 | stream.Seek(FileModeLength + FileOwnerUserIDLength + FileGroupUserIDLength, SeekOrigin.Current); 64 | stream.Read(buffer, 0, FileSizeLength); 65 | 66 | long fileSize = Convert.ToInt64( 67 | Encoding.ASCII.GetString(buffer, 0, FileSizeLength).Trim('\0', ' '), 68 | 8); // Octal 69 | 70 | stream.Seek(RemainingBytes, SeekOrigin.Current); 71 | 72 | var fileData = new byte[fileSize]; 73 | stream.Read(fileData, 0, fileData.Length); 74 | 75 | yield return new Tuple(fileName, fileData); 76 | 77 | long position = stream.Position; 78 | long offset = BlockSize - (position % BlockSize); 79 | if (offset == BlockSize) 80 | { 81 | offset = 0; 82 | } 83 | 84 | stream.Seek(offset, SeekOrigin.Current); 85 | } 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SyntySearchEditorWindow 2 | Unity Synty Search editor tool 3 | 4 | ![Synty Search Screenshot](https://user-images.githubusercontent.com/80069077/167243235-0a3f822d-849b-4b7e-a1b2-06a37c486a3a.PNG) 5 | 6 | - A Synty "POLYGON" and "Simple" package organizer that works offline in the Unity editor. 7 | - Use alongside the Synty Package Importer tool. 8 | - The search results are organized by asset package. 9 | - The prefab preview images and name text can be scaled using the slider control. 10 | - When you set the disc location of your Unity packages, each package will be indicated as being "unowned", "owned" or "imported". 11 | - Clicking an "unowned" item will navigate you to the Synty store (www.syntystore.com) page for that package. 12 | - Clicking an "owned" item will prompt you to import the package into your Unity project. 13 | - Clicking an "imported" item will highlight that prefab in your project window, for easy location! 14 | - You can set multiple package locations. 15 | - Setting a location will re-scan all files in that folder. 16 | - The Unity asset download folder is always scanned when you set a location. 17 | - Adding other locations is useful if you've downloaded unitypackage files from Unity or the Synty store. 18 | - The tool scans each location for any "POLYGON" unitypackage files and "Simple" unitypackage and zip files. 19 | - The tool builds a database of the asset paths. This scan may take a few minutes but the data is cached and is available across all of your Unity projects! 20 | - This a community project and is not affiliated with Synty Studios. 21 | 22 | Release Notes 1.4 (Nov 3 2023) 23 | ------------------------------ 24 | 25 | - Removed use of Synty Search website. 26 | - This change limits the search to the Synty packages you own and have downloaded, either to the default Unity asset store location or a folder of your choice. 27 | - Please use the Synty Search page for a complete online experience, but consider this tool as an offline solution to searching for Synty assets that you own. 28 | 29 | Release Notes 1.3 (5/14/2022) 30 | ------------------------ 31 | 32 | - "Simple" unitypackage and zip files are now supported! 33 | - Text search now available for "Simple" packages 34 | - Prefab preview images available for "Simple" packages (unitypackage files only) 35 | - Can now click on a search result for an imported "Simple" package to locate the prefab the Unity project window 36 | - Click an "owned" item to import the package (interactive) into your project (unitypackage files only) 37 | - Setting a location will always scan the contents, useful for refreshing the location 38 | - Clicking on package icon will show list of owned packages 39 | - Now resides under Tools menu 40 | 41 | Release Notes 1.2 (5/7/2022) 42 | ------------------------ 43 | 44 | - Search image size saved in EditorPrefs 45 | - Number of imported and owned packages displayed 46 | - UI improvements 47 | 48 | Release Notes 1.1 (5/5/2022) 49 | ------------------------ 50 | 51 | - Fixed image resizing 52 | - Can now use ENTER key to perform search 53 | - Reduced size of asset database cache 54 | 55 | Known issues 56 | ------------ 57 | - No way to remove folder locations 58 | -------------------------------------------------------------------------------- /Packages/packages-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.unity.collab-proxy": { 4 | "version": "1.14.12", 5 | "depth": 0, 6 | "source": "registry", 7 | "dependencies": {}, 8 | "url": "https://packages.unity.com" 9 | }, 10 | "com.unity.ext.nunit": { 11 | "version": "1.0.6", 12 | "depth": 1, 13 | "source": "registry", 14 | "dependencies": {}, 15 | "url": "https://packages.unity.com" 16 | }, 17 | "com.unity.ide.rider": { 18 | "version": "1.2.1", 19 | "depth": 0, 20 | "source": "registry", 21 | "dependencies": { 22 | "com.unity.test-framework": "1.1.1" 23 | }, 24 | "url": "https://packages.unity.com" 25 | }, 26 | "com.unity.ide.visualstudio": { 27 | "version": "2.0.14", 28 | "depth": 0, 29 | "source": "registry", 30 | "dependencies": { 31 | "com.unity.test-framework": "1.1.9" 32 | }, 33 | "url": "https://packages.unity.com" 34 | }, 35 | "com.unity.ide.vscode": { 36 | "version": "1.2.4", 37 | "depth": 0, 38 | "source": "registry", 39 | "dependencies": {}, 40 | "url": "https://packages.unity.com" 41 | }, 42 | "com.unity.test-framework": { 43 | "version": "1.1.30", 44 | "depth": 0, 45 | "source": "registry", 46 | "dependencies": { 47 | "com.unity.ext.nunit": "1.0.6", 48 | "com.unity.modules.imgui": "1.0.0", 49 | "com.unity.modules.jsonserialize": "1.0.0" 50 | }, 51 | "url": "https://packages.unity.com" 52 | }, 53 | "com.unity.textmeshpro": { 54 | "version": "2.1.6", 55 | "depth": 0, 56 | "source": "registry", 57 | "dependencies": { 58 | "com.unity.ugui": "1.0.0" 59 | }, 60 | "url": "https://packages.unity.com" 61 | }, 62 | "com.unity.timeline": { 63 | "version": "1.2.18", 64 | "depth": 0, 65 | "source": "registry", 66 | "dependencies": { 67 | "com.unity.modules.director": "1.0.0", 68 | "com.unity.modules.animation": "1.0.0", 69 | "com.unity.modules.audio": "1.0.0", 70 | "com.unity.modules.particlesystem": "1.0.0" 71 | }, 72 | "url": "https://packages.unity.com" 73 | }, 74 | "com.unity.ugui": { 75 | "version": "1.0.0", 76 | "depth": 0, 77 | "source": "builtin", 78 | "dependencies": { 79 | "com.unity.modules.ui": "1.0.0", 80 | "com.unity.modules.imgui": "1.0.0" 81 | } 82 | }, 83 | "com.unity.modules.ai": { 84 | "version": "1.0.0", 85 | "depth": 0, 86 | "source": "builtin", 87 | "dependencies": {} 88 | }, 89 | "com.unity.modules.androidjni": { 90 | "version": "1.0.0", 91 | "depth": 0, 92 | "source": "builtin", 93 | "dependencies": {} 94 | }, 95 | "com.unity.modules.animation": { 96 | "version": "1.0.0", 97 | "depth": 0, 98 | "source": "builtin", 99 | "dependencies": {} 100 | }, 101 | "com.unity.modules.assetbundle": { 102 | "version": "1.0.0", 103 | "depth": 0, 104 | "source": "builtin", 105 | "dependencies": {} 106 | }, 107 | "com.unity.modules.audio": { 108 | "version": "1.0.0", 109 | "depth": 0, 110 | "source": "builtin", 111 | "dependencies": {} 112 | }, 113 | "com.unity.modules.cloth": { 114 | "version": "1.0.0", 115 | "depth": 0, 116 | "source": "builtin", 117 | "dependencies": { 118 | "com.unity.modules.physics": "1.0.0" 119 | } 120 | }, 121 | "com.unity.modules.director": { 122 | "version": "1.0.0", 123 | "depth": 0, 124 | "source": "builtin", 125 | "dependencies": { 126 | "com.unity.modules.audio": "1.0.0", 127 | "com.unity.modules.animation": "1.0.0" 128 | } 129 | }, 130 | "com.unity.modules.imageconversion": { 131 | "version": "1.0.0", 132 | "depth": 0, 133 | "source": "builtin", 134 | "dependencies": {} 135 | }, 136 | "com.unity.modules.imgui": { 137 | "version": "1.0.0", 138 | "depth": 0, 139 | "source": "builtin", 140 | "dependencies": {} 141 | }, 142 | "com.unity.modules.jsonserialize": { 143 | "version": "1.0.0", 144 | "depth": 0, 145 | "source": "builtin", 146 | "dependencies": {} 147 | }, 148 | "com.unity.modules.particlesystem": { 149 | "version": "1.0.0", 150 | "depth": 0, 151 | "source": "builtin", 152 | "dependencies": {} 153 | }, 154 | "com.unity.modules.physics": { 155 | "version": "1.0.0", 156 | "depth": 0, 157 | "source": "builtin", 158 | "dependencies": {} 159 | }, 160 | "com.unity.modules.physics2d": { 161 | "version": "1.0.0", 162 | "depth": 0, 163 | "source": "builtin", 164 | "dependencies": {} 165 | }, 166 | "com.unity.modules.screencapture": { 167 | "version": "1.0.0", 168 | "depth": 0, 169 | "source": "builtin", 170 | "dependencies": { 171 | "com.unity.modules.imageconversion": "1.0.0" 172 | } 173 | }, 174 | "com.unity.modules.subsystems": { 175 | "version": "1.0.0", 176 | "depth": 1, 177 | "source": "builtin", 178 | "dependencies": { 179 | "com.unity.modules.jsonserialize": "1.0.0" 180 | } 181 | }, 182 | "com.unity.modules.terrain": { 183 | "version": "1.0.0", 184 | "depth": 0, 185 | "source": "builtin", 186 | "dependencies": {} 187 | }, 188 | "com.unity.modules.terrainphysics": { 189 | "version": "1.0.0", 190 | "depth": 0, 191 | "source": "builtin", 192 | "dependencies": { 193 | "com.unity.modules.physics": "1.0.0", 194 | "com.unity.modules.terrain": "1.0.0" 195 | } 196 | }, 197 | "com.unity.modules.tilemap": { 198 | "version": "1.0.0", 199 | "depth": 0, 200 | "source": "builtin", 201 | "dependencies": { 202 | "com.unity.modules.physics2d": "1.0.0" 203 | } 204 | }, 205 | "com.unity.modules.ui": { 206 | "version": "1.0.0", 207 | "depth": 0, 208 | "source": "builtin", 209 | "dependencies": {} 210 | }, 211 | "com.unity.modules.uielements": { 212 | "version": "1.0.0", 213 | "depth": 0, 214 | "source": "builtin", 215 | "dependencies": { 216 | "com.unity.modules.imgui": "1.0.0", 217 | "com.unity.modules.jsonserialize": "1.0.0" 218 | } 219 | }, 220 | "com.unity.modules.umbra": { 221 | "version": "1.0.0", 222 | "depth": 0, 223 | "source": "builtin", 224 | "dependencies": {} 225 | }, 226 | "com.unity.modules.unityanalytics": { 227 | "version": "1.0.0", 228 | "depth": 0, 229 | "source": "builtin", 230 | "dependencies": { 231 | "com.unity.modules.unitywebrequest": "1.0.0", 232 | "com.unity.modules.jsonserialize": "1.0.0" 233 | } 234 | }, 235 | "com.unity.modules.unitywebrequest": { 236 | "version": "1.0.0", 237 | "depth": 0, 238 | "source": "builtin", 239 | "dependencies": {} 240 | }, 241 | "com.unity.modules.unitywebrequestassetbundle": { 242 | "version": "1.0.0", 243 | "depth": 0, 244 | "source": "builtin", 245 | "dependencies": { 246 | "com.unity.modules.assetbundle": "1.0.0", 247 | "com.unity.modules.unitywebrequest": "1.0.0" 248 | } 249 | }, 250 | "com.unity.modules.unitywebrequestaudio": { 251 | "version": "1.0.0", 252 | "depth": 0, 253 | "source": "builtin", 254 | "dependencies": { 255 | "com.unity.modules.unitywebrequest": "1.0.0", 256 | "com.unity.modules.audio": "1.0.0" 257 | } 258 | }, 259 | "com.unity.modules.unitywebrequesttexture": { 260 | "version": "1.0.0", 261 | "depth": 0, 262 | "source": "builtin", 263 | "dependencies": { 264 | "com.unity.modules.unitywebrequest": "1.0.0", 265 | "com.unity.modules.imageconversion": "1.0.0" 266 | } 267 | }, 268 | "com.unity.modules.unitywebrequestwww": { 269 | "version": "1.0.0", 270 | "depth": 0, 271 | "source": "builtin", 272 | "dependencies": { 273 | "com.unity.modules.unitywebrequest": "1.0.0", 274 | "com.unity.modules.unitywebrequestassetbundle": "1.0.0", 275 | "com.unity.modules.unitywebrequestaudio": "1.0.0", 276 | "com.unity.modules.audio": "1.0.0", 277 | "com.unity.modules.assetbundle": "1.0.0", 278 | "com.unity.modules.imageconversion": "1.0.0" 279 | } 280 | }, 281 | "com.unity.modules.vehicles": { 282 | "version": "1.0.0", 283 | "depth": 0, 284 | "source": "builtin", 285 | "dependencies": { 286 | "com.unity.modules.physics": "1.0.0" 287 | } 288 | }, 289 | "com.unity.modules.video": { 290 | "version": "1.0.0", 291 | "depth": 0, 292 | "source": "builtin", 293 | "dependencies": { 294 | "com.unity.modules.audio": "1.0.0", 295 | "com.unity.modules.ui": "1.0.0", 296 | "com.unity.modules.unitywebrequest": "1.0.0" 297 | } 298 | }, 299 | "com.unity.modules.vr": { 300 | "version": "1.0.0", 301 | "depth": 0, 302 | "source": "builtin", 303 | "dependencies": { 304 | "com.unity.modules.jsonserialize": "1.0.0", 305 | "com.unity.modules.physics": "1.0.0", 306 | "com.unity.modules.xr": "1.0.0" 307 | } 308 | }, 309 | "com.unity.modules.wind": { 310 | "version": "1.0.0", 311 | "depth": 0, 312 | "source": "builtin", 313 | "dependencies": {} 314 | }, 315 | "com.unity.modules.xr": { 316 | "version": "1.0.0", 317 | "depth": 0, 318 | "source": "builtin", 319 | "dependencies": { 320 | "com.unity.modules.physics": "1.0.0", 321 | "com.unity.modules.jsonserialize": "1.0.0", 322 | "com.unity.modules.subsystems": "1.0.0" 323 | } 324 | } 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 489 | USA 490 | 491 | Also add information on how to contact you by electronic and paper mail. 492 | 493 | You should also get your employer (if you work as a programmer) or your 494 | school, if any, to sign a "copyright disclaimer" for the library, if 495 | necessary. Here is a sample; alter the names: 496 | 497 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 498 | library `Frob' (a library for tweaking knobs) written by James Random 499 | Hacker. 500 | 501 | , 1 April 1990 502 | Ty Coon, President of Vice 503 | 504 | That's all there is to it! 505 | -------------------------------------------------------------------------------- /Assets/editor/SyntyStore_Search/SyntyStoreSearch.cs: -------------------------------------------------------------------------------- 1 | using PixelShenanigans.EditorUtilities; 2 | using PixelShenanigans.FileUtilities; 3 | 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.IO.Compression; 9 | using System.Linq; 10 | using System.Net; 11 | using System.Text; 12 | 13 | using UnityEditor; 14 | 15 | using UnityEngine; 16 | 17 | namespace PixelShenanigans.SyntyStoreSearch 18 | { 19 | public class SyntyStoreSearch : EditorWindow 20 | { 21 | const string HeaderTexture = "Assets/Editor/SyntyStore_Search/synty_search_header.png"; 22 | const string IconTexture = "Assets/Editor/SyntyStore_Search/synty_search_icon.png"; 23 | const string ImageResizeIconTexture = "Assets/Editor/SyntyStore_Search/resize_image_icon.png"; 24 | const string NoPreviewTexture = "Assets/Editor/SyntyStore_Search/no_prefab_preview.png"; 25 | const string EditorTitle = "Synty Store Prefab Search"; 26 | const string StoreUrl = "https://syntystore.com/"; 27 | const string SiteUrl = "https://www.syntysearch.com"; 28 | const string ApiPart = "/api/"; 29 | const string SearchPart = "prefab/search/"; 30 | const string SetLocationText = "Set package location"; 31 | const string AssetPathPrefix = "Assets/"; 32 | const string PrefabFileExt = ".prefab"; 33 | const string UnityPackageFileExt = ".unitypackage"; 34 | const string CacheFileName = "cache.json"; 35 | const string LocationFileName = "locations.json"; 36 | const string SearchFileName = "search.json"; 37 | const string PreviewsFolderName = "previews"; 38 | const string ImageSizeEditorPrefsKey = "ImageSize"; 39 | const string CurrentAppVersion = "1.3"; 40 | const string CurrentCacheVersion = "1.1"; 41 | const int WindowWidth = 500; 42 | const int WindowHeight = 700; 43 | const int BannerHeight = 130; 44 | const int ImageMargin = 5; 45 | const int imageWidthMin = 40; 46 | const int imageWidthMax = 200; 47 | const int fontSizeMin = 8; 48 | const int fontSizeMax = 14; 49 | 50 | string _cachePath = null; 51 | public string CacheLocation 52 | { 53 | get { return _cachePath ?? (_cachePath = GetCacheLocation()); } 54 | } 55 | bool cacheIsDirty = false; 56 | int imageSize = 120; 57 | int fontSize = 15; 58 | int ownedPackageCount = 0; 59 | int importedPackageCount = 0; 60 | string searchMessage = ""; 61 | string searchTerm = ""; 62 | GUIStyle imageStyle; 63 | GUIStyle labelStyle; 64 | GUIStyle nameStyle; 65 | GUIStyle buttonStyle; 66 | GUIStyle iconStyle; 67 | GUIStyle iconLabelStyle; 68 | GUIStyle textBoxStyle; 69 | GUIStyle sliderIconStyle; 70 | GUIStyle sliderStyle; 71 | GUIStyle containerStyle; 72 | Texture2D buttonTexture; 73 | Texture2D iconTexture; 74 | Texture2D imageResizeIconTexture; 75 | Texture2D noPreviewTexture; 76 | GUIContent buttonContent; 77 | GUIContent iconContent; 78 | GUIContent imageResizeIconContent; 79 | GUIContent setLocationContent; 80 | Vector2 scrollPos; 81 | List sortedSearchData; 82 | List packageLocations = new List(); 83 | List localSearchTermList = new List(); 84 | Dictionary ownedPackages = new Dictionary(); 85 | 86 | [MenuItem("Tools/Synty Tools/" + EditorTitle)] 87 | private static void OpenWindow() 88 | { 89 | SyntyStoreSearch window = GetWindow(); 90 | window.titleContent = new GUIContent(EditorTitle, GetImageFromUrlAsTexture(IconTexture)); 91 | window.minSize = new Vector2(WindowWidth, WindowHeight); 92 | } 93 | 94 | private void OnEnable() 95 | { 96 | searchMessage = ""; 97 | AssetDatabase.importPackageCompleted += OnImportPackageCompleted; 98 | 99 | if (EditorPrefs.HasKey(ImageSizeEditorPrefsKey)) 100 | { 101 | imageSize = EditorPrefs.GetInt(ImageSizeEditorPrefsKey); 102 | } 103 | 104 | ownedPackages = LoadOwnedPackageData(); 105 | CalculatePackageStats(); 106 | 107 | packageLocations = LoadLocationData(); 108 | localSearchTermList = LoadSearchData(); 109 | } 110 | 111 | private Dictionary LoadOwnedPackageData() 112 | { 113 | var cacheFilePath = Path.Combine(CacheLocation, CacheFileName); 114 | 115 | if (File.Exists(cacheFilePath)) 116 | { 117 | string cacheJson = File.ReadAllText(cacheFilePath); 118 | var packagesDto = JsonUtility.FromJson(cacheJson); 119 | var packageDtos = packagesDto.packageDtos; 120 | 121 | if (packagesDto.Version != CurrentCacheVersion 122 | || packageDtos.Count == 0) 123 | { 124 | searchMessage = "Cache files need to be updated - set package location"; 125 | File.Delete(cacheFilePath); 126 | return new Dictionary(); 127 | } 128 | 129 | return ToModels(packageDtos); 130 | } 131 | 132 | return new Dictionary(); 133 | } 134 | 135 | private List LoadLocationData() 136 | { 137 | var locationsFilePath = Path.Combine(CacheLocation, LocationFileName); 138 | if (File.Exists(locationsFilePath)) 139 | { 140 | string locationsJson = File.ReadAllText(locationsFilePath); 141 | var folderPathsDto = JsonUtility.FromJson(locationsJson); 142 | return folderPathsDto.folderPaths; 143 | } 144 | 145 | return new List(); 146 | } 147 | 148 | private List LoadSearchData() 149 | { 150 | var searchFilePath = Path.Combine(CacheLocation, SearchFileName); 151 | if (File.Exists(searchFilePath)) 152 | { 153 | string searchJson = File.ReadAllText(searchFilePath); 154 | var fileSearchDataDto = JsonUtility.FromJson(searchJson); 155 | var searchTermDataListDto = fileSearchDataDto.PackageSearchTerms; 156 | return ToModels(searchTermDataListDto); 157 | } 158 | 159 | return new List(); 160 | } 161 | 162 | private void OnDestroy() 163 | { 164 | AssetDatabase.importPackageCompleted -= OnImportPackageCompleted; 165 | 166 | EditorPrefs.SetInt(ImageSizeEditorPrefsKey, imageSize); 167 | 168 | if (!cacheIsDirty) return; 169 | 170 | Directory.CreateDirectory(CacheLocation); 171 | 172 | SaveOwnedPackageData(); 173 | SaveLocationData(); 174 | SaveSearchData(); 175 | 176 | cacheIsDirty = false; 177 | } 178 | 179 | private void SaveOwnedPackageData() 180 | { 181 | var cacheFilePath = Path.Combine(CacheLocation, CacheFileName); 182 | var ownedPackagesDto = new OwnedPackagesDto() 183 | { 184 | packageDtos = FromModels(ownedPackages), 185 | Version = CurrentCacheVersion 186 | }; 187 | string cacheJson = JsonUtility.ToJson(ownedPackagesDto); 188 | File.WriteAllText(cacheFilePath, cacheJson); 189 | } 190 | 191 | private void SaveLocationData() 192 | { 193 | var locationsFilePath = Path.Combine(CacheLocation, LocationFileName); 194 | var folderPathsDto = new PackageFolderPathsDto() 195 | { 196 | folderPaths = packageLocations 197 | }; 198 | string locationsJson = JsonUtility.ToJson(folderPathsDto); 199 | File.WriteAllText(locationsFilePath, locationsJson); 200 | } 201 | 202 | private void SaveSearchData() 203 | { 204 | var textSearchFilePath = Path.Combine(CacheLocation, SearchFileName); 205 | var searchDataDto = new FileSearchDataListDto() 206 | { 207 | PackageSearchTerms = FromModels(localSearchTermList) 208 | }; 209 | string searchJson = JsonUtility.ToJson(searchDataDto); 210 | File.WriteAllText(textSearchFilePath, searchJson); 211 | } 212 | 213 | void OnInspectorUpdate() 214 | { 215 | Repaint(); 216 | } 217 | 218 | void OnGUI() 219 | { 220 | InitializeStyles(); 221 | 222 | GUILayout.BeginVertical(containerStyle); 223 | 224 | DisplayHeaderContent(); 225 | 226 | DisplaySearchContent(); 227 | 228 | if (sortedSearchData != null) 229 | { 230 | DisplaySearchResults(); 231 | } 232 | 233 | GUILayout.EndVertical(); 234 | } 235 | 236 | private void InitializeStyles() 237 | { 238 | imageStyle = new GUIStyle(GUI.skin.box) 239 | { 240 | margin = new RectOffset(ImageMargin, ImageMargin, ImageMargin, ImageMargin) 241 | }; 242 | imageStyle.normal.background = new Texture2D(imageSize, imageSize); 243 | 244 | nameStyle = new GUIStyle(GUI.skin.textArea) 245 | { 246 | fontSize = fontSize 247 | }; 248 | 249 | labelStyle = new GUIStyle() 250 | { 251 | fixedHeight = 25, 252 | fontSize = 15, 253 | alignment = TextAnchor.MiddleLeft, 254 | margin = new RectOffset(10, 10, 5, 5), 255 | padding = new RectOffset(10, 10, 2, 2) 256 | }; 257 | labelStyle.normal.textColor = Color.yellow; 258 | 259 | buttonStyle = new GUIStyle(GUI.skin.button) 260 | { 261 | fixedHeight = 25, 262 | fontSize = 14, 263 | margin = new RectOffset(10, 10, 5, 5), 264 | padding = new RectOffset(10, 10, 2, 2), 265 | stretchWidth = false 266 | }; 267 | 268 | iconStyle = new GUIStyle(GUI.skin.box) 269 | { 270 | fixedHeight = 20, 271 | fixedWidth = 20, 272 | margin = new RectOffset(0, 0, 8, 0) 273 | }; 274 | 275 | iconLabelStyle = new GUIStyle() 276 | { 277 | fixedHeight = 20, 278 | fontSize = 12, 279 | alignment = TextAnchor.MiddleLeft, 280 | margin = new RectOffset(5, 5, 7, 5) 281 | }; 282 | iconLabelStyle.normal.textColor = Color.white; 283 | 284 | textBoxStyle = new GUIStyle(GUI.skin.textField) 285 | { 286 | fixedHeight = 25, 287 | fontSize = 14, 288 | margin = new RectOffset(10, 10, 5, 5), 289 | padding = new RectOffset(10, 10, 2, 2), 290 | alignment = TextAnchor.MiddleLeft, 291 | stretchWidth = false 292 | }; 293 | 294 | sliderIconStyle = new GUIStyle(GUI.skin.box) 295 | { 296 | fixedHeight = 20, 297 | fixedWidth = 20, 298 | margin = new RectOffset(0, 5, 8, 5) 299 | }; 300 | 301 | sliderStyle = new GUIStyle(GUI.skin.horizontalSlider) 302 | { 303 | margin = new RectOffset(2, 2, 8, 0), 304 | alignment = TextAnchor.MiddleLeft 305 | }; 306 | 307 | containerStyle = new GUIStyle() 308 | { 309 | padding = new RectOffset(10, 10, 2, 2) 310 | }; 311 | } 312 | 313 | private void InitializeContent() 314 | { 315 | if (buttonTexture == null) 316 | { 317 | buttonTexture = GetImageFromUrlAsTexture(HeaderTexture); 318 | buttonContent = new GUIContent(buttonTexture, "Visit the online Synty Store"); 319 | } 320 | 321 | if (iconContent == null) 322 | { 323 | iconTexture = GetImageFromUrlAsTexture(IconTexture); 324 | iconContent = new GUIContent(iconTexture, "Package Count"); 325 | } 326 | 327 | if (imageResizeIconTexture == null) 328 | { 329 | imageResizeIconTexture = GetImageFromUrlAsTexture(ImageResizeIconTexture); 330 | imageResizeIconContent = new GUIContent(imageResizeIconTexture, "Resize Images"); 331 | } 332 | 333 | if (noPreviewTexture == null) 334 | { 335 | noPreviewTexture = GetImageFromUrlAsTexture(NoPreviewTexture); 336 | } 337 | 338 | setLocationContent = new GUIContent( 339 | SetLocationText, 340 | packageLocations.Count == 0 341 | ? "Specify the location of your Synty package files" 342 | : string.Join("\n", packageLocations)); 343 | } 344 | 345 | private void DisplayHeaderContent() 346 | { 347 | InitializeContent(); 348 | 349 | if (GUILayout.Button(buttonContent, GUILayout.Width(WindowWidth - 20), GUILayout.Height(BannerHeight))) 350 | { 351 | Application.OpenURL(StoreUrl); 352 | } 353 | 354 | GUILayout.BeginHorizontal(); 355 | 356 | GUILayout.Label($"Version {CurrentAppVersion} by PixelShenanigans"); 357 | 358 | GUILayout.EndHorizontal(); 359 | 360 | GUILayout.BeginHorizontal(); 361 | 362 | if (GUILayout.Button("Visit the online Synty Search page", buttonStyle)) 363 | { 364 | Application.OpenURL(SiteUrl); 365 | } 366 | 367 | if (GUILayout.Button(setLocationContent, buttonStyle)) 368 | { 369 | var path = EditorUtility.OpenFolderPanel(SetLocationText, packageLocations.LastOrDefault(), ""); 370 | #if UNITY_EDITOR_WIN 371 | path = path.Replace("/", @"\"); 372 | #endif 373 | FetchPackageFiles(path); 374 | } 375 | 376 | if (GUILayout.Button(iconContent, iconStyle)) 377 | { 378 | EditorUtility.DisplayDialog("Owned Package Information", GetPackageInformationText(), "Ok"); 379 | } 380 | 381 | GUILayout.Label( 382 | new GUIContent($"{importedPackageCount}/{ownedPackageCount}", "Imported/Owned"), 383 | iconLabelStyle); 384 | 385 | GUILayout.FlexibleSpace(); 386 | 387 | GUILayout.EndHorizontal(); 388 | } 389 | private void DisplaySearchContent() 390 | { 391 | GUILayout.BeginHorizontal(); 392 | 393 | GUI.SetNextControlName("search"); 394 | searchTerm = GUILayout.TextField(searchTerm, this.textBoxStyle, GUILayout.MinWidth(200)); 395 | 396 | bool performedSearch = GUILayout.Button("Search", this.buttonStyle); 397 | 398 | DisplayImageSizeSlider(); 399 | 400 | if (!string.IsNullOrEmpty(searchTerm) 401 | && Event.current.type == EventType.KeyUp 402 | && Event.current.keyCode == KeyCode.Return 403 | && GUI.GetNameOfFocusedControl() == "search") 404 | { 405 | performedSearch = true; 406 | } 407 | 408 | if (performedSearch) 409 | { 410 | sortedSearchData = GetSearchResults(searchTerm); 411 | } 412 | 413 | GUILayout.EndHorizontal(); 414 | 415 | GUILayout.Label(searchMessage, labelStyle); 416 | } 417 | 418 | private void DisplaySearchResults() 419 | { 420 | scrollPos = EditorGUILayout.BeginScrollView(scrollPos, true, true); 421 | 422 | for (int i = 0; i < sortedSearchData.Count; i++) 423 | { 424 | DisplayPackContent(sortedSearchData[i]); 425 | } 426 | 427 | EditorGUILayout.EndScrollView(); 428 | } 429 | 430 | private void DisplayPackContent(SearchPackageModel package) 431 | { 432 | GUIContent foldoutContent = new GUIContent(); 433 | 434 | foldoutContent.text = "- "; 435 | if (ownedPackages.Count > 0) 436 | { 437 | if (ownedPackages.ContainsKey(package.Name)) 438 | { 439 | if (ownedPackages[package.Name].IsCurrentlyImported) 440 | { 441 | foldoutContent.text += "Imported"; 442 | foldoutContent.tooltip = "Click an item to view it in your project!"; 443 | } 444 | else 445 | { 446 | foldoutContent.text += "Owned"; 447 | if (package.IsUnityPackage) 448 | { 449 | foldoutContent.tooltip = "Click an item to import the package!"; 450 | } 451 | else 452 | { 453 | foldoutContent.tooltip = "This package is a zip file and has no prefab previews"; 454 | } 455 | } 456 | } 457 | else 458 | { 459 | foldoutContent.text += "Not owned"; 460 | foldoutContent.tooltip = "Click an item to buy!"; 461 | } 462 | } 463 | 464 | foldoutContent.text = $"{package.Name} ({package.SearchItems.Count}) {foldoutContent.text}"; 465 | 466 | package.FoldoutIsExpanded = EditorGUILayout.BeginFoldoutHeaderGroup(package.FoldoutIsExpanded, foldoutContent); 467 | 468 | if (package.FoldoutIsExpanded) 469 | { 470 | DisplayFoldoutContent(package); 471 | } 472 | 473 | EditorGUILayout.EndFoldoutHeaderGroup(); 474 | } 475 | 476 | private void DisplayImageSizeSlider() 477 | { 478 | GUILayout.Button(imageResizeIconContent, sliderIconStyle); 479 | 480 | //GUILayout.Label(imageSize.ToString(), sliderLabelStyle); 481 | 482 | float size = GUILayout.HorizontalSlider(imageSize, imageWidthMin, imageWidthMax, sliderStyle, 483 | GUI.skin.horizontalSliderThumb, GUILayout.Width(150)); 484 | 485 | imageSize = Mathf.RoundToInt(size); 486 | fontSize = MapRange(imageSize, imageWidthMin, imageWidthMax, fontSizeMin, fontSizeMax); 487 | 488 | GUILayout.FlexibleSpace(); 489 | } 490 | 491 | private void DisplayFoldoutContent(SearchPackageModel searchPackage) 492 | { 493 | int horizontalImageSpace = 0; 494 | EditorGUILayout.BeginHorizontal(); 495 | 496 | foreach (var item in searchPackage.SearchItems) 497 | { 498 | if (item.ImageTexture == null) 499 | { 500 | if (item.ImagePath != null) 501 | { 502 | if (searchPackage.IsLocal) 503 | { 504 | item.ImageTexture = GetImageFromFileAsTexture(item.ImagePath); 505 | } 506 | else 507 | { 508 | item.ImageTexture = GetImageFromUrlAsTexture(SiteUrl + ApiPart + item.ImagePath); 509 | } 510 | } 511 | else 512 | { 513 | item.ImageTexture = noPreviewTexture; 514 | } 515 | } 516 | 517 | horizontalImageSpace = CalculateImagePosition(horizontalImageSpace); 518 | 519 | GUILayout.BeginVertical(); 520 | 521 | if (GUILayout.Button(new GUIContent(item.ImageTexture, item.Name), 522 | imageStyle, 523 | GUILayout.Width(imageSize), 524 | GUILayout.Height(imageSize), 525 | GUILayout.ExpandWidth(false))) 526 | { 527 | if (ownedPackages.ContainsKey(searchPackage.Name)) 528 | { 529 | var package = ownedPackages[searchPackage.Name]; 530 | if (package != null) 531 | { 532 | if (package.IsCurrentlyImported) 533 | { 534 | if (package.Assets.ContainsKey(item.Name)) 535 | { 536 | EditorGUIUtility.PingObject( 537 | AssetDatabase.LoadMainAssetAtPath( 538 | Path.Combine(AssetPathPrefix, package.Name, package.Assets[item.Name]))); 539 | } 540 | } 541 | else 542 | { 543 | if (package.Path.EndsWith(UnityPackageFileExt)) 544 | { 545 | AssetDatabase.ImportPackage(package.Path, true); 546 | } 547 | } 548 | } 549 | } 550 | else 551 | { 552 | Application.OpenURL(searchPackage.StoreUrl); 553 | } 554 | } 555 | 556 | GUILayout.TextArea(item.Name, nameStyle, GUILayout.Width(imageSize)); 557 | GUILayout.EndVertical(); 558 | } 559 | 560 | GUILayout.FlexibleSpace(); 561 | EditorGUILayout.EndHorizontal(); 562 | } 563 | 564 | private void FetchPackageFiles(string path) 565 | { 566 | if (string.IsNullOrWhiteSpace(path)) return; 567 | 568 | cacheIsDirty = true; 569 | 570 | searchMessage = "Processing Unity package files..."; 571 | 572 | CoroutineEditorUtility.StartCoroutine(LoadUnityPackagesRoutine(path)); 573 | } 574 | 575 | private IEnumerator LoadUnityPackagesRoutine(string path) 576 | { 577 | string defaultPackageLocation = GetDefaultUnityPackageLocation(); 578 | 579 | CoroutineEditorUtility.StartCoroutine(GetUnityPackagesRoutine(defaultPackageLocation, "polygon*" + UnityPackageFileExt)); 580 | CoroutineEditorUtility.StartCoroutine(GetUnityPackagesRoutine(defaultPackageLocation, "simple*" + UnityPackageFileExt, fullTextSearch: true)); 581 | 582 | if (!packageLocations.Contains(defaultPackageLocation)) 583 | { 584 | packageLocations.Add(defaultPackageLocation); 585 | } 586 | 587 | CoroutineEditorUtility.StartCoroutine(GetUnityPackagesRoutine(path, "polygon*" + UnityPackageFileExt)); 588 | CoroutineEditorUtility.StartCoroutine(GetUnityPackagesRoutine(path, "simple*" + UnityPackageFileExt, fullTextSearch: true)); 589 | CoroutineEditorUtility.StartCoroutine(GetZipFilesRoutine(path, "simple*.zip", fullTextSearch: true)); 590 | 591 | if (!packageLocations.Contains(path)) 592 | { 593 | packageLocations.Add(path); 594 | } 595 | 596 | yield return null; 597 | 598 | searchMessage = ""; 599 | } 600 | 601 | private IEnumerator GetUnityPackagesRoutine(string path, string pattern, bool fullTextSearch = false) 602 | { 603 | string[] files = Directory.GetFiles(path, pattern, SearchOption.AllDirectories); 604 | 605 | foreach (var file in files) 606 | { 607 | Debug.Log($"Scanning {file}"); 608 | 609 | var package = GetUnityPackageContents(file, fullTextSearch); 610 | if (!ownedPackages.ContainsKey(package.Name)) 611 | { 612 | ownedPackages.Add(package.Name, package); 613 | CalculatePackageStats(); 614 | } 615 | 616 | yield return null; 617 | } 618 | } 619 | 620 | private PackageModel GetUnityPackageContents(string filePath, bool fullTextSearch = false) 621 | { 622 | var assets = new Dictionary(); 623 | string firstAssetPath = null; 624 | int assetIndex = 0; 625 | var fileSearchTermData = new FileSearchTermData() 626 | { 627 | SearchTerms = new Dictionary>() 628 | }; 629 | string assetPath = string.Empty; 630 | string assetName = string.Empty; 631 | 632 | using (var fileStream = File.Open(filePath, FileMode.Open)) 633 | { 634 | MemoryStream memoryStream = TarFileUtility.DecompressGzFile(fileStream); 635 | using (MemoryStream stream = new MemoryStream(memoryStream.ToArray())) 636 | { 637 | foreach (var (fileName, fileData) in TarFileUtility.ReadFiles(stream)) 638 | { 639 | if (fileName.EndsWith("pathname")) 640 | { 641 | assetPath = Encoding.ASCII.GetString(fileData).Trim('\n', '0'); 642 | 643 | if (assetPath.EndsWith(PrefabFileExt)) 644 | { 645 | //TODO: Contains? 646 | if (assetPath.StartsWith(AssetPathPrefix)) 647 | { 648 | assetPath = assetPath.Remove(0, AssetPathPrefix.Length); 649 | if (firstAssetPath == null) 650 | { 651 | firstAssetPath = assetPath; 652 | fileSearchTermData.PackageName = GetPackageName(firstAssetPath); 653 | } 654 | assetPath = RemovePackageName(assetPath); 655 | } 656 | 657 | assetName = GetAssetName(assetPath); 658 | if (!assets.ContainsKey(assetName)) 659 | { 660 | assets.Add(assetName, assetPath); 661 | 662 | if (fullTextSearch) 663 | { 664 | AddTextSearch(fileSearchTermData, assetName, assetIndex); 665 | } 666 | 667 | assetIndex++; 668 | } 669 | // else duplicate asset name 670 | } 671 | } 672 | else if (fullTextSearch 673 | && assetPath.EndsWith(PrefabFileExt) 674 | && fileName.EndsWith("preview.png")) 675 | { 676 | SavePreviewImage(fileSearchTermData.PackageName, assetName, fileData); 677 | } 678 | } 679 | 680 | if (fullTextSearch) 681 | { 682 | AddPackageSearch(fileSearchTermData); 683 | } 684 | } 685 | } 686 | 687 | return new PackageModel() 688 | { 689 | Name = GetPackageName(firstAssetPath), 690 | Path = filePath, 691 | Assets = assets, 692 | IsCurrentlyImported = IsCurrentlyImported(firstAssetPath) 693 | }; 694 | } 695 | 696 | private string SavePreviewImage(string packageName, string assetName, byte[] fileData) 697 | { 698 | string previewFilePath = string.Empty; 699 | 700 | if (!string.IsNullOrEmpty(packageName)) 701 | { 702 | previewFilePath = Path.Combine(CacheLocation, PreviewsFolderName, packageName); 703 | Directory.CreateDirectory(previewFilePath); 704 | 705 | previewFilePath = Path.Combine(previewFilePath, assetName + ".png"); 706 | if (!File.Exists(previewFilePath)) 707 | { 708 | File.WriteAllBytes(previewFilePath, fileData); 709 | } 710 | } 711 | 712 | return previewFilePath; 713 | } 714 | 715 | private IEnumerator GetZipFilesRoutine(string path, string pattern, bool fullTextSearch = false) 716 | { 717 | string[] files = Directory.GetFiles(path, "simple*.zip", SearchOption.AllDirectories); 718 | 719 | foreach (var file in files) 720 | { 721 | Debug.Log($"Scanning {file}"); 722 | 723 | var package = GetZipFileContents(file); 724 | if (!ownedPackages.ContainsKey(package.Name)) 725 | { 726 | ownedPackages.Add(package.Name, package); 727 | CalculatePackageStats(); 728 | } 729 | 730 | yield return null; 731 | } 732 | } 733 | 734 | private PackageModel GetZipFileContents(string filePath) 735 | { 736 | var assets = new Dictionary(); 737 | string firstAssetPath = null; 738 | 739 | using (var zip = ZipFile.Open(filePath, ZipArchiveMode.Read)) 740 | { 741 | var fileSearchTermData = new FileSearchTermData() 742 | { 743 | SearchTerms = new Dictionary>() 744 | }; 745 | 746 | int assetIndex = 0; 747 | 748 | foreach (var entry in zip.Entries) 749 | { 750 | string assetPath = entry.FullName; 751 | 752 | if (assetPath.EndsWith(PrefabFileExt)) 753 | { 754 | assetPath = GetPathUnderAssets(assetPath); 755 | 756 | if (string.IsNullOrWhiteSpace(fileSearchTermData.PackageName)) 757 | { 758 | firstAssetPath = assetPath; 759 | fileSearchTermData.PackageName = GetPackageName(assetPath); 760 | } 761 | 762 | assetPath = RemovePackageName(assetPath); 763 | 764 | string assetName = GetAssetName(assetPath); 765 | if (!assets.ContainsKey(assetName)) 766 | { 767 | assets.Add(assetName, assetPath); 768 | 769 | AddTextSearch(fileSearchTermData, assetName, assetIndex); 770 | 771 | assetIndex++; 772 | } 773 | //else duplicate asset name 774 | } 775 | } 776 | 777 | AddPackageSearch(fileSearchTermData); 778 | } 779 | 780 | return new PackageModel() 781 | { 782 | Name = GetPackageName(firstAssetPath), 783 | Path = filePath, 784 | Assets = assets, 785 | IsCurrentlyImported = IsCurrentlyImported(firstAssetPath) 786 | }; 787 | } 788 | 789 | private void AddTextSearch(FileSearchTermData fileSearchTermData, string assetName, int assetIndex) 790 | { 791 | var words = assetName.SplitCamelCase(); 792 | 793 | foreach (var word in words) 794 | { 795 | if (word.Length > 2) 796 | { 797 | string wordToAdd = word.ToLower(); 798 | 799 | if (fileSearchTermData.SearchTerms.TryGetValue(wordToAdd, out var assetList)) 800 | { 801 | assetList.Add(assetIndex); 802 | } 803 | else 804 | { 805 | assetList = new List(); 806 | assetList.Add(assetIndex); 807 | fileSearchTermData.SearchTerms.Add(wordToAdd, assetList); 808 | } 809 | } 810 | } 811 | } 812 | 813 | private List GetSearchResults(string term) 814 | { 815 | searchMessage = ""; 816 | 817 | if (term.Length >= 3) 818 | { 819 | var searchData = GetSearchResultsFromWeb(term); 820 | 821 | var localSearchData = GetSearchResultsFromLocal(term); 822 | 823 | if (searchData != null) 824 | { 825 | if (localSearchData != null) 826 | { 827 | searchData.AddRange(localSearchData); 828 | } 829 | } 830 | else 831 | { 832 | searchData = localSearchData; 833 | } 834 | 835 | searchMessage = $"Search returned {searchData.Sum(x => x.SearchItems.Count)} results."; 836 | 837 | return searchData; 838 | } 839 | else 840 | { 841 | searchMessage = "Search term must have at least 3 characters."; 842 | } 843 | 844 | return null; 845 | } 846 | 847 | private List GetSearchResultsFromWeb(string term) 848 | { 849 | string url = SiteUrl + ApiPart + SearchPart + term; 850 | using (WebClient client = new WebClient()) 851 | { 852 | client.Headers.Add("Referer", SiteUrl); 853 | byte[] data = client.DownloadData(url); 854 | if (data != null) 855 | { 856 | var searchData = JsonUtility.FromJson(Encoding.ASCII.GetString(data)); 857 | if (searchData.success == true) 858 | { 859 | var webSearchData = SortWebSearchResults(searchData); 860 | return webSearchData; 861 | } 862 | } 863 | } 864 | 865 | searchMessage = "Unable to get search results, please try later."; 866 | 867 | return null; 868 | } 869 | 870 | private List GetSearchResultsFromLocal(string term) 871 | { 872 | List packages = new List(); 873 | 874 | foreach (var package in localSearchTermList) 875 | { 876 | List results = package.SearchTerms.Keys 877 | .Where(x => x.Contains(term)) 878 | .SelectMany(x => package.SearchTerms[x]).ToList(); 879 | 880 | if (results.Count > 0) 881 | { 882 | var ownedPackage = ownedPackages[package.PackageName]; 883 | packages.Add(new SearchPackageModel 884 | { 885 | Name = package.PackageName, 886 | SearchItems = results.Select(x => { 887 | var assetName = ownedPackage.Assets.ElementAt(x).Key; 888 | return new SearchItemModel 889 | { 890 | Name = assetName, 891 | ImagePath = GetPreviewImagePath(package.PackageName, assetName), 892 | ImageTexture = null 893 | }; 894 | }).ToList(), 895 | IsLocal = true, 896 | IsUnityPackage = ownedPackage.Path.EndsWith(UnityPackageFileExt) 897 | }); 898 | } 899 | } 900 | 901 | return packages; 902 | } 903 | 904 | private string GetPreviewImagePath(string packageName, string assetName) 905 | { 906 | string path = Path.Combine(CacheLocation, PreviewsFolderName, packageName, assetName + ".png"); 907 | if (File.Exists(path)) 908 | { 909 | return path; 910 | } 911 | 912 | return null; 913 | } 914 | 915 | private List SortWebSearchResults(WebSearchData results) 916 | { 917 | List packages = new List(); 918 | 919 | var packageNames = results.data 920 | .Select(x => x.pack) 921 | .Distinct() 922 | .ToList(); 923 | 924 | foreach (var packageName in packageNames) 925 | { 926 | packages.Add(new SearchPackageModel() 927 | { 928 | Name = packageName, 929 | StoreUrl = results.data 930 | .Where(x => x.pack == packageName) 931 | .Select(x => x.packStoreUrl) 932 | .First(), 933 | FoldoutIsExpanded = false, 934 | SearchItems = results.data 935 | .Where(x => x.pack == packageName) 936 | .Select(x => new SearchItemModel() 937 | { 938 | Name = x.name, 939 | ImagePath = x.imagePath, 940 | ImageTexture = null 941 | }) 942 | .ToList(), 943 | IsLocal = false, 944 | IsUnityPackage = true 945 | }); 946 | } 947 | 948 | return packages; 949 | } 950 | 951 | private void OnImportPackageCompleted(string packageName) 952 | { 953 | Debug.Log($"Imported {packageName}"); 954 | 955 | if (ownedPackages.TryGetValue(packageName, out var package)) 956 | { 957 | package.IsCurrentlyImported = true; 958 | } 959 | } 960 | 961 | private void AddPackageSearch(FileSearchTermData searchTermData) 962 | { 963 | int index = localSearchTermList.FindIndex(x => x.PackageName == searchTermData.PackageName); 964 | if (index != -1) 965 | { 966 | localSearchTermList.RemoveAt(index); 967 | } 968 | 969 | localSearchTermList.Add(searchTermData); 970 | } 971 | 972 | private string GetPathUnderAssets(string assetPath) 973 | { 974 | int index = assetPath.IndexOf(AssetPathPrefix) + AssetPathPrefix.Length; 975 | return assetPath.Substring(index); 976 | } 977 | 978 | private string RemovePackageName(string assetPath) 979 | { 980 | int index = assetPath.IndexOf("/"); 981 | return assetPath.Remove(0, index + 1); 982 | } 983 | 984 | private string GetAssetName(string assetPath) 985 | { 986 | int startIndex = assetPath.LastIndexOf("/") + 1; 987 | int endIndex = assetPath.LastIndexOf("."); 988 | return assetPath.Substring(startIndex, endIndex - startIndex); 989 | } 990 | 991 | private string GetPackageName(string assetPath) 992 | { 993 | int endIndex = assetPath.IndexOf("/"); 994 | return assetPath.Substring(0, endIndex); 995 | } 996 | 997 | private bool IsCurrentlyImported(string assetPath) 998 | { 999 | string importedAssetPath = Path.Combine(Application.dataPath, assetPath); 1000 | return File.Exists(importedAssetPath); 1001 | } 1002 | 1003 | private bool IsCurrentlyImported(string name, string assetPath) 1004 | { 1005 | string importedAssetPath = Path.Combine(Application.dataPath, name, assetPath); 1006 | return File.Exists(importedAssetPath); 1007 | } 1008 | 1009 | private void CalculatePackageStats() 1010 | { 1011 | ownedPackageCount = ownedPackages.Count; 1012 | importedPackageCount = ownedPackages.Values.Count(x => x.IsCurrentlyImported); 1013 | } 1014 | 1015 | private int CalculateImagePosition(int position) 1016 | { 1017 | position += imageSize + ImageMargin; 1018 | if (position > (base.position.width - imageWidthMin - 1)) 1019 | { 1020 | position = imageSize + ImageMargin; 1021 | GUILayout.FlexibleSpace(); 1022 | EditorGUILayout.EndHorizontal(); 1023 | EditorGUILayout.BeginHorizontal(); 1024 | } 1025 | 1026 | return position; 1027 | } 1028 | 1029 | private string GetPackageInformationText() 1030 | { 1031 | StringBuilder sb = new StringBuilder(); 1032 | 1033 | foreach (var packageName in ownedPackages.Keys) 1034 | { 1035 | var package = ownedPackages[packageName]; 1036 | int count = package.Assets.Count; 1037 | string imported = package.IsCurrentlyImported ? "*" : ""; 1038 | sb.Append($"{imported}{packageName} ({count} prefabs)\n"); 1039 | } 1040 | 1041 | return sb.ToString(); 1042 | } 1043 | 1044 | private static Texture2D GetImageFromUrlAsTexture(string url) 1045 | { 1046 | Texture2D texture = new Texture2D(2, 2); 1047 | 1048 | using (WebClient client = new WebClient()) 1049 | { 1050 | byte[] data = client.DownloadData(url); 1051 | texture.LoadImage(data); 1052 | } 1053 | 1054 | return texture; 1055 | } 1056 | 1057 | private static Texture2D GetImageFromFileAsTexture(string path) 1058 | { 1059 | Texture2D texture = new Texture2D(2, 2); 1060 | 1061 | byte[] data = File.ReadAllBytes(path); 1062 | texture.LoadImage(data); 1063 | 1064 | return texture; 1065 | } 1066 | 1067 | private string GetDefaultUnityPackageLocation() 1068 | { 1069 | #if UNITY_EDITOR_OSX 1070 | string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); 1071 | return Path.Combine(appDataPath, @"Library/Unity/Asset Store-5.x/Synty Studios"; 1072 | #else 1073 | string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); 1074 | return Path.Combine(appDataPath, @"Unity\Asset Store-5.x\Synty Studios"); 1075 | #endif 1076 | } 1077 | 1078 | public string GetCacheLocation() 1079 | { 1080 | #if UNITY_EDITOR_OSX 1081 | string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); 1082 | return Path.Combine(appDataPath, @"Library/SyntySearch"; 1083 | #else 1084 | string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); 1085 | return Path.Combine(appDataPath, @"SyntySearch"); 1086 | #endif 1087 | } 1088 | 1089 | private int MapRange(int value, int minA, int maxA, int minB, int maxB) 1090 | { 1091 | float aValue = Mathf.InverseLerp(minA, maxA, value); 1092 | float bValue = Mathf.Lerp(minB, maxB, aValue); 1093 | return Mathf.RoundToInt(bValue); 1094 | } 1095 | 1096 | private Dictionary ToModels(List packageDtos) 1097 | { 1098 | Dictionary packages = new Dictionary(); 1099 | 1100 | foreach (var packageDto in packageDtos) 1101 | { 1102 | packages.Add(packageDto.Name, ToModel(packageDto)); 1103 | } 1104 | 1105 | return packages; 1106 | } 1107 | 1108 | private PackageModel ToModel(PackageDto packageDto) 1109 | { 1110 | var package = new PackageModel() 1111 | { 1112 | Name = packageDto.Name, 1113 | Path = packageDto.Path, 1114 | Assets = packageDto.Assets 1115 | .Select(x => x.Split('|')) 1116 | .ToDictionary(s => s[0], s => s[1]) 1117 | }; 1118 | 1119 | package.IsCurrentlyImported = IsCurrentlyImported(package.Name, package.Assets.Values.First()); 1120 | 1121 | return package; 1122 | } 1123 | 1124 | private List FromModels(Dictionary packages) 1125 | { 1126 | List packageDtos = new List(); 1127 | 1128 | foreach (var package in packages.Values) 1129 | { 1130 | packageDtos.Add(FromModel(package)); 1131 | } 1132 | 1133 | return packageDtos; 1134 | } 1135 | 1136 | private PackageDto FromModel(PackageModel package) 1137 | { 1138 | return new PackageDto() 1139 | { 1140 | Name = package.Name, 1141 | Path = package.Path, 1142 | Assets = package.Assets 1143 | .Select(x => string.Join("|", x.Key, x.Value)) 1144 | .ToList() 1145 | }; 1146 | } 1147 | 1148 | private List ToModels(List searchTermDataDtos) 1149 | { 1150 | List searchTermDataList = new List(); 1151 | 1152 | foreach (var searchTermDataDto in searchTermDataDtos) 1153 | { 1154 | searchTermDataList.Add(ToModel(searchTermDataDto)); 1155 | } 1156 | 1157 | return searchTermDataList; 1158 | } 1159 | 1160 | private FileSearchTermData ToModel(FileSearchTermDataDto searchTermDto) 1161 | { 1162 | var searchTermDataList = new FileSearchTermData() 1163 | { 1164 | PackageName = searchTermDto.PackageName, 1165 | SearchTerms = searchTermDto.SearchTerms 1166 | .Select(x => x.Split('|')) 1167 | .ToDictionary( 1168 | s => s[0], 1169 | s => s[1].Split(',') 1170 | .Select(int.Parse) 1171 | .ToList()) 1172 | }; 1173 | 1174 | return searchTermDataList; 1175 | } 1176 | 1177 | private List FromModels(List searchTermDataList) 1178 | { 1179 | List searchTermDtos = new List(); 1180 | 1181 | foreach (var searchTermData in searchTermDataList) 1182 | { 1183 | searchTermDtos.Add(FromModel(searchTermData)); 1184 | } 1185 | 1186 | return searchTermDtos; 1187 | } 1188 | 1189 | private FileSearchTermDataDto FromModel(FileSearchTermData searchTermData) 1190 | { 1191 | return new FileSearchTermDataDto() 1192 | { 1193 | PackageName = searchTermData.PackageName, 1194 | SearchTerms = searchTermData.SearchTerms 1195 | .Select(x => string.Join("|", x.Key, string.Join(",", x.Value))) 1196 | .ToList() 1197 | }; 1198 | } 1199 | } 1200 | 1201 | [Serializable] 1202 | public class WebSearchData 1203 | { 1204 | public bool success; 1205 | public WebSearchItem[] data; 1206 | } 1207 | 1208 | [Serializable] 1209 | public class WebSearchItem 1210 | { 1211 | public string name; 1212 | public string pack; 1213 | public string packStoreUrl; 1214 | public string imagePath; 1215 | } 1216 | 1217 | public class SearchPackageModel 1218 | { 1219 | public string Name; 1220 | public string StoreUrl; 1221 | public bool FoldoutIsExpanded; 1222 | public List SearchItems; 1223 | public bool IsLocal; 1224 | public bool IsUnityPackage; 1225 | } 1226 | 1227 | public class SearchItemModel 1228 | { 1229 | public string Name; 1230 | public string ImagePath; 1231 | public Texture2D ImageTexture; 1232 | } 1233 | 1234 | public class PackageModel 1235 | { 1236 | public string Name; 1237 | public string Path; 1238 | public Dictionary Assets; 1239 | public bool IsCurrentlyImported; 1240 | } 1241 | 1242 | [Serializable] 1243 | public class PackageDto 1244 | { 1245 | public string Name; 1246 | public string Path; 1247 | public List Assets; 1248 | } 1249 | 1250 | [Serializable] 1251 | public class PackageFolderPathsDto 1252 | { 1253 | public List folderPaths; 1254 | } 1255 | 1256 | [Serializable] 1257 | public class OwnedPackagesDto 1258 | { 1259 | public string Version; 1260 | public List packageDtos; 1261 | } 1262 | 1263 | [Serializable] 1264 | public class FileSearchDataListDto 1265 | { 1266 | public List PackageSearchTerms; 1267 | } 1268 | 1269 | [Serializable] 1270 | public class FileSearchTermDataDto 1271 | { 1272 | public string PackageName; 1273 | public List SearchTerms; 1274 | } 1275 | 1276 | public class FileSearchTermData 1277 | { 1278 | public string PackageName; 1279 | public Dictionary> SearchTerms; 1280 | } 1281 | 1282 | public class FileSearchData 1283 | { 1284 | public string Name; 1285 | public string PackageName; 1286 | } 1287 | } --------------------------------------------------------------------------------