├── Extra
├── classdata.tpk
├── unity_default_resources
└── empty.ttf
├── .gitmodules
├── .gitignore
├── CLI.Unity.Trimmer
├── .gitignore
├── CLI.Unity.Trimmer.sln.DotSettings
├── CLI.Unity.Trimmer.csproj.DotSettings
├── CLI.Unity.Trimmer.sln
├── Source
│ ├── Program.cs
│ └── Commands
│ │ ├── Version.cs
│ │ └── Trim.cs
└── CLI.Unity.Trimmer.csproj
├── LICENSE
├── README.md
└── .gitattributes
/Extra/classdata.tpk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/starburst997/Unity.Trimmer/HEAD/Extra/classdata.tpk
--------------------------------------------------------------------------------
/Extra/unity_default_resources:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/starburst997/Unity.Trimmer/HEAD/Extra/unity_default_resources
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "Dependencies/AssetsTools.NET"]
2 | path = Dependencies/AssetsTools.NET
3 | url = git@github.com:starburst997/AssetsTools.NET.git
4 |
--------------------------------------------------------------------------------
/Extra/empty.ttf:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:0083416ffb54c21dee698d5b098c2060b50b85636a04026d40e21a95dc427357
3 | size 1380
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | logs/
3 | .vs/
4 | .gradle/
5 | .DS_Store
6 | DevAuthToken.txt*
7 | *.user
8 |
9 | [Tt]emp/
10 | Logs/
11 | [Oo]bj/
12 | [Bb]in/
13 | [Pp]ublish/
14 | [Bb]inMono/
--------------------------------------------------------------------------------
/CLI.Unity.Trimmer/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | logs/
3 | .idea/
4 | .vs/
5 | .gradle/
6 | .DS_Store
7 | DevAuthToken.txt*
8 |
9 | [Tt]emp/
10 | Logs/
11 | [Oo]bj/
12 | [Bb]in/
13 | [Pp]ublish*/
14 |
15 | settings
--------------------------------------------------------------------------------
/CLI.Unity.Trimmer/CLI.Unity.Trimmer.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | True
--------------------------------------------------------------------------------
/CLI.Unity.Trimmer/CLI.Unity.Trimmer.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | True
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Jean-Denis Boivin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Unity.Trimmer
2 | Reduce size of unity asset bundled by default
3 |
4 | Unity include a file by default called `unity_default_resources` which includes a bunch of unnecessary assets.
5 |
6 | There is no easy way to remove it and it doesn't seems like the build process attempts to remove unnecessary assets from it either, so the file is simply copied as is... All 3.36MB (WebGL / 2021.1.28f1) of it... Which is kind of a lot for web games.
7 |
8 | For example, a 2048x1024 2MB Texture called "UnitySplash-cube" is include in it, the default unity splash screen, even if you overwrite it in Project Setting.
9 |
10 | It is very disappointing to see this, especially since the WebGL team at Unity is making great progress attempting to reduce the size of the code and yet they leave an unused 2MB Texture by default with no way to remove it.
11 |
12 | This tools use [AssetsTools.NET](https://github.com/nesrak1/AssetsTools.NET) to read that file, replace all `Texture2D` inside of it and write a new trimmed copy of the file.
13 |
14 | Replace that file in Unity Editor folder: `C:\Program Files\Unity\Hub\Editor\XXXXXXX\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\data\unity_default_resources`
15 |
16 | Build your project and notice how you reduce the output size by 3MB!
17 |
18 | The file is also included in the "Extra" folder for those that don't want to build the program.
--------------------------------------------------------------------------------
/CLI.Unity.Trimmer/CLI.Unity.Trimmer.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBD}") = "CLI.Unity.Trimmer", "CLI.Unity.Trimmer.csproj", "{CCACCC96-9AF6-4048-AE42-7FE602C12EE4}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetsTools.NET", "..\Dependencies\AssetsTools.NET\AssetTools.NET\AssetsTools.NET.csproj", "{98998C85-C405-4F11-A458-538B2C7F4596}"
6 | EndProject
7 | Global
8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
9 | Debug|Any CPU = Debug|Any CPU
10 | Release|Any CPU = Release|Any CPU
11 | EndGlobalSection
12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
13 | {CCACCC96-9AF6-4048-AE42-7FE602C12EE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
14 | {CCACCC96-9AF6-4048-AE42-7FE602C12EE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
15 | {CCACCC96-9AF6-4048-AE42-7FE602C12EE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
16 | {CCACCC96-9AF6-4048-AE42-7FE602C12EE4}.Release|Any CPU.Build.0 = Release|Any CPU
17 | {98998C85-C405-4F11-A458-538B2C7F4596}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
18 | {98998C85-C405-4F11-A458-538B2C7F4596}.Debug|Any CPU.Build.0 = Debug|Any CPU
19 | {98998C85-C405-4F11-A458-538B2C7F4596}.Release|Any CPU.ActiveCfg = Release|Any CPU
20 | {98998C85-C405-4F11-A458-538B2C7F4596}.Release|Any CPU.Build.0 = Release|Any CPU
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/CLI.Unity.Trimmer/Source/Program.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 | using System.CommandLine.Invocation;
3 | using System.Threading.Tasks;
4 | using Unity.Trimmer.Cli.Commands;
5 | using Version = Unity.Trimmer.Cli.Commands.Version;
6 |
7 | namespace unity.trimmer.cli
8 | {
9 | internal static class Program
10 | {
11 | private static async Task Main(params string[] args)
12 | {
13 | // Parse arguments
14 | RootCommand rootCommand = new RootCommand("Unity Trimmer CLI Tools.");
15 |
16 | // Trim
17 | var trimCommand = new Command("trim", "Trim an asset bundle (ex: 'unity_default_resources')");
18 | trimCommand.AddAlias("n");
19 | trimCommand.AddArgument(new Argument("input"));
20 | trimCommand.AddArgument(new Argument("output"));
21 | trimCommand.AddOption(new Option(new []{"--classdata", "--c"}, () => "", "'classdata.tpk' file path"));
22 | trimCommand.AddOption(new Option(new []{"--font", "--f"}, () => "", "Empty font file path"));
23 | trimCommand.Handler = CommandHandler.Create(Trim.Execute);
24 | rootCommand.AddCommand(trimCommand);
25 |
26 | // Version
27 | var versionCommand = new Command("version", "Print version information");
28 | versionCommand.AddOption(new Option(new []{"--json", "--j"}, () => false, "JSON Output"));
29 | versionCommand.AddOption(new Option(new []{"--show-system", "--s"}, () => false, "Show system assemblies as well"));
30 | versionCommand.Handler = CommandHandler.Create(Version.Execute);
31 | rootCommand.AddCommand(versionCommand);
32 |
33 | return await rootCommand.InvokeAsync(args);
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/CLI.Unity.Trimmer/CLI.Unity.Trimmer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | Unity.Trimmer.Cli
6 | false
7 | win-x64;linux-x64;osx-x64
8 | Debug;Release
9 | AnyCPU
10 | 2022.0
11 | 2022.0
12 | 2022.0
13 | 2022.0
14 | 2022.0
15 | net6.0
16 |
17 |
18 |
19 | true
20 |
21 |
22 |
23 | true
24 | portable
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | 2.0.0-beta1.20574.7
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/CLI.Unity.Trimmer/Source/Commands/Version.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Reflection;
4 |
5 | namespace Unity.Trimmer.Cli.Commands
6 | {
7 | public static class Version
8 | {
9 | public static void Execute(bool json, bool showSystem)
10 | {
11 | AppDomain currentDomain = AppDomain.CurrentDomain;
12 | Assembly[] assemblies = currentDomain.GetAssemblies();
13 |
14 | var assembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly();
15 | var dict = new Dictionary();
16 |
17 | var info = assembly.GetName();
18 | if (info.Name != null) dict[info.Name] = info;
19 |
20 | GetAssemblies(assembly, dict, showSystem);
21 |
22 | if (json)
23 | {
24 | var assemblyVersionAttribute = assembly.GetCustomAttribute();
25 | Console.WriteLine($"{{\"success\": true, \"version\": \"{assemblyVersionAttribute?.InformationalVersion ?? assembly.GetName().Version.ToString()}\", \"versions\": [");
26 | }
27 |
28 | var first = true;
29 | foreach (var pair in dict)
30 | {
31 | var comma = first ? ' ' : ',';
32 |
33 | if (json)
34 | Console.WriteLine($"{comma}{{\"name\": \"{pair.Key}\", \"version\": \"{pair.Value.Version}\"}}");
35 | else
36 | Console.WriteLine($"{pair.Key} : {pair.Value.Version}");
37 |
38 | first = false;
39 | }
40 |
41 | if (json)
42 | Console.WriteLine("]}");
43 | }
44 |
45 | private static bool IsSystem(string name)
46 | {
47 | return name.StartsWith("Microsoft") ||
48 | name.StartsWith("System") ||
49 | name.StartsWith("netstandard") ||
50 | name.StartsWith("mscorlib");
51 | }
52 |
53 | private static void GetAssemblies(Assembly assembly, Dictionary dict, bool allowSystem = false)
54 | {
55 | foreach (var child in assembly.GetReferencedAssemblies())
56 | {
57 | if (child.Name != null && (allowSystem || !IsSystem(child.Name)) && !dict.ContainsKey(child.Name))
58 | {
59 | dict[child.Name] = child;
60 |
61 | try
62 | {
63 | var childAssembly = Assembly.Load(child);
64 | GetAssemblies(childAssembly, dict, allowSystem);
65 | }
66 | catch (Exception _)
67 | {
68 | // Nothing
69 | }
70 | }
71 | }
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | ## Text files ##
3 | *.mat text
4 | *.shader text
5 | *.meta text
6 | *.md5 text
7 | *.compute text
8 | *.md text
9 | *.prefab text
10 | *.mixer text
11 | *.ShaderGraph text
12 | *.anim text
13 | *.asmdef text
14 | *.json text
15 | *.overrideController text
16 | *.cginc text
17 | *.hlsl text
18 | *.giparams text
19 | *.controller text
20 | *.mask text
21 | *.playable text
22 | *.asset text
23 | ## Binaries ##
24 | # Image
25 | *.jpg filter=lfs diff=lfs merge=lfs -text
26 | *.jpeg filter=lfs diff=lfs merge=lfs -text
27 | *.png filter=lfs diff=lfs merge=lfs -text
28 | *.gif filter=lfs diff=lfs merge=lfs -text
29 | *.psd filter=lfs diff=lfs merge=lfs -text
30 | *.ai filter=lfs diff=lfs merge=lfs -text
31 | *.tga filter=lfs diff=lfs merge=lfs -text
32 | *.TGA filter=lfs diff=lfs merge=lfs -text
33 | *.tif filter=lfs diff=lfs merge=lfs -text
34 | *.TIF filter=lfs diff=lfs merge=lfs -text
35 | *.bmp filter=lfs diff=lfs merge=lfs -text
36 | *.tiff filter=lfs diff=lfs merge=lfs -text
37 | # Audio
38 | *.mp3 filter=lfs diff=lfs merge=lfs -text
39 | *.wav filter=lfs diff=lfs merge=lfs -text
40 | *.ogg filter=lfs diff=lfs merge=lfs -text
41 | *.mod filter=lfs diff=lfs merge=lfs -text
42 | *.s3m filter=lfs diff=lfs merge=lfs -text
43 | *.xm filter=lfs diff=lfs merge=lfs -text
44 | *.mo3 filter=lfs diff=lfs merge=lfs -text
45 | *.it filter=lfs diff=lfs merge=lfs -text
46 | *.note filter=lfs diff=lfs merge=lfs -text
47 | # Video
48 | *.mp4 filter=lfs diff=lfs merge=lfs -text
49 | *.mov filter=lfs diff=lfs merge=lfs -text
50 | *.ogv filter=lfs diff=lfs merge=lfs -text
51 | # 3D Object
52 | *.FBX filter=lfs diff=lfs merge=lfs -text
53 | *.fbx filter=lfs diff=lfs merge=lfs -text
54 | *.blend filter=lfs diff=lfs merge=lfs -text
55 | *.obj filter=lfs diff=lfs merge=lfs -text
56 | *.OBJ filter=lfs diff=lfs merge=lfs -text
57 | *.gltf filter=lfs diff=lfs merge=lfs -text
58 | # ETC
59 | *.a filter=lfs diff=lfs merge=lfs -text
60 | *.ma filter=lfs diff=lfs merge=lfs -text
61 | *.hdr filter=lfs diff=lfs merge=lfs -text
62 | *.exr filter=lfs diff=lfs merge=lfs -text
63 | *.tga filter=lfs diff=lfs merge=lfs -text
64 | *.pdf filter=lfs diff=lfs merge=lfs -text
65 | *.zip filter=lfs diff=lfs merge=lfs -text
66 | *.dll filter=lfs diff=lfs merge=lfs -text
67 | *.pdb filter=lfs diff=lfs merge=lfs -text
68 | *.exe filter=lfs diff=lfs merge=lfs -text
69 | *.unitypackage filter=lfs diff=lfs merge=lfs -text
70 | *.aif filter=lfs diff=lfs merge=lfs -text
71 | *.ttf filter=lfs diff=lfs merge=lfs -text
72 | *.rns filter=lfs diff=lfs merge=lfs -text
73 | *.reason filter=lfs diff=lfs merge=lfs -text
74 | *.lxo filter=lfs diff=lfs merge=lfs -text
75 | *.so filter=lfs diff=lfs merge=lfs -text
76 | *.bundle filter=lfs diff=lfs merge=lfs -text
77 | *.mdb filter=lfs diff=lfs merge=lfs -text
78 | *.pdt filter=lfs diff=lfs merge=lfs -text
79 | *.bytes filter=lfs diff=lfs merge=lfs -text
80 | *.sbsar filter=lfs diff=lfs merge=lfs -text
81 | *.unity filter=lfs diff=lfs merge=lfs -text
82 | *.entities filter=lfs diff=lfs merge=lfs -text
83 | *.bin filter=lfs diff=lfs merge=lfs -text
84 | *.tpsheet filter=lfs diff=lfs merge=lfs -text
85 | *.wasm filter=lfs diff=lfs merge=lfs -text
86 | *.unityweb filter=lfs diff=lfs merge=lfs -text
87 | *.note filter=lfs diff=lfs merge=lfs -text
88 | *.s3m filter=lfs diff=lfs merge=lfs -text
89 | *.mod filter=lfs diff=lfs merge=lfs -text
90 | *.xm filter=lfs diff=lfs merge=lfs -text
91 | *.it filter=lfs diff=lfs merge=lfs -text
92 | *.mo3 filter=lfs diff=lfs merge=lfs -text
93 | *.fla filter=lfs diff=lfs merge=lfs -text
94 | *.br filter=lfs diff=lfs merge=lfs -text
95 | *.gz filter=lfs diff=lfs merge=lfs -text
96 |
97 | # Line endings
98 | *.txt text eol=lf
99 | *.cs diff=csharp text eol=lf
100 | *.tt text eol=lf
101 | *.sh text eol=lf
102 | *.md text eol=lf
103 | *.json text eol=lf
--------------------------------------------------------------------------------
/CLI.Unity.Trimmer/Source/Commands/Trim.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using AssetsTools.NET;
5 | using AssetsTools.NET.Extra;
6 |
7 | namespace Unity.Trimmer.Cli.Commands
8 | {
9 | public static class Trim
10 | {
11 | public static void Execute(string input, string output, string classdata, string font)
12 | {
13 | var manager = new AssetsManager();
14 | var asset = manager.LoadAssetsFile(input, false);
15 |
16 | if (!string.IsNullOrEmpty(classdata))
17 | manager.LoadClassPackage(classdata); // TODO: Is this necessary?
18 |
19 | manager.LoadClassDatabaseFromPackage(asset.file.typeTree.unityVersion);
20 |
21 | Console.WriteLine($"Asset found: {asset.name} ({asset.file.typeTree.unityVersion})");
22 |
23 | byte[] fontBytes = null;
24 | if (!string.IsNullOrEmpty(font))
25 | fontBytes = File.ReadAllBytes(font);
26 |
27 | var replacers = new List();
28 | var empty = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // 2x2 32bit
29 |
30 | // Loop all assets to find all Texture2D and replace them with 2x2 empty one
31 | // TODO: Still left with ~350kb of crap, seems like there is a compute shader worth 200kb, not sure if worth trying to slim the rest, when compressed with brotli we're left with less than 66kb which is a huge gain compared to before
32 | foreach (var info in asset.table.assetFileInfo)
33 | {
34 | var baseField = manager.GetTypeInstance(asset, info).GetBaseField();
35 | var name = baseField.Get("m_Name").GetValue().AsString();
36 |
37 | Console.WriteLine($"Found asset: {name} / {(AssetClassID) info.curFileType}");
38 |
39 | // We've got a `Texture2D` so replace it
40 | if (info.curFileType == (int) AssetClassID.Texture2D)
41 | {
42 | var texture = TextureFile.ReadTextureFile(baseField);
43 | if (texture.m_Width < 2 && texture.m_Height < 2) continue;
44 |
45 | Console.WriteLine($"{texture.m_Width} / {texture.m_Height} / {(TextureFormat) texture.m_TextureFormat}");
46 |
47 | // Create the new texture
48 | texture.m_TextureFormat = (int) TextureFormat.RGBA32;
49 | var data = TextureFile.Encode(empty, (TextureFormat) texture.m_TextureFormat, 2, 2);
50 |
51 | texture.SetTextureDataRaw(data, 2, 2);
52 | texture.WriteTo(baseField);
53 |
54 | var bytes = baseField.WriteToByteArray();
55 | var replacer = new AssetsReplacerFromMemory(0, info.index, (int) info.curFileType, 0xffff, bytes);
56 | replacers.Add(replacer);
57 |
58 | Console.WriteLine($"*** Texture replaced!");
59 | }
60 |
61 | // Also replace "Arial" font since we don't need it
62 | if (fontBytes != null && info.curFileType == (int) AssetClassID.Font)
63 | {
64 | Console.WriteLine($"Found Font!");
65 | foreach (var child in baseField.children)
66 | {
67 | Console.WriteLine($"Child: {child.GetName()} / {child.GetFieldType()}");
68 |
69 | if (child.GetName() == "m_FontData")
70 | {
71 | Console.WriteLine($"Data:");
72 | foreach (var child2 in child.children)
73 | {
74 | Console.WriteLine($"Child2: {child2.GetName()} / {child2.GetFieldType()}");
75 |
76 | if (child2.GetName() == "Array")
77 | {
78 | // Extract font data
79 | /*var size = child2.GetValue().AsArray().size;
80 | var bytes = new byte[size];
81 | for (int i = 0; i < size; i++)
82 | {
83 | bytes[i] = (byte) child2[i].GetValue().AsInt();
84 | }
85 |
86 | File.WriteAllBytes($"{font}.copy", bytes);*/
87 |
88 | // Overwrite data
89 | child2.GetValue().Set(new AssetTypeArray(fontBytes.Length));
90 |
91 | AssetTypeValueField[] children = new AssetTypeValueField[fontBytes.Length];
92 | for (int i = 0; i < fontBytes.Length; i++)
93 | {
94 | AssetTypeValueField c = ValueBuilder.DefaultValueFieldFromArrayTemplate(child2);
95 | c.GetValue().Set(fontBytes[i]);
96 | children[i] = c;
97 | }
98 |
99 | child2.SetChildrenList(children);
100 |
101 | // Replace
102 | var bytes = baseField.WriteToByteArray();
103 | var replacer = new AssetsReplacerFromMemory(0, info.index, (int) info.curFileType, 0xffff, bytes);
104 | replacers.Add(replacer);
105 | }
106 | }
107 | }
108 | }
109 | }
110 | }
111 |
112 | Console.WriteLine($"Writing output to: \"{output}\"");
113 |
114 | File.Delete(output);
115 | using (var stream = File.OpenWrite(output))
116 | using (var writer = new AssetsFileWriter(stream))
117 | asset.file.Write(writer, 0, replacers, 0);
118 |
119 | Console.WriteLine($"Done!");
120 |
121 | manager.UnloadAllAssetsFiles();
122 | }
123 | }
124 | }
--------------------------------------------------------------------------------